Dockerfile的基本结构
Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。
docker build命令用于从Dockerfile构建映像。可以在docker build命令中使用 -f 标志指向文件系统中任何位置的Dockerfile。
Dockerfile由一行行命令语句组成,并且支持以#开头的注释行
Dockerfile分为四部分:基础镜像信息、维护者信息、 镜像操作指令和容器启动时执行指令。
Dockerfile文件说明
Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声
明以 # 字符开头则被视为注释。可以在Docker文件中使用 RUN , CMD , FROM , EXPOSE , ENV 等指令。
Dockerfile 有以下指令选项:
- FROM
- MAINTAINER
- RUN
- CMD
- EXPOSE
- ENV
- ADD
- COPY
- ENTRYPOINT
- VOLUME
- USER
- WORKDIR
-
1.1 FROM
用法:
FROM <image>
或者
FROM <image>
FROM指定构建镜像的基础源镜像,如果本地没有指定的镜像,则会自动从 Docker 的公共库 pull 镜像下来。
- FROM必须是 Dockerfile 中非注释行的第一个指令,即一个 Dockerfile 从FROM语句开始。
- FROM可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像。
如果FROM语句没有指定镜像标签,则默认使用latest标签。
1.2 MAINTAINER
用法:
MAINTAINER
指定创建镜像的用户
RUN 有两种使用方式RUN
- RUN “executable”, “param1”, “param2”
每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像,后续的RUN都在之前RUN提交后的镜像为基础,镜像是分层的,可以通过一个镜像的任何一个历史提交点来创建,类似源码的版本控制。
exec 方式会被解析为一个 JSON 数组,所以必须使用双引号而不是单引号。exec 方式不会调用一个命令 shell,所以也就不会继承相应的变量,如:
RUN [ "echo", "$HOME" ]
这种方式是不会达到输出 HOME 变量的,正确的方式应该是这样的
RUN [ "sh", "-c", "echo", "$HOME" ]
RUN产生的缓存在下一次构建的时候是不会失效的,会被重用,可以使用—no-cache选项,即docker build —no-cache,如此便不会缓存。
1.3 CMD
CMD有三种使用方式:
- CMD “executable”,”param1”,”param2”
- CMD “param1”,”param2”
- CMD command param1 param2 (shell form)
CMD指定在 Dockerfile 中只能使用一次,如果有多个,则只有最后一个会生效。
CMD的目的是为了在启动容器时提供一个默认的命令执行选项。如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令。CMD会在启动容器的时候执行,build 时不执行,而RUN只是在构建镜像的时候执行,后续镜像构建完成之后,启动容器就与RUN无关了,这个初学者容易弄混这个概念,这里简单注解一下。
1.4 EXPOSE
EXPOSE <port> [<port>...]
告诉 Docker 服务端容器对外映射的本地端口,需要在 docker run 的时候使用-p或者-P选项生效。
1.5 ENV
ENV <key> <value> # 只能设置一个变量ENV <key>=<value> ... # 允许一次设置多个变量
指定一个环节变量,会被后续RUN指令使用,并在容器运行时保留。
例子:
ENV myName="John Doe" myDog=Rex\ The\ Dog \myCat=fluffy
等同于
ENV myName John DoeENV myDog Rex The DogENV myCat fluffy
1.6 ADD
ADD <src>... <dest>
ADD复制本地主机文件、目录或者远程文件 URLS 从 并且添加到容器指定路径中 。
支持通过 Go 的正则模糊匹配,具体规则可参见 Go filepath.Match
ADD hom* /mydir/ # adds all files starting with "hom"ADD hom?.txt /mydir/ # ? is replaced with any single character
- 路径必须是绝对路径,如果 不存在,会自动创建对应目录
- 路径必须是 Dockerfile 所在路径的相对路径
- 如果是一个目录,只会复制目录下的内容,而目录本身则不会被复制
- 在使用该指令的时候还可以加上
--chown=<user>:<group>选项来改变文件的所属用户及所属组。ADD --chown=55:mygroup files* /mydir/ADD --chown=bin files* /mydir/ADD --chown=1 files* /mydir/ADD --chown=10:11 files* /mydir/
1.7 COPY
COPY <src>... <dest>
COPY复制新文件或者目录从 并且添加到容器指定路径中 。用法同ADD,唯一的不同是不能指定远程文件 URLS。
注: 使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。
ADD 更高级的复制文件
ADD 指令和 COPY 的格式和性质基本一致。如果 <源路径> 为一个 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去。
因此在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。
1.8 ENTRYPOINT
- ENTRYPOINT “executable”, “param1”, “param2”
- ENTRYPOINT command param1 param2 (shell form)
配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖,而CMD是可以被覆盖的。如果需要覆盖,则可以使用docker run —entrypoint选项。
每个 Dockerfile 中只能有一个ENTRYPOINT,当指定多个时,只有最后一个生效。
Exec form ENTRYPOINT 例子
通过ENTRYPOINT使用 exec form 方式设置稳定的默认命令和选项,而使用CMD添加默认之外经常被改动的选项。
FROM ubuntuENTRYPOINT ["top", "-b"]CMD ["-c"]
通过 Dockerfile 使用ENTRYPOINT展示前台运行 Apache 服务
FROM debian:stableRUN apt-get update && apt-get install -y --force-yes apache2EXPOSE 80 443VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
Shell form ENTRYPOINT 例子
这种方式会在/bin/sh -c中执行,会忽略任何CMD或者docker run命令行选项,为了确保docker stop能够停止长时间运行ENTRYPOINT的容器,确保执行的时候使用exec选项。
FROM ubuntuENTRYPOINT exec top -b
如果在ENTRYPOINT忘记使用exec选项,则可以使用CMD补上:
FROM ubuntuENTRYPOINT top -bCMD --ignored-param1 # --ignored-param2 ... --ignored-param3 ... 依此类推
1.9 VOLUME
VOLUME ["/data"]
创建一个可以从本地主机或其他容器挂载的挂载点,后续具体介绍。
1.10 USER
USER daemon
指定运行容器时的用户名或 UID,后续的RUN、CMD、ENTRYPOINT也会使用指定用户。
1.11 WORKDIR
WORKDIR /path/to/workdir
为后续的RUN、CMD、ENTRYPOINT指令配置工作目录。可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。
WORKDIR /aWORKDIR bWORKDIR cRUN pwd
最终路径是/a/b/c。
WORKDIR指令可以在ENV设置变量之后调用环境变量:
ENV DIRPATH /pathWORKDIR $DIRPATH/$DIRNAME
1.12 ONBUILD
ONBUILD [INSTRUCTION]
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,Dockerfile 使用如下的内容创建了镜像 image-A:
[...]ONBUILD ADD . /app/srcONBUILD RUN /usr/local/bin/python-build --dir /app/src[...]
如果基于 image-A 创建新的镜像时,新的 Dockerfile 中使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。
# Automatically run the followingADD . /app/srcRUN /usr/local/bin/python-build --dir /app/src
使用ONBUILD指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild。
docker build命令
docker build 命令用于使用 Dockerfile 创建镜像
语法
docker build [OPTIONS] PATH | URL | -
常用参数
—build-arg=[] :设置镜像创建时的变量;
-f :指定要使用的Dockerfile路径;
—rm :设置镜像成功后删除中间容器;
—tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。
要求
DockerFile大致流程
- docker 从基础镜像运行一个容器
- 执行一条指令并对容器做出修改
- 执行类似 docker commit 的操作提交一个新的镜像层
- docker 再基于刚提交的镜像运行一个新容器
- 执行dockerfile 中的下一条执行直到所有的指令都执行完成
总结
从应用软件的角度来看,DockerFile、Docker镜像和Docker容器分别代表软件的三个不同阶段
- DockerFile 是软件的原材料
- Docker镜像是软件的交付品
- Docker 容器可以认为是软件的运行 状态
DockerFile 面向开发,Docker镜像成为交付标准,Docker容器则设计部署与运维,三者缺一不可,合力充当Docker体系的基石。
示例:
测试默认使用centos 镜像启动一个容器进入,查看基本的常用的命令都是不存在的,例如ifconfig ip vim 之类的
那么与常用的基本环境不一样,不适合长久使用。利用源镜像做修改,配置一些本身不存在的命令环境。
默认文件名就叫Dokerfile 否则还需要加参数 -f 指定文件
]# vim DokerfileFROM hub.c.163.com/library/centos # 源镜像,可以通过docker images 查看MAINTAINER Meng # 指定创建镜像的用户,自行填写RUN yum -y install vim # 安装vim命令RUN yum -y install net-tools # 安装ifconfig命令CMD /bin/bash保存退出[root@vms10 ~]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES[root@vms10 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEnginx latest 605c77e624dd 2 months ago 141MBhub.c.163.com/library/wordpress latest dccaeccfba36 4 years ago 406MBhub.c.163.com/library/centos latest 328edcd84f1b 4 years ago 193MBhub.c.163.com/library/mysql latest 9e64176cd8a2 4 years ago 407MB[root@vms10 ~]# cat DockerfileFROM hub.c.163.com/library/centosMAINTAINER MengRUN yum -y install vimRUN yum -y install net-toolsCMD /bin/bash[root@vms10 ~]# docker build -t centos:v1 . # 指定路径 . 当前路径,默认找Dokerfile... ... ... ...Removing intermediate container 0938679b34ee---> 8855d43bd825Step 5/5 : CMD /bin/bash---> Running in 00e5f83fd884Removing intermediate container 00e5f83fd884---> 7d68fa2f7814Successfully built 7d68fa2f7814Successfully tagged centos:v1[root@vms10 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEcentos v1 7d68fa2f7814 21 seconds ago 559MBnginx latest 605c77e624dd 2 months ago 141MBhub.c.163.com/library/wordpress latest dccaeccfba36 4 years ago 406MBhub.c.163.com/library/centos latest 328edcd84f1b 4 years ago 193MBhub.c.163.com/library/mysql latest 9e64176cd8a2 4 years ago 407MB

[root@vms10 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEcentos v1 7d68fa2f7814 21 seconds ago 559MBnginx latest 605c77e624dd 2 months ago 141MBhub.c.163.com/library/wordpress latest dccaeccfba36 4 years ago 406MBhub.c.163.com/library/centos latest 328edcd84f1b 4 years ago 193MBhub.c.163.com/library/mysql latest 9e64176cd8a2 4 years ago 407MB[root@vms10 ~]# docker run -it --rm --name centos centos:v1[root@18dece04eeca /]# ifconfigeth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255ether 02:42:ac:11:00:02 txqueuelen 0 (Ethernet)RX packets 5 bytes 418 (418.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536inet 127.0.0.1 netmask 255.0.0.0loop txqueuelen 1 (Local Loopback)RX packets 0 bytes 0 (0.0 B)RX errors 0 dropped 0 overruns 0 frame 0TX packets 0 bytes 0 (0.0 B)TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0[root@18dece04eeca /]# which vim/usr/bin/vim

每多一个 RUN 就会多一层,可以通过history查看。
[root@vms10 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEcentos v1 7d68fa2f7814 12 minutes ago 559MBnginx latest 605c77e624dd 2 months ago 141MBhub.c.163.com/library/wordpress latest dccaeccfba36 4 years ago 406MBhub.c.163.com/library/centos latest 328edcd84f1b 4 years ago 193MBhub.c.163.com/library/mysql latest 9e64176cd8a2 4 years ago 407MB[root@vms10 ~]# docker history centos:v1IMAGE CREATED CREATED BY SIZE COMMENT7d68fa2f7814 12 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B8855d43bd825 12 minutes ago /bin/sh -c yum -y install net-tools 156MB0f677939a1ac 12 minutes ago /bin/sh -c yum -y install vim 211MB7e4a9ed3a8e2 13 minutes ago /bin/sh -c #(nop) MAINTAINER Meng 0B328edcd84f1b 4 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B<missing> 4 years ago /bin/sh -c #(nop) LABEL name=CentOS Base Im… 0B<missing> 4 years ago /bin/sh -c #(nop) ADD file:63492ba809361c51e… 193MB[root@vms10 ~]# docker history hub.c.163.com/library/centos:latestIMAGE CREATED CREATED BY SIZE COMMENT328edcd84f1b 4 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B<missing> 4 years ago /bin/sh -c #(nop) LABEL name=CentOS Base Im… 0B<missing> 4 years ago /bin/sh -c #(nop) ADD file:63492ba809361c51e… 193MB

上述容器镜像源中默认使用的是CentOS官网的yum源,安装会很慢,将本地yum源复制到 容器中,使用本地yum源安装
[root@vms10 ~]# cd /etc/yum.repos.d/[root@vms10 yum.repos.d]# lsCentOS-Base.repo docker-ce.repo epel.repo k8s.repo[root@vms10 yum.repos.d]# mv CentOS-Base.repo centos-base.repo[root@vms10 yum.repos.d]# lscentos-base.repo docker-ce.repo epel.repo k8s.repo[root@vms10 yum.repos.d]# tar zcf /root/repo.tar.gz *[root@vms10 yum.repos.d]# cd[root@vms10 ~]# tar tf repo.tar.gz # 查看压缩包信息centos-base.repodocker-ce.repoepel.repok8s.repo[root@vms10 ~]# vim DockerfileFROM hub.c.163.com/library/centosMAINTAINER MengADD repo.tar.gz /etc/yum.repos.d/RUN rm -rf /etc/yum.repos.d/CentOS*RUN yum -y install vimRUN yum -y install net-toolsCMD /bin/bash[root@vms10 ~]# docker build -t centos:v1 . # 重新构建

基于上述镜像创建一个nginx的镜像:
[root@vms10 ~]# vim dockerfile_nginxFROM centos:v1MAINTAINER MengRUN yum install nginx -yADD index.html /usr/share/nginx/html/ # 复制本机文件到容器中的文件EXPOSE 80 # 通告使用的是 80CMD ["nginx","-g","daemon off;"][root@vms10 ~]# docker build -t nginx:v1 . -f dockerfile_nginx... ... ...Complete!Removing intermediate container c2d3673bd401---> 45c18228331fStep 5/5 : CMD ["nginx","-g","daemon off;"]---> Running in 1b689dbfa7aaRemoving intermediate container 1b689dbfa7aa---> ea5f67bdd18bSuccessfully built ea5f67bdd18bSuccessfully tagged nginx:v1[root@vms10 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEnginx v1 ea5f67bdd18b 29 seconds ago 872MBcentos v1 9fcc8c7162fb 12 minutes ago 643MBnginx latest 605c77e624dd 2 months ago 141MB[root@vms10 ~]# docker run -dit --name=nginx -p 80:80 nginx:v1b8d8314f18157a9e72a0e7868ebe2b29f86bae399f096d5feb9c9639bde2e994[root@vms10 ~]# curl 127.0.0.1Hello Nginx !
ADD与COPY的区别
[root@vms10 ~]# vim dockerfile_copy-addFROM centos:v1MAINTAINER MengRUN mkdir /add /copyADD repo.tar.gz /addCOPY repo.tar.gz /copyCMD /bin/bash[root@vms10 ~]# docker build -t centos:copy-add . -f dockerfile_copy-addSending build context to Docker daemon 461.1MBStep 1/6 : FROM centos:v1---> 9fcc8c7162fbStep 2/6 : MAINTAINER Meng---> Using cache---> ec789341500eStep 3/6 : RUN mkdir /add /copy---> Running in 2936d14cdd9cRemoving intermediate container 2936d14cdd9c---> aa9c5c38dce0Step 4/6 : ADD repo.tar.gz /add---> ea3a78ec452bStep 5/6 : COPY repo.tar.gz /copy---> 4a6f415eb517Step 6/6 : CMD /bin/bash---> Running in 8c275f90d557Removing intermediate container 8c275f90d557---> 192b0d7c6d4aSuccessfully built 192b0d7c6d4aSuccessfully tagged centos:copy-add

[root@vms10 ~]# docker run -it --rm centos:copy-add[root@1ac1b96097a8 /]# lsadd bin dev home lib64 media opt root sbin sys usranaconda-post.log copy etc lib lost+found mnt proc run srv tmp var[root@1ac1b96097a8 /]# ls add/ # ADD会自动解压到目标路径centos-base.repo docker-ce.repo epel.repo k8s.repo[root@1ac1b96097a8 /]# ls /copy/ # COPY不会解压,直接复制进入目标repo.tar.gz[root@1ac1b96097a8 /]#

ENV、USER、VOLUME
[root@vms10 ~]# vim dockerfile_otherFROM centos:v1MAINTAINER MengRUN useradd tom # 创建用户ENV aaa=hahaha # 设置变量VOLUME ["/data1"] # 创建挂载数据卷 ,相当于手动 -v 参数,自动在容器内创建并且映射挂载到宿主机器中的某个目录,通过docker inspect查看mountsUSER tomCMD /bin/bash[root@vms10 ~]# docker build -t centos:other . -f dockerfile_other... ... ...Successfully built d21324796009Successfully tagged centos:other
启动运行容器查看
[root@vms10 ~]# docker run -it --rm centos:other # 默认没有指定用户进入[tom@aaf0245e6065 /]$ echo $aaa # 有已经设置好的变量hahaha[tom@aaf0245e6065 /]$ ls /data1/ # 有挂载的数据卷[tom@aaf0245e6065 /]$ exit[root@vms10 ~]# docker run -it -u root --rm centos:other # 指定用户 -u 进入后就是对应的用户[root@9c449947d4ef /]# lsanaconda-post.log data1 etc lib lost+found mnt proc run srv tmp varbin dev home lib64 media opt root sbin sys usr[root@9c449947d4ef /]# pwd/

练习:
写一个可以ssh的镜像
1、先书写Dockerfile文件
[root@vms10 ~]# vim dockerfile_sshFROM centos:v1MAINTAINER MengRUN yum -y install openssh-clients openssh-serverRUN echo 123456 | passwd --stdin rootEXPOSE 22CMD ["/usr/sbin/sshd","-D"]
2、构建容器
[root@vms10 ~]# docker build -t centos:ssh . -f dockerfile_ssh... ... ...Successfully built 9f88b76650feSuccessfully tagged centos:ssh[root@vms10 ~]# docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEcentos ssh 9f88b76650fe 42 seconds ago 847MB
3、启动容器
[root@vms10 ~]# docker run -dit --restart=always centos:ssh后会有报错[root@vms10 ~]# docker logs 9f5Could not load host key: /etc/ssh/ssh_host_rsa_keyCould not load host key: /etc/ssh/ssh_host_ecdsa_keyCould not load host key: /etc/ssh/ssh_host_ed25519_keysshd: no hostkeys available -- exiting.Could not load host key: /etc/ssh/ssh_host_rsa_keyCould not load host key: /etc/ssh/ssh_host_ecdsa_keyCould not load host key: /etc/ssh/ssh_host_ed25519_key
4、修改Docker文件并建立启动
[root@vms10 ~]# cat dockerfile_sshFROM centos:v1MAINTAINER MengRUN yum -y install openssh-clients openssh-server && \ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key && \ssh-keygen -t ecdsa -f /etc/ssh/ssh_host_ecdsa_key && \ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key && \echo 123456 | passwd --stdin rootEXPOSE 22CMD ["/usr/sbin/sshd","-D"][root@vms10 ~]# docker build -t centos:ssh . -f dockerfile_ssh... ... ...Successfully built 166f8df0c528Successfully tagged centos:ssh[root@vms10 ~]# docker run -dit --name=ssh --restart=always centos:ssh[root@vms10 ~]# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESefbec4c7bdec centos:ssh "/usr/sbin/sshd -D" 2 seconds ago Up 2 seconds 22/tcp
5、链接测试
[root@vms10 ~]# docker inspect ssh | grep -i ipaddr"SecondaryIPAddresses": null,"IPAddress": "172.17.0.3","IPAddress": "172.17.0.3",[root@vms10 ~]# ssh 172.17.0.3The authenticity of host '172.17.0.3 (172.17.0.3)' can't be established.ECDSA key fingerprint is SHA256:2iVxe4OfNZBTdjyO8s2Lt8+S3KvrzWzFzA7X0fFwQCk.ECDSA key fingerprint is MD5:bf:c6:1a:40:1b:48:4f:ce:59:de:07:1e:22:85:e2:c7.Are you sure you want to continue connecting (yes/no)? yesWarning: Permanently added '172.17.0.3' (ECDSA) to the list of known hosts.root@172.17.0.3's password:[root@efbec4c7bdec ~]# pwd/root[root@efbec4c7bdec ~]# exitlogoutConnection to 172.17.0.3 closed.[root@vms10 ~]#
如果需要映射到宿主机
[root@vms10 ~]# docker run -dit --name=ssh --restart=always -p 22:22 centos:ssh启动后使用宿主机端口链接

