构建微服务实验环境(二):Docker Swarm 集群

构建微服务实验环境(二):Docker Swarm 集群

【摘要】微服务架构目前最流行的方案(最佳实践)是容器集群。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 集群
    • 1Docker Swarm 体验
      • 1 了解集群Swarm clusters
      • 2 配置集群
      • 3 在集群中部署服务和使用服务
      • 4 部署一个简单应用
    • 2容器与集群技术基础
      • 1 容器网络
      • 2 容器存储管理
        • 21 联合文件系统Union FS简介
        • 22 数据卷
        • 23 创建共享数据卷
        • 24 数据卷管理
      • 3 Docker 对象标签
    • 3Docker Swarm 管理
      • 1 管理集群节点与服务
      • 2 集群工作原理
      • 3 服务任务和容器
      • 4 Overlay 网络与服务发现
    • 小结

1、Docker Swarm 体验

docker swarm 是 Docker 公司的一个开源项目。自 v1.12.3 以来, docker swarm 成为 docker 引擎内置的服务。它和谷歌的 K8s 产生了直接竞争。很难说哪个好,对于熟悉 docker 容器的人来说,docker swarm 应该友好一些。

本部分内容主要来自 docker 官网 Get Started, Part 4~5

1.1 了解集群(Swarm clusters)

群集是一组运行 Docker 的计算机组成,其中部分机器是集群管理机(Swarm Manager) ,其他的是工作机(Worker)。在集群中,在 Manager 机器上使用 Docker 命令,会在群集上执行。群集中的机器可以是物理机或虚拟机。加入群集后,它们被统称节点(Node)

Swarm管理机可以使用几种策略来运行容器,例如“最空闲节点”- 容器会填充最少使用的机器。或“全局策略”,它确保每个机器能只能获得指定容器的一个实例。您可以将这些策略写在 docker-compose.yml 这样的服务组合定义文件中,Swarm管理机会按策略部署。

Swarm Manager是群集中唯一可以执行 docker 命令的机器,或授权其他机器作为工人机(worker)加入群集。worker 只是提供能力,没有权力指挥其他机器工作。

如果您已经熟悉单主机中使用Docker。现在,可切换到 Docker 集群模式。启用群组模式(swarm mode)使当前的机器成为群组管理机。这时,这台 Docker 将运行管理这个群集的指令,而不再是在当前的机器上运行。

主要概念

  • Swarm Manager 集群的管理者
  • worker 集群中的执行者
  • Node 集群中任意的 docker 机器
  • Swarm mode 集群模式, docker 机器的状态

1.2 配置集群

(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,配置如下:

  • docker-worker1:配置 192.168.56.111/24;主机名=“docker-worker1”
  • docker-worker2:配置 192.168.56.112/24;主机名=“docker-worker2”

配置完成后,一般用无界面模式启动,用 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.3 在集群中部署服务和使用服务

(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上发布的服务的路由网格的示例:

构建微服务实验环境(二):Docker Swarm 集群_第1张图片

注意:如果您遇到连接故障,请注意,为了在集群中使用入口网络,需要在启用群组模式之时,在群集节点之间使用以下端口:

端口7946用于容器网络发现的TCP / UDP。
端口4789 UDP用于容器入口网络。

1.4 部署一个简单应用

(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

一切似乎都很完美,其实服务之间依赖、在集群中数据迁移都是重要研究的话题。问题:

  • Redis 就必须绑定某个主机?
  • 服务启动顺序对应用有影响吗?
  • 管理机只能一台?
  • … …

当然,最大的麻烦还是:上午在官网读某文档,下午网站内容变了。

2、容器与集群技术基础

仓库、镜像、容器、服务、服务栈、dockerfile、compose.yaml 让我们看到容器简单易用的一面。事实上,任何和云相关的技术,需要你对网络、数据存储、操作系统、应用开发等等技术的修养,俗称“全栈”。

2.1 容器网络

首先,我们讨论容器的组网。本部分主要内容来源:Network containers

(1)准备环境

前面我们建立了3台机器的集群。现在我们把 worker1 撤出集群,完成以下实验。ssh 登陆 worker1:

# docker swarm leave

Node left the swarm.

成功退出 manager 组织的集群。

(2)容器运行的默认网络

Docker 通过使用网络驱动程序来支持容器的网络。Docker 目前提供bridgeoverlay 两个网络驱动程序。你可以利用网络驱动插件,创建自己的网络驱动。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 Swarm 集群_第2张图片

使用命令 docker network disconnect bridge networktest 可以将容器与网络断开,也可以使用 docker network connect bridge networktest 连接。

(3)创建用户的 bridge 网络

现在我们需要定制如图网络,怎么做呢?

构建微服务实验环境(二):Docker Swarm 集群_第3张图片

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.3db 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

小结:

  • 使用 docker network 命令可以创建、立表、删除 用户定义网络
  • 使用 –net 可以在特定网络上创建容器,也可以用 connect/disconnect 挂接或断开网络
  • 在自定义网络上,自带 dns 服务,可以用 容器名 访问服务

官网在不醒目的地方提及:

  • * bridge 网络上没有 dns 的支持,是为了兼容*
  • 服务与容器的区别只有在机器重新启动后你才知道,服务是自动启动的,docker run 的容器则不会。

2.2 容器存储管理

2.2.1 联合文件系统(Union FS)简介

容器文件系统是复杂的,入门者知道概念与原理及可。本部分细节请阅读 Understand images, containers, and storage drivers

容器文件系统:是容器镜像文件系统(只读)与可读写层(Thin)的文件系统联合而成的。如图:

一个容器文件,是多层叠加起来的,在下载容器时就可以直观观察到。Thin R/W 层,是当前容器文件读写层,如果你修改镜像层的文件,就会在 Thin 产生一个覆盖。多个容器实例运行时,层次共享情形如下:

为了高效管理容器文件,适应集群中跨机数据迁移、多机共享等应用场景,产生了许多解决方案,它依然是容器技术热点研究技术之一。

2.2.2 数据卷

数据卷是一个越过 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 目录下。

注意

  • docker run -v 主机目录卷仅支持全路径。如果 -v web:/webapp 则表示命名为 web 的数据卷,通常位置: /var/lib/docker/volumes/web
  • 数据卷旨在持久保留数据,而与容器的生命周期无关。因此,Docker 在删除容器时不会自动删除卷,也不会自动删除容器不再引用的卷。

(3)与主机共享文件/将主机文件作为卷

例如:

docker run -itd --name web -v ~/.bash_history:/root/.bash_history centos

2.2.3 创建共享数据卷

为了在容器间共享数据,需要创建共享数据卷。共享数据卷应用场景大致分为几类:

  • 一台主机上的多个容器共享数据卷
  • 通过 back-end 共享存储,如 iSCSI, NFS 等,创建多主机共享数据卷
  • 在集群中创建可在节点间迁移的数据卷

(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)、高可靠等多种设备解决方案。

2.2.4 数据卷管理

  • 数据卷列表: 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工具读写它们。在大多数情况下,您不应该这样做,因为如果容器和应用程序不知道您的直接访问,它可能会导致数据损坏。

2.3 Docker 对象标签

标签(Label)用于分类、描述对象语义,方便脚本过滤、选择容器进行操作。 可标签的对象包括:

  • 镜像
  • 容器
  • 本地守护进程
  • 数据卷
  • 网络
  • 集群节点
  • 集群服务

标签是一个键值对,通常在对象创建时用 –label 参数创建。

更多细节见 Docker object labels

3、Docker Swarm 管理

Docker Swarm 特点:

  • 与引擎集成
  • 去中心化的设计
  • 声明式的服务模型
  • 动态可伸缩服务
  • 自动状态协调
  • 多主机(Multi-host)网络
  • 负载均衡
  • 滚动更新
  • 默认安全模式

3.1 管理集群、节点与服务

Getting started with swarm mode 这部分主要讲了集群构建、节点管理与服务管理的命令,主要包括:

docker swarm
docker service
docker node

三个命令组。如果你了解前面的概念,按文档操作是比较容易的。

3.2 集群工作原理

一个 docker swarm 集群的组成结构,如图:

构建微服务实验环境(二):Docker Swarm 集群_第4张图片

它分为两类节点,manager 和 worker。

manager 节点处理集群管理任务:

  • 维护集群状态(配置)
  • 调度服务
  • 提供集群模式 HTTP API 端点服务

管理节点共同维护一个强一致状态数据(你可以认为每个管理节点拥有一个同步的集群状态/配置数据库),任何一个经理退出都不会导致集群失效。

高可靠模式 是指容器集群拥有 3 个以上 manager。一个 manager 是基数(如 3、5、7),一半以下 manager 失效都不会导致集群状态失效。Docker为群组建议最多七个管理器节点。

多个 7 个 manager 不会提高可靠性,反而会提高开销

强一致状态数据库常见实现包括: etcd 、consul 、zookeeper 等等。

worker 节点的唯一目的就是运行容器(服务)。默认情况下,manager 同时承担 worker 的职能,所以一个 manager 可以单节点运行,所有容器运行在一个节点中。

对于大型的容器集群,需要设置管理器节点的可用性设置为 Drain 以阻止服务的任务运行。

docker node update 是一个很有用的命令,可以改变节点的 role,label 等

3.3 服务、任务和容器

在集群模式下,服务(Service)部署时, manager 管理并维持服务的状态。如果服务定义了 N 个负载均衡任务,它按策略在集群中启动 N 个任务(Task),每个任务运行在集群的一个容器(Container)中。

构建微服务实验环境(二):Docker Swarm 集群_第5张图片

任务调度策略

docker 目前提供 复制全局 两种策略。复制策略可指定任务的数量,它可以按资源定义,选择合适的 host 运行任务。 全局策略指每个主机(host)启动一个服务,用于监控主机相关信息。

安全策略

Docker Engine内置的群集模式公钥基础设施(PKI)系统使安全部署容器编排系统变得简单。群集中的节点使用相互传输层安全(TLS)来认证,授权和加密他们与群集中其他节点之间的通信。

3.4 Overlay 网络与服务发现

docker 集群默认使用 Overlay 网络驱动,Overlay 驱动实现了跨主机集群内部虚拟网络。它的作用:

  • 将运行的多个容器(不同主机),附加(attach to)到一个网络
  • 默认情况下,服务发现为群集中的每个服务分配虚拟IP地址(VIP)和 动态 DNS,使其可以通过服务名称将其提供给同一网络上的容器。即在一个 Overlay 虚拟网络内,使用服务名称访问,将实现任务级别的负载均衡
  • 在群集中使用覆盖网络,需要在群集节点之间打开以下端口:
    • 端口7946 TCP / UDP用于容器网络发现。
    • 端口4789 UDP用于容器覆盖网络。

(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 作为服务部署, 为什么?

4 、 小结

本文给出了在 centos 环境下,完成 docker 官方集群教程(Part4-5)的要点。并给出了容器网络、存储、集群、服务发现管理的基础,以及集群的工作原理。

本文围绕集群、Manger、Worker、Service、Task 核心知识以及相关的操作,读者应可以在集群上发布与部署简单的 web 应用。

你可能感兴趣的:(微服务)