使用 Dockerfile 构建镜像时常见有如下指令:
FROM 添加基础镜像MAINTAINER 添加作者信息LABEL 为镜像添加Label元数据,docker inspect 可查看RUN 构建过程中要运行的命令CMD 指定容器启动时要运行的命令ENTRYPOINT 和CMD类似,但有细微区别ADD 将构建目录下的文件复制到容器内COPY 和ADD类似,但有细微区别EXPOSE 指定运行该镜像容器使用的端口VOLUME 为容器添加卷WORKDIR 设置一个工作目录ENV 设置环境变量ARG 定义build时传递给构建运行时的变量USER 指定用户,不指定默认为rootONBUILD 添加触发器STOPSIGNAL 指定停止容器时使用的系统信号
更多信息参考:
https://docs.docker.com/engine/reference/builder/
RUN、CMD、ENTRYPOINT解析
1号进程之争:exec模式和shell模式
关于RUN指令
RUN 主要在build时,在现有的基础上新添一层layer,创建一个新的image。(一般用来安装软件包)
比如前面的第一个Dockerfile中的例子:
RUN apt-get update && apt-get install -y nginx
RUN echo ‘Hi, I am in your container’ > /usr/share/nginx/html/index.html
关于CMD
FROM alpine:3.9
CMD [ "top" ]
# 如果是这种模式,容器中任务进程就是1号进程,但没有环境变量,如 $HOME
FROM alpine:3.9
CMD [ "sh", "-c", "echo $HOME" ]
# 但这种就是通过shell来运行了, 相当于下面的代码
FROM alpine:3.9
CMD top
PS1:作为启动命令,一个dockerfile中只能有一个CMD,如果有多个,那么只有最后一个会生效。
PS2:docker run -i -t testimage:v1 /bin/ps 中 docker run 会覆盖掉CMD指令
ENTRYPOINT 也有 exec 和 shell 模式,相较于CMD,它不容易被覆盖
示例:
ENTRYPOINT ["/usr/sbin/nginx"]
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]
ENTRYPOINT 还可以和 CMD 配合使用:
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]
# 这样CMD可以将 -h 选项传递给 ENTRYPOINT,最终执行 /usr/sbin/nginx -h
# 也可以实现 docker run 命令将 -h 选项覆盖掉
参考一下:
https://zhuanlan.zhihu.com/p/30555962
COPY 和 ADD 解析
COPY 和 ADD 两个指令也非常相似,如下写法也会将 dirtocp/ 下的所有子文件 (包括子目录但不包括 dirtocp 本身)添加到容器内指定目录下:
# Version: 0.0.1
FROM ubuntu:14.04
MAINTAINER DaFei "fly1248@hotmail.com"
RUN apt-get update && apt-get install -y nginx
RUN echo 'Hi, I am in your container' > /usr/share/nginx/html/index.html
EXPOSE 80
ADD dirtocp/ /usr/share/nginx/html/
COPY dirtocp/ /usr/share/nginx/ # 这里 ADD 和 COPY 指令没有区别
CMD ["/usr/sbin/nginx", "-g", "daemon off;"]
WORKDIR /usr/share/nginx/html/
但如果是对压缩包 file.tar.gz,ADD 指令会将解压后内容添加到目标目录,而COPY指令会直接将压缩包复制到目标目录,没有解压动作。
ADD file.tar.gz /usr/share/nginx/html/
COPY file.tar.gz /usr/share/nginx/
ENV 和 ARG 指令
ENV 指令用来在镜像构建过程中设置环境变量。设置的环境变量可以在后续任何RUN指令中引用。
ENV RVM_PATH /home/rvm/
RUN gem install unicorn
# 使用 ENV 设置多个环境变量
ENV RVM_PATH=/home/rvm/ RVM_ARCHFLAGS="-arch i386"
# 在其他指令中使用环境变量
ENV TARGET_DIR /opt/app
WORKDIR $TARGET_DIR
ARG 指令用来定义可以在docker build命令运行时传递给构建运行时的变量。在构建时使用 —build-arg 标记。
# 添加ARG指令
ARG build
ARG webapp_user=user
# 使用ARG指令
docker build --build-arg build=1234 -t dafei/webapp .
# 这里构建webapp镜像时,build变量会设置为1234,而webapp_user变量则继承默认值user
Docker 预定义了一组 ARG变量,可以在构建时直接使用
HTTP_PROXY (或 http_proxy)
HTTPS_PROXY (或 https_proxy)
FTP_PROXY (或 ftp_proxy)
NO_PROXY (或 no_proxy)
要使用这些预定义的变量,只要给 docker build 命令传递 —build-arg
USER 指令
USER nginx
该指令使对应容器以nginx用户的身份来运行。除了用户名,我们还可以指定用的UID,组名,GID或者俩者组合
USER user
USER user : group
USER uid
USER uid : gid
USER user : gid
USER uid : group
# docker run -it --name mynginx7 nginx:v7 /bin/bash
nginx@c71e3a4b5f94:/usr/share/nginx/html$ id
uid=1000(nginx) gid=1000(nginx) groups=1000(nginx)
PS:使用USER指令前,要确保用户存在,可以用指令 RUN useradd nginx 添加该用户。否则构建可能没有问题,运行容器时会报错:
docker: Error response from daemon: unable to find user nginx: no matching entries in passwd file.
ERRO[0000] error waiting for container: context canceled
PS:如果不指定用户,默认用户都是root。
LABEL 指令
用于为镜像添加元数据,以键值对形式展现。
LABEL version="1.0"
LABEL location="New York" type="Data Center" role="Web Server"
LABEL tested=true
构建之后使用 docker inspect 命令查看镜像详情可以看到 Labels:
"Labels": {
"location": "New York",
"role": "Web Server",
"tested": "true",
"type": "Data Center",
"version": "1.0"
}
ONBUILD 指令
ONBUILD指令为镜像添加触发器,当镜像被用做其他镜像的基础镜像时,该镜像中的触发器将被执行。
触发器会在构建过程中插入新指令,我们可以认为这些指令是紧跟在FROM之后指定的。
ONBUILD ADD . /app/src
ONBUILD RUN cd /app/src && make
示例:
FROM ubuntu:16.04
MAINTAINER Dafei
RUN apt-get update && apt-get install -y apache2
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ONBUILD ADD . /var/www/
EXPOSE 80
ENTRYPOINT ["/usr/sbin/apache2"]
CMD ["-D", "FOREGROUND"]
docker build -t apache:v1 .
...
Step 7/10 : ONBUILD ADD . /var/www
---> Running in 85019ce138aa
Removing intermediate container 85019ce138aa
---> 454c282c23a7
...
# ONBUILD 指令也可以用 docker inspect 命令来查看
docker inspect apache:v1
...
"OnBuild": [
"ADD . /var/www"
],
...
# 然后我们基于该模板来构建新的镜像:
FROM apache:v1
MAINTAINER Dafei
ENV APPLICATION_NAME webapp
ENV ENVIRONMENT development
# docker build -t "webapp:v1" .
Step 1/4 : FROM apache:v1
# Executing 1 build trigger
---> 2c3284e3ca94
# 步骤1 会执行之前定义的触发器 "ADD . /var/www"
# 通过新镜像来运行容器
docker run --name mywebapp -d webapp:v1
# docker exec -it mywebapp /bin/bash
root@3adfda48cfe4:/# cd /var/www
root@3adfda48cfe4:/var/www# ll
total 28
drwxr-xr-x 1 root root 4096 Apr 12 13:41 ./
drwxr-xr-x 1 root root 4096 Apr 12 13:06 ../
-rw-r--r-- 1 root root 89 Apr 12 13:09 Dockerfile
-rw-r--r-- 1 root root 224 Apr 12 13:36 Dockerfile.gz
drwxr-xr-x 2 root root 4096 Apr 12 13:06 html/
-rw-r--r-- 1 root root 24 Apr 12 12:56 index.htmle
# 进入容器我们发现"ADD . /var/www"指令执行的结果
VOLUME 指令
可以创建一个匿名数据卷挂载点,一般用于存放数据库和需要持久化的数据。
特性:
- 卷可以在容器间共享和重用
- 卷修改不对更新镜像产生影响
- 卷会一直存在到没有任何容器再使用它
示例:
VOLUME ["/data"]
VOLUME ["/opt/project", "/data"]
VOLUME /opt/project /data
这里向 /data 或 /opt/project 中写入的信息都不会记录进入容器的存储层
PS:docker cp 命令是和 VOLUME 指令相关的实用命令,可以从容器负载文件,或复制文件到容器上。
用 docker inspect 命令查看镜像的 VOLUME 指令信息:
# docker inspect nginx:v9
...
"Volumes": {
"/mydata": {},
"/opt/prOject,": {}
},
...
通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,而是自动生成的。
通过镜像运行容器后,我们仍然可以通过 docker inspect 命令来查看卷的挂载信息:
# docker inspect mynginx9
...
"Mounts": [
{
"Type": "volume",
"Name": "bcc2b294f19dace0a20f945ec3d8a94fd821e3d33aacba42bddc22a4bd66a397",
"Source": "/var/lib/docker/volumes/bcc2b294f19dace0a20f945ec3d8a94fd821e3d33aacba42bddc22a4bd66a397/_data",
"Destination": "/opt/project,",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "72ba49a23d9ce5ee1d026a17cfac10831126a7b7788139b8d90e35e2d11907cb",
"Source": "/var/lib/docker/volumes/72ba49a23d9ce5ee1d026a17cfac10831126a7b7788139b8d90e35e2d11907cb/_data",
"Destination": "/mydata",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
...
或:
# docker inspect mynginx9 -f {{.Mounts}}
[{volume 9df48a661984521f1731965eb298ff5d1440677c56a503fdfc0d213852a65a7b /var/lib/docker/volumes/9df48a661984521f1731965eb298ff5d1440677c56a503fdfc0d213852a65a7b/_data /opt/project local true }]
WORKDIR 指令
从镜像创建一个新容器时,WORKDIR指令可以在容器内设置一个工作目录。RUN、ENTRYPOINT 或 CMD 指令指定的程序会在这个目录下执行。
WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT ["rackup"]
# 执行 docker exec/run -it ... /bin/bash 命令时,也将会在 WORKDIR 下执行,
# 不过可以使用 -w 选项在运行时覆盖工作目录:
# docker run -it -w /var/log ubuntu pwd
/var/log
