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 Doe
ENV myDog Rex The Dog
ENV 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 ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
通过 Dockerfile 使用ENTRYPOINT展示前台运行 Apache 服务
FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/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 ubuntu
ENTRYPOINT exec top -b
如果在ENTRYPOINT忘记使用exec选项,则可以使用CMD补上:
FROM ubuntu
ENTRYPOINT top -b
CMD --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 /a
WORKDIR b
WORKDIR c
RUN pwd
最终路径是/a/b/c。
WORKDIR指令可以在ENV设置变量之后调用环境变量:
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
1.12 ONBUILD
ONBUILD [INSTRUCTION]
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,Dockerfile 使用如下的内容创建了镜像 image-A:
[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
如果基于 image-A 创建新的镜像时,新的 Dockerfile 中使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。
# Automatically run the following
ADD . /app/src
RUN /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 Dokerfile
FROM 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 ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@vms10 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 605c77e624dd 2 months ago 141MB
hub.c.163.com/library/wordpress latest dccaeccfba36 4 years ago 406MB
hub.c.163.com/library/centos latest 328edcd84f1b 4 years ago 193MB
hub.c.163.com/library/mysql latest 9e64176cd8a2 4 years ago 407MB
[root@vms10 ~]# cat Dockerfile
FROM hub.c.163.com/library/centos
MAINTAINER Meng
RUN yum -y install vim
RUN yum -y install net-tools
CMD /bin/bash
[root@vms10 ~]# docker build -t centos:v1 . # 指定路径 . 当前路径,默认找Dokerfile
... ... ... ...
Removing intermediate container 0938679b34ee
---> 8855d43bd825
Step 5/5 : CMD /bin/bash
---> Running in 00e5f83fd884
Removing intermediate container 00e5f83fd884
---> 7d68fa2f7814
Successfully built 7d68fa2f7814
Successfully tagged centos:v1
[root@vms10 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos v1 7d68fa2f7814 21 seconds ago 559MB
nginx latest 605c77e624dd 2 months ago 141MB
hub.c.163.com/library/wordpress latest dccaeccfba36 4 years ago 406MB
hub.c.163.com/library/centos latest 328edcd84f1b 4 years ago 193MB
hub.c.163.com/library/mysql latest 9e64176cd8a2 4 years ago 407MB
[root@vms10 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos v1 7d68fa2f7814 21 seconds ago 559MB
nginx latest 605c77e624dd 2 months ago 141MB
hub.c.163.com/library/wordpress latest dccaeccfba36 4 years ago 406MB
hub.c.163.com/library/centos latest 328edcd84f1b 4 years ago 193MB
hub.c.163.com/library/mysql latest 9e64176cd8a2 4 years ago 407MB
[root@vms10 ~]# docker run -it --rm --name centos centos:v1
[root@18dece04eeca /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.2 netmask 255.255.0.0 broadcast 172.17.255.255
ether 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 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
loop txqueuelen 1 (Local Loopback)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX 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 images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos v1 7d68fa2f7814 12 minutes ago 559MB
nginx latest 605c77e624dd 2 months ago 141MB
hub.c.163.com/library/wordpress latest dccaeccfba36 4 years ago 406MB
hub.c.163.com/library/centos latest 328edcd84f1b 4 years ago 193MB
hub.c.163.com/library/mysql latest 9e64176cd8a2 4 years ago 407MB
[root@vms10 ~]# docker history centos:v1
IMAGE CREATED CREATED BY SIZE COMMENT
7d68fa2f7814 12 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
8855d43bd825 12 minutes ago /bin/sh -c yum -y install net-tools 156MB
0f677939a1ac 12 minutes ago /bin/sh -c yum -y install vim 211MB
7e4a9ed3a8e2 13 minutes ago /bin/sh -c #(nop) MAINTAINER Meng 0B
328edcd84f1b 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:latest
IMAGE CREATED CREATED BY SIZE COMMENT
328edcd84f1b 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]# ls
CentOS-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]# ls
centos-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.repo
docker-ce.repo
epel.repo
k8s.repo
[root@vms10 ~]# vim Dockerfile
FROM hub.c.163.com/library/centos
MAINTAINER Meng
ADD repo.tar.gz /etc/yum.repos.d/
RUN rm -rf /etc/yum.repos.d/CentOS*
RUN yum -y install vim
RUN yum -y install net-tools
CMD /bin/bash
[root@vms10 ~]# docker build -t centos:v1 . # 重新构建
基于上述镜像创建一个nginx的镜像:
[root@vms10 ~]# vim dockerfile_nginx
FROM centos:v1
MAINTAINER Meng
RUN yum install nginx -y
ADD index.html /usr/share/nginx/html/ # 复制本机文件到容器中的文件
EXPOSE 80 # 通告使用的是 80
CMD ["nginx","-g","daemon off;"]
[root@vms10 ~]# docker build -t nginx:v1 . -f dockerfile_nginx
... ... ...
Complete!
Removing intermediate container c2d3673bd401
---> 45c18228331f
Step 5/5 : CMD ["nginx","-g","daemon off;"]
---> Running in 1b689dbfa7aa
Removing intermediate container 1b689dbfa7aa
---> ea5f67bdd18b
Successfully built ea5f67bdd18b
Successfully tagged nginx:v1
[root@vms10 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v1 ea5f67bdd18b 29 seconds ago 872MB
centos v1 9fcc8c7162fb 12 minutes ago 643MB
nginx latest 605c77e624dd 2 months ago 141MB
[root@vms10 ~]# docker run -dit --name=nginx -p 80:80 nginx:v1
b8d8314f18157a9e72a0e7868ebe2b29f86bae399f096d5feb9c9639bde2e994
[root@vms10 ~]# curl 127.0.0.1
Hello Nginx !
ADD与COPY的区别
[root@vms10 ~]# vim dockerfile_copy-add
FROM centos:v1
MAINTAINER Meng
RUN mkdir /add /copy
ADD repo.tar.gz /add
COPY repo.tar.gz /copy
CMD /bin/bash
[root@vms10 ~]# docker build -t centos:copy-add . -f dockerfile_copy-add
Sending build context to Docker daemon 461.1MB
Step 1/6 : FROM centos:v1
---> 9fcc8c7162fb
Step 2/6 : MAINTAINER Meng
---> Using cache
---> ec789341500e
Step 3/6 : RUN mkdir /add /copy
---> Running in 2936d14cdd9c
Removing intermediate container 2936d14cdd9c
---> aa9c5c38dce0
Step 4/6 : ADD repo.tar.gz /add
---> ea3a78ec452b
Step 5/6 : COPY repo.tar.gz /copy
---> 4a6f415eb517
Step 6/6 : CMD /bin/bash
---> Running in 8c275f90d557
Removing intermediate container 8c275f90d557
---> 192b0d7c6d4a
Successfully built 192b0d7c6d4a
Successfully tagged centos:copy-add
[root@vms10 ~]# docker run -it --rm centos:copy-add
[root@1ac1b96097a8 /]# ls
add bin dev home lib64 media opt root sbin sys usr
anaconda-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_other
FROM centos:v1
MAINTAINER Meng
RUN useradd tom # 创建用户
ENV aaa=hahaha # 设置变量
VOLUME ["/data1"] # 创建挂载数据卷 ,相当于手动 -v 参数,自动在容器内创建并且映射挂载到宿主机器中的某个目录,通过docker inspect查看mounts
USER tom
CMD /bin/bash
[root@vms10 ~]# docker build -t centos:other . -f dockerfile_other
... ... ...
Successfully built d21324796009
Successfully 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 /]# ls
anaconda-post.log data1 etc lib lost+found mnt proc run srv tmp var
bin dev home lib64 media opt root sbin sys usr
[root@9c449947d4ef /]# pwd
/
练习:
写一个可以ssh的镜像
1、先书写Dockerfile文件
[root@vms10 ~]# vim dockerfile_ssh
FROM centos:v1
MAINTAINER Meng
RUN yum -y install openssh-clients openssh-server
RUN echo 123456 | passwd --stdin root
EXPOSE 22
CMD ["/usr/sbin/sshd","-D"]
2、构建容器
[root@vms10 ~]# docker build -t centos:ssh . -f dockerfile_ssh
... ... ...
Successfully built 9f88b76650fe
Successfully tagged centos:ssh
[root@vms10 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos ssh 9f88b76650fe 42 seconds ago 847MB
3、启动容器
[root@vms10 ~]# docker run -dit --restart=always centos:ssh
后会有报错
[root@vms10 ~]# docker logs 9f5
Could not load host key: /etc/ssh/ssh_host_rsa_key
Could not load host key: /etc/ssh/ssh_host_ecdsa_key
Could not load host key: /etc/ssh/ssh_host_ed25519_key
sshd: no hostkeys available -- exiting.
Could not load host key: /etc/ssh/ssh_host_rsa_key
Could not load host key: /etc/ssh/ssh_host_ecdsa_key
Could not load host key: /etc/ssh/ssh_host_ed25519_key
4、修改Docker文件并建立启动
[root@vms10 ~]# cat dockerfile_ssh
FROM centos:v1
MAINTAINER Meng
RUN 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 root
EXPOSE 22
CMD ["/usr/sbin/sshd","-D"]
[root@vms10 ~]# docker build -t centos:ssh . -f dockerfile_ssh
... ... ...
Successfully built 166f8df0c528
Successfully tagged centos:ssh
[root@vms10 ~]# docker run -dit --name=ssh --restart=always centos:ssh
[root@vms10 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
efbec4c7bdec 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.3
The 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)? yes
Warning: 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 ~]# exit
logout
Connection to 172.17.0.3 closed.
[root@vms10 ~]#
如果需要映射到宿主机
[root@vms10 ~]# docker run -dit --name=ssh --restart=always -p 22:22 centos:ssh
启动后使用宿主机端口链接