目录

    本篇主要阐述了为什么需要服务发现功能,对几种服务发现工具进行了比较。同时,在 CentOS 上,对 Etcd、Confd 、Nginx 实现服务发现功能进行了实践。

    1. 服务注册与发现

    1.1 为什么需要注册和发现服务

    随着微服务的兴起,大量接口服务化。当新的微服务加入或微服务的信息发生变更时,服务方如何通知周边系统、使用方如何知道这些变更呢?

    这时就需要服务的注册配置和发现功能。

    • 服务注册配置——存储的信息至少包括正在运行的服务的主机和端口信息
    • 服务发现——允许其他用户可以发现在服务注册配置阶段存储的信息。

    1.2 几种服务发现工具的比较

    FeatureConsulZookeeperEtcdEuerka
    服务健康检查服务状态,内存,硬盘等(弱)长连接,keepalive连接心跳可配支持
    多数据中心支持
    kv存储服务支持支持支持
    一致性raftpaxosraft
    capcacpcpap
    使用接口(多语言能力)支持http和dns客户端http/grpchttp(sidecar)
    watch支持全量/支持long polling支持支持 long polling支持 long polling/大部分增量
    自身监控metricsmetricsmetrics
    安全acl /httpsaclhttps支持(弱)
    spring cloud集成已支持已支持已支持已支持
    注:
    - C,强一致性 (Consistency)
    - A,可用性 (Availability)
    - P,网络分区故障的容错性 (Partition Tolerance)
    

    Consul 是使用 Go 语言开发的分布式协调系统,对业务发现的管理提供很好的支持,它的 HTTP API 也能很好的和不同的语言绑定,并支持跨数据中心的应用。缺点是相对较新,适合喜欢尝试新事物的用户。

    ZooKeeper 功能全,社区活跃,用户群体很大,对所有典型的用例都有很好的封装,支持不同语言的绑定。缺点是,整个应用比较重,依赖于 Java,不支持跨数据中心。

    Etcd 是一个更轻量级的分布式协调的应用,更适合一些轻量级的应用来使用,同时 Etcd 也提供 HTTP API 操作接口。值得注意的是,Kubernetes 采用了 Etcd 作为配置中心。

    Eureka 是 Netflix 开源的一个 RESTful 服务,主要用于服务的注册发现。Eureka 由两个组件组成:Eureka 服务器和 Eureka 客户端。Eureka 服务器用作服务注册服务器。Eureka 客户端是一个 Java 客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。

    2. Etcd

    2.1 简介

    Etcd 是一个分布式、使用 Raft 算法维护一致性的 key-value 存储系统,与其类似产品有 Zookeeper、Consul 等,Etcd 相对 Zookeeper,更加轻量、易运维。同时,Etcd 支持 TLS 通信,具备高性能的写入能力。

    2.2 Raft 算法

    很多的分布式系统都会采用 Paxos 协议,但是 Paxos 协议难以理解,并且在实际实现中差别比较大。所以 Etcd 选择了 Raft 作为它的一致性协议。Raft 是 Diego Ongaro 和 John Ousterhout 在 In Search of an Understandable Consensus Algorithm 中提出的。它在牺牲很少可用性,达到相似功能的情况下,对 Paxos 做了很大的优化,并且比 Paxos 简单易懂很多。

    它主要集中在解决两个问题:

    • 领导者选举(Leader Election) Raft 先通过领导选举选出一个 Leader,后续的一致性维护都由 Leader 来完成,这就简化了一致性的问题。Raft 会保证一个时间下只会有一个 Leader,并且在超过一半节点投票的情况下才会被选为 Leader。当 Leader 挂掉的时候,新的 Leader 将会被选出来。

    • 日志复制 (Log Replication) 为了维护状态,系统会记录下来所有的操作命令日志。Leader 在收到客户端操作命令后,会追加到日志的尾部。然后 Leader 会向集群里所有其它节点发送 AppendEntries RPC 请求,每个节点都通过两阶段提交来复制命令,这保证了大部分的节点都能完成。

    3. Etcd + Confd + Nginx

    在进行应用部署时,服务运行起来后,通过接口向 Etcd 注册相关 key-value 信息,Confd 检测到 Etcd 的 key-value 变化后,立即触发程序通过模板形成新的 Nginx 配置文件。

    Nginx 先做离线语法测试,如果没问题就覆盖原配置,进而 reload,测试不通过就不覆盖原配置,整个过程安全可控。

    3.1 Nginx 安装配置

    • 安装以 CentOS 为例
    # 安装 nginx
    yum install -y nginx
    # 查看 nginx 安装位置
    whereis nginx
    nginx: /usr/sbin/nginx /usr/lib64/nginx /etc/nginx /usr/share/nginx /usr/share/man/man3/nginx.3pm.gz /usr/share/man/man8/nginx.8.gz
    # 查看 nginx 配置文件位置
    nginx -t
    nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
    nginx: configuration file /etc/nginx/nginx.conf test is successful
    # 查看配置
    cat /etc/nginx/nginx.conf
    ...
        include /etc/nginx/conf.d/*.conf;
    ...
    

    include /etc/nginx/conf.d/*.conf; 这句配置会将 conf.d 目录下 .conf 后缀的文件配置载入 Nginx。

    3.2 Confd 安装配置

    直接从 github 下载 Confd 的 Linux 编译版本,然后将可执行文件移动到 /usr/sbin/ 目录即可。

    # 下载 Confd 包
    wget https://github.com/kelseyhightower/confd/releases/download/v0.15.0/confd-0.15.0-linux-amd64
    # 复制可执行文件
    mv confd-0.15.0-linux-amd64 /usr/sbin/confd
    # 增加可执行权限
    chmod +x /usr/sbin/confd
    

    3.3 Etcd 安装配置

    • 安装以 CentOS 为例
    yum -y install etcd
    
    • 配置文件目录,/etc/etcd/etcd.conf
    # 默认配置,可以不修改
    ETCD_NAME=default
    ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
    ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
    ETCD_ADVERTISE_CLIENT_URLS="http://0.0.0.0:2379"
    
    • 启动服务
    service etcd start
    

    3.4 Confd 安装配置

    • 创建配置目录
    mkdir -p /etc/confd/{conf.d,templates}
    

    在 conf.d 下创建 .toml 配置文件

    /etc/confd/conf.d/app1.toml

    [template]
    prefix = "/app1"
    src = "subdomain.conf.tmpl"
    dest = "/tmp/app1-subdomain-confd-auto.conf"
    keys = [
      "/subdomain",
      "/upstream",
    ]
    
    [template]
    prefix = "/app1"
    src = "subdomain-nginx.conf.tmpl"
    dest = "/etc/nginx/conf.d/app1-subdomain-confd-nginx-auto.conf"
    keys = [
      "/subdomain",
      "/upstream",
    ]
    check_cmd = "/usr/sbin/nginx -t"
    reload_cmd = "/usr/sbin/nginx -s reload"
    

    在 templates 下创建 .tmpl 模板文件

    /etc/confd/templates/subdomain.conf.tmpl

    [subdomain]
    subdomain = {{getv "/subdomain"}}
    upstream = {{getv "/upstream"}}
    

    Nginx 模板配置

    /etc/confd/templates/subdomain-nginx.conf.tmpl

    upstream {{getv "/subdomain"}} {
    {{range getvs "/upstream/*"}}
        server {{.}};
    {{end}}
    }
    
    server {
        server_name  {{getv "/subdomain"}}.example.com;
        location / {
            proxy_pass        http://{{getv "/subdomain"}};
            proxy_redirect    off;
            proxy_set_header  Host             $host;
            proxy_set_header  X-Real-IP        $remote_addr;
            proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
       }
    }
    

    监听 Etcd 的 key-value 值的变化

    # 只处理一次
    confd -onetime -backend etcd -node http://127.0.0.1:2379
    # 按时间轮询
    confd -interval=60 -backend etcd -node http://127.0.0.1:2379 &   
    

    3.5 注册服务

    可以通过 etcdctl 命令,也可以通过 Etcd 提供的 HTTP API,在 Etcd 插入 subdomain 和 upstream 数据。下面以 etcdctl 为例:

    etcdctl set /app1/subdomain app1
    etcdctl set /app1/upstream/instance1 "127.0.0.1:5601"
    

    由于在本地 5601 端口,启动了 ELK 服务,通过访问 http://app1.example.com 打开 Kibana 。

    如果需要 Nginx 进行负载均衡,可以在 upstream 上配置多个键值。同时,需要在 /etc/confd/conf.d 目录下新建两个文件 app2-nginx.toml 和 app2.toml,内容上只需要将 app1-nginx.toml 和 app1.toml 中的 app1 修改为 app2 即可。

    etcdctl set /app2/subdomain app2
    etcdctl set /app2/upstream/instance1 "127.0.0.2:80"
    etcdctl set /app2/upstream/instance2 "127.0.0.1:80"
    

    4. 参考