Swarm 的部署

什么是 Docker

容器是一个伟大的工具。它将过去繁琐的部署过程变得简单。

在容器化的时代,开发者只需要将自己的应用打包成一个容器,然后将这个容器部署到任何支持容器的环境中,就可以运行自己的应用。

因此,运行一个软件的过程被完美的划分成了两个领域:

  • 开发一个优质的容器 (安全、稳定、快速、无状态、可复制)
  • 运维一个容器平台 (高可用、可扩展、自动化、监控、日志、网络、报警)

而 Docker 就是一个优秀的容器平台。它提供了一套完整的容器解决方案,包括容器的构建、部署、运行等。

file

这篇博客假想你已经完全掌握了 Docker 的基本概念,如果你还不了解 Docker,请先阅读 在服务器中部署单独的Docker应用

了解 Docker Compose

和 Docker 可以单独的隔绝一个进程的运行环境不同,Docker Compose 专注于使用一种声明式的语法来定义多个容器的运行环境。

Docker Compose 的核心概念是 docker-compose.yml 文件。这个文件中定义了多个容器的运行环境。例如:

version: '3'
networks:
  my-net:
    driver: overlay

services:
  web:
    build: .
    ports:
      - "5000:5000"
    networks:
      - my-net
    volumes:
      - .:/code
    depends_on:
      - redis

  redis:
    image: "redis:alpine"
    networks:
      - my-net

在上面的例子中,我们定义了两个服务:webredis

  • web 服务使用了一个自定义的镜像,它将本地的 . 目录挂载到了容器的 /code 目录中。
  • redis 服务使用了官方的 redis:alpine 镜像。

当我们使用 docker-compose up 启动时,Docker Compose 会自动的创建一个网络,然后将 webredis 两个容器连接到这个网络中。这样,web 容器就可以通过 redis 容器的服务名来访问 redis 服务。

file

Docker Compose 完美的解决了复杂的容器组合的需求下的部署流程。它的语法简单易懂,使用方便,是一个非常优秀的容器编排工具。

但是,Docker Compose 也有一些缺点。例如,它不支持集群模式,不支持动态扩容等。这些功能需要使用更加复杂的容器编排工具来实现。

了解 Docker Swarm Mode

Docker Swarm Mode,我这里简称 Docker Swarm,它是 Docker 官方提供的一个容器集群编排工具。它的目标是提供一个简单易用的容器编排解决方案。

Docker Swarm 可以使用和本地 Docker Composer 一致的定义来部署应用程序的编排计划。

Docker Swarm Mode 可以非常容易的让你实现:

  • 可重现:使用与本地相同的语法来部署应用程序,并得到相同的运行效果。
  • 集群:可以使用多台机器来部署应用程序,实现高可用和负载均衡。
  • 动态扩容:可以根据负载情况动态的扩容和缩容应用程序。
  • 简单:不需要额外的配置、安装,不需要学习新的概念。

如果你的电脑已经安装了 Docker,那么你也已经安装了 Docker Swarm。不需要额外的安装任何软件。

过时了?

Docker Swarm 并不流行,因为社区里有许多更加强大的容器编排工具:

  • Kubernetes
  • Mesos
  • Nomad
  • ...

file

但是,上述工具都需要你花费数天的时间来学习,会额外带来一组全新的概念,需要你掌握全新的配置、语法、文件格式、命令……

而 Docker Swarm 只需要你掌握 Docker Compose 的语法就可以直接使用。几乎不需要额外学习新知识。这是一个非常大的优势。

对于家庭的 HomeLab,员工人数小于 200,物理服务器数量小于 2000 的小公司,想搞点儿创业项目、业余项目、Hackthon等,我都更推荐 Docker Swarm 而不是其他的容器编排工具。

file

当然,如果你是 Google,Amazon 或 Microsoft,那么 Kubernetes 是一个更好的选择。

同样的,如果你阅读了这篇文章,决定去学习 Kubernetes,那么我也会为你感到高兴。因为 Kubernetes 是一个非常优秀的容器编排工具。

万丈高楼平地起,建起你的小集群

你需要提前掌握的知识:

  • Linux
  • Docker
  • 创建服务器

创建 3 台服务器

首先你需要准备 3 台服务器。名字分别叫:master, worker1, worker2

这 3 台服务器可以是物理服务器,也可以是虚拟机。你可以去某宝买几个小主机,也可以去云计算厂商那里购买VPS,当然也可以自己用 HyperV、ESXI、PVE、KVM 等软件创建。

file

这里你的 3 台服务器可以是任意操作系统的。但是我更推荐你使用 Ubuntu 22.04。我的例子中,所有的服务器都是 Ubuntu 22.04。

准备网络

这里,我建议你的三台服务器都在同一个专属局域网中。这样你就不需要额外的配置防火墙、路由器等。

云厂商一般都提供了虚拟交换机的功能,你可以使用这个功能来创建一个专属局域网。当然,HyperV、ESXI、PVE、KVM 等软件也都提供了类似的功能。

我假设你的三台服务器的内网 IP 地址分别是:

  • master: 10.0.0.100/8
  • worker1: 10.0.0.101/8
  • worker2: 10.0.0.102/8

使用下面的命令分别给三台服务器设置 Hostname:

# Server 1
sudo hostnamectl set-hostname master
# Server 2
sudo hostnamectl set-hostname worker1
# Server 3
sudo hostnamectl set-hostname worker2

这里你需要确保三台服务器之间的网络都是通的。你可以使用 ping 命令来测试。

配置服务器

这样你的三台服务器的 Hostname 分别叫:

  • master
  • worker1
  • worker2

这里 master 将用于管理集群,三台服务器都将可以运行容器。

为它们安装好必要的更新:

# Run on all 3 servers!
sudo apt update
sudo apt upgrade -y

分别在三台服务器上安装 Docker:

# Run on all 3 servers!
curl -fsSL get.docker.com -o get-docker.sh
CHANNEL=stable sh get-docker.sh
rm get-docker.sh

file

新建集群

这里登录你的 master 服务器,然后运行下面的命令:

sudo docker swarm init --advertise-addr 10.0.0.100

对于 Vsphere 数据中心,建设的 Swarm 集群可能无法使用 Overlay Network 。我被这个错误折磨了很久,最终找到了一个解决方案:

sudo docker swarm init --advertise-addr=10.0.0.100 --listen-addr=0.0.0.0 --data-path-port=7779 --force-new-cluster=true

这里,10.0.0.100 是你的 master 服务器的内网 IP 地址。

很快,你就会看到一个输出,指导你如何将 worker1 和 worker2 加入到集群中。

file

docker swarm join --token SWMTKN-1-XXXXXXXXX 10.0.0.100

你需要在 worker1 和 worker2 服务器上运行这个命令,它们就加入这个集群了:

file

现在检查一下你的集群吧!前往 master 服务器,运行下面的命令:

sudo docker node ls

你会看到成功的输出:

file

当然,上面的方法,setup好的集群是一个 Manager,两个 Worker。它带来的问题是:如果 Manager 挂了,就会丧失管理能力。你也可以选择将三个节点全部加成 Manager,来提高一些可用性。

# 在 Manager 上运行
sudo docker swarm join-token manager
# 在 Worker 上运行
docker swarm join --token SWMTKN-1-YYYYYYYYY 10.64.50.159:2377

集群配置完成

就这么简单。现在你已经有了一套分布式 Docker Swarm 集群了。

我们接下来将详细介绍如何为这个集群部署应用程序。

部署一个图形界面的管理器

显然,看着这些命令行,或许令人感到非常不直观。我的集群到底什么样了?

目前最流行的 Docker Swarm 图形界面管理器是 Swarmpit。它是一个开源的项目,提供了一个非常漂亮的图形界面来管理你的 Docker Swarm 集群。

file

在 master 节点上运行下面的命令:

sudo docker run -it --rm \
  --name swarmpit-installer \
  --volume /var/run/docker.sock:/var/run/docker.sock \
swarmpit/install:1.9

这会在你的集群中部署 Swarmpit。

上面的命令可能需要一些时间来完成。你可以使用下面的命令来查看部署的状态:

sudo docker service ls

如果 service 的 replicas 数量一直是 0/1,可能是部署失败了。你可以先检查一下它的进程状态:

sudo docker service ps <app-name, may be 'swarmpit_app'>

你可以使用下面的命令来查看部署的日志:

sudo docker service logs <app-name, may be 'swarmpit_app'>

file

如果最终你看到了上面类似的输出,证明你的 Stack 里所有 Service 都部署好了。你可以正常浏览访问了!

你可以在浏览器中访问 http://master:888 来查看 Swarmpit 的图形界面。

第一次访问可以创建一个管理员用户。

file

当然,如果你不小心搞砸了什么想重来这一步,直接运行下面的命令:

sudo docker stack rm swarmpit

学习基本概念

这个博客的顺序看起来奇怪,直到这里,我才开始向你讲述基本概念的关系。

我相信你已经理解了 Docker Stack 的概念。Stack 和 docker-compose.yml 文件的概念是一样的。它们都是用来定义多个容器的运行环境。

每个 docker-compose.yml 文件都可以被看作是一个 Stack 的模板。

而 Service 是 Stack 的基本组成单位。它代表了一个 Stack 需要运行的一个服务。注意:Service 并不是一个容器,而是一组容器,它们共享同一个配置、网络、存储等,共同完成一个任务。

而每个 Service 又可以包含多个 Task。Task 是 Service 的基本组成单位。它代表了一个 Service 需要运行的一个真正容器。

Task 是可以和容器一一对应的。一个 Task 就是一个容器。因此,对于一个 Service 来说,它的 Task 数量就是它的容器数量。你也可以通过 docker ps 来查看 Task 的状态。

file

而这些 Task 一定需要真实的硬件资源来运行。这些资源就是 Node。Node 是 Docker Swarm 集群的基本组成单位。它代表了一个真实的 Linux 服务器。

Node 可以是 master 服务器,也可以是 worker 服务器。它们都可以运行容器。

一个 Stack 除了可能包含一系列的 Service 之外,还可以包含一系列的 Volume。

Volume 是用来存储数据的。它可以被多个容器共享。它可以被用来存储数据库、文件、日志等。Service 可以显示的指定自己哪些路径需要使用哪些 Volume。

除了 Volumn,还有一个重要的概念就是 Network。Network 是用来连接容器的。它可以被多个容器共享。它可以被用来连接数据库、缓存、日志等。

练习管理这些基本概念

在 Swarmpit 的图形界面中,你可以看到所有的 Service、Task、Node、Volume、Network。

当然,这里我们仍然使用命令行,你也可以结合 Swarmpit 的图形界面来练习管理这些基本概念。

file

对于 Stack 的管理:

# 创建一个 Stack
sudo docker stack deploy -c <stack.yml> <stack-name>

# 查看所有的 Stack
sudo docker stack ls

# 查看一个 Stack 里的所有 Service
sudo docker stack services <stack-name>

# 删除一个 Stack
sudo docker stack rm <stack-name>

file

对于 Service 的管理:

# 查看所有的 Service
sudo docker service ls

# 查看一个 Service 里的所有 Task
sudo docker service ps <service-name>

# 查看一个 Service 的详细信息
sudo docker service inspect <service-name>

# 查看一个 Service 的日志
sudo docker service logs <service-name>

# 删除一个 Service
sudo docker service rm <service-name>

file

对于 Node 的管理:

# 查看添加 Node 的命令
sudo docker swarm join-token worker
sudo docker swarm join-token manager

# 查看所有的 Node
sudo docker node ls

# 查看一个 Node 的详细信息
sudo docker node inspect <node-name>

# 查看一个 Node 上运行的所有 Task
sudo docker node ps <node-name>

# 将一个 Node 下线维护
sudo docker node update --availability drain <node-name>

# 将一个 Node 上线
sudo docker node update --availability active <node-name>

# 踢出一个 Node
sudo docker node rm <node-name>

file

有时,我们需要在一个具体的容器里运行一些调试命令。可以:

# 先通过 Service 定位其是哪个 Task
sudo docker service ps <service-name>
<task-name> <node-name>

# SSH 到这个 Node
ssh user@<node-name>

# 找到其对应的容器 ID
sudo docker ps
<container-id> <image-name>

# 进入容器
sudo docker exec -it <container-id> sh

file

部署一个真实的业务

现在你对核心概念都很了解了,也部署了 Swarmpit 来管理。是时候部署你真正的业务了。.

真正部署时,你只需要写好yml文件。如果业务发生了变更,直接修改 yml 文件,重新运行一次部署脚本即可。

例如,如果你需要部署 moongladepure:

version: '3.3'

services:
  moongladepure:
    image: hub.aiursoft.cn/aiursoft/moongladepure:latest
    volumes:
     - moonglade-data:/data
    deploy:
      replicas: 5
      endpoint_mode: vip

    ports:
      - 8000:5000

volumes:
  moonglade-data:
    driver: local

然后运行:

sudo docker stack deploy -c moongladepure.yaml moon

很快,你访问任意一个 Node 的 8000 端口就是你部署的业务了。

当然,上面这种暴露 8000 端口的方式并不是最佳实践。

你可以部署一个 caddy,将其 80 和 443 端口暴露,从而提供自动化 HTTPS 。

无限的玩法召唤着你!

建设共享存储

# Assuming using /dev/sdb is your new empty disk
# Assuming 10.0.0.* is your private network

# Install and configure GlusterFS. (Run on all nodes)
apt-get install glusterfs-server -y
systemctl start glusterd
systemctl enable glusterd

# Format the disk and mount it (Run on all nodes)
mkfs.xfs /dev/sdb
echo '/dev/sdb /var/no-direct-write-here/gluster-bricks xfs defaults 0 0' >> /etc/fstab
mkdir -p /var/no-direct-write-here/gluster-bricks
mount /var/no-direct-write-here/gluster-bricks

# Add the peers (Run on node1)
gluster peer probe node2
gluster peer probe node3
gluster peer status
gluster pool list

# Create the volume (Run on node1)
gluster volume create swarm-vol replica 3 \
  node1:/var/no-direct-write-here/gluster-bricks/swarm-vol \
  node2:/var/no-direct-write-here/gluster-bricks/swarm-vol \
  node3:/var/no-direct-write-here/gluster-bricks/swarm-vol
gluster volume set swarm-vol auth.allow 10.64.50.*
gluster volume start swarm-vol
gluster volume status
gluster volume info swarm-vol

# Mount the volume (Run on all nodes) 
echo 'localhost:/swarm-vol /swarm-vol glusterfs defaults,_netdev,noauto,x-systemd.automount 0 0' >> /etc/fstab
mkdir -p /swarm-vol
mount /swarm-vol
chown -R root:docker /swarm-vol

# Final check
cd /swarm-vol/
df -Th .

# READ: https://docs.gluster.org/en/v3/Administrator%20Guide/Managing%20Volumes/

让节点重启后上线

echo "[Unit]
Description=Update Docker node availability
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c 'sleep 10 && sudo docker node update --availability active $(hostname)'

[Install]
WantedBy=multi-user.target" | sudo tee /etc/systemd/system/update-docker-node-availability.service