加载中...

利用 kOps 在 AWS 部署 k8s 集群


参数与版本

本文中涉及的所有工具或参数,如无特殊描述,均与下表相同:

条目 参数值/版本号
kOps 1.19.0
kubectl 1.19.7
k8s cluster version 1.19.7
k8s cluster name exp.k8s.local
s3 name exp.k8s.local-state
region us-east-1
zone us-east-1c
metrics-server 集群内置
dashboard 2.0.1

由于 k8s 更新迭代非常快,而且对版本要求很严格,因此本文主要为了叙述搭建 k8s 集群的方法,涉及到的版本和参数可能在若干年月后就过时了,所以在部署的时候,对于出现版本号和参数的地方一定要格外留意,尽量不要直接复制命令执行,否则最终搭建出来的集群很可能会出现各种兼容性问题。详细可参考《Kubernetes 版本及版本偏差支持策略》。

工具说明

工具 用途
AWS 亚马逊云计算平台,类似于阿里云、腾讯云等,可以提供搭建 k8s 集群的云主机、S3 存储桶等资源
awscli AWS 命令行工具,可用于管理 AWS 平台上的授权服务
kOps 用于在 AWS 上搭建 k8s 集群的 oob(out-of-band) 工具,通过配置授权,它可以直接调度 AWS 上的资源,全自动创建并管理一个高可用的 k8s 集群
kubectl k8s 命令行工具,可用于对 k8s 集群下发命令
kubernetes 简称 k8s,可跨多个主机管理容器化的应用程序的开源系统
dashboard k8s 的 GUI 仪表板,可选插件
metrics-server 用于收集 k8s 集群指标(如 CPU、内存等)的可选插件,因已内置到 k8s 集群,不再推荐独立安装,避免出现版本兼容性问题

关于 metrics-server 版本兼容性问题,可以参考《kubectl edit 或者 apply 报 SchemaError》的说明,主要是 metrics-server 使用了新的 openapi-generator,导致生成的 openapi 格式与某些版本的 k8s 有差异,导致 kubectl 在解析 openapi 的 schema 时发生异常,以致只能回滚或重建集群。

因为我们是使用 kOps 安装的集群,为了避免出现版本兼容性问题,以后要安装 metrics-server、dashboard 之类的插件,最好直接通过 kops/addons 目录指引按需安装。

准备工作:部署 oob-server

首先大概了解一下部署架构:

如上图,oob-server 是在 AWS 手动申请的一台 EC2 服务器,我们在这台服务器上安装 awscli、kOps、kubectl 工具后,就可以以 oob(带外管理)方式调度 AWS 的资源(包括 EC2、S3、VPC、LB、安全组等)创建 k8s 集群。

因为 oob-server 不属于集群的一部分,因此在 aws 申请一台低配的机器即可(本文为 2C4G 的 Ubuntu 18.04)。尔后所有操作均在 oob-server 上执行即可,无需登陆 k8s 集群的任何节点。

安装 kubectl

若了解 k8s 集群的同学可以知道,集群中有一个 kube-apiserver(由 kOps 负责安装),此处所安装的 kubectl 就是其客户端。

安装命令如下:

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
mv kubectl /usr/local/bin/

若要下载特定版本,只需把命令中的 $(curl -L -s https://dl.k8s.io/release/stable.txt) 部分替换为指定版本即可。建议一定要去看下当前最新的稳定版是什么,k8s 对版本兼容性是有严格要求的,一旦不匹配集群会跑不起来。

验证安装命令如下:

kubectl version --short --client

kubectl version 可同时查看客户端和服务端版本,但现在服务端还没安装,查询会超时,故先查客户端。

安装 kOps

安装命令如下:

curl -Lo kops https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64
chmod +x kops
mv kops /usr/local/bin/kops

若要下载特定版本,只需把命令中的 $(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4) 部分替换为指定版本即可。建议一定要去看下当前最新的稳定版是什么,确保 kOps 支持安装的 k8s 集群版本与前面安装的 kubectl 版本一致。

关于 kOps 支持安装的 k8s 版本可以参考《kOps版本和版本控制》。

验证安装命令如下:

kops version

安装 awscli

安装命令如下:

apt-get update
apt-get install awscli

验证安装命令如下:

aws

为了通过命令行执行 aws 控制台的部分功能,需要为其配置你个人 AWS 账号的 AccessKeyID 和 SecretAccessKey 进行授权。

打开 AWS 的 IAM 控制面板,点击左侧导航栏的【用户】便可找到你自己的个人账号,点击该账号,点击【安全证书】标签,点击【创建访问密钥】,即可得到 AccessKeyID 和 SecretAccessKey。

这个密钥只是临时用的,下面创建了 kops 用户之后记得回来删除掉。

执行命令 aws configure 配置 awscli 权限,会提问 4 个问题:

  • AWS Access Key ID : 输入上面生成的 AccessKeyID
  • AWS Secret Access Key: 输入上面生成的 SecretAccessKey
  • Default region name : 输入你期望 k8s 的 EC2 实例应该创建在哪个地区,本文统一为 us-east-1
  • Default output format: awscli 执行结果回显到命令行界面的格式, 默认为 text, 本文统一为 json

注意 AWS 关于 《Regions, Availability Zones, and Local Zones》 的区别。此处填的是 Regions(地区),例如 us-east-1。 后面会要求细化粒度到 Availability Zones(可用地区),例如 us-east-1aus-east-1bus-east-1c 等。

创建 kOps 专用的 IAM 用户

为了使用 kOps 部署集群,需要创建一个专用的 IAM 用户 kops,并分配这些权限:

  • AmazonEC2FullAccess
  • AmazonRoute53FullAccess
  • AmazonS3FullAccess
  • IAMFullAccess
  • AmazonVPCFullAccess

由于前面 awscli 已经拥有了你的 AWS 账号权限,因此可以直接在命令行创建 kops 用户并为其分配上述权限:

aws iam create-group --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonRoute53FullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonS3FullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/IAMFullAccess --group-name kops
aws iam attach-group-policy --policy-arn arn:aws:iam::aws:policy/AmazonVPCFullAccess --group-name kops
aws iam create-user --user-name kops
aws iam add-user-to-group --user-name kops --group-name kops

创建成功后,可以在 AWS 的 IAM 控制台 UI 上看到用户 kops

然后生成 kops 用户密钥:

aws iam create-access-key --user-name kops

此命令会返回 kops 用户的 AccessKeyID 和 SecretAccessKey (与在 AWS 的 IAM 控制面板生成的效果是一样的)。

执行命令 aws configure 更新 awscli 为 kops 用户的权限,依然会问 4 个问题:

  • AWS Access Key ID : 输入 kops 用户的 AccessKeyID
  • AWS Secret Access Key: 输入 kops 用户的 SecretAccessKey
  • Default region name : 保持不变即可
  • Default output format: a保持不变即可

同时把 kops 用户的配置信息写入到 ~/.bashrc 环境变量:

echo 'export AWS_ACCESS_KEY_ID=...' >> ~/.bashrc
echo 'export AWS_SECRET_ACCESS_KEY=...' >> ~/.bashrc
echo 'export AWS_REGION=us-east-1' >> ~/.bashrc

最后生成 ssh 密钥(会有几个问题,回车到底即可):

ssh-keygen

生成的密钥位置为 ~/.ssh/,此密钥可用于之后直接 ssh 登陆 k8s 集群的任意节点。

创建 S3 Bucket

S3 存储桶用于存储集群数据,其命名格式要求为: <custom_name>.k8s.local-state

本文 S3 取名为 exp.k8s.local-state (注意后缀一个字符都不能改)。

创建命令如下:

aws s3 mb s3://exp.k8s.local-state --region us-east-1 --endpoint-url https://s3.us-east-1.amazonaws.com

命令中的 --region--endpoint-url 参数均涉及 region 名称,和前面配置 awscli 时的 region 名称保持一致即可。

存储桶名称要求全球唯一,如果创建失败了,注意看提示是否名称已经被占用了。

创建成功后,访问 AWS 的 S3 控制面板 可以看到该存储桶:

最后把 s3 的配置信息写入到 ~/.bashrc 环境变量:

echo 'export KOPS_STATE_STORE=s3://exp.k8s.local-state' >> ~/.bashrc

创建 k8s 集群

前面准备工作完成后,即可使用 kOps 创建集群。

生成集群的配置文件:

kops create cluster \
     --name=exp.k8s.local \
     --zones=us-east-1c \
     --master-size="t2.medium" \
     --node-size="t2.medium" \
     --ssh-public-key="~/.ssh/id_rsa.pub" \
     --kubernetes-version="1.19.7"

参数说明:

  • --name: 集群名称,为了让 kops 创建基于 gossip 的集群,固定格式为 <custom_name>.k8s.local,这样它就可以自动关联到前面的 S3 Bucket 名称 exp.k8s.local-state
  • --zones: 这里填的不是 Regions(地区),而是 Availability Zones(可用地区)即 Availability Zones,本文固定为 us-east-1c
  • --master-size: 为 k8s 集群的 master 节点申请的 EC2 实例类型,决定了实例的硬件配置,本文固定为 t2.medium,具体的 ec2 类型可参考这里
  • --node-size: 同上,申请的是 k8s 集群的 worker 节点。
  • --ssh-public-key: 前面生成的 ssh 密钥路径,目的是把 oob-server 的 ssh 公钥写入 k8s 集群的每一个节点,以便之后可以直接通过 ssh 登陆。
  • --kubernetes-version: 期望创建的 k8s 集群版本,可以参考《kOps版本和版本控制》查看当前所安装的 kOps 支持安装的版本范围,本文固定为 1.19.7

create 命令执行成功后, 会提示执行 kops update cluster --name exp.k8s.local --yes --admin 命令正式创建集群,但这里先不创建

我们需要执行命令 kops edit cluster exp.k8s.local 调整集群的配置文件,主要修改两处:

  • 修改 kubelet.anonymousAuth: true: 因为搭建的是测试用的集群,避免认证麻烦,这里把匿名打开。
  • 追加 metricsServer.enabled: true: 因为后面需要安装 dashboard,所以这里要启用内置的 metrics-server 插件。

修改后的集群配置文件如下(仅适用于 1.19.7 版本):

# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: kops.k8s.io/v1alpha2
kind: Cluster
metadata:
  creationTimestamp: "2021-02-08T03:28:29Z"
  generation: 1
  name: exp.k8s.local
spec:
  api:
    loadBalancer:
      class: Classic
      type: Public
  authorization:
    rbac: {}
  channel: stable
  cloudProvider: aws
  configBase: s3://exp.k8s.local-state/exp.k8s.local
  containerRuntime: docker
  etcdClusters:
  - cpuRequest: 200m
    etcdMembers:
    - instanceGroup: master-us-east-1c
      name: c
    memoryRequest: 100Mi
    name: main
  - cpuRequest: 100m
    etcdMembers:
    - instanceGroup: master-us-east-1c
      name: c
    memoryRequest: 100Mi
    name: events
  iam:
    allowContainerRegistry: true
    legacy: false
  kubelet:
    anonymousAuth: false
  kubernetesApiAccess:
  - 0.0.0.0/0
  kubernetesVersion: 1.19.7
  masterInternalName: api.internal.exp.k8s.local
  masterPublicName: api.exp.k8s.local
  metricsServer:
    enabled: true
  networkCIDR: 172.20.0.0/16
  networking:
    kubenet: {}
  nonMasqueradeCIDR: 100.64.0.0/10
  sshAccess:
  - 0.0.0.0/0
  subnets:
  - cidr: 172.20.32.0/19
    name: us-east-1c
    type: Public
    zone: us-east-1c
  topology:
    dns:
      type: Public
    masters: public
    nodes: public

配置文件默认以 vim 模式编辑,只要格式符合 yaml ,并且配置项都是当前 k8s 版本支持的,就可以成功保存修改内容(否则会提示保存失败)。

执行 kops update cluster --name exp.k8s.local --yes --admin 命令即可真正创建 k8s 集群。

创建过程大概需要 10 分钟,此时切换到 AWS 的 EC2 控制面板 查看【实例】,可以看见 k8s 的实例正在创建中。

当前版本的 kOps 所创建的集群只有 2 个节点(master 和 worker 节点各一),较旧的 kOps 版本所创建的集群有 3 个节点(1 个 master 节点和 2 个 worker 节点)。

通过 kops validate cluster 命令可查看集群各个节点状态,若均为【Ready】即代表集群创建成功:

操作 k8s 集群

常用操作命令

以 kOps 方式部署的集群,可以在 oob-server 通过 kubectl 直接操作,无需登陆到集群的任何节点。常用命令如下:

  • kubectl get nodes: 查看集群节点
  • kubectl cluster-info: 查看集群域名地址信息
  • kubectl version: 查看集群版本
  • kubectl config view: 查看集群配置(脱敏),其实就是查看 ~/.kube/config 文件(未脱敏)
  • kubectl get deployments --all-namespaces: 查看所有名字空间下的 Deployment
  • kubectl get rs --all-namespaces: 查看所有名字空间下的 ReplicaSet
  • kubectl get pods --all-namespaces: 查看所有名字空间下的 pod
  • kubectl get svc --all-namespaces: 查看所有名字空间下的 service

登陆到集群节点

因为前面在 oob-server 创建集群时,已经通过 kOps 配置了 ssh 密钥,所以可以直接从 oob-server 登陆到集群的任意节点:从 EC2 控制台获取集群节点的 IP 后,直接 ssh 登陆即可(注意配置安全组策略)。

旧版本的 kOps 所创建集群节点,其 ssh 的登陆账号默认都是 admin,但是新版本会以集群节点的操作系统名称作为 ssh 的登陆账号,例如 ubuntu、centos 等。 集群节点所使用的操作系统一般与 oob-server 服务器一致,也可在 EC2 控制台直接查看各个节点实例的操作系统名称。

访问 k8s 集群

获取服务地址

通过命令 kubectl cluster-info 可查看集群的域名地址信息,其中【Kubernetes master】即为 master 节点的服务地址,也是 kube-apiserver 的服务地址。

在 AWS 环境中,该域名地址是由 LB (负载均衡器)提供的,可以在 AWS 控制台的【负载均衡器】找到它,可以发现它被解析到 master 节点:

获取账号密码

当通过浏览器访问 kube-apiserver 时,会要求输入 BasicAuth 。

默认情况下集群有两个系统账号 adminkube,可以通过 kOps 获取其密码:

kops get secrets kube --type secret -oplaintext
kops get secrets admin --type secret -oplaintext

某些旧版本的 kOps 有 BUG,获取到的密码是反的,就是说 kube 返回的是 admin 的密码, admin 返回的是 kube 的密码。

部署 dashboard

安装

为了方便从 GUI 管理 k8s ,推荐安装 kubernetes-dashboard。又因为前面是通过 kOps 安装集群的,为了避免版本兼容性问题,可以直接从 kops/addons/kubernetes-dashboard/addon.yaml 获取匹配的版本,然后获取其 yaml 脚本进行安装。

例如当前安装的 k8s 集群版本为 1.19.7 ,则应使用 2.0.1 版本的 dashboard, 故而安装命令为:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/kops/master/addons/kubernetes-dashboard/v2.0.1.yaml

通过 kubectl get pods --all-namespaces 命令可验证是否安装成功: 当名为 kubernetes-dashboard-xxxxx 的 pod 其状态为 READY=1/1STATUS=Running 时,即安装成功。

配置登陆账号

为了访问 dashboard,我们还需为其配置一个服务账号

在 oob-server 创建名为 eks-admin-service-account.yaml 的配置文件(文件名随意),其内容为:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: eks-admin
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: eks-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: eks-admin
  namespace: kube-system

该配置文件定义了一个服务账号 eks-admin(若需要自定义其他账号名称,修改 eks-admin 即可,下同)。

然后执行命令 kubectl apply -f eks-admin-service-account.yaml 将其绑定到 k8s 集群即可。

以后访问 dashboard 时,都需要提供 token 用于登陆,此时可在 oob-server 执行命令 kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep eks-admin | awk '{print $1}'),该命令可生成用户 eks-admin 的临时 token, 将其填入登陆界面即可。

注意 token 有效期只有半小时,过期后需要执行上述命令重新生成

访问 dashboard

根据 kubernetes-dashboard 的官方说明,dashboard 的访问地址为:https://<cluster-address>/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

其中 <cluster-address>kubectl cluster-info 命令得到 Kubernetes master 地址。

但是事实上无法访问,会一直报错 401 Unauthorized

原因是在 1.16 版本之后,如果不是从 localhost 访问 kube-apiserver,都必须要认证后方可访问的。但是我们部署集群时,因为是测试环境,既没有配置证书、也没有配置 BasicAuth,所以从公网是无法直接访问的。

但是因为是测试用集群,不想过于麻烦,有没有办法绕过访问限制呢?下面提供两种绕过方法。

NodePort 访问方式

此方法参考自《Web基础配置篇(十七): Kubernetes dashboard 安装配置》,执行起来较为简单。

NodePort 原理是把 dashboard 的服务端口映射到 master 节点的宿主机的某个端口,然后直接访问 https://<master-ip>:<port>/ 即可(注意配置 EC2 安全组策略)。

这种访问方式绕过了 kube-apiserver,好处是访问 dashboard 服务时不需要再输入一串极长的地址,坏处是把端口暴露到公网,不但安全性下降,而且丧失了 LB 带来的好处,不建议在生产环境中使用。

具体的配置方式如下:

  • 查看 dashboard 服务:kubectl --namespace=kubernetes-dashboard get service kubernetes-dashboard,可见当前服务类型为 ClusterIP
  • 编辑 dashboard 服务:kubectl --namespace=kubernetes-dashboard edit service kubernetes-dashboard,把 type: ClusterIP 修改为 type: NodePort,因为是 vim 编辑,故用 wq 保存即可
  • 再次查看 dashboard 服务:kubectl --namespace=kubernetes-dashboard get service kubernetes-dashboard,服务类型已变更为 NodePort,且服务端口从 443 映射到了 30689(随机的

记住映射的端口,然后我们到 AWS 的 EC2 控制面板 查看集群 master 节点的公网 IP,访问 https://<master-ip>:<port>/,填入前面 eks-admin 账号的 token 即可登陆。

注意端口是从 443 映射出来的,因此必须用 https 协议访问,但由于证书不可信,使用 chrome 访问该地址会拦截,可以鼠标点击页面空白处,键盘输入 thisisunsafe 强制访问。

代理转发访问方式

这个方法会比前一个方法更复杂一些。

需要知道,若 k8s 集群是部署在本地,那么通过 kubectl proxy 命令可以直接把 kube-apiserver 服务映射到本地 http/8001 端口的,再根据 kubernetes-dashboard 的官方说明,直接访问 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ 即可。

但现在集群是部署在 AWS 上的,我们需要通过公网访问,才有了限制。

于是我们尝试放开 kubectl proxy 限制条件,在 oob-server 执行命令:

kubectl proxy --address='0.0.0.0' --port='8001' --accept-hosts='^*$'

意即所有网卡(公网和内网)均转发了 8001 端口服务,而且接受所有来源的请求(注意配置 EC2 安全组策略)。

此时从公网访问 http://<oob-server-ip>:8001 已可成功打开 kube-apiserver 服务,不会再报 401 Unauthorized 异常了。

但是从公网访问 http://<oob-server-ip>:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/ 时,虽然可以打开 dashboard 登陆页面,但是无法登陆(按钮变灰),报错为:Insecure access detected. Sign in will not be available. Access Dashboard securely over HTTPS or using localhost.

详细原因见《Accessing Dashboard》,主要是 dashboard 为了安全起见,在 1.16 版本之后,如果使用 http 协议,只能从本地登陆(127.0.0.1 或 localhost); 如果使用 https ,才可以从非本地登陆。

简而言之,kubectl proxy 只是绕过了 kube-apiserver 的限制,没有绕过 dashboard 的限制。

为此,这里参考了 《访问 K8s Dashboard 的几种方式》,可以做一个 ssh 本地端口转发,把本地流量通过 ssh 转发到 oob-server 的 8001 端口,oob-server 再通过 kubectl proxy 把流量转发到 k8s 集群(生产环境不建议使用此方式)。

具体方法如下:

  • 修改 oob-server 的 sshd 服务,除了 22 端口外,增加监听一个端口用于本地端口转发,如 54322 :vim /etc/ssh/sshd_config,取消 Port 22 注释,并添加一行 Port 2222, 重启 sshd 服务
  • 在 oob-server 执行命令 kubectl proxy (即只代理 127.0.0.1:8001, 不能是 0.0.0.0:8001)
  • 在本地终端执行命令: ssh -L localhost:8001:localhost:8001 -NT ubuntu@<oob-server-ip> -p 2222
  • 在本地浏览器访问 http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login,此时 dashboard 的登陆按钮不再是灰色,可点击登陆

删除 k8s 集群

删除集群(会自动释放 EC2 实例、LB、安全组等资源,但 IAM 账号 和 S3 不会释放):

kops delete cluster exp.k8s.local --yes

删除配置(主要是集群的一些配置信息,包括登陆认证等,为了避免以后重建集群时出现各种原因不明的认证问题,最好是删掉):

rm -rf ~/.kube

k8s 集群数据是存储在 S3 Bucket 的,只能访问 AWS 的 S3 控制面板 删除

FAQ

集群部署过了一段时间后,kubectl 报错 Unauthorized

例如在 oob-server 执行 kubectl version 命令后,只能查看 kubectl 的 Client 端版本,而 Server 端版本则要求登陆:

Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.7", GitCommit:"1dd5338295409edcfff11505e7bb246f0d325d15", GitTreeState:"clean", BuildDate:"2021-01-13T13:23:52Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}
error: You must be logged in to the server (the server has asked for the client to provide credentials)

这是因为从 kOps 1.19 开始,kOps 从集群导出的证书仅在 18h 内有效,过期 oob-server 和集群就无法通讯了。此时需要在 oob-server 手动执行命令刷新证书:

# 集群名称根据实际情况修改
kops update cluster --name exp.k8s.local --yes --admin

测试环境尚可接受,为了一劳永逸,在生产环境建议使用其他身份验证机制。

此问题只存在于 oob-server,若直接登陆到集群节点执行 kubectl 命令,是不会有此问题的

登陆 dashboard 一段时间后,报错 Unauthorized

前面已经说过,dashboard 的服务账号 eks-admin 的 token 是临时生成的,每次有效期只有半小时,过期后需要执行以下命令重新生成:

# 服务账号名称根据实际情况修改
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep eks-admin | awk '{print $1}')

参考资料


文章作者: EXP
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 EXP !
  目录