超詳細(xì)!Kubernetes 學(xué)習(xí)筆記總結(jié)
寫在前面
筆者今年 9 月從端側(cè)開發(fā)轉(zhuǎn)到后臺開發(fā),第一個(gè)系統(tǒng)開發(fā)任務(wù)就強(qiáng)依賴了 K8S,加之項(xiàng)目任務(wù)重、排期緊,必須馬上對 K8S 有概念上的了解。然而,很多所謂“K8S 入門\概念”的文章看的一頭霧水,對于大部分新手來說并不友好。經(jīng)歷了幾天痛苦地學(xué)習(xí)之后,回顧來看,K8S 根本不復(fù)雜。于是,決心有了這一系列的文章:一方面希望對新手同學(xué)有幫助;另一方面,以文會友,希望能夠有機(jī)會交流討論技術(shù)。
本文組織方式:
1. K8S 是什么,即作用和目的。涉及 K8S 架構(gòu)的整理,Master 和 Node 之間的關(guān)系,以及 K8S 幾個(gè)重要的組件:API Server、Scheduler、Controller、etcd 等。
2. K8S 的重要概念,即 K8S 的 API 對象,也就是常常聽到的 Pod、Deployment、Service 等。
3. 如何配置 kubectl,介紹kubectl工具和配置辦法。
4. 如何用kubectl 部署服務(wù)。
5. 如何用kubectl 查看、更新/編輯、刪除服務(wù)。
6.?如何用kubectl?排查部署在K8S集群上的服務(wù)出現(xiàn)的問題
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.
用于自動部署、擴(kuò)展和管理“容器化(containerized)應(yīng)用程序”的開源系統(tǒng)。
翻譯成大白話就是:“K8S 是 負(fù)責(zé)自動化運(yùn)維管理多個(gè) Docker 程序的集群”。那么問題來了:Docker 運(yùn)行可方便了,為什么要用 K8S,它有什么優(yōu)勢?
插一句題外話:
為什么 Kubernetes 要叫 Kubernetes 呢?維基百科已經(jīng)交代了(老美對星際是真的癡迷):
Kubernetes(在希臘語意為“舵手”或“駕駛員”)由 Joe Beda、Brendan Burns 和 Craig McLuckie 創(chuàng)立,并由其他谷歌工程師,包括 Brian Grant 和 Tim Hockin 等進(jìn)行加盟創(chuàng)作,并由谷歌在 2014 年首次對外宣布 。該系統(tǒng)的開發(fā)和設(shè)計(jì)都深受谷歌的 Borg 系統(tǒng)的影響,其許多頂級貢獻(xiàn)者之前也是 Borg 系統(tǒng)的開發(fā)者。在谷歌內(nèi)部,Kubernetes 的原始代號曾經(jīng)是Seven,即星際迷航中的 Borg(博格人)。Kubernetes 標(biāo)識中舵輪有七個(gè)輪輻就是對該項(xiàng)目代號的致意。
為什么 Kubernetes 的縮寫是 K8S 呢?我個(gè)人贊同Why Kubernetes is Abbreviated k8s中說的觀點(diǎn)“嘛,寫全稱也太累了吧,不如整個(gè)縮寫”。其實(shí)只保留首位字符,用具體數(shù)字來替代省略的字符個(gè)數(shù)的做法,還是比較常見的。
1.2 為什么是 K8S?
試想下傳統(tǒng)的后端部署辦法:把程序包(包括可執(zhí)行二進(jìn)制文件、配置文件等)放到服務(wù)器上,接著運(yùn)行啟動腳本把程序跑起來,同時(shí)啟動守護(hù)腳本定期檢查程序運(yùn)行狀態(tài)、必要的話重新拉起程序。
有問題嗎?顯然有!最大的一個(gè)問題在于:**如果服務(wù)的請求量上來,已部署的服務(wù)響應(yīng)不過來怎么辦?**傳統(tǒng)的做法往往是,如果請求量、內(nèi)存、CPU 超過閾值做了告警,運(yùn)維馬上再加幾臺服務(wù)器,部署好服務(wù)之后,接入負(fù)載均衡來分擔(dān)已有服務(wù)的壓力。
問題出現(xiàn)了:從監(jiān)控告警到部署服務(wù),中間需要人力介入!那么,有沒有辦法自動完成服務(wù)的部署、更新、卸載和擴(kuò)容、縮容呢?
這,就是 K8S 要做的事情:自動化運(yùn)維管理 Docker(容器化)程序。
1.3 K8S 怎么做?
我們已經(jīng)知道了 K8S 的核心功能:自動化運(yùn)維管理多個(gè)容器化程序。那么 K8S 怎么做到的呢?這里,我們從宏觀架構(gòu)上來學(xué)習(xí) K8S 的設(shè)計(jì)思想。首先看下圖,圖片來自文章Components of Kubernetes Architecture:

K8S 是屬于主從設(shè)備模型(Master-Slave 架構(gòu)),即有 Master 節(jié)點(diǎn)負(fù)責(zé)核心的調(diào)度、管理和運(yùn)維,Slave 節(jié)點(diǎn)則在執(zhí)行用戶的程序。但是在 K8S 中,主節(jié)點(diǎn)一般被稱為Master Node 或者 Head Node(本文采用 Master Node 稱呼方式),而從節(jié)點(diǎn)則被稱為Worker Node 或者 Node(本文采用 Worker Node 稱呼方式)。
要注意一點(diǎn):Master Node 和 Worker Node 是分別安裝了 K8S 的 Master 和 Woker 組件的實(shí)體服務(wù)器,每個(gè) Node 都對應(yīng)了一臺實(shí)體服務(wù)器(雖然 Master Node 可以和其中一個(gè) Worker Node 安裝在同一臺服務(wù)器,但是建議 Master Node 單獨(dú)部署),所有 Master Node 和 Worker Node 組成了 K8S 集群,同一個(gè)集群可能存在多個(gè) Master Node 和 Worker Node。
首先來看Master Node都有哪些組件:
API Server。K8S 的請求入口服務(wù)。API Server 負(fù)責(zé)接收 K8S 所有請求(來自 UI 界面或者 CLI 命令行工具),然后,API Server 根據(jù)用戶的具體請求,去通知其他組件干活。 Scheduler。K8S 所有 Worker Node 的調(diào)度器。當(dāng)用戶要部署服務(wù)時(shí),Scheduler 會選擇最合適的 Worker Node(服務(wù)器)來部署。 Controller Manager。K8S 所有 Worker Node 的監(jiān)控器。Controller Manager 有很多具體的 Controller,在文章Components of Kubernetes Architecture中提到的有 Node Controller、Service Controller、Volume Controller 等。Controller 負(fù)責(zé)監(jiān)控和調(diào)整在 Worker Node 上部署的服務(wù)的狀態(tài),比如用戶要求 A 服務(wù)部署 2 個(gè)副本,那么當(dāng)其中一個(gè)服務(wù)掛了的時(shí)候,Controller 會馬上調(diào)整,讓 Scheduler 再選擇一個(gè) Worker Node 重新部署服務(wù)。 etcd。K8S 的存儲服務(wù)。etcd 存儲了 K8S 的關(guān)鍵配置和用戶配置,K8S 中僅 API Server 才具備讀寫權(quán)限,其他組件必須通過 API Server 的接口才能讀寫數(shù)據(jù)(見Kubernetes Works Like an Operating System)。
接著來看Worker Node的組件,筆者更贊同HOW DO APPLICATIONS RUN ON KUBERNETES文章中提到的組件介紹:
Kubelet。Worker Node 的監(jiān)視器,以及與 Master Node 的通訊器。Kubelet 是 Master Node 安插在 Worker Node 上的“眼線”,它會定期向 Worker Node 匯報(bào)自己 Node 上運(yùn)行的服務(wù)的狀態(tài),并接受來自 Master Node 的指示采取調(diào)整措施。 Kube-Proxy。K8S 的網(wǎng)絡(luò)代理。私以為稱呼為 Network-Proxy 可能更適合?Kube-Proxy 負(fù)責(zé) Node 在 K8S 的網(wǎng)絡(luò)通訊、以及對外部網(wǎng)絡(luò)流量的負(fù)載均衡。 Container Runtime。Worker Node 的運(yùn)行環(huán)境。即安裝了容器化所需的軟件環(huán)境確保容器化程序能夠跑起來,比如 Docker Engine。大白話就是幫忙裝好了 Docker 運(yùn)行環(huán)境。 Logging Layer。K8S 的監(jiān)控狀態(tài)收集器。私以為稱呼為 Monitor 可能更合適?Logging Layer 負(fù)責(zé)采集 Node 上所有服務(wù)的 CPU、內(nèi)存、磁盤、網(wǎng)絡(luò)等監(jiān)控項(xiàng)信息。 Add-Ons。K8S 管理運(yùn)維 Worker Node 的插件組件。有些文章認(rèn)為 Worker Node 只有三大組件,不包含 Add-On,但筆者認(rèn)為 K8S 系統(tǒng)提供了 Add-On 機(jī)制,讓用戶可以擴(kuò)展更多定制化功能,是很不錯的亮點(diǎn)。
總結(jié)來看,K8S 的 Master Node 具備:請求入口管理(API Server),Worker Node 調(diào)度(Scheduler),監(jiān)控和自動調(diào)節(jié)(Controller Manager),以及存儲功能(etcd);而 K8S 的 Worker Node 具備:狀態(tài)和監(jiān)控收集(Kubelet),網(wǎng)絡(luò)和負(fù)載均衡(Kube-Proxy)、保障容器化運(yùn)行環(huán)境(Container Runtime)、以及定制化功能(Add-Ons)。
到這里,相信你已經(jīng)對 K8S 究竟是做什么的,有了大概認(rèn)識。接下來,再來認(rèn)識下 K8S 的 Deployment、Pod、Replica Set、Service 等,但凡談到 K8S,就繞不開這些名詞,而這些名詞也是最讓 K8S 新手們感到頭疼、困惑的。
II. K8S 重要概念
2.1 Pod 實(shí)例
官方對于Pod的解釋是:
Pod是可以在 Kubernetes 中創(chuàng)建和管理的、最小的可部署的計(jì)算單元。
這樣的解釋還是很難讓人明白究竟 Pod 是什么,但是對于 K8S 而言,Pod 可以說是所有對象中最重要的概念了!因此,我們必須首先清楚地知道“Pod 是什么”,再去了解其他的對象。
從官方給出的定義,聯(lián)想下“最小的 xxx 單元”,是不是可以想到本科在學(xué)校里學(xué)習(xí)“進(jìn)程”的時(shí)候,教科書上有一段類似的描述:資源分配的最小單位;還有”線程“的描述是:CPU 調(diào)度的最小單位。什么意思呢?”最小 xx 單位“要么就是事物的衡量標(biāo)準(zhǔn)單位,要么就是資源的閉包、集合。前者比如長度米、時(shí)間秒;后者比如一個(gè)”進(jìn)程“是存儲和計(jì)算的閉包,一個(gè)”線程“是 CPU 資源(包括寄存器、ALU 等)的閉包。
同樣的,Pod 就是 K8S 中一個(gè)服務(wù)的閉包。這么說的好像還是有點(diǎn)玄乎,更加云里霧里了。簡單來說,Pod 可以被理解成一群可以共享網(wǎng)絡(luò)、存儲和計(jì)算資源的容器化服務(wù)的集合。再打個(gè)形象的比喻,在同一個(gè) Pod 里的幾個(gè) Docker 服務(wù)/程序,好像被部署在同一臺機(jī)器上,可以通過 localhost 互相訪問,并且可以共用 Pod 里的存儲資源(這里是指 Docker 可以掛載 Pod 內(nèi)的數(shù)據(jù)卷,數(shù)據(jù)卷的概念,后文會詳細(xì)講述,暫時(shí)理解為“需要手動 mount 的磁盤”)。筆者總結(jié) Pod 如下圖,可以看到:同一個(gè) Pod 之間的 Container 可以通過 localhost 互相訪問,并且可以掛載 Pod 內(nèi)所有的數(shù)據(jù)卷;但是不同的 Pod 之間的 Container 不能用 localhost 訪問,也不能掛載其他 Pod 的數(shù)據(jù)卷。

對 Pod 有直觀的認(rèn)識之后,接著來看 K8S 中 Pod 究竟長什么樣子,具體包括哪些資源?
K8S 中所有的對象都通過 yaml 來表示,筆者從官方網(wǎng)站摘錄了一個(gè)最簡單的 Pod 的 yaml:
apiVersion:?v1
kind:?Pod
metadata:
??name:?memory-demo
??namespace:?mem-example
spec:
??containers:
??-?name:?memory-demo-ctr
????image:?polinux/stress
????resources:
??????limits:
????????memory:?"200Mi"
??????requests:
????????memory:?"100Mi"
????command:?["stress"]
????args:?["--vm",?"1",?"--vm-bytes",?"150M",?"--vm-hang",?"1"]
????volumeMounts:
????-?name:?redis-storage
??????mountPath:?/data/redis
??volumes:
??-?name:?redis-storage
????emptyDir:?{}
看不懂不必慌張,且耐心聽下面的解釋:
apiVersion記錄 K8S 的 API Server 版本,現(xiàn)在看到的都是v1,用戶不用管。kind記錄該 yaml 的對象,比如這是一份 Pod 的 yaml 配置文件,那么值內(nèi)容就是Pod。metadata記錄了 Pod 自身的元數(shù)據(jù),比如這個(gè) Pod 的名字、這個(gè) Pod 屬于哪個(gè) namespace(命名空間的概念,后文會詳述,暫時(shí)理解為“同一個(gè)命名空間內(nèi)的對象互相可見”)。spec記錄了 Pod 內(nèi)部所有的資源的詳細(xì)信息,看懂這個(gè)很重要:containers記錄了 Pod 內(nèi)的容器信息,containers包括了:name容器名,image容器的鏡像地址,resources容器需要的 CPU、內(nèi)存、GPU 等資源,command容器的入口命令,args容器的入口參數(shù),volumeMounts容器要掛載的 Pod 數(shù)據(jù)卷等。可以看到,上述這些信息都是啟動容器的必要和必需的信息。volumes記錄了 Pod 內(nèi)的數(shù)據(jù)卷信息,后文會詳細(xì)介紹 Pod 的數(shù)據(jù)卷。
2.2 Volume 數(shù)據(jù)卷
K8S 支持很多類型的 volume 數(shù)據(jù)卷掛載,具體請參見K8S 卷。前文就“如何理解 volume”提到:“需要手動 mount 的磁盤”,此外,有一點(diǎn)可以幫助理解:數(shù)據(jù)卷 volume 是 Pod 內(nèi)部的磁盤資源。
其實(shí),單單就 Volume 來說,不難理解。但是上面還看到了volumeMounts,這倆是什么關(guān)系呢?
volume 是 K8S 的對象,對應(yīng)一個(gè)實(shí)體的數(shù)據(jù)卷;而 volumeMounts 只是 container 的掛載點(diǎn),對應(yīng) container 的其中一個(gè)參數(shù)。但是,volumeMounts 依賴于 volume,只有當(dāng) Pod 內(nèi)有 volume 資源的時(shí)候,該 Pod 內(nèi)部的 container 才可能有 volumeMounts。
2.3 Container 容器
本文中提到的鏡像 Image、容器 Container,都指代了 Pod 下的一個(gè)container。關(guān)于 K8S 中的容器,在 2.1Pod 章節(jié)都已經(jīng)交代了,這里無非再啰嗦一句:一個(gè) Pod 內(nèi)可以有多個(gè)容器 container。
在 Pod 中,容器也有分類,對這個(gè)感興趣的同學(xué)歡迎自行閱讀更多資料:
標(biāo)準(zhǔn)容器 Application Container。 初始化容器 Init Container。 邊車容器 Sidecar Container。 臨時(shí)容器 Ephemeral Container。
一般來說,我們部署的大多是標(biāo)準(zhǔn)容器( Application Container)。
2.4 Deployment 和 ReplicaSet(簡稱 RS)
除了 Pod 之外,K8S 中最常聽到的另一個(gè)對象就是 Deployment 了。那么,什么是 Deployment 呢?官方給出了一個(gè)要命的解釋:
一個(gè)?Deployment?控制器為 Pods 和 ReplicaSets 提供聲明式的更新能力。
你負(fù)責(zé)描述 Deployment 中的?目標(biāo)狀態(tài),而 Deployment 控制器以受控速率更改實(shí)際狀態(tài), 使其變?yōu)槠谕麪顟B(tài)。你可以定義 Deployment 以創(chuàng)建新的 ReplicaSet,或刪除現(xiàn)有 Deployment,并通過新的 Deployment 收養(yǎng)其資源。
翻譯一下:Deployment 的作用是管理和控制 Pod 和 ReplicaSet,管控它們運(yùn)行在用戶期望的狀態(tài)中。哎,打個(gè)形象的比喻,Deployment 就是包工頭,主要負(fù)責(zé)監(jiān)督底下的工人 Pod 干活,確保每時(shí)每刻有用戶要求數(shù)量的 Pod 在工作。如果一旦發(fā)現(xiàn)某個(gè)工人 Pod 不行了,就趕緊新拉一個(gè) Pod 過來替換它。
新的問題又來了:那什么是 ReplicaSets 呢?
ReplicaSet 的目的是維護(hù)一組在任何時(shí)候都處于運(yùn)行狀態(tài)的 Pod 副本的穩(wěn)定集合。因此,它通常用來保證給定數(shù)量的、完全相同的 Pod 的可用性。
再來翻譯下:ReplicaSet 的作用就是管理和控制 Pod,管控他們好好干活。但是,ReplicaSet 受控于 Deployment。形象來說,ReplicaSet 就是總包工頭手下的小包工頭。
筆者總結(jié)得到下面這幅圖,希望能幫助理解:

新的問題又來了:如果都是為了管控 Pod 好好干活,為什么要設(shè)置 Deployment 和 ReplicaSet 兩個(gè)層級呢,直接讓 Deployment 來管理不可以嗎?
回答:不清楚,但是私以為是因?yàn)橄扔?ReplicaSet,但是使用中發(fā)現(xiàn) ReplicaSet 不夠滿足要求,于是又整了一個(gè) Deployment(有清楚 Deployment 和 ReplicaSet 聯(lián)系和區(qū)別的小伙伴歡迎留言啊)。
但是,從 K8S 使用者角度來看,用戶會直接操作 Deployment 部署服務(wù),而當(dāng) Deployment 被部署的時(shí)候,K8S 會自動生成要求的 ReplicaSet 和 Pod。在K8S 官方文檔中也指出用戶只需要關(guān)心 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 section.
這實(shí)際上意味著您可能永遠(yuǎn)不需要操作 ReplicaSet 對象:直接使用 Deployments 并在規(guī)范部分定義應(yīng)用程序。
補(bǔ)充說明:在 K8S 中還有一個(gè)對象 ---?ReplicationController(簡稱 RC),官方文檔對它的定義是:
ReplicationController?確保在任何時(shí)候都有特定數(shù)量的 Pod 副本處于運(yùn)行狀態(tài)。換句話說,ReplicationController 確保一個(gè) Pod 或一組同類的 Pod 總是可用的。
怎么樣,和 ReplicaSet 是不是很相近?在Deployments, ReplicaSets, and pods教程中說“ReplicationController 是 ReplicaSet 的前身”,官方也推薦用 Deployment 取代 ReplicationController 來部署服務(wù)。
2.5 Service 和 Ingress
吐槽下 K8S 的概念/對象/資源是真的多啊!前文介紹的 Deployment、ReplicationController 和 ReplicaSet 主要管控 Pod 程序服務(wù);那么,Service 和 Ingress 則負(fù)責(zé)管控 Pod 網(wǎng)絡(luò)服務(wù)。
我們先來看看官方文檔中 Service 的定義:
將運(yùn)行在一組 Pods 上的應(yīng)用程序公開為網(wǎng)絡(luò)服務(wù)的抽象方法。
使用 Kubernetes,您無需修改應(yīng)用程序即可使用不熟悉的服務(wù)發(fā)現(xiàn)機(jī)制。Kubernetes 為 Pods 提供自己的 IP 地址,并為一組 Pod 提供相同的 DNS 名, 并且可以在它們之間進(jìn)行負(fù)載均衡。
翻譯下:K8S 中的服務(wù)(Service)并不是我們常說的“服務(wù)”的含義,而更像是網(wǎng)關(guān)層,是若干個(gè) Pod 的流量入口、流量均衡器。
那么,為什么要 Service 呢?
私以為在這一點(diǎn)上,官方文檔講解地非常清楚:
Kubernetes Pod 是有生命周期的。它們可以被創(chuàng)建,而且銷毀之后不會再啟動。如果您使用 Deployment 來運(yùn)行您的應(yīng)用程序,則它可以動態(tài)創(chuàng)建和銷毀 Pod。
每個(gè) Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一時(shí)刻運(yùn)行的 Pod 集合可能與稍后運(yùn)行該應(yīng)用程序的 Pod 集合不同。
這導(dǎo)致了一個(gè)問題:如果一組 Pod(稱為“后端”)為群集內(nèi)的其他 Pod(稱為“前端”)提供功能, 那么前端如何找出并跟蹤要連接的 IP 地址,以便前端可以使用工作量的后端部分?
補(bǔ)充說明:K8S 集群的網(wǎng)絡(luò)管理和拓?fù)湟灿刑貏e的設(shè)計(jì),以后會專門出一章節(jié)來詳細(xì)介紹 K8S 中的網(wǎng)絡(luò)。這里需要清楚一點(diǎn):K8S 集群內(nèi)的每一個(gè) Pod 都有自己的 IP(是不是很類似一個(gè) Pod 就是一臺服務(wù)器,然而事實(shí)上是多個(gè) Pod 存在于一臺服務(wù)器上,只不過是 K8S 做了網(wǎng)絡(luò)隔離),在 K8S 集群內(nèi)部還有 DNS 等網(wǎng)絡(luò)服務(wù)(一個(gè) K8S 集群就如同管理了多區(qū)域的服務(wù)器,可以做復(fù)雜的網(wǎng)絡(luò)拓?fù)洌?/p>
此外,筆者推薦k8s 外網(wǎng)如何訪問業(yè)務(wù)應(yīng)用對于 Service 的介紹,不過對于新手而言,推薦閱讀前半部分對于 service 的介紹即可,后半部分就太復(fù)雜了。我這里做了簡單的總結(jié):
Service 是 K8S 服務(wù)的核心,屏蔽了服務(wù)細(xì)節(jié),統(tǒng)一對外暴露服務(wù)接口,真正做到了“微服務(wù)”。舉個(gè)例子,我們的一個(gè)服務(wù) A,部署了 3 個(gè)備份,也就是 3 個(gè) Pod;對于用戶來說,只需要關(guān)注一個(gè) Service 的入口就可以,而不需要操心究竟應(yīng)該請求哪一個(gè) Pod。優(yōu)勢非常明顯:一方面外部用戶不需要感知因?yàn)?Pod 上服務(wù)的意外崩潰、K8S 重新拉起 Pod 而造成的 IP 變更,外部用戶也不需要感知因升級、變更服務(wù)帶來的 Pod 替換而造成的 IP 變化,另一方面,Service 還可以做流量負(fù)載均衡。
但是,Service 主要負(fù)責(zé) K8S 集群內(nèi)部的網(wǎng)絡(luò)拓?fù)洹D敲醇和獠吭趺丛L問集群內(nèi)部呢?這個(gè)時(shí)候就需要 Ingress 了,官方文檔中的解釋是:
Ingress 是對集群中服務(wù)的外部訪問進(jìn)行管理的 API 對象,典型的訪問方式是 HTTP。
Ingress 可以提供負(fù)載均衡、SSL 終結(jié)和基于名稱的虛擬托管。
翻譯一下:Ingress 是整個(gè) K8S 集群的接入層,復(fù)雜集群內(nèi)外通訊。
最后,筆者把 Ingress 和 Service 的關(guān)系繪制網(wǎng)絡(luò)拓?fù)潢P(guān)系圖如下,希望對理解這兩個(gè)概念有所幫助:

2.6 namespace 命名空間
和前文介紹的所有的概念都不一樣,namespace 跟 Pod 沒有直接關(guān)系,而是 K8S 另一個(gè)維度的對象。或者說,前文提到的概念都是為了服務(wù) Pod 的,而 namespace 則是為了服務(wù)整個(gè) K8S 集群的。
那么,namespace 是什么呢?
上官方文檔定義:
Kubernetes 支持多個(gè)虛擬集群,它們底層依賴于同一個(gè)物理集群。這些虛擬集群被稱為名字空間。
翻譯一下:namespace 是為了把一個(gè) K8S 集群劃分為若干個(gè)資源不可共享的虛擬集群而誕生的。
也就是說,可以通過在 K8S 集群內(nèi)創(chuàng)建 namespace 來分隔資源和對象。比如我有 2 個(gè)業(yè)務(wù) A 和 B,那么我可以創(chuàng)建 ns-a 和 ns-b 分別部署業(yè)務(wù) A 和 B 的服務(wù),如在 ns-a 中部署了一個(gè) deployment,名字是 hello,返回用戶的是“hello a”;在 ns-b 中也部署了一個(gè) deployment,名字恰巧也是 hello,返回用戶的是“hello b”(要知道,在同一個(gè) namespace 下 deployment 不能同名;但是不同 namespace 之間沒有影響)。前文提到的所有對象,都是在 namespace 下的;當(dāng)然,也有一些對象是不隸屬于 namespace 的,而是在 K8S 集群內(nèi)全局可見的,官方文檔提到的可以通過命令來查看,具體命令的使用辦法,筆者會出后續(xù)的實(shí)戰(zhàn)文章來介紹,先貼下命令:
#?位于名字空間中的資源
kubectl?api-resources?--namespaced=true
#?不在名字空間中的資源
kubectl?api-resources?--namespaced=false
不在 namespace 下的對象有:

在 namespace 下的對象有(部分):

2.7 其他
K8S 的對象實(shí)在太多了,2.1-2.6 介紹的是在實(shí)際使用 K8S 部署服務(wù)最常見的。其他的還有 Job、CronJob 等等,在對 K8S 有了比較清楚的認(rèn)知之后,再去學(xué)習(xí)更多的 K8S 對象,不是難事。
III. 配置 kubectl
3.1 什么是 kubectl?
官方文檔中介紹 kubectl 是:
Kubectl 是一個(gè)命令行接口,用于對 Kubernetes 集群運(yùn)行命令。Kubectl 的配置文件在$HOME/.kube 目錄。我們可以通過設(shè)置 KUBECONFIG 環(huán)境變量或設(shè)置命令參數(shù)--kubeconfig 來指定其他位置的 kubeconfig 文件。
也就是說,可以通過 kubectl 來操作 K8S 集群,基本語法:
使用以下語法?
kubectl?從終端窗口運(yùn)行命令:kubectl?[command]?[TYPE]?[NAME]?[flags]其中?
command、TYPE、NAME?和?flags?分別是:
command:指定要對一個(gè)或多個(gè)資源執(zhí)行的操作,例如?create、get、describe、delete。
TYPE:指定資源類型。資源類型不區(qū)分大小寫,可以指定單數(shù)、復(fù)數(shù)或縮寫形式。例如,以下命令輸出相同的結(jié)果:```shell
kubectl?get?pod?pod1
kubectl?get?pods?pod1
kubectl?get?po?pod1
-?`NAME`:指定資源的名稱。名稱區(qū)分大小寫。如果省略名稱,則顯示所有資源的詳細(xì)信息?`kubectl get pods`。
在對多個(gè)資源執(zhí)行操作時(shí),您可以按類型和名稱指定每個(gè)資源,或指定一個(gè)或多個(gè)文件:
-?要按類型和名稱指定資源:
??-?要對所有類型相同的資源進(jìn)行分組,請執(zhí)行以下操作:`TYPE1 name1 name2 name<#>`。
?例子:`kubectl get pod example-pod1 example-pod2`
??-?分別指定多個(gè)資源類型:`TYPE1/name1 TYPE1/name2 TYPE2/name3 TYPE<#>/name<#>`。
?例子:`kubectl get pod/example-pod1 replicationcontroller/example-rc1`
-?用一個(gè)或多個(gè)文件指定資源:`-f file1 -f file2 -f file<#>`
??-?[使用?YAML?而不是?JSON](https://kubernetes.io/zh/docs/concepts/configuration/overview/#general-config-tips)?因?yàn)?YAML 更容易使用,特別是用于配置文件時(shí)。
?例子:`kubectl get -f ./pod.yaml`
-?`flags`:?指定可選的參數(shù)。例如,可以使用?`-s`?或?`-server`?參數(shù)指定 Kubernetes API 服務(wù)器的地址和端口。
就如何使用 kubectl 而言,官方文檔已經(jīng)說得非常清楚。不過對于新手而言,還是需要解釋幾句:
kubectl 是 K8S 的命令行工具,并不需要 kubectl 安裝在 K8S 集群的任何 Node 上,但是,需要確保安裝 kubectl 的機(jī)器和 K8S 的集群能夠進(jìn)行網(wǎng)絡(luò)互通。 kubectl 是通過本地的配置文件來連接到 K8S 集群的,默認(rèn)保存在$HOME/.kube 目錄下;也可以通過 KUBECONFIG 環(huán)境變量或設(shè)置命令參數(shù)--kubeconfig 來指定其他位置的 kubeconfig 文件【官方文檔】。
接下來,一起看看怎么使用 kubectl 吧,切身感受下 kubectl 的使用。
請注意,如何安裝 kubectl 的辦法有許多非常明確的教程,比如《安裝并配置 kubectl》,本文不再贅述。
3.2 怎么配置 kubectl?
第一步,必須準(zhǔn)備好要連接/使用的 K8S 的配置文件,筆者給出一份杜撰的配置:
apiVersion:?v1
clusters:
-?cluster:
????certificate-authority-data:?thisisfakecertifcateauthoritydata00000000000
????server:?https://1.2.3.4:1234
??name:?cls-dev
contexts:
-?context:
????cluster:?cls-dev
????user:?kubernetes-admin
??name:?kubernetes-admin@test
current-context:?kubernetes-admin@test
kind:?Config
preferences:?{}
users:
-?name:?kubernetes-admin
??user:
????token:?thisisfaketoken00000
解讀如下:
clusters記錄了 clusters(一個(gè)或多個(gè) K8S 集群)信息:name是這個(gè) cluster(K8S 集群)的名稱代號server是這個(gè) cluster(K8S 集群)的訪問方式,一般為 IP+PORTcertificate-authority-data是證書數(shù)據(jù),只有當(dāng) cluster(K8S 集群)的連接方式是 https 時(shí),為了安全起見需要證書數(shù)據(jù)users記錄了訪問 cluster(K8S 集群)的賬號信息:name是用戶賬號的名稱代號user/token是用戶的 token 認(rèn)證方式,token 不是用戶認(rèn)證的唯一方式,其他還有賬號+密碼等。contexts是上下文信息,包括了 cluster(K8S 集群)和訪問 cluster(K8S 集群)的用戶賬號等信息:name是這個(gè)上下文的名稱代號cluster是 cluster(K8S 集群)的名稱代號user是訪問 cluster(K8S 集群)的用戶賬號代號current-context記錄當(dāng)前 kubectl 默認(rèn)使用的上下文信息kind和apiVersion都是固定值,用戶不需要關(guān)心preferences則是配置文件的其他設(shè)置信息,筆者沒有使用過,暫時(shí)不提。
第二步,給 kubectl 配置上配置文件。
--kubeconfig參數(shù)。第一種辦法是每次執(zhí)行 kubectl 的時(shí)候,都帶上--kubeconfig=${CONFIG_PATH}。給一點(diǎn)溫馨小提示:每次都帶這么一長串的字符非常麻煩,可以用 alias 別名來簡化碼字量,比如alias k=kubectl --kubeconfig=${CONFIG_PATH}。KUBECONFIG環(huán)境變量。第二種做法是使用環(huán)境變量KUBECONFIG把所有配置文件都記錄下來,即export KUBECONFIG=$KUBECONFIG:${CONFIG_PATH}。接下來就可以放心執(zhí)行 kubectl 命令了。$HOME/.kube/config 配置文件。第三種做法是把配置文件的內(nèi)容放到$HOME/.kube/config 內(nèi)。具體做法為: 如果$HOME/.kube/config 不存在,那么 cp ${CONFIG_PATH} $HOME/.kube/config即可;如果如果 $HOME/.kube/config已經(jīng)存在,那么需要把新的配置內(nèi)容加到 $HOME/.kube/config 下。單單只是 cat ${CONFIG_PATH} >> $HOME/.kube/config是不行的,正確的做法是:KUBECONFIG=$HOME/.kube/config:${CONFIG_PATH} kubectl config view --flatten > $HOME/.kube/config?。解釋下這個(gè)命令的意思:先把所有的配置文件添加到環(huán)境變量KUBECONFIG中,然后執(zhí)行kubectl config view --flatten打印出有效的配置文件內(nèi)容,最后覆蓋$HOME/.kube/config 即可。
請注意,上述操作的優(yōu)先級分別是 1>2>3,也就是說,kubectl 會優(yōu)先檢查--kubeconfig,若無則檢查KUBECONFIG,若無則最后檢查$HOME/.kube/config,如果還是沒有,報(bào)錯。但凡某一步找到了有效的 cluster,就中斷檢查,去連接 K8S 集群了。
第三步:配置正確的上下文
按照第二步的做法,如果配置文件只有一個(gè) cluster 是沒有任何問題的,但是對于有多個(gè) cluster 怎么辦呢?到這里,有幾個(gè)關(guān)于配置的必須掌握的命令:
kubectl config get-contexts。列出所有上下文信息。
kubectl config current-context。查看當(dāng)前的上下文信息。其實(shí),命令 1 線束出來的*所指示的就是當(dāng)前的上下文信息。
kubectl config use-context ${CONTEXT_NAME}。更改上下文信息。
kubectl config set-context ${CONTEXT_NAME}|--current --${KEY}=${VALUE}。修改上下文的元素。比如可以修改用戶賬號、集群信息、連接到 K8S 后所在的 namespace。
關(guān)于該命令,還有幾點(diǎn)要啰嗦的:
config set-context可以修改任何在配置文件中的上下文信息,只需要在命令中指定上下文名稱就可以。而--current 則指代當(dāng)前上下文。上下文信息所包括的內(nèi)容有:cluster 集群(名稱)、用戶賬號(名稱)、連接到 K8S 后所在的 namespace,因此有
config set-context嚴(yán)格意義上的用法:kubectl config set-context [NAME|--current] [--cluster=cluster_nickname] [--user=user_nickname] [--namespace=namespace] [options](備注:[options]可以通過 kubectl options 查看)
綜上,如何操作 kubectl 配置都已交代。
IV. kubectl 部署服務(wù)
K8S 核心功能就是部署運(yùn)維容器化服務(wù),因此最重要的就是如何又快又好地部署自己的服務(wù)了。本章會介紹如何部署 Pod 和 Deployment。
4.1 如何部署 Pod?
通過 kubectl 部署 Pod 的辦法分為兩步:1). 準(zhǔn)備 Pod 的 yaml 文件;2). 執(zhí)行 kubectl 命令部署
第一步:準(zhǔn)備 Pod 的 yaml 文件。關(guān)于 Pod 的 yaml 文件初步解釋,本系列上一篇文章《K8S 系列一:概念入門》已經(jīng)有了初步介紹,這里再復(fù)習(xí)下:
apiVersion:?v1
kind:?Pod
metadata:
??name:?memory-demo
??namespace:?mem-example
spec:
??containers:
??-?name:?memory-demo-ctr
????image:?polinux/stress
????resources:
??????limits:
????????memory:?"200Mi"
??????requests:
????????memory:?"100Mi"
????command:?["stress"]
????args:?["--vm",?"1",?"--vm-bytes",?"150M",?"--vm-hang",?"1"]
????volumeMounts:
????-?name:?redis-storage
??????mountPath:?/data/redis
??volumes:
??-?name:?redis-storage
????emptyDir:?{}
繼續(xù)解讀:
metadata,對于新入門的同學(xué)來說,需要重點(diǎn)掌握的兩個(gè)字段:name。這個(gè) Pod 的名稱,后面到 K8S 集群中查找 Pod 的關(guān)鍵字段。namespace。命名空間,即該 Pod 隸屬于哪個(gè) namespace 下,關(guān)于 Pod 和 namespace 的關(guān)系,上一篇文章已經(jīng)交代了。spec記錄了 Pod 內(nèi)部所有的資源的詳細(xì)信息,這里我們重點(diǎn)查看containers下的幾個(gè)重要字段:name。Pod 下該容器名稱,后面查找 Pod 下的容器的關(guān)鍵字段。image。容器的鏡像地址,K8S 會根據(jù)這個(gè)字段去拉取鏡像。resources。容器化服務(wù)涉及到的 CPU、內(nèi)存、GPU 等資源要求。可以看到有limits和requests兩個(gè)子項(xiàng),那么這兩者有什么區(qū)別嗎,該怎么使用?在What's the difference between Pod resources.limits and resources.requests in Kubernetes?回答了:limits是 K8S 為該容器至多分配的資源配額;而requests則是 K8S 為該容器至少分配的資源配額。打個(gè)比方,配置中要求了 memory 的requests為 100M,而此時(shí)如果 K8S 集群中所有的 Node 的可用內(nèi)存都不足 100M,那么部署服務(wù)會失敗;又如果有一個(gè) Node 的內(nèi)存有 16G 充裕,可以部署該 Pod,而在運(yùn)行中,該容器服務(wù)發(fā)生了內(nèi)存泄露,那么一旦超過 200M 就會因?yàn)?OOM 被 kill,盡管此時(shí)該機(jī)器上還有 15G+的內(nèi)存。command。容器的入口命令。對于這個(gè)筆者還存在很多困惑不解的地方,暫時(shí)挖個(gè)坑,有清楚的同學(xué)歡迎留言。args。容器的入口參數(shù)。同上,有清楚的同學(xué)歡迎留言。volumeMounts。容器要掛載的 Pod 數(shù)據(jù)卷等。請務(wù)必記住:Pod 的數(shù)據(jù)卷只有被容器掛載后才能使用!
第二步:執(zhí)行 kubectl 命令部署。有了 Pod 的 yaml 文件之后,就可以用 kubectl 部署了,命令非常簡單:kubectl create -f ${POD_YAML}。
隨后,會提示該命令是否執(zhí)行成功,比如 yaml 內(nèi)容不符合要求,則會提示哪一行有問題:

修正后,再次部署:

4.2 如何部署 Deployment?
第一步:準(zhǔn)備 Deployment 的 yaml 文件。首先來看 Deployment 的 yaml 文件內(nèi)容:
?apiVersion:?extensions/v1beta1
?kind:?Deployment
?metadata:
???name:?rss-site
???namespace:?mem-example
?spec:
???replicas:?2
???template:
?????metadata:
???????labels:
?????????app:?web
?????spec:
??????containers:
???????-?name:?memory-demo-ctr
?????????image:?polinux/stress
?????????resources:
?????????limits:
???????????emory:?"200Mi"
?????????requests:
???????????memory:?"100Mi"
?????????command:?["stress"]
?????????args:?["--vm",?"1",?"--vm-bytes",?"150M",?"--vm-hang",?"1"]
?????????volumeMounts:
?????????-?name:?redis-storage
???????????mountPath:?/data/redis
?????volumes:
?????-?name:?redis-storage
???????emptyDir:?{}
繼續(xù)來看幾個(gè)重要的字段:
metadata同 Pod 的 yaml,這里提一點(diǎn):如果沒有指明 namespace,那么就是用 kubectl 默認(rèn)的 namespace(如果 kubectl 配置文件中沒有指明 namespace,那么就是 default 空間)。spec,可以看到 Deployment 的spec字段是在 Pod 的spec內(nèi)容外“包了一層”,那就來看 Deployment 有哪些需要注意的:metadata,新手同學(xué)先不管這邊的信息。spec,會發(fā)現(xiàn)這完完全全是上文提到的 Pod 的spec內(nèi)容,在這里寫明了 Deployment 下屬管理的每個(gè) Pod 的具體內(nèi)容。replicas。副本個(gè)數(shù)。也就是該 Deployment 需要起多少個(gè)相同的 Pod,如果用戶成功在 K8S 中配置了 n(n>1)個(gè),那么 Deployment 會確保在集群中始終有 n 個(gè)服務(wù)在運(yùn)行。template。
第二步:執(zhí)行 kubectl 命令部署。Deployment 的部署辦法同 Pod:kubectl create -f ${DEPLOYMENT_YAML}。由此可見,K8S 會根據(jù)配置文件中的kind字段來判斷具體要創(chuàng)建的是什么資源。
這里插一句題外話:部署完 deployment 之后,可以查看到自動創(chuàng)建了 ReplicaSet 和 Pod,如下圖所示:

還有一個(gè)有趣的事情:通過 Deployment 部署的服務(wù),其下屬的 RS 和 Pod 命名是有規(guī)則的。讀者朋友們自己總結(jié)發(fā)現(xiàn)哦。
綜上,如何部署一個(gè) Pod 或者 Deployment 就結(jié)束了。
V. kubectl 查看、更新/編輯、刪除服務(wù)
作為 K8S 使用者而言,更關(guān)心的問題應(yīng)該是本章所要討論的話題:如何通過 kubectl 查看、更新/編輯、刪除在 K8S 上部署著的服務(wù)。
5.1 如何查看服務(wù)?
請務(wù)必記得一個(gè)事情:在 K8S 中,一個(gè)獨(dú)立的服務(wù)即對應(yīng)一個(gè) Pod。即,當(dāng)我們說要 xxx 一個(gè)服務(wù)的就是,也就是操作一個(gè) Pod。而與 Pod 服務(wù)相關(guān)的且需要用戶關(guān)心的,有 Deployment。
通過 kubectl 查看服務(wù)的基本命令是:
$?kubectl?get|describe?${RESOURCE}?[-o?${FORMAT}]?-n=${NAMESPACE}
#?${RESOURCE}有:?pod、deployment、replicaset(rs)
在此之前,還有一個(gè)需要回憶的事情是:Deployment、ReplicaSet 和 Pod 之間的關(guān)系 - 層層隸屬;以及這些資源和 namespace 的關(guān)系是 - 隸屬。如下圖所示。

因此,要查看一個(gè)服務(wù),也就是一個(gè) Pod,必須首先指定 namespace!那么,如何查看集群中所有的 namespace 呢?kubectl get ns:

于是,只需要通過-n=${NAMESPACE}就可以指定自己要操作的資源所在的 namespace。比如查看 Pod:kubectl get pod -n=oona-test,同理,查看 Deployment:kubectl get deployment -n=oona-test。
問題又來了:如果已經(jīng)忘記自己所部屬的服務(wù)所在的 namespace 怎么辦?這么多 namespace,一個(gè)一個(gè)查看過來嗎?
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有什么區(qū)別嗎?實(shí)際操作下就明白了,其他資源亦然:

哎,我們看到之前部署的 Pod 服務(wù) memory-demo 顯示的“ImagePullBackOff”是怎么回事呢?先不著急,我們慢慢看下去。
5.2 如何更新/編輯服務(wù)?
兩種辦法:1). 修改 yaml 文件后通過 kubectl 更新;2). 通過 kubectl 直接編輯 K8S 上的服務(wù)。
方法一:修改 yaml 文件后通過 kubectl 更新。我們看到,創(chuàng)建一個(gè) Pod 或者 Deployment 的命令是kubectl create -f ${YAML}。但是,如果 K8S 集群當(dāng)前的 namespace 下已經(jīng)有該服務(wù)的話,會提示資源已經(jīng)存在:

通過 kubectl 更新的命令是kubectl apply -f ${YAML},我們再來試一試:

(備注:命令kubectl apply -f ${YAML}也可以用于首次創(chuàng)建一個(gè)服務(wù)哦)
方法二:通過 kubectl 直接編輯 K8S 上的服務(wù)。命令為kubectl edit ${RESOURCE} ${NAME},比如修改剛剛的 Pod 的命令為kubectl edit pod memory-demo,然后直接編輯自己要修改的內(nèi)容即可。
但是請注意,無論方法一還是方法二,能修改的內(nèi)容還是有限的,從筆者實(shí)戰(zhàn)下來的結(jié)論是:只能修改/更新鏡像的地址和個(gè)別幾個(gè)字段。如果修改其他字段,會報(bào)錯:
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)
如果真的要修改其他字段怎么辦呢?恐怕只能刪除服務(wù)后重新部署了。
5.3 如何刪除服務(wù)?
在 K8S 上刪除服務(wù)的操作非常簡單,命令為kubectl delete ${RESOURCE} ${NAME}。比如刪除一個(gè) Pod 是:kubectl delete pod memory-demo,再比如刪除一個(gè) Deployment 的命令是:kubectl delete deployment ${DEPLOYMENT_NAME}。但是,請注意:
如果只部署了一個(gè) Pod,那么直接刪除該 Pod 即可;

如果是通過 Deployment 部署的服務(wù),那么僅僅刪除 Pod 是不行的,正確的刪除方式應(yīng)該是:先刪除 Deployment,再刪除 Pod。

關(guān)于第二點(diǎn)應(yīng)該不難想象:僅僅刪除了 Pod 但是 Deployment 還在的話,Deployment 定時(shí)會檢查其下屬的所有 Pod,如果發(fā)現(xiàn)失敗了則會再拉起。因此,會發(fā)現(xiàn)過一會兒,新的 Pod 又被拉起來了。
另外,還有一個(gè)事情:有時(shí)候會發(fā)現(xiàn)一個(gè) Pod 總也刪除不了,這個(gè)時(shí)候很有可能要實(shí)施強(qiáng)制刪除措施,命令為kubectl delete pod --force --grace-period=0 ${POD_NAME}。
VI. kubectl 排查服務(wù)問題
上文說道:部署的服務(wù) memory-demo 失敗了,是怎么回事呢?本章就會帶大家一起來看看常見的 K8S 中服務(wù)部署失敗、服務(wù)起來了但是不正常運(yùn)行都怎么排查呢?
首先,祭出筆者最愛的一張 K8S 排查手冊,來自博客《Kubernetes Deployment 故障排除圖解指南》:

哈哈哈,對于新手同學(xué)來說,上圖還是不夠友好,下面我們簡單來看兩個(gè)例子:
6.1 K8S 上部署服務(wù)失敗了怎么排查?
請一定記住這個(gè)命令:kubectl describe ${RESOURCE} ${NAME}。比如剛剛的 Pod 服務(wù) memory-demo,我們來看:

拉到最后看到Events部分,會顯示出 K8S 在部署這個(gè)服務(wù)過程的關(guān)鍵日志。這里我們可以看到是拉取鏡像失敗了,好吧,大家可以換一個(gè)可用的鏡像再試試。
一般來說,通過kubectl describe pod ${POD_NAME}已經(jīng)能定位絕大部分部署失敗的問題了,當(dāng)然,具體問題還是得具體分析。大家如果遇到具體的報(bào)錯,歡迎分享交流。
6.2 K8S 上部署的服務(wù)不正常怎么排查?
如果服務(wù)部署成功了,且狀態(tài)為running,那么就需要進(jìn)入 Pod 內(nèi)部的容器去查看自己的服務(wù)日志了:
查看 Pod 內(nèi)部某個(gè) container 打印的日志: kubectl log ${POD_NAME} -c ${CONTAINER_NAME}。進(jìn)入 Pod 內(nèi)部某個(gè) container: kubectl exec -it [options] ${POD_NAME} -c ${CONTAINER_NAME} [args],嗯,這個(gè)命令的作用是通過 kubectl 執(zhí)行了docker exec xxx進(jìn)入到容器實(shí)例內(nèi)部。之后,就是用戶檢查自己服務(wù)的日志來定位問題。
顯然,線上可能會遇到更復(fù)雜的問題,需要借助更多更強(qiáng)大的命令和工具。
寫在后面
本文希望能夠幫助對 K8S 不了解的新手快速了解 K8S。筆者一邊寫文章,一邊查閱和整理 K8S 資料,過程中越發(fā)感覺 K8S 架構(gòu)的完備、設(shè)計(jì)的精妙,是值得深入研究的,K8S 大受歡迎是有道理的。
- END -
?推薦閱讀? 基于 Nginx+lua+Memcache 實(shí)現(xiàn)灰度發(fā)布 Kubernetes生產(chǎn)環(huán)境最佳實(shí)踐 一文搞懂藍(lán)綠發(fā)布、灰度發(fā)布和滾動發(fā)布 高性能 Nginx HTTPS 調(diào)優(yōu) - 如何為 HTTPS 提速 30% 記一次 Linux服務(wù)器被入侵后的排查思路 用了3年Kubernetes,我們得到的5個(gè)教訓(xùn) Linux 運(yùn)維必備的 40 個(gè)命令總結(jié),收好了~
點(diǎn)亮,服務(wù)器三年不宕機(jī)

