百种弊病,皆从懒生

EKS 评测

2018.09.11

EKS 正式 launch 还没有正经用过, 最近总算试了一把, 记录一点.

Setup

AWS 官方的 Guide 只提供了一个 cloudformation template 来设置 worker node, 我喜欢用 terraform, 可以跟着这个文档尝试:https://www.terraform.io/docs/providers/aws/guides/eks-getting-started.html 来设置完整的 eks cluster 和管理 worker node 的 autoscaling group.

设置完 EKS 后需要添加一条 ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: aws-auth
  namespace: kube-system
data:
  mapRoles: |
    - rolearn: arn:aws:iam::<account-id>:role/eksNodeRole
      username: system:node:{{EC2PrivateDNSName}}
      groups:
        - system:bootstrappers
        - system:nodes

这样 worker node 节点才能加入集群.

网络

之前一直没有在 AWS 上尝试构建 k8s 的一个原因, 就是不喜欢 overlay 网络, 给系统带来了额外的复杂度和管理开销, VPC flowlog 看不到 pod 之间流量, 封包后 tcpdump 不好 debug 应用层流量.

随着 EKS 的发布,AWS 开源了基于 VPC 的 CNI 插件(https://github.com/aws/amazon-vpc-cni-k8s),之前也大致写过: https://blog.monsterxx03.com/2018/04/09/aws-%E7%9A%84-k8s-cni-plugin/

这个项目分成两部分, cni 插件 aws-cni (给 kubelet 调用) 和 aws-k8s-agent, 会以 daemonset 的形式运行(运行时 pod 名字是 aws-node),实际上是一个L-IPAM (local ip address management), 负责将 eni 绑定到 ec2 instance, 将 ip 地址绑定到 eni.

这样 pod 之间就能用真正的 vpc ip 进行通信了.

不同类型的 ec2 instance 可以分配的 eni 数目,每个 eni 能绑定的 ip 地址数目都不同, 具体可见: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html

一台 ec2 可分配的 pod 总数目是 # of eni * (# of ip-per-eni - 1) + 2, 这里 -1, 是因为每个 eni 上有个 ip 是 primiary ip, 不可分配给 pod, +2 是每个 node 启动时候固定会启动的两个 pod aws-nodekube-proxy, 这两个 pod 不需要和其他 pod 通信, 没有额外占用 ip, 实际用了 eni 的 primiary ip.

比如 m4.large,可分配 eni 是2, 每个 eni 能绑定 ip 数是 10, 所以一台 m4.large 最多可以承载 2 * (10 - 1) + 2 = 20 个 pod, m4.xlarge 就是 4 * (15 -1) + 2 = 58

启动 worker node 之后,可以看下 pod 状态, kubectl get pods -n kube-system -o wide:

NAME                                                    READY     STATUS    RESTARTS   AGE       IP            NODE
aws-node-455j8                                          1/1       Running   1          56m       10.100.33.212   ip-10-100-33-212.ec2.internal
cni-metrics-helper-56485f8b9d-tpmcm                     1/1       Running   0          58m       10.100.32.89    ip-10-100-33-212.ec2.internal
foolhardy-fly-aws-cluster-autoscaler-599b6f9b68-tlfln   1/1       Running   0          58m       10.100.32.41    ip-10-100-33-212.ec2.internal
honest-kitten-metrics-server-754cddf4f9-9sgcp           1/1       Running   0          58m       10.100.38.70    ip-10-100-33-212.ec2.internal
kube-dns-64b69465b4-h42nz                               3/3       Running   0          58m       10.100.33.194   ip-10-100-33-212.ec2.internal
kube-proxy-nj6lc                                        1/1       Running   0          56m       10.100.33.212   ip-10-100-33-212.ec2.internal
tiller-deploy-78ddf4757c-ljs7p                          1/1       Running   0          58m       10.100.33.20    ip-10-100-33-212.ec2.internal

aws-nodekube-proxy ip 都是 10.100.33.212, 这个就是当前 eni 的 primary ip.

为了让一个 node 上启动的 pod 数目不超过可分配 ip 数目,需要告诉 kubelet 限制 --max-pods, 目前的做法是在 build 镜像的时候把 instance type 和 最大 pod 数映射文件放进去, 在 ec2 启动的时候, 用 user-data 注入脚本, 根据 instance type 来自动限制, 详情可以看: https://github.com/awslabs/amazon-eks-ami/blob/master/files/bootstrap.sh

debug 方法:

  • aws-node 这个 pod 默认会启动一个 http server, 监听61678 端口, 提供两个接口 /v1/enis/v1/pods, 用来显示 ip 分配和 pod 之间的关系.
  • log 会输出在 worker node 的 /var/log/aws-routed-eni
  • 可以启动一个收集 cni metrics 的 pod, kubectl apply -f https://raw.githubusercontent.com/liwenwu-amazon/amazon-vpc-cni-k8s-1/metrics1/misc/cni_metrics_helper.yaml, 之后 kubectl logs cni-metrics-xxx -n kube-system 可以看到 pod 的一些 metrics 信息.

Metrics server 的问题

为了测试 auto scaling, 需要在 EKS 中部署 metrics server. EKS 最早发布的时候不支持 metrics server(因此 HPA 也不支持), EKS 的 PlantformVersion 需要是 ‘eks.2’.

之前不支持的原因是 metrics server 基于 api aggregation , core api server 只能用 client 证书认证, EKS 为了和 IAM 交互用的是 webhook. 后来 EKS 的人修了下: https://github.com/kubernetes/kubernetes/pull/66394/files#diff-59f09be65a7ae1feb263f6bce1d856b1

详情看: https://aws.amazon.com/cn/blogs/opensource/horizontal-pod-autoscaling-eks/

https://github.com/kubernetes-incubator/metrics-server/tree/master/deploy/1.8%2B 提供的配置尝试部署 metrics-server.

现在 metrics server 能正常启动了,但 kubectl top 还是取不到数据, metrics server 的 log 里能看到:

E0911 06:18:58.805099       1 manager.go:102] unable to fully collect metrics: unable to fully scrape metrics from source kubelet_summary:ip-10-100-39-198.ec2.internal:
 unable to fetch metrics from Kubelet ip-10-100-39-198.ec2.internal (ip-10-100-39-198.test.com): Get https://ip-10-100-39-198.test.com:10250/stats/summary/: dial tcp: lookup ip-10-100-39-198.test.com on 172.20.0.10:53: no such host

看上去是因为我在 vpc 中设置了 dhcp option, 默认 domain 为 test.com, ec2 worker node 启动后 hostname 自动变成了 ip-10-100-39-198.test.com, metrics server 启动后在 pull node 信息的时候,走了 node 的 hostname .这个域名自然是不存在的, 所以连不上.

解决办法可以把 dhcp option 设回 ec2.internal, 或者让 metrics-server 用 ip 来连接:

  command:
   - /metrics-server
   - --kubelet-insecure-tls
   - --kubelet-preferred-address-types=InternalIP

还有一个问题是 eks worker node 的 /etc/resolv.conf 里的内容变成了:

search test.com us-west-2.compute.internal
nameserver 10.100.0.2

test.com 是 dhcp option 分发的域名, us-west-2.compute.internal 哪来的? 而且我的测试节点是启动在 us-east-1 的.

配置 helm

给 helm 创建 service account, 和 cluster-admin 的 role 绑定:

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

用 tiller 帐号初始化 helm 的 tiller server helm init --service-account tiller

helm ls 下试试权限有没有问题.

AutoScaling on EKS

梳理下要在 EKS 上做 auto scaling, 我们需要些什么. 这里的 auto scaling 分两部分, pod 的 auto scaling, worker node 的 auto scaling.

pod 的 scale 由 HorizonalPodAutoscaler(aka: HPA) 负责, HPA 通过 metrics-server 采集到的数据, 计算要将 pod 的某个 metrics 维持在一个 threshold 额外需要多少个 pod. 然后会去更新 ReplicationSet 的 replica 字段.

当前 node 资源不足以 allocate 新的 pod 时, 新的 pod 会处在 pending 状态. cluster autoscaler 检测到 pending 状态的 pod, 就会修改 aws autoscaling group 的 desired 数目来 launch 新的节点.

用 helm 来部署 metrics-servercluster-autoscaler.

helm install stable/metrics-server --namespace kube-system

成功获取 metrics 信息:

kubectl top nodes

NAME                          CPU(cores)   CPU%      MEMORY(bytes)   MEMORY%   
ip-10-100-39-200.ec2.internal   33m          1%        468Mi           5%  

配置 cloud-autoscaler charts, 这里我用 audodiscovery 的方式让 cloud-autoscaler 找到 aws 的 autoscaling group, 需要提前在 auto scaling group 上设置 tag: k8s.io/cluster-autoscaler/enabled, kubernetes.io/cluster/<ClusterName>, value 随意.

autoDiscovery:
  clusterName:  <ClusterName>
sslCertPath: /etc/kubernetes/pki/ca.crt
rbac:
  create: true

注意这里的 sslCertPath 默认是 /etc/ssl/certs/ca-certificates.crt, eks 放的位置不太一样.

cloud-autoscaler 还需要特定的 iam 权限, 可以把它绑定到 worker node 的 instance role 上:

 {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "autoscaling:DescribeAutoScalingGroups",
                "autoscaling:DescribeAutoScalingInstances",
                "autoscaling:DescribeLaunchConfigurations",
                "autoscaling:DescribeTags",
                "autoscaling:SetDesiredCapacity",
                "autoscaling:TerminateInstanceInAutoScalingGroup"
            ],
            "Resource": "*"
        }
    ]
}

helm install stable/cloud-autoscaler --namespace kube-system

简单测试一下 cluster-autoscaler, 现在的测试节点里只有一台 m4.large, 总共可以放 20 个 pod, 已经预先跑了一些系统 pod(aws-node, kube-proxy, tiller, metrics-server, cluster-autoscaler…), 尝试部署一个 20 replica 的 nginx, 同时用 kubectl get pods -w -o wide 观察 pod 的分配情况, kubectl get nodes -w 观察新 node:

kubectl run nginx --image=nginx -r 20

可以看到在原先的 node 上创建了一部分 nginx pod, 然后新的 node 被 cluster-autoscaler 启动, 并加入集群, 接着剩余 pod 在新 node 上创建.

HPA 就不测了, 关于 k8s 的资源管理, 包括 scale in 时候的策略, pod 的 reallocate 是个挺复杂的话题, 之后再讲吧.

comments powered by Disqus