本文首发于我的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组。
所以,整个外部流量进入集群的路径是这样的:
- 外部请求的客户端通过dns查询得到我们elb的ip
- 根据查询到的ip流量会进入elb
- elb从我们配置的转发规则中的node中选择一个,把流量导向该node的31742端口
- 流量到node的31742端口之后,因为traefik的
hostPort: 31742的原因,到31742端口的流量会由traefik-controller Pod处理 - traefik-controller 根据我们的ingress资源,把流量导入某个service
- service从关联的endpont资源中选择一个Pod,把流量导入具体的Pod
这样就完成了外部流量到Pod的整个过程。
部署过程的感受
虽然有kops工具帮我们简化了这个过程,自建k8s集群坑还是多。比如我们遇到了Node过一段时间就是NotReady导致node上的pod被驱逐,问题竟然是AMI的问题,coreos对aws的多网卡的机器支持不好,换成debian系统就好了(现在kops-cn默认就是debian系统了)😁。
出了问题需要从aws服务一致排查到k8s内部组件,对debug能力要求很高,尤其是需要对aws服务非常了解,对k8s内部组件非常了解,对于没做过运维的我挑战很大,当然中间收获也很多😆。