《精通Docker第三版》完整目录:
第一章 Docker概览
第二章 创建容器镜像
第三章 存储和发布镜像
第四章 管理容器
第五章 Docker Compose
第六章 Windows容器
第七章 Docker Machine
第八章 Docker Swarm
第九章 Docker和Kubernetes
第十章 在公有云上运行Docker
第十一章 Portainer – 一个Docker的GUI
第十二章 Docker安全
第十三章 Docker工作流
第十四章 Docker进阶
本章中,我们将一起来学习Portainer。Portainer是一个允许我们通过网页界面管理Docker资源的工具。本章将涵盖如下主要内容:
- Portainer的发展历程
- 设置和运行Portainer
- 使用Portainer和Docker Swarm
技术准备
和此前章节一样,我们将继续使用本地Docker软件。同样,本章中的截图来自我个人偏好的操作系统macOS。在本章的最后,我们将使用Docker Machine和VirtualBox来启动本地Docker Swarm集群。
如前,我们所运行的Docker命令可以在安装了Docker的三种操作系统上运行,但其中有少量的支持命令,可能仅能在macOS和Linux操作系统中使用。
观看如下视频可查看实时代码操作效果:
Portainer的历程
在我们撸起袖子安装和运行Portainer之前,先来谈谈这个项目的背景。本书的第一版讲到过Docker UI。Docker UI由Michael Crosby编写,在开发一年之后将该项目转交给了Kevan Ahlquist。正是在这一阶段,由于商标的问题,该项目被重命名为UI for Docker。
UI for Docker的开发持续到Docker开始在核心Docker引擎中加速引入一些功能如Swarm模式。在这期间 UI for Docker项目被复制到了后来成为Portainer的项目中,它的第一个大版本发布在2016年6月。
在初次公开发布之后,Portainer幕后团队评估大部分代码已更新或重写,于2017年中,添加了新功能,诸如其于角色的控制和对Docker Compose的支持。
2016年12月,UI for Docker的GitHub仓库中提交了一份通知,说明该项目已过时,应使用Portainer。
启动和运行Portainer
首先来看使用Portainer来管理本地运行的单Docker实例。我使用的是Docker for macOS,但操作指南应该适用其它系统安装的Docker。
- 首先,只需运行如下命令来从Docker Hub抓取容器镜像
12$ docker image pull portainer/portainer$ docker image ls - 可以通过运行docker image ls命令查看到,Portainer的镜像仅为74.1MB。启动Portainer,我们只需在macOS或Linux上运行如下命令:
1234$ docker container run -d \-p 9000:9000 \-v /var/run/docker.sock:/var/run/docker.sock \portainer/portainer - Windows用户需要运行如下命令:
12$ docker container run -d -p 9000:9000 -v\\.\pipe\docker_engine:\\.\pipe\docker_engine portainer/portainerℹ️可以从刚刚运行的命令中看出,我们在Docker主机上为Docker引擎挂载了套接字文件。这么做可以允许Portainer对宿主机上的Docker引擎拥有全部的访问权限。这样它能管理主机上的Docker,但这也意味着Portainer对宿主机拥有了全部的权限,需要在访问时以及在远程主机上对外暴露Portainer时要格外小心。
以下是在macOS执行时的显示:
123456789101112131415$ docker image pull portainer/portainerUsing default tag: latestlatest: Pulling from portainer/portainerd1e017099d17: Pull complete0b1e707a06d2: Pull completeDigest: sha256:d6cc2c20c0af38d8d557ab994c419c799a10fe825e4aa57fea2e2e507a13747dStatus: Downloaded newer image for portainer/portainer:latest$ docker image lsREPOSITORY TAG IMAGE ID CREATED SIZEportainer/portainer latest 19d07168491a 7 weeks ago 74.1MB$ docker container run -d \> -p 9000:9000 \> -v /var/run/docker.sock:/var/run/docker.sock \> portainer/portainer973962fbfa9c8d5fc2368aa868be9a8c1bffc97b9fdae2330aa1a87c90a02b3a - 对于最基本类型的安装,我们只需要运行这些命令。还有一些步骤可以完善这一安装,都是在浏览器中进行执行的。访问http://localhost:9000/来进行完善。
第一个欢迎页面是要求你为admin用户设置密码。 - 设置好密码后,你会进入到登录页面:输入用户名admin和你刚刚配置的密码。在登录后,会询问你所想要管理的Docker实例。有两个选项:
- 管理Portainer运行处的Docker实例
- 管理远程Docker实例
此时,我们想要管理Portainer所运行处的实例,即本地选项,而非默认的远程选项:
因为我们已经在启动我们的Portainer容器时考虑到了挂载的Docker套接字文件,可以点击Connect来完成安装。这样就会直接进入到Portainer本身,并显示仪表盘。
使用Portainer
现在我们已经运行了Portainer并配置与我们的Docker软件进行通讯,我们可以开操作左侧菜单上的任意功能,最上面的为仪表盘,也是Portainer软件的默认落脚页。
仪表盘
从下图中可以看出,仪表盘为我们展示了配置为与Portainer通讯的Docker实例当前状态的一个概览:
我这里显示了运行的容器数,当前仅有一个运行中的Portainer容器,还有一个我下载的镜像(译者本机原有镜像未删除,数量略多)。我们还可以看到Docker实例上可用的数据卷及网络的数量,同时它还会显示运行中的栈的数量。
它还显示了Docker实例自身的一些基本信息,可以看到Docker实例运行的是Moby Linux,有两个 CPU 和2 GB内存。这是Docker for Mac的默认配置。
仪表盘会根据Portainer所运行的环境来进行适配,我们在后面将Portainer关联到Docker Swarm集群时会再次进行访问。
应用模板
接着,我们来App模板。这一版块可能是唯一不直接存在于核心Docker引擎中的功能,它是从Docker Hub上下载容器来运行常用应用的一种方式:
Portainer中默认带有大约25个模板。模板以JSON格式进行定义。例如,nginx模板内容是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
{ "type": "container", "title": "Nginx", "description": "High performance web server", "categories": ["webserver"], "platform": "linux", "logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/nginx.png", "image": "nginx:latest", "ports": [ "80/tcp", "443/tcp" ], "volumes": ["/etc/nginx", "/usr/share/nginx/html"] } |
你还可以添加更多选项,例如MariaDB的模板文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
{ "type": "container", "title": "MariaDB", "description": "Performance beyond MySQL", "categories": ["database"], "platform": "linux", "logo": "https://portainer-io-assets.sfo2.digitaloceanspaces.com/logos/mariadb.png", "image": "mariadb:latest", "env": [ { "name": "MYSQL_ROOT_PASSWORD", "label": "Root password" } ], "ports": [ "3306/tcp" ], "volumes": ["/var/lib/mysql"] } |
可以看到,模板文件和Docker Compose文件非常相似,但这种格式仅用于Portainer。大部分选项的意思都一目了然,这里我们要讲到Name和Label选项。
对于通过环境变量传递自定义值来定义选项的容器,Name和Label选项让你可以在容器启动前要完成的自定义表单项展示给客户,如下图所示:
可以看到,我们可以输入一个希望MariaDB容器使用的root密码字段。填写该字段会将值作为环境变量进行传递,构建如下命令来启动容器:
1 |
$ docker container run --name [Name of Container] -p 3306 -e MYSQL_ROOT_PASSWORD=[Root password] -d mariadb:latest |
有关应用模板的更多信息,我推荐查看文档,在本章的扩展阅读部分可以找到相关链接。
容器
接下来看的左侧菜单是Containers。在这里我们启动Docker实例上运行的容器并与其交互。点击Containers菜单项会进入一个所有容器的列表,包含Docker实例上运行中和停止的容器。
可以看到,当前我只运行了一个容器,这正是Portainer容器。我们先不与它进行交互,点击+ Add container按钮来添加一个运行前面章节使用的cluster应用的容器。
在Create container页面上有好几个选项,可以填入如下内容:
- Name: cluster
- Image: russmckendrick/cluster
- Always pull the image: On
- Publish all exposed ports: On
最后,点击+ map additional port按钮来添加8080端口到容器的80端口的端口映射。完成后的表单类似下图:
完成后,点击Deploy the container,等待几秒,会返回一个运行中容器的列表,此时可以看到新运行的容器:
使用列表中每个容器左侧的复选框可以激活顶部的按钮,通过它们可以控制容器的状态,确保不要Kill或删除掉Portainer容器。点击容器的名称,比如cluster,会显示容器本身更多的信息:
可以看到,这里获得的容器的相关信息与运行如下命令所获取的信息相同:
1 |
$ docker container inspect cluster |
通过点击Inspect可以看到完整的输出。还可以看到有针对 统计、日志和控制台的按钮。
统计
Stats页面显示CPU、内存和网络工具,以及所检查的容器的进程列表:
如果保持页面打开图表会自动刷新,重新刷新页面会将图中的值置为0,从头开始记录。这是因为Portainer是使用如下命令通过Docker API来接收这一信息的:
1 |
$ docker container stats cluster |
每次刷新页面时,该命令就从头开始,因为Portainer当前没有在后台轮询Docker来记录每个运行中容器的数据。
日志
接着来看Logs页面。它显示运行如下命令的结果:
1 |
$ docker container logs cluster |
这显示STDOUT及STDERR的日志:
你还可以为输出添加时间戳,与运行如下命令是相同的:
1 |
$ docker container logs --timestamps cluster |
控制台
最后,还有Console。这会打开一个HTML5终端并允许你登录到运行中的容器中。在连接到容器前,你需要选择一个shell。有三种shell可供选择:/bin/bash , /bin/sh或/bin/ash,还可以选择连接所用的用户,默认为root。虽然cluster镜像两种都进行了安装,我选择使用/bin/bash:
1 |
$ docker container exec -it cluster /bin/sh |
从截图中可以看到,bash进行的PID为13。这个进程由docker container exec命令所创建,它是在从shell会话中断连接后会终止的唯一一个进程。
镜像
接下来一起来看左侧菜单Images。通过这里,你可以管理、下载及上传镜像:
在页面的顶部,你可以选择拉取镜像。例如,在输入框中输入amazonlinux,然后点击Pull the Image会从 Docker Hub下载一个亚马逊Linux容器镜像的拷贝。Portainer所执行的命令为:
1 |
$ docker image pull amazonlinux |
你可以通过点击镜像ID来查找各个镜像的更多信息,这时会进入到一个对运行如下命令的输出进行美化渲染的页面:
1 |
$ docker image inspect russmckendrick/cluster |
查看如下截图:
你不仅可以获得有关镜像的所有信息,还可以选择推送镜像的拷贝到所选择的仓库,默认为Docker Hub。
你还可以看到镜像中所包含的各层的完整做好事,显示在构建时所执行的命令以及各层的大小。
网络和数据卷
菜单中接下来的两项供我们管理网络和数据卷。我不会在这里过多讨论,因为它们本身也没太多可说的。
网络
这里你可以使用默认的bridge驱动器快速地添加一个网络。点击高级设置会进入一个包含更多选项的页面。包括使用其它驱动器、定义子网、添加标签和限制对网络的外部访问(译者注:新版已合并至一个页面)。和其它版块一样,你可以删除网络以及检查已有网络。
数据卷
这里除添加或删除数据卷外并没有太多选项。在添加数据卷时,你可以选择驱动器以及填写传递给驱动器的选项,这允许我们使用第三方驱动器插件。除此之外没什么可以再了解的了,连检查的选项都没有。
事件
events页面显示过去24小时的所有事件,你还可以对结果进行过滤,这样可以快速找到需要查找的信息:
这相当于运行如下命令:
1 |
$ docker events --since '2019-04-24T8:30:00' --until '2019-04-25T8:30:00' |
引擎
最后一条仅仅是输出如下信息:
1 |
$ docker info |
以下显示了该命令的输出:
如果你在定位多个Docker实例端点并需要知道端点所运行的环境时这会非常有用。
译者注:新版界面中有一些变化
到这里我们将进一步了解在Docker Swarm上运行Portainer,所以可以删除运行中的容器以及在启动Portainer时所创建的数据卷,你可以使用如下命令删除数据卷:
1 |
$ docker volume prune |
Portainer和Docker Swarm
前面一节,我们一起看了如何在独立的Docker实例上使用Portainer。Portainer还支持Docker Swarm集群,以及适配集群环境的界面选项。我们先来创建一个Swarm,然后启动Portainer来作为一个服务并查看变化。
创建Swarm
如同Docker Swarm一章那样,我们将使用Docker Machine来本地创建一个Swarm,只需要运行如下命令:
1 2 3 |
$ docker-machine create -d virtualbox swarm-manager $ docker-machine create -d virtualbox swarm-worker01 $ docker-machine create -d virtualbox swarm-worker02 |
启动了这三个实例之后,运行如下命令来初始化Swarm:
1 2 3 |
$ docker $(docker-machine config swarm-manager) swarm init \ --advertise-addr $(docker-machine ip swarm-manager):2377 \ --listen-addr $(docker-machine ip swarm-manager):2377 |
然后运行如下命令,插入你自己的令牌来添加worker节点:
1 2 3 4 5 6 7 |
$ SWARM_TOKEN=SWMTKN-1-45acey6bqteiro42ipt3gy6san3kec0f8dh6fb35pnv1xz291v-4l89ei7v6az2b85kb5jnf7nku $ docker $(docker-machine config swarm-worker01) swarm join \ --token $SWARM_TOKEN \ $(docker-machine ip swarm-manager):2377 $ docker $(docker-machine config swarm-worker02) swarm join \ --token $SWARM_TOKEN \ $(docker-machine ip swarm-manager):2377 |
现在已经形成了集群,运行如下命令来将本地Docker客户端指向manager节点:
1 |
$ eval $(docker-machine env swarm-manager) |
最后,使用如下命令查看Swarm的状态:
1 |
$ docker node ls |
Portainer服务
现在已经有了一个Docker Swarm集群,并且本地客户端已配置与manager节点进行通讯,我们只需通过运行如下命令来启动Portainer服务:
1 2 3 4 5 6 7 |
$ docker service create \ --name portainer \ --publish 9000:9000 \ --constraint 'node.role == manager' \ --mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \ portainer/portainer \ -H unix:///var/run/docker.sock |
可以看到,这会在manager节点上启动Portainer来作为服务,并让服务挂载manager节点的套接字文件,这样Swarm中其它节点对其都可见。你可以通过运行如下命令来查看服务启动是否有报错:
1 2 |
$ docker service ls $ docker service inspect portainer --pretty |
以下显示了输出:
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 |
$ docker service inspect portainer --pretty ID: dxfw2ofanczjpjkviljv32pyt Name: portainer Service Mode: Replicated Replicas: 1 Placement: Constraints: [node.role == manager] UpdateConfig: Parallelism: 1 On failure: pause Monitoring Period: 5s Max failure ratio: 0 Update order: stop-first RollbackConfig: Parallelism: 1 On failure: pause Monitoring Period: 5s Max failure ratio: 0 Rollback order: stop-first ContainerSpec: Image: portainer/portainer:latest@sha256:d6cc2c20c0af38d8d557ab994c419c799a10fe825e4aa57fea2e2e507a13747d Args: -H unix:///var/run/docker.sock Init: false Mounts: Target: /var/run/docker.sock Source: /var/run/docker.sock ReadOnly: false Type: bind Resources: Endpoint Mode: vip Ports: PublishedPort = 9000 Protocol = tcp TargetPort = 9000 PublishMode = ingress |
现在已启动了服务,你可以使用集群中任意节点的 IP 上的9000端口来访问Portainer,或者运行如下命令:
1 |
$ open http://$(docker-machine ip swarm-manager):9000 |
页面打开时,你会再次被要求为admin用户设置密码,设置完成后就会进入登录页面。登录后会直接进入仪表盘。原因是这次我们启动Portainer时,传递了参数-H unix:///var/run/docker.sock,它告诉Portainer选择我们在单机启动Portainer时手动选择的选项。
Swarm的不同
已经提到,在连接到Docker Swarm集群时Portainer界面会有一些变化。这一部分中会进行讲解。如果某部分未有的电脑,说明与在单机模式下运行的Portainer并不分别。
端点(Endpoint)
登录后的第一件事是选择端点,在下面的截图中可以看到有一个名为 primary的:
点击该端点会进入仪表盘,我们会在这一部分的最后再次来看端点。
仪表盘和Swarm
你会注意到的第一个变化是仪表盘现在展示了Swarm集群的信息,例如:
注意CPU 处为3、总内存为3.1 GB,集群内的每个节点有1个CPU且内存为1 GB,所以这些值为集群的总和。
点击 cluster vizualizer会进入Swarm页面, 这里有一个集群的视觉总览,唯一运行的服务是当前的Portainer:
栈
左侧菜单中我们没有讲到的是Stacks,这里我们可以人像在Docker Swarm一章中一样启动栈。让我们来拿已经使用过的Docker Compose文件为例,内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
version: "3" services: redis: image: redis:alpine volumes: - redis_data:/data restart: always mobycounter: depends_on: - redis image: russmckendrick/moby-counter ports: - "8080:80" restart: always volumes: redis_data: |
点击+ Add stack 按钮,然后将上面的内容粘贴到网页编辑器中,输入名称MobyCounter,不要添加空格或特殊字符,因为这个名称Docker要使用,然后Deploy the stack按钮。
部署完成后就可以点击MobyCounter并管理栈:
栈是服务的集合,下面我就来看这些服务。
服务
在这个页面你可以创建和管理服务,应该已经显示了几个包含Portainer在内的服务。为避免给运行中的Portainer带来任何问题,我们创建一个新服务。中需点击+ Add Service按键来进行创建。在加载的页面中输入如下内容:
- Name: cluster
- Image: russmckendrick/cluster
- Scheduling mode: Replicated
- Replicas : 1
这次我们需要添加一个主机8000端口与容器80端口之间的端口映射,因为我们在上一部分中所启动的栈已占用了主机上的8080端口:
输入宛信息后,点击Create the service按钮。你会回到服务列表页面,现在应该就会包含我们刚刚添加的cluster服务了。你可能注意到在scheduling mode页有一个scale的选项。点击它并将我们集群服务的拷贝的数量增至6.
点击名称列的cluster会进入一个服务的总览页面。可以看到,有很多服务相关的信息:
可以对服务进行实时的修改,包括位置约束、重启策略、添加服务标签等等。在页面的最底部是与服务关联的任务列表:
可以看到,我们有6个运行中的任务,三个节点上各有两具。点击左侧菜单中的Containers会显示一些你可能没预料到的信息:
只列出了三个容器,并且其中一个是Portainer服务。为什么呢?
如果你还记得Docker Swarm一章的话,我们学习了docker container命令实际上仅在运行它们的节点上进行了应用,因为Portainer是在和manager进行对话,那是docker container命令所唯一进行运行的节点。记住Portainer只是Docker API的一个网页界面,因此它与命令行中运行 docker container ls的结果相同。
添加端点(endpoint)
但是我们可以将剩下的两个集群节点加到Portainer中。要进行添加,点击左侧菜单的Endpoint项。
要添加端点,我们需要知道端点URL并可以访问证书,这样Portainer才能对运行在节点上的Docker守护进程验证自己。所幸的是,,我们使用Docker Machine启动的主机,这个工作非常简单。运行如下命令来获取端点URL:
1 |
$ docker-machine ls |
我本机的端点 URL 是192.168.99.101:2376和192.168.99.102:2376,你的可能会不同。我们需要上传的证书在你电脑的~/.docker/machine/certs/文件夹下。我建议运行如下命令来在finder中打开文件夹:
1 2 |
$ cd ~/.docker/machine/certs/ $ open . |
添加了节点之后,你可以使用Settings / Endpoints页面的+ Add Endpoint按钮来对其进行修改。
在这填写如下信息:
- Name: swarm-worker01
- Endpoint URL: 192.168.99.101:2376
- Public IP: 192.168.99.101
- TLS: On
- TLS with server and client verification: 勾选
- 从~/.docker/machine/certs/上传证书
然后点击+ Add endpoint按键,点击Home会带你进入本章这部分开头处所见到的Endpoint总览页面。从下面的截图中可以看到,worker节点分别运行了三个容器,并且它们 被标记为单机版而非 Swarm:
你会注意到除了在Endpoint中有的提及Swarm,其它地方没有提及Swarm服务。同样,这是因为Portainer所了解的内容和你的Docker节点一样,Swarm模式仅允许角色为manager的节点启动服务和任务来在集群内与其它节点交互。
别忘了通过如下命令删除本地的Docker Swarm集群:
1 |
$ docker-machine rm swarm-manager swarm-worker01 swarm-worker02 |
总结
至此就结束Portainer的深入学习。可以看出,Portainer非常的强大,但又很容易上手,还将继续壮大并集成更多的Docker生态系统所发布的功能。使用Portainer,你可以做大量的操作,不仅是对主机也可对单个或集群主机上运行的容器和服务。
下一章我们将在看如何保持Docker主机的安全以及如何对容器镜像进行扫描。
课后问题
- macOS或Linux电脑上,挂载Docker套接字文件的路径是什么?
- Portainer运行的默认端口是什么?
- 是非题:你可以使用Docker Compose文件来作为应用模板?
- 是非题:Portainer中显示的数字仅为实时的,你无法浏览历史数据?
扩展阅读
有关Portainer的更多信息参见:
- 官网:https://portainer.io/
- Portainter的GitHub地址:https://github.com/portainer/
- 最新文档:https://portainer.readthedocs.io/en/latest/index.html
- 模板文档:http://portainer.readthedocs.io/en/latest/templates.html