Kubernetes的etcd多节点扩容实战技巧

原创
2018/12/19 11:25
阅读数 1.9W

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

  • 注意:
    • 因为扩容过程中,需要将原来的etcd库删除,会导致kubernetes集群的master节点信息丢失
      • 因此在扩容之前,建议使用etcdctl snapshot命令进行备份。或者,另建etcd节点,将原来的数据传送过去。
    • 如果出现“ID mismatch”等错误,整个etcd集群将无法连接,后续的member remove等操作都无法进行,只能全部废弃。
    • 可以将/var/lib/etcd目录和etcd容器实例全部删除。重启动kubelet服务后可以恢复,但是里面存储的数据将会全部丢失。
  • 提示:

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的容器镜像。

! 参照上面的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进行多节点部署,实现状态控制和调度功能的多节点迁移,即可完整实现容错机制。

参考:

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部