# 贤文杂谈-kubernetes
大纲
* 如何高效部署自建k8s集群
* k8s-master 控制面集群模式优劣分析
* 核心组件性能调优
* 组件版本选择
* ingress 日志分析
* etcd
* CNI 网络优选
在2020 这个特殊的寒冬时节,运维圈的兄弟们是不是已感觉,如今在各个微信群中不提Kubernetes、Istio会不会有一种异样的感觉。是的云原生时代已经来临,微服务、容器、服务与治理已经从一线TOP行业公司逐渐向中小规模企业推进。刚入行的小铁们时常面临集群如何快速部署?错综复杂的核心组件如何选择?平台性能如何调优保证SLA。本文以平台选型为视角,为大家分析k8s集群是自建集群还是选择公有云服务托管服务。
## 如何高效部署k8s集群
无论是马爸爸的ACK还是任爷爷的CCE在公有云平台都提供了自动化部署集群的能力,公有云的容器平台能在短短几分钟内快速拉起一套生产集群。而对于自建IDC的小铁门公有云的服务无法享受时,或者想多折腾的技术控们,我们应该如何自建一套生产标准的k8s集群。是小铁们面临的第一个问题。
如果你是首次阅读kubernetes官方文档,会发现官方推荐使用kubeadm工具部署k8s集群,小铁们有没有在第一步创建CA 、apiserver、etcd、kubelet各类证书文件的时候就卡主就有一种放弃想跳楼的经历。为达到生产环境要求master控制面板如何选择高可用方案?集群计算node节点如何快速水平扩容?各类繁杂网络CNI插件如何选择? 这些最基础的问题如何破解,小编推荐小铁们使用kubespray工具部署集群,Kubespray是Google开源的一个部署生产级别的Kubernetes服务器集群的开源项目,它整合了Ansible作为部署的工具。[Kubespray项目地址](%5Bhttps://github.com/kubernetes-sigs/kubespray%5D(https://github.com/kubernetes-sigs/kubespray))
### 组件版本如何选择
kuberspray的版本管理会跟随社区版本保持最新版本的的更新,如果我们选择在生产环境部署使用,建议选择比当前最新版本晚半年的版本。比如kubernetes当前最新版本 v1.17.5,倒退两个版本选择v1.15.11 。
### Kuberspray HA-MODE
![](https://img.kancloud.cn/c8/df/c8dfb0d31c72788a4085c364dcba2ce8_914x674.png)
从架构图中我们获取到kubespray使用三个独立master方式分别部署。在集群kube-system 命名空间巧妙的增加了一个nginx-proxy组件,客户端节点kube-node 通过 nginx-proxy反向代理请求kube-apiserver。
我们在任爷爷家创建容器服务后会发现,CCE的容器服务也是提供了独立的三master 集群模式。虽然master空面需要单独付费,但是这样能更能保证用户的数据安全性及稳定性。
![](https://img.kancloud.cn/df/47/df47b4a5c4e420d23365bd346a89dc3d_1474x429.png)
### Docker 存储的选择
Docker storage driver 不同的存储驱动直接关乎到 volume 挂在磁盘I/O性能。如果你使用centos7.X 最新发行版本推荐直接使用overlay2作为默认的存储驱动。通过查看[Docker CE 18.03 源码]([https://github.com/docker/docker-ce/blob/18.03/components/engine/daemon/graphdriver/driver\_linux.go#L50](https://github.com/docker/docker-ce/blob/18.03/components/engine/daemon/graphdriver/driver_linux.go#L50))会发现源码排序也是优先推荐使用overlay2。
可以使用`docker info` 命令可以查看当前驱动方式:
![](https://img.kancloud.cn/8b/ee/8bee2ff71d5a3d91872850ebf6de7d60_983x576.png)
### Ingress 日志分析
ingress作为kubernetes南北流量的解决方案,与大家日常熟悉的nginx在架构设计及配置使用方法都有着较大的差异。常见的Ingress controller有ingress-nginx、ingress-Traefik 、ingress-kong 。kubespray 选择ingress-nginx做为ingress controller控制器,其功能特性最为贴合与生产应用需求。例如下图展示了在生产环境应用中我们会通过采集nginx access.log 日志获取一些关键指标:
- PV、UV 用户访问流量
- 应用API TOP10 流量
- 2XX 状态码
- 5XX 状态码
- 请求成功率
![](https://img.kancloud.cn/3f/39/3f39e984c98709265f0a4a4c1f180586_2482x1181.png)
可以通过ingress configmap 给ingress-nginx 定制acces.log日志格式。cm-ingress-nginx.yml 配置内容如下:
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ingress-nginx
namespace: kube-system
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
data:
enable-underscores-in-headers: 'true'
map-hash-bucket-size: '128'
ssl-protocols: SSLv2 TLSv1 TLSv1.1 TLSv1.2
enable-underscores-in-headers: "true"
client-header-timeout: "120"
access-log-path: /var/log/nginx/ingress.access.log
error-log-path: /var/log/nginx/ingress.error.log
log-format-escape-json: 'true'
log-format-upstream: '{ "datetime": "$time_iso8601", "remote_addr": "$remote_addr", "x-forward-for": "$http_x_forwarded_for", "remote_user": "$remote_user", "bytes_sent": $bytes_sent, "request_time": $request_time, "status":$status, "vhost": "$host", "request_proto": "$server_protocol", "path": "$uri", "request_query": "$args", "request_length": $request_length, "upstream_addr": "$upstream_addr", "upstream_response_time": "$upstream_response_time", "method": "$request_method", "http_referrer": "$http_referer", "http_user_agent": "$http_user_agent" }'
consolelog-format-upstream: '{ "datetime": "$time_iso8601", "remote_addr": "$remote_addr", "x-forward-for": "$http_x_forwarded_for", "remote_user": "$remote_user", "bytes_sent": $bytes_sent, "request_time": $request_time, "status":$status, "vhost": "$host", "request_proto": "$server_protocol", "path": "$uri", "request_query": "$args", "request_length": $request_length, "upstream_addr": "$upstream_addr", "upstream_response_time": "$upstream_response_time", "method": "$request_method", "http_referrer": "$http_referer", "http_user_agent": "$http_user_agent" }'
核心参数说明:
- ssl-protocols 开启SSL 功能。注意默认只添加了SSLv2,需要完整SSL 协议栈
- access-log-path:访问日志访问路径
- error-log-path:错误日志访问路径
- log-format-upstream: 日志输出格式
- consolelog-format-upstream:conso 日志输出格式
日志采集方式:
kubespray 使用DaemonSet + nodeSelector 策略,让ingress组件绑定部署在三台master实例主机上。通过上面的设置把ingress-nginx访问日志输出在pod /var/nginx/log/目录里,需要在DaemonSet 配置中添加volume 把日志文件映射到宿主机指定目录(/opt/ingress_log/)。
ds-ingress-nginx-controller.yml 配置文件内容如下:
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ingress-nginx-controller
namespace: kube-system
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
spec:
serviceAccountName: ingress-nginx
hostNetwork: true
nodeSelector:
node-role.kubernetes.io/master: ''
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/master
operator: Equal
value: 'unschedulable'
priorityClassName: system-node-critical
volumes:
- name: ingresslogs
hostPath:
path: /opt/ingress_logs/
containers:
- name: ingress-nginx-controller
image: harbor.corp.jccfc.com/kubernetes-ingress-controller/nginx-ingress-controller:0.24.1
imagePullPolicy: Always
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/ingress-nginx
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --annotations-prefix=nginx.ingress.kubernetes.io
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# www-data -> 33
runAsUser: 33
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: ingresslogs
mountPath: /var/log/nginx/
ports:
- name: http
containerPort: 80
hostPort: 80
- name: https
containerPort: 443
hostPort: 443
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
### etcd备份与内存优化
etcd 是一个分布式的k/V存储系统。核心使用了RAFT分布式一致性协议。etcd 集群都是至少 3 台机器,官方也说明了集群容错为 (N-1)/2,所以备份数据一般都是用不到,但是作为生产核心系统也是运维同学不得不考虑的重要一环。etcd使用快照Snapshot文件备份数据,默认备份的快照文件保存在本地磁盘,或者同步到备份服务器。如何保证备份服务器的数据安全又会引出一个新的问题。而在公有云托管k8s服务etcd 的快照文件会自动的备份在对象存储中。同过备份在对象存储解决了这个矛盾的问题。
etcd 备份方法:
在初始创建集群时我们需要修改etcd 两个内存参数:
- --quota-backend-bytes
ETCD db数据大小,默认是2G,当数据达到2G的时候就不允许写入,必须对历史数据进行压缩才能继续写入; 参加1里面说的,我们启动的时候就应该提前确定大小,官方推荐是8G,这里我们也使用8G的配置
- etcd-memory-limit
## CNI 网络插件
如何选择CNI网络插件关乎整个k8s集群的稳定性及性能,是至关重要的一环。常见的开源CNI 网络解决方案有flannel、Calico 。云厂商也有各家的专属CNI插件,任爷爷家的ICAN与马爸爸家的Terway各家又有独特优势再次一一解答。
- flannel
Flannel是CoreOS开源的CNI网络插件,下图flannel官网提供的一个数据包经过封包、传输以及拆包的示意图,从这个图片里面里面可以看出两台机器的docker0分别处于不同的段:10.1.20.1/24 和 10.1.15.1/24 ,如果从Web App Frontend1 pod(10.1.15.2)去连接另一台主机上的Backend Service2 pod(10.1.20.3),网络包从宿主机192.168.0.100发往192.168.0.200,内层容器的数据包被封装到宿主机的UDP里面,并且在外层包装了宿主机的IP和mac地址。这就是一个经典的overlay网络,因为容器的IP是一个内部IP,无法从跨宿主机通信,所以容器的网络互通,需要承载到宿主机的网络之上。
flannel的支持多种网络模式,常用用都是vxlan、UDP、hostgw、ipip,vxlan和UDP的区别是vxlan是内核封包,而UDP是flanneld用户态程序封包,所以UDP的方式性能会稍差;hostgw模式是一种主机网关模式,容器到另外一个主机上容器的网关设置成所在主机的网卡地址,这个和calico非常相似,只不过calico是通过BGP声明,而hostgw是通过中心的etcd分发,所以hostgw是直连模式,不需要通过overlay封包和拆包,性能比较高,但hostgw模式最大的缺点是必须是在一个二层网络中,毕竟下一跳的路由需要在邻居表中,否则无法通行。
![](https://img.kancloud.cn/03/7e/037e5480319cedd1e662c925bce23b3e_1130x779.jpg)
- ICAN
任爷爷家的ICAN网络基于底层VPC网络,另构建了独立的VXLAN隧道化容器网络,适用于一般场景。VXLAN是将以太网报文封装成UDP报文进行隧道传输。容器网络是承载于VPC网络之上的Overlay网络平面,具有付出少量隧道封装性能损耗,获得了通用性强、互通性强、高级特性支持全面(例如Network Policy网络隔离)的优势,可以满足大多数应用需求。
![](https://img.kancloud.cn/b2/f1/b2f137dab370c74381661496d57a88ef_589x516.png)
ICAN 容器网络相比开源的flannel网络性能提升20%。
![](https://img.kancloud.cn/6c/ef/6cefa1dda42b3e3b08543b51f339e138_1287x722.png)
- Terway网络插件
Terway网络插件是阿里云容器服务自研的网络插件,使用原生的弹性网卡分配给Pod实现Pod网络:
* 将阿里云的弹性网卡和辅助IP分配给容器。
* 支持基于Kubernetes标准的NetworkPolicy来定义容器间的访问策略,兼容Calico的Network Policy。
![](https://img.kancloud.cn/aa/50/aa502140e88117b324f4c506b6b392d0_1850x739.png)
在Terway网络插件中,每个Pod拥有自己网络栈和IP地址。同一台ECS内的Pod之间通信,直接通过机器内部的转发,跨ECS的Pod通信,报文通过VPC的弹性网卡直接转发。由于不需要使用VxLAN等的隧道技术封装报文,因此具有较高的通信性能。
通过对以上CNI 网络插件的分析简单总结如下:
- 如果你选择自建k8s集群,flannel 网络性能较低
- pod IP 不能与VPC网络互通,在容器化改造中会带来诸多的不便之处。例如开发者无法直连pod进行debug 调试。