前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Docker Swarm入门:容器编排与服务部署

Docker Swarm入门:容器编排与服务部署

作者头像
KenTalk
发布2023-06-06 15:33:53
5420
发布2023-06-06 15:33:53
举报
文章被收录于专栏:Ken的杂谈Ken的杂谈

一、前言

Docker Swarm是Docker官方提供的容器集群管理以及容器编排解决方案,Docker Swarm基于Docker Compose组件以及网络等基础能力,提供了服务编排、负载均衡、动态伸缩、滚动更新等能力,本文ken.io主要介绍基于Docker Swarm进行容器编排、服务部署与更新等等

1、本文主要内容

  • 使用 Docker Swarm 部署一组服务
  • 使用 Docker Swarm 部署/扩容服务并指定节点
  • 使用 Docker Swarm 滚动更新服务
  • 使用 Docker Swarm 回滚服务

2、本文环境信息

环境

说明

Docker

Docker CE 23.0.1

Linux Server

Ubuntu 22.04.2 LTS

Golang

1.20

Windows

Windows 11

curl for Windows

7.87

3、前置知识

二、准备工作

1、部署Docker Swarm集群

参考:Docker Swarm入门:集群搭建与管理 - Ken的杂谈 ,准备Docker Swarm集群,3-5个节点即可

机器名

IP

必要

操作系统

环境信息

swarm-manager-01

192.168.99.131

openEuler 22.03 LTS /CentOS 7

Docker CE 23.0.1

swarm-manager-02

192.168.99.132

openEuler 22.03 LTS /CentOS 7

Docker CE 23.0.1

swarm-manager-03

192.168.99.133

openEuler 22.03 LTS /CentOS 7

Docker CE 23.0.1

swarm-worker-01

192.168.99.141

openEuler 22.03 LTS /CentOS 7

Docker CE 23.0.1

swarm-worker-02

192.168.99.142

openEuler 22.03 LTS /CentOS 7

Docker CE 23.0.1

为了方便测试,请关闭防火墙,或除Docker Swarm必要端口外,开放8000、8080端口

2、准备代码

创建镜像制作根目录,例如:d:\docker\helloworld(Windows),~/docker/helloworld(macOS),用于存放代码以及Dockerfile等文件

这里ken.io用golang写的一个简单http server,监听8000端口,对访问的请求通过Redis进行计数并记录日志,在日志中增加环境、服务版本、主机名等信息,并提供日志读取入口,以便后续测试~

新建helloweb.go保存以下代码

代码语言:javascript
复制
package main

import (
    "fmt"
    "io"
    "log"
    "net"
    "net/http"
    "os"
    "runtime"
    "strings"

    "github.com/go-redis/redis"
)

var cache = redis.NewClient(&redis.Options{
    Addr: "redis:6379",
})

var version = "1.0"
var env = "DEV"
var logRoot = "/app/logs/"
var logFilePath = logRoot + "default.log"

// 获取主机名
func getHostName() string {
    hostname, err := os.Hostname()
    if err != nil {
        log.Fatalf("Failed to open log file: %v", err)
    }
    return hostname
}

// 获取IP地址(IPV4)
func getIpAddresses() string {
    addrs, err := net.InterfaceAddrs()
    if err != nil {
        log.Fatal(err)
        return ""
    }
    var ips []string
    for _, address := range addrs {
        if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
            if ipnet.IP.To4() != nil {
                ips = append(ips, ipnet.IP.String())
            }
        }
    }
    return strings.Join(ips, ",")
}

// 日志配置
func configLog() {
    // 创建文件夹
    err := os.MkdirAll(logRoot, 0755)
    if err != nil {
        log.Fatalf("Failed to open log file: %v", err)
        return
    }
    // 打开或者创建一个日志文件
    file, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        log.Fatalf("Failed to open log file: %v", err)
        return
    }
    // 设置日志输出到文件&终端
    multiLog := io.MultiWriter(file, os.Stdout)
    log.SetOutput(multiLog)

    // 设置环境变量env、hostname作为日志前缀
    log.SetPrefix("[" + env + "] - Service:" + version + " - " + getHostName() + " - " + getIpAddresses() + " - ")

}

// 读取日志文件
func readLog() string {
    file, err := os.Open(logFilePath)
    if err != nil {
        log.Fatalf("Failed to open log file: %v", err)
        return ""
    }
    defer file.Close()
    content, err := io.ReadAll(file)
    return string(content)
}

// 处理HTTP请求
func handler(w http.ResponseWriter, r *http.Request) {
    log.Println("request:", r.Host, r.URL)
    if r.URL.Path == "/log" {
        fmt.Fprintf(w, readLog())
    } else {
        fmt.Fprintf(w, "Hello, World! ---helloweb image by ken.io\r\n")
        fmt.Fprintf(w, "Total view:%s\r\n", cache.Incr("count"))
    }
    fmt.Fprintf(w, "Host:%s,OS:%s/%s\r\n", getHostName(), runtime.GOOS, runtime.GOARCH)
    fmt.Fprintf(w, "ENV:%s,IP:%s\r\n", env, getIpAddresses())
    fmt.Fprintf(w, "Service Version:%s\r\n", version)
}

func main() {
    //设置环境变量
    if os.Getenv("env") != "" {
        env = os.Getenv("env")
    }
    //初始化日志
    configLog()
    //处理HTTP请求
    http.HandleFunc("/", handler)
    log.Println("starting server on port 8000")
    log.Fatal(http.ListenAndServe(":8000", nil))
}

3、Dockerfile准备

新建helloweb.build作为Dockerfile,并填充以下内容

代码语言:javascript
复制
# 使用官方提供的 Go 镜像作为基础镜像
FROM golang:1.20

# 将工作目录设置为 /app
WORKDIR /app

# 将helloweb.go复制到 /app 下
COPY helloweb.go /app

# 设置go mod 镜像
RUN go env -w GO111MODULE=on
RUN go env -w  GOPROXY=https://goproxy.cn,direct

# 导入依赖的Redis go module
RUN go mod init helloweb
RUN go get github.com/go-redis/redis

# 允许宿主机访问容器的 8000 端口
EXPOSE 8000

# 设置容器进程为:go run helloweb.go
CMD go run helloweb.go

4、制作镜像

代码语言:javascript
复制
# 登录
docker login

# 进入目录
d: && cd d:\docker\helloweb

# 查看Buildx版本(确认buildx已启用,如未启用,使用docker buildx install安装)
docker buildx version

# 构建镜像并推动到DockerHub(默认为latest)(注意结尾一定要加.)
# 这里选择构建常见的linux/amd64,linux/arm64 架构,如需其他的可以自行追加
docker buildx build \
--platform linux/amd64,linux/arm64 \
-f helloweb.build   \
-t kentalk/helloweb:1.0 --push .

三、服务部署与容器编排

在Docker Swarm环境中,可以在Manager节点通过docker service create 命令创建一个服务

代码语言:javascript
复制
docker service create --replicas 1 --name myweb01 -p 8001:8000 kentalk/helloworld

但往往我们的服务还会依赖其他下游服务,以及数据库、缓存等,如果基于docker service create命令来创建服务,那我们就要逐个进行依赖的创建、服务的创建等等,这是比较麻烦的,不过Docker Swarm支持使用Compose文件来一次配置、启动多个服务,

1、准备Compose配置

在Swarm任意Manager节点,新建目录/var/docker,然后新建helloweb.yml并保存以下内容:

代码语言:javascript
复制
# 创建目录
mkdir /var/docker

# 创建文件
vi /var/docker/helloweb.yml
代码语言:javascript
复制
version: '3'

services:
  web:
    image: "kentalk/helloweb:1.0"
    ports:
      - "8000:8000"
    networks:
      - default_net
    environment:
      env: TEST
    deploy:
      mode: replicated
      replicas: 2
      placement:
        constraints: [node.role == worker]


  redis:
    image: "redis:6.0"
    networks:
      - default_net
    deploy:
      placement:
        constraints: [node.role == worker]

networks:
  default_net:
    driver: overlay

这里定义了两个服务web、redis,并制定了端口、网络、环境等参数,也约束了只在Work节点部署

其中网络配置以及部署节点的约束也可不指定,根据情况来即可

2、部署并测试服务

2.1、部署服务

通过Compose配置部署一组服务使用 docker stack deploy命令,其中 -c 参数指定 compose 文件

代码语言:javascript
复制
# 进入配置目录
cd /var/docker

# 部署服务
docker stack deploy -c helloweb.yml helloweb

# 输出示例
Creating network helloweb_default_net
Creating service helloweb_redis
Creating service helloweb_web

2.2、测试服务

在宿主机执行以下命令,进行服务访问测试,并查看相关信息

代码语言:javascript
复制
# 访问默认地址
curl 192.168.99.131:8000
curl 192.168.99.141:8000
curl 192.168.99.142:8000

# 输出示例
Hello, World! ---helloweb image by ken.io
Total view:incr count: 3
Host:2e66c23b1d66,OS:linux/arm64
ENV:TEST,IP:10.0.0.33,172.18.0.4,10.0.8.7
Service Version:1.0

虽然load balancer提供了负债均衡的能力,且默认负载均衡策略师轮询,但并不是真正的逐个访问,在访问到一定量级时才会接近轮询的访问分布,所以这里看到的Host大概率是同一个

返回内容中可以关注incr count 3,以及ENV:TEST,这说明Redis在正常工作,环境参数也生效了

3、查看服务信息

3.1、查看Stack

代码语言:javascript
复制
# 查看所有Stack
docker stack ls

# 输出示例
NAME       SERVICES
helloweb   2

3.2、查看Stack服务信息

代码语言:javascript
复制
# 查看Stack服务信息
docker stack services helloweb

# 输出示例
ID             NAME             MODE         REPLICAS   IMAGE                  PORTS
jcbr9ao9fa05   helloweb_redis   replicated   1/1        redis:6.0              
06nty9feerlp   helloweb_web     replicated   2/2        kentalk/helloweb:1.0   *:8000->8000/tcp

3.3、查看Stack任务信息

代码语言:javascript
复制
# 查看Stack任务信息
docker stack ps helloweb

# 输出示例
ID             NAME               IMAGE                  NODE              DESIRED STATE   CURRENT STATE        
2pnu76lzrwpn   helloweb_redis.1   redis:6.0              swarm-worker-01   Running         Running about a minute ago
w707x4pl94tb   helloweb_web.1     kentalk/helloweb:1.0   swarm-worker-02   Running         Running about a minute ago
85956lq880rd   helloweb_web.2     kentalk/helloweb:1.0   swarm-worker-01   Running         Running about a minute ago

3.4、查看服务信息

代码语言:javascript
复制
# 查看服务信息
docker service ps helloweb_web

# 输出示例
ID             NAME             IMAGE                  NODE              DESIRED STATE   CURRENT STATE
w707x4pl94tb   helloweb_web.1   kentalk/helloweb:1.0   swarm-worker-02   Running         Running 2 minutes ago
85956lq880rd   helloweb_web.2   kentalk/helloweb:1.0   swarm-worker-01   Running         Running about a minute ago

3.5、查看网络信息

代码语言:javascript
复制
# 查看网络信息
docker network inspect helloweb_default_net

四、服务滚动升级

1、修改代码

修改代码中的版本为1.1

代码语言:javascript
复制
/*
*省略部分内容
*/

var version = "1.1"

/*
*省略部分内容
*/

2、制作镜像

制作并上传kentalk/helloweb:1.1 版本镜像

代码语言:javascript
复制
docker buildx build \
--platform linux/amd64,linux/arm64 \
-f helloweb.build   \
-t kentalk/helloweb:1.1 --push .

3、滚动升级

将服务helloweb_web滚动升级到kentalk/helloweb:1.1版本

代码语言:javascript
复制
# 滚动升级服务
docker service update --image kentalk/helloweb:1.1 helloweb_web

# 输出示例
helloweb_web
overall progress: 2 out of 2 tasks 
1/2: running   [==================================================>] 
2/2: running   [==================================================>] 
verify: Service converged

滚动升级过程中,Docker Swarm会先启动新版本的任务容器,新的任务容器启动成功后替换旧的任务容器

在滚动升级过程中,新开一个Manger节点连接,不断执行docker service ps helloweb_web,就可以看到这个过程

升级完成后,我我们也可以通过命令查看服务状态

代码语言:javascript
复制
# 查看服务详情
docker service ps helloweb_web

# 输出示例
ID             NAME                 IMAGE                  NODE              DESIRED STATE   CURRENT STATE
o1yzdlbdklab   helloweb_web.1       kentalk/helloweb:1.1   swarm-worker-02   Running         Running about a minute ago
w707x4pl94tb    \_ helloweb_web.1   kentalk/helloweb:1.0   swarm-worker-02   Shutdown        Shutdown about a minute ago
tjajda18uebt   helloweb_web.2       kentalk/helloweb:1.1   swarm-worker-01   Running         Running about a minute ago
85956lq880rd    \_ helloweb_web.2   kentalk/helloweb:1.0   swarm-worker-01   Shutdown        Shutdown about a minute ago

这里我们会发现原有两个1.0版本的任务实例还是保留的,这有可能助于特殊情况进行手动回滚(但是ken.io没找到相关办法)

这里我们通过命令访问下helloweb,测试下升级后的服务

代码语言:javascript
复制
# 访问服务
curl 192.168.99.131:8000

# 输出实例
Hello, World! ---helloweb image by ken.io
Total view:incr count: 6
Host:4742142b0647,OS:linux/arm64
ENV:TEST,IP:10.0.0.35,172.18.0.3,10.0.8.10
Service Version:1.1

4、服务扩容

代码语言:javascript
复制
# 扩容helloweb_web
docker service scale helloweb_web=4

# 输出示例
helloweb_web scaled to 4
overall progress: 4 out of 4 tasks 
1/4: running   [==================================================>] 
2/4: running   [==================================================>] 
3/4: running   [==================================================>] 
4/4: running   [==================================================>] 
verify: Service converged 

# 查看扩容后的服务
docker service ps helloweb_web

五、服务回滚

为了更好的测试,服务回滚基于Redis服务来作

1、升级服务

1.1、 升级Redis到7.0

代码语言:javascript
复制
# 升级Redis到7.0
docker service update --image redis:7.0 helloweb_redis

# 查看升级后的服务
docker service ps helloweb_redis

#输出示例
docker service ps helloweb_redis
ID             NAME                   IMAGE       NODE              DESIRED STATE   CURRENT STATE
ujb6gzk1aik8   helloweb_redis.1       redis:7.0   swarm-worker-01   Running         Running 2 seconds ago
2pnu76lzrwpn    \_ helloweb_redis.1   redis:6.0   swarm-worker-01   Shutdown        Shutdown 3 seconds ago

1.2、 访问测试

这里我们通过命令访问下helloweb,测试下升级后的情况

代码语言:javascript
复制
# 访问服务
curl 192.168.99.131:8000

# 输出示例
Hello, World! ---helloweb image by ken.io
Total view:incr count: 1
Host:4742142b0647,OS:linux/arm64
ENV:TEST,IP:10.0.0.35,172.18.0.3,10.0.8.10
Service Version:1.1

2、回滚服务

代码语言:javascript
复制
# 回滚Redis
docker service rollback helloweb_redis

# 输出示例
helloweb_redis
rollback: manually requested rollback 
overall progress: rolling back update: 1 out of 1 tasks 
1/1: running   [==================================================>] 
verify: Service converged 

# 查看升级后的服务
docker service ps helloweb_redis

#输出示例
ID             NAME                   IMAGE       NODE              DESIRED STATE   CURRENT STATE
6dmt5vmoqquv   helloweb_redis.1       redis:6.0   swarm-worker-01   Running         Running 23 minutes ago
ujb6gzk1aik8    \_ helloweb_redis.1   redis:7.0   swarm-worker-01   Shutdown        Shutdown 23 minutes ago
2pnu76lzrwpn    \_ helloweb_redis.1   redis:6.0   swarm-worker-01   Shutdown        Shutdown 29 minutes ago

这时候会发现,使用docker service rollback回滚服务,只是帮我们根据上一个版本重新创建了容器,所以Docker Swarm提供的回滚并不适用于有状态的服务

六、备注

1、其他命令

代码语言:javascript
复制
# 停用Stack
docker stack down helloweb

# 删除Stack
docker stack rm helloweb

2、本文参考

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2023-06-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
    • 1、本文主要内容
      • 2、本文环境信息
        • 3、前置知识
        • 二、准备工作
          • 1、部署Docker Swarm集群
            • 2、准备代码
              • 3、Dockerfile准备
                • 4、制作镜像
                • 三、服务部署与容器编排
                  • 1、准备Compose配置
                    • 2、部署并测试服务
                      • 3、查看服务信息
                      • 四、服务滚动升级
                        • 1、修改代码
                          • 2、制作镜像
                            • 3、滚动升级
                              • 4、服务扩容
                              • 五、服务回滚
                                • 1、升级服务
                                  • 2、回滚服务
                                  • 六、备注
                                    • 1、其他命令
                                      • 2、本文参考
                                      相关产品与服务
                                      容器镜像服务
                                      容器镜像服务(Tencent Container Registry,TCR)为您提供安全独享、高性能的容器镜像托管分发服务。您可同时在全球多个地域创建独享实例,以实现容器镜像的就近拉取,降低拉取时间,节约带宽成本。TCR 提供细颗粒度的权限管理及访问控制,保障您的数据安全。
                                      领券
                                      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档