Docker 培训动手实验文档¶
Attention
本文档使用reST格式编写,内容托管于GitHub。文档内容正在持续编写中,如果您在使用中遇到问题,可以通过扫描文末我们的微信公众的二维码来提交问题。
本文档提供2个主要版本:
- stable: 稳定版本,按照发布日期进行tag,如:v2016.12.10 表示所对应的是 2016年12月10日发布的版本
- latest: 最新版本,持续更新。
版本记录:
- v2016.12.12: 当前最新稳定版本
概述¶
本文档配合《构建容器化DevOps数据中心》培训提供。从Docker的基本功能和操作开始,讲解Docker背后的容器化核心技术cgroup和namespace,分析docker容器化应用所带来的问题和解决方案;最后结合 Apache Mesos 和Marathon为您介绍如何在企业中构建基于混合云的DevOps数据中心,并对其主要应用场景进行演示和讲解。
背景¶
现代软件开发和计算机技术的快速发展造成了大量异构系统的出现,这给应用开发,测试和部署都带来了巨大的复杂度,如何有效的管理这种复杂度同时保持开发和运维的效率是急待解决的问题。 容器化技术在主机时代已经被证明是非常有效的应用隔离技术,可以帮助企业有效的利用计算资源,同时提供很好的敏捷特性。随着Linux在企业级系统中应用的增多,虚拟化的广泛使用和云计算的兴起,Docker这种围绕容器技术构建的工具/生态系统被广泛接受,成为解决异构系统上应用开发和运维问题的最佳选择,同时也为企业构建混合云数据中心提供了更好的选择和可能性。 Docker所代表的容器化分布式数据中心运维方案同时也带来了一些新的问题,比如应用编排,集群管理,作业控制,资源监控等。当前的解决方案主要有Mesos, Kubernetes和Swarm;其中来自Apache基金会的Mesos/Marathon是唯一经过了Twitter/airbnb等大型企业上万台主机集群环境验证的成熟的企业级开源解决方案。
目标人群¶
本培训为300级别培训,适合企业IT应用开发和运维人员,参训者需要具备基础的Linux/Windows操作系统知识,对网络,存储,虚拟化,云计算和应用开发,测试,部署流程较为熟悉,并具备实际操作经验。
更新日期: | Aug 03, 2017 |
---|---|
作者: | LEANSOFT |
主页: | DevOps Hub |
内容¶
Docker 基本操作¶
Docker 运行环境的组成¶
Docker 运行环境如下图,并由代码库,Docker主机(运行了docker daemon)的服务器和镜像仓库这3个最主要的部分组成。

- 代码库
- Git / SVN 等
- Docker 引擎
- Linux: docker daemon 驻守程序, Windows: com.docker.service 服务
- Docker 容器化主机
- 运行了以上docker引擎的服务器(VM或实体机)
- 镜像仓库
- 用于容器镜像存储和版本控制的服务器
- 开发、测试环境
- Windows: Docker for Windows 或 Docker Toolbox, macOS:Docker for Mac,Linux: Docker 原生支持
- 生产运维环境
- 容器化主机,容器化编排引擎:Apache Mesos / k8s / Docker Swarm
本部分实验内容将指导你使用最基本的docker, docker-machine和docker-compose命令来管理这套环境。请参考 常见问题及解决方案 | 安装Docker for Windows 或 安装Docker Toolbox 预先配置您的本地环境。
练习列表
练习一:使用docker基本命令¶
01. 使用容器运行Hello World¶
使用命令行运行以下命令,此命令将启动一个ubuntu的容器并在其中输出 Hello World文本,执行完毕后,容器自动退出。
$ docker run harbor.devopshub.cn/library/ubuntu /bin/echo 'Hello world'
Hello world
02. 与容器进行交互¶
使用命令行运行以下命令,此命令将启动一个ubuntu容器并在其中运行bash交互命令行界面,你可以尝试运行pwd,ls,ps等命令查看容器内环境,就如同远程操作一台服务器一样。
$ docker run -t -i harbor.devopshub.cn/library/ubuntu /bin/bash
root@af8bae53bdd3:/#
03. 在容器中运行持续任务并管理容器生命周期¶
使用命令行运行以下命令,此命令将启动一个ubuntu容器并在其中持续运行echo hello world,启动后容器会持续输出hello world文本。
$ docker run -d harbor.devopshub.cn/library/ubuntu /bin/sh -c "while true; do echo hello world; sleep 1; done"
1e5535038e285177d5214659a068137486f96ee5c2e85a4ac52dc83f2ebe4147
注意当你运行以上命令后,命令行直接退出并没有持续输出hello world文本,这是因为我们使用了-d参数,这时容器在后台运行,你可以通过docker logs命令获取容器内的日志输出,注意替换c3a2为你的容器ID的前四位,如下:
$ docker logs -f c3a2
hello world
hello world
hello world
hello world
hello world
hello world
...
为了查看当前正在运行状态的容器,你可以使用docker ps命令,如下:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c3a251374851 ubuntu "/bin/sh -c 'while..." Less than a second ago Up 2 minutes evil_ride
你也可以查看到那些没有在运行状态的容器,使用docker ps -a命令,如下:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c3a251374851 ubuntu "/bin/sh -c 'while..." Less than a second ago Up 6 minutes evil_ride
b6d4324edfbc ubuntu "/bin/bash" Less than a second ago Exited (0) 6 minutes ago small_beaver
3363b0a14324 ubuntu "/bin/echo 'Hello ..." Less than a second ago Exited (0) 7 minutes ago hungry_stonebraker
注意以上出了第一个容器正在运行意外,另外2个ubuntu容器都已经停止,但是容器仍然存在。你可以理解为他们是没有被运行中的应用,而应用的文件存在于你的docker环境中。
现在,你可以通过docker stop {id}命令来停止正在运行的容器,如下:
λ docker stop c3a2
c3a2
然后,通过docker rm {id}命令来删除所有未运行的容器,(注意将id替换成你自己的容器ID的前四位)如下:
λ docker rm c3a2 b6d4 3363
c3a2
b6d4
3363
也可以通过这个命令自动枚举所有容器并停止,删除:
FOR /f "tokens=*" %i IN ('docker ps -a -q') DO docker stop %i
FOR /f "tokens=*" %i IN ('docker ps -a -q') DO docker rm %i
04. 运行web应用并通过浏览器访问¶
使用命令行运行以下命令
$ docker run -itd -p 8080:80 harbor.devopshub.cn/training/php-sample:5
fbf9012502229877066ad5e63a1be5727055243857927a1d36ede432d7c3cc20
完成后打开浏览器并导航到 http://localhost:8080,你应该可以看到类似以下页面

注意以上命令与之前的最大区别在于使用了-p参数来映射网络端口,这样我们就可以通过容器主机的8080端口来访问容器的80端口,类似于实现了一个简单的NAT。你也可以使用-P(大写)参数来让docker自动分配主机端口,这样可以避免我们手动分配端口造成冲突。
你可以尝试使用以上实验中的docker logs和docker ps等命令查看此正在运行容器的状态和其中的web服务器所输出的日志,如下
λ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
dda5078bd856 training/php-sample:5 "apache2-foreground" Less than a second ago Up 2 seconds 0.0.0.0:8080->80/tcp high_albattani
运行以下命令式请注意替换dda5为你自己的容器id,同时可以尝试刷新浏览器看到日志的实时输出
λ docker logs -f dda5
[Sun Dec 11 12:40:12.681308 2016] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.10 (Debian) PHP/7.0.13 configured -- resuming normal operations
[Sun Dec 11 12:40:12.681454 2016] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
172.17.0.1 - - [11/Dec/2016:12:40:31 +0000] "GET / HTTP/1.1" 200 629 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
172.17.0.1 - - [11/Dec/2016:12:40:38 +0000] "GET / HTTP/1.1" 200 629 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"
另外,你还可以使用docker top {id}命令查看容器中的进程列表
λ docker top dda5
PID USER TIME COMMAND
4241 root 0:00 apache2 -DFOREGROUND
4262 xfs 0:00 apache2 -DFOREGROUND
4263 xfs 0:00 apache2 -DFOREGROUND
4264 xfs 0:00 apache2 -DFOREGROUND
4265 xfs 0:00 apache2 -DFOREGROUND
4266 xfs 0:00 apache2 -DFOREGROUND
4300 xfs 0:00 apache2 -DFOREGROUND
4302 xfs 0:00 apache2 -DFOREGROUND
或者通过 docker exec 命令直接进入容器进行操作
λ docker exec -it dda5 bash
root@dda5078bd856:/var/www/html# pwd
/var/www/html
root@dda5078bd856:/var/www/html# ls -la
total 24
drwxr-xr-x 1 www-data www-data 4096 Dec 5 21:04 .
drwxr-xr-x 1 root root 4096 Nov 8 23:22 ..
-rwxr-xr-x 1 root root 573 Dec 6 03:19 index.php
-rwxr-xr-x 1 root root 6452 Dec 6 03:04 small_h.png
-rwxr-xr-x 1 root root 21 Dec 6 02:56 test.php
小结¶
至此,你应该已经基本掌握了运行容器的主要命令,下一节中,我们将尝试完成一个新的容器镜像的构建并运行我们自己构建的容器镜像。
练习二:使用docker build来构建容器镜像并运行¶
实验准备
请使用以下命令清理本地docker环境,确保后续实验的正确运行
FOR /f "tokens=*" %i IN ('docker ps -a -q') DO docker stop %i
FOR /f "tokens=*" %i IN ('docker ps -a -q') DO docker rm %i
01. 准备容器开发环境¶
在磁盘上建立docker-training目录,并在其中创建php-webapp目录,然后输入code .启动 Visual Studio Code 并打开php-webapp作为工作目录
λ mkdir docker-training
λ cd docker-training\
λ mkdir php-webapp
λ cd php-webapp\
λ code .
在 Visual Studio Code 中点击新建文件夹按钮,创建src目录用来存放源代码

02. 创建项目代码¶
分别创建以下文件

请注意文件的位置,一个在src目录中,另外一个在根目录
- src/index.php
<html>
<head>
<title>Hello world!</title>
</head>
<body>
<?php if($_ENV["HOSTNAME"]) {?><h3>My hostname is <?php echo $_ENV["HOSTNAME"]; ?></h3><?php } ?>
</body>
</html>
- Dockerfile
FROM harbor.devopshub.cn/library/php:7.0-apache
COPY src/ /var/www/html/
03. 构建容器镜像¶
进入/docker-training/php-webapp目录,并执行命令完成容器镜像的构建
D:\docker-training\php-webapp
λ docker build -t php-webapp:1 .
执行结果如下,你可以看到这个命令执行了3部分内容
- 将我们应用程序的内容上传至docker daemon
- 从我们的私有镜像仓库拉取了Dockerfile里面所定义的基础镜像(FROM)
- 按照Dockerfile里面所定义的步骤完成打包操作
Sending build context to Docker daemon 3.584 kB
Step 1/2 : FROM harbor.devopshub.cn/library/php:7.0-apache
7.0-apache: Pulling from library/php
386a066cd84a: Already exists
269e95c6053a: Already exists
6243d5c57a34: Already exists
872f6d38a33b: Already exists
e5ea5361568c: Already exists
f81f18e77719: Already exists
f9dbc878ca0c: Already exists
195935e4100b: Already exists
935d0c2409b2: Pull complete
d14786710093: Pull complete
b7dff268d83a: Pull complete
d1083150956d: Pull complete
9284aa2927a6: Pull complete
Digest: sha256:05fe69944d513bd618ad81cf6160e2f0e8237a3abf8383c816b8bbbc5ff83418
Status: Downloaded newer image for harbor.devopshub.cn/library/php:7.0-apache
---> 336e2be8a343
Step 2/2 : COPY src/ /var/www/html/
---> 0996039f9f99
Removing intermediate container f5f368e823a1
Successfully built 0996039f9f99
完成后,使用docker images {镜像名称} 命令来查看构建好的容器镜像
λ docker images php-webapp
REPOSITORY TAG IMAGE ID CREATED SIZE
php-webapp 1 0996039f9f99 Less than a second ago 403 MB
04. 运行我们的容器镜像¶
完成以上容器镜像构建后,我们就可以使用docker run来运行我们的应用了
λ docker run -itd -p 8080:80 php-webapp:1
51c60a16acb1fa17655470cf6304803f738f6f576e7e3d250fba865c0ff5be72
运行以上命令后打开浏览器并导航至 http://localhost:8080 ,你将看到刚才我们所编写的index.php的内容

05. 修改代码并重新打包并运行¶
完成以上步骤后,你就可以按照日常开发的流程,修改代码,构建容器,运行容器来完成应用的开发和调试工作了。请注意在每次构建容器镜像的时候将标签号码增加或者设置为自己认为合适的值,这样便于你管理自己的容器镜像。重复几次构建后,再使用docker images php-webapp命令的效果应该类似如下输出。
λ docker images php-webapp
REPOSITORY TAG IMAGE ID CREATED SIZE
php-webapp 1 0996039f9f99 Less than a second ago 403 MB
php-webapp 2 0996039f9f99 Less than a second ago 403 MB
php-webapp 3 0996039f9f99 Less than a second ago 403 MB
小结¶
至此,我们就完成了一个简单的php web应用的容器化打包和运行过程。如果你本地没有安装php环境,你就会注意到使用容器进行应用开发,打包和运行的好处。我们不需要再根据自己所开发的应用来繁琐重复的配置自己的开发、测试和运行环境,所有的应用依赖都可以运行在容器中。这样做我们既简化了开发环境的配置,也防止了应为华宁不一致而造成的很多问题。
在下一节中,我们将使用docker-machine命令来管理容器化主机,容器化主机是容器运行的环境,通过docker-machine我们就可以统一管理这些主机,让我们可以将自己构建好的容器镜像发送到不同的主机上去运行。
练习三:使用docker-machine命令¶
所谓容器化主机Dockerized Host,就是安装了docker engine的主机,可以使用docker工具进行管理。使用docker来协助开发,我们至少需要本地和云端的容器化主机,本地用于开发,测试,打包;云端的用于生产环境发布。使用容器化主机我们可以将所有的环境标准化,也即是使用统一的docker工具来完成容器在不同环境中的迁移(搬运),真正将容器化开发的优势发挥出来。
docker-machine就是docker工具集中提供的用来管理容器化主机的工具,用来管理运行在不同环境的主机,包括:本地虚拟机,远程虚拟机,公有云中的虚拟机都可以通过一个命令统一进行管理。
01. 在本地虚拟化环境中创建容器化主机¶
以下操作可以在Windows操作系统上执行,根据你所使用的是Hyper-V或者Virtual Box你可以分别选择合适的命令创建主机
λ docker-machine create -d hyperv --hyperv-virtual-switch "External Wireless" local-docker01
注意以上命令冲使用了–hyperv-virtual-switch执行了hyper-v上的外网网卡,这样才能确保创建好的容器化主机可以正常访问外网,从而可以下载镜像。
以上命令的运行结果如下
λ docker-machine create -d hyperv --hyperv-virtual-switch "External Wireless" local-docker01
Running pre-create checks...
(local-docker01) Default Boot2Docker ISO is out-of-date, downloading the latest release...
(local-docker01) Latest release for github.com/boot2docker/boot2docker is v1.12.4-rc1
(local-docker01) Downloading C:\Users\leixu\.docker\machine\cache\boot2docker.iso from https://github.com/boot2docker/boot2docker/releases/download/v1.12.4-rc1/boot2docker.iso...
(local-docker01) 0%....10%....20%....30%....40%....50%....60%....70%....80%....90%....100%
Creating machine...
(local-docker01) Copying C:\Users\leixu\.docker\machine\cache\boot2docker.iso to C:\Users\leixu\.docker\machine\machines\local-docker01\boot2docker.iso...
(local-docker01) Creating SSH key...
(local-docker01) Creating VM...
(local-docker01) Using switch "External Wireless"
(local-docker01) Creating VHD
(local-docker01) Starting VM...
(local-docker01) Waiting for host to start...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env local-docker01
如果以上命令执行失败,一般是因为下载boot2docker.iso这个文件失败造成的。你可以实现从培训网盘中下载这个文件并放置在 C:Users{用户名}.dockermachinecache 目录中,这样就可以避免下载动作。
02. 查看docker-machine列表并使用ssh连接¶
通过docker-machine创建的容器化主机会在你的计算机上保存记录,可以通过docker-machine ls这个命令查询到所有已经连接到当前计算机的容器化主机。你可以看到以下列表中,我有1台本地hyper-v中的主机和2台运行在微软Azure云中的主机都可以被我本地的docker-machine进行操作。
λ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
local-docker01 - hyperv Running tcp://192.168.25.108:2376 v1.12.4-rc1
lxbj-docker01 - generic Stopped Unknown
lxhk-docker01 - azure Running tcp://207.46.131.190:2376 v1.12.3
lxhk-docker02 - azure Running tcp://207.46.130.226:2376 v1.12.3
这些主机的记录保存在 C:Users{当前用户}.dockermachinemachines 这个目录下

这些目录总保存了使用ssh key-pair连接这些主机所需要的所有证书和密钥文件,需要妥善保存。同时,这样我们也就可以直接ssh远程登录到这些主机上进行操作了
λ docker-machine ssh local-docker01
## .
## ## ## ==
## ## ## ## ## ===
/"""""""""""""""""\___/ ===
~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ / ===- ~~~
\______ o __/
\ \ __/
\____\_______/
_ _ ____ _ _
| |__ ___ ___ | |_|___ \ __| | ___ ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__| < __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
WARNING: this is a build from test.docker.com, not a stable release.
Boot2Docker version 1.12.4-rc1, build HEAD : 2a30a73 - Thu Dec 8 18:24:06 UTC 2016
Docker version 1.12.4-rc1, build 0d5bc84
docker@local-docker01:~$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
docker@local-docker01:~$
03. 将docker工具链接到远程docker-machine主机¶
由于docker工具本身是通过rest api调用远程主机的docker daemon的接口实现操作,我们可以通过修改本地docker命令所链接的远程主机地址的方式来实现操作目标的切换,docker-machine给我们提供了简化的操作命令。通过 docker-machine env {主机名称} 就可以获取这些链接参数并配置docker工具。
C:\Users\leixu
λ docker-machine env local-docker01
SET DOCKER_TLS_VERIFY=1
SET DOCKER_HOST=tcp://192.168.25.108:2376
SET DOCKER_CERT_PATH=C:\Users\leixu\.docker\machine\machines\local-docker01
SET DOCKER_MACHINE_NAME=local-docker01
SET DOCKER_API_VERSION=1.24
SET COMPOSE_CONVERT_WINDOWS_PATHS=true
REM Run this command to configure your shell:
REM @FOR /f "tokens=*" %i IN ('docker-machine env local-docker01') DO @%i
C:\Users\leixu
λ @FOR /f "tokens=*" %i IN ('docker-machine env local-docker01') DO @%i
C:\Users\leixu
λ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
你可以看到在以上操作中,我通过调用
@FOR /f "tokens=*" %i IN ('docker-machine env local-docker01') DO @%i
将本地docker命令链接到了local-docker01这台容器主机上,后面的docker ps操作所针对的就是这台主机了。
04. docker-machine 的其他命令¶
docker-machine还提供很多丰富的命令可以帮助你管理容器化主机的生命周期,如:
- docker-machine rm {主机名}
- 删除主机
- docker-machine regenerate-certs {主机名}
- 重新生成证书和密钥,如果远程主机的ip地址发生了变化,我们一般需要使用这个命令才能从新连接
- docker-machine ip {主机名}
- 获取远程主机的ip地址
小结¶
至此,我们了解了如何使用docker-machine这个工具来管理多台容器化主机,这个工具可以帮助我们同事管理和操作多台主机,让我们可以把容器输送到不同的环境中运行。
在下一节中,我们将使用docker-compose命令完成单机上的多容器环境编排。
练习四:使用docker-compose命令¶
前面的实验中,我们虽然可以使用docker run命令来灵活的管理容器运行配置,但是我们必须每次都输入繁琐的命令。在真正的容器化开发运维环境中,我们需要一种方法可以将这些配置使用文件固化下来,以便简化操作避免在重复操作中出现失误。
docker-compose就是这样一个工具,我们可以通过一个配置文件完成一个复杂应用(可能涉及多个容器)的配置,并使用同一的命令来启动这个预先配置好的部署。
01. 准备开发环境¶
在之前创建的docker-training目录中创建一个叫做wordpress的子目录,并使用code .打开这个目录作为工作目录。
D:\docker-training
λ mkdir wordpress
λ cd wordpress\
λ code .
02. 创建docker-compose.yml文件¶
使用Visual Studio Code在wordpress目录中创建docker-compose.yml文件,并将以下内容复制进去保存。
version: '2'
services:
db:
image: harbor.devopshub.cn/library/mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: wordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
wordpress:
depends_on:
- db
image: harbor.devopshub.cn/library/wordpress:latest
links:
- db
ports:
- "8000:80"
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_PASSWORD: wordpress
03. 启动部署¶
配置文件编写完成后,我们在同一目录下就可以执行docker-compose up命令来启动这个部署了
λ docker-compose up -d
Starting wordpress_db_1
Starting wordpress_wordpress_1
注意以上我加入了-d参数,这和docker run中的-d参数是一样的会让容器进行后台运行。
这个部署中我们共启动了2个容器,分别为wordpress和mysql数据库,同时将这两个容器链接到一起形成一个网络。部署完成后,我们可以打开浏览器并那导航到http://localhost:8000 看到WordPress的初始化页面,你可以尝试初始化这个WordPress站点。

这个docker-compose配置文件中的内容如果使用docker run命令也可以完成,但是就必须依靠脚本来进行管理,不利于进行版本控制和编辑。相应的docker run命令如下
λ docker run -itd -e MYSQL_ROOT_PASSWORD=wordpress -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=wordpress --name db harbor.devopshub.cn/library/mysql:5.7
λ docker run -itd --link db:mysql -p 8000:80 -e WORDPRESS_DB_PASSWORD=wordpress harbor.devopshub.cn/library/wordpress:latest
04. 其他docker-compose命令¶
docker-compose也提供了其他一些命令帮助你管理容器的生命周期,如:
- docker-comopse start
- 启动当前部署
- docker-compose stop
- 停止当前部署
- docker-compose down
- 删除当前部署
- docker-compose ps
- 获取当前部署的运行情况
小结¶
至此,我们了解了一种更加便捷高效的容器部署方法docker-compose。容器化开发运维的优势也越发显现出来,你可以看到,使用docker-compose我们可以将非常复杂的应用部署使用一个文件进行描述,对此配置文件进行版本控制,并使用简单的docker-compose up完成部署动作。这使得应用打包部署变得非常简单而且标准化,让我们后续构建DevOps交付管道的操作变得更加便捷。
练习五:创建私有容器仓库¶
在前面的实验中,我们一直都会从harbor.devopshub.cn这个站点中拉取各种容器镜像,这个站点就是一个私有的容器镜像仓库。在使用容器进行开发和部署的过程中,私有镜像仓库是必备的基础设施之一。我们使用docker build构建好的镜像需要通过这个仓库作为中转才能被转移到不同的主机上运行。
在这一节的实验中,我们将部署一个私有的容器镜像仓库并在不同的主机中进行部署。
01. 使用docker pull获取registry容器镜像¶
docker registry本身也是通过容器方式进行部署的,在docker hub上提供这个镜像,我们为了实验的方便讲此容器放置在了培训专用的仓库上,大家可以通过以下命令拉取这个镜像
λ docker pull harbor.devopshub.cn/library/registry
Using default tag: latest
latest: Pulling from library/registry
3690ec4760f9: Already exists
930045f1e8fb: Pull complete
feeaa90cbdbc: Pull complete
61f85310d350: Pull complete
b6082c239858: Pull complete
Digest: sha256:1152291c7f93a4ea2ddc95e46d142c31e743b6dd70e194af9e6ebe530f782c17
Status: Downloaded newer image for harbor.devopshub.cn/library/registry:latest
02. 部署docker registry容器¶
使用以下命令完成docker registry的本地部署
λ docker run -itd -p 5000:5000 harbor.devopshub.cn/library/registry
475f759532350258f1b01c6bd844f01617120b89d2b46f4073bb1d922e117564
λ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
475f75953235 harbor.devopshub.cn/library/registry "/entrypoint.sh /e..." Less than a second ago Up 50 seconds 0.0.0.0:5000->5000/tcp serene_noether
03. 向私有仓库推送镜像¶
为了能够将镜像推送到我们制定的仓库中,我们将借助docker tag命令来从新标识镜像,指定仓库地址并使用docker push命令完成推送。
λ docker tag php-webapp:1 localhost:5000/php-webapp:1
λ docker push localhost:5000/php-webapp:1
The push refers to a repository [localhost:5000/php-webapp]
1e6118bd9b21: Pushed
2e70ba22f008: Pushed
ffaf2a595e63: Pushed
983f9b97006e: Pushed
5a263f0f3836: Pushed
c4f7d35bb2df: Pushed
dbf739521f53: Pushed
3e019add9ad9: Pushed
18e3751aa1ef: Pushed
4eb95f543324: Pushed
a594229cce80: Pushed
69a54e38f06b: Pushed
d2808f8124fd: Pushed
fe4c16cbf7a4: Pushed
1: digest: sha256:cad34beb069e401143f4201e17c00b791a902259a7ca837c03cdb9fc2dd0b832 size: 3242
注意我们在tag命令中,将镜像名称前面添加了localhost:5000这个标识,这将允许docker push命令讲此容器识别为存放在localhost:5000这个地址上的仓库上的镜像,并推送到这个仓库。完成以上操作后,可以通过浏览器打开 http://localhost:5000/v2/_catalog 就可以看到我们推送的镜像了。

04. 从私有仓库中拉取镜像¶
你可以尝试使用以下命令删除本地的php-webapp:1这个镜像,然后再从刚才推送的仓库中拉取
λ docker rmi localhost:5000/php-webapp:1
λ docker rmi php-webapp:1
λ docker pull localhost:5000/php-webapp:1
1: Pulling from php-webapp
1fad42e8a0d9: Already exists
80da5904bcf7: Already exists
4a10fe3aed7b: Already exists
acfc8d985f74: Already exists
91dcca1807b6: Already exists
4d14b09788fd: Already exists
ce00e8bd626b: Already exists
3fb9a5d71f50: Already exists
4b446b1983e7: Already exists
d2750e5e5d54: Already exists
f542860ae524: Already exists
f3aa6073e05d: Already exists
1fe59f2680fe: Already exists
5682b09a0f60: Pull complete
Digest: sha256:cad34beb069e401143f4201e17c00b791a902259a7ca837c03cdb9fc2dd0b832
Status: Downloaded newer image for localhost:5000/php-webapp:1
你会注意到以上拉取过程中只有最后一个镜像层5682b09a0f60真正做了拉取操作,而其他的层都标识别 Already exists,这时因为我们在构建php-webapp这个镜像时所使用的php:7.0-apache镜像仍然在本地存在,这些 Already exists 的镜像层数据就会直接从这个镜像中重复使用,不必再次拉取。
小结¶
至此,我们就完成了Docker基本操作的所有试验。通过这些试验,你已经熟悉了使用docker进行容器化开发,打包和部署所需要的基本技能。灵活使用这些工具和命令,你就可以完成应用的容器化打包和部署。
练习六:端到端的docker开发,测试运维场景¶
在前面的练习中我们已经学习了如何使用基本的docker命令来管理容器的运行,打包和镜像拉取推送操作。在这个实验中,我们将把这些步骤串接起来形成完成的容器化开发,测试和运维场景。
01. 将远端的主机连接到本地docker-machine工具上¶
虽然docker-machine提供了非常方便的创建云端容器化主机的功能,但是在日常的开发中,我们往往会先有一台Linux主机存在,然后我们希望在上面安装docker引擎。在这个场景下,docker-machine所提供的通用驱动就非常有用了。
下面的命令就可以帮助你将一台已经在数据中心或者云端运行的主机连接到本地docker-machine工具上。
docker-machine create --driver generic --generic-ip-address={主机ip地址} --generic-ssh-key "{ssh连接私钥文件}" --generic-ssh-user {管理员用户名} --engine-install-url "https://get.daocloud.io/docker" {docker-machine 名称}
运行以上命令你需要具备以下条件:
- 主机可以通过ssh key的方式密码登录
- 主机管理员用户可以通过免密码的方式执行sudo
- 可以从本地访问的主机ip地址
- 主机防火墙开放了2376端口
以上所涉及的参数请从培训讲师处获取。
从讲师处获取到ssh-key以后,解压到docker-training/TeamX目录中,然后进入这个目录执行以下命令,确保通过./id_rsa可以访问到密钥文件。
以下为正常的输出结果
D:\docker-training\docker-machine-keys\demo
λ docker-machine create --driver generic --generic-ip-address=139.217.0.48 --generic-ssh-key "./id_rsa" --generic-ssh-user azureuser --engine-install-url "http s://get.daocloud.io/docker" docker-training-demo
Running pre-create checks...
Creating machine...
(docker-training-demo) Importing SSH key...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with ubuntu(systemd)...
Installing Docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env docker-training-demo
02. 配置docker-machine使用私有容器仓库¶
docker默认要求使用https来连接镜像仓库,使用https需要我们配置证书。在一般的开发环境中,这不是必须的,所以我们可以通过以下配置来允许docker使用http来连接私有仓库。
#使用docker-machine连接主机
docker-machine ssh {主机名称}
#编辑/etc/docker/daemon.json并添加私有仓库地址
sudo vi /etc/docker/daemon.json
- 在vi中按i输入以下内容
- { “insecure-registries”:[“harbor.devopshub.cn”,”localhost:5000”] }
按ESC退出编辑, 按:wq存盘退出
#从新启动docker daemon
sudo service docker restart
完成以上操作后,测试一下命令,如果可以成功拉取镜像则表示操作成功
#从harbor.devopshub.cn上拉取nginx镜像
sudo docker pull harbor.devopshub.cn/library/nginx
#正常的输出如下
azureuser@docker-training-demo:~$ sudo docker pull harbor.devopshub.cn/library/nginx
Using default tag: latest
latest: Pulling from library/nginx
386a066cd84a: Pull complete
386dc9762af9: Pull complete
d685e39ac8a4: Pull complete
Digest: sha256:3861a20a81e4ba699859fe0724dc6afb2ce82d21cd1ddc27fff6ec76e4c2824e
Status: Downloaded newer image for harbor.devopshub.cn/library/nginx:latest
03. 使用docker-machine远程操作主机¶
使用docker-machine env命令,我们可以将本地的docker工具链接到远程主机上,按照以下方式操作,以下操作将docker-machine env输出的最后一行命令执行后,本地的docker工具就已经连接到了远程的主机上。后续的操作都是针对远程主机执行。
λ docker-machine env docker-training-demo
SET DOCKER_TLS_VERIFY=1
SET DOCKER_HOST=tcp://139.217.0.48:2376
SET DOCKER_CERT_PATH=C:\Users\leixu\.docker\machine\machines\docker-training-demo
SET DOCKER_MACHINE_NAME=docker-training-demo
SET DOCKER_API_VERSION=1.24
SET COMPOSE_CONVERT_WINDOWS_PATHS=true
REM Run this command to configure your shell:
REM @FOR /f "tokens=*" %i IN ('docker-machine env docker-training-demo') DO @%i
D:\docker-training\docker-machine-keys\demo
λ @FOR /f "tokens=*" %i IN ('docker-machine env docker-training-demo') DO @%i
D:\docker-training\docker-machine-keys\demo
λ docker run -itd -p 80:80 harbor.devopshub.cn/library/nginx
c369822797c6da3b315587805702ff1157495f0e67c778592565f4f4c3837b10
D:\docker-training\docker-machine-keys\demo
λ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c369822797c6 harbor.devopshub.cn/library/nginx "nginx -g 'daemon ..." About a minute ago Up About a minute 0.0.0.0:80->80/tcp, 443/tcp elated_pasteur
以上命令执行完成后,用浏览器导航到主机ip地址,可以看到nginx的默认页面如下。

04. 在远程主机上部署docker registry¶
使用以下命令在远程主机上部署你自己的私有镜像仓库
D:\docker-training\docker-machine-keys\demo
λ docker run -itd -p 5000:5000 harbor.devopshub.cn/library/registry
Unable to find image 'harbor.devopshub.cn/library/registry:latest' locally
latest: Pulling from library/registry
3690ec4760f9: Pull complete
930045f1e8fb: Pull complete
feeaa90cbdbc: Pull complete
61f85310d350: Pull complete
b6082c239858: Pull complete
Digest: sha256:1152291c7f93a4ea2ddc95e46d142c31e743b6dd70e194af9e6ebe530f782c17
Status: Downloaded newer image for harbor.devopshub.cn/library/registry:latest
95488ebcbeba3b2cce47d946aa1fe87499fa82c51a02b26e13db92b8fcf7125b
D:\docker-training\docker-machine-keys\demo
λ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 95488ebcbeba harbor.devopshub.cn/library/registry "/entrypoint.sh /e..." 6 seconds ago Up 3 seconds 0.0.0.0:5000->5000/tcp agitated_galileo
c369822797c6 harbor.devopshub.cn/library/nginx "nginx -g 'daemon ..." 8 minutes ago Up 8 minutes 0.0.0.0:80->80/tcp, 443/tcp elated_pasteur
使用以下命令讲nginx镜像推送到你自己的私有镜像仓库
D:\docker-training\docker-machine-keys\demo
λ docker tag harbor.devopshub.cn/library/nginx localhost:5000/nginx
D:\docker-training\docker-machine-keys\demo
λ docker push localhost:5000/nginx
The push refers to a repository [localhost:5000/nginx]
a55ad2cda2bf: Pushed
cfbe7916c207: Pushed
fe4c16cbf7a4: Pushed
latest: digest: sha256:3861a20a81e4ba699859fe0724dc6afb2ce82d21cd1ddc27fff6ec76e4c2824e size: 948
完成后,用浏览器导航到 http://{主机ip}:5000/v2/_catalog 可以看到如下显示

05. 将我们之前打包的php-webapp应用通过私有镜像仓库部署到远程主机¶
如果你之前已经完成了练习二,那么在你本地的Docker for Windows环境中应该有一个php-webapp:1的镜像,现在我们要将这个镜像通过私有的镜像仓库部署到我们的远程主机中。
首先,我们需要对本地的Docker for Windows环境进行配置,允许它使用我们的私有仓库,在系统托盘的Docker图标上右键点击,并选择settings。在弹出的配置窗口中选择Daemon,并将{主机ip:5000}输入到insecure_registries配置节中,然后点击Apply。

完成后执行以下命令(注意,以下操作请打开新的cmder窗口进行操作,这样才能从新连接到本地的Docker上)
D:\docker-training\php-webapp
λ docker tag php-webapp:1 139.217.0.48:5000/php-webapp:1
D:\docker-training\php-webapp
λ docker push 139.217.0.48:5000/php-webapp:1
The push refers to a repository [139.217.0.48:5000/php-webapp]
6ea3081a3758: Pushed
2e70ba22f008: Pushed
ffaf2a595e63: Pushed
983f9b97006e: Pushed
5a263f0f3836: Pushed
c4f7d35bb2df: Pushed
dbf739521f53: Pushed
3e019add9ad9: Pushed
18e3751aa1ef: Pushed
4eb95f543324: Pushed
a594229cce80: Pushed
69a54e38f06b: Pushed
d2808f8124fd: Pushed
fe4c16cbf7a4: Pushed
1: digest: sha256:6b4024068e4a51d6981837bcdf57040a251bd889750595e0d62596d3a7928039 size: 3242
完成以上推送后,使用之前连接到远程主机的cmder窗口执行以下操作
D:\docker-training\docker-machine-keys\demo
λ docker run -itd -p 81:80 localhost:5000/php-webapp:1
Unable to find image 'localhost:5000/php-webapp:1' locally
1: Pulling from php-webapp
1fad42e8a0d9: Pull complete
80da5904bcf7: Pull complete
4a10fe3aed7b: Pull complete
acfc8d985f74: Pull complete
91dcca1807b6: Pull complete
4d14b09788fd: Pull complete
ce00e8bd626b: Pull complete
3fb9a5d71f50: Pull complete
4b446b1983e7: Pull complete
d2750e5e5d54: Pull complete
f542860ae524: Pull complete
f3aa6073e05d: Pull complete
1fe59f2680fe: Pull complete
7d3f664fb179: Pull complete
Digest: sha256:6b4024068e4a51d6981837bcdf57040a251bd889750595e0d62596d3a7928039
Status: Downloaded newer image for localhost:5000/php-webapp:1
34f7b90869cb3a70d25145ed5a8d183f04993baa65d2d90ea54b94ef4ae96a08
D:\docker-training\docker-machine-keys\demo
λ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 34f7b90869cb localhost:5000/php-webapp:1 "apache2-foreground" 7 seconds ago Up 4 seconds 0.0.0.0:81->80/tcp sleepy_heyrovsky
95488ebcbeba harbor.devopshub.cn/library/registry "/entrypoint.sh /e..." 21 minutes ago Up 21 minutes 0.0.0.0:5000->5000/tcp agitated_galileo
c369822797c6 harbor.devopshub.cn/library/nginx "nginx -g 'daemon ..." 29 minutes ago Up 29 minutes 0.0.0.0:80->80/tcp, 443/tcp elated_pasteur
完成以上操作后,使用浏览器打开 http://{主机ip}:81 就可以看到运行在远程主机上的php-webapp:1这个容器了。

小结¶
至此,我们就完成标准的容器化开发部署过程。在这个过程中,我们在本地完成容器镜像打包,上传到私有镜像仓库,并在远程主机上通过私有镜像仓库完成了容器部署。
Docker 普遍问题和解决方案¶
这部分实验将对Docker中普遍存在的问题和解决方式演练。
练习列表
练习一:熟悉并使用volume持久化容器数据¶
想要了解Docker Volume,首先我们需要知道Docker的文件系统是如何工作的。Docker镜像是由多个文件系统(只读层)叠加而成。当我们启动一个容器的时候,Docker会加载只读镜像层并在其上添加一个读写层。如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除Docker容器,并通过该镜像重新启动时,之前的更改将会丢失。在Docker中,只读层及在顶部的读写层的组合被称为Union File System(联合文件系统)。
为了能够保存(持久化)数据以及共享容器间的数据,Docker提出了Volume的概念。简单来说,Volume就是目录或者文件,它可以绕过默认的联合文件系统,而以正常的文件或者目录的形式存在于宿主机上。
01. 启动容器并挂接本地文件系统¶
运行以下命令启动容器,并将本地的docker-training目录内容挂接到容器内的docker-training目录中。
D:\docker-training
λ docker run -it -v d:/docker-training:/docker-training ubuntu /bin/bash
root@bdc134295528:/# cd docker-training/
root@bdc134295528:/docker-training# ll
total 4
drwxr-xr-x 2 root root 0 Dec 12 12:18 ./
drwxr-xr-x 1 root root 4096 Dec 12 22:41 ../
drwxr-xr-x 2 root root 0 Dec 12 13:54 docker-machine-keys/
drwxr-xr-x 2 root root 0 Dec 12 05:37 mesos-training-keys/
drwxr-xr-x 2 root root 0 Dec 11 05:44 php-webapp/
drwxr-xr-x 2 root root 0 Dec 11 07:24 wordpress/
root@bdc134295528:/docker-training# touch abc.txt
root@bdc134295528:/docker-training#
注意以上在容器内执行了 touch abc.txt 命令,创建了一个abc.txt的文件。现在从Windows上打开这个文件夹,也同样可以看到abc.txt文件。

你也可以尝试在Windows上直接编辑这个文件,然后在容器内执行 cat abc.txt,就可以看到修改过的内容了。
02. 使用数据卷进行应用开发和调试¶
进入之前我们创建好的php-webapp目录,然后执行以下命令,将src目录挂接到php:7.0-apache 这个容器的/var/www/html目录中。
D:\docker-training\php-webapp
λ docker run -itd -v d:/docker-training/php-webapp/src:/var/www/html -w /var/www/html --rm -p 8080:80 php:7.0-apache
68354123d5e20c4d8b76070933cd814f0332c3d25ae95d56da9475be9944614f
执行完毕后,打开浏览器并导航到 http://localhost:8080 ,同时使用Visual Studio Code 编辑index.php文件,刷新浏览器即可看到实时的编辑结果。

03. 使用数据卷容器¶
我们也可以通过 –volumes-from 这个参数将另外一个容器的数据卷目录挂接过来,如下:
D:\docker-training\php-webapp
λ docker create -v d:/docker-training/php-webapp/src:/var/www/html --name php-webapp-data php:7.0-apache /bin/true
0f46d6f1cd1e9f71b107b26af8a7450d4407efc4c1e53e70c32a48bc5260a35d
D:\docker-training\php-webapp
λ docker run -itd --volumes-from php-webapp-data --name php-webapp-run -w /var/www/html --rm -p 8080:80 php:7.0-apache
f53035d6a573d41ffaf5c3ebf1e5dd13798c1b47750f5e7d493c0eb8a776500c
注意第一个命令我们使用了docker create命令,这个命令只是创建了容器但是并没有运行容器。这样做的原因是因为第一个叫做php-webapp-data的容器只是为了给其他容器提供数据存储使用,本身并不需要运行。
使用数据卷容器,我们可以让其他容器使用同一的方式来运行,而不必关心数据来自磁盘的那个位置。同时,只要我们不使用docker rm命令销毁数据卷容器,那么这些数据就会一直存在(即便我们在执行docker create的时候不使用-v挂接主机目录)。这样,我们就可以随时启动,停止和销毁运行的容器,并在需要的时候挂接数据卷容器进行数据备份和迁移。
小结¶
至此,你应该对于如何对容器数据进行持久化有了初步的了解,灵活运用数据卷可以帮助我们更好的管理容器中的数据,完成如数据备份,数据迁移,容器转移等操作。
练习二:获取容器日志并对容器进行监控¶
01. 使用docker logs -f获取容器日志实时输出¶
使用以下命令链接到之前创建的docker-machine上,并针对php-webapp:1这个容器执行docker logs -f {容器id}命令,然后你可以使用浏览器刷新这个应用并看到日志的实时输出。
D:\docker-training
λ @FOR /f "tokens=*" %i IN ('docker-machine env {主机名称}') DO @%i
D:\docker-training
λ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
NAMES
2d6d22b5aa93 harbor.devopshub.cn/google/cadvisor:latest "/usr/bin/cadvisor..." 7 minutes ago Up 7 minutes 0.0.0.0:8080->8080/tcp
cadvisor
34f7b90869cb localhost:5000/php-webapp:1 "apache2-foreground" About an hour ago Up About an hour 0.0.0.0:81->80/tcp
sleepy_heyrovsky
95488ebcbeba harbor.devopshub.cn/library/registry "/entrypoint.sh /e..." 2 hours ago Up 2 hours 0.0.0.0:5000->5000/tcp
agitated_galileo
c369822797c6 harbor.devopshub.cn/library/nginx "nginx -g 'daemon ..." 2 hours ago Up 2 hours 0.0.0.0:80->80/tcp, 443/tcp
elated_pasteur
D:\docker-training
λ docker logs -f 34f7
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
[Mon Dec 12 13:47:44.156252 2016] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.10 (Debian) PHP/7.0.13 configured -- resuming normal operations
[Mon Dec 12 13:47:44.156480 2016] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
114.242.250.8 - - [12/Dec/2016:13:48:24 +0000] "GET / HTTP/1.1" 200 393 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36"
02. 使用google/cAdvisor对容器主机和应用进行监控¶
在以上主机中运行如下命令部署google/cadvisor
D:\docker-training\
λ docker run --volume=/:/rootfs:ro --volume=/var/run:/var/run:rw --volume=/sys:/sys:ro --volume=/var/lib/docker/:/var/lib/docker:ro --publish=8080:8080 --deta ch=true --name=cadvisor harbor.devopshub.cn/google/cadvisor:latest
time="2016-12-12T23:35:25+08:00" level=warning msg="Unable to use system certificate pool: crypto/x509: system root pool is not available on Windows"
Unable to find image 'harbor.devopshub.cn/google/cadvisor:latest' locally
latest: Pulling from google/cadvisor
e110a4a17941: Pull complete
e17fa94aae07: Pull complete
cb2aa9e31624: Pull complete
Digest: sha256:902449e23fc2cab1ca9788e2610b0928f27ee91e503f3326c7a463fa17d11dd9
Status: Downloaded newer image for harbor.devopshub.cn/google/cadvisor:latest
2d6d22b5aa9313382b9d63cbc3ab884b33acbb96cf21353169bfbcc1b892665b
部署完成后,使用浏览器导航到 http://{主机ip}:8080 ,可以看到如下页面,尝试对这台主机进行一些操作,如拉取镜像,启动和停止容器,可以看到监控数据的实时变化。

小结¶
至此,我们对如何监控容器环境进行了简单介绍。当前业界对容器监控的方案很多,大多数都是采用采集日志,并通过日志分析的方式来进行的。掌握获取容器内应用日志的方式是进行这些监控的基础。
练习三:熟悉docker网络配置¶
01. 使用docker network 命令查看默认网络¶
docker安装后会创建3个默认网络,可以通过docker network ls获取
azureuser@docker-training-demo:~$ sudo docker network ls
NETWORK ID NAME DRIVER SCOPE
ef1aabf2e9a8 bridge bridge local
5c8fb60aae54 host host local
0e4b98d9229b none null local
azureuser@docker-training-demo:~$
使用docker运行容器的时候回默认使用bridge(桥接)网络,我们可以通过以下命令看到当前连接到bridge网络的容器列表
azureuser@docker-training-demo:~$ sudo docker network inspect bridge
[
{
"Name": "bridge",
"Id": "ef1aabf2e9a8978092882473b767cd317ae5eff13f4883ea6b74397e4c916521",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": [
{
"Subnet": "172.17.0.0/16",
"Gateway": "172.17.0.1"
}
]
},
"Internal": false,
"Containers": {
"2d6d22b5aa9313382b9d63cbc3ab884b33acbb96cf21353169bfbcc1b892665b": {
"Name": "cadvisor",
"EndpointID": "85c63cc892b64ab4cb347179e47ac4c0df8cf659f78129f15f4e46499c932a84",
"MacAddress": "02:42:ac:11:00:05",
"IPv4Address": "172.17.0.5/16",
"IPv6Address": ""
},
"34f7b90869cb3a70d25145ed5a8d183f04993baa65d2d90ea54b94ef4ae96a08": {
"Name": "sleepy_heyrovsky",
"EndpointID": "bf333aef7d10ab8383afce6baf1d09ab87a0350b0962fe80d4d71363c8971db6",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/16",
"IPv6Address": ""
},
"95488ebcbeba3b2cce47d946aa1fe87499fa82c51a02b26e13db92b8fcf7125b": {
"Name": "agitated_galileo",
"EndpointID": "9edd61d6d6252b92c4bb0fdb8fb94ff7f7babee0634fcc78c84255eb80f5a8ef",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"c369822797c6da3b315587805702ff1157495f0e67c778592565f4f4c3837b10": {
"Name": "elated_pasteur",
"EndpointID": "5df25f5c9e0046ae898c567481b785514233b584c9934cace1eb3cffc813c73c",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
"Options": {
"com.docker.network.bridge.default_bridge": "true",
"com.docker.network.bridge.enable_icc": "true",
"com.docker.network.bridge.enable_ip_masquerade": "true",
"com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
"com.docker.network.bridge.name": "docker0",
"com.docker.network.driver.mtu": "1500"
},
"Labels": {}
}
]
azureuser@docker-training-demo:~$
02. docker0桥接网络工作机制¶
通过运行 brctl show 和 bridge li 命令,我们可以看到默认的docker0网桥当前连接了4个不同的虚拟网卡,这4个虚拟网卡分别来自以上4个容器。
azureuser@docker-training-demo:~$ brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242a0e02083 no veth1e723b2
veth3ab9b8f
veth798aefc
vethcc9a5d0
azureuser@docker-training-demo:~$ bridge li
6: veth798aefc state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master docker0 state forwarding priority 32 cost 2
8: veth3ab9b8f state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master docker0 state forwarding priority 32 cost 2
10: veth1e723b2 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master docker0 state forwarding priority 32 cost 2
12: vethcc9a5d0 state UP @(null): <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 master docker0 state forwarding priority 32 cost 2
在运行 iptables –list -t nat 命令,你可以清晰的看到每个容器所映射的端口
azureuser@docker-training-demo:~$ sudo iptables --list -t nat
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
DOCKER all -- anywhere anywhere ADDRTYPE match dst-type LOCAL
Chain INPUT (policy ACCEPT)
target prot opt source destination
Chain OUTPUT (policy ACCEPT)
target prot opt source destination
DOCKER all -- anywhere !127.0.0.0/8 ADDRTYPE match dst-type LOCAL
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 172.17.0.0/16 anywhere
MASQUERADE tcp -- 172.17.0.2 172.17.0.2 tcp dpt:http
MASQUERADE tcp -- 172.17.0.3 172.17.0.3 tcp dpt:5000
MASQUERADE tcp -- 172.17.0.4 172.17.0.4 tcp dpt:http
MASQUERADE tcp -- 172.17.0.5 172.17.0.5 tcp dpt:http-alt
Chain DOCKER (2 references)
target prot opt source destination
RETURN all -- anywhere anywhere
DNAT tcp -- anywhere anywhere tcp dpt:http to:172.17.0.2:80
DNAT tcp -- anywhere anywhere tcp dpt:5000 to:172.17.0.3:5000
DNAT tcp -- anywhere anywhere tcp dpt:81 to:172.17.0.4:80
DNAT tcp -- anywhere anywhere tcp dpt:http-alt to:172.17.0.5:8080
azureuser@docker-training-demo:~$
至此,你就可以理解docker的默认网络配置是如何完成容器的端口映射了。
03. 使用host网络¶
host网络与bridge网络不同,它直接将容器暴露于主机之上,与在主机上直接运行一个进程一样,这种方式下我们无法从新映射容器的端口,如果容器本身使用80端口,那么运行这个容器就意味着主机的80端口被占用。
可以使用以下命令使用host模式运行容器。
D:\docker-training
λ docker run --network=host -itd php-webapp:1
48b6ff6e82352a2231636b9c48ffc37ebd895f21ccb67d0e667054726d002d3f
D:\docker-training
λ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
48b6ff6e8235 php-webapp:1 "apache2-foreground" Less than a second ago Up 2 seconds quizzical_kirch
D:\docker-training
λ docker network inspect host
[
{
"Name": "host",
"Id": "dfb66a87b625dbc9c728a52c663714adabb9583265d491fb07abce405d498a34",
"Created": "2016-12-11T23:50:47.1002637Z",
"Scope": "local",
"Driver": "host",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": null,
"Config": []
},
"Internal": false,
"Attachable": false,
"Containers": {
"48b6ff6e82352a2231636b9c48ffc37ebd895f21ccb67d0e667054726d002d3f": {
"Name": "quizzical_kirch",
"EndpointID": "ec983950d261fb9ccefcf16438f77988a961f6103151e29ada74b1afff967b45",
"MacAddress": "",
"IPv4Address": "",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
小结¶
以上我们对docker的默认网络host和bridge进行了说明,并详细分析了docker0的bridge网络是如何提供端口转发能力的。使用docker0网桥让我们可以灵活的对已有容器的端口配置进行映射,这也让我们实现灵活部署中提供了很多方便,比如同时运行一个容器的多个实例并在他们中间实现负载均衡。
应用编排平台 Apache Mesos & Marathon¶
Mesos是Apache下的开源分布式资源管理框架,它被称为是分布式系统的内核。Mesos最初是由加州大学伯克利分校的AMPLab开发的,后在Twitter得到广泛使用。
本实验将使用微软云计算平台Microsoft Azure运行的Azure容器服务(ACS)来运行Mesos(DCOS)集群,展示其应用编排能力。Azure 容器服务特别为 Azure 优化了常用开源工具和技术的配置。你获得的开放解决方案为你的容器和应用程序配置提供可移植性。你只需选择大小、主机数和 Orchestrator (编排)工具选项,容器服务会为你处理所有其他事项。当前Azure容器服务支持以下编排工具:
- DCOS (Apache Mesos)
- Google Kubernetes
- Docker Swarm

本实验所使用的DCOS集群架构如下,为了演示用途,我们使用一个4节点的DCOS集群,其中包括
- 1个master节点用于管理员连接操作
- 1个公开的agent节点用于对外提供http服务
- 2个私有的agent节点用于集群内部服务

练习列表
练习一:连接Mesos(DCOS)集群¶
为了能够操作运行在Azure容器服务中的DCOS集群,我们需要在本地计算机和DCOS集群的master节点之间创建一个SSH隧道,并将所有流量导入到这个隧道,这样我们才可以通过本地计算机操作DCOS集群。
完成以上操作,请从培训网盘下载以下文件并从讲师处获取ssh-key文件和DCOS地址
- Firefox Setup 50.0.2.exe
- putty.zip
请预先安装好firefox并将putty.zip解压。
使用putty配置ssh连接¶
将从讲师处获得的ssh-key文件保存到docker-training目录中,打开putty.exe并按照以下方式进行配置
- 填写DCOS集群管理地址
- 连接地址
- azure@{讲师提供的dcos地址}
- 端口号
- 2200

- ssh-key配置
选择保存在docker-training目录中的mesos-training-key.ppk文件 勾选 Allow agent forwarding

- 配置Agent forwarding
启动putty目录中的PAGEANT.EXE,并右键点击系统托盘中的小图标,选择View Keys

在弹出的窗口中选择 Add Key 并选择同样的ppk文件

- 配置ssh隧道
回到putty.exe的窗口,按照以下进行配置

这里我们配置了2个隧道,一个用于通过 http://localhost 连接DCOS管理界面,另外一个提供给firefox作为连接集群内资源的通道
使用firefox连接到DCOS管理界面¶
在putty上完成配置后,点击 Open,在弹出的Security Alert中点击Yes,可以看到如下窗口

在firefox中打开 http://localhost 可以看到DCOS的管理界面

小结¶
至此,我们就完成了本地环境到DCOS集群的连接,可以开始后续的实验了。
练习二:熟悉DCOS集群¶
在我们开始向DCOS集群部署应用之前,先通过一些接口熟悉一下集群的基本情况。DCOS提供了很多服务终结点(endpoints),可以通过REST API进行访问。
01. 获取Mesos集群信息¶
使用命令行执行以下命令
curl http://localhost/mesos/master/slaves
或通过浏览器直接访问 http://localhost/mesos/master/slaves

其他常用的Mesos服务终结点还包括
http://localhost/mesos/roles http://localhost/mesos/frameworks http://localhost/mesos/tasks http://localhost/mesos/flags
完整的终结点列表可以访问 http://mesos.apache.org/documentation/latest/endpoints/
另外可以通过以下地址直接访问mesos管理界面

02. 获取Marathon信息¶
使用命令行执行以下命令
curl http://localhost/marathon/v2/info
http://localhost/marathon/v2/info
或通过浏览器直接访问 http://localhost/marathon/v2/info

其他常用的Marathon服务终结点还包括
http://localhost/marathon/v2/leader http://localhost/marathon/v2/apps http://localhost/marathon/v2/tasks
完整的终结点列表可以访问 https://mesosphere.github.io/marathon/docs/rest-api.html
另外可以通过以下地址直接访问Marathon管理界面

小结¶
现在,我们已经对DCOS集群上的主要服务终结点有了基本了解,下面让我们开始在DCOS上部署一些应用。
练习三:使用DCOS运行应用¶
在DCOS(Mesos)上,我们运行服务(持续运行)或者任务(单次或按照计划)。这个实验中我们将通过一个简单的命令行进程和一个容器来熟悉这2中运行模式。
01. 运行一个服务进程¶
在DCOS首页上点击 Service | Deploy Service

在弹出的页面中输入
ID:
myfirstapp
Command:
/bin/bash -c "for i in {1..5}; do echo MyFirstApp $i; sleep 1; done"

然后单击Deploy
服务启动后,可以在列表中看到此服务的运行状态。

02. 对应用进行扩容¶
在myfirstapp上点击齿轮标志,并选择 Scale

输入2,并点击Scale Service

稍等几秒钟,你会注意到此服务的运行状态发生变化,现在有2个节点在运行。

此时,可以切换到Dashboard上看到集群状态的变化

再次尝试将此服务扩容到3个节点,你会注意上Mesos无法完成这次扩容。这是因为我们对此服务的定义是要求每个节点使用1个CPU,而我们的集群中只有2个单核CPU的Agents,所以Mesos无法完成这次扩容。
你可以尝试修改此服务的CPU限制(比如修改为0.1),然后再次尝试扩容到更多节点数,理论上你应该可以运行20个每个0.1CPU的节点。


03. 运行单次任务¶
在DCOS首页左侧点击Jobs,并点击New Job,输入
- ID
- Myjob
Command
/bin/bash -c "for i in {1..5}; do echo MyFirstApp $i; sleep 1; done"

在 myjob 页面上点击 Run Now,任务会被启动,你可以尝试多次点击运行。

03. 运行周期性任务¶
在myjob页面上点击 Edit 按钮,切换到Schedule页面,在CRON Scheule中输入:* * * *

等待几分钟,你会注意到myjob以每分钟一次的频率在运行。

关于如何设置CRON Schedule可以参考 https://crontab.guru/
04. 使用容器运行服务¶
*执行以下操作前请将之前的myfistapp服务销毁掉
在DCOS首页上点击Service | Deploy Service
键入 /hello-world-container作为ID,并限制CPU为0.5

在 Container 页面中输入 hello-world 作为镜像名称

单击 Deploy
你会注意到这个容器会有非常短暂的时间处于Running状态,这是因为Hello-World容器只会输出一些信息然后便马上退出。但是Mesos会持续不断的尝试启动这个容器以保持1个实例的状态。如果你能捕捉到一个正在运行的实例,进入log页面。可以看到如下输出。

小结¶
DCOS 在默认安装了Marathon和Chronos这2个Framework之后,可以让我们运行长进程(服务)或者任务(单次或按照计划),而我们不必关心这些进程在集群中的那个节点上运行,这给我们充分调度集群资源提供了便利。
练习四:使用DCOS运行负载均衡的Web服务¶
这个实验中,我们将使用DCOS部署一个40节点的Web App并配置Marathon-lb对其进行负载均衡,同时允许用户通过DCOS的外部地址进行访问。
02. 部署Web App服务¶
在Service页面中点击 Deploy Service,切换至Json模式,然后复制一下内容到页面中,注意替换一下{DOCS集群的agentFQDN}为我们的集群agent对外dns地址。
{
"id": "web",
"container": {
"type": "DOCKER",
"docker": {
"image": "yeasy/simple-web",
"network": "BRIDGE",
"portMappings": [
{ "hostPort": 0, "containerPort": 80, "servicePort": 10000 }
],
"forcePullImage":true
}
},
"instances": 3,
"cpus": 0.1,
"mem": 65,
"healthChecks": [{
"protocol": "HTTP",
"path": "/",
"portIndex": 0,
"timeoutSeconds": 10,
"gracePeriodSeconds": 10,
"intervalSeconds": 2,
"maxConsecutiveFailures": 10
}],
"labels":{
"HAPROXY_GROUP":"external",
"HAPROXY_0_VHOST":"{DOCS集群的agentFQDN}",
"HAPROXY_0_MODE":"http"
}
}

完成后,等待此服务进入Runing状态。

此时,可以使用浏览器导航到 http://{DOCS集群的agentFQDN} 地址上,看到一下页面

03. 对web服务进行扩容¶
现在你可以尝试对这个web服务进行扩容,在我们的实验环境上你应该可以最大扩容到40个节点。完成刷新浏览器中的页面,你会看到基本上每次刷新WebServer的地址都会发生变化,这是因为我们的负载均衡在起作用。
04. Azure ACS 上的DCOS负载均衡架构¶
在以上配置中,我们的环境中分别由Azure的负载均衡器和运行在DCOS上面的Marathon-lb服务系统工作完成了外部流量向内部运行的container上面的负载均衡。此架构图如下:

小结¶
至此,我们就完成了在DCOS上面部署负载均衡的Web服务的操作。
常见问题¶
常见问题及解决方案¶
问题列表
安装Docker for Windows¶
Attention
安装前请确认:
- 本地操作系统为Windows 10并安装了最新更新。
- Windows 10上面已经启用了Hyper-V(如果没有启用,Docker for Windows会自动启用,不过安装过程会需要从新启动电脑)
- 如果您使用的不是Windows 10操作系统,请参考以下安装Docker Toolbox部分。注意Docker for Windows和Docker Toolbox只需要安装一个即可。Docker Toolbox是为Windows 10以下版本的Windows环境提供的Docker工具
双击文件InstallDocker.msi启动Docker安装向导。安装后启动Docker,可以在右下角看到运行的Docker托盘程序。

其他需要的安装的工具如下,请点击这里 http://pan.baidu.com/s/1kU7wmA7 下载预装软件,密码:密码:tsts。
文件列表:
- cmder.zip
- InstallDocker.msi
- VSCodeSetup-1.7.2.exe
- Git-2.11.0-64-bit.exe
安装Docker Toolbox¶
Attention
安装前请确认:
- 本地操作系统为Windows 7/8或Windows Server 2012 R2并安装了最新更新。
- Windows上面已经没有启动Hyper-V(因为Docker Toolbox会使用Virtual Box作为虚拟化工具,与Hyper-V不兼容)
- 如果您使用的是Windows 10操作系统,请参考以上安装Docker for Windows部分。注意Docker for Windows和Docker Toolbox只需要安装一个即可。Docker
双击文件DockerToolbox-1.12.3.exe启动Docker安装向导。安装后启动Docker,可以在右下角看到运行的Docker托盘程序。

其他需要的安装的工具如下,请点击这里 http://pan.baidu.com/s/1kU7wmA7 下载预装软件,密码:密码:tsts。
文件列表:
- cmder.zip
- DockerToolbox-1.12.3.exe
- VirtualBox-5.1.10-112026-Win.exe
- VSCodeSetup-1.7.2.exe
- Git-2.11.0-64-bit.exe
版权¶
本文档内容由LEANSOFT提供并拥有版权,访问者可将本文档提供的内容或服务用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯本文档及相关权利人的合法权利。除此以外,将本文档任何内容或服务用于其他用途时,须征得LEANSOFT的书面许可,并支付报酬。
版权所有(c)英捷创软科技(北京)有限公司 LEANSOFT