consul 发展到今日,早已不是只用于服务发现和配置共享的工具了。官方对其的定义是自动化网络配置,发现服务并启用跨任何云或运行时的安全连接。
特性
- 与 Kubernetes 集成和扩展。利用 Helm 在 Kubernetes 上快速部署 Consul。自动为 Kubernetes 资源注入 sidecar。将多个集群联合到一个服务网格中。
- 跨任何运行时的服务网格。在任何运行时或基础架构中跨任何云在裸机,虚拟机和 Kubernetes 集群中部署服务网格。
- 动态的负载均衡。通过集成的 DNS 解决发现的服务。自动化第三方负载均衡器(F5,NGINX,HAProxy)。无需手动配置网络设备。
- 安全的多云服务网络。利用访问策略和服务网格资源之间的自动 mTLS 加密,在任何环境中运行的安全服务。
- 通过运行状况检查发现服务。Consul 支持检测新服务的部署,对现有服务的更改,并提供实时代理运行状况以减少停机时间。
- 强大的生态系统。Consul 为许多流行的 DevOps 和网络工具提供支持并与之集成。
默认端口
consul 默认使用下列端口
- 8300(tcp): Server RPC,server 用于接受其他 agent 的请求
- 8301(tcp,udp): Serf LAN,数据中心内 gossip 交换数据用
- 8302(tcp,udp): Serf WAN,跨数据中心 gossip 交换数据用
- 8400(tcp): CLI RPC,接受命令行的 RPC 调用
- 8500(tcp): HTTP API 及 Web UI
- 8600(tcp udp): DNS 服务,可以把它配置到 53 端口来响应 dns 请求
部署
官方推荐的 helm 部署方案,包括了诸多解决方案。当我们只是用来做服务发现或是配置中心的时候,我们可以选择 yaml 部署。
本文将完成在 Kubernetes 上部署三(3)节点 Consul 集群。
- 使用 StatefulSet 的三(3)个节点 Consul 群集
- 使用 TLS 和加密密钥的 Consul 成员之间的安全通信
生成 TLS Certificates
新建 ca 目录,包含以下三个文件:
ca-config.json
{
"signing": {
"default": {"expiry": "43800h"},
"profiles": {
"default": {"usages": ["signing", "key encipherment", "server auth", "client auth"],
"expiry": "43800h"
}
}
}
}
ca-csr.json
{
"hosts": ["cluster.consul"],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "Kubernetes",
"OU": "CA",
"ST": "Oregon"
}
]
}
consul-csr.json
{
"CN": "server.dc1.cluster.consul",
"hosts": [
"server.dc1.cluster.consul",
"127.0.0.1"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "US",
"L": "Portland",
"O": "Hightower Labs",
"OU": "Consul",
"ST": "Oregon"
}
]
}
Consul 成员之间的 RPC 通信将使用 TLS 进行加密。初始化证书颁发机构(CA):
cfssl gencert -initca ca/ca-csr.json | cfssljson -bare ca
创建 Consul TLS 证书和私钥:
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=ca/ca-config.json \
-profile=default \
ca/consul-csr.json | cfssljson -bare consul
此时,您应该在当前工作目录中具有以下文件:
ca-key.pem
ca.pem
consul-key.pem
consul.pem
生成 Consul Gossip Encryption Key
Consul 成员之间的 Gossip 通信将使用共享的加密密钥进行加密。生成并存储加密密钥:
GOSSIP_ENCRYPTION_KEY=$(consul keygen)
创建 Consul Secret 和 Configmap
Consul 集群将使用 CLI 标志,TLS 证书和配置文件的组合进行配置,这些文件引用 Kubernetes configmap 和 secret。
将 Gossip 加密密钥和 TLS 证书存储在密钥中:
kubectl create secret generic consul -n discovery \
--from-literal="gossip-encryption-key=${GOSSIP_ENCRYPTION_KEY}" \
--from-file=ca.pem \
--from-file=consul.pem \
--from-file=consul-key.pem
将 Consul 服务器配置文件存储在 ConfigMap 中:
kubectl create configmap consul -n discovery --from-file=server.json
而 server.json 文件如下:
{
"bind_addr": "0.0.0.0",
"ca_file": "/etc/tls/ca.pem",
"cert_file": "/etc/tls/consul.pem",
"client_addr": "0.0.0.0",
"disable_host_node_id": true,
"data_dir": "/consul/data",
"datacenter": "dc1",
"domain": "cluster.consul",
"key_file": "/etc/tls/consul-key.pem",
"ports": {"https": 8443},
"retry_join": ["provider=k8s namespace=discovery label_selector=\"app=consul,component=server\""],"server": true,"telemetry": {"prometheus_retention_time":"5m"},"verify_incoming": true,"verify_outgoing": true,"verify_server_hostname": true,"ui": true
}
创建 Consul Service
apiVersion: v1
kind: Service
metadata:
name: consul
namespace: discovery
labels:
name: consul
spec:
clusterIP: None
ports:
- name: http
port: 8500
targetPort: 8500
- name: cli-rpc
port: 8400
targetPort: 8400
- name: serflan-tcp
protocol: "TCP"
port: 8301
targetPort: 8301
- name: serflan-udp
protocol: "UDP"
port: 8301
targetPort: 8301
- name: serfwan-tcp
protocol: "TCP"
port: 8302
targetPort: 8302
- name: serfwan-udp
protocol: "UDP"
port: 8302
targetPort: 8302
- name: agent-rpc
port: 8300
targetPort: 8300
- name: dns
port: 8600
targetPort: 8600
selector:
app: consul
创建 Consul Service Account
由于我们 retry_join 使用的是 k8s provider,所有必须要配置 rbac。给与获取 pod 的权限。
apiVersion: v1
kind: ServiceAccount
metadata:
name: consul
namespace: discovery
labels:
app: consul
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: consul
labels:
app: consul
rules:
- apiGroups: [""]
resources:
- pods
verbs:
- get
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: consul
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: consul
subjects:
- kind: ServiceAccount
name: consul
namespace: discovery
创建 Consul StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: consul
namespace: discovery
spec:
selector:
matchLabels:
app: consul
component: server
serviceName: consul
podManagementPolicy: "Parallel"
replicas: 3
template:
metadata:
labels:
app: consul
component: server
annotations:
consul.hashicorp.com/connect-inject: "false"
spec:
serviceAccountName: consul
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- consul
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 10
securityContext:
fsGroup: 1000
containers:
- name: consul
image: "consul:1.7.3"
env:
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: GOSSIP_ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: consul
key: gossip-encryption-key
args:
- "agent"
- "-advertise=$(POD_IP)"
- "-bootstrap-expect=3"
- "-config-file=/etc/consul/config/server.json"
- "-encrypt=$(GOSSIP_ENCRYPTION_KEY)"
volumeMounts:
- name: data
mountPath: /consul/data
- name: config
mountPath: /etc/consul/config
- name: tls
mountPath: /etc/tls
lifecycle:
preStop:
exec:
command:
- /bin/sh
- -c
- consul leave
ports:
- containerPort: 8500
name: ui-port
- containerPort: 8400
name: cli-port
- containerPort: 8301
name: serflan
- containerPort: 8302
name: serfwan
- containerPort: 8600
name: dns
- containerPort: 8300
name: server
volumes:
- name: config
configMap:
name: consul
- name: tls
secret:
secretName: consul
volumeClaimTemplates:
- metadata:
name: data
namespace: discovery
annotations:
everest.io/disk-volume-type: SAS
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: csi-disk-topology
由于我的安装是在华为云上,所以如果是其他公有云或是其他存储,注意替换 volumeClaimTemplates。
查看安装情况:
kubectl get pods -n discovery -l app=consul
NAME READY STATUS RESTARTS AGE
consul-0 1/1 Running 0 13h
consul-1 1/1 Running 0 13h
consul-2 1/1 Running 0 13h
验证
通过访问 UI,可以看到我们的三个实例均已成功加入集群,并且选举成功。