Consul Service Discovery实践

Consul 是什么? 官方给出的答案是:

Consul is a service mesh solution providing a full featured control plane with service discovery, configuration, and segmentation functionality.

即提供服务发现,配置中心以及安全加密通信服务的Service Mesh解决方案。包含关键特性:

服务发现:Consul client注册服务,通过DNS或HTTP接口的形式访问Consul,获取服务列表;

健康检查:agent通过自定义的脚本检查service的健康,十分灵活,可以配合后端做负载均衡;

KV存储:多级的kv存储;

加密认证通信:service之间的通信经过TLS加密并认证,intentions机制可以实时控制service之间通信权限;

多数据中心:支持多数据中心

Consul 1.2版本之后 增加了connect特性,将agent拓展为sidecar,实现了一套Service Mesh的解决方案,本文未涉及到这一部分,只涉及Service Discovery部分。

2 Service Discovery 实践

2.1 Server Cluster 部署

Consul Agent以client或者server模式运行,每个数据中心配置一个server集群,包含至少一个server agent。我们选择部署三台机器 10.0.0.1 , 10.0.0.2 , 10.0.0.3

{
    "datacenter" : "test_dc",
    "node_name" : "node1",
    "bind_addr": "10.0.0.1",
    "retry_join" : ["10.0.0.1","10.0.0.2","10.0.0.3"],
    "data_dir" : "data/",
    "server" : true,
    "bootstrap_expect" : 3,
    "log_level": "INFO",
    "ui": true,
    "ports":{
        "http":8081
    }
}

配置文件中 node_name 为统一的前缀,方便后续acl策略控制, retry_join 设置为集群的server地址。在三台机器上分别启动server agent.

./consul agent -config-dir=config/ -client=0.0.0.0

启动之后可以看到service中有三个consul实例。

2.2 服务注册、健康检查、发现

(1)服务注册

Consul的服务注册通过client模式的agent进行。在service所在机器上启动client agent,加入到server集群中,该agent的存活状态是service的默认健康状态。

配置 consul_client.json ,启动client agent。

{
    "datacenter" : "test_dc",
    "node_name" : "node4",
    "retry_join" : ["10.0.0.1","10.0.0.2","10.0.0.3"],
    "bind_addr" : "10.0.0.4",
    "data_dir" : "data/",
    "enable_script_checks": true,
    "log_level": "INFO"
}

./consul agent -config-dir=config/

service通过配置文件的形式定义,这里定义一个名为 collector 的服务,设置 id , name , address , port

service.json

{
  "service": {
    "id": "collector",
    "name": "collector",
    "tags": ["public"],
    "address": "10.0.0.4",
    "meta": {
      "meta": "test meta"
    },
    "port": 2203,
    "weights": {
      "passing": 1,
      "warning": 1
    }
  }
}

执行 reload 操作,此时UI可以看到service已经注册上来。

(2)健康检查

service中通过脚本做健康检查,可以添加任意的健康条件,示例中设置了三种状态检查,心跳、机器内存、服务负载。

service.json

{
  "service": {
    "id": "collector",
    "name": "collector",
    "tags": ["public"],
    "address": "10.0.0.4",
    "meta": {
      "meta": "test meta"
    },
    "port": 2203,
    "checks": [
      {
        "args": ["scripts/check_status.sh", "10000", "95", "1000"],
        "interval": "5s"
      },
      {
        "args": ["scripts/check_node.sh", "1"],
        "interval": "5s"
      },
      {
        "args": ["scripts/check_heartbeat.sh"],
        "interval": "5s"
      }
    ],
    "weights": {
      "passing": 1,
      "warning": 1
    }
  }
}

check_heartbeat.sh

#!/bin/sh
CODE=0
echo "check hearbeat"
ret=`curl -sSL http://localhost:12125/heartbeat`
echo $ret
if [ "$ret" = "ok" ]
then
  CODE=0
else
  CODE=-1
fi
echo "exit code" $CODE
exit $CODE

在上面配置了service之后,由于service未启动,健康检查不通过,service处于不可用状态。

启动service后,三项健康检查通过后,service处于可用状态。

(3)服务发现

服务发现可通过HTTP API或者DNS的形式获取。

GET /v1/health/node/:node
GET /v1/health/checks/:service
GET /v1/health/service/:service

2.3 KV存储

GET /v1/kv/:key
PUT /v1/kv/:key
DELETE  /v1/kv/:key

2.4 ACL

Consul提供丰富的权限控制,用以控制数据访问,以及API读写权限。ACL的核心概念有policy和token,policy配置具体数据的读写权限,token则是应用于policy上,使用指定的token来执行对应的policy。

(1)bootstrap token

bootstrap token为管理员token,拥有最高权限,不要轻易将该token应用给某个policy。

开启ACL首先需要更改server配置文件,设置默认为 deny

{
  "datacenter" : "test_dc",
  "node_name" : "node1",
  "bind_addr": "10.0.0.1",
  "data_dir" : "data/",
  "server" : true,
  "bootstrap_expect" : 1,
  "log_level": "INFO",
  "ui": true,
  "acl" : {
    "enabled" : true,
    "default_policy" : "deny",
    "enable_token_persistence" : true
   },
  "ports":{
    "http":8081
  }
}

启动其中一台server,在初始启动之后,生成bootstrap token。

./consul acl bootstrap -http-addr="http://10.0.0.1:8081"

将得到的bootstrap token写到server配置文件 acl_master_token 字段中,重新启动server。

{
  "datacenter" : "test_dc",
  "node_name" : "node1",
  "bind_addr": "10.0.0.1",
  "retry_join" : ["10.0.0.1","10.0.0.2","10.0.0.3"],
  "acl_master_token" : "xxxxxxx-xxxxxx-xxxx-xxxx-xxxxxxxxx",
  "data_dir" : "data/",
  "server" : true,
  "bootstrap_expect" : 3,
  "log_level": "INFO",
  "ui": true,
  "acl" : {
    "enabled" : true,
    "default_policy" : "deny",
    "enable_token_persistence" : true
   },
  "ports":{
    "http":8081
  }
}

UI中输入该bootstrap token,获得管理员权限

(2) agent token

在开启了deny的策略后,agent之间通信也需要token。首先增加agent之间的访问策略,配置写权限,然后为该策略生成对应的token。

将token写到server配置文件 acl_agent_token 字段中,重新启动三个server agent。该token也需要配置在各个client agent中。

{
    "datacenter" : "test_dc",
    "node_name" : "node1",
    "bind_addr": "10.0.0.1",
    "retry_join" : ["10.0.0.1","10.0.0.2","10.0.0.3"],
    "acl_agent_token" : "xxxxxxx-xxxxxx-xxxx-xxxx-xxxxxxxxx",
    "acl_master_token" : "xxxxxxx-xxxxxx-xxxx-xxxx-xxxxxxxxx",
    "data_dir" : "data/",
    "server" : true,
    "bootstrap_expect" : 3,
    "log_level": "INFO",
    "ui": true,
    "acl" : { 
        "enabled" : true,
        "default_policy" : "deny",
        "enable_token_persistence" : true
     },  
    "ports":{
        "http":8081
    }   
}

(3)service token

service token用于client agent注册服务。如需要注册 collector ,则为collector分配写权限和token

service "collector" {
  policy = "write"
}

在collector的描述文件中加入service token

{
  "service": {
    "id": "collector",
    "name": "collector",
    "tags": ["public"],
    "address": "10.0.0.4",
    "token":"xxxxxxx-xxxxxx-xxxx-xxxx-xxxxxxxxx",
    "meta": {
      "meta": "test meta"
    },
    "port": 2203,
    "enable_tag_override": false,
    "weights": {
      "passing": 1,
      "warning": 1
    }
  }
}

(4)anonymous token

配置ACL后,命令行和http请求访问均需要带上对应的token才能访问对应的数据,但某些场景下我们可能不方便保管token,这时可以使用anonymous token开放某些权限。

匿名token是系统的内置不设防的令牌。比如,我们将kv的读权限开放,便可以使用匿名token。首先配置kv策略(注意还需要开放node读权限),然后将匿名token应用在该策略上。

node_prefix "" {
  policy = "read"
}

key_prefix "" {
  policy = "read"
}

参考

  1. Introduction to Consul – https://www.consul.io/intro/index.html
  2. HashiCorp Consul 1.2: Service Mesh – https://www.hashicorp.com/blog/consul-1-2-service-mesh/
  3. Secure Consul with ACLs – https://learn.hashicorp.com/consul/security-networking/production-acls