在《Kubernetes探秘-多master节点容错部署》中介绍了通过部署多个主节点来提高Kubernetes的容错能力。其中,最为关键是存储集群控制数据的etcd服务必须在多个副节点间实时同步,而kube-apiserver通过keepalived进行IP主地址的切换。在《Kubernetes探秘-etcd节点和实例扩容》中已经详细介绍了etcd多节点扩容的步骤,但在实际操作中发现,必需要遵循一定的操作顺序、注意细节,否则会导致扩容失败,而且很容易造成整个etcd集群无法访问。这里将etcd集群扩容的实际操作经验整理出来分享。

b052ca4b3519b6f760be87606b11b5f0.png

注意:

因为扩容过程中,需要将原来的etcd库删除,会导致kubernetes集群的master节点信息丢失。

因此在扩容之前,建议使用etcdctl snapshot命令进行备份。或者,另建etcd节点,将原来的数据传送过去。

如果出现“ID mismatch”等错误,整个etcd集群将无法连接,后续的member remove等操作都无法进行,只能全部废弃。

可以将/var/lib/etcd目录和etcd容器实例全部删除。重启动kubelet服务后可以恢复,但是里面存储的数据将会全部丢失。

提示:

在宿主机安装etcd-client,就可以直接在宿主机操作容器中的etcd服务,需要提供证书参数(参见下面的命令实例)。

1、生成证书文件

全部使用https进行etcd集群的连接,需要生成和配置证书,可以参考《Kubernetes探秘-etcd节点和实例扩容》里的方法,这些证书文件需要复制到每一个节点的/etc/kubernetes/pki目录下。需要全部使用固定IP地址,在Ubuntu 18.06中使用 Netplan进行配置(参考《Ubuntu 18.04设置静态IP》),使用 sudo netplan apply让其立即生效(需要稍等会儿配置完毕)。

上传目录,示例:

sudo scp -r root@10.1.1.201:/etc/kubernetes/pki /etc/kubernetes

2、安装Kubernetes主节点

2.1 安装主节点

我们首先安装一个Kubernetes主节点,其它节点配置参数将从其衍生出来。参考:

准备工作完成后,使用下面的命令安装Kubernetes的单实例Master节点。

sudo kubeadm init --kubernetes-version=v1.13.1 --apiserver-advertise-address=10.1.1.199

因为我的机器上有多块网卡,使用 --apiserver-advertise-address=10.1.1.199 指定apiserver的服务地址,这个地址也是keepalived的虚拟IP地址(需要提前安装,参考《Keepalived快速使用》),将会在遇故障时在多个节点间自动漂移该主地址,使其它节点可以访问到。

输入 kubectl get pod --all-namespaces检查该单实例集群是否运行正常。

2.2 备份etcd数据库

主节点已经安装了一个etcd的实例,并且存放了集群运行的最基础参数。为了防止etcd集群扩容过程数据丢失,我们将其备份。具体操作参见《Kubernetes的etcd数据查看和迁移》。需要注意的是,etcd api2和api3的备份和恢复方法不同,因为从Kubernetes 1.13.0开始已经使用API3,下面介绍的都是API3的方法。

创建snapshot

ETCDCTL_API=3 etcdctl --endpoints=https://[10.1.1.202]:2379 \

--cacert=/etc/kubernetes/pki/etcd-certs/ca.pem \

--cert=/etc/kubernetes/pki/etcd-certs/client.pem \

--key=/etc/kubernetes/pki/etcd-certs/client-key.pem \

snapshot save /home/supermap/openthings/etcd$(date +%Y%m%d_%H%M%S)_snapshot.db

恢复snapshot restore:

ETCDCTL_API=3 etcdctl --endpoints=https://[10.1.1.199]:2379 \

--cacert=/etc/kubernetes/pki/etcd-certs/ca.pem \

--cert=/etc/kubernetes/pki/etcd-certs/client.pem \

--key=/etc/kubernetes/pki/etcd-certs/client-key.pem \

--data-dir=/var/lib/etcd \

--initial-advertise-peer-urls=https://10.1.1.199:2380 \

--initial-cluster=podc01=https://10.1.1.199:2380 \

--initial-cluster-token=etcd-cluster \

--name=podc01 \

snapshot restore /home/supermap/etcd_snapshot.db

上面的备份文件名可以自己起,恢复时能对上就行。

2.3 更新主节点的etcd配置参数

为什么需要更新?

集群apiserver使用地址为10.1.1.199需要在多节点漂移,本地的etcd实例使用10.1.1.201固定地址。

主节点etcd实例初始使用199地址,与最终的201不一致,这将自动生成两个不同的etcd peerID,出现“peerID mismatch”错误,引起整个etcd集群无法访问。

10.1.1.199的PeerID已写入/var/lib/etcd数据库中,只能在etcd服务可用时通过服务接口member update修改。

包括:

/var/lib/etcd目录下的数据,使用etcdctl member update命令来进行。

以及修改/etc/kubernetes/manifests/etcd.yaml配置参数,由kubelet来加载运行etcd的实例。

首先,检查etcd集群的运行状态:

ETCDCTL_API=3 etcdctl --endpoints=https://[10.1.1.199]:2379 \

--cacert=/etc/kubernetes/pki/etcd-certs/ca.pem \

--cert=/etc/kubernetes/pki/etcd-certs/client.pem \

--key=/etc/kubernetes/pki/etcd-certs/client-key.pem \

endpoint status -w table

然后,更新etcd实例的peer-urls:

ETCDCTL_API=3 etcdctl --cacert=/etc/kubernetes/pki/etcd-certs/ca.pem \

--cert=/etc/kubernetes/pki/etcd-certs/client.pem \

--key=/etc/kubernetes/pki/etcd-certs/client-key.pem \

--endpoints=https://[10.1.1.199]:2379 \

member update podc01 --peer-urls=https://10.1.1.201:2380

第三,修改etcd实例的client-urls。

此时,停止kubelet服务。

sudo systemctl stop kubelet

编辑/etc/kubernetes/manifests/etcd.yaml文件.

apiVersion: v1

kind: Pod

metadata:

annotations:

scheduler.alpha.kubernetes.io/critical-pod: ""

creationTimestamp: null

labels:

component: etcd

tier: control-plane

name: etcd

namespace: kube-system

spec:

containers:

- command:

- etcd

- --advertise-client-urls=https://10.1.1.201:2379

- --cert-file=/etc/kubernetes/pki/etcd/server.pem

- --client-cert-auth=true

- --data-dir=/var/lib/etcd

- --initial-advertise-peer-urls=https://10.1.1.201:2380

- --initial-cluster=podc01=https://10.1.1.201:2380

- --key-file=/etc/kubernetes/pki/etcd/server-key.pem

- --listen-client-urls=https://127.0.0.1:2379,https://10.1.1.201:2379

- --listen-peer-urls=https://10.1.1.201:2380

- --name=podc01

- --peer-cert-file=/etc/kubernetes/pki/etcd/peer1.pem

- --peer-client-cert-auth=true

- --peer-key-file=/etc/kubernetes/pki/etcd/peer1-key.pem

- --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem

- --snapshot-count=10000

- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem

image: k8s.gcr.io/etcd:3.2.24

imagePullPolicy: IfNotPresent

livenessProbe:

exec:

command:

- /bin/sh

- -ec

- ETCDCTL_API=3 etcdctl --endpoints=https://[127.0.0.1]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.pem

--cert=/etc/kubernetes/pki/etcd/client.pem --key=/etc/kubernetes/pki/etcd/client-key.pem

get foo

failureThreshold: 8

initialDelaySeconds: 15

timeoutSeconds: 15

name: etcd

resources: {}

volumeMounts:

- mountPath: /var/lib/etcd

name: etcd-data

- mountPath: /etc/kubernetes/pki/etcd

name: etcd-certs

hostNetwork: true

priorityClassName: system-cluster-critical

volumes:

- hostPath:

path: /etc/kubernetes/pki/etcd-certs

type: DirectoryOrCreate

name: etcd-certs

- hostPath:

path: /var/lib/etcd

type: DirectoryOrCreate

name: etcd-data

status: {}

此时,重启kubelet服务。

sudo systemctl start kubelet

检查一下etcd的服务状态:

ETCDCTL_API=3 etcdctl --endpoints=https://[10.1.1.201]:2379 --cacert=/etc/kubernetes/pki/etcd-certs/ca.pem --cert=/etc/kubernetes/pki/etcd-certs/client.pem --key=/etc/kubernetes/pki/etcd-certs/client-key.pem endpoint status -w table

注意这里的变化:--endpoints=https://[10.1.1.201]:2379,已经完成了地址切换。

2.4 修改 api-server.yaml

修改/etc/kubernetes/manifests/api-server.yaml文件,如下:

# - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt

# - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt

# - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key

# - --etcd-servers=https://127.0.0.1:2379

- --etcd-cafile=/etc/kubernetes/pki/etcd-certs/ca.pem

- --etcd-certfile=/etc/kubernetes/pki/etcd-certs/client.pem

- --etcd-keyfile=/etc/kubernetes/pki/etcd-certs/client-key.pem

- --etcd-servers=https://10.1.1.201:2379

将上面的新的etcd服务地址配置给kube-apiserver。

重启 kubelet,如下:

#重新启动kubelet服务。

sudo systemctl restart kubelet

#查看运行的容器实例。

docker ps

#查看所有运行的容器实例。

#包含已停止的,如果etcd启动失败退出,可通过该命令查看。

docker ps -a

#查看特定容器实例的日志。

docker logs idxxxx

再次检查etcd状态:

ETCDCTL_API=3 etcdctl --endpoints=https://[10.1.1.201]:2379 \

--cacert=/etc/kubernetes/pki/etcd-certs/ca.pem \

--cert=/etc/kubernetes/pki/etcd-certs/client.pem \

--key=/etc/kubernetes/pki/etcd-certs/client-key.pem \

endpoint status -w table

检查kubernetes集群状态(kubectl get pod --all-namespaces)。

2.5 注意事项

Kubernetes的kubeadm工具安装的单节点etcd实例无法直接扩展到集群,需要修改配置参数。

任何时候(以下同)修改etcd文件时,都应该停止kubelet集群,手工终止运行的etcd实例。

否则,如果“脏”信息被写入etcd数据库,将导致etcd服务全部无法访问。

在出现“ID mismatch”错误时:

如果直接删除/var/lib/etcd的内容,将丢失Kubernetes集群的配置信息。

即便etcd集群运行成功,后续也无法访问K8s服务。

可以通过备份snapshot store来重置原始数据库。

3、扩展etcd实例到多个节点

下面将节点逐个加入(etcd节点的IP地址必须在前面的证书生成时加入)。

我使用Kubernetes的kubelet来托管etcd的运行(也可以部署为独立的系统服务,如使用systemd)。

3.1 添加为k8s工作节点

使用kubeadm join加入新的节点(将会创建kubelet基础服务,而且etcd节点和kubernetes节点同时可用)。在主节点获取添加命令,如下:

#在主节点上执行

kubeadm token create --print-join-command

3.2 复制pki证书

直接将master节点的/etc/kubernetes/pki目录复制到子节点。如下:

#在子节点上执行

sudo scp -r root@10.1.1.201:/etc/kubernetes/pki /etc/kubernetes/

其中,etcd的证书放在/etc/kubernetes/pki/etc-certs中,在下面的etcd.yaml文件将按照此设置。

3.3 停止kubelet服务

命令为:

sudo systemctl stop kubelet

如果不是第一次启动,使用docker ps查看etcd实例是否已经运行。

如果已经有etcd实例运行,使用docker rm -f idxxx将该实例彻底删除。

3.4 添加节点到etcd集群

使用etcdctl的member add命令添加节点:

#在子节点上执行,将子节点peer-urls添加到etcd集群中。

ETCDCTL_API=3 etcdctl \

--cacert=/etc/kubernetes/pki/etcd-certs/ca.pem \

--cert=/etc/kubernetes/pki/etcd-certs/client.pem \

--key=/etc/kubernetes/pki/etcd-certs/client-key.pem \

--endpoints=https://[10.1.1.201]:2379 \

member add podc02 --peer-urls=https://10.1.1.202:2380

此时,etcdctl member list查看成员为unstarted状态。命令如下:

ETCDCTL_API=3 etcdctl --endpoints=https://[10.1.1.201]:2379 \

--cacert=/etc/kubernetes/pki/etcd-certs/ca.pem \

--cert=/etc/kubernetes/pki/etcd-certs/client.pem \

--key=/etc/kubernetes/pki/etcd-certs/client-key.pem \

member list -w table

在编辑etcd.yaml过程中,保持kubelet不要启动(否则触发etcd实例启动,如果写入错误数据到etcd集群,将可能导致集群无法访问)。

3.5 复制etcd.yaml

将etcd.yaml文件放入各个子节点的/etc/kubernetes/manifests目录下,跟master节点一样,然后sudo systemctl restart kubelet重启kubelet服务,kubelet启动时将会自动启动/etc/kubernetes/manifests下的所有*.yaml实例为静态pod(静态pod在Dashboard删除时会删除当前的运行实例,然后被kubelet自动重启,不会永久删除)。

复制etcd.yaml文件,如下:

#在子节点上执行

sudo scp -r root@10.1.1.201:/etc/kubernetes/manifests/etcd.yaml /etc/kubernetes/manifests/

编辑 etcd.conf 文件,如下:

sudo nano /etc/kubernetes/manifest/etcd.yaml

内容如下:

#子节点podc02上的/etc/kubernetes/manifests/etcd.yaml

apiVersion: v1

kind: Pod

metadata:

annotations:

scheduler.alpha.kubernetes.io/critical-pod: ""

creationTimestamp: null

labels:

component: etcd

tier: control-plane

name: etcd

namespace: kube-system

spec:

containers:

- command:

- etcd

- --advertise-client-urls=https://10.1.1.202:2379

- --cert-file=/etc/kubernetes/pki/etcd/server.pem

- --client-cert-auth=true

- --data-dir=/var/lib/etcd

- --initial-advertise-peer-urls=https://10.1.1.202:2380

- --initial-cluster=podc01=https://10.1.1.201:2380,podc02=https://10.1.1.202:2380

- --initial-cluster-token=etcd-cluster

- --initial-cluster-state=existing

- --key-file=/etc/kubernetes/pki/etcd/server-key.pem

- --listen-client-urls=https://127.0.0.1:2379,https://10.1.1.202:2379

- --listen-peer-urls=https://10.1.1.202:2380

- --name=podc02

- --peer-cert-file=/etc/kubernetes/pki/etcd/peer2.pem

- --peer-client-cert-auth=true

- --peer-key-file=/etc/kubernetes/pki/etcd/peer2-key.pem

- --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem

- --snapshot-count=10000

- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.pem

image: k8s.gcr.io/etcd:3.2.24

imagePullPolicy: IfNotPresent

livenessProbe:

exec:

command:

- /bin/sh

- -ec

- ETCDCTL_API=3 etcdctl --endpoints=https://[127.0.0.1]:2379 --cacert=/etc/kubernetes/pki/etcd/ca.pem

--cert=/etc/kubernetes/pki/etcd/client.pem --key=/etc/kubernetes/pki/etcd/client-key.pem

get foo

failureThreshold: 8

initialDelaySeconds: 15

timeoutSeconds: 15

name: etcd

resources: {}

volumeMounts:

- mountPath: /var/lib/etcd

name: etcd-data

- mountPath: /etc/kubernetes/pki/etcd

name: etcd-certs

hostNetwork: true

priorityClassName: system-cluster-critical

volumes:

- hostPath:

path: /etc/kubernetes/pki/etcd-certs

type: DirectoryOrCreate

name: etcd-certs

- hostPath:

path: /var/lib/etcd

type: DirectoryOrCreate

name: etcd-data

status: {}

提示:

修改的地方:

修改:本地IP地址全部设为10.1.1.202。

修改:--name=podc02

修改:--initial-cluster=podc01=https://10.1.1.201:2380,podc02=https://10.1.1.202:2380

添加:initial-cluster-token=etcd-cluster、- --initial-cluster-state=existing两个参数。

修改:证书格式为*.pem,cfssl生成的格式与kubernetes原始的有些不同,用法一样。

修改:host-path的证书目录指向到/etc/kubernetes/pki/etcd-certs。

仔细检查设置参数是否有错。

3.6 重启kubelet服务

确认etcd参数正确,现在可以启动kubelet服务了。命令为:

sudo systemctl start kubelet

使用docker ps查看etcd实例是否已经运行。

如果etcd实例未运行,使用docker ps -a查看未运行的etcd实例ID。

使用命令 docker logs idxxx 查看etcd实例的日志信息,根据信息修改etcd.yaml重新启动kubelet。

命令为:sudo systemctl restart kubelet

⚠️注意:该节点必须已有etcd的容器镜像。

参考《Kubernetes镜像的离线保存和载入》将与主节点版本一致的镜像传送到该节点。

! 参照上面的3.1-3.6方法将所有etcd集群子节点加入到集群中(注意严格按照顺序操作)。

3.7 查看etcd集群信息

可以在主机安装etcd-client,然后etcdctl可以直接连接到容器中的etcd服务。

查看etcd集群成员列表:

# etcd cluster member list

echo ""

echo "============================="

echo "+ etcd cluster member list..."

ETCDCTL_API=3 etcdctl \

--cacert=/etc/kubernetes/pki/etcd-certs/ca.pem \

--cert=/etc/kubernetes/pki/etcd-certs/client.pem \

--key=/etc/kubernetes/pki/etcd-certs/client-key.pem \

member list -w table --endpoints=https://[10.1.1.201]:2379

输出如下:

=============================

+ etcd cluster member list...

+------------------+---------+--------+-------------------------+-------------------------+

| ID | STATUS | NAME | PEER ADDRS | CLIENT ADDRS |

+------------------+---------+--------+-------------------------+-------------------------+

| 741ead392743e35 | started | podc02 | https://10.1.1.202:2380 | https://10.1.1.202:2379 |

| 72077d56570df47f | started | podc01 | https://10.1.1.201:2380 | https://10.1.1.201:2379 |

| dfc70cacefa4fbbb | started | podc04 | https://10.1.1.204:2380 | https://10.1.1.204:2379 |

| e3ecb8f6d5866785 | started | podc03 | https://10.1.1.203:2380 | https://10.1.1.203:2379 |

+------------------+---------+--------+-------------------------+-------------------------+

查看etcd集群成员状态:

# member list, local

echo ""

echo "========================="

echo "+ etcd cluster status... "

ETCDCTL_API=3 etcdctl \

--cacert=/etc/kubernetes/pki/etcd-certs/ca.pem \

--cert=/etc/kubernetes/pki/etcd-certs/client.pem \

--key=/etc/kubernetes/pki/etcd-certs/client-key.pem \

--endpoints=https://[10.1.1.201]:2379,https://[10.1.1.202]:2379,https://[10.1.1.203]:2379,https://[10.1.1.204]:2379 \

endpoint status -w table

输出如下:

=========================

+ etcd cluster status...

+---------------------------+------------------+---------+---------+-----------+-----------+------------+

| ENDPOINT | ID | VERSION | DB SIZE | IS LEADER | RAFT TERM | RAFT INDEX |

+---------------------------+------------------+---------+---------+-----------+-----------+------------+

| https://[10.1.1.201]:2379 | 72077d56570df47f | 3.2.24 | 4.2 MB | true | 1875 | 253980 |

| https://[10.1.1.202]:2379 | 741ead392743e35 | 3.2.24 | 4.2 MB | false | 1875 | 253980 |

| https://[10.1.1.203]:2379 | e3ecb8f6d5866785 | 3.2.24 | 4.2 MB | false | 1875 | 253980 |

| https://[10.1.1.204]:2379 | dfc70cacefa4fbbb | 3.2.24 | 4.2 MB | false | 1875 | 253980 |

+---------------------------+------------------+---------+---------+-----------+-----------+------------+

3.8 修改etcd节点的api-server.yaml

修改/etc/kubernetes/manifests/api-server.yaml文件,如下:

# - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt

# - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt

# - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key

# - --etcd-servers=https://127.0.0.1:2379

- --etcd-cafile=/etc/kubernetes/pki/etcd-certs/ca.pem

- --etcd-certfile=/etc/kubernetes/pki/etcd-certs/client.pem

- --etcd-keyfile=/etc/kubernetes/pki/etcd-certs/client-key.pem

- --etcd-servers=https://10.1.1.201:2379

将上面的新的etcd服务地址配置给kube-apiserver。

⚠️提示:

etcd集群要么一个节点,要么至少三个节点,可以对一个节点失败提供容错。

etcd集群如果只有两个节点,一个节点失败时,整个集群将无法访问,即使还有一个节点可用。

下一步:

再配合keepalived的虚拟IP漂移功能,节点故障时主IP转移到子节点上的apiserver服务,并使用本地的etcd实例访问存储。

此时,主节点的状态控制和调度功能在子节点上还没有,因此无法使用。

还需要对kube-control-manager和kube-scheduler进行多节点部署,实现状态控制和调度功能的多节点迁移,即可完整实现容错机制。

参考:

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐