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.pemca.pemconsul-key.pemconsul.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: v1kind: Servicemetadata:  name: consul  namespace: discovery  labels:    name: consulspec:  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: v1kind: ServiceAccountmetadata:  name: consul  namespace: discovery  labels:    app: consul---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata:  name: consul  labels:    app: consulrules:  - apiGroups: [""]    resources:      - pods    verbs:      - get      - list---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:  name: consulroleRef:  apiGroup: rbac.authorization.k8s.io  kind: ClusterRole  name: consulsubjects:  - kind: ServiceAccount    name: consul    namespace: discovery

创建 Consul StatefulSet

apiVersion: apps/v1kind: StatefulSetmetadata:  name: consul  namespace: discoveryspec:  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=consulNAME       READY   STATUS    RESTARTS   AGEconsul-0   1/1     Running   0          13hconsul-1   1/1     Running   0          13hconsul-2   1/1     Running   0          13h

验证

通过访问UI,可以看到我们的三个实例均已成功加入集群,并且选举成功。