k8s 是幹什麼的?容器編排解決的問題與 Docker 的區別
k8s 就是 Kubernetes,一個容器編排平台。你告訴它“這個網站跑 3 個副本、用這個映象、對外提供 80 連接埠”,它負責把容器安排到合適的伺服器上,掛了自動拉起,按負載增減副本,發新版時逐個替換,並給這組容器一個固定的存取位址。Docker 管的是一台機器上的容器,k8s 管的是一群機器上的容器。
k8s 是幹什麼的?一句話:在多台伺服器上自動部署、執行和管理容器。你用一份 YAML 檔案描述想要的狀態,比如“網站要 3 個副本、用 1.4.2 版本的映象、每個副本要半核 CPU 和 256 MB 記憶體”,k8s 負責把它變成現實,並一直維持下去:容器掛了自動重啟,伺服器當機就在別的伺服器上重建,流量上來了加副本,發新版時一個一個替換。k8s 是 Kubernetes 的縮寫,K 和 s 之間正好 8 個字母,於是寫成 k8s。這個詞來自希臘語,意思是舵手。它由 Google 在 2014 年開源,吸收了 Google 內部叢集管理系統 Borg 的經驗,2015 年釋出 1.0 並捐給了新成立的雲原生計算基金會(CNCF),現在是主流的容器編排平台。
從部署一個網站說起:沒有 k8s 時要做什麼
假設你要維運一個電商網站:前面是 nginx,中間是 3 個應用例項,後面是 Redis 和 MySQL,一共 3 台伺服器。不用任何編排工具,日常工作是這樣的:
- 部署:登入每台伺服器裝執行環境、上傳程式碼、改配置、啟動程序。三台機器的系統補丁、依賴版本稍有不同,就會出現“這台好的、那台報錯”。
- 故障:凌晨應用程序崩潰,靠監控告警叫醒值班人員去重啟;一台伺服器硬體故障,上面的例項要手工挪到另外兩台上,還要改 nginx 的 upstream 列表。
- 擴容:大促前加兩台機器,重複一遍部署步驟,再把新例項加進負載均衡;活動結束後再手工撤下來。
- 發版:逐台停服務、換包、啟動、驗證;第二台出問題,再逐台回滾。
- 互相存取:應用連線 Redis 和 MySQL 的位址寫在配置檔案裡,換一台機器就要把配置改一圈。
Docker 解決的是第 1 件事:把應用和依賴打成映象,在任何裝了 Docker 的機器上執行結果都一樣。但後面 4 件事跨越了多台機器,Docker 本身管不了,這正是 k8s 要做的。
k8s 解決的五個問題
k8s 的工作方式可以概括為“宣告期望狀態,由控制器持續糾偏”:你只說要什麼,不說怎麼做;叢集裡的各個控制器不停地對比實際狀態和期望狀態,發現不一致就動手修正。上面的五件事對應到 k8s 裡是這樣的:
| 問題 | k8s 的做法 | 相關的物件或命令 |
|---|---|---|
| 排程:容器放在哪台機器上 | 你宣告每個容器需要的 CPU 和記憶體(requests),排程器在叢集裡挑資源夠用的節點放置 | Pod、resources、排程器 |
| 自愈:掛了怎麼辦 | 容器程序退出會被重啟;健康檢查失敗的容器也會被重啟;節點失聯後,由 Deployment 管理的 Pod 預設約 5 分鐘後在其他節點重建 | 重啟策略、存活探針、Deployment |
| 擴縮容 | 改副本數即可;配合 HPA(水平自動擴縮)可以按 CPU 使用率等指標自動增減,前提是叢集裡裝了 metrics-server | kubectl scale、HorizontalPodAutoscaler |
| 滾動更新與回滾 | 換映象版本後分批替換,預設多出或缺少的 Pod 都不超過副本數的 25%,新 Pod 透過就緒檢查後才繼續替換下一批;出問題一條命令退回上一版 | kubectl set image、kubectl rollout undo |
| 服務發現與負載均衡 | Service 給一組 Pod 一個固定的虛擬 IP 和 DNS 名稱,請求在這組 Pod 之間分發,Pod 重建換了 IP 也不用改配置 | Service、叢集 DNS |
另外,配置和密碼可以從映象裡拆出來,放進 ConfigMap 和 Secret,同一個映象在測試和生產環境掛不同的配置。
把上面那個網站的應用層寫成 k8s 的清單,大致是這樣(映象位址是範例):
apiVersion: apps/v1
kind: Deployment
metadata:
name: shop-web
spec:
replicas: 3
selector:
matchLabels:
app: shop-web
template:
metadata:
labels:
app: shop-web
spec:
containers:
- name: web
image: registry.example.internal/shop/web:1.4.2
ports:
- containerPort: 8080
resources:
requests:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /healthz
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: shop-web
spec:
selector:
app: shop-web
ports:
- port: 80
targetPort: 8080
Deployment 描述“跑 3 個副本、用哪個映象、怎麼判斷它準備好了”,Service 給這 3 個副本一個統一入口:叢集裡的其他程式存取 shop-web 這個名字(完整寫法是 shop-web.default.svc.cluster.local)的 80 連接埠,請求會被分到某個就緒的副本上。日常操作用 kubectl:
kubectl apply -f shop-web.yaml # 建立或更新
kubectl get pods -l app=shop-web -o wide # 3 個 Pod 分別在哪個節點上
kubectl delete pod shop-web-7d9c8b6f5-x2k4q # 模擬故障:刪掉一個,幾秒後會出現新的補位
kubectl scale deployment shop-web --replicas=6 # 擴到 6 個副本
kubectl set image deployment/shop-web web=registry.example.internal/shop/web:1.4.3
kubectl rollout status deployment/shop-web # 觀察滾動更新進度
kubectl rollout undo deployment/shop-web # 新版本有問題,退回上一版
delete pod 那一條最能說明 k8s 的思路:你沒有告訴它“再起一個”,是 Deployment 控制器發現實際只有 2 個副本、少於期望的 3 個,自己補上的。
k8s 和 Docker、Docker Compose 的區別
三者經常被放在一起比較,但它們不在同一層:
- Docker 做兩件事:用
docker build把應用打成映象,用docker run在一台機器上執行容器。 - Docker Compose 用一個 compose.yaml 描述多個容器組成的應用(web、redis、db),
docker compose up -d一條命令在一台機器上全部拉起,容器之間按服務名互相存取。 - k8s 不負責構建映象,它負責在多台機器上執行和管理容器。映象照樣用 Docker 構建、推到映象倉庫,k8s 的節點再從倉庫拉取。節點上實際啟動容器的是 containerd、CRI-O 這類容器執行時;k8s 從 1.24 起不再直接對接 Docker Engine,但 Docker 構建的映象符合 OCI 標準,在 k8s 裡照常執行。
所以“k8s 和 Docker 的區別”的答案是:它們是上下游關係,不是二選一。Docker 也有自己的叢集編排 Docker Swarm,概念少、上手快,但周邊生態和社群規模不如 k8s。
按網站那個例子,把四種方式放在一起對比:
| 對比項 | 手工維運 | Docker | Docker Compose | k8s |
|---|---|---|---|---|
| 交付的東西 | 程式碼包加部署文件 | 映象 | 映象加 compose.yaml | 映象加 YAML 清單 |
| 環境一致性 | 靠人和文件 | 映象保證 | 映象保證 | 映象保證 |
| 管理範圍 | 每台單獨操作 | 一台機器上的單個容器 | 一台機器上的一組容器 | 整個叢集 |
| 程序崩潰 | 告警後人工重啟 | 設定重啟策略後自動重啟 | 同 Docker | 自動重啟,健康檢查失敗也會重啟 |
| 伺服器當機 | 人工遷移 | 無法處理 | 無法處理 | 在其他節點重建 |
| 擴容 | 加機器、重複部署 | 手工多起幾個容器 | --scale 在單機上加副本 |
改副本數,或按指標自動擴縮 |
| 發版與回滾 | 逐台操作 | 手工停舊起新 | 重建容器,期間會短暫中斷 | 滾動更新,一條命令回滾 |
| 服務互相存取 | 配置裡寫死 IP | 連接埠對映加手工配置 | 同一專案內按服務名存取 | Service 名稱和叢集 DNS |
| 學習與維護成本 | 低 | 低 | 低 | 高:控制平面、網路、儲存、版本升級都要有人管 |
什麼規模的業務值得上 k8s
k8s 的收益來自“機器多、服務多、變化多”,成本則是一整套需要專人維護的系統。可以對照下面幾條判斷:
| 判斷依據 | 傾向於上 k8s | 傾向於先不上 |
|---|---|---|
| 服務數量 | 多個獨立部署的服務,或者多個專案想共用一套基礎設施 | 一個應用加一個資料庫 |
| 伺服器數量 | 3 台以上,並且還在增加 | 一兩台 |
| 釋出頻率 | 每週多次甚至每天發版 | 一個月一兩次 |
| 流量 | 峰谷明顯,需要臨時擴容 | 平穩 |
| 團隊 | 有人能負責叢集升級、網路和儲存排障 | 沒有專人,出了問題沒人能修 |
| 應用形態 | 能容器化的無狀態 Web、API、後台任務 | 強依賴本機狀態的老系統、Windows 桌面類軟體 |
左列佔了多數,k8s 帶來的自動化值得它的維護成本;只有一兩台伺服器、一個月發一次版,用 Docker Compose 加 systemd 管理就足夠,出問題時排查路徑也短得多。
維護成本要提前算清楚:k8s 每年釋出三個左右的小版本,每個小版本的補丁支援約 14 個月,升級又只能逐個小版本進行,所以一個叢集至少每年要升級一次;控制平面的證書、etcd 的備份、網路外掛和儲存外掛的版本,也都要有人跟。沒有人力承擔這些,叢集跑上一兩年就會變成不敢動的系統。
機房裡幾台伺服器怎麼起步
用自有機房的物理機起步,最常見的形態是 3 台伺服器組成一個小叢集,每台既是控制平面又跑業務。選 3 台的原因是 etcd(k8s 存放全部叢集狀態的資料庫)靠多數派工作:3 個節點能容忍壞 1 台,2 個節點壞 1 台就失去多數,和 1 台沒有區別。資料庫先留在叢集外的獨立伺服器上。
搭建工具按需要選:
| 方式 | 特點 | 適合 |
|---|---|---|
| kubeadm | 官方工具,元件標準;網路外掛、對外入口、儲存都要自己選擇和安裝 | 想按標準方式搭建,後續規模會變大 |
| k3s | 單個二進位制,預設自帶 flannel 網路、Traefik 入口、ServiceLB 負載均衡和本地儲存,高可用模式內建 etcd | 小叢集,想先跑起來再說 |
| 雲廠商託管 | 控制平面由廠商維護 | 業務在雲上而不在自有機房 |
以 k3s 為例,3 台伺服器內網位址為 10.10.30.11 至 10.10.30.13,以 root 執行:
# 第一台:生成共享金鑰,以內建 etcd 初始化叢集
TOKEN=$(openssl rand -hex 16); echo "$TOKEN"
curl -sfL https://get.k3s.io | K3S_TOKEN=$TOKEN sh -s - server --cluster-init
# 第二、三台:用第一台輸出的金鑰加入,同樣作為 server 參與 etcd
curl -sfL https://get.k3s.io | K3S_TOKEN=第一台輸出的金鑰 sh -s - server --server https://10.10.30.11:6443
# 任意一台上確認三個節點都是 Ready
k3s kubectl get nodes -o wide
國內網路存取安裝指令碼和 GitHub 較慢時,按 k3s 官方文件裡的說明改用國內映象,或者用離線(air-gap)方式安裝,映象位址以文件當前寫法為準。叢集起來只是第一步,物理機上沒有云平台代勞的那些東西,要自己補齊:
| 要補的 | 為什麼 | 常見做法 |
|---|---|---|
| 對外入口 | 沒有云負載均衡,LoadBalancer 型別的 Service 拿不到位址 | k3s 自帶 ServiceLB;kubeadm 叢集常用 MetalLB,從機房分配的一段位址中給 Service 分配 IP |
| 持久化儲存 | Pod 換了節點還要能讀到原來的資料 | 起步用本地儲存,資料跟著節點走;要跨節點就用 NFS、Ceph 或 Longhorn |
| 映象倉庫 | 節點從倉庫拉映象,公網倉庫在國內可能拉不下來 | 自建 Harbor 等私有倉庫 |
| 備份 | 全部叢集狀態都在 etcd 裡 | k3s 內建 etcd 時預設定時生成快照,要把快照再拷到叢集之外 |
| 監控與日誌 | 容器隨時會被重建,登入節點翻日誌不現實 | Prometheus 採集指標,日誌集中收集 |
| 網段 | Pod、Service 網段不能和機房已有的網段重疊 | k3s 預設 Pod 網段 10.42.0.0/16、Service 網段 10.43.0.0/16,衝突時在安裝時修改 |
容量也要按“壞一台”來算:3 個節點要能在其中 1 台故障時把所有 Pod 挪到另外 2 台上,所以平時所有 Pod 的 requests 加起來,不宜超過叢集總容量的三分之二左右。
在機房裡怎麼落地
k8s 的思路是把節點當成可以隨時替換的資源:節點出了問題,與其登入上去慢慢修,不如把它移出叢集、重灌系統、再加回來。這要求節點之間的硬體和系統儘量一致,重灌也要足夠快。Toplink DCIM 的 IPMI 遠端管理可以把一批伺服器設為從 PXE 啟動、由任務佇列批次重灌,壞掉的節點遠端重灌後重新加入叢集即可;機櫃與資產會向伺服器下發採集指令碼,記錄每台機器的 CPU、記憶體、硬碟配置並保留歷史,節點的配件被更換或者配置對不上,在台帳裡能直接看出來,不會等到排程出問題才發現。
常見問題
學 k8s 之前要先學 Docker 嗎?
建議先學。k8s 裡執行的就是容器映象,映象怎麼構建、容器怎麼啟動、連接埠和資料卷怎麼對映,這些在 Docker 上更容易理解。會用 Docker 和 Docker Compose 跑起一個多容器應用,再學 k8s 的 Pod、Deployment、Service 會順很多。
資料庫適合放進 k8s 嗎?
能放,但不建議起步就放。資料庫依賴穩定的儲存和網路,遷移、備份、故障切換都比無狀態服務複雜。常見做法是先把 Web、API 這類無狀態服務搬進 k8s,資料庫繼續跑在獨立的伺服器上,等團隊熟悉了持久化儲存和相關的維運工具後再評估。
k8s 能管理虛擬機器嗎?
原生的 k8s 只管容器。社群有 KubeVirt 這類擴充套件專案,可以在 k8s 上執行和管理虛擬機器,但這屬於額外的元件。只是想把物理機切成虛擬機器,用 ESXi、Proxmox VE 等虛擬化平台更直接。