在 Kubernetes 中面向虛擬機節點分發文件、執行腳本
本文轉載自微信公眾號「問其」,作者陳少文。轉載本文請聯系問其公眾號。
1. 本文主要討論什么
勿在浮沙筑高臺。業務量的增長、業務形態的進化都需要堅實強勁的 IT 系統支撐。業務內容對市場是透明的,但是 IT 系統不是一朝一夕能建設完善的。未來公司之間的競爭主要也會來自于 IT 系統之間的競爭,能不能快速響應業務需求是決勝的關鍵。
IT 系統也在不斷進化。建設高效、智能的 IT 系統成本是很高的。剛開始只需要夠用,接著是好用,最后成為核心競爭力。
變化并不可怕,可怕的是沉重的歷史包袱。對于技術人員來說,新需求不是什么難事,難的是在高速飛行狀態下更換部件。既能保證原有功能正常,又能滿足新的需求,還要更替 IT 基礎設施。
在進行容器化、Kubernetes 化轉變的過程中,如何直接給虛擬機 (VM) 分發文件,在虛擬機上執行腳本是本文思考的重點。直接操作虛擬機,不符合云原生不可變的基礎設施定義,但歷史業務場景要求,作為 IT 平臺方需要提供解決方案。本文將對此給出答案。
2. 為什么需要一個 PaaS 平臺
當一個 IT 運維團隊開始建設 PaaS 時,他們才真正算站起來了。
在目前的環境下,業務模式、形態不再是商業機密。信息、人員的快速流動,讓公司之間赤裸對峙。你有的業務,我也可以有;我有的功能,你也可以加。業務短時間爆發增長的時代已過,我們正處于一個精細化運營、數據化決策的時代。
新時代對 IT 系統有著更多的需求,這些需求在傳統模式下是無法滿足的。傳統的模式是針對特定的場景開發 SaaS 服務,將技能封裝在固定的流程中,降本增效,控制風險。這在早期也夠用,但隨著業務規模發展,運維人員會陷入無休止地加班改功能、加功能的狀態。
PaaS 的目的是為了抽象一些公共的功能。中臺也是這樣建設的,不變的領域實現落地到平臺,對外提供服務接口。讓前端直接與業務綁定在一起,應對市場的快速變化。
當有 PaaS 平臺時,IT 技能才會有一個沉淀的方向,IT 人員才能從重復、繁雜的任務中抽離出來思考業務,通過拼裝才能快速支撐業務。
3. 如何實現文件分發、腳本執行
3.1 在傳統 PaaS 平臺下
如果讓一個運維人員批量分發一個文件、執行一個腳本,他使用 Ansible 可以很快實現。
但是上面提到要解放雙手,建設 PaaS 平臺。下面是一張傳統的 IT 設施架構圖:
在傳統的 IT 流程中,購買的每一臺機器都需要在 CMDB 中注冊登記,然后安裝 Agent 進行管理。通過 Agent 提供的文件、腳本管道,上層的平臺可以實現文件分發、腳本執行的功能。
但 Agent 的開發成本很高。無數次的業務故障才能打磨出一個高并發、高性能、高可用、高穩定性、高安全性的 Agent。在一些開源的解決方案中,Agent 作為公司 IT 核心不會開放源碼。
3.2 在 Kubernetes 下
在云原生的背景下,直接修改 IaaS 層 VM 的狀態是不被允許的,稱之為不可變的基礎設施。在有些實踐中,甚至會禁用容器的 SSHD,一旦有 SSH 登錄,容器會即刻退出。
在 Kubernetes 下是不提倡直接向節點分發文件、執行腳本的。
不可變的基礎設施 (IaC) 的邏輯是為了保證狀態能復現,符合聲明式的語義。直接修改基礎設施是一個過程式的操作,基礎設施處于正在運行的狀態,存在很多的不確定性,無法準確描述。
下面是云原生下的 IT 設施架構圖:
Kubernetes 接管了 IaaS 層的資源,控制著整個系統的運作。而業務的服務主要通過鏡像倉庫下發,業務的日志采集和監控還需要借助其他開源組件。
4. Kubernetes 分發文件、執行腳本計劃
4.1 演練的準備
下面是清單:
- 一個 Kuberentes 集群,需要能執行 kubectl 命令
- 待分發的 VM 已經添加到集群節點中
- Docker 環境以及 Dockerhub 賬戶
4.2 演練的內容
- 演練分為如下步驟:
- 準備執行的腳本和文件
- 構建并推送鏡像
創建 Kubernetes Job 進行分發
4.3 演練的目標
- 演練的目標如下:
- 在虛擬機上運行一個 Web 服務,提供文件下載功能
將一個文件分發到虛擬機,并添加到下載服務中
5. Kubernetes 分發文件、執行腳本
5.1 集群描述
- kubectl get node -o wide
- NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
- test Ready master,worker 6d2h v1.17.9 10.160.6.35 <none> CentOS Linux 7 (Core) 3.10.0-957.21.3.el7.x86_64 docker://20.10.6
由于預算有限,這里沒有布置多節點的環境。但為了貼合真實場景,在執行 Job 時會使用 nodeSelector 選擇指定的節點,而不會讓分發過程失控。
5.2 準備分發文件、執行腳本
1.文件目錄結構
- demo
- Dockerfile
- start.sh
以下構建鏡像相關的命令都是在 demo 目錄中執行。
2.腳本 start.sh 內容
- cd /data
- nohup python -m SimpleHTTPServer 8000&
Kubernetes 集群使用的是 CentOS 7 操作系統,自帶 Python 2 的解釋器。這里為了簡單,使用 SimpleHTTPServer 提供下載服務。
3.Dockerfile 內容
- FROM docker.io/alpine:3.12
- ARG file
- ADD ${file} /data/
4.待分發的文件內容
文件可以是構建環境中的本地文件,也可以是任意的 URL 文件鏈接。這里我選擇的是一個 PDF 的文件鏈接:https://www.chenshaowen.com/static/file/ui-autotest.pdf
5.3 構建鏡像
在 Kubernetes 中通用的是 OCI 鏡像,因此需要對文件、腳本進行封裝,將文件、腳本打包到鏡像中,通過鏡像倉庫進行分發。
將待分發的文件打包到鏡像中
- docker build --build-arg file=https://www.chenshaowen.com/static/file/ui-autotest.pdf -t shaowenchen/file-1:latest ./
推送鏡像:
- docker push shaowenchen/file-1:latest
將待執行的腳本打包到鏡像中
- docker build --build-arg file=./start.sh -t shaowenchen/shell-1:latest ./
推送鏡像:
- docker push shaowenchen/shell-1:latest
- 查看 Dockerhub 中的鏡像
5.4 Kubernetes 節點預處理
除了待分發的節點需要添加到 Kubernetes 集群,另外一個重要的地方是需要對節點進行預處理。
節點預處理主要是給節點添加 label,對節點進行標記,便于準確分發。在生產中,通常網絡是分區的,因此引入兩個維度的標記:zone 和 ip。
- 標記節點 zone 、ip
zone 表示分區,這里標記為 a。ip 表示虛擬機在這個分區中的 IP 地址。在實踐過程中,可以在安裝 Kubernetes 集群時批量處理。
- kubectl label node test zone=a
- kubectl label node test ip=10.160.6.35
- 查看標記的標簽
- kubectl get nodes --show-labels
- NAME STATUS ROLES AGE VERSION LABELS
- test Ready master,worker 6d2h v1.17.9 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ip=10.160.6.35,kubernetes.io/arch=amd64,kubernetes.io/hostname=test,kubernetes.io/os=linux,node-role.kubernetes.io/master=,node-role.kubernetes.io/worker=,zone=a
5.5 向指定節點分發腳本并執行
- cat <<EOF | kubectl apply -f -
- apiVersion: batch/v1
- kind: Job
- metadata:
- name: shell-1
- spec:
- template:
- spec:
- containers:
- - name: shell-1
- spec:
- containers:
- - name: shell-1
- command: ["sh"]
- args: ["-c", "cp /data/start.sh /hostdata/; echo 'sh /data/start.sh&' | nsenter -t 1 -m -u -i -n;sleep 99999"]
- image: shaowenchen/shell-1:latest
- securityContext:
- privileged: true
- volumeMounts:
- - name: hostdata
- mountPath: /hostdata
- hostIPC: true
- hostNetwork: true
- hostPID: true
- volumes:
- - name: hostdata
- hostPath:
- path: /data/
- restartPolicy: Never
- nodeSelector:
- zone: a
- ip: 10.160.6.35
- EOF
由于鏡像很小,很快腳本就能得到執行。登錄到虛擬機上,查看是否有相關的服務進程:
- ps aux |grep SimpleHTTPServer
- root 16523 0.1 0.0 198028 10120 ? S 22:38 0:00 python -m SimpleHTTPServer 8000
- root 17558 0.0 0.0 112684 1000 pts/1 S+ 22:39 0:00 grep --color=auto SimpleHTTPServer
表明 SimpleHTTPServer 服務已經在虛擬機上運行成功
5.6 向指定節點分發文件
- cat <<EOF | kubectl apply -f -
- apiVersion: batch/v1
- kind: Job
- metadata:
- name: file-1
- spec:
- template:
- spec:
- containers:
- - name: file-1
- spec:
- containers:
- - name: file-1
- command: ["sh"]
- args: ["-c", "cp -R /data/* /hostdata/; sleep 10"]
- image: shaowenchen/file-1:latest
- securityContext:
- privileged: true
- volumeMounts:
- - name: hostdata
- mountPath: /hostdata
- hostIPC: true
- hostNetwork: true
- hostPID: true
- volumes:
- - name: hostdata
- hostPath:
- path: /data/
- restartPolicy: Never
- nodeSelector:
- zone: a
- ip: 10.160.6.35
- EOF
通過頁面訪問,可以查看到提供的下載頁面:
在虛擬機上查看分發的文件:
- ls /data/
- start.sh ui-autotest.pdf
6. 總結
本文主要是在 Kubernetes 下,演示了面向虛擬機如何進行文件分發、腳本執行,給大家在設計 PaaS 平臺時提供一點思路。
將 Kubelet 當做傳統的 Agent 使用。Kubelet 管理 Pod ,而 Agent 管理 IaaS。兩者之間有共同點可以思考。另外,Kubernetes 單集群支持高達 5000 個節點,能滿足絕大部分需求場景。通過多集群可以支持更多節點。
可以支持更多來源的二進制分發。示例中使用的是 https 文件,也可以使用本地文件,還可以將 S3 中的文件下載到本地再打包。同時,最終的鏡像只比原始文件大幾 M。
腳本執行可以繼續優化。當 Job 執行完成時,腳本執行也會結束。在實踐過程中,應該向主機添加托管的服務。這里為了演示簡便,沒有深究。
直接使用 hostIPC/hostPID 的 Pod 替代傳統虛擬機上的服務進程也是一種方案。