【摘要】微服务架构目前最流行的方案(最佳实践)是容器集群。AWS 的 ECS、Docker 的 Swarm、google 的 Kubernetes(K8s),apache 的 mesos,包括 Rancher Labs 的 Rancher 。 建议使用 AWS 或 阿里云 等共有云部署,会有更多技术支持,让你体验不同方案的优势。这里仅为了让你更好理解容器集群工作原理,介绍在 Centos 集群上部署 Swarm、K8s、 Rancher 的方案。
(1)容器与应用: 讲述 centos docker的安装,仓库、镜像、容器、服务的概念, dockerfile,compose 文件,容器管理图形界面,以单机操作为主线。
(2)Docker Swarm 集群:讲述集群(cluster)manager,worker,node 的概念与应用在集群部署。重点讲述容器网络、存储管理、集群管理、服务发现等知识。
(3)Rancher 管理平台:讲述多租户多主机容器运行管理平台 Rancher 的租户、环境、主机、环境模板概念与应用。包括 Rancher 自带集群管理 Cattle 的栈、服务的管理,需要读者对比与Docker Swarm 集群在容器基础服务、管理方式方面的差异
docker swarm 是 Docker 公司的一个开源项目。自 v1.12.3 以来, docker swarm 成为 docker 引擎内置的服务。它和谷歌的 K8s 产生了直接竞争。很难说哪个好,对于熟悉 docker 容器的人来说,docker swarm 应该友好一些。
本部分内容主要来自 docker 官网 Get Started, Part 4~5
群集是一组运行 Docker 的计算机组成,其中部分机器是集群管理机(Swarm Manager) ,其他的是工作机(Worker)。在集群中,在 Manager 机器上使用 Docker 命令,会在群集上执行。群集中的机器可以是物理机或虚拟机。加入群集后,它们被统称节点(Node)。
Swarm管理机可以使用几种策略来运行容器,例如“最空闲节点”- 容器会填充最少使用的机器。或“全局策略”,它确保每个机器能只能获得指定容器的一个实例。您可以将这些策略写在 docker-compose.yml 这样的服务组合定义文件中,Swarm管理机会按策略部署。
Swarm Manager是群集中唯一可以执行 docker 命令的机器,或授权其他机器作为工人机(worker)加入群集。worker 只是提供能力,没有权力指挥其他机器工作。
如果您已经熟悉单主机中使用Docker。现在,可切换到 Docker 集群模式。启用群组模式(swarm mode)使当前的机器成为群组管理机。这时,这台 Docker 将运行管理这个群集的指令,而不再是在当前的机器上运行。
主要概念:
(1) 准备机器
1) 清理 docker-master 机器
docker rm -f $(docker ps -q)
docker swarm leave --force
docker rmi registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello
2) 复制 docker-master 生成 workers
关闭 docker-master ,以它为模板,用 VBox 链接复制 workers,配置如下:
配置完成后,一般用无界面模式启动,用 ssh登陆
(2)创建 docker 集群
1) 创建第一个 Manager
ssh 连接 docker-master 机器上,启动 docker swarm mode :
# docker swarm init --advertise-addr 192.168.56.110
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-0o8kwg6rwcsyllo8yr4x24t304wtjvh01h1mq3jhcxi1qy5qrl-00hvxabe2qpuwyh4odii07omo \
192.168.56.110:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
这时,manager 将在端口 192.168.56.110:2377
侦听集群节点请求。
docker swarm join-token worker
显示作为 worker 加入该集群的指令docker swarm join-token manager
显示作为 manager 加入该集群的指令2) 将两个 worker 分别加入集群
分别用 ssh 连接两个 worker 。贴入 manager 上 docker swarm join-token worker
显示的指令, 格式如下:
docker swarm join \
--token \
:
This node joined a swarm as a worker.
这时,我们的第一个集群就建完了。
提示, 如果因为防火墙,无法加入集群,可使用以下命令关闭防火墙:
setenforce 0
systemctl disable iptables-services firewalld
systemctl stop iptables-services firewalld
3) 在 manager 上检查结果,这时使用 docker node
指令组
# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
srm9pnuj0zum7z79xb8ptnmdi * docker-master Ready Active Leader
vhrpxjbbu3ijt84t6c9cm2t64 docker-worker2 Ready Active
yzzt6k9rfcrti19ergdpde77u docker-worker1 Ready Active
在 manager 启动 Portainer 容器
在浏览器中, Swarm 菜单看到类似的结果。
(1) 部署服务
我们在 docker-master 机器上执行同样的部署命令
docker stack deploy -c docker-compose.yml myservice
检查结果:
# docker stack ps myservice
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
w04szt4o9u9b myservice_web.1 registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello docker-worker2 Running Running 5 minutes ago
t6bhw4yoybcr myservice_web.2 registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello docker-worker1 Running Running 5 minutes ago
wzdvrjgfeyiy myservice_web.3 registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello docker-worker2 Running Running 5 minutes ago
mr3zw4q6y99n myservice_web.4 registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello docker-master Running Running 5 minutes ago
kdu43k6ayfnz myservice_web.5 registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello docker-worker1 Running Running 5 minutes ago
果然,服务(容器)被平均分配到三台机器上了。
(2) 访问服务
使用集群中任意一台机器的 IP, 我们都能看到循环访问不同容器服务的结果,体现了负载均衡。
每个IP地址都工作的原因是群集中的节点参与入口路由网格(route mesh)。这样可以确保在群集中某个端口部署的服务始终将该端口保留给其自身,无论实际运行的是哪个节点。以下是在三节点群集my-web端口8080上发布的服务的路由网格的示例:
注意:如果您遇到连接故障,请注意,为了在集群中使用入口网络,需要在启用群组模式之时,在群集节点之间使用以下端口:
端口7946用于容器网络发现的TCP / UDP。
端口4789 UDP用于容器入口网络。
(1)集群容器可视化工具
官方文档 Part 5 介绍了官方的简单容器状态可视化工具,并将它dockersamples/visualizer
添加到项目中。
工作原理同 Portainer
, docker 的示例源代码 docker-swarm-visualizer
(2)添加数据服务
现在我们为 web 服务添加 Redis 数据服务。
1) 新建一个 compose 文件
cd && mkdir myapp && cd myapp
cp ../service_test/docker-compose.yml docker-compose.yml
mkdir data
注: data 目录用来存放 redis 的数据
2) 修改服务栈配置 vi docker-compose.yml
version: "3"
services:
web:
image: registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello
deploy:
replicas: 5
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "80:80"
networks:
- webnet
redis:
image: redis
ports:
- "6379:6379"
volumes:
- ./data:/data
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
networks:
webnet:
上述配置中,添加了 redis 这个服务,redis 服务被指定到 [node.role == manager] 机器,因为它与这台机器要共享数据卷 ./data 映射到容器 /data 数据卷
启动这个栈:
docker stack deploy -c docker-compose.yml myservice
网站会计数了,它们共享一个 Redis 数据服务。最后, docker stack rm myservice
(3) 什么是“栈(stack)”
就是一个用 compose 文件描述的一组服务的组合。 更多 compose 知识: Compose file version 3 reference 、Docker Cloud stack file YAML reference
一切似乎都很完美,其实服务之间依赖、在集群中数据迁移都是重要研究的话题。问题:
当然,最大的麻烦还是:上午在官网读某文档,下午网站内容变了。
仓库、镜像、容器、服务、服务栈、dockerfile、compose.yaml 让我们看到容器简单易用的一面。事实上,任何和云相关的技术,需要你对网络、数据存储、操作系统、应用开发等等技术的修养,俗称“全栈”。
首先,我们讨论容器的组网。本部分主要内容来源:Network containers
(1)准备环境
前面我们建立了3台机器的集群。现在我们把 worker1 撤出集群,完成以下实验。ssh 登陆 worker1:
# docker swarm leave
Node left the swarm.
成功退出 manager 组织的集群。
(2)容器运行的默认网络
Docker 通过使用网络驱动程序来支持容器的网络。Docker 目前提供bridge
和 overlay
两个网络驱动程序。你可以利用网络驱动插件,创建自己的网络驱动。Docker Engine 安装都会自动包含三个默认网络。使用 docker network
命令管理这些网络,例如:
# docker network ls
NETWORK ID NAME DRIVER SCOPE
4ff847509bb3 bridge bridge local
68f3b3d86d97 host host local
690f242840f5 none null local
bridge
是一个特殊的网络。除非您特别说明,否则Docker会在此网络中启动容器。例如:
docker run -itd --name=networktest centos
我们在后台启动了一个叫 networktest
的容器,运行了 contos 容器。可以用一下命令,检查容器网络:
# docker network inspect bridge
...
"Containers": {
"56d7e244d4ddcf442ddd0211c0433d11871a6bd88be3704f61ca2ece734f2a06": {
"Name": "networktest",
"EndpointID": "7720f7cd31f8124c5177f002628988418e6bda92d7810511f66e033a297247db",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
...
或者
# docker inspect networktest
...
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "79e0f4bbc9211925bc5fd08c3401a581520ccd61dac014ae75dcbf1d1c3a3fa6",
"EndpointID": "7720f7cd31f8124c5177f002628988418e6bda92d7810511f66e033a297247db",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02"
}
}
...
这时,容器网络如图:
使用命令 docker network disconnect bridge networktest
可以将容器与网络断开,也可以使用 docker network connect bridge networktest
连接。
(3)创建用户的 bridge 网络
现在我们需要定制如图网络,怎么做呢?
1) 创建虚拟桥接网络 my_bridge
# docker network create -d bridge my_bridge
2)在虚拟桥接网络 my_bridge
创建名字叫 db
的容器
docker run -itd --name=db --net=my_bridge centos
检查结果:
# docker network ls
# docker network inspect my_bridge
记录 db 在 my_bridge 上的 ip 地址。
3) 在 bridge 上创建 web 的容器,并同时绑到 my_bridge
docker run -itd --name=web centos
docker network connect my_bridge web
检查结果, web ip 172.17.0.4;172.19.0.3
及 db ip 172.19.0.2
。 进入容器 web
docker exec -it web /bin/bash
你可以在容器中 ping db
在容器 db 中 ping web
4) 清理
docker rm $(docker ps -q) -f
docker network rm my_bridge
小结:
官网在不醒目的地方提及:
容器文件系统是复杂的,入门者知道概念与原理及可。本部分细节请阅读 Understand images, containers, and storage drivers
容器文件系统:是容器镜像文件系统(只读)与可读写层(Thin)的文件系统联合而成的。如图:
一个容器文件,是多层叠加起来的,在下载容器时就可以直观观察到。Thin R/W 层,是当前容器文件读写层,如果你修改镜像层的文件,就会在 Thin 产生一个覆盖。多个容器实例运行时,层次共享情形如下:
为了高效管理容器文件,适应集群中跨机数据迁移、多机共享等应用场景,产生了许多解决方案,它依然是容器技术热点研究技术之一。
数据卷是一个越过 Union FS 的特殊目录。用它可以实现数据持久化、和数据共享。简单描述,读写数据卷只会发生在该数据卷中,而不会访问 Union FS 系统,而提升容器数据访问效率。
(1)添加数据卷
例如:
docker run -itd --name web -v /webapp centos
这时,你在容器文件系统 /webapp
的位置创建了一个卷。进入该容器 docker exec -it web /bin/bash
,使用 ls
就会看到该目录,你可以用 vi aaa.txt
在其中创建文件。退出该容器
docker inspect web
...
"Mounts": [
{
"Type": "volume",
"Name": "8b6f417df2f99d59924276f9476a0759d73afb0d27db5d57d7d24d5b7ff3c56b",
"Source": "/var/lib/docker/volumes/8b6f417df2f99d59924276f9476a0759d73afb0d27db5d57d7d24d5b7ff3c56b/_data",
"Destination": "/webapp",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
...
这时,使用命令 ls
就看到你刚创建的文件了。
(2)将本机目录作为数据卷
docker rm web -f
docker run -itd --name web -v /root/web:/webapp centos
本命令就是将本机当前目录下的 /root/web
目录挂载到容器的 /webapp
目录下。
注意:
-v web:/webapp
则表示命名为 web 的数据卷,通常位置: /var/lib/docker/volumes/web
。(3)与主机共享文件/将主机文件作为卷
例如:
docker run -itd --name web -v ~/.bash_history:/root/.bash_history centos
为了在容器间共享数据,需要创建共享数据卷。共享数据卷应用场景大致分为几类:
(1)创建共享数据卷
Docker 17.04 通过 local
数据卷驱动(overlay)提供卷服务,其他版本,请使用 docker info
查看。
创建共享数据卷,非常简单。使用 docker volume
命令创建一个命名卷。例如:
docker volume create webdata
通过命令 ls /var/lib/docker/volumes/
就可以看到这个数据卷,使用也非常简单,例如:
docker run -itd --name web -v webdata:/webapp centos
(2)创建共享存储数据卷
官网资料提供了在 iSCSI 或 NFS 提供共享卷服务的案例,使用的驱动是 rancher/convoy 。
这是需要专题研究的内容,因为在不同网络和存储环境下,需要使用不同的驱动,参见:Use Docker Engine plugins 。涉及多租户(multi-tenant)、多主机(multi-host)、高可靠等多种设备解决方案。
docker volume ls
docker volume rm $(docker volume ls -q)
备份数据卷
由于一个容器可以加载多个数据卷,通常将 backup 目录加载到容器接口,典型的命令如下:
docker run -itd --name backup --volumes-from dbcontainer -v $(pwd):/backup centos
这样就把 dbcontainer 数据卷加载到容器 backup 中,然后,你可以随时进入 backup 容器, tar 到备份目录中。
使用共享卷的重要提示
多个容器也可以共享一个或多个数据卷。但是,写入单个共享卷的多个容器可能会导致数据损坏。确保您的应用程序旨在写入共享数据存储。
数据卷可以从Docker主机直接访问。这意味着您可以使用普通的Linux工具读写它们。在大多数情况下,您不应该这样做,因为如果容器和应用程序不知道您的直接访问,它可能会导致数据损坏。
标签(Label)用于分类、描述对象语义,方便脚本过滤、选择容器进行操作。 可标签的对象包括:
标签是一个键值对,通常在对象创建时用 –label 参数创建。
更多细节见 Docker object labels
Docker Swarm 特点:
Getting started with swarm mode 这部分主要讲了集群构建、节点管理与服务管理的命令,主要包括:
docker swarm
docker service
docker node
三个命令组。如果你了解前面的概念,按文档操作是比较容易的。
一个 docker swarm 集群的组成结构,如图:
它分为两类节点,manager 和 worker。
manager 节点处理集群管理任务:
管理节点共同维护一个强一致状态数据(你可以认为每个管理节点拥有一个同步的集群状态/配置数据库),任何一个经理退出都不会导致集群失效。
高可靠模式 是指容器集群拥有 3 个以上 manager。一个 manager 是基数(如 3、5、7),一半以下 manager 失效都不会导致集群状态失效。Docker为群组建议最多七个管理器节点。
多个 7 个 manager 不会提高可靠性,反而会提高开销
强一致状态数据库常见实现包括: etcd 、consul 、zookeeper 等等。
worker 节点的唯一目的就是运行容器(服务)。默认情况下,manager 同时承担 worker 的职能,所以一个 manager 可以单节点运行,所有容器运行在一个节点中。
对于大型的容器集群,需要设置管理器节点的可用性设置为 Drain
以阻止服务的任务运行。
docker node update
是一个很有用的命令,可以改变节点的 role,label 等
在集群模式下,服务(Service)部署时, manager 管理并维持服务的状态。如果服务定义了 N 个负载均衡任务,它按策略在集群中启动 N 个任务(Task),每个任务运行在集群的一个容器(Container)中。
任务调度策略
docker 目前提供 复制 和 全局 两种策略。复制策略可指定任务的数量,它可以按资源定义,选择合适的 host 运行任务。 全局策略指每个主机(host)启动一个服务,用于监控主机相关信息。
安全策略
Docker Engine内置的群集模式公钥基础设施(PKI)系统使安全部署容器编排系统变得简单。群集中的节点使用相互传输层安全(TLS)来认证,授权和加密他们与群集中其他节点之间的通信。
docker 集群默认使用 Overlay 网络驱动,Overlay 驱动实现了跨主机集群内部虚拟网络。它的作用:
(1) 创建 Overlay 网络
创建 Overlay 网络与创建 local 网络一样,仅需指定 --driver overlay
,例如:
docker network create \
--driver overlay \
--subnet 10.0.9.0/24 \
--opt encrypted \
my-network
默认情况下,群集中的节点会加密自身与其他节点之间的流量。可选–opt encrypted标志在覆盖驱动程序中为不同节点上的容器之间的vxlan流量启用附加的加密层。
(2)服务发现
默认情况下,创建一个连接到网络的服务时,群集将为该服务分配VIP。VIP根据服务名称映射到DNS别名。网络上的容器使用共享服务的DNS映射,因此网络上的任何容器都可以通过其服务名称访问该服务。
注意:只有用户自定义网络,才有 dns 服务和服务自动发现
如果你需要自己定义负载均衡,见 Attach services to an overlay network
最后
将 Portainer 和 Registry 作为服务部署, 为什么?
本文给出了在 centos 环境下,完成 docker 官方集群教程(Part4-5)的要点。并给出了容器网络、存储、集群、服务发现管理的基础,以及集群的工作原理。
本文围绕集群、Manger、Worker、Service、Task 核心知识以及相关的操作,读者应可以在集群上发布与部署简单的 web 应用。