利用 etcdhosts 插件搭建分布式 CoreDNS

目前宿主机上全部采用的 dnsmasq 作为 DNS 管理,其中有一个很大的问题是需要进行 DNS 冗余,dnsmasq 每次修改都要多台机器同步,所以自己写了一个插件配合 CoreDNS 实现分布式部署,如果想了解插件编写方式请参考 Writing Plugin for Coredns

一、etcdhosts 插件简介

etcdhosts 顾名思义,就是将 hosts 文件存储在 Etcd 中,然后多个 CoreDNS 共享一份 hosts 文件;得益于 Etcd 提供的 watch 功能,当 Etcd 中的 hosts 文件更新时,每台 CoreDNS 服务器都会接到推送,同时完成热重载;etcdhosts 基本架构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
+-----------------------------------------------------------------------------+
| |
| +-----------+ |
| | | |
| | CoreDNS +---------------------+ |
| | | | |
| +-----------+ | +------------------+ |
| | | | |
| +--------v---------+ | | |
| +-----------+ | | | | |
| | | | | | dnsctl or | |
| | CoreDNS +------------> Etcd Cluster <------+ other etcd tool | |
| | | | | | | |
| +-----------+ | | | | |
| +---------^--------+ | | |
| | | | |
| +-----------+ | +------------------+ |
| | | | |
| | CoreDNS +----------------------+ |
| | | |
| +-----------+ |
| |
| |
+-----------------------------------------------------------------------------+

二、编译 CoreDNS

etcdhosts release 页已经提供部分版本的预编译文件,可以直接下载使用。

etcdhosts 作为一个 CoreDNS 扩展插件采用直接偶合的方式编写(未采用 gRPC 是因为考虑性能影响),这意味着需要重新编译 CoreDNS 来集成插件,以下为 CoreDNS 编译过程(使用 docker):

1
2
3
4
# clone source code
git clone https://github.com/ytpay/etcdhosts.git
# build
cd etcdhosts && ./build v1.8.0

编译完成后将在 build 目录下生成各个平台的二进制文件压缩包。

三、搭建 Etcd 集群

Etcd 集群搭建将直接采用 deb 安装包,具体细节这里不再阐述,本次搭建系统为 Ubuntu 20,以下为搭建步骤。

2.1、安装软件包

1
2
3
4
5
6
# 下载 cfssl 安装包,用于签署证书
wget https://github.com/mritd/etcd-deb/releases/download/v3.4.13/cfssl_1.4.1_amd64.deb
# 下载 etcd 安装包
wget https://github.com/mritd/etcd-deb/releases/download/v3.4.13/etcd_3.4.13_amd64.deb
# 执行安装
dpkg -i cfssl_1.4.1_amd64.deb etcd_3.4.13_amd64.deb

2.2、创建证书

创建证书需要先修改证书配置文件(etcd-csr.json)然后借助 cfssl 工具来创建证书

/etc/etcd/cfssl/etcd-csr.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"O": "etcd",
"OU": "etcd Security",
"L": "Beijing",
"ST": "Beijing",
"C": "CN"
}
],
"CN": "etcd",
"hosts": [
"127.0.0.1",
"localhost",
"*.etcd.node",
"*.kubernetes.node",
+ "172.16.11.71",
+ "172.16.11.72",
+ "172.16.11.73"
]
}

通过脚本创建证书

1
2
3
cd /etc/etcd/cfssl
./create.sh
cp *.pem /etc/etcd/ssl

证书创建完成后需要分发到其他两台机器上,保证三台节点的 /etc/etcd/ssl 目录证书相同。

1
2
3
4
5
# 复制证书
scp /etc/etcd/ssl/*.pem root@NODE2:/etc/etcd/ssl
scp /etc/etcd/ssl/*.pem root@NODE3:/etc/etcd/ssl
# 修复权限(三台都要修复)
chown -R etcd:etcd /etc/etcd/

2.3、调整集群配置

证书签署完成后,简单的调整每台机器上的集群节点配置即可

/etc/etcd/etcd.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# [member]
+ # 节点号自行修改,推荐格式: etcd+节点IP,例如 etcd21
+ ETCD_NAME=etcd1
ETCD_DATA_DIR="/var/lib/etcd/data"
ETCD_WAL_DIR="/var/lib/etcd/wal"
ETCD_SNAPSHOT_COUNT="100"
+ # 修改为当前机器 IP
+ ETCD_LISTEN_PEER_URLS="https://172.16.11.71:2380"
+ # 修改为当前机器 IP
+ ETCD_LISTEN_CLIENT_URLS="https://172.16.11.71:2379,http://127.0.0.1:2379"
ETCD_QUOTA_BACKEND_BYTES="8589934592"
ETCD_MAX_REQUEST_BYTES="10485760"

# [cluster]
+ # 修改为当前机器 IP
+ ETCD_INITIAL_ADVERTISE_PEER_URLS="https://172.16.11.71:2380"
# if you use different ETCD_NAME (e.g. test), set ETCD_INITIAL_CLUSTER value for this name, i.e. "test=http://..."
+ # 三台机器都要按照格式写好
+ ETCD_INITIAL_CLUSTER="etcd1=https://172.16.11.71:2380,etcd2=https://172.16.11.72:2380,etcd3=https://172.16.11.73:2380"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
+ # 修改为当前机器 IP
+ ETCD_ADVERTISE_CLIENT_URLS="https://172.16.11.71:2379"

ETCD_AUTO_COMPACTION_MODE="revision"
ETCD_AUTO_COMPACTION_RETENTION="16"
ETCD_QUOTA_BACKEND_BYTES="5368709120"

# [security]
ETCD_CERT_FILE="/etc/etcd/ssl/etcd.pem"
ETCD_KEY_FILE="/etc/etcd/ssl/etcd-key.pem"
ETCD_TRUSTED_CA_FILE="/etc/etcd/ssl/etcd-root-ca.pem"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_AUTO_TLS="true"
ETCD_PEER_CERT_FILE="/etc/etcd/ssl/etcd.pem"
ETCD_PEER_KEY_FILE="/etc/etcd/ssl/etcd-key.pem"
ETCD_PEER_CLIENT_CERT_AUTH="true"
ETCD_PEER_TRUSTED_CA_FILE="/etc/etcd/ssl/etcd-root-ca.pem"
ETCD_PEER_AUTO_TLS="true"

最后每台机器执行 systemctl start etcd 启动即可,验证集群是否健康可以使用如下命令测试:

1
2
3
4
5
etcdctl endpoint health --cert /etc/etcd/ssl/etcd.pem --key /etc/etcd/ssl/etcd-key.pem --cacert /etc/etcd/ssl/etcd-root-ca.pem --endpoints https://172.16.11.71:2379,https://172.16.11.72:2379,https://172.16.11.73:2379

https://172.16.11.71:2379 is healthy: successfully committed proposal: took = 33.07493ms
https://172.16.11.72:2379 is healthy: successfully committed proposal: took = 32.132266ms
https://172.16.11.73:2379 is healthy: successfully committed proposal: took = 40.745291ms

三、搭建 CoreDNS 集群

3.1、CoreDNS 安装

系统级 CoreDNS 安装推荐直接使用 systemd 管理,官方目前提供了 systemd 相关配置文件: https://github.com/coredns/deployment/tree/master/systemd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 安装二进制文件
tar -zxvf coredns_1.8.0_linux_amd64.tgz
mv coredns /usr/bin/coredns

# 安装 systemd 配置
wget https://raw.githubusercontent.com/coredns/deployment/master/systemd/coredns-sysusers.conf -O /usr/lib/sysusers.d/coredns-sysusers.conf
wget https://raw.githubusercontent.com/coredns/deployment/master/systemd/coredns-tmpfiles.conf -O /usr/lib/tmpfiles.d/coredns-tmpfiles.conf
wget https://raw.githubusercontent.com/coredns/deployment/master/systemd/coredns.service -O /usr/lib/systemd/system/coredns.service

# reload
systemctl daemon-reload
# 初始化用户
systemd-sysusers
# 初始化临时目录
systemd-tmpfiles --create
# 创建配置目录
mkdir -p /etc/coredns/ssl

3.2、etcdhosts 配置

etcdhosts 的配置类似官方的 etcd 插件,其配置格式如下:

1
2
3
4
5
6
7
8
9
10
11
etcdhosts [ZONES...] {
[INLINE]
ttl SECONDS
no_reverse
fallthrough [ZONES...]
key ETCD_KEY
endpoint ETCD_ENDPOINT...
credentials ETCD_USERNAME ETCD_PASSWORD
tls ETCD_CERT ETCD_KEY ETCD_CACERT
timeout ETCD_TIMEOUT
}

以下是一个简单的可启动的样例配置:

/etc/coredns/Corefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
. {
# 绑定接口地址
bind 172.16.11.71

# cache
cache 30 . {
success 4096
}

# etcdhosts 配置
etcdhosts . {
fallthrough .
key /etcdhosts
timeout 5s
tls /etc/coredns/ssl/etcd.pem /etc/coredns/ssl/etcd-key.pem /etc/coredns/ssl/etcd-root-ca.pem
endpoint https://172.16.11.71:2379 https://172.16.11.72:2379 https://172.16.11.73:2379
}

# 上游 DNS 配置
forward . 114.114.114.114:53 {
max_fails 2
expire 20s
policy random
health_check 0.2s
}

# 日志配置
errors
log . "{remote}:{port} - {>id} \"{type} {class} {name} {proto} {size} {>do} {>bufsize}\" {rcode} {>rflags} {rsize} {duration}"
}

由于 etcdhosts 插件需要连接 etcd 集群,所以需要将证书复制到 Corefile 指定的位置:

1
2
3
4
# 实际生产环境 coredns 与 etcd 一般不在一台机器上,请自行 scp
cp /etc/etcd/ssl/*.pem /etc/coredns/ssl
# 修复权限
chown -R coredns:coredns /etc/coredns

最后直接启动即可(首次启动会出现 [ERROR] plugin/etcdhosts: invalid etcd response: 0 错误,属于正常情况):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 启动
systemctl start coredns

# 测试
dig @172.16.11.71 baidu.com

; <<>> DiG 9.16.1-Ubuntu <<>> @172.16.11.71 baidu.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 35323
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 8e3137531ed0b57a (echoed)
;; QUESTION SECTION:
;baidu.com. IN A

;; ANSWER SECTION:
baidu.com. 30 IN A 220.181.38.148
baidu.com. 30 IN A 39.156.69.79

;; Query time: 8 msec
;; SERVER: 172.16.11.71#53(172.16.11.71)
;; WHEN: Mon Nov 16 20:18:25 CST 2020
;; MSG SIZE rcvd: 100

最后在多台机器上通过同样的配置启动 CoreDNS 即可,此时所有 CoreDNS 服务器通过 Etcd 提供一致性的记录解析。

四、记录调整

所有 CoreDNS 启动成功后,默认 etcdhosts 插件将会读取 Etcd 中的 /etcdhosts key 作为 hosts 文件载入;载入成功后将会在内存级进行 Cache,多次查询不会造成疯狂的 Etcd 请求,只有当触发 reload 时(包括 Etcd 更新)才会重新查询 Etcd。所以此时只需要向 Etcd 的 /etcdhosts key 写入一个 hosts 文件即可;写入 Etcd 可以使用 etcdctl 以及其他的开源工具,甚至自己开发都可以,记录更改只需要跟 Etcd 打交道,不需要理会 CoreDNS;由于本人实在是比较菜,前端页面写不出来,所以弄了一个命令行版本的工具: dnsctl

dnsctl 只有一个可执行文件,默认情况下 dnsctl 读取 $HOME/.dnsctl.yaml 配置文件来沟通 Etcd,配置文件格式如下:

1
2
3
4
5
6
7
8
9
10
11
# etcd 中 etcdhosts 插件的 key
dnskey: /etcdhosts
# etcd 集群配置
etcd:
cert: /etc/etcd/ssl/etcd.pem
key: /etc/etcd/ssl/etcd-key.pem
ca: /etc/etcd/ssl/etcd-root-ca.pem
endpoints:
- https://172.16.11.71:2379
- https://172.16.11.72:2379
- https://172.16.11.73:2379

dnsctl 提供如下命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
dnsctl for etcdhosts plugin

Usage:
dnsctl [flags]
dnsctl [command]

Available Commands:
config show example config
dump dump hosts
edit edit hosts
help Help about any command
upload upload hosts from file
version show hosts version

Flags:
--config string config file (default is $HOME/.dnsctl.yaml)
-h, --help help for dnsctl
-v, --version version for dnsctl

Use "dnsctl [command] --help" for more information about a command.

其中 edit 命令将会打开系统默认编辑器(例如 vim),然后编辑完保存后会自动上传到 Etcd 中,此后 CoreDNS 的 etcdhosts 插件将会立即重载;**dump 命令用于将 Etcd 中的 hosts 文件保存到本地用于备份,upload 命令可以将已有的 hosts 文件上传到 Etcd 用于恢复。**


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 国际许可协议进行许可,转载请注明出处。