目录

云原生打造企业内部DNS+ETCD+NTP+Quay高可用实战

依托Openshift+分布式技术,利用云原生方式打造企业内部公共类服务DNS+NTP+Quay镜像仓库高可用实战。

包括大量ETCD集群自定义证书部署实战

云原生打造企业内部DNS+ETCD+NTP+Quay高可用实战


[TOC]

1.前言

今天想针对DNS服务、NTP服务做容器化高可用部署教程。

为什么要做这个教程?

之前编写了Openshit4.5部署安装以及MySQL MGR在OCP平台部署的相关教程。

接下来针对内部企业镜像仓库Quay方案以及存储持久化Ceph方案做进一步教程。

事实上,无论OCP安装,还是Quay,再到Ceph的部署安装,类似DNS服务、NTP服务、YUM源等基本服务都是各项软件,特别是集群类软件必要的前提条件。

云原生是今后云架构的最佳形态,因此有必要在OCP上通过云原生的形态部署上述公关类服务,为企业打造内部高可用的公共类服务。

**这里存在一个难点:**如果DNS服务需要对企业其他容器化PaaS平台内的Pod提供统一管理的DNS服务,由于服务发现机制的需求,造成Pod存在搜索域的机制,因此CoreDns需要正确处理搜索域的解析。

2.整体架构

既然是作为企业内部关键业务公共类服务,如何提供高可靠、高可用的服务是必须考虑的问题。

设计采用Openshit作为公共类服务云计算操作平台,利用OCP再部署诸如ETCD分布式KV数据库等特性来提供诸如高可用和数据一致性的特点。

整体的设计架构如下

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20201221212226939.png

3.ETCD

先解决DNS的容器化改造。

DNS需要存储域名解析和反向解析等数据,需要存储相关数据信息,因此是有状态业务类型。

传统的BIND服务,相关数据库信息是保存在本地文件内,无法做到分布式存储。

相较而言,从云原生角度考虑问题。DNS服务可以采用CNCF毕业项目CoreDNS+ETCD搭配作为解决方案。

DNS记录都是诸如域名解析和反向解析等配置类数据记录,采用ETCD这种分布式KV数据库非常合适。

事实上K8S平台内部数据库就是采用ETCD。

3.1 获取二进制文件

开始在OCP下部署3节点的ETCD集群。

etcd是CoreOS团队于2013年6月发起的开源项目,它的目标是构建一个高可用的分布式键值(key-value)数据库。etcd内部采用raft协议作为一致性算法,etcd基于Go语言实现。

etcd作为服务发现系统,有以下的特点:

简单:安装配置简单,而且提供了HTTP API进行交互,使用也很简单

安全:支持SSL证书验证

快速:根据官方提供的benchmark数据,单实例支持每秒2k+读操作

可靠:采用raft算法,实现分布式系统数据的可用性和一致性

编制自定义镜像etcdDockerfile

先手动下载etcd二进制文件。

注意二进制文件和随后的Dockfile要放在一个目录下。

1
2
3
4
# 下载
curl -L https://github.com/etcd-io/etcd/releases/download/v3.4.14/etcd-v3.4.14-linux-amd64.tar.gz -o etcd-v3.4.14-linux-amd64.tar.gz
# 解压缩
tar xvf etcd-v3.4.14-linux-amd64.tar.gz

3.2 自定义镜像文件

参考https://etcd.io/docs/v3.4.0/op-guide/container/

注意:这里提前将准备好的TLS证书拷贝至cert目录下,方便后续命令参数的输入。

整个配置文件的目录结构如下。

 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
tree
.
├── cert
│   ├── ca-config.json
│   ├── ca-key.pem
│   ├── ca.pem
│   ├── etcdclient.csr
│   ├── etcdclient-csr.json
│   ├── etcdclient-key.pem
│   ├── etcdclient.pem
│   ├── etcdpeer.csr
│   ├── etcdpeer-csr.json
│   ├── etcdpeer-key.pem
│   ├── etcdpeer.pem
│   ├── etcdserver.csr
│   ├── etcdserver-csr.json
│   ├── etcdserver-key.pem
│   └── etcdserver.pem
├── etcd
├── etcdconfigmap.yaml
├── etcdctl
├── etcdDockfile
├── etcdnode1.yaml
├── etcdnode2.yaml
├── etcdnode3.yaml
└── etcdsvc.yaml

【编写Dockfile,采用alpine作为基础镜像】

注意:在此之前,请将相关证书拷贝至当前目录,其中ca-key.pem提前移除etcdDockfile文件当前目录。

 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
cat > etcdDockfile << EOF

FROM alpine:latest

# author ChengZhang
MAINTAINER ChengZhang

RUN apk update && \
    apk add --no-cache bash

# install etcd
COPY etcd /bin/ 
COPY etcdctl /bin/
# install tls cert
COPY cert/*.pem /bin

#RUN mkdir -p /var/etcd/ && mkdir -p /var/lib/etcd/
# Alpine Linux doesn't use pam, which means that there is no /etc/nsswitch.conf,
# but Golang relies on /etc/nsswitch.conf to check the order of DNS resolving
# (see https://github.com/golang/go/commit/9dee7771f561cf6aee081c0af6658cc81fac3918)
# To fix this we just create /etc/nsswitch.conf and add the following line:
#RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf

EXPOSE 2379 2380

# Define default command.
ENTRYPOINT ["/bin/etcd"]
EOF

注:这里关于/etc/nsswitch.conf文件的作用并不理解。

而且无论是否启用,对于集群功能并不影响。

Alpine 操作系统是一个面向安全的轻型 Linux 发行版。

它不同于通常 Linux 发行版,Alpine 采用了 musl libc 和 busybox 以减小系统的体积和运行时资源消耗,但功能上比 busybox 又完善的多,因此得到开源社区越来越多的青睐。

在保持瘦身的同时,Alpine 还提供了自己的包管理工具 apk,可以通过 https://pkgs.alpinelinux.org/packages 网站上查询包信息,也可以直接通过 apk 命令直接查询和安装各种软件。

例如查找chrony软件。

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20201216222736287.png

【编译镜像】

1
2
3
4
5
podman build -f etcdDockfile -t registry.cj.io:5000/etcd:0.0.1 .

# 镜像大小47.3MB
podman image ls | grep etcd  
registry.cj.io:5000/etcd  0.0.1  4d679e042020   About a minute ago   47.3 MB

【推送到企业内部镜像仓库】

1
podman push registry.cj.io:5000/etcd:0.0.1

3.3 网络拓扑持久化

ETCD各节点在POD内,如果要通过固定域名进行互相通讯,还是需要通过Headless ServiceStatefulSet配合完成。

【先创建一个名为common的新命名空间】

注意:这里common的命名空间直接影响到后续证书中SAN内容的作用范围。

1
oc new-project common

【创建Headless Service:etcdsvc.yaml 】

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
cat > etcdsvc.yaml <<EOF
kind: Service
apiVersion: v1
metadata:
  name: etcdsvc
  namespace: common
  labels:
    app: etcd
spec:
  selector:
    app: etcd
  type: ClusterIP
  clusterIP: None
EOF

【启动Headless Service】

1
oc create -f etcdsvc.yaml 

3.4 自定义TLS证书

网络通讯必然有安全要求,包括:

1、身份认证:保护客户端和服务器之间的访问身份,确保访问正确的服务器。

2、数据安全:确保通讯加密,一般采用非对称加密对对称加密的秘钥进行交换,然后用对称加密进行数据传输。

3、数据的完整性:传输内容不被篡改。

配置TLS证书进行节点与节点之间的加密通讯,客户端与节点服务器之间的加密通讯。

自定义TLS证书可利用OpenSSL或者cfssl工具进行编制。

TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。

关于OpenSSL文章可以参考我博客一篇文章数字证书那点事(附CP4D及VCenter7定制TLS证书部署案例参考)

官方推荐使用cfssl工具。

个人认为cfssl工具相较OpenSSL是因为json格式的请求文件比较直观,而且支持多profile字段,方便服务器、客户端及节点之间不同的自定义证书快速生成。

如果用openssl参考:

[req] req_extensions = v3_req distinguished_name = req_distinguished_name [req_distinguished_name] [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment extendedKeyUsage = clientAuth, serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = *.the-etcd-cluster.example-com.svc DNS.2 = *.the-etcd-cluster.example-com.svc.cluster.local

参考官方文档:

1、https://github.com/coreos/docs/blob/master/os/generate-self-signed-certificates.md

2、https://etcd.io/docs/v3.4.0/op-guide/security/

ETCD的TLS通讯涉及参数比较多,所以这里先提前说清楚关于ETCD的自定义TLS证书。

参考:https://github.com/etcd-io/etcd/blob/master/Documentation/op-guide/security.md

1、https://github.com/etcd-io/etcd/issues/8320 主要主题就是ETCD TLS still supports weak 64-bit block ciphers,这个后续有提及,处理内容就是加密套件禁用DES,启用AES。

2、认证通讯,参数怎么配置;加密通讯,参数怎么配置。

3.4.1 证书类型

在开始配置之前,先说明ETCD证书的分类,为了更好的说明,直接上英文原版。

Certificate types which are used inside Container Linux

  • client certificate is used to authenticate client by server. For example etcdctl, etcd proxy, or docker clients.
  • server certificate is used by server and verified by client for server identity. For example docker server or kube-apiserver.
  • peer certificate is used by etcd cluster members as they communicate with each other in both ways.

3.4.2 安装cfssl

下载地址:https://pkg.cfssl.org/

后续请求文件及证书文件均在cert文件目录下完成。

1
2
3
4
5
6
7
8
9
curl https://pkg.cfssl.org/R1.2/cfssl_linux-amd64  -o cfssl_linux-amd64
curl https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64  -o cfssljson_linux-amd64

# 注意这里拷贝后,二进制名称已改,后续直接用cfssl命令即可
cp cfssl_linux-amd64 /bin/cfssl
cp cfssljson_linux-amd64 /bin/cfssljson

# 赋予执行权限
chmod +x /bin/cfssl /bin/cfssljson

3.4.3 配置CA证书

参考:https://github.com/coreos/docs/blob/master/os/generate-self-signed-certificates.md

【配置CA Options】

这里采用了三个profiles,这里创建一个peer的profiles,配置了server authclient auth

438000h为10年。

这三个profiles可以自定义不同的过期时间(expiry),用途(usages)

server auth对应只用于服务器认证,client auth对应只用于客户端认证。

可以看出peer字段可用于etcd节点之间证书的生成,peer字段之间的TLS通讯包括了服务器和客户端双向认证。

 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
cat > ca-config.json << EOF
{
    "signing": {
        "default": {
            "expiry": "438000h"
        },
        "profiles": {
            "server": {
                "expiry": "438000h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth"
                ]
            },
            "client": {
                "expiry": "438000h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "client auth"
                ]
            },
            "peer": {
                "expiry": "438000h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            }
        }
    }
}
EOF

【配置CA证书的请求文件,名称为etcdca,CN字段其实并不重要】

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
cat > ca-csr.json << EOF
  {
    "CN": "etcdca",
    "hosts": [
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "shanghai",
            "L": "shanghai",
            "O": "etcd",
            "OU": "dev"
        }
    ],
    "ca": {
        "expiry": "438000h"
    }
   }
EOF

【生成CA证书】

1
2
3
4
5
6
7
8
cfssl gencert -initca ca-csr.json | cfssljson -bare ca

2020/12/24 08:13:24 [INFO] generating a new CA key and certificate from CSR
2020/12/24 08:13:24 [INFO] generate received request
2020/12/24 08:13:24 [INFO] received CSR
2020/12/24 08:13:24 [INFO] generating key: rsa-2048
2020/12/24 08:13:25 [INFO] encoded CSR
2020/12/24 08:13:25 [INFO] signed certificate with serial number 289965528201880294599527036642790401433300338912

这里会生成三个文件ca.csrca-key.pemca.pem

其中两个pem文件要完整保留,特别是这个ca-key.pem。一旦被窃取就可以生成其他服务器证书和客户端证书,务必妥善保管。

——————-至此CA证书配置完成——————-

3.4.4 配置服务器证书

【配置请求文件etcdserver-csr.json

格式上需要关注,因为是JSON格式,注意这里【"*.etcdsvc.common.svc.cluster.local"、“size”: 2048、“OU”: “dev”,包括倒数第二和第三行都没有逗号】

最重要的是这里的hosts字段

这里的hosts对应X509v3 Subject Alternative Name

意味着客户端在访问etcd集群过程中,如果返回的服务器域名地址与hosts(SAN)不匹配时候,将发生报错,具体可以看3.8.3试验章节内容。

注意这里的hosts字段,从etcd3.2.5开始,支持泛域名解析(wildcard DNS)。

从Chrome 58开始,只通过校验SAN属性验证证书的有效性。

SAN(Subject Alternative Name) 是 SSL 标准 x509 中定义的一个扩展。使用了 SAN 字段的 SSL 证书,可以扩展此证书支持的域名,使得一个证书可以支持多个不同域名的解析。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
cat > etcdserver-csr.json << EOF
{
  "CN": "etcdserver",
  "hosts": [
    "*.etcdsvc.common.svc.cluster.local"
  ],
  "key": {
    "algo": "rsa",
    "size": 4096
  },
  "names": [
    {
        "C": "CN",
        "L": "Shanghai",
        "ST": "Shanghai",
        "O": "cj",
        "OU": "dev"}
  ]
}
EOF

附:数字证书中主题(Subject)中字段的含义 一般的数字证书产品的主题通常含有如下字段: 公用名称 (Common Name) 简称:CN 字段,对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端证书则为证书申请者的姓名; 组织名称,公司名称(Organization Name) 简称:O 字段,对于 SSL 证书,一般为网站域名;而对于代码签名证书则为申请单位名称;而对于客户端单位证书则为证书申请者所在单位名称; 组织单位名称,公司部门(Organization Unit Name) 简称:OU字段

证书申请单位所在地 所在城市 (Locality) 简称:L 字段 所在省份 (State/Provice) 简称:S 字段,State:州,省 所在国家 (Country) 简称:C 字段,只能是国家字母缩写,如中国:CN

———-开始生成证书————

这里传入几个文件:

1、ca证书文件和ca公钥:ca.pemca-key.pem

2、请求及配置文件:ca-config.jsonetcdserver-csr.json

这里注意一个参数即:-profile=server

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=server etcdserver-csr.json |cfssljson -bare etcdserver

2020/12/24 21:34:16 [INFO] generate received request
2020/12/24 21:34:16 [INFO] received CSR
2020/12/24 21:34:16 [INFO] generating key: rsa-4096
2020/12/24 21:34:19 [INFO] encoded CSR
2020/12/24 21:34:19 [INFO] signed certificate with serial number 551953658513819464568048688662572564897970274106
2020/12/24 21:34:19 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").

其中有关Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates说明。具体可以查考https://cabforum.org

目前国际通行的全球服务器证书标准有以下几种:

  1、Trust Service Principles and Criteria for Certification Authorities Version 2.0

  简称Webtrust2.0,是一项对CA的基础性认证,是必须通过的标准。(由Webtrust发布)

  2、Baseline Requirements for the Issuance and Management of Publicly-Trusted Certificates

  简称BR,是全球服务器证书基准也是最新标准。(由CA/浏览器论坛发布)

  3、Guidelines For The Issuance And Management Of Extended Validation Certificates

  简称EV,是最高安全级别EV证书的规范(由CA/浏览器论坛发布)

  4、Guidelines For The Issuance And Management Of Extended Validation Code Signing Certificates

  简称EV CodeSign,是EV证书代码签名证书的特别规范(由CA/浏览器论坛发布)

  在以上标准审计通过后,才可获得Webtrust的相关认证,而上述标准中有3个是由CA/浏览器论坛(简称CAB)发布的,CAB是一个国际CA与浏览器的联合组织,目前国际一流的CA大都已加入。

注意这里·-bare etcdserver参数,就是生成的服务器TLS证书的文件前缀。这里-profile=server传入,对应多配置文件里的server profiles

最后生成etcdserver开头的四个文件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
tree cert
cert
├── ca-config.json
├── ca-key.pem
├── ca.pem
├── etcdclient.csr
├── etcdclient-csr.json
├── etcdclient-key.pem
├── etcdclient.pem
├── etcdpeer.csr
├── etcdpeer-csr.json
├── etcdpeer-key.pem
├── etcdpeer.pem
├── etcdserver.csr
├── etcdserver-csr.json
├── etcdserver-key.pem
└── etcdserver.pem

——————-至此服务器证书配置完成——————-

3.4.5 配置客户端证书

对于客户端证书而言,服务器端对这里的hosts字段即证书中SAN内容不进行校验。

从后面的cfssl gecert命令看出,传入的参数主要是CA证书和CA私钥,生成对应的客户端证书。

从安全而言,CA秘钥务必要妥善保存。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
cat > etcdclient-csr.json << EOF
{
  "CN": "client",
  "hosts": [
    "client"
  ],
  "key": {
    "algo": "rsa",
    "size": 4096
  },
  "names": [
    {
        "C": "CN",
        "L": "Shanghai",
        "ST": "Shanghai",
        "O": "cj",
        "OU": "dev"}
  ]
}
EOF

生成证书,区别前面服务器证书,这里的-profile=client-bare etcdclient

1
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=client etcdclient-csr.json |cfssljson -bare etcdclient

——————-至此客户端证书配置完成——————-

3.4.6 配置peer证书

前面文章中说明了ETCD服务器TLS证书以及客户端TKLS证书的配置。

服务器TLS证书是为了解决服务器身份验证。客户端TKLS证书是为了确保只有拥有正确的客户端证书和秘钥才能访问ETCD集群。

对于ETCD集群内部而言,相互之间及既存在服务器身份验证又存在客户端身份验证的两种场景。

因此对应了peer证书来满足上述场景,对应了CA请求文件ca-config.jsonpeer字段中 "server auth", "client auth"两者内容。

注意这里的hosts字段,从etcd3.2.5开始,支持泛域名解析(wildcard DNS)。

总的原则仍是前述证书一样,确保hosts字段即SAN内容与通讯之间的主机名能够匹配。

参考官方文档:https://etcd.io/docs/v3.4.0/op-guide/security/#example-3-transport-security–client-certificates-in-a-cluster

Since v3.2.5, server supports reverse-lookup on wildcard DNS SAN. For instance, if peer cert contains only DNS names (no IP addresses) in Subject Alternative Name (SAN) field, server first reverse-lookups the remote IP address to get a list of names mapping to that address (e.g. nslookup IPADDR). Then accepts the connection if those names have a matching name with peer cert’s DNS names (either by exact or wildcard match). If none is matched, server forward-lookups each DNS entry in peer cert (e.g. look up example.default.svc when the entry is *.example.default.svc), and accepts connection only when the host’s resolved addresses have the matching IP address with the peer’s remote IP address. For example, peer B’s CSR (with cfssl) is:

1
2
3
4
5
6
{
  "CN": "etcd peer",
  "hosts": [
    "*.example.default.svc",
    "*.example.default.svc.cluster.local"
  ],

when peer B’s remote IP address is 10.138.0.2. When peer B tries to join the cluster, peer A reverse-lookup the IP 10.138.0.2 to get the list of host names. And either exact or wildcard match the host names with peer B’s cert DNS names in Subject Alternative Name (SAN) field. If none of reverse/forward lookups worked, it returns an error "tls: "10.138.0.2" does not match any of DNSNames ["*.example.default.svc","*.example.default.svc.cluster.local"]. See issue#8268 for more detail.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
cat > etcdpeer-csr.json << EOF
{
  "CN": "etcdnode",
  "hosts": [
    "*.etcdsvc.common.svc.cluster.local"
  ],
  "key": {
    "algo": "rsa",
    "size": 4096
  },
  "names": [
    {
        "C": "CN",
        "L": "Shanghai",
        "ST": "Shanghai",
        "O": "cj",
        "OU": "dev"}
  ]
}
EOF

注意:这里仍旧采用了*.etcdsvc.common.svc.cluster.local泛域名写法。

生成证书,区别前面服务器证书,这里的-profile=peer,-bare etcdpeer

1
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=peer etcdpeer-csr.json |cfssljson -bare etcdpeer

——————-至此peer证书配置完成——————-

3.4.7 验证证书

总的生成了caserverclientpeer四类证书

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
tree
.
├── ca-config.json
├── ca-key.pem
├── ca.pem
├── etcdclient.csr
├── etcdclient-csr.json
├── etcdclient-key.pem
├── etcdclient.pem
├── etcdpeer.csr
├── etcdpeer-csr.json
├── etcdpeer-key.pem
├── etcdpeer.pem
├── etcdserver.csr
├── etcdserver-csr.json
├── etcdserver-key.pem
└── etcdserver.pem

通过openssl(需要另行安装)进行验证

 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
# 验证CA证书

openssl x509 -in ca.pem -text -noout
# 关键点这里CA:TRUE
X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:2
            X509v3 Subject Key Identifier: 
                51:E5:75:E5:D7:B1:FD:6D:BF:CC:4D:61:B9:07:1B:E4:9C:C8:C6:5E
            X509v3 Authority Key Identifier: 
                keyid:51:E5:75:E5:D7:B1:FD:6D:BF:CC:4D:61:B9:07:1B:E4:9C:C8:C6:5E
# 这里SAN为 DNS:*.etcdsvc.common.svc.cluster.local, IP Address:127.0.0.1`    


openssl x509 -in etcdserver.pem -text -noout
X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier: 
                17:1A:CF:A2:A7:09:0B:DB:CF:19:AF:C6:DE:83:9D:DF:09:DB:F7:80
            X509v3 Authority Key Identifier: 
                keyid:51:E5:75:E5:D7:B1:FD:6D:BF:CC:4D:61:B9:07:1B:E4:9C:C8:C6:5E
            # 泛域名
            X509v3 Subject Alternative Name: 
                DNS:*.etcdsvc.common.svc.cluster.local

3.4.8 几种证书的用途和参数结合说明

参考:https://etcd.io/docs/v3.4.0/op-guide/security/

因为server和client证书都是ca证书生成的,前两者和ca证书都是成对的,及都是成对的非对称加密对。

因此可以满足互相解密达到认证身份的功能,这是后续实现认证的关键。

一句话概况:--cacert配合CA根证书来验证服务器身份有效性,--cert --key配合客户端证书和秘钥来让服务器验证客户端身份有效性,确保被授权的客户端才能访问集群。

3.4.8.1 TLS 服务器端加密和认证

服务端启动通过--cert-file--key-file参数组合,一般指定服务器证书,来实现客户端与服务端的TLS认证及加密通讯,即确保客户端访问身份正确的服务器

一般客户端通过--cacert /path/to/ca.crt参数去实现与服务器的加密通讯

1
curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v

通常可以拷贝到 /etc/pki/tls/certs 或者 /etc/ssl/certs路径,然后刷新update-ca-trust是更新系统的CA证书。

注:curl 7.30.0 on OSX 10.9环境下,需要将ca.crt通过钥匙链手动导入系统中。

3.4.8.2 客户端加密和认证

--client-cert-auth开启客户端认证,确保只有授权客户端能访问服务器。

--trusted-ca-file=/path/to/ca.crt指定ca证书路径。

服务端启动通过上述两个参数组合,客户端需要指定--cert /path/to/client.crt --key /path/to/client.key两个参数,指定客户端证书。

3.4.8.3 解决SWEET32问题

服务端启动--cipher-suites TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384参数禁用DES算法。

Cipher Suite。根据 RFC 5246 附录 C 的定义,Cipher Suite 共分为三个部分,分别是秘钥交换 (Key Exchange)、加密演算法 (Cipher) 及讯息鉴别码 (MAC;Message Authentication Code)

客户端访问必须指定参数--ciphers

1
2
3
4
5
6
curl \
  --cacert ./ca.crt \
  --cert ./client.crt \
  --key ./client.key \
  -L [CLIENT-URL]/metrics \
  --ciphers ECDHE-RSA-AES128-GCM-SHA256

参考:https://github.com/etcd-io/etcd/issues/8320

3.4.8.4 集群内部认证和加密

通过--peer-client-cert-auth 开启集群内部TLS认证和加密

配合参数--peer-trusted-ca-file=/path/to/ca.crt --peer-cert-file=/path/to/member1.crt--peer-key-file=/path/to/member1.key

--peer-auto-tls--auto-tls开启集群内部自动TLS。后者是自签名TLS证书方式,通过这两个参数配合,适用于集群内部的自签名认证和加密,用户无需管理证书,而且对外不需要提供管理接口。

自签名方式有个问题。即通过curl等客户端访问时候,是会报curl: (60) SSL certificate problem: Invalid certificate chain错误。解决办法是通过-k参数即允许curl使用非安全的ssl连接并且传输数据。

3.4.9 其他注意事项

直接上英文原版说明。

  • Don’t put your ca-key.pem into a Container Linux Config, it is recommended to store it in safe place. This key allows to generate as much certificates as possible.
  • Keep key files in safe. Don’t forget to set proper file permissions, i.e. chmod 0600 server-key.pem.
  • Certificates in this TLDR example have both server auth and client auth X509 V3 extensions and you can use them with servers and clients' authentication.
  • You are free to generate keys and certificates for wildcard * address as well. They will work on any machine. It will simplify certificates routine but increase security risks.

3.5 各节点部署

这里开始编写ETCD各节点的StatefulSet的yaml文件。

这里的参数比较长,3.6章节做了有关参数的详细解释,特别是各yaml文件中--cert-file'参数开始均是关于TLS内容。

另外这里采用了OCP的sa,建立了ethan的这个特权账户。因此添加sa。

使用root用户启动赋予了容器过高的权限,存在很大的安全问题,如以static pod的方式部署etcd有待研究。

1
2
3
4
5
oc create sa ethan
serviceaccount/ethan created

oc adm policy add-scc-to-user anyuid -z ethan
clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "ethan"

编制etcdnode1的StatefulSet的yaml文件

注:这里--listen-peer-urls--listen-client-urls两个参数必须配置成ip形式,否则容器中etcd命令会报错

注:2379端口提供HTTP API服务,2380端口和peer通信。

但是如果配置成0.0.0.0的话,每个节点周期性报错2020-12-25 13:58:41.225158 I | embed: rejected connection from "127.0.0.1:55108" (error "tls: failed to verify client's certificate: x509: certificate specifies an incompatible key usage", ServerName "") WARNING: 2020/12/25 13:58:41 grpc: addrConn.createTransport failed to connect to {0.0.0.0:2379 <nil> 0 <nil>}. Err :connection error: desc = "transport: authentication handshake failed: remote error: tls: bad certificate". Reconnecting...。但不影响集群功能。【这点有待解决,懂的高人请赐教,可发邮件21802259@qq.com】。

 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
cat > etcdnode1.yaml <<EOF

apiVersion: apps/v1
#kind: Deployment
kind: StatefulSet
metadata:
  name: etcdnode1
  namespace: common
spec:
  serviceName: etcdsvc
  replicas: 1
  selector:
    matchLabels:
      app: etcd
  template:
    metadata:
      labels:
        app: etcd
    spec:
      serviceAccount: ethan
      serviceAccountName: ethan
      volumes:
      - name: etcdstorage
        persistentVolumeClaim:
          claimName: pvc-etcd
      containers:
        - name: etcd
          volumeMounts:
          - name: etcdstorage
            mountPath: "/etcd-data"
          image: 'registry.cj.io:5000/etcd:0.0.1'
          imagePullPolicy: Always
          command: ["/bin/etcd"]
          args: 
            - '--data-dir'
            - '/etcd-data/etcdnode1'
            - '--name'
            - 'node1'
            - '--initial-advertise-peer-urls'
            - 'https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2380'
            - '--listen-peer-urls'
            - 'https://0.0.0.0:2380'
            - '--listen-client-urls'
            - 'https://0.0.0.0:2379'
            - '--advertise-client-urls'
            - 'https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379'
            - '--initial-cluster-token'
            - 'etcd-cluster-coredns'
            - '--initial-cluster'
            - 'node1=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2380,node2=https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2380,node3=https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2380'          
            - '--initial-cluster-state'
            - 'new'
            - '--cert-file'
            - '/bin/etcdserver.pem'
            - '--key-file'
            - '/bin/etcdserver-key.pem'
            - '--client-cert-auth'
            - '--trusted-ca-file'
            - '/bin/ca.pem'
            - '--peer-cert-file'
            - '/bin/etcdpeer.pem'
            - '--peer-key-file'
            - '/bin/etcdpeer-key.pem'
            - '--peer-client-cert-auth'
            - '--peer-trusted-ca-file'
            - '/bin/ca.pem'
            - '--cipher-suites'
            - 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
EOF

注意最后的加密套件字段参数。

这里存储持久化采用相对简单的NFS方案。后续会有ceph统一存储方案教程。

nfs服务端相关设置

1
2
3
4
5
mkdir -p /data/nfs/etcd/
echo /data/nfs/etcd/ *'(rw,sync,no_wdelay,root_squash,insecure)' > /etc/exports.d/etcd.exports
chown -R nfsnobody.nfsnobody /data/nfs/etcd/
chmod 777 -R /data/nfs/etcd/
exportfs -rav

编写pv

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
cat >etcdpv.yaml<<EOF
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-etcd
spec:
  capacity:
    storage: 100Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /data/nfs/etcd/
    server: nfs.cj.io
    readOnly: false
EOF    

编写pvc

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
cat >etcdpvc.yaml<<EOF
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-etcd
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 100Gi
EOF    

分别创建pv和pvc

1
2
3
4
oc create	-f etcdpv.yaml
oc get pv
oc create -f etcdpvc.yaml
oc get pvc

3.6 相关参数解释

--data-dir 指定节点的数据存储目录,这些数据包括节点ID,集群ID,集群初始化配置,Snapshot文件,若未指定--wal-dir,还会存储WAL文件;

--wal-dir 指定节点的wal文件的存储目录,若指定了该参数,wal文件会和其他数据文件分开存储。

--name 节点名称

--initial-advertise-peer-urls 用于其他member使用,其他member通过该地址与本member交互信息。一定要保证从其他member能可访问该地址。静态配置方式下,该参数的value一定要同时在–initial-cluster参数中存在。

memberID的生成受--initial-cluster-token--initial-advertise-peer-urls影响。

-- listen-peer-urls 监听URL,用于与其他节点通讯,本member侧使用,用于监听其他member发送信息的地址。ip为全0代表监听本member侧所有接口。

-- advertise-client-urls etcd客户使用,客户通过该地址与本member交互信息。一定要保证从客户侧能可访问该地址,告知客户端url, 也就是服务的url。

--initial-cluster-token 集群的ID,当使用相同配置文件再启动一个集群时,只要该 token 值不一样,etcd 集群就不会相互影响。

-- initial-cluster集群中所有节点列表。

-data-dir指定节点的数据存储目录,这些数据包括节点ID,集群ID,集群初始化配置,Snapshot文件,若未指定-wal-dir,还会存储WAL文件;如果不指定会用缺省目录,缺省目录为/default.etcd里面包括了member文件夹,此文件夹包括snap和wal目。

--cert-file=<path> 用于到 etcd d的 SSL / TLS连接的证书。当设置此选项时,advertise-client-urls 可以使用 HTTPS 模式。

--key-file=<path>证书的秘钥, 必须是不加密的.

--client-cert-auth当这个选项被设置时,etcd 将为受信任CA签名的客户端证书检查所有的传入的 HTTPS 请求,不能提供有效客户端证书的请求将会失败。

--trusted-ca-file=<path> 受信任的认证机构

--auto-tls为客户端的 TLS 连接,使用自动生成的自签名证书 --peer-cert-file=<path> 用于对等体之间的 SSL / TLS 连接的证书。这将用于在对等地址上监听以及向其他对等体发送请求

--peer-key-file=<path>证书的秘钥, 必须是不加密的.

--peer-client-cert-auth 当这个选项被设置时,etcd 将为受信任CA签名的客户端证书检查所有的传入的对等请求。

--peer-trusted-ca-file=<path> 受信任的认证机构.

--peer-auto-tls 为对等体之间的 TLS 连接使用自动生成的自签名证书

【启动etcdnode1】

1
oc create -f etcdnode1.yaml 

oc logs etcdnode1-0返回相关信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
[WARNING] Deprecated '--logger=capnslog' flag is set; use '--logger=zap' flag instead
2020-12-25 15:17:57.857455 I | etcdmain: etcd Version: 3.4.14
2020-12-25 15:17:57.857491 I | etcdmain: Git SHA: 8a03d2e96
2020-12-25 15:17:57.857497 I | etcdmain: Go Version: go1.12.17
2020-12-25 15:17:57.857504 I | etcdmain: Go OS/Arch: linux/amd64
2020-12-25 15:17:57.857510 I | etcdmain: setting maximum number of CPUs to 8, total number of available CPUs is 8
[WARNING] Deprecated '--logger=capnslog' flag is set; use '--logger=zap' flag instead
2020-12-25 15:17:57.857565 I | embed: peerTLS: cert = /bin/etcdpeer.pem, key = /bin/etcdpeer-key.pem, trusted-ca = /bin/ca.pem, client-cert-auth = true, crl-file = 
2020-12-25 15:17:57.858496 I | embed: name = node1
2020-12-25 15:17:57.858510 I | embed: data dir = /etcd-date
2020-12-25 15:17:57.858517 I | embed: member dir = /etcd-date/member
2020-12-25 15:17:57.858523 I | embed: heartbeat = 100ms
2020-12-25 15:17:57.858530 I | embed: election = 1000ms
2020-12-25 15:17:57.858535 I | embed: snapshot count = 100000
2020-12-25 15:17:57.858544 I | embed: advertise client URLs = https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379

同样编制node2和node3相关yam文件。

主要是name以及args参数字段的内容有所区别。当然也可以将node2和node3合并在一个yaml文件内,通过InitContainer内脚本进行初始化。

【node2节点配置】

 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
cat > etcdnode2.yaml <<EOF

apiVersion: apps/v1
#kind: Deployment
kind: StatefulSet
metadata:
  name: etcdnode2
  namespace: common
spec:
  serviceName: etcdsvc
  replicas: 1
  selector:
    matchLabels:
      app: etcd
  template:
    metadata:
      labels:
        app: etcd
    spec:
      serviceAccount: ethan
      serviceAccountName: ethan
      volumes:
      - name: etcdstorage
        persistentVolumeClaim:
          claimName: pvc-etcd
      containers:
        - name: etcd
          volumeMounts:
          - name: etcdstorage
            mountPath: "/etcd-data"
          image: 'registry.cj.io:5000/etcd:0.0.1'
          imagePullPolicy: Always
          command: ["/bin/etcd"]
          args: 
            - '--data-dir'
            - '/etcd-data/etcdnode2'
            - '--name'
            - 'node2'
            - '--initial-advertise-peer-urls'
            - 'https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2380'
            - '--listen-peer-urls'
            - 'https://0.0.0.0:2380'
            - '--listen-client-urls'
            - 'https://0.0.0.0:2379'
            - '--advertise-client-urls'
            - 'https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379'
            - '--initial-cluster-token'
            - 'etcd-cluster-coredns'
            - '--initial-cluster'
            - 'node1=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2380,node2=https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2380,node3=https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2380'          
            - '--initial-cluster-state'
            - 'existing'
            - '--cert-file'
            - '/bin/etcdserver.pem'
            - '--key-file'
            - '/bin/etcdserver-key.pem'
            - '--client-cert-auth'
            - '--trusted-ca-file'
            - '/bin/ca.pem'
            - '--peer-cert-file'
            - '/bin/etcdpeer.pem'
            - '--peer-key-file'
            - '/bin/etcdpeer-key.pem'
            - '--peer-client-cert-auth'
            - '--peer-trusted-ca-file'
            - '/bin/ca.pem'
            - '--cipher-suites'
            - 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
EOF

【node3节点配置】

 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
cat > etcdnode3.yaml <<EOF

apiVersion: apps/v1
#kind: Deployment
kind: StatefulSet
metadata:
  name: etcdnode3
  namespace: common
spec:
  serviceName: etcdsvc
  replicas: 1
  selector:
    matchLabels:
      app: etcd
  template:
    metadata:
      labels:
        app: etcd
    spec:
      serviceAccount: ethan
      serviceAccountName: ethan
      volumes:
      - name: etcdstorage
        persistentVolumeClaim:
          claimName: pvc-etcd
      containers:
        - name: etcd
          volumeMounts:
          - name: etcdstorage
            mountPath: "/etcd-data"
          image: 'registry.cj.io:5000/etcd:0.0.1'
          imagePullPolicy: Always
          command: ["/bin/etcd"]
          args: 
            - '--data-dir'
            - '/etcd-data/etcdnode3'
            - '--name'
            - 'node3'
            - '--initial-advertise-peer-urls'
            - 'https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2380'
            - '--listen-peer-urls'
            - "https://0.0.0.0:2380"
            - '--listen-client-urls'
            - 'https://0.0.0.0:2379'
            - '--advertise-client-urls'
            - 'https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379'
            - '--initial-cluster-token'
            - 'etcd-cluster-coredns'
            - '--initial-cluster'
            - 'node1=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2380,node2=https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2380,node3=https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2380'          
            - '--initial-cluster-state'
            - 'new'
            - '--cert-file'
            - '/bin/etcdserver.pem'
            - '--key-file'
            - '/bin/etcdserver-key.pem'
            - '--client-cert-auth'
            - '--trusted-ca-file'
            - '/bin/ca.pem'
            - '--peer-cert-file'
            - '/bin/etcdpeer.pem'
            - '--peer-key-file'
            - '/bin/etcdpeer-key.pem'
            - '--peer-client-cert-auth'
            - '--peer-trusted-ca-file'
            - '/bin/ca.pem'
            - '--cipher-suites'
            - 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
EOF

【继续启动etcdnode2和etcdnode3】

1
oc create -f etcdnode2.yaml  -f etcdnode3.yaml 

配置证书后,如果发生以下典型错误提示。

1
2
3
2020-12-25 13:32:06.744224 I | embed: rejected connection from "10.131.1.157:47848" (error "remote error: tls: bad certificate", ServerName "etcdnode3-0.etcdsvc.common.svc.cluster.local")

2020-12-25 13:32:06.750887 I | embed: rejected connection from "10.128.3.81:45194" (error "remote error: tls: bad certificate", ServerName "etcdnode3-0.etcdsvc.common.svc.cluster.local")

解决办法:检查peer证书中hosts字段。

3.7 高可用设计和测试

3.7.1 功能介绍

ETCD最多7个节点,一般而言,5个节点是最佳设计,允许2个节点的容错。

高可用场景应付的故障场景包括

1、单节点故障,但是数据保留,需要将节点重新加入集群

2、单节点故障,但是数据丢失,需要从现有集群中进行恢复并加入集群

3、集群出现灾难,但是数据可恢复,需要恢复集群。

这里我们从一个正常已部署好的ETCD集群开始

安装完成后

【节点状态】

1
etcdctl --write-out=table --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 endpoint status
1
2
3
4
5
6
7
+------+----+-------+-----+-------+-------+-----------+------------+-----------+--------+
|ENDPOINT| ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+------+----+-------+-----+-------+-------+-----------+------------+-----------+--------+
| https://e:2379 | 5c8126421593b906 | 3.4.14 | 20 kB |  true |  false |  114 | 9 |   9 |   |
| https://e:2379 | cf326bb6e04b728f | 3.4.14 | 20 kB | false |  false |  114 | 9 |   9 |   |
| https://e:2379 | da5d0211556da0f7 | 3.4.14 | 20 kB | false |  false |  114 | 9 |   9 |   |
+------+----+-------+-----+-------+-------+-----------+------------+-----------+--------+

【准备测试数据】

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 准备测试数据
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 put Hi "张诚"
OK

# 验证
# 获取数据,节点1
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 get Hi
Hi
张诚

# 获取数据,节点2
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 get Hi
Hi
张诚

# 获取数据,节点3
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 get Hi
Hi
张诚

3.7.2 单节点故障(数据不丢失)

模拟手动处理主节点故障,因为节点挂载在远端NFS服务器上的,所以主节点的数据未丢失。

这是本次模拟故障场景。

1
oc delete -f etcdnode1.yaml

查看集群状态【node1节点故障】

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
etcdctl --write-out=table --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 endpoint health
{"level":"warn","ts":"2020-12-28T04:53:29.110Z","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"endpoint://client-e6c0d9ff-2c9c-4ba9-a3e0-41b6b175b31a/etcdnode1-0.etcdsvc.common.svc.cluster.local:2379","attempt":0,"error":"rpc error: code = DeadlineExceeded desc = latest balancer error: all SubConns are in TransientFailure, latest connection error: connection error: desc = \"transport: Error while dialing dial tcp: lookup etcdnode1-0.etcdsvc.common.svc.cluster.local on 172.30.0.10:53: no such host\""}
+-----------------------------------------------------------+--------+--------------+---------------------------+
|                         ENDPOINT                          | HEALTH |     TOOK     |           ERROR           |
+-----------------------------------------------------------+--------+--------------+---------------------------+
| https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 |   true |  46.588745ms |                           |
| https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 |   true |   56.82892ms |                           |
| https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 |  false | 5.000640719s | context deadline exceeded |

Error: unhealthy cluster

最后一行提示Error: unhealthy cluster

节点2已经是主节点。

1
2
3
4
5
6
7
8
9
etcdctl --write-out=table --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 endpoint status

{"level":"warn","ts":"2020-12-28T04:56:35.737Z","caller":"clientv3/retry_interceptor.go:62","msg":"retrying of unary invoker failed","target":"passthrough:///https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379","attempt":0,"error":"rpc error: code = DeadlineExceeded desc = latest balancer error: connection error: desc = \"transport: Error while dialing dial tcp: lookup etcdnode1-0.etcdsvc.common.svc.cluster.local on 172.30.0.10:53: no such host\""}
Failed to get the status of endpoint https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 (context deadline exceeded)
+-----------------------------------------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|                         ENDPOINT                          |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------------------------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 | cf326bb6e04b728f |  3.4.14 |   20 kB |      true |      false |       114 |         13 |                 13 |        |
| https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 | da5d0211556da0f7 |  3.4.14 |   20 kB |     false |      false |       114 |         13 |                 13 |        |

由于三节点,最多容错1台,目前集群还是可以正常工作,只不过已经没有容错。

【继续向节点2推送数据】

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put Hi2 "徐志刚"
OK
#  验证节点2
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 get Hi2
Hi2
徐志刚
# 验证节点3
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 get Hi2
Hi2
徐志刚

模拟节点1恢复,本地数据不丢失情况下

1
oc create -f etcdnode1.yaml

验证数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 这是节点1故障前集群数据,已恢复
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 get Hi
Hi
张诚
# 这是节点1故障后集群数据,已恢复
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 get Hi2
Hi2
徐志刚
# 节点恢复后,集群工作情况,继续想节点1推送数据
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 put Hi3 "迪丽热巴"
# 验证
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 get Hi3
Hi3
迪丽热巴

etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 get Hi3
Hi3
迪丽热巴

在不超过(节点数/2) -1的情况下,容器化部署的ETCD集群几乎不用人工干预,具备自愈功能。

3.7.3 单节点故障(数据丢失)

参考:https://etcd.io/docs/v3.4.0/op-guide/runtime-configuration/

目前主节点是节点2,模拟节点2故障。

并且登录nfs服务器,将节点2的存储数据全部删除,模拟节点2数据丢失。

1
2
oc delete -f etcdnode2.yaml
rm -rf etcdnode2

验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
oc rsh centos7-57546cfd99-j9zfl

etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 get Hi3
# node2无法连接

etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 member list
# 返回
5c8126421593b906, started, node1, https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2380, https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379, false
cf326bb6e04b728f, started, node2, https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2380, https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379, false
da5d0211556da0f7, started, node3, https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2380, https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379, false

查看node2的pod状态

1
2
3
4
oc get pod | grep etcd
etcdnode1-0                1/1     Running            0          4h2m
etcdnode2-0                0/1     CrashLoopBackOff   3          74s
etcdnode3-0                1/1     Running            0          5h48m

节点2在启动时候就报错了。

【实际上,这种故障场景等于是出现了不健康的节点了】

对于不健康的故障场景,重点是首先必须先从集群中删除故障节点

参考:https://etcd.io/docs/v3.4.0/faq/

首先ETCD采用的是基于法定人数的仲裁模型(quorum model),只有多数人(超过半数)同意后才能提交集群,

如果不先删除故障节点,例如三个节点的集群,有一台故障,集群还能继续往前,但是存在脑裂风险(split brain inconsistency)。

如果这时候不删除原有故障节点,再增加一个节点,这时候的成员为4,而法定人数(大多数票)增加到了3,而此时健康成员的总数为3,由于法定人数增加,因此该额外成员在容错方面一无所获;群集仍然是一个无法恢复的节点故障。

此外,该新成员具有风险,因为它可能配置错误或无法加入群集。在这种情况下,无法恢复仲裁,因为集群的成员减少了两个,成员增加了两个,但是需要三票才能更改成员身份才能撤消增加的成员资格。默认情况下,etcd将拒绝成员添加尝试,从而可能以此方式关闭集群。

另一方面,如果首先从集群成员资格中删除被拒绝的成员,则成员数变为2,并且法定人数保持为2。此后,通过添加新成员进行删除也将使法定人数稳定在2。因此,即使无法启动新节点,仍然可以通过仲裁删除其余活动成员上的新成员。

这里的删除的参数为节点id,节点id可以通过member list查看

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
oc rsh centos7-57546cfd99-j9zfl

etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 member remove cf326bb6e04b728f
# 返回
Member cf326bb6e04b728f removed from cluster 49246e9575907abc
# 验证,查看成员
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 member list
# 返回成员信息
5c8126421593b906, started, node1, https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2380, https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379, false
da5d0211556da0f7, started, node3, https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2380, https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379, false

然后是增加节点成员,

重要:增加节点成员,这里分为两个阶段

阶段1-通知集群新配置

要将成员添加到etcd集群中,请进行API调用以请求将新成员添加到集群中。这是将新成员添加到现有集群中的唯一方法。当集群同意配置更改时,API调用将返回。

【注意这里--peer-urls后面必须用双引号,3.4.0有bug?】

1
2
3
4
5
6
7
8
9
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 member add node2 --peer-urls="https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2380"

# 相关信息返回
Member d05568ec2487dee4 added to cluster 49246e9575907abc

ETCD_NAME="node2"
ETCD_INITIAL_CLUSTER="node1=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2380,node2=https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2380,node3=https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2380"
ETCD_INITIAL_CLUSTER_STATE="existing"

阶段2-开始新成员

要将新的etcd成员加入现有集群,请指定正确的initial-cluster并将其设置initial-cluster-stateexisting。成员启动时,它将首先与现有群集联系,并验证当前群集配置是否与中指定的预期配置匹配initial-cluster。当新成员成功启动时,群集已达到预期的配置。

编辑原etcdnode2.yaml文件,其中initial-cluster-stateexisting.

重新启动node2

1
oc create -f etcdnode2.yaml

验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 成员列表状态
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 member list
5c8126421593b906, started, node1, https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2380, https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379, false
d05568ec2487dee4, started, node2, https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2380, https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379, false
da5d0211556da0f7, started, node3, https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2380, https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379, false


# 故障数据均已全部恢复
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 get Hi3
Hi3
迪丽热巴

【重要:数据恢复后】

编辑原etcdnode2.yaml文件,其中initial-cluster-state再改为new.

1
oc apply -f etcdnode2.yaml

【注意3.4版本开始有新的更加安全设计】

从v3.4开始,etcd支持将新成员添加为学习者/非投票成员。动机和设计可以在设计文档中找到 。为了使添加新成员的过程更加安全,并减少添加新成员时群集的停机时间,建议将新成员作为学习者添加到群集中,直到赶上为止。这可以描述为三步过程:

1、etcdctl member add --learner命令将新成员添加为学习者

2、使用新的群集配置启动新成员,包括已更新成员的列表(现有成员+新成员)。此步骤与之前完全相同。

3、etcdctl member promote命令将新添加的学习者提升为有投票权的成员。

在v3.4中,etcd服务器将群集可以拥有的学习者数量限制为一个。主要考虑因素是限制由于领导者向学习者传播数据而导致的领导者额外工作量。

利用etcdctl member add与标志--learner一起使用,以将新成员添加为群集作为学习者。

3.7.4 灾难恢复

一个etcd群集自动从临时故障(例如,计算机重新启动)中恢复,并且对于N个成员的群集最多可承受(N-1)/ 2个永久性故障。如果成员由于硬件故障或磁盘损坏而永久失败,则它将失去对群集的访问权限。如果群集永久丢失的成员数超过(N-1)/ 2个,则灾难性地失败,将不可避免地丢失仲裁。一旦仲裁丢失,群集将无法达成共识,因此无法继续接受更新。

为了从灾难性故障中恢复,etcd v3提供了快照和还原功能来重新创建群集,而不会丢失v3关键数据。

参考:https://etcd.io/docs/v3.4.0/op-guide/recovery/

实际上,容器化部署ETCD,存储往往采用分布式存储进行持久化

经过实践,即使计算节点故障导致ETCD整个集群出现灾难,只要ETCD的数据目录不丢失,将集群各节点全部重启后,集群即可自愈。

当数据发生丢失时,集群的回复也是按照上述逻辑进行。

当数据丢失造成集群宕机,集群恢复的大致步骤:

周期性地备份快照数据——–建立一个临时的空数据的集群———-利用备份快照数据——–重启集群

备份

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 snapshot save snapshot.db

# 返回
{"level":"info","ts":1609165250.4595175,"caller":"snapshot/v3_snapshot.go:119","msg":"created temporary db file","path":"snapshot.db.part"}
{"level":"info","ts":"2020-12-28T14:20:50.503Z","caller":"clientv3/maintenance.go:200","msg":"opened snapshot stream; downloading"}
{"level":"info","ts":1609165250.5038223,"caller":"snapshot/v3_snapshot.go:127","msg":"fetching snapshot","endpoint":"etcdnode2-0.etcdsvc.common.svc.cluster.local:2379"}
{"level":"info","ts":"2020-12-28T14:20:50.506Z","caller":"clientv3/maintenance.go:208","msg":"completed snapshot read; closing"}
{"level":"info","ts":1609165250.5088973,"caller":"snapshot/v3_snapshot.go:142","msg":"fetched snapshot","endpoint":"etcdnode2-0.etcdsvc.common.svc.cluster.local:2379","size":"20 kB","took":0.04929757}
{"level":"info","ts":1609165250.5089912,"caller":"snapshot/v3_snapshot.go:152","msg":"saved","path":"snapshot.db"}
Snapshot saved at snapshot.db

恢复数据

1
2
3
4
5
etcdctl snapshot restore snapshot.db \
  --name node1 \
  --initial-cluster m1=http://host1:2380,m2=http://host2:2380,m3=http://host3:2380 \
  --initial-cluster-token etcd-cluster-1 \
  --initial-advertise-peer-urls http://host1:2380

关闭集群,然后再重新打开,集群即可恢复。

写到最后,上图(TL;DR)

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20201229083120214.png

3.8 验证测试

我们部署一个centos7的pod,在centos下安装etcdctl二进制文件,并将ca证书、客户端证书、客户端秘钥拷贝至该测试用例pod的/root下。

【验证测试环境准备】

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
cd /root/etcdDockfile/cert

# 获取centos7的pod名称
oc get pod | grep centos7
centos7-57546cfd99-j9zfl   1/1     Running   0          3d13h

# 拷贝三个证书文件至远端/root目录下
oc cp ca.pem centos7-57546cfd99-j9zfl:/root
oc cp etcdclient.pem centos7-57546cfd99-j9zfl:/root
oc cp etcdclient-key.pem centos7-57546cfd99-j9zfl:/root

# 进入容器
oc rsh centos7-57546cfd99-j9zfl
cd /root
# 下载
curl -L https://github.com/etcd-io/etcd/releases/download/v3.4.14/etcd-v3.4.14-linux-amd64.tar.gz -o etcd-v3.4.14-linux-amd64.tar.gz
# 解压缩
tar xvf etcd-v3.4.14-linux-amd64.tar.gz

cp etcd-v3.4.14-linux-amd64/etcdctl /usr/local/sbin/

ceentos7的yaml文件如下

 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
cat > centos7.ymal <<EOF
apiVersion: apps/v1
#kind: StatefulSet
kind: Deployment
metadata:
  name: centos7
  #namespace: mysql-mgr
  #namespace: mysql-mgr
spec:
  #serviceName: mysqlmgr-svc
  replicas: 1
  selector:
    matchLabels:
      app: centos7
  template:
    metadata:
      labels:
        app: centos7
    spec:
      serviceAccount: ethan
      serviceAccountName: ethan
      containers:
        - name: mysqlshell
          image: 'registry.cj.io:5000/centos:latest'
          command:
          - /usr/sbin/init
EOF        

3.8.1 查看集群状态

注意,当时写StatefulSet的yaml文件中serviceName字段忘记注明,导致外部pod无法通过etcdnode1-0.etcdsvc.common.svc.cluster.local进行访问。

以下均为正确安装客户端证书才能正常访问

【节点状态】

1
etcdctl --write-out=table --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 endpoint status
1
2
3
4
5
6
7
+------+----+-------+-----+-------+-------+-----------+------------+-----------+--------+
|ENDPOINT| ID | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+------+----+-------+-----+-------+-------+-----------+------------+-----------+--------+
| https://e:2379 | 5c8126421593b906 | 3.4.14 | 20 kB |  true |  false |  114 | 9 |   9 |   |
| https://e:2379 | cf326bb6e04b728f | 3.4.14 | 20 kB | false |  false |  114 | 9 |   9 |   |
| https://e:2379 | da5d0211556da0f7 | 3.4.14 | 20 kB | false |  false |  114 | 9 |   9 |   |
+------+----+-------+-----+-------+-------+-----------+------------+-----------+--------+

【节点健康状况】

1
etcdctl --write-out=table --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 endpoint health

【查看集群成员】

1
etcdctl --write-out=table --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379  member list

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20201224140313906.png

【删除集群成员】

1
etcdctl --write-out=table --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379,https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 member remove ${MEMBER_ID}

3.8.2 验证数据

推送数据

1
2
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 put Hi "张诚"
OK

验证数据【访问任一pod节点】

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 获取数据,节点1
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 get Hi
Hi
张诚

# 获取数据,节点2
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 get Hi
Hi
张诚

# 获取数据,节点3
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode3-0.etcdsvc.common.svc.cluster.local:2379 get Hi
Hi
张诚


# json格式输出
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 --write-out="json" get Hi
{"header":{"cluster_id":5270459052131908284,"member_id":14930114147877876367,"revision":2,"raft_term":4},"kvs":[{"key":"SGk=","create_revision":2,"mod_revision":2,"version":1,"value":"5byg6K+a"}],"count":1}

删除数据

1
2
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 del Hi
1

3.8.3 重要:验证证书的有效性

这里单独拉起一个centos的pod,在里面安装etcd,将相关证书拷贝至该pod内etcd二进制文件同目录下。

一句话概况:--cacert配合CA根证书来验证服务器身份有效性,--cert --key配合客户端证书和秘钥来让服务器验证客户端身份有效性,确保被授权的客户端才能访问集群。

【测试试验1】

我们可以将服务器证书请求文件中的host字段填写错误,不匹配,例如etcdnode1-0.etcdsvc.common.svc.cluster.local2,注意后面多了个2

执行curl命令测试。

注意这里TLS handshake, Finished (20),后期会单独写一篇文章详细分析TLS1.2的握手过程。

 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
curl --cacert ca.pem https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379/v2/keys/Hi --cert etcdclient.pem --key etcdclient-key.pem -XPUT -d value=bar -v --ciphers ECDHE-RSA-AES128-GCM-SHA256

*   Trying 10.128.3.86...
* TCP_NODELAY set
* Connected to etcdnode1-0.etcdsvc.common.svc.cluster.local (10.128.3.86) port 2379 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ECDHE-RSA-AES128-GCM-SHA256
* successfully set certificate verify locations:
*   CAfile: ca.pem
  CApath: none
 # 尝试使用TLS1.3版本
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
# 确认使用TLS1.2版本
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
# 返回服务器证书,这里SAN实际为etcdnode1-0.etcdsvc.common.svc.cluster.local2`,与服务器的主机名`etcdnode1-0.etcdsvc.common.svc.cluster.local`不匹配。
* Server certificate:
*  subject: C=CN; ST=Shanghai; L=Shanghai; O=cj; OU=dev; CN=etcdserver
*  start date: Dec 25 14:46:00 2020 GMT
*  expire date: Mar 23 14:46:00 2050 GMT
*  subjectAltName does not match etcdnode1-0.etcdsvc.common.svc.cluster.local
* SSL: no alternative certificate subject name matches target host name 'etcdnode1-0.etcdsvc.common.svc.cluster.local'
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, close notify (256):
curl: (51) SSL: no alternative certificate subject name matches target host name 'etcdnode1-0.etcdsvc.common.svc.cluster.local'

【注意这里TLSv1.2 (IN), TLS handshake, Finished (20):参数,后续将发布详细的TLS 握手的教程】

【测试试验2:增加了-k参数,及采用不验证服务器证书的有效性,继续采用非TLS方式访问】

 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
curl -k --cacert ca.pem https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379/v2/keys/foo --cert etcdclient.pem --key etcdclient-key.pem -XPUT -d value=bar -v --ciphers ECDHE-RSA-AES128-GCM-SHA256
*   Trying 10.128.3.86...
* TCP_NODELAY set
* Connected to etcdnode1-0.etcdsvc.common.svc.cluster.local (10.128.3.86) port 2379 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ECDHE-RSA-AES128-GCM-SHA256
* successfully set certificate verify locations:
*   CAfile: ca.pem
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
*  subject: C=CN; ST=Shanghai; L=Shanghai; O=cj; OU=dev; CN=etcdserver
*  start date: Dec 25 14:46:00 2020 GMT
*  expire date: Mar 23 14:46:00 2050 GMT
*  issuer: C=CN; ST=shanghai; L=shanghai; O=etcd; OU=dev; CN=etcd
*  SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55a05b8d1450)
> PUT /v2/keys/foo HTTP/2
> Host: etcdnode1-0.etcdsvc.common.svc.cluster.local:2379
> User-Agent: curl/7.61.1
> Accept: */*
> Content-Length: 9
> Content-Type: application/x-www-form-urlencoded
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 250)!
* We are completely uploaded and fine
< HTTP/2 404 
< access-control-allow-headers: accept, content-type, authorization
< access-control-allow-methods: POST, GET, OPTIONS, PUT, DELETE
< access-control-allow-origin: *
< content-type: text/plain; charset=utf-8
< x-content-type-options: nosniff
< content-length: 19
< date: Fri, 25 Dec 2020 14:59:02 GMT
< 
# 已经成功访问,主要是访问页面不存在,实际已成功连接
404 page not found
* Connection #0 to host etcdnode1-0.etcdsvc.common.svc.cluster.local left intact

【测试试验3:不添加客户端证书和秘钥,因为etcd的启动增加了--client-cert-auth开启客户端认证,确保只有授权客户端能访问服务器。因此这里无法访问服务器,确保了只有获得了客户端证书的客户端才能访问集群】

 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
curl --cacert ca.pem https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379/v2/keys/foo  -v --ciphers ECDHE-RSA-AES128-GCM-SHA256

*   Trying 10.128.3.86...
* TCP_NODELAY set
* Connected to etcdnode1-0.etcdsvc.common.svc.cluster.local (10.128.3.86) port 2379 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ECDHE-RSA-AES128-GCM-SHA256
* successfully set certificate verify locations:
*   CAfile: ca.pem
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Request CERT (13):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS alert, bad certificate (554):
* error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate
* Closing connection 0
# 访问失败
curl: (35) error:14094412:SSL routines:ssl3_read_bytes:sslv3 alert bad certificate

【测试试验4:curl不指定加密套件,客户端会根据服务器协商采用服务器指定加密套件。如果客户端指定采用DES加密算法通讯,提示TLSv1.3 (IN), TLS alert, handshake failure (552):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
curl --cacert ca.pem https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379/v2/keys/foo --cert etcdclient.pem --key etcdclient-key.pem -XPUT -d value=bar -v --ciphers ECDHE-RSA-DES-CBC3-SHA

*   Trying 10.128.3.89...
* TCP_NODELAY set
* Connected to etcdnode1-0.etcdsvc.common.svc.cluster.local (10.128.3.89) port 2379 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ECDHE-RSA-DES-CBC3-SHA
* successfully set certificate verify locations:
*   CAfile: ca.pem
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS alert, handshake failure (552):
* error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure
* Closing connection 0
curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure

对应地服务器会报错,提示拒绝连接。

1
embed: rejected connection from "10.131.1.143:44596" (error "tls: no cipher suite supported by both client and server", ServerName "etcdnode3-0.etcdsvc.common.svc.cluster.local")

【测试试验5:客户端证书hosts字段对验证的影响】

无论客户端证书hosts内容发生改变,只要通过CA证书和CA秘钥生成的,均可以被服务器正确响应。

【测试试验6:安装根证书后测试】

1
2
3
4
5
6
7
# 安装根证书
oc cp ca.pem centos7-57546cfd99-j9zfl:/etc/pki/ca-trust/source/anchors/
# 更新系统的证书
update-ca-trust

# 直接crul测试,这里并没有--cacert ca.pem,
curl  https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379/v2/keys/Hi --cert etcdclient.pem --key etcdclient-key.pem -XPUT -d value=bar -v

3.9 ETCD部署最佳实践

参考:https://etcd.io/docs/v3.4.0/faq/

3.9.1 最大群集大小?

从理论上讲,没有硬性限制。但是,一个etcd集群可能最多只能有七个节点。 类似于etcd的Google Chubby Lock服务已经在Google中广泛部署了很多年,建议运行五个节点。一个5成员的etcd集群可以容忍两个成员的故障,这在大多数情况下就足够了。尽管较大的群集可提供更好的容错能力,但由于必须在更多计算机之间复制数据,因此写入性能会受到影响。

3.9.2 集群重新配置操作

要更新单个成员peerURL,请执行更新操作

要替换正常的单个成员,请删除旧成员,然后添加新成员

要从3名增加到5名成员,请执行两次添加操作

要从5减少到3,请执行两次删除操作

——————-至此ETCD配置完成——————-

4.Coredns

作为Coredns云原生存储的ETCD部署完毕后,现在将通过ETCD和Coredns配合提供集群内和集群外的DNS服务。

ETCD集群保存域名记录,Coredns负责域名解析,类似之前的Skydns+ETCD组合。

Coredns是个啥?它就是用Golang写的二进制文件,特点就是插件化一把梭解决方案(插件全部编译好了,一个二进制文件,走遍全世界)。

特长是干DNS和服务发现。有高手用来搭建无污染DNS服务器用来科学上网。

扯远了,其实用来做云原生的企业内部DNS服务器再合适不过。

4.1 获取二进制文件

参考:https://github.com/coredns/coredns/releases/tag/v1.8.0

1
2
3
curl -L https://github.com/coredns/coredns/releases/download/v1.8.0/coredns_1.8.0_linux_amd64.tgz -o coredns_1.8.0_linux_amd64.tgz

tar xvf coredns_1.8.0_linux_amd64.tgz

Coredns二进制文件与后续corednsDockfile在同一个目录下。

4.2 自定义镜像文件

Coredns官方镜像文件:https://hub.docker.com/r/coredns/coredns

如果通过官方镜像文件加载,无法执行容器里的bash。

编译自定义的镜像文件corednsDockfile

注意:在此之前,请将相关证书拷贝至当前目录,其中ca-key.pem提前移除corednsDockfile文件当前目录,避免ca秘钥泄露。

 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
cat > corednsDockfile <<EOF
FROM alpine:latest

# author ChengZhang
MAINTAINER ChengZhang

#RUN apk update && \
#    apk add --no-cache bash

# install etcd
COPY coredns /bin/
COPY *.pem /bin/

#RUN mkdir -p /var/etcd/ && mkdir -p /var/lib/etcd/
# Alpine Linux doesn't use pam, which means that there is no /etc/nsswitch.conf,
# but Golang relies on /etc/nsswitch.conf to check the order of DNS resolving
# (see https://github.com/golang/go/commit/9dee7771f561cf6aee081c0af6658cc81fac3918)
# To fix this we just create /etc/nsswitch.conf and add the following line:
RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf

EXPOSE 53

# Define default command.
ENTRYPOINT ["/bin/coredns"]
EOF

注:这里关于/etc/nsswitch.conf文件的作用并不理解,暂时注释掉。

【编译镜像】

1
2
3
4
5
podman build -f corednsDockfile -t registry.cj.io:5000/coredns:0.0.1 .

# 镜像大小48.1MB
podman image ls | grep coredns  
registry.cj.io:5000/coredns        0.0.1          c4c597bd667b   3 minutes ago   48.1 MB

【推送到企业内部镜像仓库】

1
podman push registry.cj.io:5000/coredns:0.0.1

4.3 CoreDNS配置文件

当 CoreDNS 启动的时候,如果 -conf 没有被配置,就会在当前目录查找 Corefile 文件。Coredns相关配置由配置文件定义。

这种配置文件场景适合采用Configmap这种对象传给Pod。

configmap格式上注意:node1.cnf: |后面的data数据,要注意对齐,不能超过node1.cnf: |本身。

这里通过ETCD插件去访问已部署的ETCD集群,相应的endpoint地址用空格隔开。采用TLS证书访问。

当ETCD集群内无对应的DNS域名请求后,ETCD插件将返回插件,因为fallthrough插件将进入下一个forward插件工作。缺少这一个指令,后面的 plugins 配置就无意义了。

forward插件内部配置了114.114.114.114 223.5.5.5 223.6.6.6

也可以配置了google DNS over TLS地址tls://8.8.8.8 tls://8.8.4.4

forward插件policy可以配置成随机、随机轮询和

1
2
3
4
5
6
7
8
forward . tls://8.8.8.8 tls://8.8.4.4 {
        tls_servername dns.google
        force_tcp
        max_fails 3
        expire 10s
        health_check 5s
        policy round_robin
        }

关于DoH和DoT的解释如下:

传统的DNS查询和应答采用UDP和TCP明文传输,存在网络监听、DNS劫持、中间设备干扰的风险:

  • 网络监听风险:即便用户采用HTTPs加密的方式访问站点,DNS查询应答并没有采用加密传输。
  • DNS劫持:传统DNS应答数据会被篡改,用户的访问会被路由到钓鱼网站和恶意站点。
  • 中间设备干扰:主要是一些防火墙对DNS查询的干扰,基于域名的过滤,还有大包MTU分片的影响等。

DoH(RFC8484)和DoT(RFC7858) 标准对外提供DNS的安全传输服务, 支持 DNS over HTTP(s), DNS Json API, 和DNS over TLS三种安全传输模式。DNS的安全传输服务可以适用于移动应用程序、浏览器、操作系统、物联网设备和网关路由器等多个场景。通过传输加密的方式发送DNS查询,加强了用户访问互联网的安全性、解析稳定和隐私保护。

除了隐私加密以外,DNS安全传输服务采用TCP和HTTP连接用户端和DNS服务器,一方面可以服务精准的基于位置的DNS解析和流量调度,另一方面基于DNS端到端的连接特性,DNS的动态变更可以实现秒级端到端生效。

目前firefox浏览器支持DOH

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/Jietu20190416-142524@2x.jpg

编写configmap文件。

注意:每个服务器块和插件块的第一个大括号前必须留有空格。

 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
40
41
42
43
44
45
46
47
48
49
50
51
cat > corednsconfigmap.yaml << EOF

apiVersion: v1
kind: ConfigMap
metadata:
  labels:
    app: corednsconfigmap
  name: corednsconfigmap
data:
  coredns.cnf: |
    openshift4.cj.io.openshift4.cj.io.:53 {      
      template IN ANY openshift4.cj.io {
        authority "{{ .Zone }} 60 IN SOA ns.openshift4.cj.io admin.openshift4.cj.io (1 60 60 60 60)"
        fallthrough
      }
      reload 6s
      prometheus
      cache 160
    }
    
    cj.io.:53 { 
      template IN A apps.openshift4.cj.io {
        match .*\.apps\.openshift4\.cj\.io
        answer "{{ .Name }} 60 IN A 172.18.1.2"       
        fallthrough
      } 
      etcd {
        path /coredns 
        endpoint https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379
        tls /bin/etcdclient.pem /bin/etcdclient-key.pem /bin/ca.pem
      }
    }

    .:53 {
      forward . 114.114.114.114 223.5.5.5 223.6.6.6 {
        max_fails 3
        expire 10s
        health_check 5s
        policy round_robin
      }
    }
    
    172.18.1.0/24 {
      etcd {
        path /coredns 
        endpoint https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379
        tls /bin/etcdclient.pem /bin/etcdclient-key.pem /bin/ca.pem
      }
    }  
        
EOF

这里反向解析Zone,172.18.1.0/24等同于1.18.172.in-addr.arpa.:53

创建configmap

1
oc create -f corednsconfigmap.yaml

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20210101171323730.png

特别注意:如何更新configmap后,重新挂载pod内的subPath: 有待研究。

4.4 配置文件说明

参考:https://coredns.io/manual/toc/#configuration

Coredns的配置文件均为以服务器块出现,内部配置插件形式完成最终配置。

理解各项插件的功能就能充分利用coredns完成各项功能的弹性扩展,这也是coredns在程序设计上的最大亮点。

但必须要理解插件链的工作模式,详见《4.4.3插件》内容

插件功能基于 Caddy 服务器框架,CoreDNS 实现了一个插件链的架构,将大量应用端的逻辑抽象成 plugin (下文将混用 plugin 和 插件 这两个词汇)的形式(如 Kubernetes 的 DNS 服务发现,Prometheus 监控等)暴露给使用者。CoreDNS 以预配置的方式将不同的 plugin 串成一条链,按序执行 plugin 的逻辑。从编译层面,用户选择所需的 plugin 编译到最终的可执行文件中,使得运行效率更高。CoreDNS 采用 Go 编写,所以从具体代码层面来看,每个 plugin 其实都是实现了其定义的 interface 的组件而已。第三方只要按照 CoreDNS Plugin API 去编写自定义插件,就可以很方便地集成于 CoreDNS;

4.4.1 环境变量

在配置文件中,可以引用环境变量,用法是采用{$envname}或者{%envname}。Coredns在加载配置文件过程中,对环境变量进行替换

4.4.2 服务器块 (Server Blocks)

例如,.下所有的zones; 基本上,这个 server 应该处理所有的查询。

服务器块内开始配置插件,例如这里配置了chaos,

1
2
3
4
. {
    # Plugins defined here.
    chaos CoreDNS-001 info@coredns.io
}

同样可以监听多个端口,配合多个zone。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
coredns.io:5300 {
    file db.coredns.io
}

# 后续都是标准的53端口,同一个端口下,不同的zone,分别是example.io、example.net以及root zone。
example.io:53 {
    log
    errors
    file db.example.io
}

example.net:53 {
    file db.example.net
}

.:53 {
    kubernetes
    forward . 8.8.8.8
    log
    errors
    cache
}

当Coredns对上述配置文件解析时,就是官网这张图片

注意插件链的概念

例如example.io:53的插件编写顺序如下,但是实际插件的执行顺序按图示执行。.:53的插件工作逻辑一样。

1
2
3
4
5
example.io:53 {
    log
    errors
    file db.example.io
}

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/webp

4.4.3 插件

插件的工作逻辑

1、如果有当前请求的 server 有多个 zone,将采用贪心原则选择最匹配的 zone;

2、一旦找到匹配的 server,按照 plugin.cfg 定义的顺序执行插件链上的插件;注意并不是按照server block内编写插件名称的顺序

3、每个插件将判断当前请求是否应该处理,将有以下几种可能

  • 请求被当前插件处理:插件将生成对应的响应并回给客户端,此时请求结束,如 whoami 插件;;
  • 请求不被当前插件处理:直接调用下一个插件。如果最后一个插件执行错误,服务器返回 SERVFAIL 响应;
  • 请求被当前插件以 Fallthrough 形式处理:如果请求在该插件处理过程中有可能将跳转至下一个插件,该过程称为 fallthrough,并以关键字 fallthrough 来决定是否允许此项操作,例如 host 插件,当查询域名未位于 /etc/hosts,则调用下一个插件;
  • 请求在处理过程被携带 Hint:请求被插件处理,并在其响应中添加了某些信息(hint)后继续交由下一个插件处理。这些额外的信息将组成对客户端的最终响应,如 metric 插件;

介绍几种插件

【file插件】

Coredns也可以使用file插件读取zone文件,和bind9一样兼容DNS标准文档(见RFC1035)

可以应付从 bind 迁移,还能兼容 old-style dns server 的运维习惯;

1
2
3
4
example.org:53 {
    file db.example.org
    log
}

对应的zone文件可参考

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ORIGIN example.org.
@   3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. (
                2017042745 ; serial
                7200       ; refresh (2 hours)
                3600       ; retry (1 hour)
                1209600    ; expire (2 weeks)
                3600       ; minimum (1 hour)
                )

    3600 IN NS a.iana-servers.net.
    3600 IN NS b.iana-servers.net.

www     IN A     127.0.0.1
        IN AAAA  ::1

【ETCD插件】

参考:https://coredns.io/plugins/etcd/

配置启用 etcd 插件,后面可以指定域名,例如 etcd test.com {

etcd 里面的路径 默认为 /skydns,以后所有的 dns 记录都存储在该路径下,本案例配置了path /coredns

注意这个值是代表在etcd各分布式节点下存储的目录名,并非coredns节点的存储目录名。如果不使用file插件,而使用etcd插件,你可以理解为完全非状态化的应用。

etcd 访问地址,多个空格分开。例如endpoint https://etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 https://etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 https://etcdnode3-0.etcdsvc.common.svc.cluster.local:2379

tls CERT KEY CACERT :可选参数,etcd认证证书设置。

注意:其中后续的CERT KEY CACERT 这三个参数,均为证书的实际路径。

CoreDNS 除了支持 DNS 协议,也支持 TLS 和 gRPC,即 DNS-over-TLS 和 DNS-over-gRPC 模式:

1
2
3
tls://example.org:1443 {
#...
}

4.5 节点部署

参考:https://coredns.io/manual/toc/#configuration

Coredns的启动可以通过-conf配合配置文件启动。而配置文件通过volumeMountssubPath将签署configmap挂载到容器内的特定文件。

编写Coredns节点的yaml文件。

注意这里replicas: 1暂时为1,后续可快速弹性扩展Pod数量应对高并发场景。

 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
cat > coredns.yaml<<EOF
apiVersion: apps/v1
kind: Deployment
#kind: StatefulSet
metadata:
  name: coredns
  namespace: common
spec:
  serviceName: cordnssvc
  replicas: 1
  selector:
    matchLabels:
      app: coredns
  template:
    metadata:
      labels:
        app: coredns
    spec:
      serviceAccount: ethan
      serviceAccountName: ethan
      containers:
        - name: coredns
          image: 'registry.cj.io:5000/coredns:0.0.1'
          imagePullPolicy: Always
          command: ["/bin/coredns"]
          args: 
            - '-conf'
            - '/bin/coredns.cnf'
          volumeMounts:
          - name: corednsconfigmap
            mountPath: "/bin/coredns.cnf"
            subPath: coredns.cnf
      volumes:
      - name: corednsconfigmap
        configMap:
          name: corednsconfigmap
EOF

创建Deplayment

1
oc create -f coredns.yaml

后续可以借助Openshift平台的快速弹性扩展能力,在并发量高的场景下,快速调整副本数量。

当然在高并发场景下,外部的LB集群也需要响应做调整。

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20201227201731186.png

4.6 网络拓扑持久化

Pod是有生命周期的,Pod的IP地址会随着容器的销毁和再次启动发生变化,会随着容器为了满足集群内和集群外提供DNS服务,还需要对网络地址进行固定。

如《2.整体架构》描述,这里采用NodePort方式对外提供DNS服务。整个集群通过外部统一的LB节点,对外再提供DNS服务。由于NodePort的默认端口在30000-32767之间,因此这里选择的NodePort端口为了便于记忆,选择了30053

【创建Service:corednssvc.yaml 】

PS:这里后续在拷贝下面文字到bash中时,切记最后EOF不要带空格,否则无法正常输入至文件内。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
cat > corednssvc.yaml << EOF
apiVersion: v1
kind: Service
metadata:
  name: corednssvc
spec:
  selector:
    app: coredns
  ports:
  - port: 53
    name: dns-port
    protocol: UDP
    targetPort: 53
    nodePort: 30053
  type: NodePort
  
EOF

创建svc

1
oc create -f corednssvc.yaml 

4.7.验证测试

4.7.1 功能测试

这里仍采用ETCD验证测试章节部署的centos7环境

1
2
3
4
5
6
7
8
# centos7-57546cfd99-j9zfl是部署的centos7 pdo名称
oc rsh centos7-57546cfd99-j9zfl

cat /etc/resolv.conf 
# 这里的dns服务器地址为openshift默认的内部172.30.0.10地址
search common.svc.cluster.local svc.cluster.local cluster.local openshift4.cj.io
nameserver 172.30.0.10
options ndots:5

假设这里设置一个www.test.com域名,对应的A记录是1.1.1.1

注意这里K/V记录值,K为域名,ETCD的目录结构和域名是相反的,其中最前面的/coredns,就是Coredns配置文件中ETCD插件中path /coredns 值,V为{"host":"1.1.1.1","ttl":10}

关于下面etcdctl的用法,详见《3.4 自定义TLS证书》相关内容,利用客户端证书直接对ETCD进行写入put操作。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
cd /root

# put 域名信息
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/com/test/www '{"host":"1.1.1.1","ttl":10}'

# 查看以”/coredns”为开头的数据
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode1-0.etcdsvc.common.svc.cluster.local:2379 get /coredns --prefix
# 返回信息
/coredns/com/test/www
{"host":"1.1.1.1","ttl":10}

验证(继续再centos7 这个pod内执行),这里的172.18.1.47是Openshift的work节点的物理IP地址、30053端口就是NotePort Service配置文件的地址。详见《4.6 网络拓扑持久化》相关内容。

首先直接集群外进行访问

 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
# 利用dig命令
dig -p 30053 @172.18.1.47 www.test.com

; <<>> DiG 9.11.20-RedHat-9.11.20-5.el8 <<>> -p 30053 @172.18.1.47 www.test.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 52066
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
; COOKIE: cea83a958798d43f (echoed)
;; QUESTION SECTION:
;www.test.com.                  IN      A

;; ANSWER SECTION:
www.test.com.           10      IN      A       1.1.1.1

;; Query time: 8 msec
;; SERVER: 172.18.1.47#30053(172.18.1.47)
;; WHEN: Sun Dec 27 03:49:32 UTC 2020
;; MSG SIZE  rcvd: 81

同样的,我们也可以直接访问集群内承载了coredns的pod,得到的结果是一致的。

1
2
3
4
5
6
7
# 获取cordnspod的当前ip地址
oc get pod -owide | grep dns
coredns-5dbf7fcb8f-tpkqs   1/1 Running  0  5h11m   10.128.3.129 worker2.openshift4.cj.io  <none>    <none>

# 直接访问pod的ip地址,因为是默认的53端口,不需要`-p`参数
dig @10.128.3.129 www.test.com +short
1.1.1.1

继续在集群外访问不在当前ETCD数据库内的域名信息,例如www.baidu.com

ETCD无法访问,由于forward插件的作用,Coredns去轮询访问114.114.114.114 223.5.5.5 223.6.6.6这三个配置的DNS地址。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 利用dig命令
dig -p 30053 @172.18.1.47 www.baidu.com

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>> -p 30053 @172.18.1.47 www.baidu.com
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 19803
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;www.baidu.com.                 IN      A

;; ANSWER SECTION:
www.baidu.com.          133     IN      CNAME   www.a.shifen.com.
www.a.shifen.com.       133     IN      A       180.101.49.12
www.a.shifen.com.       133     IN      A       180.101.49.11

;; Query time: 35 msec
;; SERVER: 172.18.1.47#30053(172.18.1.47)
;; WHEN: Sun Dec 27 20:20:22 CST 2020
;; MSG SIZE  rcvd: 149

4.7.2 性能测试

参考:https://www.isc.org/download/

下载地址:https://ftp.isc.org/isc/bind9

如果是安装bind,这里介绍下。

编译,确保libuv-devel组件以及libcap-devel已安装

libuv是一个支持多平台的异步IO库,libcap主要用于网络嗅探。

1
2
yum install -y libuv-devel libcap-devel
# 注意是libuv-devel,不是libuv。

如果不是安装bind组件,直接安装queryper,用下面步骤【9.17版本并不包括queryperf】

1
2
3
4
5
wget https://ftp.isc.org/isc/bind9/9.11.2/bind-9.11.2.tar.gz
cd bind-9.7.3/contrib/queryperf/
./configure
make
cp queryperf /usr/bin/

编写一个creattest.sh的脚本文件

1
2
3
4
5
6
7
8
#!/bin/sh   
var=1
while [ $var -le 1000000 ]
do
echo "www.test.com A " >> test.txt
var=$(($var + 1 ))
done
exit 0

添加执行权限

1
chmod +x creattest.sh

执行测试

1
queryperf -d test.txt -s 172.18.1.47 -p 30053

【执行结果】

 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
DNS Query Performance Testing Tool
Version: $Id: queryperf.c,v 1.12 2007-09-05 07:36:04 marka Exp $

[Status] Processing input data
[Status] Sending queries (beginning with 172.18.1.47)
[Status] Testing complete

Statistics:

  Parse input file:     once
  Ended due to:         reaching end of file
  # 发送的条数
  Queries sent:         1530911 queries
  # 完成的条数
  Queries completed:    1530911 queries
  Queries lost:         0 queries
  Queries delayed(?):   0 queries

  RTT max:              0.081727 sec
  RTT min:              0.000231 sec
  RTT average:          0.001068 sec
  RTT std deviation:    0.000776 sec
  RTT out of range:     0 queries

  Percentage completed: 100.00%
  Percentage lost:        0.00%

  Started at:           Mon Dec 28 11:30:33 2020
  Finished at:          Mon Dec 28 11:31:57 2020
  Ran for:              84.381046 seconds
  # 每秒查询次数
  Queries per second:   18142.830322 qps

将Coredns的副本数调整为1,执行结果。结果相差不到2.4%

1
Queries per second:   17693.135645 qps

将Coredns的副本数调整为3,调整到通过外部LB(地址是172.18.1.3)节点再进行访问。

负载均衡器的设置详见后续章节。

结果并不能提升。

1
2
queryperf -d test.txt -s 172.18.1.3 -p 153
Queries per second:   16329.070963 qps

将LB节点的CPU从2C增加至4C,内存从16G增加至32G,平均测试结果提升有限。

1
Queries per second:   16797.956565 qps

LB节点虚拟机的相关资源情况

https://typorabyethancheung911.oss-cn-shanghai.aliyuncs.com/typora/image-20201228121722090.png

4.8 CoreDNS+ETCD实战

———————至此完整功能的云原生DNS系统已经部署完毕——————-

相比较传统BIND ,采用ZONE文件DNS标准文档(见RFC1035),Coredns+ETCD的方式更加直观、方便快捷。

数据才是应用系统的核心资产,通过分布式、高可靠的ETCD集群,Coredns可以持续对外提供高性能的DNS服务。

接下来我们通过Openshift所需的DNS记录,来演示如何快速添加企业内部DNS记录。

注:

1、openshift4是本OCP集群的cluster_namecj.io是OCP集群的企业内部基本域(base_domain)

2、ETCD集群各节点部署在OCP Master各节点上,使用2380对外提供集群内的peer通讯。由于OCP的etcd集群内部并没有采用我前述无头服务+Stateful进行了域名的网络持久化。因此必须采用服务发现的方式。采用DNS SRV的记录方式,与常见的A记录、cname不同的是,SRV中除了记录服务器的地址,还记录了服务的端口,并且可以设置每个服务地址的优先级和权重。访问服务的时候,本地的DNS resolver从DNS服务器查询到一个地址列表,根据优先级和权重,从中选取一个地址作为本次请求的目标地址。这里。建立对应每台 etcd 节点的 SRV DNS 记录,优先级 0,权重 10 和端口 2380。_etcd-server-ssl._tcp.openshift4.cj.io符合[rfc-2782]给出DNS SRV的建议标准。

3、域名格式最后有.是根域名。

4、LB集群的VIP是172.18.1.2

类别 域名 IP地址 备注
Openshift4 API api.openshift4.cj.io. 172.18.1.2 OCP集群,一般都要配置外部的LoadBalance,本方案中,我们部署了两个节点的LB集群,对外通过VIP提供LB服务。因此IP地址解析指向OCP控制平面的LB的VIP
Openshift4 API-int api-int.openshift4.cj.io. 172.18.1.2 同上。客户端和work调用
ETCD etcd-0.openshift4.cj.io. 172.18.1.44 etcd指向各Master物理实际节点的ip地址。
ETCD etcd-1.openshift4.cj.io. 172.18.1.45 它们以 0 开头,以 n-1 结束,其中 n 是集群中控制平面节点的数量。
ETCD etcd-2.openshift4.cj.io. 172.18.1.46 集群中的所有节点必须都可以解析此记录
Master节点 master1.openshift4.cj.io. 172.18.1.44 指向master1实际节点
Master节点 master2.openshift4.cj.io. 172.18.1.45 指向master2实际节点
Master节点 master3.openshift4.cj.io. 172.18.1.46 指向master3实际节点
Work节点 work1.openshift4.cj.io. 172.18.1.47 指向Work1实际节点
Work节点 work2.openshift4.cj.io. 172.18.1.48 指向Work2实际节点
Work节点 work3.openshift4.cj.io. 172.18.1.49 指向Work3实际节点
应用发布 .*apps.openshift4.ci.io. 172.18.1.2 这里是泛域名解析,实际作用就是对外应用发布router。也是指向外部LB集群的VIP。由LB再负载均衡到集群的router,再通过后者负载均衡到各work节点(如果master打了污点)的内部Pod。
ETCD2380端口SRV etcd-server-ssl._tcp.openshift4.cj.io etcd-0.openshift4.cj.io. 指向etcd0,优先级 0,权重 10 和端口 2380
ETCD2380端口SRV etcd-server-ssl._tcp.openshift4.cj.io etcd-1.openshift4.cj.io. 指向etcd1,优先级 0,权重 10 和端口 2380
ETCD2380端口SRV etcd-server-ssl._tcp.openshift4.cj.io etcd-2.openshift4.cj.io. 指向etcd2,优先级 0,权重 10 和端口 2380
bootstrap bootstrap.openshift4.cj.io. 172.18.1.43 用于集群启动安装,安装完毕后可以删除
镜像仓库 registry.cj.io 172.18.1.1 指向镜像仓库
企业主页 portal.cj.io 172.18.1.2 指向LB集群VIP地址
NTP ntp.cj.io 172.18.1.2 指向LB集群VIP地址
YUM yum.cj.io 172.18.1.1 指向YUM源

开始通过etcdctl推送数据

相关命令在前述centos容器内执行,注意证书的参数配置。

注:泛域名解析功能通过Coredns的template实现,查看《4.4配置文件说明》

 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
40
41
42
43
# api、api-int
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/api '{"host":"172.18.1.2","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/api-int '{"host":"172.18.1.2","ttl":60}'

#etcd
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/etcd-0 '{"host":"172.18.1.44","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/etcd-1 '{"host":"172.18.1.45","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/etcd-2 '{"host":"172.18.1.46","ttl":60}'


etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/_tcp/_etcd-server-ssl/x1 '{"host":"etcd-0.openshift4.cj.io","ttl":60,"priority":0,"weight":10,"port":2380}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/_tcp/_etcd-server-ssl/x2 '{"host":"etcd-1.openshift4.cj.io","ttl":60,"priority":0,"weight":10,"port":2380}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/_tcp/_etcd-server-ssl/x3 '{"host":"etcd-2.openshift4.cj.io","ttl":60,"priority":0,"weight":10,"port":2380}'

# 应用发布
# 这里直接通过 template实现泛域名功能。
#template IN A apps.openshift4.cj.io {
#        match .*\.apps\.openshift4\.cj\.io
#        answer "{{ .Name }} 60 IN A 172.18.1.2"       
#        fallthrough
#      } 


# master和work记录
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/bootstrap '{"host":"172.18.1.43","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/master1 '{"host":"172.18.1.44","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/master2 '{"host":"172.18.1.45","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/master3 '{"host":"172.18.1.46","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/worker1 '{"host":"172.18.1.47","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/worker2 '{"host":"172.18.1.48","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/worker3 '{"host":"172.18.1.49","ttl":60}'

# 其他诸如registry、portal.cj.io等其他
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/registry '{"host":"172.18.1.1","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/portal '{"host":"172.18.1.2","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/ntp '{"host":"172.18.1.2","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/yum '{"host":"172.18.1.1","ttl":60}'

etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/vmware7/vcenter7 '{"host":"192.168.100.250","ttl":60}'


etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/ns '{"host":"ns22.openshift4.cj.io","ttl":60}'
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/io/cj/openshift4/io/cj/openshift4/worker3 '{"host":"172.18.1.49","ttl":60}'

【验证】

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
dig +short *.apps.openshift4.cj.io @172.18.1.2
dig +short api.openshift4.cj.io @172.18.1.2
dig +short api-int.openshift4.cj.io @172.18.1.2
dig +short xx.apps.openshift4.cj.io @172.18.1.2
dig +short etcd-0.openshift4.cj.io @172.18.1.2
dig +short etcd-1.openshift4.cj.io @172.18.1.2
dig +short etcd-2.openshift4.cj.io @172.18.1.2
dig +short master1.openshift4.cj.io @172.18.1.2
dig +short master2.openshift4.cj.io @172.18.1.2
dig +short master3.openshift4.cj.io @172.18.1.2
dig +short worker1.openshift4.cj.io @172.18.1.2 
dig +short worker2.openshift4.cj.io @172.18.1.2
dig +short worker3.openshift4.cj.io @172.18.1.2
dig +short registry.cj.io @172.18.1.2
dig +short portal.cj.io @172.18.1.2
dig +short yum.cj.io @172.18.1.2
dig +short ntp.cj.io @172.18.1.2

再继续验证下SRV记录

1
2
3
4
5
dig _etcd-server-ssl._tcp.openshift4.cj.io SRV +short @172.18.1.2
# 返回
0 10 2380 etcd-1.openshift4.cj.io.
0 10 2380 etcd-2.openshift4.cj.io.
0 10 2380 etcd-0.openshift4.cj.io.

配置反向解析

1
2
3
etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/arpa/in-addr/172/18/1/44 '{"host":"master1.openshift4.cj.io."}'

etcdctl --cacert=ca.pem --cert=etcdclient.pem --key=etcdclient-key.pem --endpoints=etcdnode2-0.etcdsvc.common.svc.cluster.local:2379 put /coredns/arpa/in-addr/172/18/1/2 '{"host":"ntp.cj.io."}'

反向解析验证

1
dig -x 172.18.1.44 +short

4.9 关键:搜索域的概念解释

首先我们查看默认配置下,启动完毕容器环境下的解析器配置

1
2
3
4
cat /etc/resolv.conf 
search common.svc.cluster.local svc.cluster.local cluster.local openshift4.cj.io
nameserver 172.30.0.10
options ndots:5

这里四个搜索域,会按照顺序去进行搜索

1、common.svc.cluster.local

2、svc.cluster.local

3、cluster.local

4、openshift4.cj.io

OCP(K8S原理一样,只是VIP地址不同) 的 Service 的 ClusterIP,这个IP是虚拟IP,无法ping,但可以访问。

1
2
oc get svc --all-namespaces | grep 172.30.0.10
openshift-dns  dns-default   ClusterIP   172.30.0.10  <none>  53/UDP,53/TCP,9154/TCP  89d

有几个关键事项:

1、Pod与Pod之间、服务与服务之间,都是通过dns-default命名空间下这个ClusterIP SVC提供了服务发现以及内部DNS服务。

访问mysql-mgr-node2-0.mysqlmgr-svc.mysql-mgr实际上,全域名为mysql-mgr-node2-0.mysqlmgr-svc.mysql-mgr.svc.cluster.local

首先它会访问第一给搜索域common.svc.cluster.local

无法成功后,访问第二个搜索域svc.cluster.local

1
2
3
4
5
# 访问其他命名空间下svc服务下的某个pod mysql-mgr-node2-0.mysqlmgr-svc.mysql-mgr.svc.cluster.local
ping mysql-mgr-node2-0.mysqlmgr-svc.mysql-mgr
PING mysql-mgr-node2-0.mysqlmgr-svc.mysql-mgr (10.131.1.178) 56(84) bytes of data.
64 bytes from mysql-mgr-node2-0.mysqlmgr-svc.mysql-mgr (10.131.1.178): icmp_seq=1 ttl=64 time=0.391 ms
64 bytes from mysql-mgr-node2-0.mysqlmgr-svc.mysql-mgr (10.131.1.178): icmp_seq=2 ttl=64 time=0.056 ms

2、以上也是OCP内部服务的发现访问机制,另外如果是访问外部资源(集群外)地址,怎么流程?

这里做个试验,我们将我们《4.4 CoreDNS配置文件》中有关搜索域SOA解析的配置删除

1
2
3
4
5
6
7
8
9
openshift4.cj.io.openshift4.cj.io.:53 {      
      template IN ANY openshift4.cj.io {
        authority "{{ .Zone }} 60 IN SOA ns.openshift4.cj.io admin.openshift4.cj.io (1 60 60 60 60)"
        fallthrough
      }
      reload 6s
      prometheus
      cache 160
    }

删除配置删除后,我们再访问外网,原形毕露了。

1
2
3
4
5
6
sh-4.4# ping www.baidu.com
PING www.baidu.com.openshift4.cj.io (162.253.133.66) 56(84) bytes of data.
64 bytes from static.162.253.133.66.macminivault.com (162.253.133.66): icmp_seq=1 ttl=48 time=170 ms
64 bytes from static.162.253.133.66.macminivault.com (162.253.133.66): icmp_seq=2 ttl=48 time=170 ms
64 bytes from static.162.253.133.66.macminivault.com (162.253.133.66): icmp_seq=3 ttl=48 time=170 ms
64 bytes from static.162.253.133.66.macminivault.com (162.253.133.66): icmp_seq=4 ttl=48 time=170 ms

很明显可以看出,/etc/resolv.conf 前三个搜索域不能访问后,Pod将尝试第四个搜索域openshift4.cj.io,这个是集群搭建时候的基本域名。

显然对于这种添加了搜索域的地址www.baidu.com.openshift4.cj.io需要在CoreDNS中进行处理。

5.NTP服务

DNS服务部署及验证完毕,相对而言NTP服务的容器化改造相对简单。

主要工作在于自定义镜像和流量的重定向。

5.1 自定义镜像文件

这里设计了一个脚本,在Pod启动后先执行将环境变量设置至chronyd的配置文件

然后再启动chronyd的后台服务进程。

5.1.1 启动脚本

这里默认采用time.cloudflare.comNTP的地址

 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
40
41
42
43
44
45
#!/bin/sh

DEFAULT_NTP="time.cloudflare.com"
CHRONY_CONF_FILE="/etc/chrony/chrony.conf"

# update permissions on chrony directories
chown -R chrony:chrony /run/chrony /var/lib/chrony
chmod o-rx /run/chrony

# remove previous pid file if it exist
rm -f /var/run/chrony/chronyd.pid

## dynamically populate chrony config file.
{
  echo "# https://github.com/cturra/docker-ntp"
  echo
  echo "# chrony.conf file generated by startup script"
  echo "# located at /opt/startup.sh"
  echo
  echo "# time servers provided by NTP_SERVER environment variables."
} > ${CHRONY_CONF_FILE}


# NTP_SERVERS environment variable is not present, so populate with default server
if [ -z "${NTP_SERVERS}" ]; then
  NTP_SERVERS="${DEFAULT_NTP}"
fi

IFS=","
for N in $NTP_SERVERS; do
  # strip any quotes found before or after ntp server
  echo "server "${N//\"}" iburst" >> ${CHRONY_CONF_FILE}
done

# final bits for the config file
{
  echo "driftfile /var/lib/chrony/chrony.drift"
  echo "makestep 0.1 3"
  #echo "rtcsync"
  echo
  echo "allow all"
} >> ${CHRONY_CONF_FILE}

## startup chronyd in the foreground
exec /usr/sbin/chronyd -d -s

5.1.2 编写Dockfile

参考https://github.com/cturra/docker-ntp

注意最终的ENTRYPOINT [ "/bin/sh", "/opt/startup.sh" ]参数copy到镜像内的执行脚本。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
cat chronyDockerfile

FROM alpine:latest
# author ChengZhang
MAINTAINER ChengZhang
# install chrony
RUN apk add --no-cache chrony

# script to configure/startup chrony (ntp)
COPY startup.sh /opt/startup.sh
RUN mkdir -p /etc/chrony/
RUN mkdir -p /var/lib/dpkg/info

# ntp port
EXPOSE 123/udp

# let docker know how to test container health
# HEALTHCHECK CMD chronyc tracking || exit 1

# start chronyd in the foreground
ENTRYPOINT [ "/bin/sh", "/opt/startup.sh" ]

5.1.3 编译镜像

1
2
3
4
podman build -f chronyDockerfile -t registry.cj.io:5000/chrony:0.0.1 .

# 镜像大小6.27MBpodman image ls | grep chrony
registry.cj.io:5000/chrony       0.0.1        a9d4c925474e   2 days ago      6.27 MB

【推送到企业内部镜像仓库】

1
podman push registry.cj.io:5000/chrony:0.0.1

5.2 节点部署

编写chrony服务的yaml文件,这里设置副本数为3。

注意这里的环境变量NTP_SERVERS值为ntp.aliyun.com

 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
cat > chrony.yaml <<EOF
apiVersion: apps/v1
#kind: Deployment
kind: StatefulSet
metadata:
  name: chrony
  namespace: common
spec:
  serviceName: chronysvc
  replicas: 3
  selector:
    matchLabels:
      app: chrony
  template:
    metadata:
      labels:
        app: chrony
    spec:
      serviceAccount: ethan
      serviceAccountName: ethan
      containers:
        - name: etcd
          image: 'registry.cj.io:5000/chrony:0.0.1'
          imagePullPolicy: Always
          env:
          - name: NTP_SERVERS
            value: ntp.aliyun.com
          securityContext:
            capabilities:
              add:
              - SYS_TIME
EOF              

启动

1
oc create -f  chrony.yaml

5.3 关键:权限注意事项

这里有一个非常注意事项,实际上Pod在更改系统事件需要设置特殊权限

而OCP的权限设置颗粒度更加细致。

不通过一定的权限操作,Pod会报错,具体如下:

1
2
2020-12-19T14:18:26Z CAP_SYS_TIME not present
2020-12-19T14:18:26Z Fatal error : adjtimex(0x8001) failed : Operation not permitted

解决方式:

采用了OCP的sa,建立了ethan的这个特权账户。因此添加sa。

1
2
3
4
5
oc create sa ethan
serviceaccount/ethan created

oc adm policy add-scc-to-user anyuid -z ethan
clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "ethan"

将原定义的ethan用户添加特权。

1
oadm policy add-scc-to-user privileged -z ethan

同时继续以ethan账户启动。

赋予SYS_TIME的权限,详见上述Pod的yaml文件参数。

1
2
3
4
securityContext:
            capabilities:
              add:
              - SYS_TIME

5.4 测试

手动调整时间

1
2
3
4
5
6
# 查看当前时间
date
Sun Dec 20 11:51:42 CST 2020
# 手动设置时间
date -s "2020-12-20 11:50:00"
Sun Dec 20 11:50:00 CST 2020

查看本机Chrony状态

1
2
3
4
5
6
7
Dec 20 11:44:54 localhost.localdomain chronyd[7267]: Selected source 172.18.1.2
Dec 20 11:44:54 localhost.localdomain chronyd[7267]: System clock wrong by -3.561087 seconds, adjustment started
Dec 20 11:44:50 localhost.localdomain chronyd[7267]: System clock was stepped by -3.561087 seconds
Dec 20 11:50:08 localhost.localdomain chronyd[7267]: Backward time jump detected!
Dec 20 11:50:08 localhost.localdomain chronyd[7267]: Can't synchronise: no selectable sources
Dec 20 11:53:03 localhost.localdomain chronyd[7267]: Selected source 172.18.1.2
Dec 20 11:53:03 localhost.localdomain chronyd[7267]: System clock wrong by 155.561438 seconds, adjustment started

查看源状态,172.18.1.2地址前*表示NTP服务器网络可达。

1
2
3
4
5
[root@localhost yumcache]# chronyc sources –v   
210 Number of sources = 1
MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^* 172.18.1.2                    3   6   377     0  -6171ns[-2296us] +/-   29ms

一般而言都是慢慢调整时间

这里采用立即步进地校正时钟,时间已校正。

1
2
3
4
5
chronyc -a makestep
200 OK

date
Sun Dec 20 12:08:24 CST 2020

5.5 网络拓扑持久化

经过测试验证,目前NTP服务可以为集群内提供服务

为了将NTP服务暴露给集群外提供企业统一的高可用NTP服务。

这里采用NodePort SVC的方式

编写yaml文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
cat > chronysvc.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: chronysvc
spec:
  selector:
    app: chrony
  ports:
  - port: 123
    name: ntp-port
    protocol: UDP
    targetPort: 123
    nodePort: 30123
  type: NodePort
EOF  

启动svc

1
oc create -f chronysvc.yaml

由于NodePort的默认端口在30000-32767之间,因此这里选择的NodePort端口为了便于记忆,选择了30123

6.外部LoadBalance节点

至此,NTP及DNS服务的容器化改造全部完成。

相关服务可以正常在集群内提供服务。

但是存在最后一公里的问题,就是如何暴露服务给集群外提供给企业内部使用。

NTP和DNS服务的服务端口分别是UDP的123和UPD的53。

采用OCP的router和Ingress均无法实现集群对外的4层服务代理。

而采用NodePort,存在端口范围不匹配以及单点故障问题。

理想的架构是在集群外部署企业级的硬件负载均衡器,将下游的UDP的123和UPD的53直接负载均衡转发至所有OCP计算节点的NodePort SVC端口上。

在《4.6 网络拓扑持久化》和《5.5 网络拓扑持久化》章节中,我们通过NodePort SVC,将OPC计算节点的30123和30053进行了暴露,各OCP计算节点的上述端口会通过svc yaml文件中的描述,通过负载均衡的方式将外部流量达到内部Pod的服务端口上。

另外选择多台节点组成LoadBalance集群,这里采用keepalived通过VRRP协议实现高可用,对外提供统一的一个固定VIP地址,对外提供服务。

UDP流量的负载均衡采用Nginx进行代理转发。

TCP流量的负载均衡采用Haproxy进行代理转发。后期会有Servicer mesh以及Envoy教程。

采用Keepalived做双机热备切换,消除单点故障。适合Nginx和Haproxy这种无状态的应用HA。

6.1 keepalived

参考https://www.keepalived.org/index.html

1
2
3
4
5
6
# 下载
wget https://www.keepalived.org/software/keepalived-2.1.5.tar.gz
# 解压缩
tar xf keepalived-2.1.5.tar.gz
# 编译安装
cd keepalived-2.1.5

编译

之前要安装gcc openssl-devel pcre-devel

1
./configure --prefix=/usr/local/keepalived

安装后执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 拷贝安装目录下的二进制文件
ln -s /usr/local/keepalived /usr/local/keepalived

# 建立配置文件目录
mkdir -p /etc/keepalived/


cp /usr/local/keepalived/etc/keepalived/keepalived.conf /etc/keepalived/
cp /usr/local/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
ln -s /usr/local/keepalived/sbin/keepalived /usr/sbin/
# 这个从keepalived源码目录复制,安装目录中没有
cp /root/keepalived-2.1.5/keepalived/etc/init.d/keepalived /etc/init.d/
cp /root/keepalived-2.1.5/keepalived/keepalived.service /etc/systemd/system/

chmod 755 /etc/init.d/keepalived

配置keepalived

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
cat  > /etc/keepalived/keepalived.conf <<EOF

! Configuration File for keepalived
global_defs {
   router_id master01     #主备不需要一样
}
vrrp_instance VI_1 {
    state MASTER             #主的MASTER,备的是BACKUP
    interface ens192          #需要绑定的网卡
    virtual_router_id 50
    priority 100              #主的100,后续可以不一样
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1111
    }
    virtual_ipaddress {
        172.18.1.2            #这个就是VRRP
    }
}

EOF

关键:配置防火墙

1
firewall-cmd --direct --permanent --add-rule ipv4 filter INPUT 0 --in-interface  ens192 --destination 224.0.0.18 --protocol vrrp -j ACCEPT

keepalived比较简单,这里不详细展开。

6.2 Nginx配置UDP转发

6.2.1 Nginx编译

Nginx默认是不支持UDP转发的,需要重新手动编译

参考http://nginx.org/

1
2
3
wget wget http://nginx.org/download/nginx-1.18.0.tar.gz
tar xvf nginx-1.18.0.tar.gz 
cd nginx-1.18.0

开始编译

编译前务必安装

1
yum -y install proc* openssl* pcre*

正式编译

1
./configure  --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-threads --with-stream --with-stream_ssl_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic'

返回信息

从这里的返回信息可以看到关键配置文件的路径

例如/etc/nginx/nginx.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
checking for ioctl(FIONBIO) ... found
checking for ioctl(FIONREAD) ... found
checking for struct tm.tm_gmtoff ... found
checking for struct dirent.d_namlen ... not found
checking for struct dirent.d_type ... found
checking for sysconf(_SC_NPROCESSORS_ONLN) ... found
checking for sysconf(_SC_LEVEL1_DCACHE_LINESIZE) ... found
checking for openat(), fstatat() ... found
checking for getaddrinfo() ... found
checking for PCRE library ... found
checking for PCRE JIT support ... found
checking for OpenSSL library ... found
checking for zlib library ... found
creating objs/Makefile

Configuration summary
  + using threads
  + using system PCRE library
  + using system OpenSSL library
  + using system zlib library

  nginx path prefix: "/etc/nginx"
  nginx binary file: "/usr/sbin/nginx"
  nginx modules path: "/etc/nginx/modules"
  nginx configuration prefix: "/etc/nginx"
  nginx configuration file: "/etc/nginx/nginx.conf"
  nginx pid file: "/var/run/nginx.pid"
  nginx error log file: "/var/log/nginx/error.log"
  nginx http access log file: "/var/log/nginx/access.log"
  nginx http client request body temporary files: "/var/cache/nginx/client_temp"
  nginx http proxy temporary files: "/var/cache/nginx/proxy_temp"
  nginx http fastcgi temporary files: "/var/cache/nginx/fastcgi_temp"
  nginx http uwsgi temporary files: "/var/cache/nginx/uwsgi_temp"
  nginx http scgi temporary files: "/var/cache/nginx/scgi_temp"

./configure: warning: the "--with-ipv6" option is deprecated

安装

 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
40
41
42
43
44
45
46
47
make
make install

# 返回相关信息
make -f objs/Makefile installmake[1]: Entering directory `/root/nginx-1.18.0'
test -d '/etc/nginx' || mkdir -p '/etc/nginx'
test -d '/usr/sbin' \
        || mkdir -p '/usr/sbin'
test ! -f '/usr/sbin/nginx' \
        || mv '/usr/sbin/nginx' \
                '/usr/sbin/nginx.old'
cp objs/nginx '/usr/sbin/nginx'
test -d '/etc/nginx' \
        || mkdir -p '/etc/nginx'
cp conf/koi-win '/etc/nginx'
cp conf/koi-utf '/etc/nginx'
cp conf/win-utf '/etc/nginx'
test -f '/etc/nginx/mime.types' \
        || cp conf/mime.types '/etc/nginx'
cp conf/mime.types '/etc/nginx/mime.types.default'
test -f '/etc/nginx/fastcgi_params' \
        || cp conf/fastcgi_params '/etc/nginx'
cp conf/fastcgi_params \
        '/etc/nginx/fastcgi_params.default'
test -f '/etc/nginx/fastcgi.conf' \
        || cp conf/fastcgi.conf '/etc/nginx'
cp conf/fastcgi.conf '/etc/nginx/fastcgi.conf.default'
test -f '/etc/nginx/uwsgi_params' \
        || cp conf/uwsgi_params '/etc/nginx'
cp conf/uwsgi_params \
        '/etc/nginx/uwsgi_params.default'
test -f '/etc/nginx/scgi_params' \
        || cp conf/scgi_params '/etc/nginx'
cp conf/scgi_params \
        '/etc/nginx/scgi_params.default'
test -f '/etc/nginx/nginx.conf' \
        || cp conf/nginx.conf '/etc/nginx/nginx.conf'
cp conf/nginx.conf '/etc/nginx/nginx.conf.default'
test -d '/var/run' \
        || mkdir -p '/var/run'
test -d '/var/log/nginx' \
        || mkdir -p '/var/log/nginx'
test -d '/etc/nginx/html' \
        || cp -R html '/etc/nginx'
test -d '/var/log/nginx' \
        || mkdir -p '/var/log/nginx'
make[1]: Leaving directory `/root/nginx-1.18.0'

通过配置stream模块来解决UDP的负载均衡。

UDP负载均衡解决了两个关键点:高可用性和横向扩展。UDP设计是不保证端至端传送数据的,因此需要在客户端软件来处理网络级错误和重传机制。

6.2.1 配置Nginx

验证Nginx配置文件是否正确

顺便查看手动编译Nginx采用的模块

1
2
3
4
5
$nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: [emerg] getpwnam("nginx") failed
# 这里是因为进程默认采用nginx用户,实际未添加
nginx: configuration file /etc/nginx/nginx.conf test failed

注意其中error_logpid等字段内容文件是否存在,可参考make install输出结果中的相关路径信息

 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
cat <<EOF > /etc/nginx/nginx.conf

user root;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

pid        /var/run/nginx.pid;


events {
    worker_connections  20480;
}

stream{    
    upstream ntp {
      server 172.18.1.47:30123;
      server 172.18.1.48:30123;
      server 172.18.1.49:30123;
        
      # check interval=10000 rise=2 fall=3 timeout=2000 default_down=false type=udp;
    }
    upstream dns2 {
      server 172.18.1.1:53;
    }
    upstream dns {
      server 172.18.1.47:30053;
      server 172.18.1.48:30053;
      server 172.18.1.49:30053;
    }
    server {
      listen 123 udp;
      proxy_pass ntp;
      proxy_responses 1;
      #proxy_bind $remote_addr transparent;
      proxy_timeout 20s;
    }
    server {
      listen 53 udp;
      proxy_pass dns;
      proxy_responses 1;
      #proxy_bind $remote_addr transparent;
      proxy_timeout 20s;
    }
    server {
      listen 153 udp;
      proxy_pass dns;
      proxy_responses 1;
      #proxy_bind $remote_addr transparent;
      proxy_timeout 20s;
    }
}
EOF

注意:新版本Nginx的健康检查没有了,需要手动添加模块。

验证

查看端口绑定情况,发现由nginx进行了绑定

1
2
3
4
5
6
$ss -ulnp | grep 123
UNCONN   0    0     *:111   *:*  users:(("rpcbind",pid=23123,fd=5),("systemd",pid=1,fd=100))
UNCONN   0    0     *:123   *:*  users:(("nginx",pid=9032,fd=5),("nginx",pid=9031,fd=5))
UNCONN   0    0     *:826   *:*  users:(("rpcbind",pid=23123,fd=10))
UNCONN   0    0    :::111  :::*  users:(("rpcbind",pid=23123,fd=7),("systemd",pid=1,fd=102))
UNCONN   0    0    :::826  :::*  users:(("rpcbind",pid=23123,fd=11))

6.3 Haproxy

参考https://www.haproxy.org/

下载

1
wget https://www.haproxy.org/download/2.4/src/devel/haproxy-2.4-dev3.tar.gz

解压缩

1
2
tar xvf haproxy-2.4-dev3.tar.gz
cd haproxy-2.4-dev3

编译

1
2
3
4
5
6
# 确保openssl安装
yum install -y openssl

make TARGET=linux-glibc PREFIX=/usr/local/haproxy

make install PREFIX=/usr/local/haproxy

注意

1
2
3
4
5
6
# 如果用下面,将提示错误
make TARGET=linux26  PREFIX=/usr/local/haproxy

# Target 'linux26' was removed from HAProxy 2.0 due to being irrelevant and
often wrong. Please use 'linux-glibc' instead or define your custom target
by checking available options using 'make help TARGET=<your-target>'.

安装完毕后

1
2
3
4
5
useradd haproxy
mkdir -p /etc/haproxy/
vi /etc/haproxy/haproxy.cfg
cp examples/haproxy.init /etc/init.d/haproxy
/usr/local/haproxy/sbin/haproxy -f /etc/haproxy/haproxy.cfg

检查配置文件自动生成和系统haproxy用户

1
2
3
4
5
6
# 验证配置文件
$ll /etc/haproxy/haproxy.cfg 
-rw-r--r--. 1 root root 3142 Mar 28  2019 /etc/haproxy/haproxy.cfg
# 验证用户
$cat /etc/passwd | grep haproxy
haproxy❌188:188:haproxy:/var/lib/haproxy:/sbin/nologin

————————到此安装haproxy完成——————

HAProxy配置文件详解 其配置文件主要由五个部分组成,分别为global部分,defaults部分,frontend部分,backend部分,liste部分。 1)global部分 用于设置全局配置参数 2) defaults部分 默认参数的配置部分。 3) frontend部分 用于设置接收用户请求的前端虚拟节点。frontend可以根据ACL规则直接指定要使用的后端backend。 4) backend部分 用于设置集群后端服务集群的配置,也就是添加一组真实服务器,以处理前端用户的请求。

  1. listen部分此部分是frontend和backend部分的结合体。配置此部分不需要在配置

配置前务必要关闭SELINUX

1
2
sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config
reboot

配置完成后

1
2
$systemctl restart haproxy
$systemctl status haproxy

验证

1
2
3
4
5
6
ss -lutnp | grep haproxy
tcp    LISTEN     0      128       *:9000     *:*    users:(("haproxy",pid=23559,fd=5))
tcp    LISTEN     0      128       *:6443     *:*    users:(("haproxy",pid=23559,fd=7))
tcp    LISTEN     0      128       *:80       *:*    users:(("haproxy",pid=23559,fd=9))
tcp    LISTEN     0      128       *:443      *:*    users:(("haproxy",pid=23559,fd=10))
tcp    LISTEN     0      128       *:22623    *:*    users:(("haproxy",pid=23559,fd=8))

防火墙配置

1
2
3
4
5
6
7
firewall-cmd --zone=public --add-port=6443/tcp --permanent
firewall-cmd --zone=public --add-port=443/tcp --permanent
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --zone=public --add-port=22623/tcp --permanent
firewall-cmd --zone=public --add-port=53/udp --permanent
firewall-cmd --zone=public --add-port=123/udp --permanent
firewall-cmd --reload

至此(DNS+NTP部署前篇)完毕

后续将做Quay分布式镜像仓库及Ceph在OCP应用专题教程。

By 2021.1.1 张诚