dockers进阶

docker file

docker file 是用于构建镜像的脚本,由一系列指令构成,通过docker build命令构建镜像时,docker file会由上至下按照顺序执行指令,每一条指令都将会构建出一个镜像,也就是镜像的分层。因此,指令越多,镜像的分层也就越多,效率也就越低。所以在编写docker file时,能使用一条命令完成的指令,就别使用两条。

指令

指令无视大小写,但一般用大写,方便与其他命令区分,指令后至少会携带一个参数。#号开头的为注释。

FROM

#语法:FROM<image>[:<tag>]
#解析:用于指定镜像,必须作为第一条指令,如果镜像后没有写tag,默认认为是lates

ADD

#语法1:ADD [src] [dest]
#语法2:ADD ["<src>","<dest>"] 路径中存在空格时需要用双引号阴起来
#解析:该命令将复制src的文件到容器中的dest目录中。src可以是宿主机的相对路径或绝对路径,如果使用相对路径,需要与docker build命令后的路径相匹配。src可以是文件、压缩文件或url,如果是压缩文件复制到容器后会自动解压,如果是url,此时的ADD就相当于wget命令。但是src最好不要是目录,因为会将目录中的内容复制到指定的dest中。dest必须是绝对路径,并且需要在最后加上斜杠,不然系统会认为是一个文件。

COPY

#用法与ADD完全相同,但是src不能是url,并且压缩文件不会自动解压。

MAINTAINER

#语法:MAINTAINER name
#解析:一般用于填写镜像维护者的姓名和邮箱,但是官方已经不建议使用该指令,使用LABEL指令代替

LABEL

#语法:LABEL <key>=<value> <key>=<value> ......
#解析:采用键值对的方式,代替MAINTAINER指令,可以包含任何元素。通过docker inspect查看MAINTAINER和LABEL的内容

ENV

#语法一:ENV <key> <value>
#解析:定义变量,但是只能定义一个变量

#语法二:ENV <key>=<value> <key>=<value> ......
#解析:定义变量,可以定义多个

WORKDIR

#语法:WORKDIR <path>
#解析:容器打开后默认进入的目录,可以使用-w参数覆盖构建时设置的。WORKDIR可以使用多个,后续的WORKDIR如果使用的是相对路径,会基于之前的WORKDIR指定的路径。

RUN

#语法一:RUN <command>
#解析:run后面直接跟上需要执行的命令,在docker build执行的过程中会使用shell运行command

#语法二:RUN ["EXECUTABLE","PARAM1","PARAM2",....]
#解析:在docker build执行的过程中,会调用第一个参数"EXECUTABLE"指定的应用程序运行,并使用后面第二第三等参数作为应用程序的运行参数

ARG

#语法:ARG <varname>[=<default value>]
#解析:定义一个变量,该变量将会使用于镜像构建运行时。如果需要定义多个变量,则需要定义多个ARG指令

默认会使用dockerfile里的ARG变量,如果需要更改,可以在构建镜像时单独指定:

docker build -t myarg:2.0 -build--arg name=tom
#这里就是将name的值编为tom。

CMD(重要)

#语法一:CMD["EXECUTABLE","PARAM1","PARAM2".....]
#解析:在容器启动后,执行完RUN指令之后,会调用“EXECUTABLE”,并将后续的“PARAM”作为运行参数

#语法二:CMD command param1 param2 .....
#解析:command指shell命令,容器启动后会执行此shell命令

#语法三:CMD["PARAM1","PARAM2".....]
#解析:提供给ENTRYPOINT的默认参数

cmd里的命令是可以被替换的,例如在docker run命令的最后,一般会加上/bin/bash,来指定以bash交互式来运行容器,如果镜像的cmd中不是/bin/bash,那么在run命令后面加上/bin/bash,就会将cmd中的命令覆盖,强制执行/bin/bash。但是在ENTRYPOINT中不生效。

举个例子:例如tomcat的镜像,运行时如果直接用“docker run --name mytom -it tomcat:8.5.49 ”来启动,会直接占用前台来运行,这是因为在tomcat的镜像文件的最后设置了cmd,如果使用“docker run --name mytom -it tomcat:8.5.49 /bin/bash”,那么就会进入交互式界面,而不是占用前台。

ENTRYPOINT(重要)

#语法一:ENTRYPOINT["EXECUTABLE","PARAM1","PARAM2"....."]
#解析:在容器启动后,执行完RUN指令之后,会调用“EXECUTABLE”,并将后续的“PARAM”作为运行参数

#语法二:ENTRYPOINT command param1 param2 .....
#解析:command指shell命令,容器启动后会执行此shell命令

docker run 命令后有选项时,ENTRYPOINT后如果是shell命令,那么就会忽略选项,直接执行shell命令。如果ENTRYPOINT后是"EXECUTABLE"的形式,那么会进行拼接,将"EXECUTABLE"和选项结合一起执行。

构建镜像

在构建镜像前需要先了解一个特殊镜像:scratch。scratch是一个空镜像,是所有镜像的base image(可以理解为是所有镜像的父镜像)。scratch只能在docker file中被继承,不能通过pull拉取,不能run也没有tag,并且不会生成镜像中的文件系统层。在docker中,scratch是一个保留字,无法被用于命名镜像。

构建centos镜像

从docker hub上拉取的centos镜像里面都是最小化安装,很多常用命令都没有,包括yum源都是默认的国外源,拉取过来根本无法使用,我们可以构建自己的centos镜像,通过docker file来修改yum源和安装日常所需软件,这样下次需要使用centos镜像时就可以做到拉取即用。

#创建工作目录
[root@test2 ~]# mkdir mycentos
[root@test2 ~]# cd mycentos/
[root@test2 mycentos]# touch Dockerfile
[root@test2 mycentos]# vim Dockerfile 
[root@test2 mycentos]# cat Dockerfile 
#基于centos7镜像构建
FROM centos:7
#创作者的信息,但是MAINTAINER指令官方不推荐使用
MAINTAINER wxb 1285061762@qq.com
#LABEL命令与MAINTAINER一样都是用来写创作者信息,官方更推荐用LABEL 
LABEL auth="wxb" email="1285061762@qq.com"
#定义变量
ENV work_dir /usr/local
#容器启动后会自动切换到该目录下
WORKDIR $work_dir
#需要执行的命令
RUN yum -y install wget
CMD /bin/bash

#使用docker build 命令,通过dockerfile来构建镜像,最后的路径里需要确保有dockerfile文件,可以是相对路径也可以是绝对路径
[root@test2 mycentos]# docker build -t mycentos:1.0 /root/mycentos/ 

#构建时,会逐步执行dockerfile中的每条命令
#使用构建的镜像启动一个容器
[root@test2 ~]# docker run --name mycentos -it mycentos:1.0
[root@c00870f6c07c local]# w        
w             watch         wc            weak-modules  whereis       who           wipefs        
wait          watchgnupg    wdctl         wget          while         whoami        write #进入容器后发现,默认在local目录下,并且按照了wget命令

玄虚镜像

玄虚镜像是指没有既“repository”也没有“tag”的镜像。当创建一个新镜像时,指定已经存在的tag或repository时,原来的镜像就会变为玄虚镜像。玄虚镜像会占用内存,一般会定期清理掉。

#可以通过查看镜像后,根据镜像id删除,也可以批量删除
#如果存在通过玄虚镜像启动的容器,无论是否在运行,都会导致无法删除,所以需要先删除对应的容器,再删除镜像
[root@test2 ~]# docker image prune

持久化

数据卷持久化

容器的实际用途还是用来跑服务,如果是用来跑数据库类的服务,比如跑一台MySQL,如果执行了误删操作,MySQL中的数据没有备份保存,那么后续恢复会很困难,如果能够实时的将容器中的内容保存到本地,那么就可以避免误操作带来的后果。

docker提供了三种实时同步(宿主机与容器FS之间数据的同步)的方式:

1、数据卷

2、bind mounts (绑定挂载)

3、tmpfs(临时文件系统)

以上三种方式中,官方更推荐使用“数据卷”,而在生产中,使用的最多的也是“数据卷”。

数据卷

数据卷是宿主机中比较特殊的目录,它与容器直接相关联,在任意一端进行操作,都会影响到对端。而这个目录在宿主机中被称为数据卷,在容器中,对应的目录被称为数据卷的挂载点。数据卷的设计目的就是为了实现数据持久化,它完全独立于容器的生命周期,属于宿主机的文件系统,但是不属于unionFS。所以容器删除时,并不会删除其挂载的数据卷。

简单来说,在宿主机中有一个被称为数据卷的目录,它会挂载到容器中,如果容器被删除,这个目录也不会删除,会保留数据信息,因为它是独立于容器的,属于宿主机文件系统的一个目录。

数据卷的特性

  1. 数据卷在容器启动时初始化,如果容器启动后其本身已经包含了数据,那么这些数据会在容器启动3后直接出现在数据卷中。反之,如果数据卷中有内容,那么容器在启动后也会有数据卷中的内容。
  2. 对数据卷或容器挂载节点中的内容直接修改,修改后对方也会立即被修改
  3. 数据卷会一直存在,即使容器已经被删除
  4. 数据卷可以在容器之间重用和共享

创建数据卷

数据卷需要在容器启动时指定。格式为:docker run -it -v /宿主机、目录绝对路径:/容器内目录绝对路径 镜像

#运行容器时创建数据卷
#运行一个centos容器,并在/root/MountNode目录中创建一个文件
[root@test2 ~]# docker run --name mycent -v /tmp/host:/root/MountNode -it centos
[root@990b6e352dd1 /]# ls
bin  etc   lib	  lost+found  mnt  proc  run   srv  tmp  var
dev  home  lib64  media       opt  root  sbin  sys  usr
[root@990b6e352dd1 /]# cd /root/MountNode/
[root@990b6e352dd1 MountNode]# ls
[root@990b6e352dd1 MountNode]# echo "hello world" > DataVolume 
[root@990b6e352dd1 MountNode]# ls
DataVolume
[root@990b6e352dd1 MountNode]# cat DataVolume 
hello world
[root@990b6e352dd1 MountNode]# exit
exit
[root@test2 ~]# cat /tmp/host/DataVolume 
hello world
#删除容器,再查看数据卷是否存在
[root@test2 ~]# docker rm -f $(docker ps -qa)
990b6e352dd1
24e147c84a36
[root@test2 ~]# cat /tmp/host/DataVolume 
hello world
[root@test2 ~]# 

关于数据卷的权限

数据卷的权限分为两个部分,宿主机对数据卷的权限,和容器对数据卷的权限。其中,宿主机对数据卷始终都有读写权限,而容器有只读或读写权限两种

#查看容器对数据卷的权限
[root@test2 ~]# docker run --name mycent -v /tmp/DataVolume:/root/MountNode -it centos
[root@642610c417a9 /]# exit
exit
[root@test2 ~]# docker inspect mycent

.........

        "Mounts": [
            {
                "Type": "bind",
                "Source": "/tmp/DataVolume",
                "Destination": "/root/MountNode",
                "Mode": "",
                "RW": true,  #权限为RW读写
                "Propagation": "rprivate"
            }
            
..........         

创建容器设置只读数据卷,只需要在命令后加上“:ro”

[root@test2 ~]# docker run --name mycent -v /tmp/DataVolume:/root/MountNode:ro centos
#设置容器只读挂载点,需要在容器挂载目录后加是冒号ro。只能在容器的挂载目录后加,在宿主机路径后加会报错。
[root@test2 ~]# docker inspect mycent

.......

        "Mounts": [
            {
                "Type": "bind",
                "Source": "/tmp/DataVolume",
                "Destination": "/root/MountNode",
                "Mode": "ro",   #权限改为了RO只读
                "RW": false,    #读写权限变为了false
                "Propagation": "rprivate"
            }
        ],

......

数据卷共享

当一个容器与另一个容器使用相同的数据卷时,就称这两个容器实现了数据卷共享。

实现共享的方式很简单,两个容器,使用相同的数据卷,那么命令中的宿主机数据卷目录写成一样的就行了,这方法最简单,但是会造成数据的冗余,所以不使用此方法。

那么这里就得用到另一种方法了,数据卷容器。

数据卷容器时实现数据卷共享的一种非常有效的方案。

当一个容器A启动运行时挂载了数据卷,如果其他容器也需要共享该数据卷,这些容器只需要在docker run创建时通过 --volumes-from [容器A] ,即可实现数据卷的共享,此时容器A就成为数据卷容器。

#先创建容器A
[root@test2 ~]# docker run --name mycentosA -v /tmp/DataVolume:/root/MountNode -it centos
#再在数据卷中写入一些数据
[root@test2 ~]# cd /tmp/DataVolume/
[root@test2 DataVolume]# echo "`date`" > time
[root@test2 DataVolume]# cat time 
Mon Aug 28 15:59:51 CST 2023
[root@test2 DataVolume]# 
#创建新容器B,数据卷目录改为容器A
[root@test2 DataVolume]# docker run --name mycentosB --volumes-from mycentosA -it centos /bin/bash
[root@805ec8c1efe4 /]# cd /root/MountNode/
[root@805ec8c1efe4 MountNode]# ls
time
[root@805ec8c1efe4 MountNode]# cat time 
Mon Aug 28 15:59:51 CST 2023
[root@805ec8c1efe4 MountNode]# 
#容器B中也可以看见之前写入的文件内容

dockerfile持久化

dockerfile持久化,是通过使用dockerfile的volume指令指定数据卷方式实现的持久化。

volume指令

volume指令可以在容器中创建挂载数据卷的挂载点。其参数可以是字符串数组,也可以是使用空格隔开的多个纯字符串。
例如,VOLUME [“/var/www”,“/etc/apache”]或VOLUME /var/www /etc/apache。要注意,dockerfile中的路径都是指挂载点,也就是容器中目录的位置,可以有多个。

[root@test2 ~]# cd dockerfile/
[root@test2 dockerfile]# vim dockerfile
[root@test2 dockerfile]# cat dockerfile 
FROM centos
VOLUME /root/A /root/B
CMD /bin/bash
[root@test2 dockerfile]# 

#构建镜像
[root@test2 dockerfile]# docker build -t myvolume .
[+] Building 0.0s (5/5) FINISHED                                                                                                 docker:default
 => [internal] load build definition from dockerfile                                                                                       0.0s
 => => transferring dockerfile: 86B                                                                                                        0.0s
 => [internal] load .dockerignore                                                                                                          0.0s
 => => transferring context: 2B                                                                                                            0.0s
 => [internal] load metadata for docker.io/library/centos:latest                                                                           0.0s
 => CACHED [1/1] FROM docker.io/library/centos                                                                                             0.0s
 => exporting to image                                                                                                                     0.0s
 => => exporting layers                                                                                                                    0.0s
 => => writing image sha256:a21aef4afdae8841ff0013ab0574ba1fd4ef0435d1e8291b3c04e2527cb9220d                                               0.0s
 => => naming to docker.io/library/myvolume                                                                                                0.0s
#使用构建的镜像创建容器
[root@test2 dockerfile]# docker run --name volume -it myvolume
[root@99e11881c9c3 /]# cd /root/
[root@99e11881c9c3 ~]# ls
A  B  anaconda-ks.cfg  anaconda-post.log  original-ks.cfg
[root@99e11881c9c3 ~]# 
#这里可以发现在dockerfile中写的两个目录都是挂载点,而宿主机的数据卷目录路径可以通过docker inspect查询
[root@test2 dockerfile]# docker inspect volume 
      ......

        "Mounts": [
            {
                "Type": "volume",
                "Name": "911c5ad4a9ce8bc48b8d4ff949a817dd7b452fb2e830482667c07a8515af7db7",
                "Source": "/var/lib/docker/volumes/911c5ad4a9ce8bc48b8d4ff949a817dd7b452fb2e830482667c07a8515af7db7/_data",
                "Destination": "/root/A",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            },
            {
                "Type": "volume",
                "Name": "951f99a136184a599d1275654f3b474980788a9cc2d4c04012a6df8742d41534",
                "Source": "/var/lib/docker/volumes/951f99a136184a599d1275654f3b474980788a9cc2d4c04012a6df8742d41534/_data",
                "Destination": "/root/B",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
      ......

持久化总结

数据卷属于宿主机文件系统,与unionFS是挂载关系,与容器挂载点是硬链接关系,容器是否存在,与数据卷没有关系。

数据卷持久化可以做到对容器实时变更的实时性持久化,不会出现数据丢失的情况。

dockerfile持久化是利用了dockerfile中的VOLUME指令,这种方式与docker run -v存在区别:

docker run -v 是针对已经存在的镜像,dockerfile的VOLUME指令针对于准备创建的镜像。

docker run -v 可以指定数据卷目录的路径,dockerfile的VOLUME指令的数据卷路径由系统指定,可以通过查看容器信息查看数据卷目录路径。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值