基于Dockerfile构建镜像

1,031 阅读15分钟

基于Dockerfile构建镜像

1. Dockerfile:source code for building Docker file

  • Docker可以通过从Dockerfile文件中读取指令自动构建镜像
  • Dockerfile是一个文本文档,它包含用户可以在命令行上调用的所有命令来组装映像
  • 使用Docker build命令,用户可以通过逐条执行几条命令自动创建镜像

2. Dockerfile语法格式:

  • 首行必须是以#开头的行
  • 不区分大小写,但约定俗成的惯例都是使用全部大写。
  • Docker按顺序在Dockerfile中运行指令
  • 第一个指令必须是“FROM”,以便指定要从其中构建的基本映像

3. 环境变量(使用ENV语句声明)也可以在某些指令中使用,作为由Dockerfile解释的变量。

  • 在Dockerfile中,环境变量可以是variablenamevariable_name或{variable_name}
    • ${variable_name}语法还支持一些标准bash修饰符
    • ${variable:-word}表示,如果设置了变量,那么结果将是该值。如果变量未设置,则结果为word。
    • ${variable:+word}表示如果设置了变量,那么结果将是word,否则结果将是空字符串。

4. .dockerignore文件

  • 在工作目录中若有子目录,而子目录中有些文件不想引用,就可以用.dockerignore来隐藏文件。
  • .dockerignore文件内指明不引用的文件。

4. FROM

  • 用来指定基础镜像,若指定的镜像不存在,会先到Docker Hub中下载
  • 语法:
    • FROM [AS ] #[AS ]:别名
    • FROM [:] [AS ] #tag:标签
    • FROM [@] [AS ] #digest:哈希码

5. docker build命令

  • Build an image from a Dockerfile
  • Options
    • -t, --tag list Name and optionally a tag in the 'name:tag' format
    • -m, --memory bytes Memory limit
    • -c, --cpu-shares int CPU shares (relative weight)

6. LABEL

  • 添加镜像文件的元数据,可出现多次,强烈建议只使用一条,因为一条指令会添加一个层,层数越多,运行效率越低。
  • 语法格式:
    • LABEL = = =... #可指定多个LABEL
    • LABEL #只可指定一个LABEL,第一个空格后的内容都会被当作value

示例 只修改一个镜像的FROM、LABEL

  1. 创建工作目录image
[root@docker ~]# mkdir image
[root@docker ~]# cd image
  1. 编辑Dockerfile文件,添加以下内容:
[root@docker image]# vim Dockerfile
#Test Image Build
FROM alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"
  1. 制作镜像:
[root@docker image]# docker build .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM alpine
 ---> 3fd9065eaf02
Step 2/2 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Running in e1ce9acfc453
Removing intermediate container e1ce9acfc453
 ---> 1deb17a1af32
Successfully built 1deb17a1af32
  1. 查看:REPOSITORY和TAG都为空的即为新创建的镜像文件
[root@docker image]# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
<none>              <none>              1deb17a1af32        5 minutes ago       4.15MB
nginx               latest              cd5239a0906a        3 weeks ago         109MB
busybox             latest              8c811b4aec35        5 weeks ago         1.15MB
httpd               2.4                 fb2f3851a971        8 weeks ago         178MB
alpine              latest              3fd9065eaf02        5 months ago        4.15MB
  • 创建docker镜像时,可使用-t后跟 'name:tag' 指定TAG
  • 也可用如下命令添加标签:
[root@docker image]# docker image tag 1deb17a1af32 alpine:lxk
[root@docker image]# docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED             SIZE
alpine                      lxk                 1deb17a1af32        5 minutes ago       4.41MB

7. COPY

  • 用于从Docker宿主机复制文件至创建的新映像文件
  • 语法:
    • COPY ...
    • COPY ["",... ""]
      • :要复制的源文件或目录,支持使用通配符
      • :目标路径,即正在创建的image的文件系统路径;建议为使用绝对路径,否则,COPY指定则以WORKDIR为其起始路径
      • 注意:在路径中有空白字符时,通常使用第二种格式
  • 文件复制准则:
    • 必须是build上下文中的路径,不能是其父目录中的文件
    • 如果是目录,则其内部文件或子目录会被递归复制,但目录自身不会被复制
    • 如果指定了多个,或在中使用了通配符,则必须是一个目录,且必须以/结尾
    • 如果事先不存在,它将会被自动创建,这包括其父目录路径

示例:复制单个文件

  1. 在image目录下为index.html添加内容:
[root@docker image]#  echo '<h1>hello,docker!</h1>' > index.html
  1. 编辑Dockerfile文件,添加语句:
[root@docker image]# vim Dockerfile 
#Test Image Build
FROM alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

COPY index.html /var/www/html/
  1. 用alpine启动一个容器,查看是否有/var/www/html目录
[root@docker image]# docker run -it --name a1 alpine
/ # ps aux 
PID   USER     TIME   COMMAND
    1 root       0:00 /bin/sh
    7 root       0:00 ps aux
/ # ls /var/www/html
ls: /var/www/html: No such file or directory
  1. 制作镜像
[root@docker image]# docker build -t cpindex:latest .
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM alpine
 ---> 3fd9065eaf02
Step 2/3 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Running in 9d96e0655a82
Removing intermediate container 9d96e0655a82
 ---> 56049399eb78
Step 3/3 : COPY index.html /var/www/html/
 ---> bace8e55c97b
Successfully built bace8e55c97b
Successfully tagged cpindex:latest
  1. 查看制作的镜像:
[root@docker image]# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
cpindex             latest              bace8e55c97b        52 seconds ago      4.15MB
  1. 以制作的镜像启动一个容器,并查看文件是否存在
[root@docker image]# docker run --name copyfile -it --rm cpindex:latest
/ # ls /var/www/html
index.html
/ # cat /var/www/html/index.html
<h1>hello,docker!</h1>

示例:复制目录下的多个文件至目录

  1. 复制一个目录至/root/image下
[root@docker image]# cp -r /etc/default/ ./ 
[root@docker image]# ls
default  Dockerfile  index.html
[root@docker image]# ls default/
grub  kibana  nss  useradd
  1. 修改Dockerfile文件为以下内容:
#Test Image Build
FROM alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

COPY default /tmp/
  1. 制作镜像:
[root@docker image]# docker build -t cpdir:latest ./
Sending build context to Docker daemon  9.216kB
Step 1/3 : FROM alpine
 ---> 3fd9065eaf02
Step 2/3 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 56049399eb78
Step 3/3 : COPY default /tmp/
 ---> 18cacf50aef9
Successfully built 18cacf50aef9
Successfully tagged cpdir:latest
  1. 查看并验证:
[root@docker image]# docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
cpdir               latest              18cacf50aef9        24 seconds ago      4.15MB

[root@docker image]# docker run --name cpdir -it --rm cpdir:latest
WARNING: IPv4 forwarding is disabled. Networking will not work.
/ # ls /tmp
grub     kibana   nss      useradd

示例:使用数组格式创建配置文件

  1. 修改配置文件如下:
#Test Image Build
FROM alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

COPY ["default","/tmp/default"]
  1. 创建镜像并验证
[root@docker image]# docker build -t cp:latest ./
Sending build context to Docker daemon  9.216kB
Step 1/3 : FROM alpine
 ---> 3fd9065eaf02
Step 2/3 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 56049399eb78
Step 3/3 : COPY ["default","/tmp/default"]
 ---> bf0799319943
Successfully built bf0799319943
Successfully tagged cp:latest
[root@docker image]# docker run --name cp -it --rm cp:latest
/ # cd /tmp
/tmp # ls
default
/tmp # cd default/
/tmp/default # ls
grub     kibana   nss      useradd
/tmp/default # exit

8. ADD

  • ADD类似于COPY指令,ADD支持tar文件和URL路径
  • Syntax
    • ADD ...
    • ADD [""...""]
  • 操作准则:
    • 同COPY指令
    • 如果为URL且不以/结尾,则指定的文件将被下载并直接被创建为;如果以/结尾,则文件名URL指定的文件将被直接下载并保存为/
    • 如果是一个本地系统上的压缩格式的tar文件,它将被展开为一个目录,其行为类似于“tar -x”命令;然而,通过URL获取到的tar文件将不会自动展开;
    • 如果有多个,或其间接或直接使用了通配符,则必须是一个以/结尾的目录路径;如果不以/结尾,则其被视作一个普通文件,的内容将被直接写入到

示例:下载一个文件至镜像文件

  1. 在工作目录下编写Dockerfile文件(使用URL时,用ftp协议失败)
#Test Image Build
FROM alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

COPY ["default","/tmp/default"]

ADD https://mirrors.aliyun.com/centos/7.5.1804/os/x86_64/Packages/zsh-5.0.2-28.el7.x86_64.rpm /tmp/
  1. 创建镜像文件
[root@docker image]# docker build -t zsh:latest ./
Sending build context to Docker daemon  9.216kB
Step 1/4 : FROM alpine
 ---> 3fd9065eaf02
Step 2/4 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 56049399eb78
Step 3/4 : COPY ["default","/tmp/default"]
 ---> Using cache
 ---> bf0799319943
Step 4/4 : ADD https://mirrors.aliyun.com/centos/7.5.1804/os/x86_64/Packages/zsh-5.0.2-28.el7.x86_64.rpm /tmp/
Downloading [==================================================>]  2.494MB/2.494MB

 ---> 538ab9c6983e
Successfully built 538ab9c6983e
Successfully tagged zsh:latest
  1. 创建容器并查看
[root@docker image]# docker run -it --name zsh --rm zsh:latest
/ # cd /tmp
/tmp # ls
default                      zsh-5.0.2-28.el7.x86_64.rpm

示例:ADD一个压缩包至镜像文件

  1. 复制压缩包至工作目录,并在工作目录编辑Dockerfile文件
[root@docker image]# cp /root/wordpress-4.8.1-zh_CN.tar.gz ./
[root@docker image]# vim Dockerfile 
#Test Image Build
FROM alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

ADD wordpress-4.8.1-zh_CN.tar.gz /tmp/
  1. 制作镜像文件
[root@docker image]# docker build -t wordpress:latest ./
Sending build context to Docker daemon  8.652MB
Step 1/3 : FROM alpine
 ---> 3fd9065eaf02
Step 2/3 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 56049399eb78
Step 3/3 : ADD wordpress-4.8.1-zh_CN.tar.gz /tmp/
 ---> 58c32caba31e
Successfully built 58c32caba31e
Successfully tagged wordpress:latest
  1. 创建容器并查看
[root@docker image]# docker run --name a1 -it --rm wordpress:latest
/ # ls /tmp/wordpress/
index.php             wp-admin              wp-content            wp-load.php           wp-signup.php
license.txt           wp-blog-header.php    wp-cron.php           wp-login.php          wp-trackback.php
readme.html           wp-comments-post.php  wp-includes           wp-mail.php           xmlrpc.php
wp-activate.php       wp-config-sample.php  wp-links-opml.php     wp-settings.php
/ # exit

9. WORKDIR

  • 用于为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY和ADD指定设定工作目录
  • Syntax
    • WORKDIR
      • 在Dockerfile文件中,WORKDIR指令可出现多次,其路径也可以为相对路径,不过,其是相对此前一个WORKDIR指令指定的路径
      • 另外,WORKDIR也可调用由ENV指定定义的变量
    • 例如:
      • WORKDIR /var/log
      • WORKDIR $STATEPATH

示例

  1. 在工作目录下编辑Dockerfile文件:
#Test Image Build
FROM alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

WORKDIR /tmp
ADD wordpress-4.8.1-zh_CN.tar.gz src
  1. 创建镜像:
[root@docker image]# docker build -t wordpress:v0.1 ./
Sending build context to Docker daemon  8.652MB
Step 1/4 : FROM alpine
 ---> 3fd9065eaf02
Step 2/4 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 56049399eb78
Step 3/4 : WORKDIR /tmp
Removing intermediate container 08bef4630472
 ---> 54f97eda6b49
Step 4/4 : ADD wordpress-4.8.1-zh_CN.tar.gz src
 ---> 0811aff4fa7d
Successfully built 0811aff4fa7d
Successfully tagged wordpress:v0.1
  1. 创建容器后,默认工作路径就是WORKDIR所指的目录/tmp
[root@docker image]# docker run --name a1 -it --rm wordpress:v0.1
/tmp # ls
src
/tmp # ls src/
wordpress
/tmp # ls src/wordpress/
index.php             wp-admin              wp-content            wp-load.php           wp-signup.php
license.txt           wp-blog-header.php    wp-cron.php           wp-login.php          wp-trackback.php
readme.html           wp-comments-post.php  wp-includes           wp-mail.php           xmlrpc.php
wp-activate.php       wp-config-sample.php  wp-links-opml.php     wp-settings.php
/tmp # exit

10. VOLUME

  • 用于在image中创建一个挂载点目录,以挂载Docker host上的卷或其它容器上的卷
  • Syntax
    • VOLUME
    • VOLUME [""]
  • 如果挂载点目录路径下此前有文件存在,docker run命令会把原文件隐藏.
  • 如果此前挂载点不存在,docker 会自动创建该目录.
  • 挂载点下文件变化都可以在宿主机查看
    • 查看方法:
      • docker volume ls #查看宿主机所有挂载的目录
      • docker inspect -f {{.Mounts}} a1 #通过查看指定容器信息查看挂载点路径

示例

  1. 在需要挂载的目录下提供文件
[root@docker image]# echo "hello,test dockerfile" > /var/www/html/index.html
  1. 在工作目录下编辑Dockerfile文件
#Test Image Build
FROM alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

#WORKDIR /tmp
#ADD wordpress-4.8.1-zh_CN.tar.gz src/

VOLUME /var/www/html
  1. 创建镜像文件
[root@docker image]# docker build -t file:v0.1 ./
Sending build context to Docker daemon  8.651MB
Step 1/3 : FROM alpine
 ---> 3fd9065eaf02
Step 2/3 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 56049399eb78
Step 3/3 : VOLUME /var/www/html
 ---> [Warning] IPv4 forwarding is disabled. Networking will not work.
 ---> Running in da84d9f4ca1e
Removing intermediate container da84d9f4ca1e
 ---> b26c2d7ea64c
Successfully built b26c2d7ea64c
Successfully tagged file:v0.1
  1. 创建容器:
[root@docker image]# docker run --name a1 -it --rm file
/ # cd /var/www/html/
/var/www/html # echo abc > index.html
/var/www/html # cat index.html
abc
  1. 查看宿主机上的文件:
[root@docker ~]# docker volume ls       #查看本机所有容器挂载的目录
DRIVER              VOLUME NAME
local               6368d0a0b462f5329a4b3bdcb7030e0d6f724bf9f801386f87fdac7660cd1735
[root@docker ~]# docker inspect -f {{.Mounts}} a1       #查看a1容器的挂载文件路径
[{volume 6368d0a0b462f5329a4b3bdcb7030e0d6f724bf9f801386f87fdac7660cd1735 /var/lib/docker/volumes/6368d0a0b462f5329a4b3bdcb7030e0d6f724bf9f801386f87fdac7660cd1735/_data /var/www/html local  true }]
[root@docker ~]# cd /var/lib/docker/volumes/6368d0a0b462f5329a4b3bdcb7030e0d6f724bf9f801386f87fdac7660cd1735/_data/
[root@docker _data]# ls
index.html
[root@docker _data]# cat index.html 
abc         #该内容与容器中index.html内容一样

11. EXPOSE

  • 用于为容器打开指定要监听的端口以实现与外部通信
  • 实质是通过iptables添加DNAT规则,把外网主机访问宿主机的请求转发至指定容器.
  • 需要宿主机开启核心转发功能.
  • 可通过iptables -t nat -nvL查看添加的规则.
  • 通过Dockerfile制作镜像时若不用EXPOSE指定要暴露的端口,可用以下两种方法暴露端口:
    • 容器运行后,自行添加DNAT规则实现端口暴露.
    • docker run时使用-p选项指定暴露的端口.
    • docker run时使用-P选项暴露所有容器内监听的端口.
  • docker run时,使用-p选项指定的优先级要高于Dockerfile制作镜像时指定要暴露的端口.
  • Syntax
    • EXPOSE [/] [[/] ...]
      • 用于指定传输层协议,可为tcp或udp二者之一,默认为TCP协议
  • EXPOSE指令可一次指定多个端口,例如
    • EXPOSE 11211/udp 11211/tcp

不用EXPOSE时,宿主机内容器若要被外网主机访问的情况

  1. 下载redis:4-alpine
[root@docker ~]# docker pull redis:4-alpine
4-alpine: Pulling from library/redis
ff3a5c916c92: Pull complete 
5fbab8756652: Pull complete 
ff7d4663b06c: Pull complete 
0b5cf71258c2: Pull complete 
54bbb9bad8ba: Pull complete 
8fe9a341d124: Pull complete 
Digest: sha256:686ab026fae07b3b99a8e74210c361714a80311ecc55f23b349ae930ed2f5a95
Status: Downloaded newer image for redis:4-alpine
[root@docker ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
redis               4-alpine            caaeda72bf8f        12 days ago         27.8MB
  1. 运行redis镜像
[root@docker ~]# docker run --name db1 -d --rm -p 6379 redis:4-alpine
881d5648c7388449a39c67024206c5710b1538f4c941039fa3905bb601b09699
[root@docker ~]# docker exec -it db1 ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02  
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:8 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:648 (648.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1 
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

[root@docker ~]# docker exec -it db1 /bin/sh
/data # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 0.0.0.0:6379            0.0.0.0:*               LISTEN      
tcp        0      0 :::6379                 :::*                    LISTEN      
/data # 
  1. 查看映射的端口
[root@docker ~]# docker container port db1
6379/tcp -> 0.0.0.0:32768
  1. 外网主机访问本地宿主机容器
[root@node1 tmp]# redis-cli -h 192.168.1.106 -p 32768
192.168.1.106:32768> select 1
OK
192.168.1.106:32768[1]> set mykey hi
OK
192.168.1.106:32768[1]> keys *
1) "mykey"
192.168.1.106:32768[1]> exit
  1. 查看本地容器内是否有数据
[root@docker ~]# docker exec -it db1 /bin/sh
/data # redis-cli 
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> keys *
1) "mykey"
127.0.0.1:6379[1]> exit
/data # exit

开启自动端口暴露

  1. 在工作目录下编写Dockerfile文件
#Test Image Build
FROM redis:4-alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"
EXPOSE 6379/tcp 26379/tcp
  1. 制作镜像文件
[root@docker images]# docker build -t expose_db:latest ./
Sending build context to Docker daemon  8.645MB
Step 1/3 : FROM redis:4-alpine
 ---> caaeda72bf8f
Step 2/3 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Running in f43f9e43b27a
Removing intermediate container f43f9e43b27a
 ---> e98bb940a8a2
Step 3/3 : EXPOSE 6379/tcp 26379/tcp
 ---> Running in f53a9be4f661
Removing intermediate container f53a9be4f661
 ---> ea40417716a0
Successfully built ea40417716a0
Successfully tagged expose_db:latest
  1. 运行并查看效果
[root@docker images]# docker run --name a1 -d --rm -P redis_expose:latest
ad1225390f8f246cc5bde693ea99b120ee3a2f474416603b0797cda94787cc03
[root@docker images]# docker container port a1
6379/tcp -> 0.0.0.0:32772
  1. 换一台主机连接数据库查看
[root@node1 ~]# redis-cli -h 192.168.200.45 -p 32772
192.168.200.45:32772> select 1
OK
192.168.200.45:32772[1]> keys *
(empty list or set)
192.168.200.45:32772[1]> set test dockerfile
OK
192.168.200.45:32772[1]> keys *
1) "test"
192.168.200.45:32772[1]> get test
"dockerfile"
  1. 在a1容器上查看:
[root@docker images]# docker exec -it a1 /bin/sh
/data # redis-cli 
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> get test
"dockerfile"

示例:验证Dockerfile与docker run时使用-p的优先级

  1. 编辑Dockerfile
[root@docker images]# cat Dockerfile 
#Test Image Build
FROM redis:4-alpine
LABEL maintainer="lixinkuan <lixinkuan@163.com>"
EXPOSE 6379/tcp 80/tcp
  1. 制作镜像:
[root@docker images]# docker build -t expose_port .
Sending build context to Docker daemon  8.645MB
Step 1/3 : FROM redis:4-alpine
 ---> caaeda72bf8f
Step 2/3 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 188775dd2e3e
Step 3/3 : EXPOSE 6379/tcp 80/tcp
 ---> Running in b0f5bfbaafae
Removing intermediate container b0f5bfbaafae
 ---> 165e707c2b23
Successfully built 165e707c2b23
Successfully tagged expose_port:latest
  1. 运行容器时指定要暴露的端口:
[root@docker images]# docker run --name db1 -d --rm -p 25 expose_port
e00f3e304103954c00651d44b00ae9961608900e0d5688eee4c08f140340f480
[root@docker images]# docker container port db1
25/tcp -> 0.0.0.0:32779
  1. 查看防火墙规则,只有暴露25端口的DNAT规则
[root@docker images]# iptables -t nat -nvL
Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0           
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:32779 to:172.17.0.2:25

12. ENV

  • 用于为镜像定义所需的环境变量,并可被Dockerfile文件中位于其后的其它指令(如ENV、ADD、COPY等)所调用
  • 调用格式为variablenamevariable_name或{variable_name}
  • Syntax
    • ENV
    • ENV = ...
  • 第一种格式中,之后的所有内容均会被视作其的组成部分,因此,一次只能设置一个变量
  • 第二种格式可用一次设置多个变量,每个变量为一个"="的键值对,如果中包含空格,可以以反斜线()进行转义,也可通过对加引号进行标识;另外,反斜线也可用于续行
  • 定义多个变量时,建议使用第二种方式,以便在同一层中完成所有功能

示例

  1. 编辑Dockerfile文件
FROM busybox
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

ENV DOCROOT="/data/web/html/"

COPY index.html ${DOCROOT}

VOLUME ${DOCROOT}
  1. 提供index及所需挂载目录:
[root@docker bbox]# mkdir -pv /data/web/html
mkdir: created directory ‘/data’
mkdir: created directory ‘/data/web’
mkdir: created directory ‘/data/web/html’
[root@docker bbox]# echo hello Docker > index.html
[root@docker bbox]# cat index.html
hello Docker
  1. 制作镜像:
[root@docker bbox]# docker build -t bbox_file:latest ./
Sending build context to Docker daemon  3.072kB
Step 1/5 : FROM busybox
latest: Pulling from library/busybox
07a152489297: Pull complete 
Digest: sha256:141c253bc4c3fd0a201d32dc1f493bcf3fff003b6df416dea4f41046e0f37d47
Status: Downloaded newer image for busybox:latest
 ---> 8c811b4aec35
Step 2/5 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Running in 87a1f2c22ad6
Removing intermediate container 87a1f2c22ad6
 ---> 56f723d6220c
Step 3/5 : ENV DOCROOT="/data/web/html/"
 ---> Running in 21fd1fcb0474
Removing intermediate container 21fd1fcb0474
 ---> c095f8dd8418
Step 4/5 : COPY index.html ${DOCROOT}
 ---> ee77cd16629a
Step 5/5 : VOLUME ${DOCROOT}
 ---> Running in 00474fde8b85
Removing intermediate container 00474fde8b85
 ---> d51ea735fdd3
Successfully built d51ea735fdd3
Successfully tagged bbox_file:latest
  1. 运行容器并查看
[root@docker bbox]# docker run --name a1 -it --rm bbox_file:latest
/ # ls /data/web/html
index.html
/ # cat /data/web/html/index.html
hello Docker
  1. 另启终端查看挂载的卷:
[root@docker ~]# docker volume ls
DRIVER              VOLUME NAME
local               1dcd37d2c4f2e6a71e0b96a385714ff01cad5d578e396c9b012922e9993aecbf
local               b2df5fcd0e1aa58c403d2e8f0ec880feb7dcb1a80a688697e76122adec55e789
[root@docker ~]# docker inspect -f {{.Mounts}} a1
[{volume 1dcd37d2c4f2e6a71e0b96a385714ff01cad5d578e396c9b012922e9993aecbf /var/lib/docker/volumes/1dcd37d2c4f2e6a71e0b96a385714ff01cad5d578e396c9b012922e9993aecbf/_data /data/web/html local  true }]

13. CMD与RUN

  1. CMD
  • 用于定义镜像启动为容器时默认运行的应用程序。
  • 类似于RUN指令,CMD指令也可用于运行任何命令或应用程序,不过,二者的运行时间点不同
    • RUN指令运行于映像文件构建过程中,而CMD指令运行于基于Dockerfile构建出的新映像文件启动一个容器时
    • CMD指令的首要目的在于为启动的容器指定默认要运行的程序,且其运行结束后,容器也将终止;不过,CMD指定的命令其可以被docker run的命令行选项所覆盖
    • 在Dockerfile中可以存在多个CMD指令,但仅最后一个会生效
  • Syntax
    • CMD
    • CMD [“”, “”, “”]
    • CMD ["",""]
  • 前两种语法格式的意义同RUN
  • 第三种则用于为ENTRYPOINT指令提供默认参数
  1. RUN
  • 指定docker build过程中运行的程序。必须是镜像中存在的命令。
  • Syntax
    • RUN
    • RUN ["", "", ""]
  • 第一种格式中,通常是一个shell命令,且以“/bin/sh -c”来运行它,这意味着此进程在容器中的PID不为1,不能接收Unix信号,因此,当使用docker stop 命令停止容器时,此进程接收不到SIGTERM信号;
  • 第二种语法格式中的参数是一个JSON格式的数组,其中为要运行的命令,后面的为传递给命令的选项或参数;然而,此种格式指定的命令不会以“/bin/sh -c”来发起,因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行;不过,如果要运行的命令依赖于此shell特性的话,可以将其替换为类似下面的格式。
    • 示例:RUN ["/bin/bash", "-c", "", ""]

示例1:基于centos基础镜像创建一个运行nginx的镜像

  1. 编辑Dockerfile文件:
FROM centos
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

COPY base.repo epel.repo /etc/yum.repos.d/

RUN yum -y install nginx \
        && yum clean all \
        && rm -rf /var/cache/yum
  1. 提供base.repo epel.repo文件:
[root@docker nginx]# wget lixinkuan.top/base.repo
--2018-06-30 11:29:08--  http://lixinkuan.top/base.repo
Resolving lixinkuan.top (lixinkuan.top)... 47.94.102.99
Connecting to lixinkuan.top (lixinkuan.top)|47.94.102.99|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 630
Saving to: ‘base.repo’

100%[=================================================================================>] 630         --.-K/s   in 0s      

2018-06-30 11:29:08 (87.9 MB/s) - ‘base.repo’ saved [630/630]

[root@docker nginx]# wget lixinkuan.top/epel.repo
--2018-06-30 11:29:16--  http://lixinkuan.top/epel.repo
Resolving lixinkuan.top (lixinkuan.top)... 47.94.102.99
Connecting to lixinkuan.top (lixinkuan.top)|47.94.102.99|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 214
Saving to: ‘epel.repo’

100%[=================================================================================>] 214         --.-K/s   in 0s      

2018-06-30 11:29:16 (44.2 MB/s) - ‘epel.repo’ saved [214/214]

[root@docker nginx]# ls
base.repo  Dockerfile  epel.repo
  1. 创建镜像:
[root@docker nginx]# docker build -t nginx:v0.1 ./
Sending build context to Docker daemon  4.608kB
Step 1/4 : FROM centos
latest: Pulling from library/centos
7dc0dca2b151: Pull complete 
Digest: sha256:b67d21dfe609ddacf404589e04631d90a342921e81c40aeaf3391f6717fa5322
Status: Downloaded newer image for centos:latest
 ---> 49f7960eb7e4
Step 2/4 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Running in 6b16128ed7ca
Removing intermediate container 6b16128ed7ca
 ---> b6ef19a3311f
Step 3/4 : COPY base.repo epel.repo /etc/yum.repos.d/
 ---> e571c2837442
Step 4/4 : RUN yum -y install nginx 	&& yum clean all 	&& rm -rf /var/cache/yum
 ---> Running in 445372de8e8d
Loaded plugins: fastestmirror, ovl
.....
执行安装过程省略
...
Cleaning repos: base epel extras updates
Cleaning up everything
Maybe you want: rm -rf /var/cache/yum, to also free up space taken by orphaned data from disabled or removed repos
Cleaning up list of fastest mirrors
Removing intermediate container 445372de8e8d
 ---> 5cf6e8e3517e
Successfully built 5cf6e8e3517e
Successfully tagged nginx:v0.1
  1. 创建容器并查看:
[root@docker nginx]# docker run --name web -it nginx:v0.1
[root@5e7adf4282c1 /]# rpm -q nginx
nginx-1.12.2-2.el7.x86_64           #nginx已安装
[root@5e7adf4282c1 /]# 

示例2:以busybox制作一个挂载本地/data/web/html目录并自动运行httpd的镜像

  1. 在工作目录编辑Dockerfile文件
FROM busybox
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

ENV DOCROOT="/data/web/html/"

COPY index.html ${DOCROOT}

VOLUME ${DOCROOT}

CMD /bin/httpd -f -h ${DOCROOT}
  1. 提供index.html并创建要挂载的目录
[root@docker bbox]# echo hello Docker > index.html
[root@docker bbox]# cat index.html
hello Docker
  1. 创建镜像文件:
[root@docker bbox]# docker build -t web:v0.1 ./
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM busybox
 ---> 8c811b4aec35
Step 2/6 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 56f723d6220c
Step 3/6 : ENV DOCROOT="/data/web/html/"
 ---> Using cache
 ---> c095f8dd8418
Step 4/6 : COPY index.html ${DOCROOT}
 ---> Using cache
 ---> ee77cd16629a
Step 5/6 : VOLUME ${DOCROOT}
 ---> Using cache
 ---> d51ea735fdd3
Step 6/6 : CMD /bin/httpd -f -h ${DOCROOT}
 ---> Running in f2fa2b284306
Removing intermediate container f2fa2b284306
 ---> b8613217ad3c
Successfully built b8613217ad3c
Successfully tagged web:v0.1
  1. 以新创建的镜像文件运行容器并查看
[root@docker bbox]# docker run --name web -d --rm web:v0.1
7b71084ebd922728ebf21d22a4e5ff3462443761c82bc22c640764c6d4925b2a
[root@docker bbox]# docker container inspect -f {{.Config.Cmd}} web
[/bin/sh -c /bin/httpd -f -h ${DOCROOT}]
[root@docker bbox]# docker exec -it web /bin/sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -h /data/web/html/
    7 root      0:00 /bin/sh
   13 root      0:00 ps aux
/ # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 :::80                   :::*                    LISTEN      

示例3:docker run 时不运行镜像默认进程,运行指定指令

  1. 查看当前镜像文件:
[root@docker bbox]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
web                 v0.1                b8613217ad3c        2 hours ago         1.15MB
  1. 以web:v0.1创建容器,不运行默认命令
[root@docker bbox]# docker run --name web -it --rm web:v0.1 /bin/sh
/ # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh
    7 root      0:00 ps aux

14. ENTRYPOINT

  • 类似CMD指令的功能,用于为容器指定默认运行程序,从而使得容器像是一个单独的可执行程序
  • 与CMD不同的是,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指定指定的程序
    • 不过,docker run命令的--entrypoint选项的参数可覆盖ENTRYPOINT指令指定的程序
  • Syntax
    • ENTRYPOINT
    • ENTRYPOINT ["", "", ""]
  • docker run命令传入的命令参数会覆盖CMD指令的内容并且附加到ENTRYPOINT命令最后做为其参数使用
  • Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个会生效

示例1:

  1. 编辑Dockerfile文件
FROM busybox
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

VOLUME /data/web/html/

COPY index.html /data/web/html/

EXPOSE 80/tcp

ENTRYPOINT ["/bin/httpd","-f","-h","/data/web/html"]
  1. 创建镜像
[root@docker bbox]# docker build -t web:v0.2 ./
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM busybox
 ---> 8c811b4aec35
Step 2/6 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 56f723d6220c
Step 3/6 : VOLUME /data/web/html/
 ---> Running in 3095065d0ebb
Removing intermediate container 3095065d0ebb
 ---> 36dc68fabc6f
Step 4/6 : COPY index.html /data/web/html/
 ---> e47f81ec7728
Step 5/6 : EXPOSE 80/tcp
 ---> Running in f86f957ec882
Removing intermediate container f86f957ec882
 ---> 01a005644fe6
Step 6/6 : ENTRYPOINT ["/bin/httpd","-f","-h","/data/web/html"]
 ---> Running in 7a5f8b4f4acf
Removing intermediate container 7a5f8b4f4acf
 ---> 43d514096d34
Successfully built 43d514096d34
Successfully tagged web:v0.2
  1. 创建容器运行并查看:
[root@docker bbox]# docker exec -it web /bin/sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -h /data/web/html
    7 root      0:00 /bin/sh
   13 root      0:00 ps aux
/ # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 :::80                   :::*                    LISTEN      
/ # 
  1. 运行容器时指定执行/bin/sh
[root@docker bbox]# docker run --name web -it --rm web:v0.2 /bin/sh

  1. 换另一tty查看
[root@docker bbox]# docker exec -it web /bin/sh
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/httpd -f -h /data/web/html /bin/sh
    7 root      0:00 /bin/sh
   13 root      0:00 ps aux

并未执行/bin/sh,而是执行默认程序,/bin/sh被当作参数传递给/bin/httpd

示例:在docker run时使用entrypoint的时候更换默认运行的程序

  • --entrypoint string Overwrite the default ENTRYPOINT of the image
  1. 使用镜像web:v0.2创建容器运行时添加--entrypoint选项
[root@docker bbox]# docker run --name web -it --rm --entrypoint /bin/sh web:v0.2
/ # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh
    7 root      0:00 ps aux
/ # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
/ # 

示例

  1. 编辑Dockerfile文件
FROM busybox
LABEL maintainer="lixinkuan <lixinkuan@163.com>"

ENV DOCROOT="/data/web/html/" MYPORT="80"

COPY index.html ${DOCROOT} 
COPY entrypoint.sh /bin/
COPY test.conf /etc/

VOLUME ${DOCROOT}

EXPOSE 80/tcp

#CMD /bin/httpd -f -h ${DOCROOT}

#CMD ["/bin/sh","-c","/bin/httpd","-f","-h","${DOCROOT}"]
ENTRYPOINT ["/bin/entrypoint.sh"]

CMD ["/bin/httpd","-f","-h","/data/web/html/"]
  1. 提供必须文件(脚本文件需加执行权限)
[root@docker bbox]# cat entrypoint.sh 
#!/bin/sh

sed -i "s@^PORT=.*@PORT=${MYPORT}@g" /etc/test.conf
exec "$@"
[root@docker bbox]# cat test.conf 
PORT=8080
  1. 制作镜像:
[root@docker bbox]# docker build -t web:v0.3 ./
Sending build context to Docker daemon   5.12kB
Step 1/10 : FROM busybox
 ---> 8c811b4aec35
Step 2/10 : LABEL maintainer="lixinkuan <lixinkuan@163.com>"
 ---> Using cache
 ---> 56f723d6220c
Step 3/10 : ENV DOCROOT="/data/web/html/" MYPORT="80"
 ---> Running in f237100ec645
Removing intermediate container f237100ec645
 ---> f754b5dcea84
Step 4/10 : COPY index.html ${DOCROOT}
 ---> 3c31424c9b3d
Step 5/10 : COPY entrypoint.sh /bin/
 ---> 46ec2f5ede8c
Step 6/10 : COPY test.conf /etc/
 ---> 7db53e00338a
Step 7/10 : VOLUME ${DOCROOT}
 ---> Running in 5ae02469f585
Removing intermediate container 5ae02469f585
 ---> 0e1e3e966318
Step 8/10 : EXPOSE 80/tcp
 ---> Running in ae76bcf870ca
Removing intermediate container ae76bcf870ca
 ---> dea89896460d
Step 9/10 : ENTRYPOINT ["/bin/entrypoint.sh"]
 ---> Running in 6862bf4a336e
Removing intermediate container 6862bf4a336e
 ---> ca568e1ff983
Step 10/10 : CMD ["/bin/httpd","-f","-h","/data/web/html/"]
 ---> Running in 2aa5dea11848
Removing intermediate container 2aa5dea11848
 ---> 26bb44795880
Successfully built 26bb44795880
Successfully tagged web:v0.3
  1. 运行容器并查看配置文件是否被修改
[root@docker bbox]# docker run --name web -d --rm web:v0.3
6ec1f5a008e6a08047e8666f6ed3ad4673360805148789faf780baf335ee5637
[root@docker bbox]# docker exec -it web /bin/sh
/ # netstat -tnl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
tcp        0      0 :::80                   :::*                    LISTEN      
/ # cat /etc/test.conf
PORT=80
/ # exit

示例:通过传递变量更改配置文件的方法:

[root@docker bbox]# docker run --name web1 -d --rm -e MYPORT=10080 web:v0.3
7e3b353e423839d598ee9423e881673066cf99626940b6590e78f34b7622834d
[root@docker bbox]# docker exec -it web1 /bin/sh
/ # cat /etc/test.conf 
PORT=10080
/ #