Kubernetes 入门进阶实战
作者:oonamao毛江云,騰訊 CSIG 應用開發工程師
寫在前面
筆者今年 9 月從端側開發轉到后臺開發,第一個系統開發任務就強依賴了 K8S,加之項目任務重、排期緊,必須馬上對 K8S 有概念上的了解。然而,很多所謂“K8S 入門\概念”的文章看的一頭霧水,對于大部分新手來說并不友好。經歷了幾天痛苦地學習之后,回顧來看,K8S 根本不復雜。于是,決心有了這一系列的文章:一方面希望對新手同學有幫助;另一方面,以文會友,希望能夠有機會交流討論技術。
本文組織方式:
1.?K8S?是什么,即作用和目的。涉及?K8S?架構的整理,Master?和?Node?之間的關系,以及?K8S?幾個重要的組件:API?Server、Scheduler、Controller、etcd?等。
2.?K8S?的重要概念,即?K8S?的?API?對象,也就是常常聽到的?Pod、Deployment、Service?等。
3.?如何配置?kubectl,介紹kubectl工具和配置辦法。
4.?如何用kubectl?部署服務。
5.?如何用kubectl?查看、更新/編輯、刪除服務。
6.?如何用kubectl?排查部署在K8S集群上的服務出現的問題
I. K8S 概覽
1.1 K8S 是什么?
K8S 是Kubernetes的全稱,官方稱其是:
Kubernetes is an open source system for managing containerized applications across multiple hosts. It provides basic mechanisms for deployment, maintenance, and scaling of applications.
用于自動部署、擴展和管理“容器化(containerized)應用程序”的開源系統。
翻譯成大白話就是:“K8 是 S 負責自動化運維管理多個 Docker 程序的集群”。那么問題來了:Docker 運行可方便了,為什么要用 K8S,它有什么優勢?
插一句題外話:
為什么 Kubernetes 要叫 Kubernetes 呢?維基百科已經交代了(老美對星際是真的癡迷):
Kubernetes(在希臘語意為“舵手”或“駕駛員”)由 Joe Beda、Brendan Burns 和 Craig McLuckie 創立,并由其他谷歌工程師,包括 Brian Grant 和 Tim Hockin 等進行加盟創作,并由谷歌在 2014 年首次對外宣布 。該系統的開發和設計都深受谷歌的 Borg 系統的影響,其許多頂級貢獻者之前也是 Borg 系統的開發者。在谷歌內部,Kubernetes 的原始代號曾經是Seven,即星際迷航中的 Borg(博格人)。Kubernetes 標識中舵輪有七個輪輻就是對該項目代號的致意。
為什么 Kubernetes 的縮寫是 K8S 呢?我個人贊同Why Kubernetes is Abbreviated k8s中說的觀點“嘛,寫全稱也太累了吧,不如整個縮寫”。其實只保留首位字符,用具體數字來替代省略的字符個數的做法,還是比較常見的。
1.2 為什么是 K8S?
試想下傳統的后端部署辦法:把程序包(包括可執行二進制文件、配置文件等)放到服務器上,接著運行啟動腳本把程序跑起來,同時啟動守護腳本定期檢查程序運行狀態、必要的話重新拉起程序。
有問題嗎?顯然有!最大的一個問題在于:**如果服務的請求量上來,已部署的服務響應不過來怎么辦?**傳統的做法往往是,如果請求量、內存、CPU 超過閾值做了告警,運維馬上再加幾臺服務器,部署好服務之后,接入負載均衡來分擔已有服務的壓力。
問題出現了:從監控告警到部署服務,中間需要人力介入!那么,有沒有辦法自動完成服務的部署、更新、卸載和擴容、縮容呢?
這,就是 K8S 要做的事情:自動化運維管理 Docker(容器化)程序。
1.3 K8S 怎么做?
我們已經知道了 K8S 的核心功能:自動化運維管理多個容器化程序。那么 K8S 怎么做到的呢?這里,我們從宏觀架構上來學習 K8S 的設計思想。首先看下圖,圖片來自文章Components of Kubernetes Architecture:
K8S 是屬于主從設備模型(Master-Slave 架構),即有 Master 節點負責核心的調度、管理和運維,Slave 節點則在執行用戶的程序。但是在 K8S 中,主節點一般被稱為Master Node 或者 Head Node(本文采用 Master Node 稱呼方式),而從節點則被稱為Worker Node 或者 Node(本文采用 Worker Node 稱呼方式)。
要注意一點:Master Node 和 Worker Node 是分別安裝了 K8S 的 Master 和 Woker 組件的實體服務器,每個 Node 都對應了一臺實體服務器(雖然 Master Node 可以和其中一個 Worker Node 安裝在同一臺服務器,但是建議 Master Node 單獨部署),所有 Master Node 和 Worker Node 組成了 K8S 集群,同一個集群可能存在多個 Master Node 和 Worker Node。
首先來看Master Node都有哪些組件:
API Server。K8S 的請求入口服務。API Server 負責接收 K8S 所有請求(來自 UI 界面或者 CLI 命令行工具),然后,API Server 根據用戶的具體請求,去通知其他組件干活。
Scheduler。K8S 所有 Worker Node 的調度器。當用戶要部署服務時,Scheduler 會選擇最合適的 Worker Node(服務器)來部署。
Controller Manager。K8S 所有 Worker Node 的監控器。Controller Manager 有很多具體的 Controller,在文章Components of Kubernetes Architecture中提到的有 Node Controller、Service Controller、Volume Controller 等。Controller 負責監控和調整在 Worker Node 上部署的服務的狀態,比如用戶要求 A 服務部署 2 個副本,那么當其中一個服務掛了的時候,Controller 會馬上調整,讓 Scheduler 再選擇一個 Worker Node 重新部署服務。
etcd。K8S 的存儲服務。etcd 存儲了 K8S 的關鍵配置和用戶配置,K8S 中僅 API Server 才具備讀寫權限,其他組件必須通過 API Server 的接口才能讀寫數據(見Kubernetes Works Like an Operating System)。
接著來看Worker Node的組件,筆者更贊同HOW DO APPLICATIONS RUN ON KUBERNETES文章中提到的組件介紹:
Kubelet。Worker Node 的監視器,以及與 Master Node 的通訊器。Kubelet 是 Master Node 安插在 Worker Node 上的“眼線”,它會定期向 Worker Node 匯報自己 Node 上運行的服務的狀態,并接受來自 Master Node 的指示采取調整措施。
Kube-Proxy。K8S 的網絡代理。私以為稱呼為 Network-Proxy 可能更適合?Kube-Proxy 負責 Node 在 K8S 的網絡通訊、以及對外部網絡流量的負載均衡。
Container Runtime。Worker Node 的運行環境。即安裝了容器化所需的軟件環境確保容器化程序能夠跑起來,比如 Docker Engine。大白話就是幫忙裝好了 Docker 運行環境。
Logging Layer。K8S 的監控狀態收集器。私以為稱呼為 Monitor 可能更合適?Logging Layer 負責采集 Node 上所有服務的 CPU、內存、磁盤、網絡等監控項信息。
Add-Ons。K8S 管理運維 Worker Node 的插件組件。有些文章認為 Worker Node 只有三大組件,不包含 Add-On,但筆者認為 K8S 系統提供了 Add-On 機制,讓用戶可以擴展更多定制化功能,是很不錯的亮點。
總結來看,K8S 的 Master Node 具備:請求入口管理(API Server),Worker Node 調度(Scheduler),監控和自動調節(Controller Manager),以及存儲功能(etcd);而 K8S 的 Worker Node 具備:狀態和監控收集(Kubelet),網絡和負載均衡(Kube-Proxy)、保障容器化運行環境(Container Runtime)、以及定制化功能(Add-Ons)。
到這里,相信你已經對 K8S 究竟是做什么的,有了大概認識。接下來,再來認識下 K8S 的 Deployment、Pod、Replica Set、Service 等,但凡談到 K8S,就繞不開這些名詞,而這些名詞也是最讓 K8S 新手們感到頭疼、困惑的。
II. K8S 重要概念
2.1 Pod 實例
官方對于Pod的解釋是:
Pod是可以在 Kubernetes 中創建和管理的、最小的可部署的計算單元。
這樣的解釋還是很難讓人明白究竟 Pod 是什么,但是對于 K8S 而言,Pod 可以說是所有對象中最重要的概念了!因此,我們必須首先清楚地知道“Pod 是什么”,再去了解其他的對象。
從官方給出的定義,聯想下“最小的 xxx 單元”,是不是可以想到本科在學校里學習“進程”的時候,教科書上有一段類似的描述:資源分配的最小單位;還有”線程“的描述是:CPU 調度的最小單位。什么意思呢?”最小 xx 單位“要么就是事物的衡量標準單位,要么就是資源的閉包、集合。前者比如長度米、時間秒;后者比如一個”進程“是存儲和計算的閉包,一個”線程“是 CPU 資源(包括寄存器、ALU 等)的閉包。
同樣的,Pod 就是 K8S 中一個服務的閉包。這么說的好像還是有點玄乎,更加云里霧里了。簡單來說,Pod 可以被理解成一群可以共享網絡、存儲和計算資源的容器化服務的集合。再打個形象的比喻,在同一個 Pod 里的幾個 Docker 服務/程序,好像被部署在同一臺機器上,可以通過 localhost 互相訪問,并且可以共用 Pod 里的存儲資源(這里是指 Docker 可以掛載 Pod 內的數據卷,數據卷的概念,后文會詳細講述,暫時理解為“需要手動 mount 的磁盤”)。筆者總結 Pod 如下圖,可以看到:同一個 Pod 之間的 Container 可以通過 localhost 互相訪問,并且可以掛載 Pod 內所有的數據卷;但是不同的 Pod 之間的 Container 不能用 localhost 訪問,也不能掛載其他 Pod 的數據卷。
對 Pod 有直觀的認識之后,接著來看 K8S 中 Pod 究竟長什么樣子,具體包括哪些資源?
K8S 中所有的對象都通過 yaml 來表示,筆者從官方網站摘錄了一個最簡單的 Pod 的 yaml:
apiVersion:?v1 kind:?Pod metadata:name:?memory-demonamespace:?mem-example spec:containers:-?name:?memory-demo-ctrimage:?polinux/stressresources:limits:memory:?"200Mi"requests:memory:?"100Mi"command:?["stress"]args:?["--vm",?"1",?"--vm-bytes",?"150M",?"--vm-hang",?"1"]volumeMounts:-?name:?redis-storagemountPath:?/data/redisvolumes:-?name:?redis-storageemptyDir:?{}看不懂不必慌張,且耐心聽下面的解釋:
apiVersion記錄 K8S 的 API Server 版本,現在看到的都是v1,用戶不用管。
kind記錄該 yaml 的對象,比如這是一份 Pod 的 yaml 配置文件,那么值內容就是Pod。
metadata記錄了 Pod 自身的元數據,比如這個 Pod 的名字、這個 Pod 屬于哪個 namespace(命名空間的概念,后文會詳述,暫時理解為“同一個命名空間內的對象互相可見”)。
spec記錄了 Pod 內部所有的資源的詳細信息,看懂這個很重要:
containers記錄了 Pod 內的容器信息,containers包括了:name容器名,image容器的鏡像地址,resources容器需要的 CPU、內存、GPU 等資源,command容器的入口命令,args容器的入口參數,volumeMounts容器要掛載的 Pod 數據卷等。可以看到,上述這些信息都是啟動容器的必要和必需的信息。
volumes記錄了 Pod 內的數據卷信息,后文會詳細介紹 Pod 的數據卷。
2.2 Volume 數據卷
K8S 支持很多類型的 volume 數據卷掛載,具體請參見K8S 卷。前文就“如何理解 volume”提到:“需要手動 mount 的磁盤”,此外,有一點可以幫助理解:數據卷 volume 是 Pod 內部的磁盤資源。
其實,單單就 Volume 來說,不難理解。但是上面還看到了volumeMounts,這倆是什么關系呢?
volume 是 K8S 的對象,對應一個實體的數據卷;而 volumeMounts 只是 container 的掛載點,對應 container 的其中一個參數。但是,volumeMounts 依賴于 volume,只有當 Pod 內有 volume 資源的時候,該 Pod 內部的 container 才可能有 volumeMounts。
2.3 Container 容器
本文中提到的鏡像 Image、容器 Container,都指代了 Pod 下的一個container。關于 K8S 中的容器,在 2.1Pod 章節都已經交代了,這里無非再啰嗦一句:一個 Pod 內可以有多個容器 container。
在 Pod 中,容器也有分類,對這個感興趣的同學歡迎自行資料:
標準容器 Application Container。
初始化容器 Init Container。
邊車容器 Sidecar Container。
臨時容器 Ephemeral Container。
一般來說,我們部署的大多是標準容器( Application Container)。
2.4 Deployment 和 ReplicaSet(簡稱 RS)
除了 Pod 之外,K8S 中最常聽到的另一個對象就是 Deployment 了。那么,什么是 Deployment 呢?官方給出了一個要命的解釋:
一個 Deployment 控制器為 Pods 和 ReplicaSets 提供聲明式的更新能力。
你負責描述 Deployment 中的 目標狀態,而 Deployment 控制器以受控速率更改實際狀態, 使其變為期望狀態。你可以定義 Deployment 以創建新的 ReplicaSet,或刪除現有 Deployment,并通過新的 Deployment 收養其資源。
翻譯一下:Deployment 的作用是管理和控制 Pod 和 ReplicaSet,管控它們運行在用戶期望的狀態中。哎,打個形象的比喻,Deployment 就是包工頭,主要負責監督底下的工人 Pod 干活,確保每時每刻有用戶要求數量的 Pod 在工作。如果一旦發現某個工人 Pod 不行了,就趕緊新拉一個 Pod 過來替換它。
新的問題又來了:那什么是 ReplicaSets 呢?
ReplicaSet 的目的是維護一組在任何時候都處于運行狀態的 Pod 副本的穩定集合。因此,它通常用來保證給定數量的、完全相同的 Pod 的可用性。
再來翻譯下:ReplicaSet 的作用就是管理和控制 Pod,管控他們好好干活。但是,ReplicaSet 受控于 Deployment。形象來說,ReplicaSet 就是總包工頭手下的小包工頭。
筆者總結得到下面這幅圖,希望能幫助理解:
新的問題又來了:如果都是為了管控 Pod 好好干活,為什么要設置 Deployment 和 ReplicaSet 兩個層級呢,直接讓 Deployment 來管理不可以嗎?
回答:不清楚,但是私以為是因為先有 ReplicaSet,但是使用中發現 ReplicaSet 不夠滿足要求,于是又整了一個 Deployment(有清楚 Deployment 和 ReplicaSet 聯系和區別的小伙伴歡迎留言啊)。
但是,從 K8S 使用者角度來看,用戶會直接操作 Deployment 部署服務,而當 Deployment 被部署的時候,K8S 會自動生成要求的 ReplicaSet 和 Pod。在K8S 官方文檔中也指出用戶只需要關心 Deployment 而不操心 ReplicaSet:
This actually means that you may never need to manipulate ReplicaSet objects: use a Deployment instead, and define your application in the spec p.
這實際上意味著您可能永遠不需要操作 ReplicaSet 對象:直接使用 Deployments 并在規范部分定義應用程序。
補充說明:在 K8S 中還有一個對象 --- ReplicationController(簡稱 RC),官方文檔對它的定義是:
ReplicationController 確保在任何時候都有特定數量的 Pod 副本處于運行狀態。換句話說,ReplicationController 確保一個 Pod 或一組同類的 Pod 總是可用的。
怎么樣,和 ReplicaSet 是不是很相近?在Deployments, ReplicaSets, and pods教程中說“ReplicationController 是 ReplicaSet 的前身”,官方也推薦用 Deployment 取代 ReplicationController 來部署服務。
2.5 Service 和 Ingress
吐槽下 K8S 的概念/對象/資源是真的多啊!前文介紹的 Deployment、ReplicationController 和 ReplicaSet 主要管控 Pod 程序服務;那么,Service 和 Ingress 則負責管控 Pod 網絡服務。
我們先來看看官方文檔中 Service 的定義:
將運行在一組 Pods 上的應用程序公開為網絡服務的抽象方法。
使用 Kubernetes,您無需修改應用程序即可使用不熟悉的服務發現機制。Kubernetes 為 Pods 提供自己的 IP 地址,并為一組 Pod 提供相同的 DNS 名, 并且可以在它們之間進行負載均衡。
翻譯下:K8S 中的服務(Service)并不是我們常說的“服務”的含義,而更像是網關層,是若干個 Pod 的流量入口、流量均衡器。
那么,為什么要 Service 呢?
私以為在這一點上,官方文檔講解地非常清楚:
Kubernetes Pod 是有生命周期的。它們可以被創建,而且銷毀之后不會再啟動。如果您使用 Deployment 來運行您的應用程序,則它可以動態創建和銷毀 Pod。
每個 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一時刻運行的 Pod 集合可能與稍后運行該應用程序的 Pod 集合不同。
這導致了一個問題:如果一組 Pod(稱為“后端”)為群集內的其他 Pod(稱為“前端”)提供功能, 那么前端如何找出并跟蹤要連接的 IP 地址,以便前端可以使用工作量的后端部分?
補充說明:K8S 集群的網絡管理和拓撲也有特別的設計,以后會專門出一章節來詳細介紹 K8S 中的網絡。這里需要清楚一點:K8S 集群內的每一個 Pod 都有自己的 IP(是不是很類似一個 Pod 就是一臺服務器,然而事實上是多個 Pod 存在于一臺服務器上,只不過是 K8S 做了網絡隔離),在 K8S 集群內部還有 DNS 等網絡服務(一個 K8S 集群就如同管理了多區域的服務器,可以做復雜的網絡拓撲)。
此外,筆者推薦k8s 外網如何訪問業務應用對于 Service 的介紹,不過對于新手而言,推薦閱讀前半部分對于 service 的介紹即可,后半部分就太復雜了。我這里做了簡單的總結:
Service 是 K8S 服務的核心,屏蔽了服務細節,統一對外暴露服務接口,真正做到了“微服務”。舉個例子,我們的一個服務 A,部署了 3 個備份,也就是 3 個 Pod;對于用戶來說,只需要關注一個 Service 的入口就可以,而不需要操心究竟應該請求哪一個 Pod。優勢非常明顯:一方面外部用戶不需要感知因為 Pod 上服務的意外崩潰、K8S 重新拉起 Pod 而造成的 IP 變更,外部用戶也不需要感知因升級、變更服務帶來的 Pod 替換而造成的 IP 變化,另一方面,Service 還可以做流量負載均衡。
但是,Service 主要負責 K8S 集群內部的網絡拓撲。那么集群外部怎么訪問集群內部呢?這個時候就需要 Ingress 了,官方文檔中的解釋是:
Ingress 是對集群中服務的外部訪問進行管理的 API 對象,典型的訪問方式是 HTTP。
Ingress 可以提供負載均衡、SSL 終結和基于名稱的虛擬托管。
翻譯一下:Ingress 是整個 K8S 集群的接入層,復雜集群內外通訊。
最后,筆者把 Ingress 和 Service 的關系繪制網絡拓撲關系圖如下,希望對理解這兩個概念有所幫助:
2.6 namespace 命名空間
和前文介紹的所有的概念都不一樣,namespace 跟 Pod 沒有直接關系,而是 K8S 另一個維度的對象。或者說,前文提到的概念都是為了服務 Pod 的,而 namespace 則是為了服務整個 K8S 集群的。
那么,namespace 是什么呢?
上官方文檔定義:
Kubernetes 支持多個虛擬集群,它們底層依賴于同一個物理集群。這些虛擬集群被稱為名字空間。
翻譯一下:namespace 是為了把一個 K8S 集群劃分為若干個資源不可共享的虛擬集群而誕生的。
也就是說,可以通過在 K8S 集群內創建 namespace 來分隔資源和對象。比如我有 2 個業務 A 和 B,那么我可以創建 ns-a 和 ns-b 分別部署業務 A 和 B 的服務,如在 ns-a 中部署了一個 deployment,名字是 hello,返回用戶的是“hello a”;在 ns-b 中也部署了一個 deployment,名字恰巧也是 hello,返回用戶的是“hello b”(要知道,在同一個 namespace 下 deployment 不能同名;但是不同 namespace 之間沒有影響)。前文提到的所有對象,都是在 namespace 下的;當然,也有一些對象是不隸屬于 namespace 的,而是在 K8S 集群內全局可見的,官方文檔提到的可以通過命令來查看,具體命令的使用辦法,筆者會出后續的實戰文章來介紹,先貼下命令:
#?位于名字空間中的資源 kubectl?api-resources?--namespaced=true#?不在名字空間中的資源 kubectl?api-resources?--namespaced=false不在 namespace 下的對象有:
在 namespace 下的對象有(部分):
2.7 其他
K8S 的對象實在太多了,2.1-2.6 介紹的是在實際使用 K8S 部署服務最常見的。其他的還有 Job、CronJob 等等,在對 K8S 有了比較清楚的認知之后,再去學習更多的 K8S 對象,不是難事。
III. 配置 kubectl
3.1 什么是 kubectl?
官方文檔中介紹 kubectl 是:
Kubectl 是一個命令行接口,用于對 Kubernetes 集群運行命令。Kubectl 的配置文件在$HOME/.kube 目錄。我們可以通過設置 KUBECONFIG 環境變量或設置命令參數--kubeconfig 來指定其他位置的 kubeconfig 文件。
也就是說,可以通過 kubectl 來操作 K8S 集群,基本語法:
使用以下語法 kubectl 從終端窗口運行命令:
kubectl?[command]?[TYPE]?[NAME]?[flags]其中 command、TYPE、NAME 和 flags 分別是:
command:指定要對一個或多個資源執行的操作,例如 create、get、describe、delete。
TYPE:指定資源類型。資源類型不區分大小寫,可以指定單數、復數或縮寫形式。例如,以下命令輸出相同的結果:
就如何使用 kubectl 而言,官方文檔已經說得非常清楚。不過對于新手而言,還是需要解釋幾句:
kubectl 是 K8S 的命令行工具,并不需要 kubectl 安裝在 K8S 集群的任何 Node 上,但是,需要確保安裝 kubectl 的機器和 K8S 的集群能夠進行網絡互通。
kubectl 是通過本地的配置文件來連接到 K8S 集群的,默認保存在$HOME/.kube 目錄下;也可以通過 KUBECONFIG 環境變量或設置命令參數--kubeconfig 來指定其他位置的 kubeconfig 文件【官方文檔】。
接下來,一起看看怎么使用 kubectl 吧,切身感受下 kubectl 的使用。
請注意,如何安裝 kubectl 的辦法有許多非常明確的教程,比如《安裝并配置 kubectl》,本文不再贅述。
1.2 怎么配置 kubectl?
第一步,必須準備好要連接/使用的 K8S 的配置文件,筆者給出一份杜撰的配置:
apiVersion:?v1 clusters: -?cluster:certificate-authority-data:?thisisfakecertifcateauthoritydata00000000000server:?https://1.2.3.4:1234name:?cls-dev contexts: -?context:cluster:?cls-devuser:?kubernetes-adminname:?kubernetes-admin@test current-context:?kubernetes-admin@test kind:?Config preferences:?{} users: -?name:?kubernetes-adminuser:token:?thisisfaketoken00000解讀如下:
clusters記錄了 clusters(一個或多個 K8S 集群)信息:
name是這個 cluster(K8S 集群)的名稱代號
server是這個 cluster(K8S 集群)的訪問方式,一般為 IP+PORT
certificate-authority-data是證書數據,只有當 cluster(K8S 集群)的連接方式是 https 時,為了安全起見需要證書數據
users記錄了訪問 cluster(K8S 集群)的賬號信息:
name是用戶賬號的名稱代號
user/token是用戶的 token 認證方式,token 不是用戶認證的唯一方式,其他還有賬號+密碼等。
contexts是上下文信息,包括了 cluster(K8S 集群)和訪問 cluster(K8S 集群)的用戶賬號等信息:
name是這個上下文的名稱代號
cluster是 cluster(K8S 集群)的名稱代號
user是訪問 cluster(K8S 集群)的用戶賬號代號
current-context記錄當前 kubectl 默認使用的上下文信息
kind和apiVersion都是固定值,用戶不需要關心
preferences則是配置文件的其他設置信息,筆者沒有使用過,暫時不提。
第二步,給 kubectl 配置上配置文件。
--kubeconfig參數。第一種辦法是每次執行 kubectl 的時候,都帶上--kubeconfig=${CONFIG_PATH}。給一點溫馨小提示:每次都帶這么一長串的字符非常麻煩,可以用 alias 別名來簡化碼字量,比如alias k=kubectl --kubeconfig=${CONFIG_PATH}。
KUBECONFIG環境變量。第二種做法是使用環境變量KUBECONFIG把所有配置文件都記錄下來,即export KUBECONFIG=$KUBECONFIG:${CONFIG_PATH}。接下來就可以放心執行 kubectl 命令了。
$HOME/.kube/config 配置文件。第三種做法是把配置文件的內容放到$HOME/.kube/config 內。具體做法為:
如果$HOME/.kube/config 不存在,那么cp ${CONFIG_PATH} $HOME/.kube/config即可;
如果如果 $HOME/.kube/config已經存在,那么需要把新的配置內容加到 $HOME/.kube/config 下。單單只是cat ${CONFIG_PATH} >> $HOME/.kube/config是不行的,正確的做法是:KUBECONFIG=$HOME/.kube/config:${CONFIG_PATH} kubectl config view --flatten > $HOME/.kube/config 。解釋下這個命令的意思:先把所有的配置文件添加到環境變量KUBECONFIG中,然后執行kubectl config view --flatten打印出有效的配置文件內容,最后覆蓋$HOME/.kube/config 即可。
請注意,上述操作的優先級分別是 1>2>3,也就是說,kubectl 會優先檢查--kubeconfig,若無則檢查KUBECONFIG,若無則最后檢查$HOME/.kube/config,如果還是沒有,報錯。但凡某一步找到了有效的 cluster,就中斷檢查,去連接 K8S 集群了。
第三步:配置正確的上下文
按照第二步的做法,如果配置文件只有一個 cluster 是沒有任何問題的,但是對于有多個 cluster 怎么辦呢?到這里,有幾個關于配置的必須掌握的命令:
kubectl config get-contexts。列出所有上下文信息。
kubectl config current-context。查看當前的上下文信息。其實,命令 1 線束出來的*所指示的就是當前的上下文信息。
kubectl config use-context ${CONTEXT_NAME}。更改上下文信息。
kubectl config set-context ${CONTEXT_NAME}|--current --${KEY}=${VALUE}。修改上下文的元素。比如可以修改用戶賬號、集群信息、連接到 K8S 后所在的 namespace。
關于該命令,還有幾點要啰嗦的:
config set-context可以修改任何在配置文件中的上下文信息,只需要在命令中指定上下文名稱就可以。而--current 則指代當前上下文。
上下文信息所包括的內容有:cluster 集群(名稱)、用戶賬號(名稱)、連接到 K8S 后所在的 namespace,因此有config set-context嚴格意義上的用法:
kubectl config set-context [NAME|--current] [--cluster=cluster_nickname] [--user=user_nickname] [--namespace=namespace] [options]
(備注:[options]可以通過 kubectl options 查看)
綜上,如何操作 kubectl 配置都已交代。
IV. kubectl 部署服務
K8S 核心功能就是部署運維容器化服務,因此最重要的就是如何又快又好地部署自己的服務了。本章會介紹如何部署 Pod 和 Deployment。
2.1 如何部署 Pod?
通過 kubectl 部署 Pod 的辦法分為兩步:1). 準備 Pod 的 yaml 文件;2). 執行 kubectl 命令部署
第一步:準備 Pod 的 yaml 文件。關于 Pod 的 yaml 文件初步解釋,本系列上一篇文章《K8S 系列一:概念入門》已經有了初步介紹,這里再復習下:
apiVersion:?v1 kind:?Pod metadata:name:?memory-demonamespace:?mem-example spec:containers:-?name:?memory-demo-ctrimage:?polinux/stressresources:limits:memory:?"200Mi"requests:memory:?"100Mi"command:?["stress"]args:?["--vm",?"1",?"--vm-bytes",?"150M",?"--vm-hang",?"1"]volumeMounts:-?name:?redis-storagemountPath:?/data/redisvolumes:-?name:?redis-storageemptyDir:?{}繼續解讀:
metadata,對于新入門的同學來說,需要重點掌握的兩個字段:
name。這個 Pod 的名稱,后面到 K8S 集群中查找 Pod 的關鍵字段。
namespace。命名空間,即該 Pod 隸屬于哪個 namespace 下,關于 Pod 和 namespace 的關系,上一篇文章已經交代了。
spec記錄了 Pod 內部所有的資源的詳細信息,這里我們重點查看containers下的幾個重要字段:
name。Pod 下該容器名稱,后面查找 Pod 下的容器的關鍵字段。
image。容器的鏡像地址,K8S 會根據這個字段去拉取鏡像。
resources。容器化服務涉及到的 CPU、內存、GPU 等資源要求。可以看到有limits和requests兩個子項,那么這兩者有什么區別嗎,該怎么使用?在What's the difference between Pod resources.limits and resources.requests in Kubernetes?回答了:
limits是 K8S 為該容器至多分配的資源配額;而requests則是 K8S 為該容器至少分配的資源配額。打個比方,配置中要求了 memory 的requests為 100M,而此時如果 K8S 集群中所有的 Node 的可用內存都不足 100M,那么部署服務會失敗;又如果有一個 Node 的內存有 16G 充裕,可以部署該 Pod,而在運行中,該容器服務發生了內存泄露,那么一旦超過 200M 就會因為 OOM 被 kill,盡管此時該機器上還有 15G+的內存。
command。容器的入口命令。對于這個筆者還存在很多困惑不解的地方,暫時挖個坑,有清楚的同學歡迎留言。
args。容器的入口參數。同上,有清楚的同學歡迎留言。
volumeMounts。容器要掛載的 Pod 數據卷等。請務必記住:Pod 的數據卷只有被容器掛載后才能使用!
第二步:執行 kubectl 命令部署。有了 Pod 的 yaml 文件之后,就可以用 kubectl 部署了,命令非常簡單:kubectl create -f ${POD_YAML}。
隨后,會提示該命令是否執行成功,比如 yaml 內容不符合要求,則會提示哪一行有問題:
修正后,再次部署:
2.2 如何部署 Deployment?
第一步:準備 Deployment 的 yaml 文件。首先來看 Deployment 的 yaml 文件內容:
?apiVersion:?extensions/v1beta1kind:?Deploymentmetadata:name:?rss-sitenamespace:?mem-examplespec:replicas:?2template:metadata:labels:app:?webspec:containers:-?name:?memory-demo-ctrimage:?polinux/stressresources:limits:emory:?"200Mi"requests:memory:?"100Mi"command:?["stress"]args:?["--vm",?"1",?"--vm-bytes",?"150M",?"--vm-hang",?"1"]volumeMounts:-?name:?redis-storagemountPath:?/data/redisvolumes:-?name:?redis-storageemptyDir:?{}繼續來看幾個重要的字段:
metadata同 Pod 的 yaml,這里提一點:如果沒有指明 namespace,那么就是用 kubectl 默認的 namespace(如果 kubectl 配置文件中沒有指明 namespace,那么就是 default 空間)。
spec,可以看到 Deployment 的spec字段是在 Pod 的spec內容外“包了一層”,那就來看 Deployment 有哪些需要注意的:
metadata,新手同學先不管這邊的信息。
spec,會發現這完完全全是上文提到的 Pod 的spec內容,在這里寫明了 Deployment 下屬管理的每個 Pod 的具體內容。
replicas。副本個數。也就是該 Deployment 需要起多少個相同的 Pod,如果用戶成功在 K8S 中配置了 n(n>1)個,那么 Deployment 會確保在集群中始終有 n 個服務在運行。
template。
第二步:執行 kubectl 命令部署。Deployment 的部署辦法同 Pod:kubectl create -f ${DEPLOYMENT_YAML}。由此可見,K8S 會根據配置文件中的kind字段來判斷具體要創建的是什么資源。
這里插一句題外話:部署完 deployment 之后,可以查看到自動創建了 ReplicaSet 和 Pod,如下圖所示:
還有一個有趣的事情:通過 Deployment 部署的服務,其下屬的 RS 和 Pod 命名是有規則的。讀者朋友們自己總結發現哦。
綜上,如何部署一個 Pod 或者 Deployment 就結束了。
V. kubectl 查看、更新/編輯、刪除服務
作為 K8S 使用者而言,更關心的問題應該是本章所要討論的話題:如何通過 kubectl 查看、更新/編輯、刪除在 K8S 上部署著的服務。
3.1 如何查看服務?
請務必記得一個事情:在 K8S 中,一個獨立的服務即對應一個 Pod。即,當我們說要 xxx 一個服務的就是,也就是操作一個 Pod。而與 Pod 服務相關的且需要用戶關心的,有 Deployment。
通過 kubectl 查看服務的基本命令是:
$?kubectl?get|describe?${RESOURCE}?[-o?${FORMAT}]?-n=${NAMESPACE} #?${RESOURCE}有:?pod、deployment、replicaset(rs)在此之前,還有一個需要回憶的事情是:Deployment、ReplicaSet 和 Pod 之間的關系 - 層層隸屬;以及這些資源和 namespace 的關系是 - 隸屬。如下圖所示。
因此,要查看一個服務,也就是一個 Pod,必須首先指定 namespace!那么,如何查看集群中所有的 namespace 呢?kubectl get ns:
于是,只需要通過-n=${NAMESPACE}就可以指定自己要操作的資源所在的 namespace。比如查看 Pod:kubectl get pod -n=oona-test,同理,查看 Deployment:kubectl get deployment -n=oona-test。
問題又來了:如果已經忘記自己所部屬的服務所在的 namespace 怎么辦?這么多 namespace,一個一個查看過來嗎?
kubectl get pod --all-namespaces
這樣子就可以看到所有 namespace 下面部署的 Pod 了!同理,要查找所有的命名空間下的 Deployment 的命令是:kubectl get deployment --all-namespaces。
于是,就可以開心地查看 Pod:kubectl get pod [-o wide] -n=oona-test,或者查看 Deployment:kubectl get deployment [-o wide] -n=oona-test。
哎,這里是否加-o wide有什么區別嗎?實際操作下就明白了,其他資源亦然:
哎,我們看到之前部署的 Pod 服務 memory-demo 顯示的“ImagePullBackOff”是怎么回事呢?先不著急,我們慢慢看下去。
3.2 如何更新/編輯服務?
兩種辦法:1). 修改 yaml 文件后通過 kubectl 更新;2). 通過 kubectl 直接編輯 K8S 上的服務。
方法一:修改 yaml 文件后通過 kubectl 更新。我們看到,創建一個 Pod 或者 Deployment 的命令是kubectl create -f ${YAML}。但是,如果 K8S 集群當前的 namespace 下已經有該服務的話,會提示資源已經存在:
通過 kubectl 更新的命令是kubectl apply -f ${YAML},我們再來試一試:
(備注:命令kubectl apply -f ${YAML}也可以用于首次創建一個服務哦)
方法二:通過 kubectl 直接編輯 K8S 上的服務。命令為kubectl edit ${RESOURCE} ${NAME},比如修改剛剛的 Pod 的命令為kubectl edit pod memory-demo,然后直接編輯自己要修改的內容即可。
但是請注意,無論方法一還是方法二,能修改的內容還是有限的,從筆者實戰下來的結論是:只能修改/更新鏡像的地址和個別幾個字段。如果修改其他字段,會報錯:
The Pod "memory-demo" is invalid: spec: Forbidden: pod updates may not change fields other than spec.containers[*].image, spec.initContainers[*].image, spec.activeDeadlineSeconds or spec.tolerations (only additions to existing tolerations)
如果真的要修改其他字段怎么辦呢?恐怕只能刪除服務后重新部署了。
3.3 如何刪除服務?
在 K8S 上刪除服務的操作非常簡單,命令為kubectl delete ${RESOURCE} ${NAME}。比如刪除一個 Pod 是:kubectl delete pod memory-demo,再比如刪除一個 Deployment 的命令是:kubectl delete deployment ${DEPLOYMENT_NAME}。但是,請注意:
如果只部署了一個 Pod,那么直接刪除該 Pod 即可;
如果是通過 Deployment 部署的服務,那么僅僅刪除 Pod 是不行的,正確的刪除方式應該是:先刪除 Deployment,再刪除 Pod。
關于第二點應該不難想象:僅僅刪除了 Pod 但是 Deployment 還在的話,Deployment 定時會檢查其下屬的所有 Pod,如果發現失敗了則會再拉起。因此,會發現過一會兒,新的 Pod 又被拉起來了。
另外,還有一個事情:有時候會發現一個 Pod 總也刪除不了,這個時候很有可能要實施強制刪除措施,命令為kubectl delete pod --force --grace-period=0 ${POD_NAME}。
VI. kubectl 排查服務問題
上文說道:部署的服務 memory-demo 失敗了,是怎么回事呢?本章就會帶大家一起來看看常見的 K8S 中服務部署失敗、服務起來了但是不正常運行都怎么排查呢?
首先,祭出筆者最愛的一張 K8S 排查手冊,來自博客《Kubernetes Deployment 故障排除圖解指南》:
哈哈哈,對于新手同學來說,上圖還是不夠友好,下面我們簡單來看兩個例子:
4.1 K8S 上部署服務失敗了怎么排查?
請一定記住這個命令:kubectl describe ${RESOURCE} ${NAME}。比如剛剛的 Pod 服務 memory-demo,我們來看:
拉到最后看到Events部分,會顯示出 K8S 在部署這個服務過程的關鍵日志。這里我們可以看到是拉取鏡像失敗了,好吧,大家可以換一個可用的鏡像再試試。
一般來說,通過kubectl describe pod ${POD_NAME}已經能定位絕大部分部署失敗的問題了,當然,具體問題還是得具體分析。大家如果遇到具體的報錯,歡迎分享交流。
4.2 K8S 上部署的服務不正常怎么排查?
如果服務部署成功了,且狀態為running,那么就需要進入 Pod 內部的容器去查看自己的服務日志了:
查看 Pod 內部某個 container 打印的日志:kubectl log ${POD_NAME} -c ${CONTAINER_NAME}。
進入 Pod 內部某個 container:kubectl exec -it [options] ${POD_NAME} -c ${CONTAINER_NAME} [args],嗯,這個命令的作用是通過 kubectl 執行了docker exec xxx進入到容器實例內部。之后,就是用戶檢查自己服務的日志來定位問題。
顯然,線上可能會遇到更復雜的問題,需要借助更多更強大的命令和工具。
寫在后面
本文希望能夠幫助對 K8S 不了解的新手快速了解 K8S。筆者一邊寫文章,一邊查閱和整理 K8S 資料,過程中越發感覺 K8S 架構的完備、設計的精妙,是值得深入研究的,K8S 大受歡迎是有道理的。
總結
以上是生活随笔為你收集整理的Kubernetes 入门进阶实战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据分析利器:XGBoost算法最佳解析
- 下一篇: 收藏 | 2020年腾讯技术工程十大热门