学习总结来自 Gitbook Docker 从入门到实践的总结;
现在容器的工具太多,国内阿里,华为等都有自己的容器产品,说到容器大多数想到或者用到的是 Docker,所以就这样来学习吧。主要学习方向参考上面的书。
这篇书讲得很全,作为开发人员可以刚开始不用全部都学习完,只是把基础的内容学习一遍,加上尝试实践,和中间学习过程中感觉到理解困难的学习点记录了下,最主要还是注重基础,有 shell 脚本基础学习起来时要轻松很多,重点是理解原理,有条件加以实战,才知道原来是这样啊,也能发现发现很多问题。
为什么要使用 Docker
什么是 Docker
Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。
传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
为什么要用 Docker
- 更高效的利用系统资源
- 更高效的利用系统资源:直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。
- 一致的运行环境
- 持续交付和部署,一次创建或配置,可以在任意地方正常运行。
- 更轻松的迁移,可以多平台运行。
- 更轻松的维护和扩展,可以自定义镜像。
了解 Docker 的基本概念
镜像
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
分层存储
Docker 设计时,充分利用 Union FS 的技术,将其设计为分层存储的架构。镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。
容器
容器是镜像运行时的实体,只是是以镜像为基础层,在其上创建一个当前容器的存储层。容器可以被创建、启动、停止、删除、暂停等。
基本使用
安装
参考 安装 Docker
镜像加速
国内使用加速:
1 | { |
重启:
1 | $ systemctl daemon-reload |
镜像
查找镜像语法
1 | $ docker search [OPTIONS] TERM |
OPTIONS说明:
–automated :只列出 automated build类型的镜像;
–no-trunc :显示完整的镜像描述;
-s :列出收藏数不小于指定值的镜像。
我们首先想使用某个镜像,就可以去 DockerHub 下载,首先去查找镜像,例如我想使用 nginx 镜像并且收藏数大于100:
1 | $ docker search nginx -s 100 |
获取镜像
从 Docker 的镜像仓库中拖取镜像 docker pull:
1 | $ docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] |
获取 nginx 镜像
1 | $ docker pull nginx:latest |
列出镜像
要想列出已经下载下来的镜像,可以使用 docker image ls 命令,或者 docker images,它会列出所有的镜像。
列表包含了 仓库名、标签、镜像 ID(唯一标识)、创建时间 以及 所占用的空间。
当然命令后面可以接条件,找出符合条件的镜像。
1 | $ docker images [OPTIONS] [REPOSITORY[:TAG]] |
OPTIONS说明:
- -a :列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层);
- –digests :显示镜像的摘要信息;
- -f :显示满足条件的镜像;
- –format :指定返回值的模板文件;
- –no-trunc :显示完整的镜像信息;
- -q :只显示镜像ID。
合理的格式获取镜像的参数,可以方便在脚本中的使用。
查看 nginx 镜像的 ID:
1 | $ docker image ls nginx:latest -q |
删除镜像
语法:
1 | $ docker image rm [选项] <镜像1> [<镜像2> ...] |
使用 ID 删除镜像:上面我们查看到 nginx 的 id 很长,但是实际上只要使用前三位以后能够确定到哪个镜像就行了 :
1
$ docker image rm c5c
使用镜像名删除镜像:也就是
<仓库名>:<标签>,来删除镜像。1
$ docker image rm nginx:latest
使用镜像摘要删除镜像:
1
2
3
4
5
6
7
$ docker image ls nginx --digests
REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
nginx latest sha256:e36d7f5dabf1429d84135bb8a8086908e1150f1a178c75719a9e0e53ebb90353 c5c4e8fa2cf7 6 days ago 109MB
$ docker image rm nginx@sha256:e36d7f5dabf1429d84135bb8a8086908e1150f1a178c75719a9e0e53ebb90353
Untagged: nginx@sha256:e36d7f5dabf1429d84135bb8a8086908e1150f1a178c75719a9e0e53ebb90353
现在我们就可以使用查询命令来配合使用删除:
1 | $ docker image rm $(docker image ls nginx -q) |
tag
给一个镜像打上指定的标签(它们拥有一样的 IMAGE ID,可以试一试):
1 | $ docker tag nginx:latest kronchan/nginx:v1.0 |
但是多个镜像有相同 ID 不能使用 ID 一次性删除:Error response from daemon: conflict: unable to delete c5c4e8fa2cf7 (must be forced) - image is referenced in multiple repositories
使用 Dockerfile 定制镜像
Dockerfile是由一系列命令和参数构成的脚本,一个Dockerfile里面包含了构建整个image的完整命令。Docker通过docker build执行Dockerfile中的一系列命令自动构建image。
FROM
所谓定制镜像,那一定是以一个镜像为基础,比如我们需要构建一个 java 项目,就必须要在 java 环境的基础上进行构建,其中基础镜像是必须指定的,FROM 就是指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。
1 | FROM <image>[:tag] |
RUN
RUN 指令是用来执行命令行命令的。由于命令行的强大能力,RUN 指令在定制镜像时是最常用的指令之一。其格式有两种:
- shell 格式:
RUN <命令>,直接追加 shell 命令行; - exec 格式:
RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。
比如给 nginx 制定欢迎页的 Dockerfile:
1 | FROM nginx |
在文件所在目录执行命令:
1 | $ docker build -t nginx:v1.0 . |
注意后面的点不能少,点 . 表示使用当前目录作为上下文环境将文件进行打包,上传到 Docker 引擎服务器,进行构建镜像。
COPY
格式:
COPY <源路径>... <目标路径>COPY ["<源路径1>",... "<目标路径>"]
和 RUN 指令一样,也有两种格式,一种类似于命令行,一种类似于函数调用。
COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。比如:
1 | COPY package.json /usr/src/app/ |
<源路径> 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 filepath.Match 规则,如:
1 | COPY hom* /mydir/ |
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)。目标路径不需要事先创建,如果目录不存在会在复制文件前先行创建缺失目录。
此外,还需要注意一点,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。这个特性对于镜像定制很有用。特别是构建相关文件都在使用 Git 进行管理的时候。
ADD
一般认为拥有和 COPY 一样的功能,但是多了一个自动解压缩的功能。
CMD
容器启动命令,其格式有两种:
- shell 格式:
RUN <命令>,直接追加 shell 命令行; - exec 格式:
RUN ["可执行文件", "参数1", "参数2"],这更像是函数调用中的格式。
在启动容器的时候,可以使用下面命令覆盖CMD 缺省值:
1 | $ sudo docker run [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...] |
ENTRYPOINT
它指定了当container执行时,需要启动哪些进程。
两种形式:
- ENTRYPOINT [“executable”, “param1”, “param2”] (exec 形式, 首选)
- ENTRYPOINT command param1 param2 (shell 形式)
shell 形式防止使用任何CMD或运行命令行参数,但是缺点是您的ENTRYPOINT将作/bin/sh -c的子命令启动,它不传递信号。这意味着可执行文件将不是容器的PID 1,并且不会接收Unix信号,因此您的可执行文件将不会从docker stop <container>接收到SIGTERM。
只有Dockerfile中最后一个ENTRYPOINT指令会有效果。
docker run <image>的命令行参数将附跟在 exec 形式的ENTRYPOINT中的所有元素之后,并将覆盖使用CMD指定的所有元素。这允许将参数传递到入口点,即docker run <image> -d将把-d参数传递给入口点。
入口点,和 CMD 一起使用比较好,
例子:
编写一个 Dockerfile:
1 | FROM ubuntu |
构建镜像,分别运行两个容器:
$ docker run -d --name test1 test/ubuntu:v1.0$ docker run -d --name test2 test/ubuntu:v1.0 -H
1 | $ docker container ls |
可以检查容器:
1 | $ docker exec -it test2 ps aux |
启动容器的时候可以使用 --entrypoint="": Overwrite the default entrypoint set by the image,覆盖缺省的值。
TODO ,这方面还需要再了解下。
ENV
格式有两种:
ENV <key> <value>ENV <key1>=<value1> <key2>=<value2>...
设置环境变量,后面可以直接调用:
1 | ENV VERSION=1.0 |
-e参数:在启动容器 的时候使用 -e VERSION=2.0 可以覆盖值。
在容器启动的时候,会缺省创建下面的变量:
| Variable | Value |
|---|---|
HOME | Set based on the value of USER |
HOSTNAME | The hostname associated with the container |
PATH | Includes popular directories, such as :/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin |
TERM | xterm if the container is allocated a psuedo-TTY |
ARG
构建参数,格式:ARG <参数名>[=<默认值>]
Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。
VOLUME
格式为:
VOLUME ["<路径1>", "<路径2>"...]VOLUME <路径>
一般上,容器是不保存任何文件的,因为容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
我们将这些动态数据挂载到主机中,就可以使用挂载卷保存数据。也可以在运行的时候覆盖挂载卷的参数:
1 | $ docker run -dit -v mydata:/data image |
mydata 为 宿主中的挂载卷,将会挂载到 docker 容器中的 data 这个文件夹中,而且会覆盖 Dockerfile 中设置的匿名挂载卷。
EXPOSE
格式为 EXPOSE <端口1> [<端口2>...]。
这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P时,会自动随机映射 EXPOSE 的端口。
运行时使用命令:
1 | $ docker run d -p 8001:8000 IMAGE |
其中 docker 容器的 8000 端口映射到宿主 8000 端口。
使用 -P 将内部容器所有开放的端口随机分发,你可以使用docker port来查找这个随机绑定端口。
1 | --expose=[]: Expose a port or a range of ports from the container |
WORKDIR
区别于 shell 中的 cd的切换命令:
1 | RUN cd /app |
由于 Dockerfile 中每个 RUN 都是开启一个容器,所以第二行的命令重启一个容器又是一个新的环境,把那个不知道你切换了文件目录,它还是在原来的位置。如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令。
启动容器的时候可以使用 -w覆盖默认缺省值。
USER
格式:USER <用户名 or UID>
USER 改变后面构建层使用命令的身份,这个用户必须是事先建立好的,否则无法切换。
启动容器的时候可以使用-u覆盖缺省值。
MAINTAINER
格式:MAINTAINER [name]
MAINTAINER指令允许您设置生成的images的作者字段。
ONBUILD
// todo
使用
docker build命令从Dockerfile和context构建image。context是PATH或URL处的文件。PATH本地文件目录。 URL是Git repository的位置。
context以递归方式处理。因此,PATH包括任何子目录,URL包括repository及submodules。一个使用当前目录作为context的简单构建命令:
1 | $ docker build . |
构建由Docker守护程序运行,而不是由CLI运行。构建过程所做的第一件事是将整个context(递归地)发送给守护进程。大多数情况下,最好是将Dockerfile和所需文件复制到一个空的目录,再到这个目录进行构建。
警告:不要使用根目录/作为PATH,因为它会导致构建将硬盘驱动器的所有内容传输到Docker守护程序。
可以使用.dockerignore文件添加到context目录中来排除文件和目录。
一般的,Dockerfile位于context的根中。但使用-f标志可指定Dockerfile的位置。
1 | $ docker build -f /path/to/a/Dockerfile . |
如果build成功,您可以指定要保存新image的repository和tag:
1 | $ docker build -t shykes/myapp . |
要在构建后将image标记为多个repositories,请在运行构建命令时添加多个-t参数:
1 | $ docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest . |
Docker守护程序一个接一个地运行Dockerfile中的指令,如果需要,将每个指令的结果提交到一个新image,最后输出新映像的ID。Docker守护进程将自动清理您发送的context。
请注意,每个指令独立运行,并导致创建一个新image - 因此RUN cd /tmp对下一个指令不会有任何影响。
只要有可能,Docker将重新使用中间images(缓存),就是以前构建镜像使用过的指令再次重复使用,加速docker build过程。
实践
写一个简单的 Dockerfile:
1 | # my demo |
开始构建:
1 | $ ls |
突然发现,ADD 指令好像也不能像前面或者网络上前辈们介绍的说自动解压文件,暂时记录,能力有限,也不能说明白,记录下以后不要踩坑,复制文件还是使用 COPY 语义更标准。
容器
启动
语法: docker run
启动 nginx
1 | $ docker run -dit nginx |
-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上,
-i 则让容器的标准输入保持打开。
-d 守护状态运行。
当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个 ip 地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
可以利用 docker container start [container ID or NAMES] 命令,直接将一个已经终止的容器启动运行。
查看守护状态运行的容器的输出信息:
1 | $ docker logs --help |
终止容器
可以使用 docker container stop [container ID or NAMES] 来终止一个运行中的容器。
进入容器
exec 命令
一般使用 -i,-t参数后就可以有终端和提示符。
删除容器
- 可以使用
docker container rm [container ID or NAMES]来删除一个处于终止状态的容器。 - 如果需要删除运行状态中的容器,加上参数
-f强制删除; $ docker container prune清理所有处于终止状态的容器。
导出和导入
导出容器:
1
$ docker export 180f > /root/springboot-docker.tar
导入容器快照:
1
$ docker import - kronchan/springboot-docker:v1.0
还可以导入 url 中的文件作为镜像。
Docker Hub
推送镜像
目前 Docker 官方维护了一个公共仓库 Docker Hub,但是国内推送的速度在没有翻墙的情况下比较尴尬,所以可以使用 阿里云镜像服务。
登陆 :
1
$ docker login registry.cn-hangzhou.aliyuncs.com
然后输入账号密码。
标记 TAG(可选):
1
$ docker tag [ImageId] com.wuwii/<image>[:镜像版本号]
推送:
1
$ docker push [image]
提交构建文件到仓库
只需要将构建镜像的 Dockerfile 和其余相关的文件一同 push 到代码托管仓库,再次 push 下来就能重新构建镜像。
数据管理
数据卷
数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
数据卷可以在容器之间共享和重用- 对
数据卷的修改会立马生效 - 对
数据卷的更新,不会影响镜像 数据卷默认会一直存在,即使容器被删除
Command:
1 | Usage: docker volume COMMAND |
容器挂载数据卷
例如我启动一个 jenkins 容器,命名为 my_jenkins,将容器的 var/jenkins_home/目录挂载到数据卷 my_jenkins:
1 | $ docker volume create my_jenkins_volume |
-v my_jenkins_volume:/var/jenkins_home/可以理解是--mount source=my_jenkins_volume,target=/var/jenkins_home/,后面一种更好理解,也是推荐使用的。
查询容器信息:
1 | $ docker inspect my_jenkins |
挂载主机文件作为数据卷
不光可以将数据卷挂载到容器中,我们还可以直接将宿主的文件直接作为数据卷使用,完成一样的效果。
上面的,我将宿主的文件夹 /var/my_jenkins,作为数据卷挂载到 /var/my_jenkins:
1 | $ docker run -p 7322:8080 -p 50000:50000 -v /var/jenkins_home/:/var/jenkins_home/ --name my_jenkins -d jenkins |
也可以用
--mount type=bind,source=/var/jenkins_home/,target=/var/jenkins_home/
网络
当 Docker 启动时,会自动在主机上创建一个 docker0 虚拟网桥,实际上是 Linux 的一个 bridge,可以理解为一个软件交换机。它会在挂载到它的网口之间进行转发。

外部访问容器
使用-p和-P 设置主机和容器的映射端口。
容器互联
使用自定义网络 (network)进行互联。
运行一个容器并连接到新建的 my-net 网络
1 | $ docker run -it --rm --name busybox1 --network my-net busybox sh |
打开新的终端,再运行一个容器并加入到 my-net 网络
1 | $ docker run -it --rm --name busybox2 --network my-net busybox sh |
再打开一个新的终端查看容器信息
1 | $ docker container ls |
下面通过 ping 来证明 busybox1 容器和 busybox2 容器建立了互联关系。
在 busybox1 容器输入以下命令
1 | / # ping busybox2 |
用 ping 来测试连接 busybox2 容器,它会解析成 172.19.0.3。
同理在 busybox2 容器执行 ping busybox1,也会成功连接到。
1 | / # ping busybox1 |
这样,busybox1 容器和 busybox2 容器建立了互联关系。
DNS
在自定义配置文件中加入:
1 | { |
以后每次启动容器都会自动配置上面的 DNS。
如果用户想要手动指定容器的配置,可以在使用 docker run 命令启动容器时加入如下参数:
-h HOSTNAME 或者 --hostname=HOSTNAME 设定容器的主机名,它会被写到容器内的 /etc/hostname 和 /etc/hosts。但它在容器外部看不到,既不会在 docker container ls 中显示,也不会在其他的容器的 /etc/hosts 看到。
--dns=IP_ADDRESS 添加 DNS 服务器到容器的 /etc/resolv.conf 中,让容器用这个服务器来解析所有不在 /etc/hosts 中的主机名。
--dns-search=DOMAIN 设定容器的搜索域,当设定搜索域为 .example.com 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 host.example.com。
注意:如果在容器启动时没有指定最后两个参数,Docker 会默认用主机上的
/etc/resolv.conf来配置容器。
容器访问外网
容器要想访问外部网络,需要本地系统的转发支持。在Linux 系统中,检查转发是否打开。
1 | $sysctl net.ipv4.ip_forward |
如果为 0,说明没有开启转发,则需要手动打开。
1 | $sysctl -w net.ipv4.ip_forward=1 |
如果在启动 Docker 服务的时候设定 --ip-forward=true, Docker 就会自动设定系统的 ip_forward 参数为 1。
Docker Compose
Docker镜像在创建之后,往往需要自己手动pull来获取镜像,然后执行run命令来运行。当服务需要用到多种容器,容器之间又产生了各种依赖和连接的时候,部署一个服务的手动操作是令人感到十分厌烦的。
Dcoker-Compose技术,就是通过一个.yml配置文件,将所有的容器的部署方法、文件映射、容器连接等等一系列的配置写在一个配置文件里,最后只需要执行docker-compose up命令就会像执行脚本一样的去一个个安装容器并自动部署他们,极大的便利了复杂服务的部署。
了解
Compose 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。
首先介绍几个术语。
- 服务 (
service):一个应用容器,实际上可以运行多个相同镜像的实例。 - 项目 (
project):由一组关联的应用容器组成的一个完整业务单元。
可见,一个项目可以由多个服务(容器)关联而成,Compose 面向项目进行管理。
安装
Linux 上默认是没有安装 docker-compose,查看版本:
1 | $ docker-compose --version |
没有安装。
从 官方 GitHub Release 处直接下载编译好的二进制文件,
使用二进制包安装,比如:
1 | $ sudo curl -L https://github.com/docker/compose/releases/download/1.17.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose |
模板指令
使用的 version: 3
build
指定 Dockerfile 所在文件夹的路径(可以是绝对路径,或者相对 docker-compose.yml 文件的路径)。 Compose 将会利用它自动构建这个镜像,然后使用这个镜像。
注意:YAML布尔值(true,false,yes,no,on,off)必须用引号引起来,以便解析器将其解释为字符串。
1 | version: '3' |
build 不能和 image 一起去使用
cap_add, cap_drop
指定容器的内核能力(capacity)分配。
让容器拥有所有能力:
1
2cap_add:
- ALL让容器移除某些能力:
1
2
3cap_drop:
- NET_ADMIN
- SYS_ADMIN
command
覆盖容器启动后默认执行的命令。
cgroup_parent
为容器指定可选的父cgroup。
1 | cgroup_parent: m-executor-abcd |
container_name
指定自定义容器名称,而不是生成的默认名称。
由于Docker容器名称必须是唯一的,因此如果您指定了自定义名称,则无法将服务扩展到1个容器之外。 尝试这样做会导致错误。
devices
指定设备映射关系。
1 | devices: |
depends_on
Express之间的依赖关系,有两个效果:
docker-compose up将按照依赖顺序启动服务。 在下面的示例中,db和redis将在web之前启动。docker-compose up SERVICE将自动包含SERVICE的依赖关系。 在以下示例中,docker-compose up web也将创建并启动db和redis。1
2
3
4
5
6
7
8
9
10
11version: '2'
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres注意:在启动web之前,depends_on不会等待db和redis“就绪”,直到它们被启动。 如果您需要等待服务准备就绪,请参阅控制启动顺序了解有关此问题的更多信息以及解决问题的策略。
dns
自定义DNS服务器。可以是单个值或列表。
1 | dns: 8.8.8.8 |
dns_search
配置 DNS 搜索域。可以是一个值,也可以是一个列表。
1 | dns_search: example.com |
tmpfs
挂载一个或者多个 tmpfs 文件系统到容器。
1 | tmpfs: /run |
env_file
从文件添加环境变量。可以是单个值或列表。
如果已使用docker-compose -f FILE指定了一个Compose文件,则env_file中的路径相对于该文件所在的目录。
在环境中指定的环境变量会覆盖这些值。
1 | env_file: .env |
Compose期望env文件中的每一行都处于VAR = VAL格式。 以#开头的行(即注释)将被忽略,空行也是如此。
1 | # Set Rails/Rack environment |
注意:如果您的service指定了build选项,则在build过程中将不会自动显示环境文件中定义的变量。 使用build的args子选项来定义构建时环境变量。
environment
设置环境变量。你可以使用数组或字典两种格式。
只给定名称的变量会自动获取运行 Compose 主机上对应变量的值,可以用来防止泄露不必要的数据。
1 | environment: |
expose
暴露端口,但不映射到宿主机,只被连接的服务访问。
仅可以指定内部端口为参数,
这个标签与Dockerfile中的EXPOSE指令一样,用于指定暴露的端口,但是只是作为一种参考,实际上docker-compose.yml的端口映射还得ports这样的标签。
1 | expose: |
extra_hosts
添加主机名的标签,就是往 /etc/hosts文件中添加一些记录,与 Docker client 的 --add-host
1 | extra_hosts: |
会在启动后的服务容器中 /etc/hosts 文件中添加相应 host。
healthcheck
通过命令检查容器是否健康运行。
1 | healthcheck: |
image
指定为镜像名称或镜像 ID。如果镜像在本地不存在,Compose 将会尝试拉取这个镜像。
1 | image: ubuntu:latest |
labels
为容器添加 Docker 元数据(metadata)信息。例如可以为容器添加辅助说明信息。
1 | labels: |
links
这个标签解决的是容器连接问题,与Docker client的 --link一样效果,会连接到其它服务中的容器。
1 | links: |
使用的别名将会自动在服务容器中的/etc/hosts里创建。例如:
1 | 172.12.2.186 db |
相应的环境变量也将被创建。
logging
配置日志选项。
1 | logging: |
目前支持三种日志驱动类型。
1 | driver: "json-file" |
options 配置日志驱动的相关参数。
1 | options: |
network_mode
设置网络模式。使用和 docker run 的 --network 参数一样的值。
1 | network_mode: "bridge" # 桥接模式,这种模式下, docker 会默认创建一个 docker0 的网桥,从它中分配 ip 提供给容器使用 |
networks
配置容器连接的网络。
1 | version: "3" |
pid
跟主机系统共享进程命名空间。打开该选项的容器之间,以及容器和宿主机系统之间可以通过进程 ID 来相互访问和操作。
1 | pid: "host" |
ports
暴露端口信息。
使用宿主端口:容器端口 (HOST:CONTAINER) 格式,或者仅仅指定容器的端口(宿主将会随机选择端口)都可以。
1 | ports: |
注意:当使用 HOST:CONTAINER 格式来映射端口时,如果你使用的容器端口小于 60 并且没放到引号里,可能会得到错误结果,因为 YAML 会自动解析 xx:yy 这种数字格式为 60 进制。为避免出现这种问题,建议数字串都采用引号包括起来的字符串格式。
secrets
Docker命令行工具提供了docker secret命令来管理敏感信息,
从 Docker Compose V3.1开始,支持在容器编排文件中使用 secret,这可以方便地在不同容器中分享所需的敏感信息。
docker secret 只能从Docker Swarm模式的manager节点调用,如果你在本机进行试验,请先执行 docker swarm init命令。
1 | $ docker secret --help |
创建一个数据库密码:
1 | $ echo "kronchan" | docker secret create db_password - |
在服务编排中:
1 | version: "3" |
在 Swarm 集群中 ,例如,我用这个密码启动一个 MYSQL 服务,在 manage 节点中:
1 | docker service create \ |
这个过程分为两个步骤:
source指定容器使用 secret 后,secret 会被解密并存放到容器的文件系统中,默认位置为/run/secrets/<secret_name>,可以使用target重新定位。- 设置环境变量
MYSQL_ROOT_PASSWORD_FILE指定从/run/secrets/mysql_root_password中读取并设置 MySQL 的管理员密码。
security_opt
指定容器模板标签(label)机制的默认属性(用户、角色、类型、级别等)。例如配置标签的用户名和角色名。
1 | security_opt: |
stop_signal
设置另一个信号来停止容器。在默认情况下使用的是 SIGTERM 停止容器。
1 | stop_signal: SIGUSR1 # 缺省值 |
sysctls
配置容器内核参数。
1 | sysctls: |
ulimit
指定容器的 ulimits 限制值。
例如,指定最大进程数为 65535,指定文件句柄数为 20000(软限制,应用可以随时修改,不能超过硬限制) 和 40000(系统硬限制,只能 root 用户提高)。
1 | ulimits: |
volumes
数据卷所挂载路径设置。可以设置宿主机路径 (HOST:CONTAINER) 或加上访问模式 (HOST:CONTAINER:ro)。
该指令中路径支持相对路径。
1 | volumes: |
例如编排一个 pqsql:
1 | version: "3" |
举个例子,自动创建一个数据卷 pq_db_data,目录在 /var/lib/docker/volumes/ 下。
将 容器 pq 的目录 /var/lib/postgresql 挂载到数据卷 pq_db_data。
volumes_from
从其它容器或者服务挂载数据卷,可选的参数是 :ro或者 :rw,前者表示容器只读,后者表示容器对数据卷是可读可写的。默认情况下是可读可写的。
1 | volumes_from: |
entrypoint
指定服务容器启动后执行的入口文件。
1 | entrypoint: /code/entrypoint.sh |
privileged
允许容器中运行一些特权命令。
1 | privileged: true |
restart
指定容器退出后的重启策略为始终重启。该命令对保持服务始终运行十分有效,在生产环境中推荐配置为 always 或者 unless-stopped。
1 | restart: always |
在 Swarm 中失效,Swarm Stacks use the
restart_policy:under thedeploy:
working_dir
指定容器中工作目录。
1 | working_dir |
user
指定容器中运行应用的用户名。
1 | user: nginx |
使用
基本的使用格式
1 | $ docker-compose [options] [COMMAND] [ARGS...] |
options:
1 | -f, --file FILE 指定启动模版文件(一个非docker-compose.yml命名的yaml文件,默认为docker-compose.yml) |
常用命令
build
格式为 docker-compose build [options] [SERVICE...]。
构建(重新构建)项目中的服务容器。
服务容器一旦构建后,将会带上一个标记名,例如对于 web 项目中的一个 db 容器,可能是 web_db。
可以随时在项目目录下运行 docker-compose build 来重新构建服务。
选项包括:
--force-rm删除构建过程中的临时容器。--no-cache构建镜像过程中不使用 cache(这将加长构建过程)。--pull始终尝试通过 pull 来获取更新版本的镜像。
config
验证 Compose 文件格式是否正确,若正确则显示配置,若格式错误显示错误原因。
down
此命令将会停止 up 命令所启动的容器,并移除网络。
exec
进入指定的容器。
help
获取命令的帮助信息。
images
列出这个 Compose 项目中包含的镜像。
kill
格式为 docker-compose kill [options] [SERVICE...]。
通过发送 SIGKILL 信号来强制停止服务容器。
支持通过 -s 参数来指定发送的信号,例如通过如下指令发送 SIGINT 信号。
1 | $ docker-compose kill -s SIGINT |
logs
格式为 docker-compose logs [options] [SERVICE...]。
查看服务容器的输出。默认情况下,docker-compose 将对不同的服务输出使用不同的颜色来区分。可以通过 --no-color 来关闭颜色。
该命令在调试问题的时候十分有用。
pause
暂停一个或多个容器。
port
格式为 docker-compose port [options] SERVICE PRIVATE_PORT。
打印某个容器端口所映射的公共端口。
选项:
--protocol=proto指定端口协议,tcp(默认值)或者 udp。--index=index如果同一服务存在多个容器,指定命令对象容器的序号(默认为 1)。
ps
格式为 docker-compose ps [options] [SERVICE...]。
列出项目中目前的所有容器。
选项:
-q只打印容器的 ID 信息。
pull
格式为 docker-compose pull [options] [SERVICE...]。
拉取服务依赖的镜像。
选项:
--ignore-pull-failures忽略拉取镜像过程中的错误。
push
推送服务依赖的镜像到 Docker 镜像仓库。
restart
格式为 docker-compose restart [options] [SERVICE...]。
重启项目中的服务。
选项:
-t, --timeout TIMEOUT指定重启前停止容器的超时(默认为 10 秒)。
rm
格式为 docker-compose rm [options] [SERVICE...]。
删除所有(停止状态的)服务容器。推荐先执行 docker-compose stop 命令来停止容器。
选项:
-f, --force强制直接删除,包括非停止状态的容器。一般尽量不要使用该选项。-v删除容器所挂载的数据卷。
run
格式为 docker-compose run [options] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]。
在指定服务上执行一个命令。
例如:
1 | $ docker-compose run ubuntu ping docker.com |
将会启动一个 ubuntu 服务容器,并执行 ping docker.com 命令。
默认情况下,如果存在关联,则所有关联的服务将会自动被启动,除非这些服务已经在运行中。
该命令类似启动容器后运行指定的命令,相关卷、链接等等都将会按照配置自动创建。
两个不同点:
- 给定命令将会覆盖原有的自动运行命令;
- 不会自动创建端口,以避免冲突。
如果不希望自动启动关联的容器,可以使用 --no-deps 选项,例如
1 | $ docker-compose run --no-deps web python manage.py shell |
将不会启动 web 容器所关联的其它容器。
选项:
-d后台运行容器。--name NAME为容器指定一个名字。--entrypoint CMD覆盖默认的容器启动指令。-e KEY=VAL设置环境变量值,可多次使用选项来设置多个环境变量。-u, --user=""指定运行容器的用户名或者 uid。--no-deps不自动启动关联的服务容器。--rm运行命令后自动删除容器,d模式下将忽略。-p, --publish=[]映射容器端口到本地主机。--service-ports配置服务端口并映射到本地主机。-T不分配伪 tty,意味着依赖 tty 的指令将无法运行。
scale
格式为 docker-compose scale [options] [SERVICE=NUM...]。
设置指定服务运行的容器个数。
通过 service=num 的参数来设置数量。例如:
1 | $ docker-compose scale web=3 db=2 |
将启动 3 个容器运行 web 服务,2 个容器运行 db 服务。
一般的,当指定数目多于该服务当前实际运行容器,将新创建并启动容器;反之,将停止容器。
选项:
-t, --timeout TIMEOUT停止容器时候的超时(默认为 10 秒)。
start
格式为 docker-compose start [SERVICE...]。
启动已经存在的服务容器。
stop
格式为 docker-compose stop [options] [SERVICE...]。
停止已经处于运行状态的容器,但不删除它。通过 docker-compose start 可以再次启动这些容器。
选项:
-t, --timeout TIMEOUT停止容器时候的超时(默认为 10 秒)。
top
查看各个服务容器内运行的进程。
unpause
格式为 docker-compose unpause [SERVICE...]。
恢复处于暂停状态中的服务。
up
格式为 docker-compose up [options] [SERVICE...]。
该命令十分强大,它将尝试自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。
链接的服务都将会被自动启动,除非已经处于运行状态。
可以说,大部分时候都可以直接通过该命令来启动一个项目。
默认情况,docker-compose up 启动的容器都在前台,控制台将会同时打印所有容器的输出信息,可以很方便进行调试。
当通过 Ctrl-C 停止命令时,所有容器将会停止。
如果使用 docker-compose up -d,将会在后台启动并运行所有的容器。一般推荐生产环境下使用该选项。
默认情况,如果服务容器已经存在,docker-compose up 将会尝试停止容器,然后重新创建(保持使用 volumes-from 挂载的卷),以保证新启动的服务匹配 docker-compose.yml 文件的最新内容。如果用户不希望容器被停止并重新创建,可以使用 docker-compose up --no-recreate。这样将只会启动处于停止状态的容器,而忽略已经运行的服务。如果用户只想重新部署某个服务,可以使用 docker-compose up --no-deps -d <SERVICE_NAME> 来重新创建服务并后台停止旧服务,启动新服务,并不会影响到其所依赖的服务。
选项:
-d在后台运行服务容器。--no-color不使用颜色来区分不同的服务的控制台输出。--no-deps不启动服务所链接的容器。--force-recreate强制重新创建容器,不能与--no-recreate同时使用。--no-recreate如果容器已经存在了,则不重新创建,不能与--force-recreate同时使用。--no-build不自动构建缺失的服务镜像。-t, --timeout TIMEOUT停止容器时候的超时(默认为 10 秒)。
version
格式为 docker-compose version。
打印版本信息。
实践
实战 WordPress 项目:
1 | version: "3" |
Docker Machine
docker技术是基于Linux内核的cgroup技术实现的,那么问题来了,在非Linux平台上是否就不能使用docker技术了呢?答案是可以的,不过显然需要借助虚拟机去模拟出Linux环境来。
Docker Machine 就是docker公司官方提出的,用于在各种平台上快速创建具有docker服务的虚拟机的技术,甚至可以通过指定driver来定制虚拟机的实现原理(一般是virtualbox)。
Docker Machine 是 Docker 官方编排(Orchestration)项目之一,负责在多种平台上快速安装 Docker 环境。
安装
查询版本信息:
1 | $ docker-machine -v |
在 Linux 64 位系统上直接下载对应的二进制包。
1 | $ sudo curl -L https://github.com/docker/machine/releases/download/v0.13.0/docker-machine-`uname -s`-`uname -m` > /usr/local/bin/docker-machine |
完成后,查看版本信息。
1 | $ docker-machine -v |
创建本地主机驱动
创建一个
virtualbox类型的驱动,名为test1,可以加上参数配置分配的硬件的信息:1
2
3$ docker-machine create -d virtualbox test1
Running pre-create checks...
Error with pre-create check: "VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path"出现了错误,需要安装VirtualBox环境
配置VirtualBox源
1
2
3
4
5
6
7
8$ vim /etc/yum.repos.d/virtualbox.repo
[virtualbox]
name=Oracle Linux / RHEL / CentOS-$releasever / $basearch - VirtualBox
baseurl=http://download.virtualbox.org/virtualbox/rpm/el/$releasever/$basearch
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://www.virtualbox.org/download/oracle_vbox.asc安装VirtualBox
CentOS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23# 1. 先搜索
# 2. 安装版本
# 3. 重新加载配置
$ yum search VirtualBox
Loaded plugins: fastestmirror
Loading mirror speeds from cached hostfile
======================================= N/S matched: VirtualBox ========================================
VirtualBox-4.3.x86_64 : Oracle VM VirtualBox
VirtualBox-5.0.x86_64 : Oracle VM VirtualBox
VirtualBox-5.1.x86_64 : Oracle VM VirtualBox
VirtualBox-5.2.x86_64 : Oracle VM VirtualBox
Name and summary matches only, use "search all" for everything.
$ yum install -y VirtualBox-5.1
$ /sbin/vboxconfig
# 中间出了点问题,说是少了
# This system is not currently set up to build kernel modules (system extensions).
#Running the following commands should set the system up correctly:
# yum install kernel-devel-3.10.0-693.2.2.el7.x86_64
$ yum install kernel-devel-3.10.0-693.2.2.el7.x86_64
# 重新再次加载
$ /sbin/vboxconfig
# 启动成功,心累然后就可以创建了虚拟驱动了。
Ubuntu
1
2# 手上暂时没有 Ubuntu 系统没有测试,网上都是这么说的,记录下,没有验证,
$ apt-get install virtualbox
还有一种问题:
1
"This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory"
issues/4271额,我之前是一直远程在阿里云上的服务器做的, 由于一直很讨厌 WINDOW 的命令模式,甩锅啦,现在好了,这个问题要修改 BIOS ,只好又回到 WINDOW 平台上做
登陆到主机:
1
2
3
4
5
6
7$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default * virtualbox Running tcp://192.168.99.100:2376 v17.12.0-ce
test1 - virtualbox Running tcp://192.168.99.101:2376 v18.04.0-ce
$ docker-machine ssh test1
# 后面就可以操作 test1 了
补充,通过 -d 选项可以选择支持的驱动类型。
- amazonec2
- azure
- digitalocean
- exoscale
- generic
- hyperv
- none
- openstack
- rackspace
- softlayer
- virtualbox
- vmwarevcloudair
- vmwarefusion
- vmwarevsphere
操作命令
active查看活跃的 Docker 主机config输出连接的配置信息create创建一个 Docker 主机env显示连接到某个主机需要的环境变量inspect输出主机更多信息ip获取主机地址kill停止某个主机ls列出所有管理的主机provision重新设置一个已存在的主机regenerate-certs为某个主机重新生成 TLS 认证信息restart重启主机rm删除某台主机sshSSH 到主机上执行命令scp在主机之间复制文件mount挂载主机目录到本地start启动一个主机status查看主机状态stop停止一个主机upgrade更新主机 Docker 版本为最新url获取主机的 URLversion输出 docker-machine 版本信息help输出帮助信息
Docker Swarm
Docker 1.12 Swarm mode 已经内嵌入 Docker 引擎,成为了 docker 子命令 docker swarm。
Swarm mode 内置 kv 存储功能,提供了众多的新特性,比如:具有容错能力的去中心化设计、内置服务发现、负载均衡、路由网格、动态伸缩、滚动更新、安全传输等。
基本概念
节点
运行 Docker 的主机可以主动初始化一个 Swarm 集群或者加入一个已存在的 Swarm 集群,这样这个运行 Docker 的主机就成为一个 Swarm 集群的节点 (node) 。
节点分为管理 (manager) 节点和工作 (worker) 节点。
管理节点用于 Swarm 集群的管理,docker swarm 命令基本只能在管理节点执行(节点退出集群命令 docker swarm leave 可以在工作节点执行)。一个 Swarm 集群可以有多个管理节点,但只有一个管理节点可以成为 leader,leader 通过 raft 协议实现。
工作节点是任务执行节点,管理节点将服务 (service) 下发至工作节点执行。管理节点默认也作为工作节点。你也可以通过配置让服务只运行在管理节点。
来自 Docker 官网的这张图片形象的展示了集群中管理节点与工作节点的关系。

服务和任务
任务 (Task)是 Swarm 中的最小的调度单位,目前来说就是一个单一的容器。
服务 (Services) 是指一组任务的集合,服务定义了任务的属性。服务有两种模式:
replicated services按照一定规则在各个工作节点上运行指定个数的任务。global services每个工作节点上运行一个任务
两种模式通过 docker service create 的 --mode 参数指定。
来自 Docker 官网的这张图片形象的展示了容器、任务、服务的关系。

创建 Swarm 集群
前面我们了解到 Swarm 集群是由 manager 和 worker 组成的。
初始化集群
初始化一个 manager,主机有多个网卡,拥有多个 IP,必须使用 --advertise-addr 指定 IP。:
1 | $ docker-machine create -d virtualbox manager |
工作节点
新增加一个工作节点
worker1:1
2
3
4
5
6$ docker-machine create -d virtualbox worker1
$ docker-machine ssh worker1
docker@worker1:~$ docker swarm join --token SWMTKN-1-3js3ppvjj191r9qx92uiva4l64z8xgy6sno69et5y9ri7a8es5-695418k4uks90r9j
9bgwag2k3 192.168.99.102:2377
This node joined a swarm as a worker.然后按照上面的步骤,新增一个节点
worker2。进入
manager节点 ,查看集群状态:1
2
3
4
5docker@manager:~$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
wwyq0ym88dvuhxkljr4963kei * manager Ready Active Leader 18.04.0-ce
dncrx0o8lbq0a5v8lcdwt2onl worker1 Ready Active 18.04.0-ce
d9d9pjhwh86lz42klh41ud8ss worker2 Ready Active
部署服务
我们使用 docker service 命令来管理 Swarm 集群中的服务,该命令只能在管理(manager)节点运行。
新建服务
在上面我们创建的 Swarm 集群中的 manager 节点中 运行一个 nginx 服务:
1 | $ docker service create --replicas 3 -p 80:80 --name nginx nginx:1.13.7-alpine |
查询服务
1 | # 查询服务 |
我的集群三个节点的 IP 分别是:
1 | $ docker-machine ls |
分别访问这几个节点的 ip 都能访问到熟悉 nginx 欢迎页。
查询日志 docker service logs <service>
删除服务
使用 docker service rm 来从 Swarm 集群移除某个服务。
使用 compose 文件部署
需要注意的 swarm 的compose 多出一些模板指令,而且以前 docker-compose 中的一些指令将失效。
1 |
部署服务使用 docker stack deploy,其中 -c 参数指定 compose 文件名。
1 | $ docker stack deploy -c docker-compose-swarm.yml myweb-name |
查看:
1 | $ docker stack ls |
移除服务:
1 | $ docker stack down |
我们以在 Swarm 集群中部署 WordPress 为例进行说明。
1 | version: "3" |
在 Swarm 集群管理节点新建该文件,其中的 visualizer 服务提供一个可视化页面,我们可以从浏览器中很直观的查看集群中各个服务的运行节点。
远程 API 架构
了解
daemon
Docker的基础服务,比如容器的创建、查看、停止、镜像的管理,其实都是由docker的守护进程(daemon)来实现的。
每次执行的Docker指令其实都是通过向daemon发送请求来实现的。
daemon的运作(通信模式)主要有两种,一种是通过unix套接字(默认,但只能在本地访问到,比较安全),一种是通过监听tcp协议地址和端口来实现(这个可以实现在远程调用到docker服务)。
远程 API
除了通过远程tcp协议访问远程主机上的docker服务外,docker还提供了一套基于HTTP的API,可以使用curl来实现操作远程主机上的docker服务,这为开发基于WEB的docker服务提供了便利。
开启远程 API
Linux
1.12版本后, 用户可以自行创建配置文件 /etc/docker/daemon.json,该文件不区分系统,是通用的,推荐使用。具体参考:官方文档。不知道版本的可以通过 $ dockerd version 查看。
方式一: 首先,你需要创建 /etc/docker/daemon.json 文件,文件内容如下:
1 | { |
开启的是 2375 端口。
然后重启生效:
1 | $ systemctl daemon-reload |
方式二 : 当然也可以更改启动命令的方式 :
1 | # Ubuntu 系统 |
使用的时候只需要在客户端的主机上加上环境变量 DOCKER_HOST=tcp://xxx.xxx.xx.xx/2375,就可以在主机上使用远程的 Docker API。
Docker Toolbox
在 window 或者 mac 上面使用
Docker-toolbox,则就比较简单了,,打开docker QuickStart Terminal终端,输入命令 :1
2
3
4$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default - virtualbox Running tcp://192.168.99.100:2376 v17.10.0-ce查询到 URL,根据上面的结果
我使用的是 WONDOW 电脑的主机上加上环境变量
DOCKER_HOST=tcp://192.168.99.100:2376;当然也可以使用
VirtualBox的端口映射到 本地的 2375端口也行,docker 默认的 host 是127.0.0.1:2376,这样环境变量也不用配置。我使用的是 WIN10 ,后面也是这个开发环境,打开
PowerShell执行命令 docker version 命令发现一个问题:1
2Get http://192.168.99.100:2376/v1.33/containers/json: malformed HTTP response "\x15\x03\x01\x00\x02\x02".
* Are you trying to connect to a TLS-enabled daemon without TLS?发现它需要 TLS ,
设置环境变量
DOCKER_TLS_VERIFY=1然后再次执行 docker version 命令,发现又出现 问题:
1
The server probably has client authentication (--tlsverify) enabled. Please check your TLS client certification settings: Get https://192.168.99.100:2376/v1.35/version: remote error: tls: bad certificate
没指定 证书
cret。 证书的路径在当前用户的/.docker/certs下:设置环境变量
DOCKER_CERT_PATH=C:/Users/KronChan/.docker/machine/certs
配置好,我们就能直接能在自己的主机中 使用 虚拟机中的 Docker API。
一些其他的问题解决
更改Docker 默认镜像路径
一般的我们系统盘不会给很大的空间,然而 Docker 镜像占用的空间一般都是非常大的,所以我们需要将镜像和容器挂在到其他数据盘下。
docker 默认的数据目录都在/var/lib/docker/ 下,我们只要将这个目录挂载到其他数据盘目录下。
1 | # 前提关闭 docker |
WARNING: No memory limit support
查询 sudo docker info的时候发现警告信息:WARNING: No memory limit support。
解决方案:
1 | $ sudo vim /etc/default/grub |
