Docker 学习笔记

什么是容器?

  • 一种虚拟化的方案
  • 操作系统级别的虚拟化
  • 只能运行相同或相似内核的操作系统
  • 依赖于 Linux 内核特性:Namespace 和 Cgroups

  更多介绍可以参考 Docker 官网说明:什么是容器?

什么是 Docker?

  Docker 是基于 Go 语言实现的开源容器项目,是开发人员和系统管理员使用容器开发,部署和运行应用程序的平台。

为什么使用 Dokcer?

  举个简单的例子,假设用户试图基于最常见的 LAMP(Linu+xApache +MySQL+PHP)组合来构建一个网站。
  按照传统的做法,首先,需要安装ApacheMySQLPHP以及它们各自运行所依赖的环境;之后分别对它们进行配置(包括创建合适的用户、配置参数等);经过大量的操作后,还需要进行功能测试,查看是否工作正常;若不正常,则进行调试追踪,这将耗费更多的时间并带来不可控的风险。
  可以想象,若应用数目变多,事情会变得更加难以处理。
  更为可怕的是,一旦需要服务器迁移(例如从亚马逊云迁移到其他云),则往往需要对每个应用都进行重新部署和调试。
  这些琐碎而无趣的“体力活”,极大地降低了工作效率
  究其根源,是这些应用直接运行在底层操作系统上,无法保证同一份应用在不同的环境中行为一致。
  而 Docker 提供了一种更为聪明的方式,通过容器来打包应用,解耦应用和运行平台。意味着迁移的时候,只需要在新的服务器上启动需要的容器就可以了,无论新旧服务器是否是同一类型的平台。这无疑将节约大量的宝贵 时间,并降低部署过程出现问题的风险。

  通过 Docker 可以很轻松地部署应用程序,这意味着:

  • 更快的项目部署速度
  • 更高效的开发生产力
  • 更低的 IT 基础设施成本
  • 更快速高效的开发生命周期

  更多介绍请参考 Docker 官网:为什么使用 Docker ?

容器和虚拟机的比较

  一个容器中运行原生 的 Linux 并且共享主机与其它容器的内核。它运行一个独立的进程,不占用任何其他可执行文件的内存,这使得它非常轻量级。

  相比之下,虚拟机(VM)运行一个完整的“客户”操作系统,通过虚拟机管理程序对主机资源进行虚拟访问。通常,VM提供的环境比大多数应用程序需要的资源更多,如下是容器 VM 的比较:


容器堆栈示例

虚拟机堆栈示例

  容器和虚拟机的性能对比如下表:

特性 容器 虚拟机
启动速度 秒级 分钟级
性能 接近原生 较弱
内存代价 很小 较多
磁盘使用 一般为 MB 一般为 GB
运行密度 单机支持上千个容器 一般几十个
隔离性 安全隔离 完全隔离
迁移性 优秀 一般

Docker 应用场景

  • 使用 Docker 容器开发、测试、部署服务
  • 创建隔离的运行环境
  • 搭建测试环境
  • 构建多用户的平台即服务(PaaS)基础设施
  • 提供软件即服务(SaaS)应用程序
  • 高性能、超大规模的宿主机部署

Docker 三大核心概念

  Docker 使用客户端-服务器 ( C/S ) 架构模式,使用远程 API 来管理和创建 Docker 容器。
  Docker 镜像可以用来创建 Docker 容器。
  镜像与容器关系类似于面向对象编程中的对象
  Docker 的大部分操作都围绕着它的三大核心概念——镜像、容器和仓库而展开,下面将分别介绍:

Docker Image (镜像)

  在 Docker 中,可以通过获取的镜像来启动容器。
  一个镜像即一个可执行的包,其中包括运行应用程序所需所有内容的代码,运行时间,库,环境变量和配置文件。

  镜像是创建 Docker 容器的基础。

  通过版本管理和增量的文件系统,Docker 提供了一套十分简单的机制来创建和更新现有的镜像,用户可以从网上下载一个已经做好的应用镜像直接使用。

Docker Container (容器)

  一个容器是一个镜像的运行时实例
  换个角度来看,镜像是静态的只读文件,而容器则带有运行时需要的可写文件层。
  若认为虚拟机是模拟运行的一整套操作系统(包括内核、应用运行态环境和其他系统环境)和跑在上面的应用,那么 Docker 容器就是独立运行的一个(或一组)应用,以及它们必需的运行环境。
  当镜像被执行时将会进入内存(这代表一个状态的镜像或一个用户进程)。

  可以使用docker ps命令查看正在运行的容器列表。

Docker Registry (仓库)

   Docker 仓库类似于代码仓库,它是 Docker 集中存放镜像文件的场所。
  根据所存储的镜像公开分享与否, Docker 仓库可以分为:

  • 公开仓库 ( Public )
  • 私有仓库 ( Private )

  目前,最大的公开仓库是官方提供的 Docker Hub ,其中存放了数量庞大的镜像供用户下载。
  当然,用户若不希望公开分享自己的镜像文件, Docker 也允许用户在本地网络内创建一个只能自己访问的私有仓库。
  当用户创建了自己的镜像之后可以使用 push 命令将它上传到指定的公有或者私有仓库。
  这样用户下次在另外一台机器上使用该镜像时,只需要将其从仓库上 pull 下来即可使用。

安装:CentOS 7一键安装 Docker 脚本

  本人服务器使用的是 Centos 7 ,,所以将 Docker 安装命令写成一键安装脚本,默认安装最新稳定版
  首先在命令行输入:

1
vim dockerInstall.sh

  之后将下面代码复制进去:

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
#!/bin/bash
# 移除掉旧的版本
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

# 删除所有旧的数据
sudo rm -rf /var/lib/docker

# 安装依赖包: yum-utils 提供了 yum-config-manager 效用,而 device-mapper-persistent-data 和 lvm2 被 devicemapper 存储驱动程序需要。
sudo yum install -y yum-utils \
device-mapper-persistent-data \
lvm2

# 添加源,使用阿里云镜像下载速度更快
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 配置缓存
sudo yum makecache fast

# 安装最新版本的Docker CE和containerd,或者转到下一步安装特定版本:
sudo yum install docker-ce docker-ce-cli containerd.io

# 配置镜像加速器
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
EOF

# 启动 docker 并设置开机启动
sudo systemctl start docker
sudo systemctl enable docker
# 增加用户组 docker
sudo groupadd docker

# root 加入 docker 用户组
sudo gpasswd -a root docker
# 当前用户加入 docker 用户组,使当前用户拥有执行权限
sudo gpasswd -a $USER docker

sudo chgrp docker /var/run/docker.pid
sudo chgrp docker /var/run/docker.sock
sudo chgrp -R docker /var/run/docker

# 重启 docker
sudo systemctl restart docker

  之后执行该文件即可:

1
sh dockerInstall.sh

注意:国内访问 Docker 太慢,一般会配置加速器,此处配置的加速器是 163 的加速器:

1
http://hub-mirror.c.163.com

  当然也可以配置阿里云的加速器(推荐哦!)
  安装过程中若出现提示,一直回车或输入y即可。

权限问题

  非 root 用户运行 docker 命令报如下错误:

1
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/search?limit=25&term=redis": dial unix /var/run/docker.sock: connect: permission denied

  通过将用户添加到 docker 用户组可以将 sudo 去掉:

1
2
3
4
5
6
# 添加 docker 用户组
sudo groupadd docker
# 将登陆用户加入到 docker 用户组中
sudo gpasswd -a $USER docker
# 更新用户组
newgrp docker

测试 Docker 版本

  运行docker --version并确保您拥有受支持的Docker版本:

1
2
3
docker --version

Docker version 17.12.0-ce, build c97c6d6

  运行docker info以查看有关Docker安装的更多详细信息:

1
2
3
4
5
6
7
8
9
10
docker info

Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 17.12.0-ce
Storage Driver: overlay2
...

测试 Docker 安装是否有效

①通过运行简单的 Docker 镜像hello-world测试您的安装是否有效 :

1
2
3
4
5
6
7
8
9
10
11
$ docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:41a65640635299bab090f783209c1e3a3f11934cf7756b09cb2f1e02147c6ed8
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

②列出下载到您的计算机的镜像如hello-world

1
2
3
4
docker image ls

REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest fce289e99eb9 6 months ago 1.84kB

③列出hello-world在显示其消息后退出的容器(由镜像生成)。若它仍在运行,您将不需要--all选项:

1
2
3
4
docker container ls --all

CONTAINER ID IMAGE COMMAND CREATED STATUS
54f4984ed6a8 hello-world "/hello" 20 seconds ago Exited (0) 19 seconds ago

配置 Docker 服务

  为了避免每次使用 Docker 命令都要用特权身份,可以将当前用户加入安装中自动创建的 Docker 用户组:

1
$ sudo usermod -aG docker USER_NAME

Docker 容器操作

使用 Docker 镜像

  Docker 运行容器前需要本地存在对应的镜像,若镜像没保存在本地, Docker 会尝试先从默认镜像仓库下载(默认使用 Docker Hub公共注册服务器中的仓库),用户亦可通过配置来使用自定义的镜像仓库。

搜寻镜像

  使用docker search命令可以搜索远端仓库中共享的镜像,默认搜索官方仓库中的镜像。
  比如搜索mysql的镜像时:

1
2
3
4
5
6
7
8
9
docker search mysql      
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 8330 [OK]
mariadb MariaDB is a community-developed fork of MyS… 2850 [OK]
mysql/mysql-server Optimized MySQL Server Docker images. Create… 620 [OK]
percona Percona Server is a fork of the MySQL relati… 438 [OK]
centurylink/mysql Image containing mysql. Optimized to be link… 60 [OK]
centos/mysql-57-centos7 MySQL 5.7 SQL database server 56
...

  可以看到返回了很多包含关键字的镜像,其中包括:

  • 镜像名字
  • 描述
  • 星级(表示该镜像的受欢迎程度))
  • 是否由官方创建
  • 是否自动创建等

:默认的输出结果将按照星级评价进行排序。

获取镜像

  搜寻到想要的镜像后,自然是拉取下来啦!
  可以使用docker pull命令直接从 Docker Hub 镜像源来下载镜像。该命令的格式为:

1
docker pull [NAME] [TAG]

  其中,2 个参数的说明:

  • NAME:镜像仓库的名称(用来区分镜像)
  • TAG:镜像的标签(往往用来表示版本信息)。若不显式指定,默认选择latest标签,这将下载仓库中最新版本的镜像。因为最新版意味着非稳定版,从稳定性上考虑,在生产环境中应该指定TAG

  下面我们举个栗子说明一下,从 Docker Hub 拉取个最新的ubuntu镜像:

1
2
3
4
5
6
7
8
9
$ docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
5b7339215d1d: Pull complete
14ca88e9f672: Pull complete
a31c3b1caad4: Pull complete
b054a26005b7: Pull complete
Digest: sha256:9b1702dcfe32c873a770a32cfd306dd7fc1c4fd134adfb783db68defc8894b3c
Status: Downloaded newer image for ubuntu:latest

  该命令实际上下载的就是ubuntu:latest镜像.
  下载过程中可以看出,镜像文件一般由若干层(layer)组 成,6c953ac5d795这样的串是层的唯一id(实际上完整的id包括256比特,由64个十六进制字符组成)。  docker pull命令下载时会获取并输出镜像的各层信息。当不同的镜像包括相同的层时,本地仅存储层的一份内容,以减小需要的存储空间。

:严格地讲,镜像的仓库名称中还应该添加仓库地址(即registry,注册服务器)作为前缀,只是我们默认使用的是 Docker Hub 服务,该前缀可以忽略。
  例如,docker pull ubuntu命令相当于docker pull registry.hub.docker.com/ubuntu命令,即从默认的注册服务器 Docker Hub Registry 中的 ubuntu 仓库来下载标记为 14.04 的镜像。

  若从非官方的仓库下载,则需要在仓库名称前指定完整的仓库地址。
  下载镜像到本地后,即可随时使用该镜像了。

查看镜像信息

列出镜像命令—— images

  docker images命令可以列出本地主机上已有镜像的基本信息。

1
2
3
4
5
$ docker images           
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 16.04 13c9f1285025 11 days ago 119MB
ubuntu latest 4c108a37151f 11 days ago 64.2MB
mysql latest c7109f74d339 2 weeks ago 443MB

  在列出的信息中,可以看到以下几个字段信息:

  • REPOSITORY:来自于哪个仓库,比如ubuntu仓库用来保存ubuntu系列的基础镜像;
  • TAG:镜像的标签信息,比如16.04、latest用来标注不同的版本信息。标签只是标记,并不能标识镜像内容;
  • IMAGE ID:镜像的 ID(唯一标识镜像),如4c108a37151f标识为ununtu镜像;
  • CREATED:创建时间,说明镜像最后的更新时间;
  • SIZE:镜像大小,优秀的镜像往往体积都较小。

  其中镜像的 ID 信息十分重要,它唯一标识了镜像。
  在使用镜像 ID 的时候,一般可以使用该 ID 的前若干个字符组成的可区分串来替代完整的ID。
  TAG信息用来标记来自同一个仓库的不同镜像。例如ubuntu仓库中有多个镜像,通过 TAG 信息来区分发行版本,包括10.04、12.04、12.10、13.04、14.04、16.04等标签。

添加镜像标签命令—— tag

  为了方便在后续工作中使用特定镜像,还可以使用docker tag命令来为本地镜像任意添加新的标签。例如添加一个新的myubuntu:latest镜像标签:

1
2
3
4
5
6
7
$ docker tag ubuntu:latest myubuntu:latest
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 16.04 13c9f1285025 11 days ago 119MB
myubuntu latest 4c108a37151f 11 days ago 64.2MB
ubuntu latest 4c108a37151f 11 days ago 64.2MB
mysql latest c7109f74d339 2 weeks ago 443MB

  再次使用docker images列出本地主机上镜像信息,可以看到多了一个拥有myubuntu:latest标签的镜像.之后,用户就可以直接使用myubuntu:latest来表示这个镜像了。
  细心的读者可能注意到,这些myubuntu:latest镜像的 ID 跟ubuntu:latest完全一致。它们实际上指向同一个镜像文件,只是别名不同而已。
  换而言之,docker tag命令添加的标签实际上起到了类似链接的作用。

查看详细信息命令—— inspect

  docker inspect命令可以获取该镜像的详细信息,包括制作者、适应架构、各层的数字摘要,容器 IP 地址等,这将返回一个 JSON 格式的消息:

1
2
3
4
5
6
7
8
9
$ docker inspect mysql
[
{
"Id": "sha256:c7109f74d339896c8e1a7526224f10a3197e7baf674ff03acbab387aa027882a",
"RepoTags": [
"mysql:latest"
],
"RepoDigests": [
...省略...

查看镜像历史命令—— history

  既然镜像文件由多个层组成,那么怎么知道各个层的内容具体是什么呢?
  这时候可以使用history子命令,该命令将列出各层的创建信息。
  例如,查看mysql镜像的创建过程,可以使用如下命令:

1
2
3
4
5
6
7
$ docker history mysql
IMAGE CREATED CREATED BY SIZE COMMENT
c7109f74d339 2 weeks ago /bin/sh -c #(nop) CMD ["mysqld"] 0B
<missing> 2 weeks ago /bin/sh -c #(nop) EXPOSE 3306 33060 0B
<missing> 2 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B
<missing> 2 weeks ago /bin/sh -c ln -s usr/local/bin/docker-entryp… 34B
...

删除镜像

  使用docker rmi命令可以删除镜像,命令格式为

1
docker rmi IMAGE[IMAGE...]

  其中[IMAGE]可以为镜像标签或镜像 ID。

镜像标签删除

  例如,要删除掉myubuntu:latest镜像,即标签为latestmyubuntu镜像可以使用如下命令:

1
2
$ docker rmi myubuntu:latest
Untagged: myubuntu:latest

  读者可能会担心,本地的ubuntu:latest镜像是否会受此命令的影响。无需担心,当同一个镜像拥有多个标签的时候,docker rmi命令只是删除该镜像多个标签中的指定标签而已,并不影响镜像文件。因此上述操作相当于只是删除了镜像4c108a37151f的一个标签而已。
  但当镜像只剩下一个标签的时候就要小心了,此时再使用docker rmi命令会彻底删除镜像。

镜像 ID 删除

  当使用docker rmi命令,并且后面跟上镜像的 ID (也可以是能进行区分的部分 ID 串前缀)时,会先尝试删除所有指向该镜像的标签,然后删除该镜像文件本身
  下面为删除镜像 ID 为4c108a37151fubuntu镜像,会先删除所有指向该镜像的标签,然后删除该镜像文件本身:

1
2
3
4
5
6
7
8
9
$ docker rmi ubuntu 4c108a37151f
Untagged: ubuntu:latest
Untagged: ubuntu@sha256:9b1702dcfe32c873a770a32cfd306dd7fc1c4fd134adfb783db68defc8894b3c
Untagged: myubbuntu:latest
Deleted: sha256:4c108a37151f54439950335c409802e948883e00c93fdb751d206c9a9674c1f6
Deleted: sha256:7c1abf1dbbfd02a48330a7317ab45a6091d53e2e9cc062f0f3dbd2b7539947a6
Deleted: sha256:5a614dda4a54650168ee2cd30ce2e39576dad5c9a0d1907c02445687b4ea5090
Deleted: sha256:bd042113a73a5c9c6680990740446b7324afb39e243ade3d33bdaa9ffaf8d294
Deleted: sha256:ba9de9d8475e7f5e40086358a1353b3cc080994fc6d31e4272dd3acb69b0151e

注意:当有该镜像创建的容器存在时,镜像文件默认是无法被删除的,此时 Docker 会提示有容器正在运行,无法删除,但若要想强行删除镜像,可以使用- f参数。

1
$ docker rmi -f ubuntu:latest

  通常并不推荐使用-f参数来强制删除一个存在容器依赖的镜像。 正确的做法是,先删除依赖该镜像的所有容器,再来删除镜像。

存出和载入镜像、上传镜像暂略

使用 Docker 容器

新建容器

  docker create [commond] [image]命令可以新建一个容器,例如:

1
2
$ docker create -it ubuntu:latest
5bbc639c21aab99607cb60a92e86b18ccdc47d793f52c1c590aa49d0268dfe2a

  当命令执行完之后,会返回一条字符串,这代表创建容器的 ID。
  create命令和后续的run命令支持的选项参数都十分复杂,主要包括如下几大类:与容器运行模式相关、与容器和环境配置相关、与容器资源限制和安全保护相关。
  前面的-it参数指的是创建交互式容器:

  • -i --interactive=true|false:表示运行容器,即告诉 Docker 守护进程始终打开标准输入,默认是false
  • -t --tty = true|false:表示容器启动后会进入命令行,即为容器分配一个 tty 终端, 默认是false

启动容器

  新建的容器处于停止状态,可用docker start [容器ID]命令来启动它:

1
2
$ docker start 5bbc639c21aab99607cb60a92e86b18ccdc47d793f52c1c590aa49d0268dfe2a
5bbc639c21aab99607cb60a92e86b18ccdc47d793f52c1c590aa49d0268dfe2a

  当然,可以简写:

1
2
$ docker start 5bbc639c
5bbc639c21aab99607cb60a92e86b18ccdc47d793f52c1c590aa49d0268dfe2a

查看容器状态

  docker ps [OPTIONS]命令可用来查看容器状态信息:

1
2
3
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5bbc639c21aa ubuntu:latest "/bin/bash" 3 hours ago Up 4 seconds angry_proskuriakova

  该命令后面可以添加参数,不同的参数意义不同:

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

新建并启动容器

  除了创建容器后再通过start命令来启动他,也可以直接新建并启动容器。
  相关命令为docker run,等价于先执行docker create命令,再执行docker start命令。

1
docker run -it ubuntu:16.04

  其中,参数作用:

  • -d:创建一个守护式容器再后台运行(这样创建容器后不会自动登录容器,若加-it参数,创建后就会自动进入容器)
  • -i:表示运行容器,即让容器的标准输入保持打开
  • -t:表示容器启动后会进入命令行,即分配一个伪终端并绑定到容器的标准输入上
  • -p:表示端口映射,如-p 33306:3306,前者是宿主机端口,后者是容器内的映射端口,可以做多个映射
  • -it加起来即让运行的容器实现”对话”的能力,上面命令即运行交互式的容器

  执行该命令时, Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  • 利用镜像创建一个容器,并启动该容器
  • 分配一个文件系统给容器,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中
  • 从网桥的地址池配置一个 IP 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被自动终止

自定义容器名

  有时候我们想要给创建的容器定义一个名字以方便后续操作,可以使用如下命令:

1
docker run --name=自定义名 -it [IMAGE]

  下面为一个示例:

1
2
3
4
5
6
7
8
9
$ docker run --name=myubuntu -it ubuntu
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
30391c72e826 ubuntu "/bin/bash" 9 seconds ago Up 8 seconds myubuntu
$ docker attach myubuntu
root@30391c72e826:/# exit
exit
$ docker rm myubuntu
myubuntu

守护式容器

  守护式容器指的是退出容器时让容器可以一直挂在后台,能够长期运行,没有交互式会话,适合运行应用程序和服务
  当我们通过命令启动交互式容器后,通过ctrl+p ctrp+q退出容器即可将其挂在后台。

终止容器

  docker stop命令可用来终止一个运行中的容器:

1
2
$ docker stop 5bbc639c21aa
5bbc639c21aa

  该命令首先向容器发送SIGTERM信号,等待一段超时时间(默认为 10 秒)后, 再发送SIGKILL信号来终止容器。
  也可使用docker kill命令会直接发送 SIGKILL 信号来强行终止容器(不推荐):

1
2
$ docker kill 5bbc639c21aa
5bbc639c21aa

  此外,当 Docker 容器中指定的应用终结时,容器也会自动终止。
  例如启动了一个终端的容器,用户通过exit命令或ctrl+d来退出终端时,所创建的容器立刻终止,处于stopped状态。

重启容器

  docker restart命令可以将一个运行态的容器先终止,然后再重新启动:

1
2
$ docker restart 5bbc639c21aa
5bbc639c21aa

进入容器内执行操作

  在使用-d参数时,容器启动后会进入后台,用户无法看到容器中的信息,也无法进行操作。
  此时若需进入容器进行操作,有多种方法,包括使用官方的attachexec命令,以及第三方的nsenter工具等。

attach 命令(了解)

  attach是 Docker 自带的命令,命令格式为:

1
docker attach [--detach-keys[=[]]] [--no-stdin] [--sig-proxy[=true]] CONTAINER

  支持三个主要选项(一般默认即可,即不加参数选项):

  • --detach-keys[=[]]:指定退出attach模式的快捷键序列,默认是CTRL+p+CTRL+q;
  • --no-stdin=true|false:是否关闭标准输入,默认是保持打开;
  • --sig-proxy=true|false:是否代理收到的系统信号给应用进程,默认为 true。

  下面为运行ubuntu容器并进入该容器的代码:

1
2
3
4
$ docker run -dit ubuntu
8b18614a220bbab7906204c5031ba73c66ff32e9fd277aed7219ec0ff5cec55b
$ docker attach 8b18
root@8b18614a220b:/#

  但是使用attach命令有时候并不方便。
  当多个窗口同时用attach命令连到同一个容器的时候,所有窗口都会同步显示。当某个窗口因命令阻塞时,其他窗口也无法执行操作了。

exec 命令(推荐)

  Docker 从 1.3.0 版本起提供了一个更加方便的 exec 命令,可以在容器内直接执行任意命令。该命令的基本格式为:

1
docker exec [-d|--detach] [--detach-keys[=[]]] [-i|--interactive] [--privileged] [-t|--tty] [-u|--user[=USER]] CONTAINER COMMAND [ARG...]。

  比较重要的参数有:

  • -i,--interactive=true|false:打开标准输入接受用户输入命令,默认为false;
  • -t,--tty=true|false:分配伪终端,默认为false;
  • --privileged=true|false:是否给执行命令以高权限,默认为false;
  • -u,--user="":执行命令的用户名或ID。

  下面为一个例子,以后台模式创建并启动一个交互式容器,并启动一个bash

1
2
3
4
$ docker run -dit ubuntu
5a35b974b0f055e764f1af83eb992ae8bfbc643026376f5c9da76793cf1b6f79
$ docker exec -it 5a35 /bin/bash
root@5a35b974b0f0:/#

  可以看到,一个 bash 终端打开了,在不影响容器内其他应用的前提下, 用户可以很容易与容器进行交互。
  通过指定-d参数以后台模式来创建并启动容器,通过指定-it参数来保持标准输入打开,并且分配一个伪终端

删除容器

  可以使用docker rm命令来删除处于终止或退出状态的容器,命令格式为:

1
docker rm[-f|--force][-l|--link][-v|--volumes]CONTAINER[CONTAINER...]。

  主要支持的选项包括:

  • -f,--force=false:是否强行终止并删除一个运行中的容器;
  • -l,--link=false:删除容器的连接,但保留容器;
  • -v,--volumes=false:删除容器挂载的数据卷。
      例如,查看处于终止状态的容器,并删除:
    1
    2
    3
    4
    5
    6
    7
    docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    5a35b974b0f0 ubuntu "/bin/bash" 8 minutes ago Exited (0) 17 seconds ago goofy_goldstine
    a60c99b35d7a ubuntu "/bin/bash" 9 minutes ago Up 9 minutes admiring_edison
    dd4bd9645748 ubuntu "/bin/bash" 10 minutes ago Exited (0) 9 minutes ago suspicious_einstein
    $ docker rm 5a35
    5a35

  默认情况下,docker rm命令只能删除处于终止或退出状态的容器,并不能删除还处于运行状态的容器。
  若要直接删除一个运行中的容器,可以添加-f参数。此种做法不推荐,一般应该先停止容器之后再删除容器。

  若想要删除 Docker 中所有已经停止的容器,可以使用如下命令:

1
docker container prune

文件拷贝出入容器

  若我们需要将文件拷贝到容器内,则可以使用 cp 命令:

1
2
3
4
5
6
7
$ docker run -dit --name=mycentos centos:7
f041ef8a57623dcb45bee932d25e908e9eb93edcc00b0cb2c94fb292563f3ed1
$ docker cp abc.txt mycentos:/usr/local
$ docker exec -it mycentos /bin/bash
[root@f041ef8a5762 /]# cd /usr/local/
[root@f041ef8a5762 local]# dir
abc.txt bin etc games include lib lib64 libexec sbin share src

  也可以将文件从容器内拷贝出来到当前目录:

1
$ docker cp mycentos:/root/anaconda-ks.cfg test_cp.cfg

目录挂载

  我们可以在创建容器的时候,将宿主机的目录与容器内的目录进行映射,这样我们就可以通过修改宿主机某个目录的文件从而去影响容器。
  创建容器时添加-v参数,后面紧跟宿主机目录:容器目录(宿主机目录的路径必须为绝对路径),如:

1
docker run -dit --name=mycentos -v /usr/local/myhtml:/usr/local/myhtml centos:7

  当在宿主机的myhtml目录创建文件后,容器中的myhtml目录也会多出那个文件。

  注意:请先将这两个目录保持同步。若你共享的是多级的目录,可能会出现权限不足的提示。因为 Centos 7 中的安全模块把权限禁掉了,需要添加参数--privileged=true来解决这个问题。

导出和导入容器

  某些时候,需要将容器从一个系统迁移到另外一个系统,此时可以使用 Docker 的导入和导出功能。

导出容器

  导出容器是指导出一个已经创建的容器到一个文件,无论当前该容器是否处于运行状态。
  可以使用docker export命令,该命令的格式为:

1
docker export[-o|--output[=""]]CONTAINER

  其中,可以通过-o选项来指定导出的tar文件名,也可以直接通过重定向来实现。
  首先查看所有的容器(一个是运行状态,一个是停止状态):

1
2
3
4
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
53a6205630a6 ubuntu "/bin/bash" About a minute ago Exited (0) 5 seconds ago stoic_kowalevski
4e366ed1e39f ubuntu "/bin/bash" About a minute ago Up About a minute amazing_lewin

  分别导出4e366ed1e39f容器和53a6205630a6容器到文件test_for_run.tar文件和test_for_stop.tar文件(使用了不同的方式导出):

1
2
3
4
5
$ docker export -o test_for_run.tar 4e3
$ docker export 53a > test_for_stop.tar
$ ll
-rw------- 1 wksky staff 63M 6 30 21:54 test_for_run.tar
-rw-r--r-- 1 wksky staff 63M 6 30 21:54 test_for_stop.tar

  之后,可将导出的 tar 文件传输到其他机器上,然后再通过导入命令导入到系统中,从而实现容器的迁移。

导入容器

  导出的文件使用docker import命令导入后又将变成镜像,该命令格式为:

1
2
docker import [-c|--change[=[]]] [-m|--message[=MESSAGE]] file|URL|-[REPOSITORY
[:TAG]]

  下面将导出的test_for_run.tar文件导入到系统中:

1
2
3
4
5
$ docker import test_for_run.tar - test/ubuntu:v1.0
sha256:9d37a6082e97aec4c3239c67f615f31849d072bcd9dc25d733621854983267fa
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB

查看容器日志

  若想要获取容器的输出信息(日志),可以如下使用如下命令:

1
docker logs container_name

Docker 定制镜像

  Dockerfile 是一个文本格式的配置文件,用户可以使用 Dockerfile 来快速创建自定义的镜像。

基本结构

  Dockerfile 由一行行命令语句组成,并且支持以#开头的注释行。
  一般而言,Dockerfile 分为四部分:

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

  下面介绍一些比较常见的命令。

FROM

  FROM 指定所创建镜像的基础镜像,若本地不存在,则默认会去 Docker Hub 下载指定镜像。
  FROM 语法格式如下:

1
2
3
4
# 3 种写法
FROM image
FROM image:tag
FROM image@digest

MAINTAINER

  MAINTAINER 指定维护者信息,格式如下:

1
MAINTAINER image_author@docker.com

  该信息会写入生成镜像的 Author 属性域中。

WORKDIR

  WORKDIR 为后续的 RUN、CMD 和 ENTRYPOINT 指令配置工作目录。

  可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如:

1
2
3
4
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

  则最终路径为 /a/b/c

EXPOSE

  EXPOSE 用于声明镜像内服务所监听的端口。比如:

1
2
3
EXPOSE 3306
# 允许同时声明多个
EXPOSE 3306 8080

  注意,该指令只是起到声明作用,并不会自动完成端口映射。
  在启动容器时需要使用-P,Docker 主机会自动分配一个宿主机的临时端口转发到指定的端口;使用-p则可以具体指定哪个宿主机的本地端口会映射过来。

ENV

  ENV 指定环境变量,在镜像生成过程中会被后续 RUN 指令使用,在镜像启动的容器中也会存在。
  示例代码如下:

1
2
ENV JAVA_HOME /user/local/openjdk-8
ENV PATH $JAVA_HOME/bin/bin:$PATH

ADD

  格式为 ADD <src> <dest>
  该命令将复制指定的<src>到容器中的<dest>。其中<src>可以是Dockerfile所在目录的一个相对路径(文件或目录);也可以是一个 URL;还可以是一个tar文件(自动解压为目录)。

COPY

  COPY 复制本地主机的(Dockerfile 所在目录的相对路径)文件或目
录下的内容到镜像中的目标路径下。
  若目标路径不存在时,会自动创建。路径同样支持正则格式。当使用本地目录为源目录时,推荐使用 COPY。

RUN

  RUN 运行指定命令,比如:

1
2
3
RUN apt-get update \
&& apt-get install -y libsnappy-dev zlib1g-dev libbz2-dev \
&& rm -rf /var/cache/apt

注意哦:该命令可以多次使用,每次使用都会构建一层镜像,推荐合并它。

CMD

  CMD 指令用来指定启动容器时默认执行的命令。
  举个栗子, tomcat 容器启动时应该执行startup.sh来启动 tomcat

注意哦:每个 Dockerfile 只能有一条 CMD 命令,若指定了多条,只有最后一条会被执行。

ENTRYPOINT

  有两种格式:

  • ENTRYPOINT ["executable", "param1", "param2"]
  • ENTRYPOINT command param1 param2 ( shell中执行)

  配置容器启动后执行的命令,并且不可被docker run提供的参数覆盖。
  注意:每个Dockerfile中只能有一个ENTRYPOINT,当指定多个ENTRYPOINT时,只有最后一个生效。

创建镜像

  下面我们编写一个基于jdk8镜像上打一个 Java 应用包的 Dockerfile 文件:

1
2
3
4
5
6
7
8
FROM java:8-alpine
ADD start.sh start.sh
ADD target/*.jar /app.jar
RUN chmod +x start.sh
ENV LANG=C.UTF-8
ENV TZ=Asia/Shanghai
EXPOSE 8080
ENTRYPOINT ["sh", "./start.sh"]

  编写完 Dockerfile ,就可以通过docker build命令来创建镜像。比如

1
docker build -t javaApp .

  其中:

  • -t代表给镜像起名,格式为name:tag,不指定tag时默认为latest
  • .代表指定Dockerfile所在目录,即在当前目录找到 Dockerfile 配置文件,并指定上下文目录并打包到 Docker Server

注意

  要想定制适合自己、高效方便的镜像,最好:

  • 精简镜像用途:尽量让每个镜像的用途都比较集中、单一,避免构造大 而复杂、多功能的镜像
  • 选用合适的基础镜像:过大的基础镜像会造成生成臃肿的镜像,一般推荐较为小巧的 debian 镜像
  • 提供足够清晰的命令注释和维护者信息:Dockerfile 也是一种代码,需要考虑他人后续的扩展使用
  • 正确使用版本号:使用明确的版本号信息,如 1.0,2.0,而非 latest, 将避免内容不一致可能引发的惨案;
  • 减少镜像层数:若希望所生成镜像的层数尽量少,则要尽量合并指令,比如多个 RUN 指令可以合并为一条;
  • 及时删除临时文件和缓存文件:特别是在执行apt-get指令后,/var/cache/apt下面会缓存一些安装包
  • 提高生成􏰀度:如合理使用缓存,减少内容目录下的文件,或使用.dockerignore文件指定等
  • 调整合理的指令顺序:在开启缓存的情况下,内容不变的指令尽量放在前面,这样可以尽量复用
  • 减少外部源的干扰:若确实要从外部引入数据,需要指定持久的地址,并带有版本信息,让他人可以重复而不出错

应用部署(推荐使用 docker-compose)

部署 MySQL 数据库

  MySQL 数据库在 Docker 的部署步骤如下:
① 拉取 mysql 镜像:

1
docker pull mysql:5.7

② 创建 mysql 容器:

1
docker run -di --name=mysql57 -p 3308:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

  其中:

  • -p:端口映射,格式为宿主机映射端口:容器运行端口,上面将容器的 3306 端口映射到宿主机的3308端口,之后即可在宿主机上通过3308端口连接该mysql
  • -e:添加环境变量,MYSQL_ROOT_PASSWORD指定root用户的登录密码

③ 远程登录 mysql :

1
mysql -h xx.xx.xx.xx -P 3306 -uroot -p

-h后的xx.xx.xx.xx为要连接宿主机的 IP,-P指定端口为3306

中文乱码

  Docker MySQL 镜像默认编码大部分是 latin1,写入中文数据会以???显示,需要修改编码格式为UTF-8

  修改时我们需要找到 MySQL 的配置文件,一般在/etc/mysql/conf.d/mysql.cnf

  若 Docker 内置的没有 vi 编辑器,可以退出 Docker,通过 Find 命令查找这个配置文件,然后进行修改。

1
2


部署 Tomcat 服务器

   Tomcat 服务器在 Docker 的部署步骤如下:
① 拉取 Tomcat 官方镜像:

1
docker pull tomcat:9.0.24

② 创建容器:

1
docker run -it --name=mytomcat -p 8080:8080 tomcat:9.0.24

③ 将相关 war 文件复制到 tocam 容器目录下:

1
docker cp logistics.war mytomca:/usr/local/tomcat/webapps/

④ 之后即可通过 ip(或域名)加 8080 端口及路径访问该项目,如:

1
https://ip:8080/logistics

:Tomcat 镜像可以在官方选择版本:

部署 Nginx 服务器

  Nginx 服务器在 Docker 的部署步骤如下:
① 拉取 Nginx 镜像:

1
docker pull nginx

② 创建容器:

1
docker run -di --name=mynginx -p 80:80 nginx

部署 Redis 数据库

  Redis 数据库在 Docker 的部署步骤如下:
① 拉取 Redis 镜像:

1
docker pull redis

② 创建容器:

1
docker run -di --name=myredis -p 6379:6379 redis

时区修改

  Docker 拉取镜像的时区可能和当地时间不同,通过下面操作可以修改为中国时区:

  第一步,进入相关容器:

1
docker exec -it containerName /bin/bash

  第二步,修改时区

1
2
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
echo "Asia/Shanghai" > /etc/timezone

  第三步:查看当前时间:

1
date

Docker Compose

  Docker Compose 是用于定义和运行多容器的云原生应用程序工具。

为什么使用 Docker Compose ?

  在上文使用 Docker 容器时,我们需要定义并 build dockerfile 文件,然后启动运行它(不同容器可能还需要配置相关参数)。
  但是,我们的系统一般都包含上百的服务,而每个服务又有多个实例,若全部采用手动方式来启动关闭的话,工作量之大可想而知。

  因此,为了把程序员从这些繁杂的工作当中解脱出来,Docker Compose 便应运而生!

现状与作用

  目前,Docker Compose 已被数百万的开发人员所使用,并在 GitHub 上拥有超过 650,000 个文件,由于它定义了一种基于多容器应用程序简单的与云和编排器无关的方式,因此被开发人员广泛接受。
  Docker Compose 通过允许开发人员在单个文件中定义一个复杂的堆栈并使用单个命令运行它,极大地简化了开发人员到云程序和工具链的代码。
  这样,便无需手动构建和启动每个容器,从而节省了开发团队的宝贵时间

安装

  不论你通过 Windows 还是 Mac 安装完 Docker Desktop ,都会默认自动安装好 docker-compose。

  Linux 可以通过 docker-compose 下载地址下载,之后执行以下命令即可:

1
2
3
4
sudo mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# 检测是否安装成功
docker-compose version

基本概念

  Docker Compose 允许用户通过一个单独的docker- compose.yml模板文件来定义一组相关联的应用容器为一个项目。
  Docker Compose 有两个重要的概念:

  • 服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器
  • 实例项目(project):由一组关联的应用容器组成的一个完整业务单元

  Docker Compose 的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。
  Docker Compose 项目由 Python 编写,实现上调用了 Docker 服务提供的 API 来对容 器进行管理。因此,只要所操作的平台支持 Docker API,就可以在其上利用 Docker Compose 来进行编排管理。

基本使用

  Docker Compose 的使用步骤如下:

  • 根据容器相关参数来编写docker-compose.yml文件
  • 执行docker-compose.yml文件

  下面以 MySQL 容器为例:
  首先,根据 MySQL 相关参数来编写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
version: '3.1'

services:
db: # 根据以下配置创建一个容器,名称为 image_db_num
image: mysql # 拉取最新镜像
command: # 配置基础参数
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
restart: always # 宕机总是重启
environment: # 环境配置:下面为 root 初识密码
MYSQL_ROOT_PASSWORD: 123456
ports: # 端口映射
- 3309:3306
volumes: # 目录挂载
- ./data:/var/lib/mysql

adminer: # 根据以下配置创建一个容器,名称为 image_adminer_num
image: adminer
restart: always
ports:
- 8091:8080

注意哦:vim 下输入:set paste则粘贴不会出现格式错误

  之后就可以执行如下命令来构建容器啦:

1
docker-compose up -d

  该命令默认会根据当前目录下的docker-compose.yml文件来构建容器。

Docker Compose 命令

  以下命令前缀均为docker-compose(如docker-compose ps):

命令 说明
ps 显示所有容器
up -d tomcat 构建并启动 tomcat 容器
exec -it tomcat /bin/bash 以交互式方式进入 tomcat 容器
start tomcat 启动 tomcat 容器
restart tomcat 重新启动 tomcat 容器
stop tomcat 停止 tomcat 容器
rm tomcat 删除容器(删除前必须关闭容器)
down 删除所有 tomcat 容器及镜像
build tomcat 构建镜像
logs tomcat 查看 tomcat 的日志
pause tomcat 暂停 tomcat 容器
unpause tomcat 恢复 tomcat 容器

Docker Compose 网络

  默认情况下,Compose会为我们的应用创建一个网络,服务的每个容器都会加入该网络中。
  这样,容器就可被该网络中的其他容器访问,不仅如此,该容器还能以服名称作为 hostname 被其他容器访问。

网络名称

  默认情况下,应用程序的网络名称基于 Compose 的工程名称,而项目名称基于docker-compose.yml文件所在目录的名称。

  如需修改工程名称,可使用–project-name标识或COMPOSE_PORJECT_NAME环境变量。

  举个例子,假如一个应用程序在名为myapp的目录中,并且docker-compose.yml如下所示:

1
2
3
4
5
6
7
8
version: '2'
services:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres

  当我们运行docker-compose up命令时,将会执行以下几步:

  • 创建一个名为myapp_default的网络;
  • 使用web服务的配置创建容器时,它将以web为姓名加入myapp_default网络
  • 使用db服务的配置创建容器时,它将以db为姓名加入myapp_default网络

  myapp_default网络的容器之间可使用服务名称(webdb)作为hostname相互访问。
  举个例子,web这个服务可使用postgres://db:5432 访问db容器。

更新容器

  当服务的配置发生更改时,可使用docker-compose up命令更新配置。

  此时,Compose 会删除旧容器并创建新容器。新容器会以不同的IP加入网络,名称保持不变。任何指向旧容器的连接都会被关闭,容器会重新找到新容器并连接上去。

  默认情况下,服务之间可使用服务名称相互访问,而links允许我们定义一个别名去访问其他服务。
  举个例子:

1
2
3
4
5
6
7
8
version: '2'
services:
web:
build: .
links:
- "db:database"
db:
image: postgres

  这样web服务就可使用dbdatabase作为hostname访问db服务了。

指定自定义网络

  一些场景下,默认的网络配置满足不了我们的需求,此时我们可使用networks命令自定义网络。
  networks命令允许我们创建更加复杂的网络拓扑并指定自定义网络驱动和选项。不仅如此,我们还可使用networks将服务连接到不是由 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
version: '2'

services:
proxy:
build: ./proxy
networks:
- front
app:
build: ./app
networks:
- front
- back
db:
image: postgres
networks:
- back

networks:
front:
# Use a custom driver
driver: custom-driver-1
back:
# Use a custom driver which takes special options
driver: custom-driver-2
driver_opts:
foo: "1"
bar: "2"

  其中,proxy 服务与 db 服务隔离,两者分别使用自己的网络;app服务可与两者通信。

  由本例不难发现,使用 networks 命令,即可方便实现服务间的网络隔离与连接。

配置默认网络

  除自定义网络外,我们也可为默认网络自定义配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: '2'

services:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres

networks:
default:
# Use a custom driver
driver: custom-driver-1&emsp;&emsp;

  这样,就可为该应用指定自定义的网络驱动。

使用已存在的网络

  一些场景下,我们并不需要创建新的网络,而只需加入已存在的网络。
  此时需要使用external属性进行配置:

1
2
3
4
networks:
default:
external:
name: zookeeper_default

Docker Compose 链接外部容器的几种方式

  在 Docker 中,容器之间的链接是一种很常见的操作:它提供了访问其中的某个容器的网络服务而不需要将所需的端口暴露给Docker Host主机的功能。
  Docker Compose中对该特性的支持同样是很方便的。然而,如果需要链接的容器没有定义在同一个docker-compose.yml中的时候,这个时候就稍微麻烦复杂了点。

  在不使用Docker Compose的时候,将两个容器链接起来使用—link参数,相对来说比较简单,以nginx镜像为例子:

1
2
3
4
# 开启一个实例test1
docker run --rm --name nginx_1 -d nginx
# 开启一个实例test2并与test1建立链接
docker run --rm --name nginx_2 --link test1 -d nginx

  这样,nginx_2nginx_1便建立了链接,就可以在nginx_2中使用访问nginx_1中的服务了。

  如果使用 Docker Compose,那么这个事情就变得更简单了,还是以上面的nginx镜像为例子,编辑docker-compose.yml文件为:

1
2
3
4
5
6
7
8
9
10
version: "3"
services:
nginx_2:
image: nginx
depends_on:
- nginx_2
links:
- nginx_2
nginx_1:
image: nginx

  最终效果与使用普通的Docker命令docker run xxxx建立的链接并无区别。这只是一种最为理想的情况。

  1. 如果容器没有定义在同一个docker-compose.yml文件中,应该如何链接它们呢?
  2. 又如果定义在docker-compose.yml文件中的容器需要与docker run xxx启动的容器链接,需要如何处理?

  针对这两种典型的情况,下面给出我个人测试可行的办法:

方式一:让需要链接的容器同属一个外部网络

  我们还是使用nginx镜像来模拟这样的一个情景:假设我们需要将两个使用Docker Compose管理的nignx容器(test1test2)链接起来,使得test2能够访问test1中提供的服务,这里我们以能ping通为准。

  首先,我们定义容器test1docker-compose.yml文件内容为:

1
2
3
4
5
6
7
8
9
10
11
version: "3"
services:
test2:
image: nginx
container_name: test1
networks:
- default
- app_net
networks:
app_net:
external: true

  容器test2内容与test1基本一样,只是多了一个external_links,需要特别说明的是:最近发布的Docker版本已经不需要使用external_links来链接容器,容器的DNS服务可以正确的作出判断,因此如果你你需要兼容较老版本的Docker的话,那么容器test2docker-compose.yml文件内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
version: "3"
services:
test2:
image: nginx
networks:
- default
- app_net
external_links:
- test1
container_name: test2
networks:
app_net:
external: true复制代码

  否则的话,test2docker-compose.ymltest1的定义完全一致,不需要额外多指定一个external_links。相关的问题请参见stackoverflow上的相关问题:docker-compose + external container

  正如你看到的那样,这里两个容器的定义里都使用了同一个外部网络app_net,因此,我们需要在启动这两个容器之前通过以下命令再创建外部网络:

1
docker network create app_net复制代码

  之后,通过docker-compose up -d命令启动这两个容器,然后执行docker exec -it test2 ping test1,你将会看到如下的输出:

1
2
3
4
5
6
7
8
docker exec -it test2 ping test1
PING test1 (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: icmp_seq=0 ttl=64 time=0.091 ms
64 bytes from 172.18.0.2: icmp_seq=1 ttl=64 time=0.146 ms
64 bytes from 172.18.0.2: icmp_seq=2 ttl=64 time=0.150 ms
64 bytes from 172.18.0.2: icmp_seq=3 ttl=64 time=0.145 ms
64 bytes from 172.18.0.2: icmp_seq=4 ttl=64 time=0.126 ms
64 bytes from 172.18.0.2: icmp_seq=5 ttl=64 time=0.147 ms

  证明这两个容器是成功链接了,反过来在test1中pingtest2也是能够正常ping通的。

  如果我们通过docker run --rm --name test3 -d nginx这种方式来先启动了一个容器(test3)并且没有指定它所属的外部网络,而需要将其与test1或者test2链接的话,这个时候手动链接外部网络即可:

1
docker network connect app_net test3复制代码

  这样,三个容器都可以相互访问了。

方式二:更改需要链接的容器的网络模式

  通过更改你想要相互链接的容器的网络模式为bridge,并指定需要链接的外部容器(external_links)即可。与同属外部网络的容器可以相互访问的链接方式一不同,这种方式的访问是单向的。

  还是以nginx容器镜像为例子,如果容器实例nginx1需要访问容器实例nginx2,那么nginx2doker-compose.yml定义为:

1
2
3
4
5
6
version: "3"
services:
nginx2:
image: nginx
container_name: nginx2
network_mode: bridge

  与其对应的,nginx1docker-compose.yml定义为:

1
2
3
4
5
6
7
8
version: "3"
services:
nginx1:
image: nginx
external_links:
- nginx2
container_name: nginx1
network_mode: bridge

需要特别说明的是,这里的external_links是不能省略的,而且nginx1的启动必须要在nginx2之后,否则可能会报找不到容器nginx2的错误。

  接着我们使用ping来测试下连通性:

1
2
3
4
5
6
7
8
$ docker exec -it nginx1 ping nginx2  # nginx1 to nginx2
PING nginx2 (172.17.0.4): 56 data bytes
64 bytes from 172.17.0.4: icmp_seq=0 ttl=64 time=0.141 ms
64 bytes from 172.17.0.4: icmp_seq=1 ttl=64 time=0.139 ms
64 bytes from 172.17.0.4: icmp_seq=2 ttl=64 time=0.145 ms

$ docker exec -it nginx2 ping nginx1 #nginx2 to nginx1
ping: unknown host复制代码

  以上也能充分证明这种方式是属于单向联通的。

  在实际应用中根据自己的需要灵活的选择这两种链接方式,如果想偷懒的话,大可选择第二种。不过我更推荐第一种,不难看出无论是联通性还是灵活性,较为更改网络模式的第二种都更为友好。

  附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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
Compose和Docker兼容性:
Compose 文件格式有3个版本,分别为1, 2.x 3.x
目前主流的为 3.x 其支持 docker 1.13.0 及其以上的版本

常用参数:
version # 指定 compose 文件的版本
services # 定义所有的 service 信息, services 下面的第一级别的 key 既是一个 service 的名称

build # 指定包含构建上下文的路径, 或作为一个对象,该对象具有 context 和指定的 dockerfile 文件以及 args 参数值
context # context: 指定 Dockerfile 文件所在的路径
dockerfile # dockerfile: 指定 context 指定的目录下面的 Dockerfile 的名称(默认为 Dockerfile)
args # args: Dockerfile 在 build 过程中需要的参数 (等同于 docker container build --build-arg 的作用)
cache_from # v3.2中新增的参数, 指定缓存的镜像列表 (等同于 docker container build --cache_from 的作用)
labels # v3.3中新增的参数, 设置镜像的元数据 (等同于 docker container build --labels 的作用)
shm_size # v3.5中新增的参数, 设置容器 /dev/shm 分区的大小 (等同于 docker container build --shm-size 的作用)

command # 覆盖容器启动后默认执行的命令, 支持 shell 格式和 [] 格式

configs # 不知道怎么用

cgroup_parent # 不知道怎么用

container_name # 指定容器的名称 (等同于 docker run --name 的作用)

credential_spec # 不知道怎么用

deploy # v3 版本以上, 指定与部署和运行服务相关的配置, deploy 部分是 docker stack 使用的, docker stack 依赖 docker swarm
endpoint_mode # v3.3 版本中新增的功能, 指定服务暴露的方式
vip # Docker 为该服务分配了一个虚拟 IP(VIP), 作为客户端的访问服务的地址
dnsrr # DNS轮询, Docker 为该服务设置 DNS 条目, 使得服务名称的 DNS 查询返回一个 IP 地址列表, 客户端直接访问其中的一个地址
labels # 指定服务的标签,这些标签仅在服务上设置
mode # 指定 deploy 的模式
global # 每个集群节点都只有一个容器
replicated # 用户可以指定集群中容器的数量(默认)
placement # 不知道怎么用
replicas # deploy 的 mode 为 replicated 时, 指定容器副本的数量
resources # 资源限制
limits # 设置容器的资源限制
cpus: "0.5" # 设置该容器最多只能使用 50% 的 CPU
memory: 50M # 设置该容器最多只能使用 50M 的内存空间
reservations # 设置为容器预留的系统资源(随时可用)
cpus: "0.2" # 为该容器保留 20% 的 CPU
memory: 20M # 为该容器保留 20M 的内存空间
restart_policy # 定义容器重启策略, 用于代替 restart 参数
condition # 定义容器重启策略(接受三个参数)
none # 不尝试重启
on-failure # 只有当容器内部应用程序出现问题才会重启
any # 无论如何都会尝试重启(默认)
delay # 尝试重启的间隔时间(默认为 0s)
max_attempts # 尝试重启次数(默认一直尝试重启)
window # 检查重启是否成功之前的等待时间(即如果容器启动了, 隔多少秒之后去检测容器是否正常, 默认 0s)
update_config # 用于配置滚动更新配置
parallelism # 一次性更新的容器数量
delay # 更新一组容器之间的间隔时间
failure_action # 定义更新失败的策略
continue # 继续更新
rollback # 回滚更新
pause # 暂停更新(默认)
monitor # 每次更新后的持续时间以监视更新是否失败(单位: ns|us|ms|s|m|h) (默认为0)
max_failure_ratio # 回滚期间容忍的失败率(默认值为0)
order # v3.4 版本中新增的参数, 回滚期间的操作顺序
stop-first #旧任务在启动新任务之前停止(默认)
start-first #首先启动新任务, 并且正在运行的任务暂时重叠
rollback_config # v3.7 版本中新增的参数, 用于定义在 update_config 更新失败的回滚策略
parallelism # 一次回滚的容器数, 如果设置为0, 则所有容器同时回滚
delay # 每个组回滚之间的时间间隔(默认为0)
failure_action # 定义回滚失败的策略
continue # 继续回滚
pause # 暂停回滚
monitor # 每次回滚任务后的持续时间以监视失败(单位: ns|us|ms|s|m|h) (默认为0)
max_failure_ratio # 回滚期间容忍的失败率(默认值0)
order # 回滚期间的操作顺序
stop-first # 旧任务在启动新任务之前停止(默认)
start-first # 首先启动新任务, 并且正在运行的任务暂时重叠

注意:
支持 docker-compose up docker-compose run 但不支持 docker stack deploy 的子选项
security_opt container_name devices tmpfs stop_signal links cgroup_parent
network_mode external_links restart build userns_mode sysctls

devices # 指定设备映射列表 (等同于 docker run --device 的作用)

depends_on # 定义容器启动顺序 (此选项解决了容器之间的依赖关系, 此选项在 v3 版本中 使用 swarm 部署时将忽略该选项)
示例:
docker-compose up 以依赖顺序启动服务,下面例子中 redis db 服务在 web 启动前启动
默认情况下使用 docker-compose up web 这样的方式启动 web 服务时,也会启动 redis db 两个服务,因为在配置文件中定义了依赖关系
version: '3'
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres

dns # 设置 DNS 地址(等同于 docker run --dns 的作用)

dns_search # 设置 DNS 搜索域(等同于 docker run --dns-search 的作用)

tmpfs # v2 版本以上, 挂载目录到容器中, 作为容器的临时文件系统(等同于 docker run --tmpfs 的作用, 在使用 swarm 部署时将忽略该选项)

entrypoint # 覆盖容器的默认 entrypoint 指令 (等同于 docker run --entrypoint 的作用)

env_file # 从指定文件中读取变量设置为容器中的环境变量, 可以是单个值或者一个文件列表, 如果多个文件中的变量重名则后面的变量覆盖前面的变量, environment 的值覆盖 env_file 的值
文件格式:
RACK_ENV=development

environment # 设置环境变量, environment 的值可以覆盖 env_file 的值 (等同于 docker run --env 的作用)

expose # 暴露端口, 但是不能和宿主机建立映射关系, 类似于 Dockerfile 的 EXPOSE 指令

external_links # 连接不在 docker-compose.yml 中定义的容器或者不在 compose 管理的容器(docker run 启动的容器, 在 v3 版本中使用 swarm 部署时将忽略该选项)

extra_hosts # 添加 host 记录到容器中的 /etc/hosts 中 (等同于 docker run --add-host 的作用)

healthcheck # v2.1 以上版本, 定义容器健康状态检查, 类似于 Dockerfile 的 HEALTHCHECK 指令
test # 检查容器检查状态的命令, 该选项必须是一个字符串或者列表, 第一项必须是 NONE, CMD 或 CMD-SHELL, 如果其是一个字符串则相当于 CMD-SHELL 加该字符串
NONE # 禁用容器的健康状态检测
CMD # test: ["CMD", "curl", "-f", "http://localhost"]
CMD-SHELL # test: ["CMD-SHELL", "curl -f http://localhost || exit 1"] 或者 test: curl -f https://localhost || exit 1
interval: 1m30s # 每次检查之间的间隔时间
timeout: 10s # 运行命令的超时时间
retries: 3 # 重试次数
start_period: 40s # v3.4 以上新增的选项, 定义容器启动时间间隔
disable: true # true 或 false, 表示是否禁用健康状态检测和 test: NONE 相同

image # 指定 docker 镜像, 可以是远程仓库镜像、本地镜像

init # v3.7 中新增的参数, true 或 false 表示是否在容器中运行一个 init, 它接收信号并传递给进程

isolation # 隔离容器技术, 在 Linux 中仅支持 default 值

labels # 使用 Docker 标签将元数据添加到容器, 与 Dockerfile 中的 LABELS 类似

links # 链接到其它服务中的容器, 该选项是 docker 历史遗留的选项, 目前已被用户自定义网络名称空间取代, 最终有可能被废弃 (在使用 swarm 部署时将忽略该选项)

logging # 设置容器日志服务
driver # 指定日志记录驱动程序, 默认 json-file (等同于 docker run --log-driver 的作用)
options # 指定日志的相关参数 (等同于 docker run --log-opt 的作用)
max-size # 设置单个日志文件的大小, 当到达这个值后会进行日志滚动操作
max-file # 日志文件保留的数量

network_mode # 指定网络模式 (等同于 docker run --net 的作用, 在使用 swarm 部署时将忽略该选项)

networks # 将容器加入指定网络 (等同于 docker network connect 的作用), networks 可以位于 compose 文件顶级键和 services 键的二级键
aliases # 同一网络上的容器可以使用服务名称或别名连接到其中一个服务的容器
ipv4_address # IP V4 格式
ipv6_address # IP V6 格式

示例:
version: '3.7'
services:
test:
image: nginx:1.14-alpine
container_name: mynginx
command: ifconfig
networks:
app_net: # 调用下面 networks 定义的 app_net 网络
ipv4_address: 172.16.238.10
networks:
app_net:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.16.238.0/24

pid: 'host' # 共享宿主机的 进程空间(PID)

ports # 建立宿主机和容器之间的端口映射关系, ports 支持两种语法格式
SHORT 语法格式示例:
- "3000" # 暴露容器的 3000 端口, 宿主机的端口由 docker 随机映射一个没有被占用的端口
- "3000-3005" # 暴露容器的 3000 到 3005 端口, 宿主机的端口由 docker 随机映射没有被占用的端口
- "8000:8000" # 容器的 8000 端口和宿主机的 8000 端口建立映射关系
- "9090-9091:8080-8081"
- "127.0.0.1:8001:8001" # 指定映射宿主机的指定地址的
- "127.0.0.1:5000-5010:5000-5010"
- "6060:6060/udp" # 指定协议

LONG 语法格式示例:(v3.2 新增的语法格式)
ports:
- target: 80 # 容器端口
published: 8080 # 宿主机端口
protocol: tcp # 协议类型
mode: host # host 在每个节点上发布主机端口, ingress 对于群模式端口进行负载均衡

secrets # 不知道怎么用

security_opt # 为每个容器覆盖默认的标签 (在使用 swarm 部署时将忽略该选项)

stop_grace_period # 指定在发送了 SIGTERM 信号之后, 容器等待多少秒之后退出(默认 10s)

stop_signal # 指定停止容器发送的信号 (默认为 SIGTERM 相当于 kill PID; SIGKILL 相当于 kill -9 PID; 在使用 swarm 部署时将忽略该选项)

sysctls # 设置容器中的内核参数 (在使用 swarm 部署时将忽略该选项)

ulimits # 设置容器的 limit

userns_mode # 如果Docker守护程序配置了用户名称空间, 则禁用此服务的用户名称空间 (在使用 swarm 部署时将忽略该选项)

volumes # 定义容器和宿主机的卷映射关系, 其和 networks 一样可以位于 services 键的二级键和 compose 顶级键, 如果需要跨服务间使用则在顶级键定义, 在 services 中引用
SHORT 语法格式示例:
volumes:
- /var/lib/mysql # 映射容器内的 /var/lib/mysql 到宿主机的一个随机目录中
- /opt/data:/var/lib/mysql # 映射容器内的 /var/lib/mysql 到宿主机的 /opt/data
- ./cache:/tmp/cache # 映射容器内的 /var/lib/mysql 到宿主机 compose 文件所在的位置
- ~/configs:/etc/configs/:ro # 映射容器宿主机的目录到容器中去, 权限只读
- datavolume:/var/lib/mysql # datavolume 为 volumes 顶级键定义的目录, 在此处直接调用

LONG 语法格式示例:(v3.2 新增的语法格式)
version: "3.2"
services:
web:
image: nginx:alpine
ports:
- "80:80"
volumes:
- type: volume # mount 的类型, 必须是 bind、volume 或 tmpfs
source: mydata # 宿主机目录
target: /data # 容器目录
volume: # 配置额外的选项, 其 key 必须和 type 的值相同
nocopy: true # volume 额外的选项, 在创建卷时禁用从容器复制数据
- type: bind # volume 模式只指定容器路径即可, 宿主机路径随机生成; bind 需要指定容器和数据机的映射路径
source: ./static
target: /opt/app/static
read_only: true # 设置文件系统为只读文件系统
volumes:
mydata: # 定义在 volume, 可在所有服务中调用

restart # 定义容器重启策略(在使用 swarm 部署时将忽略该选项, 在 swarm 使用 restart_policy 代替 restart)
no # 禁止自动重启容器(默认)
always # 无论如何容器都会重启
on-failure # 当出现 on-failure 报错时, 容器重新启动

其他选项:
domainname, hostname, ipc, mac_address, privileged, read_only, shm_size, stdin_open, tty, user, working_dir
上面这些选项都只接受单个值和 docker run 的对应参数类似

对于值为时间的可接受的值:
2.5s
10s
1m30s
2h32m
5h34m56s
时间单位: us, ms, s, m, h
对于值为大小的可接受的值:
2b
1024kb
2048k
300m
1gb
单位: b, k, m, g 或者 kb, mb, gb
networks # 定义 networks 信息
driver # 指定网络模式, 大多数情况下, 它 bridge 于单个主机和 overlay Swarm 上
bridge # Docker 默认使用 bridge 连接单个主机上的网络
overlay # overlay 驱动程序创建一个跨多个节点命名的网络
host # 共享主机网络名称空间(等同于 docker run --net=host)
none # 等同于 docker run --net=none
driver_opts # v3.2以上版本, 传递给驱动程序的参数, 这些参数取决于驱动程序
attachable # driver 为 overlay 时使用, 如果设置为 true 则除了服务之外,独立容器也可以附加到该网络; 如果独立容器连接到该网络,则它可以与其他 Docker 守护进程连接到的该网络的服务和独立容器进行通信
ipam # 自定义 IPAM 配置. 这是一个具有多个属性的对象, 每个属性都是可选的
driver # IPAM 驱动程序, bridge 或者 default
config # 配置项
subnet # CIDR格式的子网,表示该网络的网段
external # 外部网络, 如果设置为 true 则 docker-compose up 不会尝试创建它, 如果它不存在则引发错误
name # v3.5 以上版本, 为此网络设置名称
文件格式示例:
version: "3"
services:
redis:
image: redis:alpine
ports:
- "6379"
networks:
- frontend
deploy:
replicas: 2
update_config:
parallelism: 2
delay: 10s
restart_policy:
condition: on-failure
db:
image: postgres:9.4
volumes:
- db-data:/var/lib/postgresql/data
networks:
- backend
deploy:
placement:
constraints: [node.role == manager]

参考

文章信息

时间 说明
2019-05-30 初稿
0%