使用kops在aws宁夏区搭建k8s集群

1,993 阅读5分钟

本文首发于我的blog

最近使用kops在aws宁夏区搭建了一套生产用的k8s集群,这里说下完整的过程,包括中间遇到的各种坑。

物料准备

  • 创建一个vpc
  • 创建一个S3的桶后面给kops存数据用
  • AmazonEC2FullAccess,AmazonRoute53FullAccess,AmazonS3FullAccess,IAMFullAccess,AmazonVPCFullAccess权限的IAM账户
  • 一台低配的部署机器

配置部署机器

  • 安装 kubectl,并且配置命令行自动补全 echo "source <(kubectl completion bash)" >> ~/.bashrc && source ~/.bashrc
  • download kops-cn,这个项目封装了kops,把常用的镜像放到宁夏区和北京区,大家不用费心再去配置代理拉取镜像了,因为这个项目一直在更新并且没有tag管理,我用的是这次commit,大家自行评估。
  • 安装 kops(github.com/kubernetes/…),版本按照kops-cn要求的安装即可

部署k8s集群

就是按照kopsc-cn的文档来就可以了,说下文档没有提到的几点:

Makefile中有这么一段代码:

ifeq ($(TARGET_REGION) ,cn-northwest-1)
	CLUSTER_NAME ?= cluster.zhy.k8s.local
	# copy from kope.io/k8s-1.12-debian-stretch-amd64-hvm-ebs-2019-05-13
	# see https://github.com/nwcdlabs/kops-cn/issues/96
	AMI ?= ami-068b32c3754324d44
	ZONES ?= cn-northwest-1a,cn-northwest-1b,cn-northwest-1c
endif

这个CLUSTER_NAME要修改一下,不然创建出来集群名字就是cluster.zhy.k8s.local,特别怪异。

执行make edit-cluster的时候可以修改下子网CIDR,默认子网是/16,我们修改成了/24,原因是我们在vpc中已经创建了一个子网来放部署机和跳板机,如果/16的话会造成子网IP段冲突。

如果新建的集群要删除重建的话,在执行了make validate-cluster之后需要手动检查下 ELB,subnet,routetable,EC2是否删除干净,并且清除.kube文件夹。

默认创建的子网是public的,如果需要创建private的,可以自己创建private subnet和NAT,然后传给kops使用,具体参考这个issue

创建完毕之后可以部署一个很简单的应用上去,然后curl serviceName:serticePort,确保集群可以正常使用。

部署完毕之后,有三个master节点,分布在三个可用区,这样在一个可用区挂了的时候,集群不受影响,还有两个普通节点,属于nodes这个instanceGroup(kops的一个概念,对应一个aws的autoscale组)。

通过elb接入外部流量

上面步骤成功之后,集群内部的service可以通过serviceName互相访问了,如果要暴露出去给Internet访问需要配置ingress。

我们使用traefik-ingress作为ingress controller,所以首先部署traefik:

部署traefik

准备traefik使用的RBAC role文件: traefik-rbac.yaml

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
rules:
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
  name: traefik-ingress-controller
  namespace: kube-system

执行 kubectl apply -f traefik-rbac.yaml

使用Demonset的方式部署traefik,准备 traefik.yaml:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: traefik-ingress-controller
  namespace: kube-system
---
kind: DaemonSet
apiVersion: extensions/v1beta1
metadata:
  name: traefik-ingress-controller
  namespace: kube-system
  labels:
    k8s-app: traefik-ingress-lb
spec:
  template:
    metadata:
      labels:
        k8s-app: traefik-ingress-lb
        name: traefik-ingress-lb
    spec:
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 60
      containers:
      - image: traefik
        name: traefik-ingress-lb
        ports:
        - name: http
          containerPort: 80
          hostPort: 31742
        - name: admin
          containerPort: 8080
        securityContext:
          capabilities:
            drop:
            - ALL
            add:
            - NET_BIND_SERVICE
        args:
        - --api
        - --kubernetes
        - --logLevel=INFO
---
kind: Service
apiVersion: v1
metadata:
  name: traefik-ingress-service
  namespace: kube-system
spec:
  selector:
    k8s-app: traefik-ingress-lb
  ports:
    - protocol: TCP
      port: 80
      name: web
    - protocol: TCP
      port: 8080
      name: admin

注意hostPort: 31742这一句,非常关键,指的是到该Pod所在的Node的31742端口的流量,自动转交给该Pod处理。如果31742已经被占用了,可以换一个端口。

最后执行kubectl apply -f traefik.yaml,然后执行kubectl -n kube-system get pod -w,等待traefik-controller部署完毕即可。

准备application elb

上面创建集群的时候kops会创建一个classic elb,我们需要删掉这个elb然后自己创建一个application elb,elb的流量转发规则是任何到80端口上的流量,都要转发到我们上面创建的两个node节点的31742端口(和traefik的监听端口保持一致)。

并且配置好防火墙规则,elb可以连通到该Node组。

所以,整个外部流量进入集群的路径是这样的:

  1. 外部请求的客户端通过dns查询得到我们elb的ip
  2. 根据查询到的ip流量会进入elb
  3. elb从我们配置的转发规则中的node中选择一个,把流量导向该node的31742端口
  4. 流量到node的31742端口之后,因为traefik的hostPort: 31742的原因,到31742端口的流量会由traefik-controller Pod处理
  5. traefik-controller 根据我们的ingress资源,把流量导入某个service
  6. service从关联的endpont资源中选择一个Pod,把流量导入具体的Pod

这样就完成了外部流量到Pod的整个过程。

部署过程的感受

虽然有kops工具帮我们简化了这个过程,自建k8s集群坑还是多。比如我们遇到了Node过一段时间就是NotReady导致node上的pod被驱逐,问题竟然是AMI的问题,coreos对aws的多网卡的机器支持不好,换成debian系统就好了(现在kops-cn默认就是debian系统了)😁。

出了问题需要从aws服务一致排查到k8s内部组件,对debug能力要求很高,尤其是需要对aws服务非常了解,对k8s内部组件非常了解,对于没做过运维的我挑战很大,当然中间收获也很多😆。