亚马逊AWS官方博客

使用Kubeadm在亚马逊云科技国内区域自建Kubernetes集群 (一) 自建Kubernetes集群和挂载持久化存储

越来越多的用户正尝试构建以应用为中心的Kubernetes平台,比如要做istio的流量管理,Kubernetes控制器只能提供自身的运行状态,这个时候如果暴露出一个接口告诉使用方这个能力是否存在,借助Kubernetes内置的控制器功能是无法实现的。这时可以自己写一套控制器,然后用Kubernetes原生的方式去融入到istio之中,这样不但好用,而且也增强了稳定性,那么构建一套Kubernetes集群是打造平台的第一步。

1. 方案概述

本文将以常用的工具Kubeadm,自建一套Kubernetes 1.19.2集群,通过flannel网络,在Ubuntu 18.04服务器上设置一个Kubernetes集群,其中包含2个工作节点和1个主节点,并介绍如何使用EBS和EFS持久性存储。

在创建集群之前,简要地了解一些术语,API服务器、etcd、控制器管理器和调度器是主节点的组件,Docker、Kubelet 服务和Kubernetes代理服务是工作节点的组件。本文将不再讨论这些组件,可以访问Kubernetes的官方网站进行了解。

Kubeadm 是一个为创建Kubernetes集群提供“Kubeadm init” 和 “Kubeadm join”的工具。Kubeadm也是官方推荐的最小化部署 Kubernetes 集群的最佳实践,不仅能部署Kubernetes集群,还能方便的管理集群,比如集群的升级、降级、集群初始化配置等操作。

2. 集群配置规划

节点名称 系统版本

配置

(选用t2实例)

内网IP

可用区

(按多可用区规划)

master Ubuntu 18.04

t2.medium

2C/4G/20G

10.28.6.94 cn-northwest-1a
node01 Ubuntu 18.04

t2.medium

2C/4G/20G

10.28.6.83 cn-northwest-1a
node02 Ubuntu 18.04

t2.medium

2C/4G/20G

10.28.6.176 cn-northwest-1b

Mgmt

(管理节点需关联一个IAM Role,对应3个策略为:AmazonEC2FullAccess,

IAMFullAccess,AmazonVPCFullAccess)

Amazon Linux 2

t2.small

1C/2G/20G

172.31.38.210 cn-northwest-1c

 3. 预期目标

  1. 在所有节点上安装Kubeadm和Docker
  2. 部署Kubernetes Master,容器网络插件
  3. 部署Kubernetes Worker,并将节点加入Kubernetes集群中
  4. 部署EBS并测试
  5. 部署EFS并测试

4. 部署架构图

架构由一台Master,两台Node构成。Kubelet 会定时从节点上获取 pod/container的期望状态,包括运行什么容器、副本数量、网络和存储的配置等,并调用 api server以达到这个状态,如下图示:

在云上的架构设计部分在此不做讨论,可参考亚马逊云科技的EKS建议的架构规划部分,链接如下:

https://aws-quickstart.github.io/quickstart-amazon-eks/

https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html

5. 安装Kubeadm和Docker

5.1 亚马逊云科技已经置备了标准的Ubuntu 18.04镜像,可以直接进行部署

5.2 在AWS上部署3台EC2的过程,本文不再赘述,请参考官方手册

https://docs.amazonaws.cn/en_us/AWSEC2/latest/UserGuide/get-set-up-for-amazon-ec2.html

5.3 更改每台服务器的主机名

使用以下命令在分别在3台服务器上设置主机名,在每台服务器上执行以下命令后,重新登录服务器。

sudo hostnamectl set-hostname "master"
sudo hostnamectl set-hostname "node1"
sudo hostnamectl set-hostname "node2"

5.4 获取 Docker gpg 密钥(在所有节点上执行以下命令)

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

5.5 添加Docker存储库(在所有节点上执行以下命令)(该过程大概需要用约15分钟)

sudo add-apt-repository    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
  stable"

5.6 获取 Kubernetes gpg 密钥,使用下边链接(在所有节点上执行以下命令)

curl https://raw.githubusercontent.com/hlmiao/kubernetes/master/apt/doc/apt-key.gpg | apt-key add -

5.7 添加Kubernetes存储库,使用ustc源(在所有节点上执行以下命令)

cat << EOF | sudo tee /etc/apt/sources.list.d/kubernetes.list
deb http://mirrors.ustc.edu.cn/kubernetes/apt kubernetes-xenial main
EOF

 

5.8 更新软件包(在所有节点上执行以下命令)

sudo apt-get update

5.9 安装 Docker、kubelet、kubeadm 和 kubectl(在所有节点上执行以下命令,该过程大概需要用时约3分钟)

sudo apt-get install -y docker-ce=18.06.1~ce~3-0~ubuntu kubelet=1.19.2-00 kubeadm=1.19.2-00 kubectl=1.19.2-00

5.10 将它们保持在当前版本(在所有节点上执行以下命令)

sudo apt-mark hold docker-ce kubelet kubeadm kubectl

5.11 在 sysctl.conf 中添加 iptables 规则(在所有节点上执行以下命令)

echo "net.bridge.bridge-nf-call-iptables=1" | sudo tee -a /etc/sysctl.conf

5.12 启用iptables(在所有节点上执行以下命令)

sudo sysctl -p

6. 创建IAM Role和相关策略

Master和Node在访问ECR(镜像仓库)、EBS 和 EFS 时需要具有的相应的权限,为此要创建一个统一的IAM Role关联到Kubernetes集群上的所有节点。请在一台具有创建IAM Role权限的EC2上执行下列命令,也可用通过图形界面来进行创建。

6.1 创建角色Amazon_k8s(在Mgmt节点执行以下命令)

6.1.1 创建以下 IAM 信任策略文件trust-policy.json(在Mgmt节点执行以下命令)

cat <<EOF > trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com.rproxy.goskope.com.cn"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

6.1.2创建IAM角色Amazon_k8s(在Mgmt节点执行以下命令)

aws iam create-role \
  --role-name Amazon_k8s \
  --assume-role-policy-document file://trust-policy.json

6.2 将刚创建的IAM Role Amazon_k8s关联到集群中的EC2,将下边–instance-id替换为实际在集群中使用的EC2 id(在Mgmt节点执行以下命令)

aws ec2 associate-iam-instance-profile \
    --instance-id i-0ef9c39fc987654321\
    --iam-instance-profile Name="Amazon_k8s"

6.3 将ECR策略关联到角色Amazon_k8s(在Mgmt节点执行以下命令)

将已有的的IAM策略AmazonEC2ContainerRegistryFullAccess附加到角色Amazon_k8s

aws iam attach-role-policy \
  --policy-arn arn:aws-cn:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess\
  --role-name Amazon_k8s

6.4 将EBS策略关联到角色Amazon_k8s(在Mgmt节点执行以下命令)

6.4.1 下载 IAM 策略示例,让节点可以创建与修改 Amazon EBS 卷的权限(在Mgmt节点执行以下命令)

curl -o example-iam-policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-ebs-csi-driver/v0.9.0/docs/example-iam-policy.json

6.4.2 创建IAM 策略Amazon_k8s_EBS_CSI_Driver_Policy(在Mgmt节点执行以下命令)

aws iam create-policy --policy-name Amazon_k8s_EBS_CSI_Driver_Policy --policy-document file://example-iam-policy.json

6.4.3 将新的IAM策略Amazon_k8s_EBS_CSI_Driver_Policy附加到角色Amazon_k8s,替换下边的333222111为自己的Account id(在Mgmt节点执行以下命令)

aws iam attach-role-policy \
  --policy-arn arn:aws-cn:iam::333222111:policy/Amazon_k8s_EBS_CSI_Driver_Policy \
  --role-name Amazon_k8s

6.5 将EFS策略关联到角色Amazon_k8s(在Mgmt节点执行以下命令)

6.5.1 从GitHub下载IAM策略,让节点可以创建与修改EFS卷的权限

curl -o iam-policy-example.json https://raw.githubusercontent.com/kubernetes-sigs/aws-efs-csi-driver/v1.2.0/docs/iam-policy-example.json

6.5.2 创建一个IAM策略Amazon_k8S_EFS_CSI_Driver_Policy

aws iam create-policy \
    --policy-name Amazon_k8s_EFS_CSI_Driver_Policy \
    --policy-document file://iam-policy-example.json

6.5.3 将新的IAM策略附加到角色Amazon_k8s,替换下边的333222111为自己的Account id

aws iam attach-role-policy \
  --policy-arn arn:aws -cn:iam::333222111:policy/Amazon_k8S_EFS_CSI_Driver_Policy \
  --role-name Amazon_k8s

7. 部署Kubernetes Master

7.1 在Ubuntu上安装awscli(该过程大概需要用时约20分钟)

apt install awscli

7.2 此处指定的源由nwcd提供,需要访问Amazon ECR,使用 get-login-password 针对 Amazon ECR 注册表验证 Docker

aws ecr get-login-password --region cn-northwest-1 | docker login --username AWS --password-stdin 048912060910---dkr---ecr---cn-northwest-1.amazonaws.com.rproxy.goskope.com.cn

7.3 初始化集群(仅在Master节点上执行以下命令)

sudo kubeadm init --pod-network-cidr=10.244.0.0/16 --kubernetes-version=v1.19.2 --image-repository 048912060910---dkr---ecr---cn-northwest-1.amazonaws.com.rproxy.goskope.com.cn/gcr/google_containers

7.4 设置本地 kubeconfig(仅在 Master 节点上执行以下命令)

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

7.5 应用 Flannel CNI 网络覆盖(仅在 Master 节点上执行以下命令)

sudo kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

8. 部署Kubernetes Worker

8.1 在Master节点上运行kubeadm token create –print-join-command重新生成token(在Master节点执行以下命令)

kubeadm token create --print-join-command

8.2 将工作节点加入集群(在 Node1 和 Node2 上执行以下命令)

kubeadm join 10.28.6.94:6443 --token qwaunk.28f0s9utxtmng46r \ --discovery-token-ca-cert-hash sha256:7f922ea0e493c3a36c9d62a19cb0fbd6b734f98e5cf32d87eae37714eb78f718

8.3 将Node节点添加到Master过程中,Kubelet服务会遇到下列报错,可忽略并继续安装

[WARNING IsDockerSystemdCheck]: detected "cgroupfs" as the Docker cgroup driver. The recommended driver is "systemd". Please follow the guideat https://kubernetes.io/docs/setup/cri/

8.4 验证worker节点已经成功加入集群(在Master节点执行以下命令)

9. 部署Amazon EBS CSI 驱动程序

国内区域暂不支持在可用区(AZ)内将EBS(Elastic Block Store)卷共享给多个Node,每个可用区内的Node只能使用各自的PV。

9.1 将Amazon EBS Container Storage Interface (CSI) 驱动程序克隆到本地(在Master节点执行以下命令)

git clone https://github.com/kubernetes-sigs/aws-ebs-csi-driver.git

9.2 克隆过程中可能会遇到报错The TLS connection was non-properly terminated

root@master:/tmp# git clone https://github.com/kubernetes-sigs/aws-ebs-csi-driver.git
Cloning into 'aws-ebs-csi-driver'...
fatal: unable to access 'https://github.com/kubernetes-sigs/aws-ebs-csi-driver.git/': gnutls_handshake() failed: The TLS connection was non-properly terminated.

重置下Github代理后再克隆驱动程序,或者直接下载zip压缩包

git config --global  --unset https.https://github.com.proxy

git config --global  --unset http.https://github.com.proxy

9.3 修改kustomization.yaml文件中ECR地址

把Global ECR“602401143452.dkr.ecr.us-west-2.amazonaws.com”替换成国内ECR“961992271922---dkr---ecr---cn-northwest-1.amazonaws.com.rproxy.goskope.com.cn

cd aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/ecr

9.4 应用manifest,部署Amazon EBS CSI驱动程序

kubectl apply -k aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/ecr/

9.5 使用6.1所创建的IAM角色Amazon_k8s的Amazon Resource Name (ARN) 来注释ebs-csi-controller-sa Kubernetes服务账户,替换下边的333222111为自己的Account id

kubectl annotate serviceaccount ebs-csi-controller-sa \
    -n kube-system \
    role-arn=arn:aws-cn:iam::333222111:role/Amazon_k8s

10. 测试 Amazon EBS CSI 驱动程序

Amazon EBS卷是按需预置的,使用动态预置的应用程序对Amazon EBS CSI驱动程序进行测试。

10.1 将工作目录更改为包含Amazon EBS驱动程序测试文件的文件夹,运行以下命令

这个过程会创建测试所需的Kubernetes资源,kubectl命令会创建StorageClass,PersistentVolumeClaim (PVC)和Pod;Pod将引用 PVC;Pod创建时对Amazon EBS卷进行预置。

cd aws-ebs-csi-driver/examples/kubernetes/dynamic-provisioning/
kubectl apply -f specs/

10.2 查看ebs-sc存储类

kubectl describe storageclass ebs-sc

10.3 在默认命名空间中查看Pod状态,等待app Pod的状态变为Running

kubectl get pods --watch

10.4 查看引用 PVC 的 Pod 后,创建的持久性卷

kubectl get pv

10.5 查看有关持久性卷的信息,把10.4步骤中的PV Name输入到命令行

kubectl describe pv pvc-490ff318-fc22-4326-aa47-236a6eae683f

10.6 验证Pod是否正在向卷写入数据

命令会输出存储在/data/out.txt文件中的当前日期和时间。

kubectl exec -it app -- cat /data/out.txt

11. 备份Amazon EBS卷

11.1 集群中每个节点上的EBS卷,都会打上相应的标签,如下图所示:

比如CSIVolumeName: pvc-490ff318-fc22-4326-aa47-236a6eae683f

CSIVolumeName pvc-490ff318-fc22-4326-aa47-236a6eae683f
ebs.csi.aws.com/cluster TRUE
kubernetes.io/created-for/pv/name pvc-490ff318-fc22-4326-aa47-236a6eae683f
kubernetes.io/created-for/pvc/name ebs-claim
kubernetes.io/created-for/pvc/namespace default

在卷管理页面下,搜索这个pvc,可以看到详细信息

11.2 在创建快照备份策略时,可以选择有相同的标签的卷

比如11.1步骤中的ebs.csi.aws.com/cluster true,这样该备份策略可以把所有Pod创建的EBS卷都关联上

11.3 设置快照的备份时间和保留份数

12. 部署Amazon EFS CSI驱动程序

为了让应用程序Pod可以运行在任何可用区中(AZ),那么需要在所有Pod之间共享一个卷,即使用EFS(Elastic File System)作为PV,如下图示:

12.1 使用IAM角色Amazon_k8s的 ARN来注释Kubernetes服务账户

cat << EOF > efs-service-account.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: efs-csi-controller-sa
  namespace: kube-system
  labels:
    app.kubernetes.io/name: aws-efs-csi-driver
  annotations:
    role-arn: arn:aws-cn:iam::332211:role/Amazon_k8s
EOF

12.2 应用上述清单

kubectl apply -f efs-service-account.yaml

12.3 下载清单

kubectl kustomize \
    "github.com/kubernetes-sigs/aws-efs-csi-driver/deploy/kubernetes/overlays/stable/ecr?ref=release-1.3" > driver.yaml

12.4 替换容器镜像地址ECR为国内地址(共有7处image地址需要修改)

“image:” 后边把Global ECR“602401143452.dkr.ecr.us-west-2.amazonaws.com”替换成国内ECR“961992271922---dkr---ecr---cn-northwest-1.amazonaws.com.rproxy.goskope.com.cn

12.5 应用上述清单,部署Amazon EFS CSI驱动程序

Amazon EFS CSI 驱动程序允许多个Pod在ReadWriteMany模式中同时写入卷

kubectl apply -f driver.yaml

Amazon EFS CSI驱动程序安装完成后,可以看到efs-csi-controller和efs-csi-node

12.6 获取kubernetes集群的VPC ID

macid=$(curl http://169.254.169.254/latest/meta-data/network/interfaces/macs/)
curl http://169.254.169.254/latest/meta-data/network/interfaces/macs/${macid}/vpc-id

12.7 获取VPC集群的CIDR范围

aws ec2 describe-vpcs --vpc-ids YOUR_VPC_ID --query "Vpcs[].CidrBlock" --output text

12.8 创建安全组

为Amazon EFS挂载点允许入站网络文件系统(NFS)流量,运行以下命令

aws ec2 create-security-group --description efs-test-sg --group-name efs-sg --vpc-id YOUR_VPC_ID

12.9 添加EFS入站规则

方便VPC中的资源可以与Amazon EFS文件系统进行通信,将YOUR_VPC_CIDR替换为12.7步中的输出,将sg-xxx替换为12.8安全组ID

aws ec2 authorize-security-group-ingress --group-id sg-xxx --protocol tcp --port 2049 --cidr YOUR_VPC_CIDR

12.10 为kubernetes集群创建Amazon EFS文件系统, 保存FileSystemId,后边会使用

aws efs create-file-system --creation-token k8s-efs

12.11 在Master所在的EC2, 找到SubnetID,后边会使用

12.12 要为Amazon EFS创建挂载目标,输入12.10步骤中FileSystemId和12.10步骤中SubnetID,运行下边命令

aws efs create-mount-target --file-system-id FileSystemId --subnet-id SubnetID --security-group sg-xxx

12.13 创建挂载目标时,要在多个子网中创建挂载目标,必须为每个SubnetID分别运行步骤12.12中的命令。

将FileSystemId替换为前述第12.10步中(在此步骤中创建了Amazon EFS文件系统)的输出;将sg-xxx替换为前述第12.8步中(在此步骤中创建了安全组)的输出

12.14 最佳实践是在Worker节点运行的每个可用区中创建挂载目标,如下图所示:

13. 测试 Amazon EFS CSI 驱动程序

通过部署两个Pod,写入相同的文件,以测试Amazon EFS CSI驱动程序。

13.1 从AWS GitHub中克隆aws-efs-csi-driver存储库

git clone https://github.com/kubernetes-sigs/aws-efs-csi-driver.git

13.2 将工作目录更改为包含Amazon EFS CSI驱动程序测试文件的文件夹,运行以下命令

cd aws-efs-csi-driver/examples/kubernetes/multiple_pods/

13.3 检索12.11步骤中创建的Amazon EFS文件系统ID,确认之前的Kubernetes EFS文件系统ID

aws efs describe-file-systems --query "FileSystems[*].FileSystemId" --output text

13.4 在specs/pv.yaml文件中,将spec.csi.volumeHandle值替换为12.11步骤中的 Amazon EFS FileSystemId。

13.5 创建测试所需的Kubernetes资源,kubectl命令会创建Amazon EFS存储类、PVC、持久性卷和2个Pod(app1和app2)

kubectl apply -f specs/

13.6 在默认命名空间中列出持久卷,然后使用default/efs-claim查找一个持久卷

kubectl get pv -w

13.7 描述持久性卷

kubectl describe pv efs-pv

13.8 测试两个pod是否正在将数据写入文件

等待大约几秒,该输出显示由两个Pod写入到/data/out1.txt的当前日期

14. 备份Amazon EFS卷

和EBS卷不同,EFS卷在配置中可以直接选择“启用自动备份”

15. 总结

本文讨论的是使用Kubeadm在亚马逊云上创建一个基于Ubuntu18.04服务器的Kubernetes集群的过程,并使用了EBS和EFS持久化存储。Kubeadm不但与公有云环境本地集成,而且云上还有丰富的计算实例可供选择,日常配置存储和网络服务的操作也非常方便。通过云基础设施的易用性和便捷性,简化了管理和运维,在保证资源的前提下,还可以加快故障隔离和故障排除。

在使用Docker和 Kubernetes时,需要经常访问gcr.io(Google Container Registry),Docker Hub,和quay.io(Quay)镜像仓库,由于众所周知的原因,从国内访问这些镜像仓库时通常会碰到网络不稳定或无法访问的问题,唯一能访问的是 Docker Hub。本文也通过实践去访问国内已有的ECR(Elastic Container Registry)镜像仓库,去解决上述问题。

16. 参考资料

[1] KUBERNETES ARCHITECTURE

[2] 如何在AWS中国区上基于EC2搭建Kubernetes

[3] Managing the Amazon EBS CSI self-managed add-on

[4] Amazon EFS CSI driver

[5] Amazon EKS add-on container image addresses

[6] 在AWS中国区方便安全的使用海外公开容器镜像

本篇作者

缪翰林

亚马逊云科技解决方案架构师,负责基于亚马逊云科技的方案咨询,设计和架构评估。在运维,DevOps方面有丰富的经验,目前侧重于大数据领域的研究。