Consul 集群安装

小得盈满

2023/09/24

1.介绍

Consul 是一款服务网络平台,主要实现服务注册、服务发现、服务网格、服务网关、安全网络以及配置管理等多类服务,非常适合做为微服务架构的底层网络平台。

配置中心其实就是一个 KV 存储,我们如果做配置中心的话其实主要就是用 KV 存储部分,但是为了以后的可扩展性,我们可能会使用各种服务网格的能力,因此选择 Consul 相比 etcd 来说是可以使用很多扩展的功能,不需要自己再进行额外的开发了。

2.部署架构

Consul 推荐的部署方式是采用多个可用区实现部署,因为采用 Raft 一致性的算法,所以一般部署奇数个节点,官方建议生产环境部署 5 节点,分布于 3 个可用区,每个可用区内最多两个节点,这样可以承受两个节点或者 1 个可用区的丢失。多个可用区一般是多个机房,如果没有条件也可以部署在 1~2 个可用区内,如果只有 1 个机房,那么这里的可用区可以看作是机架,尽量使服务分布于不同的机架,这样可以容忍最多两个机架完全丢失。

对于小型环境建议将 Consul 代理部署在多个物理服务器上,至少运行 3 个节点,如下图所示:

image

这样可以允许 1 个节点故障,从而具备一定的容错性。

3.安装配置

3.1.前提准备

我们下面以 3 个节点为例进行部署,机器列表如下:

172.16.0.5  ec1
172.16.0.6  ec2
172.16.0.7  ec3

每个节点的操作系统都是 Ubuntu 22.04,不过我们这里选择使用二进制的安装包进行安装,和具体哪个平台的关系不大,访问下载链接:https://developer.hashicorp.com/consul/downloads 下载安装包,最新的稳定版安装包名称为:consul_1.16.1_linux_amd64.zip,也就是我们当前安装 Consul 的版本是:1.16.1

3.2.安装到节点

我们在每个节点上都通过相同的步骤进行安装:

# 解压并放到 PATH 下
unzip consul_1.16.1_linux_amd64.zip
mv consul /usr/local/bin/
# 查看版本
consul --version
# 开启命令自动提示 可以用 tab 看到多级命令
consul -autocomplete-install
complete -C /usr/local/bin/consul consul
# 创建用户
useradd --system --home /etc/consul.d --shell /bin/false consul
# 创建数据目录并授权
mkdir --parents /data/consul
chown --recursive consul:consul /data/consul
# 创建证书目录
mkdir -p /etc/consul.d/certs

这样的话每个机器都安装上了,我们这里的数据目录放在 /data/consul 下,生产环境部署时一般放到单独的数据盘下面。

3.3.生成密钥和证书

然后我们在其中一个节点,比如第一个节点生成 GOSSIP 密钥,用于客户端之间通信的加密,客户端通信采用对称加密,因此每个节点都需要使用相同的密钥:

consul keygen

生成之后会返回密钥的 base64 编码,我们将这串密钥保存好,等下配置要用。

然后我们生成用于 RPC 加密的 TLS 证书,这个用于在客户端和服务端之间通信进行加密,首先需要生成 CA,同样是在第一个节点执行:

# 进入证书目录
cd /etc/consul.d/certs/
consul tls ca create

执行之后会输出:

==> Saved consul-agent-ca.pem
==> Saved consul-agent-ca-key.pem

这样就生成了 consul-agent-ca.pemconsul-agent-ca-key.pem ,前者是公钥,后者是私钥,然后我们基于 CA 创建证书:

consul tls cert create -server -dc dc1 -domain consul

由于我们是 1 个集群,因此数据中心指定为 dc1,这样执行之后默认会生成 dc1-server-consul-0.pemdc1-server-consul-0-key.pem 证书文件。

因为接下来我们将采用客户端证书自动生成的方式,所以我们不用再手动生成客户端证书了,然后我们将生成好的 CA 和证书发送到另外两个节点:

scp -r /etc/consul.d/certs/ ec2:/etc/consul.d/
scp -r /etc/consul.d/certs/ ec3:/etc/consul.d/

其实我们这里也可以只发送 consul-agent-ca-key.pem 即可,证书可以在每个节点基于 CA 单独生成,这里全部发送过去其实是为了操作简单,每个节点都是采用相同的证书。

3.4.集群配置

现在我们开始在第 1 个节点上编写配置文件:

touch /etc/consul.d/consul.hcl
chmod 640 /etc/consul.d/consul.hcl

然后我们编辑 /etc/consul.d/consul.hcl 写入下面的配置:

datacenter = "dc1"
data_dir = "/data/consul"
encrypt = "AU4FQ/rbbxqMQXRN5vA1QP1UHoDb8lubANa9llmXOEQ="

tls {
   defaults {
      ca_file = "/etc/consul.d/certs/consul-agent-ca.pem"
      cert_file = "/etc/consul.d/certs/dc1-server-consul-0.pem"
      key_file = "/etc/consul.d/certs/dc1-server-consul-0-key.pem"

      verify_incoming = true
      verify_outgoing = true
   }

   internal_rpc {
      verify_server_hostname = true
   }
}

auto_encrypt {
  allow_tls = true
}

retry_join = ["172.16.0.6", "172.16.0.7"]

acl {
  enabled = true
  default_policy = "deny"
  enable_token_persistence = true
}

其中的配置需要注意的有:

  1. datacenter 和上面生成证书时配置一致,即:dc1
  2. data_dir 配置我们实际的数据目录,即:/data/consul
  3. encrypt 就是刚才生成的 gossip 密钥值
  4. 证书的路径注意配置正确
  5. retry_join 配置自动发现的节点,这样初始化的时候就不需要手动加入了。

配置好之后保存并退出。

然后继续创建服务配置文件:

touch /etc/consul.d/server.hcl
chmod 640 /etc/consul.d/server.hcl

然后编辑 /etc/consul.d/server.hcl 添加内容如下:

server = true
bootstrap_expect = 3
bind_addr = "172.16.0.5"
client_addr = "0.0.0.0"
performance {
  raft_multiplier = 1
}
connect {
  enabled = true
}

addresses {
  grpc = "127.0.0.1"
}

ports {
  grpc  = 8502
  grpc_tls = 8503
  http = 8500
}

其中需要注意的配置有:

  1. server 设置为 true 表示处于服务模式。
  2. bootstrap_expect 表示预期的节点数量,这里配置为 3
  3. bind_addr 表示服务绑定的地址,默认不配置也可以,默认值是:0.0.0.0
  4. client_addr 表示客户端绑定的地址,也就是 HTTP 和 DNS 绑定的地址,这个默认是 127.0.0.1 ,因此必须要设置下。
  5. addresses.grpc 表示绑定 gRPC API 的地址,由于我们使用了 TLS 的 gRPC,因此这里为了安全改为了 127.0.0.1
  6. ports 下面配置端口,其中 grpc 默认是 8502,grpc_tls 默认是 8503,http 默认是 8500

配置好之后保存并退出。

然后我们同步配置文件到另外的节点:

scp /etc/consul.d/*.hcl ec2:/etc/consul.d/
scp /etc/consul.d/*.hcl ec3:/etc/consul.d/

然后所有节点都重新设置下配置目录的权限:

# 所有节点都执行
chown --recursive consul:consul /etc/consul.d

然后我们分别在 ec2 和 ec3 上修改配置,主要是改几个和 IP 相关的:

ec2 上面修改 consul.hcl,修改 retry_join 改为另外两个节点的:

retry_join = ["172.16.0.5", "172.16.0.7"]

然后修改 server.hcl,修改 bind_addr

bind_addr = "172.16.0.6"

同样修改 ec3 上面的 consul.hcl:

retry_join = ["172.16.0.5", "172.16.0.6"]

修改 server.hcl:

bind_addr = "172.16.0.7"

最后修改完毕都保存一下。

我们需要选 1 个节点启动 Web UI 服务,一般不用每个节点都启动,这里选择第一个节点 ec1 编辑 server.hcl 配置,添加内容如下:

ui_config {
  enabled = true
}

添加后保存配置。

3.5.服务启动

然后在每个机器上都创建 service 服务文件:/etc/systemd/system/consul.service 填写内容如下:

[Unit]
Description="HashiCorp Consul - A service mesh solution"
Documentation=https://www.consul.io/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/consul.d/consul.hcl

[Service]
EnvironmentFile=-/etc/consul.d/consul.env
User=consul
Group=consul
ExecStart=/usr/local/bin/consul agent -config-dir=/etc/consul.d/
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
KillSignal=SIGTERM
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

每个机器都填写好并且保存,然后启动前先验证下配置文件的正确性:

consul validate /etc/consul.d/

如果校验失败会提示错误,比如上面的 grpc_tls 配置文档没写,但是校验不通过,因此这些报错都需要根据具体的提示进行修改,最终校验通过说明配置基本没有问题。

然后我们在所有节点上依次启动服务:

systemctl start consul

启动后查看状态和日志是否正常:

systemctl status consul
journalctl -u consul.service -f

有个别的报错一般没什么影响,比如离线环境可能远程更新会报错,以及个别连接断开的问题。在全部启动之前每个启动的节点都会等待发现的节点启动,直到所有的节点完全启动集群运行就正常了。

如果需要也可以设置开机自动启动:

systemctl enable consul

3.6.生成访问密钥

启动之后我们需要生成 SecretID 用于客户端或者 UI 的访问:

consul acl bootstrap

这个命令只能执行 1 次后续无法继续执行,创建后返回的内容要保存好,之后访问时都会用到,具体还可以设置访问策略,这里就不再详细说了,放到后续的使用文档中再来整理。

然后需要设置环境变量,建议添加到用户环境变量配置中:

# 这个 token 就是上面生成的 SecretID
export CONSUL_HTTP_TOKEN="d8004e40-7621-fb51-14de-48d9339e65e0"

然后可以查看集群的状态:

consul info
# 查看集群节点
consul members

然后我们还可以访问 Web UI,地址为:http://172.16.0.5:8500,访问之后输入上面的 token 就可以登录上去,然后就可以通过页面进行服务、Key/Value 相关的操作了。

我们如果使用 HTTP API 访问,需要在 Header 里面加上 X-Consul-Token ,比如:

curl --header "X-Consul-Token: d8004e40-7621-fb51-14de-48d9339e65e0" http://127.0.0.1:8500/v1/kv/a/b

和使用 cli 访问是类似的,比如:

consul kv get a/b

这样 Consul 集群就基本上安装完毕了。

3.7.Web UI 开启 HTTPS 配置

到上面已经部署完成了,不过默认情况下 Consul 是采用 HTTP 方式访问 Web UI 或者 API,如果有需要也可以开启 HTTPS 访问的方式,我们首先修改 Web UI 配置节点的配置文件:/etc/consul.d/consul.hcl 修改 tls 块下面的配置如下:

tls {
   defaults {
      ca_file = "/etc/consul.d/certs/consul-agent-ca.pem"
      cert_file = "/etc/consul.d/certs/dc1-server-consul-0.pem"
      key_file = "/etc/consul.d/certs/dc1-server-consul-0-key.pem"

      verify_incoming = false
      verify_outgoing = true
   }

   internal_rpc {
      verify_server_hostname = true
      verify_incoming = true
   }
}

我们将 defaults 中 verify_incoming 的值改为 false ,这样就不会拒绝 HTTPS 和 RPC 连接了,然后我们在 internal_rpc 中再添加 verify_incoming = true 的配置表示对 RPC 进行验证,这样就可以只对 HTTPS 放行了,然后我们保存配置文件。

如果我们只想 Web UI 通过 HTTPS 访问,而不使用 HTTPS API 访问其他节点,那么这个配置不需要同步,但是如果我们需要在所有的节点都想通过 HTTPS API 的方式访问服务,那么每个节点都需要按照上面进行同步修改,否则除了 Web UI 无法通过其他节点无法访问。

然后编辑 /etc/consul.d/server.hcl 配置文件,在 ports 中添加 HTTPS 访问端口:

ports {
  grpc  = 8502
  grpc_tls = 8503
  http = 8500
  https = 8501
}

这里推荐使用约定的 8501 作为 HTTPS 的端口,与此同时 HTTP 也同时开着,如果我们想关闭 HTTP 需要设置如下:

ports {
  grpc  = 8502
  grpc_tls = 8503
  http = -1
  https = 8501
}

这样就只提供 HTTPS 访问了,然后保存配置并且同步到所有的节点,以保证都开启一致的服务,也就是每个节点都需要开启 HTTPS。

上面两个配置修改完成后,所有节点依次重启 Consul 服务,然后通过 HTTPS 访问 Web UI 就可以了。

如果使用命令行方式访问,需要指定本地公钥签名的位置或者放到根证书目录中,否则 curl 认为不安全会报错:

curl --header "X-Consul-Token: d8004e40-7621-fb51-14de-48d9339e65e0" --cacert /etc/consul.d/certs/consul-agent-ca.pem https://127.0.0.1:8501/v1/kv/a/b

这样 HTTPS 服务就开启成功了。

Reference:

  1. 集群安装
  2. https://developer.hashicorp.com/consul/tutorials/production-deploy/security
  3. https://developer.hashicorp.com/consul/tutorials/security-operations/tls-encryption-openssl-secure#configure-the-consul-ui-for-https