# 賢文雜談-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

從架構圖中我們獲取到kubespray使用三個獨立master方式分別部署。在集群kube-system 命名空間巧妙的增加了一個nginx-proxy組件,客戶端節點kube-node 通過 nginx-proxy反向代理請求kube-apiserver。
我們在任爺爺家創建容器服務后會發現,CCE的容器服務也是提供了獨立的三master 集群模式。雖然master空面需要單獨付費,但是這樣能更能保證用戶的數據安全性及穩定性。

### 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` 命令可以查看當前驅動方式:

### 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 狀態碼
- 請求成功率

可以通過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模式最大的缺點是必須是在一個二層網絡中,畢竟下一跳的路由需要在鄰居表中,否則無法通行。

- ICAN
任爺爺家的ICAN網絡基于底層VPC網絡,另構建了獨立的VXLAN隧道化容器網絡,適用于一般場景。VXLAN是將以太網報文封裝成UDP報文進行隧道傳輸。容器網絡是承載于VPC網絡之上的Overlay網絡平面,具有付出少量隧道封裝性能損耗,獲得了通用性強、互通性強、高級特性支持全面(例如Network Policy網絡隔離)的優勢,可以滿足大多數應用需求。

ICAN 容器網絡相比開源的flannel網絡性能提升20%。

- Terway網絡插件
Terway網絡插件是阿里云容器服務自研的網絡插件,使用原生的彈性網卡分配給Pod實現Pod網絡:
* 將阿里云的彈性網卡和輔助IP分配給容器。
* 支持基于Kubernetes標準的NetworkPolicy來定義容器間的訪問策略,兼容Calico的Network Policy。

在Terway網絡插件中,每個Pod擁有自己網絡棧和IP地址。同一臺ECS內的Pod之間通信,直接通過機器內部的轉發,跨ECS的Pod通信,報文通過VPC的彈性網卡直接轉發。由于不需要使用VxLAN等的隧道技術封裝報文,因此具有較高的通信性能。
通過對以上CNI 網絡插件的分析簡單總結如下:
- 如果你選擇自建k8s集群,flannel 網絡性能較低
- pod IP 不能與VPC網絡互通,在容器化改造中會帶來諸多的不便之處。例如開發者無法直連pod進行debug 調試。