构建 Docker 镜像有两种方法:

  • docker commit (不推荐)
  • docker build/Dockerfile

构建全新镜像:

4.5.1 创建 Docker Hub 账号

  1. $ sudo docker login

4.5.2 用 Docker 的 commit 命令创建镜像

将此想象为我们是在往版本控制系统里提交变更.

  1. 创建容器
  1. $ sudo docker run -it ubuntu /bin/bash
  2. root@8ca8066243c7:/#
  1. 安装 Apache
  1. root@8ca8066243c7:/# apt-get -yqq update
  2. root@8ca8066243c7:/# apt-get -y install apache2
  3. ...
  1. 提交定制容器
  1. $ sudo docker commit 8ca8066243c7 jamtur01/apache2 # 用户名/仓库名
  2. sha256:d90a8d141f2548b63de4b4853536065c16444d2646ba47499784a940a52145b8

得到上次操作的容器的 ID:

  1. $ sudo docker ps -l -q

使用更多信息来提交:

  • -a: 作者信息
  1. $ sudo docker commit -m"A new custom image" -a"James Turnbull" 8ca8066243c7 jamtur01/apache2:webserver
  2. sha256:a6876d7848e3ff5bd1c8138be459b766483be8bffdeb74f22d27ba1f24e65d59
  1. 查看新创建的镜像
  1. $ sudo docker images jamtur01/apache2
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. jamtur01/apache2 latest d90a8d141f25 24 minutes ago 221MB
  1. 查看提交的镜像的详细信息
  1. $ sudo docker inspect jamtur01/apache2:webserver
  2. [
  3. {
  4. "Id": "sha256:a6876d7848e3ff5bd1c8138be459b766483be8bffdeb74f22d27ba1f24e65d59",
  5. "RepoTags": [
  6. "jamtur01/apache2:webserver"
  7. ],
  8. "RepoDigests": [],
  9. "Parent": "sha256:4e2eef94cd6b93dd4d794c18b45c763f72edc22858e0da5b6e63a4566a54c03c",
  10. "Comment": "A new custom image",
  11. "Created": "2021-01-12T07:52:26.891661295Z",
  12. "Container": "8ca8066243c70964ebb423d3bf38feb4b2c7d49ada5a938997c63992033983e4",
  13. "ContainerConfig": {
  14. "Hostname": "8ca8066243c7",
  15. "Domainname": "",
  16. "User": "",
  17. "AttachStdin": true,
  18. "AttachStdout": true,
  19. "AttachStderr": true,
  20. "Tty": true,
  21. "OpenStdin": true,
  22. "StdinOnce": true,
  23. "Env": [
  24. "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
  25. ],
  26. "Cmd": [
  27. "/bin/bash"
  28. ],
  29. "Image": "ubuntu",
  30. "Volumes": null,
  31. "WorkingDir": "",
  32. "Entrypoint": null,
  33. "OnBuild": null,
  34. "Labels": {}
  35. },
  36. "DockerVersion": "20.10.1",
  37. "Author": "James Turnbull",
  38. "Config": {
  39. "Hostname": "8ca8066243c7",
  40. "Domainname": "",
  41. "User": "",
  42. "AttachStdin": true,
  43. "AttachStdout": true,
  44. "AttachStderr": true,
  45. "Tty": true,
  46. "OpenStdin": true,
  47. "StdinOnce": true,
  48. "Env": [
  49. "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
  50. ],
  51. "Cmd": [
  52. "/bin/bash"
  53. ],
  54. "Image": "ubuntu",
  55. "Volumes": null,
  56. "WorkingDir": "",
  57. "Entrypoint": null,
  58. "OnBuild": null,
  59. "Labels": {}
  60. },
  61. "Architecture": "amd64",
  62. "Os": "linux",
  63. "Size": 220977084,
  64. "VirtualSize": 220977084,
  65. "GraphDriver": {
  66. "Data": {
  67. "LowerDir": "/var/lib/docker/overlay2/ac744ee943866cd0be76ee79f366fd892afdc4088ddf37a77453dc3336e9aaf5/diff:/var/lib/docker/overlay2/3d9270cbd9affd9d7802c5ea3064aaf94f65698dfdc9d1e78b265d7ab5886334/diff:/var/lib/docker/overlay2/2e4be4e4aa62ab5dede3ed77261a6a2d9490df614a3fb748f448f38028d27e7e/diff:/var/lib/docker/overlay2/78ac84782a0c925d8e62ae32ab677268ea7055e81844805bee9974a2486b3039/diff",
  68. "MergedDir": "/var/lib/docker/overlay2/243bad369b6733aab1f2a48bee099e9c0956401fe5915159ef883906e001cbaa/merged",
  69. "UpperDir": "/var/lib/docker/overlay2/243bad369b6733aab1f2a48bee099e9c0956401fe5915159ef883906e001cbaa/diff",
  70. "WorkDir": "/var/lib/docker/overlay2/243bad369b6733aab1f2a48bee099e9c0956401fe5915159ef883906e001cbaa/work"
  71. },
  72. "Name": "overlay2"
  73. },
  74. "RootFS": {
  75. "Type": "layers",
  76. "Layers": [
  77. "sha256:2ce3c188c38d7ad46d2df5e6af7e7aed846bc3321bdd89706d5262fefd6a3390",
  78. "sha256:ad44aa179b334bbf4aeb61ecef978c3c77a3bb27cb28bcb727f5566d7f085b31",
  79. "sha256:35a91a75d24be7ff9c68ce618dcc933f89fef502a59becac8510dbc3bf7a4a05",
  80. "sha256:a4399aeb9a0e1ddf9da712ef222fd66f707a8c7205ed2607c9c8aac0dbabe882",
  81. "sha256:5f4cc6e0bb65bbc3829e66afcae3fb13248232b562b6d12896512386e2b4f146"
  82. ]
  83. },
  84. "Metadata": {
  85. "LastTagTime": "2021-01-12T15:52:27.019057991+08:00"
  86. }
  87. }
  88. ]
  1. 从提交的镜像运行一个新容器
  1. $ sudo docker run -it jamtur01/apache2:webserver /bin/bash
  2. root@7eb750ffe730:/#

4.5.3 用 Dockerfile 构建镜像

Dockerfile 使用 DSL (Domain Specific Language) 语法.

我们的第一个 Dockerfile

  1. 创建一个示例仓库
  1. $ mkdir static_web # 构建环境 (build environment/context/build context)
  2. $ cd static_web
  3. $ touch Dockerfile
  1. 编辑 Dockerfile
    1. 指令必须大写
    2. 按顺序执行 (合理安排)
    3. 每条指令都会创建一个新的镜像层并提交
    4. 第一个指令必须是 FROM
    5. RUN 指令会在 shell 里使用命令包装器 /bin/sh -c 来执行
  1. # Version: 0.0.1
  2. FROM ubuntu:14.04
  3. MAINTAINER James Turnbull "james@example.com"
  4. RUN apt-get update && apt-get install -y nginx
  5. RUN echo 'Hi, I am in your container' > /usr/share/nginx/html/index.html
  6. EXPOSE 80

执行流程:

image.png

使用 exec 格式的 RUN 指令:

RUN [ "apt-get", "install", "-y", "nginx" ]

4.5.4 基于 Dockerfile 构建新镜像

$ sudo docker build -t="jamtur01/static_web" .
[sudo] jdxj 的密码:
Sending build context to Docker daemon  2.048kB
Step 1/5 : FROM ubuntu:14.04
...
Step 2/5 : MAINTAINER James Turnbull "james@example.com"
...
Step 3/5 : RUN apt-get update && apt-get install -y nginx
...
Step 4/5 : RUN echo 'Hi, I am in your container' > /usr/share/nginx/html/index.html
...
Step 5/5 : EXPOSE 80
...
Successfully built dec78992551e
Successfully tagged jamtur01/static_web:latest

可以指定标签:

$ sudo docker build -t="jamtur01/static_web:v1" .

指定 Git 仓库中的 Dockerfile:

$ sudo docker build -t="jamtur01/static_web:v1" git@github.com:jamtur01/docker-static_web

忽略文件:

.dockerignore

4.5.5 指令失败时会怎样

停止构建:

  • 在当前阶段运行失败的命令用于调试

4.5.6 Dockerfile 和构建缓存

缓存中间构建以便未来使用.

禁用缓存:

$ sudo docker build --no-cache -t="jamtur01/static_web" .

4.5.7 基于构建缓存的 Dockerfile 模板

利用变更强制更新缓存:

image.png

类似功能:

image.png

4.5.8 查看新镜像

$ sudo docker images jamtur01/static_web
[sudo] jdxj 的密码:
REPOSITORY            TAG       IMAGE ID       CREATED       SIZE
jamtur01/static_web   latest    dec78992551e   2 hours ago   231MB

查看镜像如何构建:

$ sudo docker history dec78992551e
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
dec78992551e   2 hours ago     /bin/sh -c #(nop)  EXPOSE 80                    0B        
1729687672ab   2 hours ago     /bin/sh -c echo 'Hi, I am in your container'…   27B       
c24e4dbf5422   2 hours ago     /bin/sh -c apt-get update && apt-get install…   34.9MB    
df46067add65   2 hours ago     /bin/sh -c #(nop)  MAINTAINER James Turnbull…   0B        
df043b4f0cf1   3 months ago    /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
<missing>      3 months ago    /bin/sh -c mkdir -p /run/systemd && echo 'do…   7B        
<missing>      3 months ago    /bin/sh -c [ -z "$(apt-get indextargets)" ]     0B        
<missing>      3 months ago    /bin/sh -c set -xe   && echo '#!/bin/sh' > /…   195kB     
<missing>      13 months ago   /bin/sh -c #(nop) ADD file:276b5d943a4d284f8…   196MB

4.5.9 从新镜像启动容器

  • -p: 在宿主机上随机挑一个端口映射到容器的80端口
$ sudo docker run -d -p 80 --name static_web jamtur01/static_web nginx -g "daemon off;"
6c9ebe08081502df2ce770446128b6accf19a08a9c8845a035891c27a3ea70f0

查看端口分配:

$ sudo docker ps -l
CONTAINER ID   IMAGE                 COMMAND                  CREATED              STATUS              PORTS                   NAMES
6c9ebe080815   jamtur01/static_web   "nginx -g 'daemon of…"   About a minute ago   Up About a minute   0.0.0.0:49153->80/tcp   static_web
$ sudo docker port 6c9ebe080815 80
0.0.0.0:49153

指定端口映射:

$ sudo docker run -d -p 80:80 --name static_web jamtur01/static_web nginx -g "daemon off;"

指定网络接口 (ip):

$ sudo docker run -d -p 127.0.0.1:80:80 --name static_web jamtur01/static_web nginx -g "daemon off;"

指定网络接口但随机端口:

$ sudo docker run -d -p 127.0.0.1::80 --name static_web jamtur01/static_web nginx -g "daemon off;"
  • 可以使用 /udp 后缀使用 UDP 端口

-P 参数可以公开在 Dockerfile 中通过 EXPOSE 指令公开的所有端口:

  • 随机绑定
$ sudo docker run -d -P --name static_web jamtur01/static_web nginx -g "daemon off;"

4.5.10 Dockerfile 指令

指令清单:

1. CMD

指定一个容器启动时要运行的命令.

  • RUN: 指定镜像被构建时要运行的命令
  • CMD 类似 docker run 后面运行命令
$ sudo docker run -i -t jamtur01/static_web /bin/true
# 等效于 Dockerfile 中
CMD ["/bin/true"]

指定参数:

  • 推荐使用数组形式
CMD ["/bin/bash", "-l"]

注意:

  • docker run 后的命令会覆盖 CMD
  • Dockerfile 中只有最后一个 CMD 生效

2. ENTRYPOINT

docker run 中指定的任何参数会被传递给 ENTRYPOINT 指定的命令.

ENTRYPOINT ["/usr/sbin/nginx"]

# 添加参数
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]

配合使用:

  • 如果 docker run 没有指定任何参数, 那么 -h 会传递给 nginx
  • 原理: docker run 会覆盖 CMD, 反而将参数传递给 ENTRYPOINT
ENTRYPOINT ["/usr/sbin/nginx"]
CMD ["-h"]

3. WORKDIR

设置容器内的工作目录, ENTRYPOINT/CMD 指定的命令在该目录中执行.

WORKDIR /opt/webapp/db
RUN bundle install
WORKDIR /opt/webapp
ENTRYPOINT [ "rackup" ]

运行时覆盖工作目录:

$ sudo docker run -ti -w /var/log ubuntu pwd
/var/log

4. ENV

在构建时使用:

  • 也可以在该镜像的容器中使用
# 格式1
ENV RVM_PATH /home/rvm # 一个
# 格式2
ENV RVM_PATH=/home/rvm RVM_ARCHFLAGS="-arch i386" # 多个

ENV TARGET_DIR /opt/app
WORKDIR $TARGET_DIR
  • -e
$ sudo docker run -ti -e "WEB_PORT=8080" ubuntu env

5. USER

指定该镜像会以什么用户去运行.

USER nginx

image.png

-u 可以覆盖.

6. VOLUME

向基于镜像创建的容器添加卷.

image.png

VOLUME ["/opt/project"] # 创建挂载点
VOLUME ["/opt/project", "/data"]
  • docker cp: 在容器和宿主机之间复制文件

7. ADD

将构建环境下的文件和目录复制到镜像中.

  • 不能在构建上下问之外执行 ADD
ADD software.lic /opt/application/software.lic # ->

image.png

  • 自动创建目的目录
  • 新创建的文件和目录的模式为0755, UID 和 GID 是0
  • ADD 指令会使构建缓存无效

8. COPY

类似于 ADD, COPY 只关心在构建上下文中复制本地文件, 而不会去做文件提取和解压的工作.

COPY conf.d/ /etc/apache2/

9. LABEL

为 Docker 镜像添加元数据.

  • 推荐将所有元数据放到一行

image.png

10. STOPSIGNAL

设置停止容器时发送什么系统调用信号给容器.

11. ARG

image.png

ARG build
ARG webapp_user=user
$ docker build --build-arg build=1234 -t jamtur01/webapp .

image.png

12. ONBUILD

为镜像添加触发器 (trigger). 当一个镜像被用做其他镜像的基础镜像时, 该镜像中的触发器将会被执行.

  • 触发器会在构建过程中插入新指令
  • 可以认为这些指令是紧跟在 FROM 之后指定的
  • 只能在子镜像中执行, 而不能在孙镜像中执行
ONBUILD ADD . /app/src
ONBUILD RUN cd /app/src && make