二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • 最佳实践

  • 迎刃而解

  • Nginx

  • Php

  • Zabbix

  • AWS

  • Prometheus

  • Grafana

  • Loki

  • CentOS

  • Supervisord

  • Systemd

  • Docker

  • Docker-Compose

  • Rancher

  • Ansible

  • OpenLdap

  • GitLab

  • GitHub

  • Etcd

  • Consul

    • consul集群部署
    • Nginx+consul实现集群主机优雅扩缩容
      • 1,前置准备
        • 1,方案选型
        • 2,规范约定
      • 2,Nginx 配置接入
        • 1,前置准备
        • 2,主要配置
      • 3,调试验证
        • 1,添加节点
        • 2,再加节点
        • 3,下线节点
      • 4,go 客户端
      • 5,参考
  • RabbitMQ

  • Kafka

  • Mysql

  • MongoDB

  • OpenVPN

  • Kvm

  • VMware

  • 配置文件详解

  • Other

  • 运维观止
  • Consul
二丫讲梵
2021-11-07
目录

Nginx+consul实现集群主机优雅扩缩容

文章发布较早,内容可能过时,阅读注意甄别。

# 1,前置准备

# 1,方案选型

这里选用的方案是:Nginx 结合 upsync 模块儿,结合 consul 配置管理,实现后端 upstream 的动态管理。

  • 选用 upsync 有如下特性:
    • 优点:
      • 可以动态对接 etcd/consul 获取 upstream,无需重启 nginx
      • 可以 dump 内存中的 upstream 配置到磁盘,即使 etcd/consul 挂掉仍可正常运行
    • 缺点:
      • 无 api 接口,只能操作 etcd/consul 来控制 upstream

模块儿添加,可直接使用二丫 (opens new window)下边定制好的 spec 文件打出 rpm 包,便于安装使用:

rpmbuild

工作中常用的RPM构建spec

- name: rpmbuild
  desc: 工作中常用的RPM构建spec
  avatar: https://avatars2.githubusercontent.com/u/416130?s=460&u=8753e86600e300a9811cdc539aa158deec2e2724&v=4 # 可选
  link: https://github.com/eryajf/rpmbuild # 可选
  bgColor: "#0074ff" # 可选,默认var(--bodyBg)。颜色值有#号时请添加单引号
  textColor: "#fff" # 可选,默认var(--textColor)
1
2
3
4
5
6

# 2,规范约定

  • 不同业务通过不通的 Cousul Key 来区分拉取配置

    基本规则为: {Env}/nginx/upstreams/{Service}-upsync/{Backend}

    • Env:用于区分环境。测试(test),预发(pre),线上(prod)。
    • Service:指定业务名字,一般与 CMDB 业务名字一致。
    • Backend:不同后端的地址信息。
  • 约定 Nginx upstream 命名和应用所在 CDMB 的集群名保持强一致性,全局唯一性

    命名规范: 小写字母和中横线组合,以小写字母开头和结尾,不允许又其他特殊字符,例如下划线等;针对业务集群过渡期间,为了区分接入了自动扩缩容和非自动扩缩容,统一约定在当前 {Service} 后面加上 -upsync 以示区分,例如:user-api,在进行 upsync 自动扩缩容改造后,其命名为 user-api-upsync。

  • 约定 upsync_dump_path 的目录与上边目录保持一致,以便于对齐两者,简化运维工作。

# 2,Nginx 配置接入

# 1,前置准备

为了便于验证一个入口多个后端的场景,二丫 (opens new window)先通过 Nginx 准备三个后端服务,一顿操作如下:

# 准备基础文件
$ cd /data/test/docker/nginx/
$ mkdir test1 test2 test3
$ echo test1 > test1/index.html
$ echo test2 > test2/index.html
$ echo test3 > test3/index.html
# 启动服务
$ docker run --name test1 -v /data/test/docker/nginx/test1:/usr/share/nginx/html:ro -d -p 9901:80  daocloud.io/library/nginx:1.15.9-alpine-perl
$ docker run --name test2 -v /data/test/docker/nginx/test2:/usr/share/nginx/html:ro -d -p 9902:80  daocloud.io/library/nginx:1.15.9-alpine-perl
$ docker run --name test3 -v /data/test/docker/nginx/test3:/usr/share/nginx/html:ro -d -p 9903:80  daocloud.io/library/nginx:1.15.9-alpine-perl
# 访问验证
$ curl localhost:9901
test1
$ curl localhost:9902
test2
$ curl localhost:9903
test3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

现在有了三个后端,访问之后能够返回对应的标识。

# 2,主要配置

接下来进入 Nginx 的主要配置阶段,将如下内容放到 Nginx 的 http 区域:

upstream eryajf-test-upsync {
    upsync 127.0.0.1:8500/v1/kv/prod/nginx/upstreams/eryajf-test-upsync/ upsync_timeout=2m upsync_interval=2s upsync_type=consul strong_dependency=off;
    upsync_dump_path /etc/nginx/upsync/eryajf-test-upsync.config;
    include /etc/nginx/upsync/eryajf-test-upsync.config;
}
server {
    listen 66;
    server_name localhost;
    location / {
        proxy_pass http://eryajf-test-upsync;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
    # 便于观测后端代理列表
    location /upstream_list {
        upstream_show;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  • upsync:定义从 consul 拉取最新的 upstream 信息并存到本地的操作。
    • 127.0.0.1:8500:建议是在每台 Nginx 部署一个 consul-client,统一请求 127,降低 server 端压力。
    • upsync_timeout:定义从 consul 拉取配置的超时时间。
    • upsync_interval:定义从 consul 拉取配置的间隔时间
    • upsync_type:配置同步接入类型,consul 或者 etcd。
    • strong_dependency:启动时是否强制依赖配置服务器,如果配置为 on,则拉取失败,nginx 同样会启用失败。
  • upsync_dump_path:定义本地持久化目录,如果 consul 服务不可用,Nginx 将会引用此路径。一般这个路径应与 consul 中的 path 一致。此文件需要预先创建,并且内容与后端配置一致。否则将检测失败。
  • include:如果 consul 服务不可用,将会从上边 dump 的路径读取对应的配置。

# 3,调试验证

# 1,添加节点

此时可通过如下请求添加一个后端:

$ curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10}' http://127.0.0.1:8500/v1/kv/prod/nginx/upstreams/eryajf-test-upsync/10.6.6.66:9901
true
1
2

返回 true 说明添加成功。

此时可在如下三个地方看到此配置:

  • upsync_dump_path

    $ cat /etc/nginx/upsync/eryajf-test-upsync.config
    server 10.6.6.66:9901 weight=10 max_fails=3 fail_timeout=10s;
    
    1
    2
  • server_upstream_list

    $ curl http://10.6.6.66:66/upstream_list
    Upstream name: eryajf-test-upsync; Backend server count: 1
            server 10.6.6.66:9901 weight=10 max_fails=3 fail_timeout=10s;
    
    1
    2
    3
  • consul_server

    $ curl -s http://10.6.6.66:8500/v1/kv/?recurse | jq
    [
      {
        "LockIndex": 0,
        "Key": "prod/nginx/upstreams/eryajf-test-upsync/10.6.6.66:9901",
        "Flags": 0,
        "Value": "eyJ3ZWlnaHQiOjEwLCJtYXhfZmFpbHMiOjMsImZhaWxfdGltZW91dCI6MTAsImRvd24iOjB9",
        "CreateIndex": 14013,
        "ModifyIndex": 14022
      }
    ]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

    其中的 Value 是经过 base64 过的,使用如下命令可查看原内容:

    $ echo 'eyJ3ZWlnaHQiOjEwLCJtYXhfZmFpbHMiOjMsImZhaWxfdGltZW91dCI6MTAsImRvd24iOjB9' | base64 -d | jq
    {
      "weight": 10,
      "max_fails": 3,
      "fail_timeout": 10,
      "down": 0
    }
    
    1
    2
    3
    4
    5
    6
    7

    以及从 web 界面也可以直接看到:

# 2,再加节点

这个时候再将另外两个节点加上:

$ curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10}' http://127.0.0.1:8500/v1/kv//prod/nginx/upstreams/eryajf-test-upsync/10.6.6.66:9902
$ curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10}' http://127.0.0.1:8500/v1/kv//prod/nginx/upstreams/eryajf-test-upsync/10.6.6.66:9903
1
2

再次查看如上三个信息:

  • upsync_dump_path

    $ cat /etc/nginx/upsync/eryajf-test-upsync.config
    server 10.6.6.66:9903 weight=1 max_fails=2 fail_timeout=10s;
    server 10.6.6.66:9902 weight=1 max_fails=2 fail_timeout=10s;
    server 10.6.6.66:9901 weight=1 max_fails=2 fail_timeout=10s;
    
    1
    2
    3
    4
  • server_upstream_list

    $ curl http://10.6.6.66:66/upstream_list
    Upstream name: eryajf-test-upsync; Backend server count: 3
            server 10.6.6.66:9903 weight=1 max_fails=2 fail_timeout=10s;
            server 10.6.6.66:9902 weight=1 max_fails=2 fail_timeout=10s;
            server 10.6.6.66:9901 weight=1 max_fails=2 fail_timeout=10s;
    
    1
    2
    3
    4
    5
  • consul_server

    $ curl -s http://10.6.6.66:8500/v1/kv/?recurse | jq
    [
      {
        "LockIndex": 0,
        "Key": "prod/nginx/upstreams/eryajf-test-upsync/10.6.6.66:9901",
        "Flags": 0,
        "Value": "eyJ3ZWlnaHQiOjEsICJtYXhfZmFpbHMiOjIsICJmYWlsX3RpbWVvdXQiOjEwfQ==",
        "CreateIndex": 25518,
        "ModifyIndex": 25518
      },
      {
        "LockIndex": 0,
        "Key": "prod/nginx/upstreams/eryajf-test-upsync/10.6.6.66:9902",
        "Flags": 0,
        "Value": "eyJ3ZWlnaHQiOjEsICJtYXhfZmFpbHMiOjIsICJmYWlsX3RpbWVvdXQiOjEwfQ==",
        "CreateIndex": 25525,
        "ModifyIndex": 25525
      },
      {
        "LockIndex": 0,
        "Key": "prod/nginx/upstreams/eryajf-test-upsync/10.6.6.66:9903",
        "Flags": 0,
        "Value": "eyJ3ZWlnaHQiOjEsICJtYXhfZmFpbHMiOjIsICJmYWlsX3RpbWVvdXQiOjEwfQ==",
        "CreateIndex": 25527,
        "ModifyIndex": 25527
      }
    ]
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

此时访问三次入口:

$ for i in a b c;do echo $i && curl localhost:66 ;done
a
test3
b
test2
c
test1
1
2
3
4
5
6
7

# 3,下线节点

下线某个后端,因为 Nginx 不能将权重 weight 置为 0,所以二丫 (opens new window)操作 down 字段来实现下线一个后端:

$ curl -X PUT -d '{"weight":1, "max_fails":2, "fail_timeout":10, "down":1}' http://127.0.0.1:8500/v1/kv/prod/nginx/upstreams/eryajf-test-upsync/10.6.6.66:9902
1

然后看到相应的变化:

$ cat /etc/nginx/upsync/eryajf-test-upsync.config
server 10.6.6.66:9903 weight=1 max_fails=2 fail_timeout=10s;
server 10.6.6.66:9902 weight=1 max_fails=2 fail_timeout=10s down;
server 10.6.6.66:9901 weight=1 max_fails=2 fail_timeout=10s;
1
2
3
4

此时访问三次入口:

$ for i in a b c;do echo $i && curl localhost:66 ;done
a
test3
b
test1
c
test3
1
2
3
4
5
6
7

可以看到已经没有 test2 的返回了。

# 4,go 客户端

package main

import (
	"encoding/json"
	"fmt"

	consulapi "github.com/hashicorp/consul/api"
)

const (
	consulAddress = "10.6.6.14:8500"
)

func InitConsulCli() *consulapi.Client {
	config := consulapi.DefaultConfig()
	config.Address = consulAddress
	client, err := consulapi.NewClient(config)
	if err != nil {
		msg := fmt.Sprintf("init consul client failed,err: %v\n", err)
		panic(msg)
	}
	return client
}

type NgConsulKey struct {
	Env     string `json:"env"`
	Service string `json:"service"`
	Backend string `json:"backend"`
}
type NgConsulValue struct {
	Weight      int `json:"weight"`
	MaxFails    int `json:"max_fails"`
	FailTimeout int `json:"fail_timeout"`
	Down        int `json:"down"`
}

func Addkv(k NgConsulKey, v NgConsulValue) (*consulapi.WriteMeta, error) {
	b, err := json.Marshal(v)
	if err != nil {
		return nil, err
	}
	kv := InitConsulCli().KV()
	p := &consulapi.KVPair{Key: fmt.Sprintf("%s/nginx/upstreams/%s/%s", k.Env, k.Service, k.Backend), Value: []byte(b)}
	wm, err := kv.Put(p, nil)
	if err != nil {
		return nil, err
	}
	return wm, nil
}

func main() {
	key := NgConsulKey{
		Env:     "prod",
		Service: "eryajf-api-upsync",
		Backend: "10.6.6.66:9902",
	}
	value := NgConsulValue{
		Weight:      10,
		MaxFails:    3,
		FailTimeout: 10,
		Down:        0,
	}
	_, err := Addkv(key, value)
	if err != nil {
		fmt.Printf("add kv failed:%v\n", err)
	}
	fmt.Println("add kv success")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

可以通过调整参数,添加不同的对象,通过调整 weight 调整后端的权重,通过调整 down 字段来决定后端服务上下线。

# 5,参考

  • 效率倍增!网易杭研 Nginx 自动扩缩容实践 (opens new window)
微信 支付宝
#nginx#consul#go
上次更新: 2024/07/04, 22:40:37
consul集群部署
Rabbitmq使用源码安装部署单机版

← consul集群部署 Rabbitmq使用源码安装部署单机版→

最近更新
01
记录二五年五一之短暂回归家庭
05-09
02
学习周刊-总第210期-2025年第19周
05-09
03
学习周刊-总第209期-2025年第18周
05-03
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式