[Share Experiences] Docker swarm 集群 - draw 图 - 随手记
Tofloor
poster avatar
魔法师
deepin
2024-04-12 11:13
Author

这是一个我在 2024 年 1 月 12 日画的图 - 随便看看吧
image.png


安装

# centos
yum install docker-ce.x86_64

# ubuntu
apt install docker.io

# windows 安装 Docker Desktop

基本配置

假设你的服务器在 192.168.10.75 上,并以 Linux 为主,Windows 是一台工作机,Linux 为通用服务与客户端侧。
以下 daemon.json 配置,主要用于配置不安全的私有 registry,且在 registry 已经部署后才配置。

配置 daemon.json

Linux: /etc/docker/daemon.json
Windows: Docker Desktop > Settings > Docker Engine

  1. 配置不安全(未开启https)的 registry

    {
        "insecure-registries: [
            "192.168.10.75:5000"
        ]
    }
    

部署服务

Portainer

  1. 拉取镜像 - 来源于 dockerhub

    docker pull portainer/potainer-ce
    
  2. 创建存储卷 - 持久的存储卷

    docker volum create portainer_data
    
    # 存储卷是一种标准的维护目录,不用自己维护本地路径
    # /var/lib/docker/volumes/portainer_data/_data
    # 用卷只需要 -v portainer_data:/data 即可持久化本地到容器的映射
    
    # (传统)不使用卷时就是自己在本地创建一个 /data/portainer 或任意你想放数据的目录
    # 然后还需要 -v /data/portainer:/data  来配置本地到容器的目录映射
    
  3. 部署容器

    在基于持久的储存卷上创建容器,并映射出 9000 端口

    docker run -d \
        --name portainer \
        -p 9000:9000 \
        --restart=always  \
        -v /var/run/docker.sock:/var/run/docker.sock  \
        -v portainer_data:/data  \
        portainer/portainer-ce
    

Registry

  1. 拉取镜像

    docker pull registry
    
  2. 创建持久存储卷

    docker volum create registry_data
    
  3. 部署容器

    docker run --name docker-registry-center \
        -p 5000:5000 \
        -v registry_data:/var/lib/registry  \
        registry
    
  4. 部署带 https 认证的 registry (未验证)

    mkdir registry-secure
    
    # htpasswd -Bbn admin 123456 > registry-secure/passwd
    # 查看技巧,可以使用现有的 registry 镜像中的 htpasswd 工具来生成
    
    docker run --name docker-registry-center \
        -p 5000:5000 \
        -v `pwd`/registry-secure:/secure \
        -e 'REGISTRY_AUTH=htpasswd' \
        -e 'REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm' \
        -e 'REGISTRY_AUTH_HTPASSWD_PATH=/secure/passwd' \
        registry
    
  5. 一些技巧 - 运行并改变容器的入口点,如调用容器内命令完成一件事

    docker run --entrypoint echo debian:10 yes
    #                       ^入口命令       ^入口参数
    #                            ^镜像   
    # 此命令会使用 debian:10 镜像中的 echo 命令输出 yes
    # 如同: echo yes
    

    调用 registry 中的 htpasswd 工具来生成配置

    docker run --entrypoint htpasswd registry -Bbn admin 123456 > registry-secure/passwd
                            ^                      ^   
    # 在 registry 中执行 htpasswd -Bbn .... 生成一个 passwd
    

Registry Web

  1. 拉取镜像

    docker pull docker-registry-web
    
  2. 部署容器

    使用 --link 连接到 docker-registry-center

    docker run --name docker-registry-web \
        --rm \
        -p 5090:8080 \
        --link docker-registry-center \
        -e REGISTRY_URL=http://192.168.10.75:5000/v2 \
        -e REGISTRY_TRUST_ANY_SSL=true  \
        -e REGISTRY_NAME=192.168.10.75:5000 \
        hyper/docker-registry-web
    

    image.png

    image.png


后续使用说明

你可以在 portainer 中配置一个 stack 来一次性部署多个服务,或创建单个 service 部署一个对应的容器,或只是简单创建一个容器而不用 service 的方式。

docker swarm 集群,是一个服务分配中心,从单一容器到服务栈,加入节点的身份角色从 leader 到 manage 再到 worker。就像老板创建了公司,在里面可以有管理或员工的身份进公司,不同角色的人群可以做相同的事,也可以由 leader 或 manage 创建 service 并配置 constraints 属性来约定运行在哪个已配置的 worker 上。

在命令行下,docker run 是在本机运行容器,docker service create 则是创建服务,并且指派服务运行在约定的节点上。
在非 worker 节点上,你可以使用 Dockerfile 构建自己的镜像,并 push 到私有 registry 中,并执行 docker service update 来更新指定的服务。这个类似于,软件打包完成一键投递更新部署测试,并在 worker 运行机上看运行效果一样(假说)。

例如在写 Dockerfile 的同时写一个 Makefile 就可以完成你想要用的一系列操作。


一种很新鲜的部署

  • 假设目录结构是这样的
    # $ tree -L 2
    .
    ├── application_shell.py
    ├── Dockerfile
    ├── Makefile
    ├── pages
    │   ├── streamlit-analysis_head_file.py
    │   ├── streamlit-apks.py
    │   ├── streamlit-apt-anaylize.py
    │   ├── streamlit-apt-configure.py
    │   ├── streamlit-custome.py
    │   ├── streamlit-deb-package.py
    │   ├── streamlit-deepin-articles.py
    │   ├── streamlit-deepin-bbs.py
    │   ├── streamlit-deepin-wiki.py
    │   ├── streamlit-digraph.py
    │   ├── streamlit-dns.py
    │   ├── streamlit-docker.py
    │   ├── streamlit-ffmpeg.py
    │   ├── streamlit-github.py
    │   ├── streamlit-html2markdown.py
    │   ├── streamlit-iptables-viewer.py
    │   ├── streamlit-linglong.py
    │   ├── streamlit-linuxcn-lctt-list.py
    │   ├── streamlit-lxc.py
    │   ├── streamlit-mysql.py
    │   ├── streamlit-openapi.py
    │   ├── streamlit-os-walk.py
    │   ├── streamlit-package-deb.py
    │   └── streamlit-regexp.py
    ├── readme.md
    ├── testpysftp.py
    ├── todo
    ├── utils
    │   ├── ani2gif.py
    │   └── dpkgCommands.py 
    ├── vcpkg.py
    ├── welcome.py
    └── workspace.py
    
    ? directories, ? files
    
  1. 构建一个运行环境

    docker run --name new-debian -it debian:latest
    
    apt install python3-pip
    python3 -m pip install streamlit
    
    
    # 另开一个终端,将刚刚的容器提交为一个镜像,并推送到私有 registry
    docker commit new-debian 192.168.10.75:5000/debian/streamlit-python:latest
    docker push 192.168.10.75:5000/debian/streamlit-python:latest
    
  2. 使用 Dockerfile 基于这个运行环境镜像构建你的定制服务镜像

    FROM 192.168.10.75:5000/debian/streamlit-python:latest
    
    # 在 /work 目录,我们添加一些文件进去
    WORKDIR /work
    ADD welcome.py /work
    
    # 
    RUN python3 -m pip install -q beautifulsoup4
    
    # 放点页面进去
    WORKDIR /work/pages
    ADD pages/streamlit-iptables-viewer.py /work/pages
    ADD pages/streamlit-regexp.py          /work/pages
    ADD pages/streamlit-dns.py             /work/pages
    ADD pages/streamlit-deepin-wiki.py     /work/pages
    
    WORKDIR /work
    CMD streamlit run welcome.py
    
  3. 创建 Makefile 来自动构建你的容器镜像并推送到 registry 并提供部署能力

    提供本地 run 运行,镜像构建,服务更新等操作,其中你可能需要在第一次构建镜像后 docker service create 才可以对服务更新指定镜像

    all:
        @echo "无事可做"
    
    run:
        streamlit run welcome.py
    
    streamlit-application:
        docker build -t 192.168.10.75:5000/streamlit-application:latest .
        docker push 192.168.10.75:5000/streamlit-application:latest
    
    streamlit-application-deploy: streamlit-application
        docker service update --image 192.168.10.75:5000/streamlit-application:latest streamlit-application-service
    

以上也有不足之处,事至此,省心直接用 portainer 创建杂项服务栈,各种奇奇怪怪的镜像都可以尝试部署。

portainer 与 docker hub 镜像转存本地

你可能需要解决从 docker hub 中 docker pull 的每日次数限制。

  1. 从 dockerhub 中获取镜像并转储到私有 registry 中。

    docker pull nginx:latest
    docker tag nginx:latest 192.168.10.75:5000/nginx:latest
    docker push 192.168.10.75:5000/nginx:latest
    
    
    docker pull containous/whoami:latest
    docker tag containous/whoami:latest 192.168.10.75:5000/containous/whoami:latest
    docker push 192.168.10.75:5000/containous/whoami:latest
    
  2. 在 portainer 中使用私有 registry 中的镜像直接部署服务栈

    你可能需要了解一些 docker-compose 这种 yml 语法,在 portainer 中直接创建 full-other-stack

    version: "3"
    services:
      whoami-service:
        image: "192.168.10.75:5000/containous/whoami:latest"
        network_mode: "host"
        ports: 
          - 4444:80
        deploy:
          placement:
            constraints:
              - node.labels.user==leader
    
      nginx-service:
        image: "192.168.10.75:5000/nginx:latest"
        network_mode: "host"
        ports:
          - 6666:80
        deploy:
          placement:
            constraints:
              - node.labels.user==leader
    

    我使用 node.labels.user==leader 是为了指定直接在服务上直接运行这个镜像,因为我没有持久的 worker 可用。

  3. 你可能需要配置的 node labels

    portainer 则在 Swarm > Nodes > Node Details > Node Labels 栏里添加 user 为 leader 的配置。

    doker node ls 中列出节点列表找到要要配置的机器

    # docker node ls
    ID                            HOSTNAME                STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
    0sjrcsm2vwjkhjf3p0rtpi1go     docker-desktop          Down                Active                                  24.0.7
    6h71flfff7x3n3lzp1bitiop0     docker-desktop          Down                Active                                  24.0.7
    bmmjevyx86overqr32ldbiji7 *   localhost.localdomain   Ready               Active              Leader              24.0.7
    6ndh3mrqj1lw0zb3sanwl7vpt     zinface-PC              Down                Active                                  18.09.1
    
    # 使用 docker node 命令更新 
    docker node update localhost.localdomain --label-add user=leader
    
异常情况处理 - The Swarm does not have a leader

这个情况是发生一个 swarm 集群挂了,无法访问节点

  1. 关键信息

    The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online
    
    Error: rpc error: code = Unknown desc = The swarm does not have a leader. It's possible that too few managers are online. Make sure more than half of the managers are online.
    
  2. 重建 swarm 集群 - 在发生错误的集群节点上执行,一般是 leader 也就是服务器本身

    docker swarm init --force-new-cluster --advertise-addr 192.168.10.75
    
  3. (可选)其它管理器节点重新加入节点,也是执行 1 的命令?

Reply Favorite View the author
All Replies
sshnuke
deepin
2024-04-12 12:03
#1

哈喽,这篇Docker swarm的教程写得挺详细的,看着挺有意思的。你从安装到配置一步步讲得清清楚楚,特别是那个Portainer和私有registry的部分,感觉很实用啊。

就是有个问题,你说的那个swarm集群没leader的情况,一般怎么解决?你帖子里的方法有效不?

先谢过你的分享了,我得找时间跟着试试。期待你后续还有更多的内容!

Reply View the author
魔法师
deepin
2024-04-12 16:08
#2
sshnuke

哈喽,这篇Docker swarm的教程写得挺详细的,看着挺有意思的。你从安装到配置一步步讲得清清楚楚,特别是那个Portainer和私有registry的部分,感觉很实用啊。

就是有个问题,你说的那个swarm集群没leader的情况,一般怎么解决?你帖子里的方法有效不?

先谢过你的分享了,我得找时间跟着试试。期待你后续还有更多的内容!

在图中,服务器作为一个想创建集群的机器,执行 docker swarm init 就创建了一个 swarm 集群,等待其它机通过 docker swarm join 加入到集群(manage 或 worker 身份),而 leader 就是集群创建者,一般情况下这样就完成了。

另外 leader 消失的几率是有,不过账本都是证书问题? 在这个地方有提到,引用点:https://blog.51cto.com/u_15499155/5044021

服务器不关机则 leader 常在,manage 只是一种像是分配的管理员,用来管理其它节点或管理服务,也应该可以让其它节点通过管理员节点加入到集群。

手上没有太多机器,从集群结构上来看,把 leader 看成主路由器, manage 就看成连接到主路由器的副路由器吧,worker 就当是其它上网的终端吧。

我也就只是玩了一下,没见过什么大场面,在小场面里常年(日)开机的一台小机器就当 swarm 集群了,其它的只是随时部署上去的一些服务,场景不大,没有 manage 身份不能操作 swarm 里的服务创建与更新,所以小笔记本就是一个 manage 了(但是为什么不用 portainer 呢),基本没有 worker 加入,但探索时拿了其它有 docker 的机器(6core 8g ram 的配置)测试 worker 身份加入。然后 manage 构建完成镜像就直接指派给 node.labels.user=6c8g 的工作者(上线了就跑,没在就等待)。当然先给那个新来的 worker 更新 node labels,不然你说的 6c8g 是指谁呢(命令行就完成的事为什么要用 portainer 呢)

完事,疯狂部署你的服务,各种 stack 疯狂改,或填表单一样创建 service ,或从古老传统 docker run 命令行创建单容器啥的开始。

tail docker swarm 只是你不配用 k8s 时的一种原生方案

Reply View the author