一、概述

Docker是一款针对程序开发人员和系统管理员来开发、部署、运行应用的一款虚拟化平台。Docker 可以让你像使用集装箱一样快速的组合成应用,并且可以像运输标准集装箱一样,尽可能的屏蔽代码层面的差异。Docker 会尽可能的缩短从代码测试到产品部署的时间。

二、架构

Docker 包括三个基本概念:

  • 镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。镜像是无状态的、只读的
  • 容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
  • 仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。

使用 --link 参数可以让容器之间安全的进行交互。

--link 参数的格式为 --link name:alias,其中 name 是要链接的容器的名称,alias 是这个连接的别名(可选项)。

三、网络模式

docker的网络配置分为四种, hostContainerNoneBridge(默认)

  • Bridge:为每一个容器分配、设置 IP 等,并将容器连接到一个 docker0 虚拟网桥,默认为该模式

  • host:容器将不会虚拟出自己的网卡,配置自己的 IP 等,而是使用宿主机的 IP 和端口。

  • Container:新创建的容器不会创建自己的网卡和配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。

  • None: 容器有独立的 Network namespace,但并没有对其进行任何网络设置,如分配 veth pair 和网桥连接,IP 等。

查看所有网络模式:

1
2
$ docker network ls
$ docker network inspect bridge # 查看bridge 有哪些正在运行的容器

1、Bridge模式

img

在该模式中,Docker 守护进程创建了一个虚拟以太网桥 docker0,新建的容器会自动桥接到这个接口,附加在其上的任何网卡之间都能自动转发数据包。

简单来说,这个模式容器通过docker0连接外网,外部需要通过映射端口连接容器,容器之前通过docker0访问。

缺点:docker0随机分配IP,容器IP不固定。

2、host模式

img

采用 host 网络模式的 Docker Container,可以直接使用宿主机的 IP 地址与外界进行通信,若宿主机的 eth0 是一个公有 IP,那么容器也拥有这个公有 IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行 NAT 转换;

host 网络模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性,安全性降低。且会出现端口冲突,这个模式使用较少。

3、none模式

none 网络模式是指禁用网络功能,只有 lo 接口 local 的简写,代表 127.0.0.1,即 localhost 本地环回接口。在创建容器时通过参数 --net none 或者 --network none 指定;

简单来说就是这个容器不联网。

4、Container模式

img

Container 网络模式即新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等

Docker 容器会共享一个网络栈,这样两个容器之间可以使用 localhost 高效快速通信。

参考: https://www.cnblogs.com/mrhelloworld/p/docker11.html

四、Docker命令

容器生命周期管理

1、run 命令

创建一个新的容器并运行一个命令

语法:

1
$ docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

OPTIONS说明:

  • -a stdin: 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
  • -d: 后台运行容器,并返回容器ID;
  • -i: 以交互模式运行容器,通常与 -t 同时使用;
  • -P: 随机端口映射,容器内部端口随机映射到主机的端口
  • -p: 指定端口映射,格式为:主机(宿主)端口:容器端口
  • -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
  • –name=”nginx-lb”: 为容器指定一个名称;
  • –dns 8.8.8.8: 指定容器使用的DNS服务器,默认和宿主一致;
  • –dns-search example.com: 指定容器DNS搜索域名,默认和宿主一致;
  • -h “mars”: 指定容器的hostname;
  • -e username=”ritchie”: 设置环境变量;
  • –env-file=[]: 从指定文件读入环境变量;
  • –cpuset=”0-2” or –cpuset=”0,1,2”: 绑定容器到指定CPU运行;
  • **-m :**设置容器使用内存最大值;
  • –net=”bridge”: 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
  • –link=[]: 添加链接到另一个容器,可以让容器之间安全的进行交互。;
  • –expose=[]: 开放一个端口或一组端口;
  • –volume , -v: 绑定一个卷

实例:

1
2
3
4
5
6
7
# 运行一个wordpress容器
$ docker run -p 8080:80 --name wordpress \ # 映射端口 本机端口:容器端口
-v /Users/zhengfa.hu/Docker/wordpress:/var/www/html \ # 绑定本机卷
-e WORDPRESS_DB_PASSWORD=root \ # 设置环境变量
-e WORDPRESS_DB_HOST=mysql:3306 \ # 设置环境变量
--link mysql \ # 连接到mysql容器
-d wordpress # -d 后台运行,镜像:<版本>

2、start/stop/restart 命令

启动/停止/重启一个或多个容器。

实例:

1
2
3
4
$ docker start $(docker ps -a | awk '{ print $1}' | tail -n +2)     # docker 启动所有的容器
$ docker stop $(docker ps -a | awk '{ print $1}' | tail -n +2) # docker 关闭所有的容器
$ docker stop $(docker ps | awk '{ print $1}' | tail -n +2) # 暂停所有正在运行的容器
$ docker restart $(docker ps | awk '{ print $1}' | tail -n +2) # 重启所有正在运行的容器

3、kill 命令

杀掉一个运行中的容器。

1
docker kill [OPTIONS] CONTAINER [CONTAINER...]

OPTIONS说明:

  • **-s :**向容器发送一个信号

实例:

1
$ docker kill -s KILL nginx

4、rm 命令

删除一个或多个容器。

语法:

1
$ docker rm [OPTIONS] CONTAINER [CONTAINER...]

OPTIONS说明:

  • **-f :**通过 SIGKILL 信号强制删除一个运行中的容器。
  • **-l :**移除容器间的网络连接,而非容器本身。
  • **-v :**删除与容器关联的卷,容器也会删除。

实例:

1
2
$ docker rm $(docker ps -a | awk '{ print $1}' | tail -n +2)   # docker 删除所有的容器
$ docker rmi $(docker images | awk '{print $3}' |tail -n +2) # docker 删除所有的镜像

5、pause/unpause 命令

docker pause :暂停容器中所有的进程。

docker unpause :恢复容器中所有的进程。

语法:

1
2
$ docker pause CONTAINER [CONTAINER...]
$ docker unpause CONTAINER [CONTAINER...]

实例:

1
2
$ docker pause nginx            # 暂停容器nginx提供服务
$ docker unpause nginx # 恢复容器nginx提供服务

6、create 命令

创建一个新的容器但不启动它。用法同 [docker run](#1、run 命令) 。

7、exec 命令

在运行的容器中执行命令

语法:

1
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

OPTIONS说明:

  • **-d :**分离模式: 在后台运行
  • **-i :**即使没有附加也保持STDIN 打开
  • **-t :**分配一个伪终端

实例:

1
$ docker exec -it <容器ID或者容器name> /bin/bash      # 进入容器

8、commit 命令

从容器创建一个新的镜像。

语法:

1
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

OPTIONS说明:

  • **-a :**提交的镜像作者;
  • **-c :**使用Dockerfile指令来创建镜像;
  • **-m :**提交时的说明文字;
  • **-p :**在commit时,将容器暂停。

实例:

1
$ docker commit -a "runoob.com" -m "my apache" mysql  mysql:v1    # 将容器mysql 保存为新的镜像

容器操作

1、ps 命令

列出容器

语法:

1
docker ps [OPTIONS]

OPTIONS说明:

  • **-a :**显示所有的容器,包括未运行的。
  • **-f :**根据条件过滤显示的内容。
  • **–format :**指定返回值的模板文件。
  • **-l :**显示最近创建的容器。
  • **-n :**列出最近创建的n个容器。
  • **–no-trunc :**不截断输出。
  • **-q :**静默模式,只显示容器编号。
  • **-s :**显示总的文件大小。

输出:

容器ID、镜像、启动容器时运行的命令、容器创建时间、容器状态、端口信息、容器名称

容器状态(七种):

created已创建、restarting重启中、running运行中、removing迁移中、paused暂停、exited停止、dead死亡

实例:

1
2
3
4
$ docker ps -f "name=nginx"             # 根据名称过滤
$ docker ps -f status=running # 根据容器状态过滤
$ docker ps --filter ancestor=nginx # 根据镜像名称过滤
$ docker ps --filter ancestor=<ID> # 根据容器ID过滤

2、inspect 命令

获取容器/镜像的元数据。

语法:

1
docker inspect [OPTIONS] NAME|ID [NAME|ID...]

OPTIONS说明:

  • **-f :**指定返回值的模板文件。
  • **-s :**显示总的文件大小。
  • **–type :**为指定类型返回JSON。

实例:

1
2
# 获取容器元数据,默认输出格式为JSON  --format 过滤输出ip
$ docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' mysql

3、top 命令

查看容器中运行的进程信息,支持 ps 命令参数。

语法:

1
docker top [OPTIONS] CONTAINER [ps OPTIONS]

参数支持docker ps

实例:

1
2
3
$ docker top mysql
$ docker top ea59b5ae9693
$ for i in `docker ps |grep Up|awk '{print $1}'`;do echo \ &&docker top $i; done

4、attach 命令

连接到正在运行中的容器。一般用不着,可以使用exec

5、events 命令

从服务器获取实时事件。

语法:

1
docker events [OPTIONS]

OPTIONS说明:

  • -f :根据条件过滤事件;
  • –since :从指定的时间戳后显示所有事件;
  • –until :流水时间显示到指定的时间为止;

实例:

1
2
$ docker events  --since="1467302400"            
$ docker events -f "image"="mysql:5.6" --since="1467302400" # 显示mysql镜像相关的事件

6、logs 命令

获取容器的日志。

语法:

1
docker logs [OPTIONS] CONTAINER

OPTIONS说明:

  • -f : 跟踪日志输出
  • **–since :**显示某个开始时间的所有日志
  • -t : 显示时间戳
  • **–tail :**仅列出最新N条容器日志

实例:

1
2
$ docker logs -f mysql
$ docker logs --since="2016-07-01" --tail=10 mysql

7、wait 命令

阻塞运行直到容器停止,然后打印出它的退出代码。

1
$ docker wait mysql

8、export 命令

将文件系统作为一个tar归档文件导出到STDOUT。导出的是一个容器的快照。

语法:

1
docker export [OPTIONS] CONTAINER

OPTIONS说明:

  • **-o :**将输入内容写到文件。

实例:

1
2
3
$ docker export -o mysql-`date +%Y%m%d`.tar a404c6c174a2  # 将容器按日期保存为tar文件
$ ls mysql-`date +%Y%m%d`.tar
mysql-20160711.tar

9、import 命令

从归档文件来载入容器包,但会恢复为镜像。

语法:

1
docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]

OPTIONS说明:

  • **-c :**应用docker 指令创建镜像;
  • **-m :**提交时的说明文字;

实例:

1
$ docker import  my_ubuntu_v3.tar runoob/ubuntu:v4  

export 和 import 导出的是一个容器的快照, 不是镜像本身, 也就是说没有 layer。

你的 dockerfile 里的 workdir, entrypoint 之类的所有东西都会丢失,commit 过的话也会丢失。

快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也更大。

10、port 命令

列出指定的容器的端口映射,或者查找将PRIVATE_PORT NAT到面向公众的端口。

1
$ docker port mysql             # 查看容器的端口映射情况

11、stats 命令

显示容器资源的使用情况,包括:CPU、内存、网络 I/O 等。

语法:

1
docker stats [OPTIONS] [CONTAINER...]

OPTIONS 说明:

  • **–all , -a :**显示所有的容器,包括未运行的。
  • **–format :**指定返回值的模板文件。
  • **–no-stream :**展示当前状态就直接退出了,不再实时更新。
  • **–no-trunc :**不截断输出。

输出:

容器ID、容器name、CPU使用率、容器内存使用信息、内存使用率、网络IO发送/接收、容器-本机数据读取和写入数据量、容器创建的进程或线程数

实例:

1
2
3
4
$ docker stats mysql
$ docker stats <ID>
$ docker stats mysql --no-stream --format "{{ json . }}" # 以JSON格式输出
$ docker stats --all --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}" mysql wordpress

12、cp 命令

用于容器与主机之间的数据拷贝。

语法:

1
2
docker cp [OPTIONS]  CONTAINER:SRC_PATH   DEST_PATH  
docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH

OPTIONS说明:

  • **-L :**保持源目标中的链接

实例:

1
2
3
4
5
6
7
# 将本机 ndnav 文件夹,拷贝到容器 themes/ 文件夹下
$ docker cp /Downloads/ndnav wordpress:/var/www/html/wp-content/themes/
# 将本机 ndnav 文件夹,拷贝到容器中并重命名为 ndnav-v1
$ docker cp /Downloads/ndnav wordpress:/var/www/html/wp-content/themes/ndnav-v1
# 将容器 themes 目录下的文件拷贝到主机的Downloads/目录下中。
$ docker cp wordpress:/var/www/html/wp-content/themes/ /Downloads/
$ cd /Downloads/themes/

13、diff 命令

检查容器里文件结构的更改。

1
$ docker diff mysql       # 查看容器mysql的文件结构更改

镜像仓库

1、login/logout 命令

登陆和登出Docker镜像仓库。如果未指定镜像仓库地址,默认是Docker Hub。

语法:

1
2
docker login [OPTIONS] [SERVER]
docker logout [OPTIONS] [SERVER]

OPTIONS说明:

  • **-u :**登陆的用户名
  • **-p :**登陆的密码

实例:

1
2
3
$ docker login -u 用户名 -p 密码 <server>           # 登陆指定的镜像仓库
$ docker login -u 用户名 -p 密码
$ docker logout

2、pull 命令

从镜像仓库中拉取或者更新指定镜像。

语法:

1
docker pull [OPTIONS] NAME[:TAG|@DIGEST]

OPTIONS说明:

  • **-a :**拉取所有 tagged 镜像
  • **–disable-content-trust :**忽略镜像的校验,默认开启

实例:

1
$ docker pull mysql:5.7

3、push 命令

将本地的镜像上传到镜像仓库,要先登陆到镜像仓库。

语法:

1
docker push [OPTIONS] NAME[:TAG]

OPTIONS说明:

  • **–disable-content-trust :**忽略镜像的校验,默认开启

实例:

1
$ docker push mysql:5.7

4、search 命令

从Docker Hub查找镜像

语法:

1
docker search [OPTIONS] TERM

OPTIONS说明:

  • **–automated :**只列出 automated build类型的镜像;
  • **–no-trunc :**显示完整的镜像描述;
  • **-f <过滤条件>:**列出收藏数不小于指定值的镜像。

参数:

NAME、DESCRIPTION描述、STARS、OFFICIAL是否是官方发布、AUTOMATED自动构建

实例:

1
$ docker search -f stars=10 java   # 从Docker Hub查找所有镜像名包含 java,并且收藏数大于 10 的镜像

本地镜像操作

1、images

列出本地镜像。

语法:

1
docker images [OPTIONS] [REPOSITORY[:TAG]]

OPTIONS说明:

  • **-a :**列出本地所有的镜像(含中间映像层,默认情况下,过滤掉中间映像层);
  • **–digests :**显示镜像的摘要信息;
  • **-f :**显示满足条件的镜像;
  • **–format :**指定返回值的模板文件;
  • **–no-trunc :**显示完整的镜像信息;
  • **-q :**只显示镜像ID。

实例:

1
2
$ docker images mysql           # 查看mysql的本地镜像
$ docker images # 查看所有本地镜像

2、rmi 命令

删除本地一个或多个镜像。

语法:

1
docker rmi [OPTIONS] IMAGE [IMAGE...]

OPTIONS说明:

  • **-f :**强制删除;
  • **–no-prune :**不移除该镜像的过程镜像,默认移除;

实例:

1
$ docker rmi -f mysql      # 强制移除本地mysql镜像

3、tag 命令

用于给镜像打标签。不会产生新的镜像。

tag 打标签就是多了一个引用,镜像ID相同,所以删除的时候不能用ID去删除,需要按照Tag去删除引用。

语法:

1
docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]

实例:

1
$ docker tag nginx:latest  nginx:v1       # TAG 标签v1

4、build 命令

命令用于使用 Dockerfile 创建镜像。

语法:

1
docker build [OPTIONS] PATH | URL | -

OPTIONS说明:

  • **–build-arg=[] :**设置镜像创建时的变量;
  • **–cpu-shares :**设置 cpu 使用权重;
  • **–cpu-period :**限制 CPU CFS周期;
  • **–cpu-quota :**限制 CPU CFS配额;
  • **–cpuset-cpus :**指定使用的CPU id;
  • **–cpuset-mems :**指定使用的内存 id;
  • **–disable-content-trust :**忽略校验,默认开启;
  • **-f :**指定要使用的Dockerfile路径;
  • **–force-rm :**设置镜像过程中删除中间容器;
  • **–isolation :**使用容器隔离技术;
  • **–label=[] :**设置镜像使用的元数据;
  • **-m :**设置内存最大值;
  • **–memory-swap :**设置Swap的最大值为内存+swap,”-1”表示不限swap;
  • **–no-cache :**创建镜像的过程不使用缓存;
  • **–pull :**尝试去更新镜像的新版本;
  • **–quiet, -q :**安静模式,成功后只输出镜像 ID;
  • **–rm :**设置镜像成功后删除中间容器;
  • **–shm-size :**设置/dev/shm的大小,默认值是64M;
  • **–ulimit :**Ulimit配置。
  • **–squash :**将 Dockerfile 中所有的操作压缩为一层。
  • –tag, -t: 镜像的名字及标签,通常 name:tag 或者 name 格式;可以在一次构建中为一个镜像设置多个标签。
  • –network: 默认 default。在构建期间设置RUN指令的网络模式

实例:

1
2
3
$ docker build -t runoob/ubuntu:v1 .          # 使用当前目录的 Dockerfile 创建镜像,注意 .  符号
$ docker build github.com/creack/docker-firefox # 使用远程Dockerfile 文件创建镜像
$ docker build -f /path/to/a/Dockerfile . # -f 指定Dockerfile 的位置

在 Docker 守护进程执行 Dockerfile 中的指令前,首先会对 Dockerfile 进行语法检查,有语法错误时会返回

5、history 命令

查看指定镜像的创建历史。

语法:

1
docker history [OPTIONS] IMAGE

OPTIONS说明:

  • **-H :**以可读的格式打印镜像大小和日期,默认为true;
  • **–no-trunc :**显示完整的提交记录;
  • **-q :**仅列出提交记录ID。

实例:

1
$ docker history mysql:5.6

6、save 命令

将指定镜像保存成 tar 归档文件。

语法:

1
docker save [OPTIONS] IMAGE [IMAGE...]

OPTIONS 说明:

  • **-o :**输出到的文件。

实例:

1
2
# 将镜像 runoob/ubuntu:v3 生成 my_ubuntu_v3.tar 
$ docker save -o my_ubuntu_v3.tar runoob/ubuntu:v3

7、load 命令

导入使用 docker save 命令导出的镜像。

语法:

1
docker load [OPTIONS]

OPTIONS 说明:

  • –input , -i : 指定导入的文件,代替 STDIN。
  • –quiet , -q : 精简输出信息。

实例:

1
$ docker load < busybox.tar.gz

save 和 load 命令作用对象是镜像,export 和 import 命令作用对象是容器。

系统操作

docker system [cmd]

1、info命令

显示 Docker 系统信息,包括镜像和容器数。

1
2
$ docker system info
$ docker system info --format # 定义输出格式

2、df 命令

整体磁盘的使用情况.

1
2
$ docker system df
$ docker system df -v # 显示详细信息

3、prune 命令

清理没有使用的数据,包括镜像数据,已经停止的容器。操作需谨慎

语法:

1
docker system prune

OPTIONS 说明:

  • –all :删除所有未使用的图像,而不仅仅是悬垂的图像。
  • –filter : 筛选(e.g. ‘label==‘)。
  • –force,-f : 不提示确认,强制删除。
  • –volumes: 删除卷。

实例:

1
2
3
4
5
6
$ docker system prune         # 一次性清理多种类型的资源
$ docker image prune # 删除所有未被 tag 标记和未被容器使用的镜像
$ docker image prune -a # 删除所有未被容器使用的镜像
$ docker container prune # 删除所有停止运行的容器
$ docker volume prune # 删除所有未被挂载的卷
$ docker network prune # 删除 docker 所有资源

四、DockerFile

1、概述

Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

Dockerfile 分为四部分:

  • 基础镜像信息
  • 维护者信息
  • 镜像操作指令
  • 容器启动时执行指令

2、docker build

2.1构建

1
2
$ docker build [选项] <上下文路径/URL/->
$ docker build -t nginx:v3 . # -t 指定镜像标签信息

注意:该命令将读取指定路径下(包括子目录)的 Dockerfile,并将该路径下所有内容发送给 Docker 服务端,由服务端来创建镜像。因此一般建议放置 Dockerfile 的目录为空目录。也可以通过 .dockerignore 文件(每一行添加一条匹配模式)来让 Docker 忽略路径下的目录和文件。

2.2 docker build的用法

直接用 Git repo 进行构建:

1
$ docker build -t hello-world https://github.com/docker-library/hello-world.git  #master:amd64/hello-world

用给定的 tar 压缩包构建

1
$ docker build http://server/context.tar.gz

从标准输入中读取 Dockerfile 进行构建

1
2
3
$ docker build - < Dockerfile
# 或者
$ cat Dockerfile | docker build -

从标准输入中读取上下文压缩包进行构建

1
$ docker build - < context.tar.gz

3、指令

2.1 FROM

功能:构建镜像基于哪个镜像。

格式:

1
2
FROM <image> 
FROM <image>:<tag>

DockerFile第一条指令必须是FROM,同一个dockerfile中创建多个镜像时可以有多个FROM,但是不建议。

2.2 EXPOSE

暴露端口,但不映射到宿主机,只被连接的服务访问。

作用:

  • 帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射。
  • 在运行时使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

格式:

1
EXPOSE <port1> [<port2>...]

2.3 LABEL

功能:指令用来给镜像以键值对的形式添加一些元数据。

格式:

1
LABEL <key>=<value> <key>=<value> <key>=<value> ...

可以给镜像添加多个 LABEL需要注意的是:每条 LABEL 指令都会生成一个新的层。所以最好是把添加的多个 LABEL 合并为一条命令

2.4 ENV

功能:设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

格式:

1
2
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

设置环境变量,定义了环境变量,那么在后续的指令中,就可以使用这个环境变量。

1
2
3
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.xz" \
&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"

2.5 ARG

功能:构建参数,与 ENV 作用一致。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效,也就是说只有 docker build 的过程中有效,构建好的镜像内不存在此环境变量

构建命令 docker build 中可以用 –build-arg <参数名>=<值> 来覆盖。

格式:

1
ARG <参数名>[=<默认值>]

2.6 WORKDIR

功能:指定工作目录(进入容器时的目录)。用 WORKDIR 指定的工作目录,会在构建镜像的每一层中都存在。(WORKDIR 指定的工作目录,必须是提前创建好的)。

docker build 构建镜像过程中的,每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。

格式:

1
WORKDIR <工作目录路径>

通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY 等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。

2.7 COPY

功能:复制指令,从上下文目录中复制文件或者目录到容器里指定路径。

格式:

1
2
COPY [--chown=<user>:<group>] <源路径1>...  <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]

**[–chown=:]**:可选参数,用户改变复制到容器内文件的拥有者和属组。

**<源路径>**:源文件或者源目录,这里可以是通配符表达式,其通配符规则要满足 Go 的 filepath.Match 规则。

<目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建

2.8 ADD

ADD 指令和 COPY 的使用格类似(同样需求下,官方推荐使用 COPY)。功能也类似,不同之处如下:

  • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。
  • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

2.9 VOLUME

功能:定义匿名数据卷。在启动容器时忘记挂载数据卷,会自动挂载到匿名卷。

作用:

  • 避免重要的数据,因容器重启而丢失,这是非常致命的。
  • 避免容器不断变大。

格式:

1
2
VOLUME ["<path1>", "<path2>"...]
VOLUME <path>

指定的是容器内的目录。本地目录是:/var/lib/docker/volumes/{容器ID} 。

在启动容器 docker run 的时候,我们可以通过 -v 参数修改挂载点。

总结: volume只是指定了一个目录,用以在用户忘记启动时指定 -v 参数也可以保证容器的正常运行。比如mysql,你不能说用户启动时没有指定 -v ,然后删了容器,就把mysql的数据文件都删了,那样生产上是会出大事故的,所以mysql的dockerfile里面就需要配置 volume,这样即使用户没有指定 -v ,容器被删后也不会导致数据文件都不在了。还是可以恢复的。

2.10 RUN

功能:为构建的镜像指定要运行的命令行命令,而这些命令是在 docker build 时执行的。

格式:

1
2
3
4
# shell格式
RUN <command>
# exec格式
RUN ["executable", "param1", "param2", ...]

每条 RUN 指令将在当前镜像的基础上执行指定命令,并提交为新的镜像,而 Dockerfile 的指令每执行一次都会在 docker 上新建一层,过多无意义的层,会造成镜像膨胀过大,所以建议将多个命令合并到同一个 RUN 指令上

2.11 CMD

类似于 RUN 指令,用于运行程序,但二者运行的时间点不同:

  • CMD 在docker run 时运行。
  • RUN 是在 docker build。

作用:为启动的容器指定默认要运行的程序,程序运行结束,容器也就结束。CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。

1
2
3
4
5
6
7
8
# 1、shell 命令格式
CMD <shell command>

# 2、exec 命令格式:推荐 ,默认执行文件是 sh。
CMD ["<可执行文件或命令>","<param1>","<param2>",...]

# 3、该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
CMD ["<param1>","<param2>",...]

注意

  • 如果 Dockerfile 中如果存在多个 CMD 指令,仅最后一个生效。
  • 如果 docker run 命令行参数中指定了要运行的程序命令,则会覆盖CMD 指令指定的程序命令。

2.12 ENTRYPOINT

功能:类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

但是, 如果运行 docker run 时使用了 –entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

优点:在执行 docker run 的时候可以指定 ENTRYPOINT 运行所需的参数。

注意:如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

格式:

1
ENTRYPOINT ["<executeable>","<param1>","<param2>",...]

可以搭配 CMD 命令使用:一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参,以下示例会提到。

示例:

假设已通过 Dockerfile 构建了 nginx:test 镜像:

1
2
3
4
FROM nginx

ENTRYPOINT ["nginx", "-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] # 变参

1、不传参运行

1
$ docker run  nginx:test

容器内会默认运行以下命令,启动主进程。

1
$ nginx -c /etc/nginx/nginx.conf

2、传参运行

1
$ docker run  nginx:test -c /etc/nginx/new.conf

容器内会默认运行以下命令,启动主进程(/etc/nginx/new.conf:假设容器内已有此文件)

1
$ nginx -c /etc/nginx/new.conf

2.13 HEALTHCHECK

功能:用于指定某个程序或者指令来监控 docker 容器服务的运行状态。

格式:

1
2
3
HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK [选项] CMD <命令> : 这边 CMD 后面跟随的命令使用,可以参考 CMD 的用法。

2.14 USER

功能:用于指定执行后续命令的用户和用户组,这边只是切换后续命令执行的用户(用户和用户组必须提前已经存在)。

可以使用用户名、UID或GID。

格式:

1
USER <user>[:<group>]

2.15 ONBUILD

功能:触发器。 用于延迟构建命令的执行。简单的说,就是 Dockerfile 里用 ONBUILD 指定的命令,在本次构建镜像的过程中不会执行(假设镜像为 test-build)。当有新的 Dockerfile 使用了之前构建的镜像 FROM test-build ,这时执行新镜像的 Dockerfile 构建时候,会执行 test-build 的 Dockerfile 里的 ONBUILD 指定的命令。

格式:

1
ONBUILD [INSTRUCTION]

3. DockerFile模版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM node:7-alpine  
LABEL maintainer "jakub.skalecki@example.com"

ENV PROJECT_DIR=/app
WORKDIR $PROJECT_DIR

COPY package.json $PROJECT_DIR
RUN npm install
COPY . $PROJECT_DIR

ENV MEDIA_DIR=/media \
NODE_ENV=production \
APP_PORT=3000

VOLUME $MEDIA_DIR
EXPOSE $APP_PORT
# 健康检查, 如果一切正常的话返回 0,否则返回 1:
HEALTHCHECK CMD curl --fail http://localhost:$APP_PORT || exit 1

ENTRYPOINT ["./entrypoint.sh"]
CMD ["start"]

五、Docker Compose

1、简介

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

Compose 中有两个重要的概念:

  • 服务 (service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
  • 项目 (project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

2、命令说明

2.1 基本格式

docker-compose 命令的基本的使用格式是

1
$ docker-compose [-f=<arg>...] [options] [COMMAND] [ARGS...]

2.2 命令选项

  • -f, --file FILE 指定使用的 Compose 模板文件,默认为 docker-compose.yml,可以多次指定。
  • -p, --project-name NAME 指定项目名称,默认将使用所在目录名称作为项目名。
  • --verbose 输出更多调试信息。
  • -v, --version 打印版本并退出。

2.3 常用命令

基本跟docker 容器操作命令类似,基本都有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ docker-compose --help
$ docker-compose up # -d 后台启动应用,推荐生产环境后台启动
# 自动完成包括构建镜像,(重新)创建服务,启动服务,并关联服务相关容器的一系列操作。
# 链接的服务都将会被自动启动,除非已经处于运行状态。

$ docker-compose down # 此命令将会停止 up 命令所启动的容器,并容器、网络、卷、镜像
$ docker-compose ps # 列出项目中的所有容器。
$ docker-compose config # 检验compose文件
$ docker-compose port # 打印某个容器所映射的公共端口

# -f 可以指定不同yml文件模板用于构建镜像
$ docker-compose build # 服务容器一旦构建后,将会带上一个标记名

# 指定服务运行的数量
$ docker-compose scale web=3 db=2 # 启动3个web服务,2个db服务
# 一般的,当指定数目多于该服务当前实际运行容器,将新创建并启动容器;反之,将停止容器。

默认情况,如果服务容器已经存在,docker-compose up 将会尝试停止容器,然后重新创建(保持使用 volumes-from 挂载的卷),以保证新启动的服务匹配 docker-compose.yml 文件的最新内容。如果用户不希望容器被停止并重新创建,可以使用 docker-compose up --no-recreate。这样将只会启动处于停止状态的容器,而忽略已经运行的服务。如果用户只想重新部署某个服务,可以使用 docker-compose up --no-deps -d <SERVICE_NAME> 来重新创建服务并后台停止旧服务,启动新服务,并不会影响到其所依赖的服务。

3、使用

Compose 使用的三个步骤:

  • 使用 Dockerfile 定义应用程序的环境。
  • 使用 docker-compose.yml 定义构成应用程序的服务,这样它们可以在隔离环境中一起运行。
  • 最后,执行 docker-compose up 命令来启动并运行整个应用程序。

Compose 模板文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
version: "3"
services:
web1:
image: bc-mall
hostname: server1
restart: on-failure
volumes:
- "./config/config.env:/root/config/config.env"
ports:
- "8093:8093"
network_mode: bridge

web2:
image: bc-mall
hostname: server2
restart: on-failure
volumes:
- "./config/config1.env:/root/config/config.env"
ports:
- "8094:8094"
network_mode: bridge

web3:
image: bc-mall
hostname: server3
restart: on-failure
volumes:
- "./config/config2.env:/root/config/config.env"
ports:
- "8095:8095"
network_mode: bridge

Docker-compose.yml配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
version: '3'
services:
# 第一部分: Building(构建镜像)
web:
# 使用当前目录下的Dockerfile
build: .
args: # 增加额外参数
APP_HOME: app
volumes: # 目录挂载
- .:/code
depends_on: # 依赖db和redis
- db
- redis

# 使用定制化的Dockerfile,指定新目录相对路径和文件名
build:
context: ./dir
dockerfile: Dockerfile.dev
container_name: app # 自定义容器名

# 基于现有镜像构建
image: ubuntu
image: ubuntu:14.04
image: remote-registry:4000/postgresql
image: bcbc65fd

# 第二部分: Ports(端口)
ports: # 指定端口映射,HOST:Container
- "6379" # 指定容器的端口6379,宿主机会随机映射端口
- "8080:80" # 宿主机端口8080,对应容器80

# 暴露端口给-link或处于同一网络的容器,不暴露给宿主机。
expose: ["3000"]

# 第三部分: Environment Variables(环境变量)
environment:
MODE: development
SHOW: 'true'

# 等同于
environment:
- MODE=development
- SHOW: 'true'

# 使用环境变量.env文件
env_file: .env
env_file:
- ./common.env
- ./apps/web.env

# 第四部分:commands (命令)
# 容器启动后默认执行命令
command: bundle exec thin -p 3000
command: ['/bin/bash/', 'start.sh']

# 容器启动后程序入口
entrypoint: /code/entrypoint.sh

# 第五部分:Networks(网络)
networks: # 使用bridge驱动创建名为frontend的网络
frontend:
driver: bridge

networks: # 使用创建的网络进行通信
- frontend

# 加入已经存在的外部网络
networks:
default:
external:
name: my-pre-existing-network

# 第六部分:Volumes(数据卷)
volumes: # 创建名为postgres_data的数据卷
postgres_data:

db:
image: postgres:latest
volumes:
- postgres_data:/var/lib/postgresql/data

# 第七部分:External Links(外部链接)
# 目的是让Compose能够连接那些不在docker-compose.yml中定义的单独运行容器
services:
web:
external_links:
- redis_1
- project_db_1:mysql

六、etcd

https://www.kancloud.cn/roeslys/linux/1593364

简介:Etcd 是什么

  • etcd是一个分布式可靠的键值存储,用于分布式系统的最关键数据,重点是:

1.简单:定义明确,面向用户的API(gRPC)

2.安全:具有可选客户端证书身份验证的自动TLS

3.快速:基准测试10,000次/秒

4.可靠:使用Raft一致性算法分布集群

  • etcd是用Go编写的,使用Raft一致性算法来管理高度可用的复制日志。
  • etcd被许多公司用于生产,开发团队在关键部署场景中支持它,其中etcd经常与KuberneteslocksmithvulcandDoorman等许多应用程序配合使用。通过严格的测试进一步确保可靠性。
  • 有关简单的命令行客户端,请参阅etcdctl(etcdctl –helper)

一、ETCD单机部署

1、下载二进制包

https://github.com/coreos/etcd/releases

1
wget https://github.com/etcd-io/etcd/releases/download/v3.3.12/etcd-v3.3.12-linux-arm64.tar.gz
2、解压缩
1
tar -zxvf etcd-v3.3.12-linux-arm64.tar.gz
3、设置环境变量
1
2
3
4
5
export ETCDPATH="/home/etcd/etcd-v3.3.12-linux-amd64"

export ETCDCTL_API=3

export PATH="$PATH:$ETCDPATH"
4、启动etcd
1
etcd --data-dir $ETCDPATH/test_data
5、客户端测试
1
2
3
4
5
6
7
8
etcdctl --endpoints=http://127.0.0.1:2379 put foo bar

etcdctl --endpoints=http://127.0.0.1:2379 get foo
[root@bogon /]# etcdctl --endpoints=http://127.0.0.1:2379 put foo bar
OK
[root@bogon /]# etcdctl --endpoints=http://127.0.0.1:2379 get foo
foo
bar

二、docker单机部署ETCD

1、拉取etcd镜像
1
docker pull quay.io/coreos/etcd:v3.3.9
2、设置环境变量和监听地址
1
2
3
-e ETCDCTL_API=3

-p 2379:2379 -p 2380:2380
3、运行etcd容器
1
2
3
4
5
6
7
8
9
docker run -d -it --rm \
--name etcd_test \
-e ETCDCTL_API=3 \
-p 2379:2379 \
-p 2380:2380 \
quay.io/coreos/etcd:v3.3.9 \
etcd \
--advertise-client-urls http://0.0.0.0:2379 \
--listen-client-urls http://0.0.0.0:2379

容器退出自动删除rm

1
2
[root@bogon etcd]# docker run -d -it --rm --name etcd_test -e ETCDCTL_API=3 -p 2379:2379 -p 2380:2380 quay.io/coreos/etcd:v3.3.9 etcd --advertise-client-urls http://0.0.0.0:2379 --listen-client-urls http://0.0.0.0:2379
ca444c094b6bf00b47726b0dee600620d3962cd0fb78d224db9fae12da94dc13

容器停止总是重启

1
2
[root@ansible-server ~]# docker run -d -it --restart=always --name etcd1 -e ETCDCTL_API=3 -p 2379:2379 -p 2380:2380 quay.io/coreos/etcd:v3.3.9 etcd --advertise-client-urls http://0.0.0.0:2379 --listen-client-urls http://0.0.0.0:2379
199b521dcdd1a742701350c8024dd76c179db2ded0d05a40dd44adfd14f2fb27

其他状态:

  • no,默认策略,在容器退出时不重启容器
  • on-failure,在容器非正常退出时(退出状态非0),才会重启容器
  • on-failure:3,在容器非正常退出时重启容器,最多重启3次
  • always,在容器退出时总是重启容器
  • unless-stopped,在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
1
2
3
[root@bogon etcd]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ca444c094b6b quay.io/coreos/etcd:v3.3.9 "etcd --advertise-..." 4 seconds ago Up 3 seconds 0.0.0.0:2379-2380->2379-2380/tcp etcd_test
4、客户端测试
1
2
3
4
5
6
7
8
9
10
11
12
13
etcdctl --endpoints=http://127.0.0.1:2379 put foo bar

etcdctl --endpoints=http://127.0.0.1:2379 get foo
[root@bogon etcd]# etcdctl --endpoints=http://127.0.0.1:2379 put foo bar
OK
[root@bogon etcd]# etcdctl --endpoints=http://127.0.0.1:2379 get foo
foo
bar
[root@bogon etcd]# etcdctl --endpoints=http://127.0.0.1:2379 put foo bar1
OK
[root@bogon etcd]# etcdctl --endpoints=http://127.0.0.1:2379 get foo
foo
bar1

三、Etcd本地集群(使用goreman管理)

1、下载etcd二进制包&&设置环境变量&&参考Etcd单机部署

2、安装goreman,安装goreman前需要安装go环境和git。

3、安装go环境

1
2
3
4
5
6
7
8
9
10
wget https://studygolang.com/dl/golang/go1.10.1.linux-amd64.tar.gz

tar -xvf go1.10.1.linux-amd64.tar.gz

vim /etc/profile

export GOROOT=/home/go/go
export GOPATH=/home/go/data
export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
source /etc/profile

4、安装git

1
yum -y install git

5、安装 goreman

1
go get github.com/mattn/goreman

下载后的文件在export GOPATH=/home/go/data设置的路径中

6、编写Procfile(管理集群)

https://github.com/coreos/etcd/blob/master/Procfile

7、启动etcd(goreman runlist)

1
goreman -f Procfile start

四、docker cluster(集群)

8、etcd集群

1
docker pull busybox
docker netword
1
2
3
4
5
6
7
8
9
10
11
#docker network create etcd_cluster

docker network create --subnet 172.16.3.0/16 etcd_cluster

docker network inspect etcd_cluster

#docker network rm etcd_cluster

docker run -itd --rm --network etcd_cluster --ip 172.16.3.0 --name test busybox
docker network ls
docker network rm etcd_cluster

9、etcd cluster

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# etcd1
docker run -itd --restart=always \
--network etcd_cluster \
--ip 172.16.3.31 \
--hostname etcd1 \
--name etcd1 \
-e ETCDCTL_API=3 \
-p 12379:2379 \
-p 12380:2380 \
quay.io/coreos/etcd:v3.3.9 \
etcd --name etcd1 \
--initial-advertise-peer-urls http://172.16.3.31:2380 \
--listen-peer-urls http://172.16.3.31:2380 \
--listen-client-urls http://172.16.3.31:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://172.16.3.31:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster etcd1=http://172.16.3.31:2380,etcd2=http://172.16.3.32:2380,etcd3=http://172.16.3.33:2380 \
--initial-cluster-state new

# etcd2
docker run -itd --restart=always \
--network etcd_cluster \
--ip 172.16.3.32 \
--hostname etcd2 \
--name etcd2 \
-e ETCDCTL_API=3 \
-p 22379:2379 \
-p 22380:2380 \
quay.io/coreos/etcd:v3.3.9 \
etcd --name etcd2 \
--initial-advertise-peer-urls http://172.16.3.32:2380 \
--listen-peer-urls http://172.16.3.32:2380 \
--listen-client-urls http://172.16.3.32:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://172.16.3.32:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster etcd1=http://172.16.3.31:2380,etcd2=http://172.16.3.32:2380,etcd3=http://172.16.3.33:2380 \
--initial-cluster-state new

# etcd3
docker run -itd --restart=always \
--network etcd_cluster \
--ip 172.16.3.33 \
--hostname etcd3 \
--name etcd3 \
-e ETCDCTL_API=3 \
-p 32379:2379 \
-p 32380:2380 \
quay.io/coreos/etcd:v3.3.9 \
etcd --name etcd3 \
--initial-advertise-peer-urls http://172.16.3.33:2380 \
--listen-peer-urls http://172.16.3.33:2380 \
--listen-client-urls http://172.16.3.33:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://172.16.3.33:2379 \
--initial-cluster-token etcd-cluster-1 \
--initial-cluster etcd1=http://172.16.3.31:2380,etcd2=http://172.16.3.32:2380,etcd3=http://172.16.3.33:2380 \
--initial-cluster-state new

# client
#进入容器
docker exec -it etcd1 bin/sh
#客户端测试
etcdctl --write-out=table --endpoints=http://127.0.0.1:2379 member list
etcdctl --endpoints=http://127.0.0.1:2379 put foo bar
etcdctl --endpoints=http://127.0.0.1:2379 get foo

#测试结果如下
[root@bogon ~]# docker exec -it etcd1 bin/sh
/ # etcdctl --write-out=table --endpoints=http://127.0.0.1:2379 member list
+------------------+---------+-------+-------------------------+-------------------------+
| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |
+------------------+---------+-------+-------------------------+-------------------------+
| c26d6ba798c079c | started | etcd3 | http://172.16.3.33:2380 | http://172.16.3.33:2379 |
| 4631df2115e1ef72 | started | etcd2 | http://172.16.3.32:2380 | http://172.16.3.32:2379 |
| a92fe5422902bc40 | started | etcd1 | http://172.16.3.31:2380 | http://172.16.3.31:2379 |
+------------------+---------+-------+-------------------------+-------------------------+
/ # etcdctl --endpoints=http://127.0.0.1:2379 put foo bar
OK
/ # etcdctl --endpoints=http://127.0.0.1:2379 get foo
foo
bar
#docker ps
[root@bogon docker]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5988c4443eb5 quay.io/coreos/etcd:v3.3.9 "etcd --name etcd3..." 37 seconds ago Up 36 seconds 0.0.0.0:32379->2379/tcp, 0.0.0.0:32380->2380/tcp etcd3
d4fdda6400cc quay.io/coreos/etcd:v3.3.9 "etcd --name etcd2..." 44 seconds ago Up 44 seconds 0.0.0.0:22379->2379/tcp, 0.0.0.0:22380->2380/tcp etcd2
af86e5fd2ae5 quay.io/coreos/etcd:v3.3.9 "etcd --name etcd1..." 53 seconds ago Up 52 seconds 0.0.0.0:2379-2380->2379-2380/tcp etcd1

参考