乐趣区

Consul-on-Kubernetes

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,可以看到我们的三个实例均已成功加入集群,并且选举成功。

退出移动版