构建 Docker 镜像有两种方法:
- docker commit (不推荐)
- docker build/Dockerfile
构建全新镜像:
4.5.1 创建 Docker Hub 账号
$ sudo docker login
4.5.2 用 Docker 的 commit 命令创建镜像
将此想象为我们是在往版本控制系统里提交变更.
- 创建容器
$ sudo docker run -it ubuntu /bin/bashroot@8ca8066243c7:/#
- 安装 Apache
root@8ca8066243c7:/# apt-get -yqq updateroot@8ca8066243c7:/# apt-get -y install apache2...
- 提交定制容器
$ sudo docker commit 8ca8066243c7 jamtur01/apache2 # 用户名/仓库名sha256:d90a8d141f2548b63de4b4853536065c16444d2646ba47499784a940a52145b8
得到上次操作的容器的 ID:
$ sudo docker ps -l -q
使用更多信息来提交:
- -a: 作者信息
$ sudo docker commit -m"A new custom image" -a"James Turnbull" 8ca8066243c7 jamtur01/apache2:webserversha256:a6876d7848e3ff5bd1c8138be459b766483be8bffdeb74f22d27ba1f24e65d59
- 查看新创建的镜像
$ sudo docker images jamtur01/apache2REPOSITORY TAG IMAGE ID CREATED SIZEjamtur01/apache2 latest d90a8d141f25 24 minutes ago 221MB
- 查看提交的镜像的详细信息
$ sudo docker inspect jamtur01/apache2:webserver[{"Id": "sha256:a6876d7848e3ff5bd1c8138be459b766483be8bffdeb74f22d27ba1f24e65d59","RepoTags": ["jamtur01/apache2:webserver"],"RepoDigests": [],"Parent": "sha256:4e2eef94cd6b93dd4d794c18b45c763f72edc22858e0da5b6e63a4566a54c03c","Comment": "A new custom image","Created": "2021-01-12T07:52:26.891661295Z","Container": "8ca8066243c70964ebb423d3bf38feb4b2c7d49ada5a938997c63992033983e4","ContainerConfig": {"Hostname": "8ca8066243c7","Domainname": "","User": "","AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Tty": true,"OpenStdin": true,"StdinOnce": true,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd": ["/bin/bash"],"Image": "ubuntu","Volumes": null,"WorkingDir": "","Entrypoint": null,"OnBuild": null,"Labels": {}},"DockerVersion": "20.10.1","Author": "James Turnbull","Config": {"Hostname": "8ca8066243c7","Domainname": "","User": "","AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Tty": true,"OpenStdin": true,"StdinOnce": true,"Env": ["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"Cmd": ["/bin/bash"],"Image": "ubuntu","Volumes": null,"WorkingDir": "","Entrypoint": null,"OnBuild": null,"Labels": {}},"Architecture": "amd64","Os": "linux","Size": 220977084,"VirtualSize": 220977084,"GraphDriver": {"Data": {"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","MergedDir": "/var/lib/docker/overlay2/243bad369b6733aab1f2a48bee099e9c0956401fe5915159ef883906e001cbaa/merged","UpperDir": "/var/lib/docker/overlay2/243bad369b6733aab1f2a48bee099e9c0956401fe5915159ef883906e001cbaa/diff","WorkDir": "/var/lib/docker/overlay2/243bad369b6733aab1f2a48bee099e9c0956401fe5915159ef883906e001cbaa/work"},"Name": "overlay2"},"RootFS": {"Type": "layers","Layers": ["sha256:2ce3c188c38d7ad46d2df5e6af7e7aed846bc3321bdd89706d5262fefd6a3390","sha256:ad44aa179b334bbf4aeb61ecef978c3c77a3bb27cb28bcb727f5566d7f085b31","sha256:35a91a75d24be7ff9c68ce618dcc933f89fef502a59becac8510dbc3bf7a4a05","sha256:a4399aeb9a0e1ddf9da712ef222fd66f707a8c7205ed2607c9c8aac0dbabe882","sha256:5f4cc6e0bb65bbc3829e66afcae3fb13248232b562b6d12896512386e2b4f146"]},"Metadata": {"LastTagTime": "2021-01-12T15:52:27.019057991+08:00"}}]
- 从提交的镜像运行一个新容器
$ sudo docker run -it jamtur01/apache2:webserver /bin/bashroot@7eb750ffe730:/#
4.5.3 用 Dockerfile 构建镜像
Dockerfile 使用 DSL (Domain Specific Language) 语法.
我们的第一个 Dockerfile
- 创建一个示例仓库
$ mkdir static_web # 构建环境 (build environment/context/build context)$ cd static_web$ touch Dockerfile
- 编辑 Dockerfile
- 指令必须大写
- 按顺序执行 (合理安排)
- 每条指令都会创建一个新的镜像层并提交
- 第一个指令必须是 FROM
- RUN 指令会在 shell 里使用命令包装器 /bin/sh -c 来执行
# Version: 0.0.1FROM ubuntu:14.04MAINTAINER James Turnbull "james@example.com"RUN apt-get update && apt-get install -y nginxRUN echo 'Hi, I am in your container' > /usr/share/nginx/html/index.htmlEXPOSE 80
执行流程:

使用 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 模板
利用变更强制更新缓存:

类似功能:

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

-u 可以覆盖.
6. VOLUME
向基于镜像创建的容器添加卷.

VOLUME ["/opt/project"] # 创建挂载点
VOLUME ["/opt/project", "/data"]
- docker cp: 在容器和宿主机之间复制文件
7. ADD
将构建环境下的文件和目录复制到镜像中.
- 不能在构建上下问之外执行 ADD
ADD software.lic /opt/application/software.lic # ->

- 自动创建目的目录
- 新创建的文件和目录的模式为0755, UID 和 GID 是0
- ADD 指令会使构建缓存无效
8. COPY
类似于 ADD, COPY 只关心在构建上下文中复制本地文件, 而不会去做文件提取和解压的工作.
COPY conf.d/ /etc/apache2/
9. LABEL
为 Docker 镜像添加元数据.
- 推荐将所有元数据放到一行

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

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

12. ONBUILD
为镜像添加触发器 (trigger). 当一个镜像被用做其他镜像的基础镜像时, 该镜像中的触发器将会被执行.
- 触发器会在构建过程中插入新指令
- 可以认为这些指令是紧跟在 FROM 之后指定的
- 只能在子镜像中执行, 而不能在孙镜像中执行
ONBUILD ADD . /app/src
ONBUILD RUN cd /app/src && make
