Kubernetes 簡稱 k8s,是 google 在 2014 年發佈的一個開源項目。前端
Kubernetes 解決了哪些問題?node
真實的生產環境應用會包含多個容器,而這些容器還極可能會跨越多個服務器主機部署。Kubernetes 提供了爲那些工做負載大規模部署容器的編排與管理能力。Kubernetes 編排讓你可以構建多容器的應用服務,在集羣上調度或伸縮這些容器,以及管理它們隨時間變化的健康狀態。git
Kubernetes 基礎概念不少,第一次接觸容易看暈,若是是新手,建議直接看實戰部分,先跑起來再說。github
k8s 中有幾個重要概念。web
概念 | 介紹 |
---|---|
cluster | 一個 k8s 集羣 |
master | 集羣中的一臺機器,是集羣的核心,負責整個集羣的管理和控制 |
node | 集羣中的一臺機器,是集羣中的工做負載節點 |
pod | k8s 最小調度單位,每一個 pod 包含一個或多個容器 |
label | 一個 key = value 鍵值對,能夠附加到各類資源對象上,方便其餘資源進行匹配 |
controller | k8s 經過 controller 來管理 pod |
service | 對外提供統一的服務,自動將請求分發到正確的 pod 處 |
namespace | 將 cluster 邏輯上劃分紅多個虛擬 cluster,每一個 cluster 就是一個 namespace |
Cluster 表明一個 k8s 集羣,是計算、存儲和網絡資源的集合,k8s 利用這些資源運行各類基於容器的應用。docker
Master 是 cluster 的大腦,運行着的服務包括 kube-apiserver、kube-scheduler、kube-controller-manager、etcd 和 pod 網絡。數據庫
Node 節點 是 pod 運行的地方。node 節點上運行的 k8s 組件有 kubelet、kube-proxy 和 pod 網絡。後端
每個 pod 包含一個或多個 container,pod 中的容器做爲一個總體被 master 調度到 node 節點上運行。api
爲何 k8s 使用 pod 管理容器,而不是直接操做容器?安全
一、由於有些容器天生就是須要緊密的聯繫,放在一個 pod 中表示一個完整的服務,k8s 同時會在每一個 pod 中加入 pause 容器,來管理內部容器組的狀態。
二、Pod 中的全部容器共享 ip,共享 volume,方便進行容器之間通訊和數據共享。
何時須要在 pod 中定義多個容器?
答:這些容器聯繫很是緊密,並且須要直接共享資源,例如一個爬蟲程序,和一個 web server 程序。web server 強烈依賴爬蟲程序提供數據支持。
Label 是一個 key = value 的鍵值對,其中 key 和 value 都由用戶本身指定。
Label 的使用場景:
總之,label 能夠給對象建立多組標籤,label 和 label selecter 共同構建了 k8s 系統中核心的應用模型,使得被管理對象可以被精細地分組管理,同時實現了整個集羣的高可用性。
k8s 一般不會直接建立 pod,而是經過 controller 來管理 pod。controller 中定義了 pod 的部署特性,好比有幾個副本,在什麼樣的 node 上運行等。爲了知足不一樣的業務場景,k8s 提供了多種類型的 controller。
提示 使用 deployment controller 建立的用例,若是出現有 pod 掛掉的狀況,會自動新建一個 pod,來維持配置中的 pod 數量。
容器按照持續運行時間可分爲兩類:服務類容器和工做類容器。
服務類容器一般持續提供服務,須要一直運行,好比 http server。工做類容器則是一次性任務,好比批處理程序,完成後容器就退出。
Controller 中 deployment、replicaSet 和 daemonSet 類型都用於管理服務類容器,對於工做類容器,咱們使用 job。
Service 是能夠訪問一組 pod 的策略,一般稱爲微服務。具體訪問哪一組 pod 是經過 label 匹配出來的。service 爲 pod 提供了負載均衡,原理是使用 iptables。
爲何要用 service ?
外網如何訪問 service?
nodeip:nodeport
,能夠從集羣的外部訪問一個 service 服務。若是有多個用戶使用同一個 k8s cluster,如何將他們建立的 controller,pod 等資源分開呢?
答:使用 namespace。
若是將物理的 cluster 邏輯上劃分紅多個虛擬 cluster,每一個 cluster 就是一個 namespace,不一樣 namespace 裏的資源是徹底隔離的。
k8s 默認建立了兩個 namespace。
強大的自愈能力是 k8s 這類容器編排引擎的一個重要特性。自愈的默認實現方式是自動重啓發生故障的容器。除此以外,用戶還能夠利用 liveness 和 readiness 探測機制設置更精細的健康檢查,進而實現以下需求:
默認狀況下,只有容器進程返回值非零,k8s 纔會認爲容器發生了故障,須要重啓。若是咱們想更加細粒度的控制容器重啓,可使用 liveness 和 readiness。
liveness 和 readiness 的原理是按期檢查 /tmp/healthy
文件是否存在,若是存在即認爲程序沒有出故障,若是文件不存在,則會採起相應的措施來進行反饋。
liveness 採起的策略是重啓容器,而 readiness 採起的策略是將容器設置爲不可用。
若是須要在特定狀況下重啓容器,可使用 liveness。 若是須要保證容器一直能夠對外提供服務,可使用 readiness。
咱們能夠將 liveness 和 readiness 配合使用,使用 liveness 判斷容器是否須要重啓,用 readiness 判斷容器是否已經準備好對外提供服務。
上文說道,pod 可能會被頻繁地銷燬和建立,當容器銷燬時,保存在容器內部文件系統中的數據都會被清除。爲了持久化保存容器的數據,可使用 k8s volume。
Volume 的生命週期獨立於容器,pod 中的容器可能被銷燬和重建,但 volume 會被保留。實質上 vloume 是一個目錄,當 volume 被 mount 到 pod,pod 中的全部容器均可以訪問到這個 volume。
Volume 支持多種類型。
Volume 提供了對各類類型的存放方式,但容器在使用 volume 讀寫數據時,不須要關心數據究竟是存放在本地節點的系統中仍是雲硬盤上。對容器來講,全部類型的 volume 都只是一個目錄。
應用程序在啓動過程當中可能須要一些敏感信息,好比訪問數據庫的用戶名和密碼。將這些信息直接保存在容器鏡像中顯然不妥,k8s 提供的解決方案是 secret。
secret 會以密文的方式存儲數據,避免直接在配置文件中保存敏感信息。secret 會以 volume 的形式被 mount 到 pod,容器可經過文件的方式使用 secret 中的敏感數據,此外容器也能夠按環境變量的形式使用這些數據。
使用配置文件建立 mysecret.yaml:
apiVersion: v1
kind Secret
metadata:
name:mysecret
data:
username:admin
password:123
複製代碼
保存配置文件後,而後執行kubectl apply -f mysecret.yaml
進行建立。
在 pod 中使用建立好的 secret:
# mypod.yaml
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: mypod
image: yhlben/notepad
volumeMounts:
- name: foo
mountPath: 'etc/foo'
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
複製代碼
執行kubectl apply -f mypod.yaml
建立 pod,並使用 secret。建立完成後,secret 保存在容器內 /etc/foo/username ,/etc/foo/password 目錄下。
建立 k8s 集羣並部署容器化應用只是第一步。一旦集羣運行起來,咱們須要確保集羣一切都是正常的,這就須要對集羣進行監控。
經常使用的可視化監控工具以下。
具體的使用步驟就直接看文檔了,這裏不詳細說明。
經過集羣監控咱們可以及時發現集羣出現的問題,但爲了方便進一步排查問題,咱們還須要進行進行日誌記錄。
經常使用的日誌管理工具以下。
咱們來實戰部署一個 k8s 記事本項目,項目使用 yhlben/notepad 鏡像進行構建,該鏡像在部署後會在 8083 端口上提供一個 web 版記事本服務,查看演示。
爲了不安裝 k8s 出現的各類坑,這裏使用 Play with Kubernetes進行演示。
首先在 Play with Kubernetes 上建立 3 臺服務器,node1 做爲 master 節點,node2 和 node3 做爲工做節點。接下來進行如下操做;
在 node1 上運行 kubeadm init 便可建立一個集羣。
kubeadm init --apiserver-advertise-address $(hostname -i)
複製代碼
執行完成後會生成 token,這樣其餘節點就能夠憑藉這個 token 加入該集羣。
在 node2 和 node3 機器上,分別執行如下命令,加入到 node1 的集羣。
kubeadm join 192.168.0.8:6443 --token nfs9d0.z7ibv3xokif1mnmv \
--discovery-token-ca-cert-hash sha256:6587f474ae1543b38954b0e560832ff5b7c67f79e1d464e7f59e33b0fefd6548
複製代碼
命令執行完畢後,便可看到 node2,node3 已經加入成功。
在 node1 節點上,執行如下命令。
kubectl get node
複製代碼
能夠看到,集羣中存在 node1 ,node2,node3 這 3 個節點,但這 3 個節點的都是 NotReady 狀態,爲何?
答:由於沒有建立集羣網絡。
執行如下代碼建立集羣網絡。
kubectl apply -n kube-system -f \
"https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 |tr -d '\n')"
複製代碼
執行命令後,稍等一下,而後查看 node 狀態,能夠看到,集羣中的 3 個節點都是 Ready 狀態了。
咱們經過配置文件來建立 deployment,新建 deployment.yaml 文件,內容以下:
# 配置文件格式的版本
apiVersion: apps/v1
# 建立的資源類型
kind: Deployment
# 資源的元數據
metadata:
name: notepad
# 規格說明
spec:
# 定義 pod 數量
replicas: 3
# 經過 label 找到對應的 pod
selector:
matchLabels:
app: mytest
# 定義 pod 的模板
template:
# pod 的元數據
metadata:
# 定義 pod 的 label
labels:
app: mytest
# 描述 pod 的規格
spec:
containers:
- name: notepad
image: yhlben/notepad
ports:
- containerPort: 8083
複製代碼
文件建立以後,執行命令:
kubectl apply -f ./deployment.yaml
# deployment.apps/notepad created
複製代碼
查看部署的 pod。
能夠看到,咱們使用 deployment 類型的 controller 建立了 3 個 pod,分別運行在 node2 和 node3 機器上,若是有更多的機器,也會自動負載均衡,合理地分配到每一個機器上。
建立 service 和 deployment 相似,新建 service.yaml 文件,內容以下:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
# 在節點上部署訪問 pod 的端口
type: NodePort
ports:
- port: 80
# service 代理的端口
targetPort: 8083
# node 節點上提供給集羣外部的訪問端口
nodePort: 30036
# 匹配 pod
selector:
app: mytest
複製代碼
文件建立以後,執行命令:
kubectl apply -f ./service.yaml
# service/my-service created
複製代碼
使用 kubectl get deployment 和 kubectl get service 查看建立結果。
能夠看到,deployment 和 service 均建立成功,而且已知 service 暴露的 ip 地址爲:10.106.74.65,端口號爲 80,因爲設置了 targetPort,service 在接收到請求時,會自動轉發到 pod 對應的 8083 端口上。
部署成功後,咱們能夠經過兩種方式進行訪問:
# 集羣內訪問
curl 10.106.74.65
# 集羣外訪問
# 192.168.0.12 是 node2 節點的ip
# 192.168.0.11 是 node3 節點的ip
curl 192.168.0.12:30036
curl 192.168.0.11:30036
複製代碼
集羣內訪問。
集羣外訪問。
到這裏,已經算部署成功了,你們確定有疑問,部署一個如此簡單的 web 應用就這麼麻煩,到底 k8s 好在哪裏?咱們接着往下看。
項目已經部署,接下來來實戰一個運維,感覺一下 k8s 帶給咱們的便利。
公司要作雙 11 活動,須要至少 100 個容器才能知足用戶要求,應該怎麼作?
首先,應該儘量利用當前擁有的服務器資源,建立更多的容器來參與負載均衡,經過 docker stats 能夠查看容器佔用的系統資源狀況。若是充分利用後仍然不能知足需求,就根據剩餘須要的容器,計算出須要購買多少機器,實現資源的合理利用。
執行如下命令就能將容器擴展到 100 個。
kubectl scale deployments/notepad --replicas=100
複製代碼
如圖,擴展 10 個 pod 的狀況,node2 和 node3 節點共同分擔負載。
也能夠經過修改 deployment.yaml 中的 replicas 字段,執行 kubectl apply -f deployment.yaml
去執行擴展。若是活動結束了,只須要將多餘的服務器刪除,縮減容器數量便可還原到以前的效果。
雙 11 活動很火爆,但忽然加了需求,須要緊急上線,若是實現滾動更新?
滾動更新就是在不宕機的狀況下,實現代碼更新。執行如下命令,修改 image 便可。
kubectl set image deployments/notepad notepad=yhlben/notepad:new
複製代碼
也能夠經過修改 deployment.yaml 中的 image 字段,執行 kubectl apply -f deployment.yaml
去執行升級。
若是更新出了問題,k8s 內置了一鍵還原上個版本的命令:
kubectl rollout undo deployments/notepad
複製代碼
經過這 2 個案例,感受到了 k8s 給運維帶來了很大的便利:快速高效地部署項目,支持動態擴展、滾動升級,同時還能按需優化使用的硬件資源。
本文的目的就是入門 k8s,經過一個簡單的集羣來實現這一點,但其中也踩了好多坑,具體以下:
最後,推薦一下《天天 5 分鐘玩轉 Kubernetes》這本書,一本很是適合入門 k8s 的實戰書。書中經過大量的簡單實戰,從易到難,讓我真正理解了 k8s,本文中的大量理論知識也都來自這本書。
更多文章,歡迎 Star 和 訂閱 個人博客。