使用 Kubernetes 部署一個記事本項目

Kubernetes 簡稱 k8s,是 google 在 2014 年發佈的一個開源項目。前端

Kubernetes 解決了哪些問題?node

真實的生產環境應用會包含多個容器,而這些容器還極可能會跨越多個服務器主機部署。Kubernetes 提供了爲那些工做負載大規模部署容器的編排與管理能力。Kubernetes 編排讓你可以構建多容器的應用服務,在集羣上調度或伸縮這些容器,以及管理它們隨時間變化的健康狀態。git

  • kubernetes 基礎
  • kubernetes 優化
  • kubernetes 實戰

Kubernetes 基礎概念不少,第一次接觸容易看暈,若是是新手,建議直接看實戰部分,先跑起來再說。github

kubernetes 基礎

k8s 中有幾個重要概念。web

概念 介紹
cluster 一個 k8s 集羣
master 集羣中的一臺機器,是集羣的核心,負責整個集羣的管理和控制
node 集羣中的一臺機器,是集羣中的工做負載節點
pod k8s 最小調度單位,每一個 pod 包含一個或多個容器
label 一個 key = value 鍵值對,能夠附加到各類資源對象上,方便其餘資源進行匹配
controller k8s 經過 controller 來管理 pod
service 對外提供統一的服務,自動將請求分發到正確的 pod 處
namespace 將 cluster 邏輯上劃分紅多個虛擬 cluster,每一個 cluster 就是一個 namespace

Cluster

Cluster 表明一個 k8s 集羣,是計算、存儲和網絡資源的集合,k8s 利用這些資源運行各類基於容器的應用。docker

Master 節點

Master 是 cluster 的大腦,運行着的服務包括 kube-apiserver、kube-scheduler、kube-controller-manager、etcd 和 pod 網絡。數據庫

  • kube-apiserver
    • apiserver 是 k8s cluster 的前端接口,提供 restful api。各類客戶端工具以及 k8s 其餘組件能夠經過它管理 cluster 中的各類資源。
  • kube-scheduler
    • 負責決定將 pod 放在哪一個 node 上運行,scheduler 在調度時會充分考慮 cluster 中各個節點的負載,以及應用對高可用、性能、數據親和性的需求。
  • kube-controller-manager
    • 負責管理 cluster 各類資源,保證資源處理預期狀態。controller-manager 由多種 controller 組成,包括 replication controller,endpoint controller,namespace controller,serviceaccount controller 等。
    • 不一樣的 controller 管理不一樣的資源,例如:replication controller 管理 deployment ,statefulset,damonset 的生命週期,namespace controller 資源管理 namespace 資源。
  • etcd(分佈式 key-value 存儲庫)
    • 負責保存 cluster 的配置信息和各類資源的狀態信息。當數據發生變化時,etcd 會快速地通知 k8s 相關組件。
  • pod 網絡
    • pod 要可以通訊,cluster 必須部署 pod 網絡,flannel 是其中一個可選方案。

Node 節點

Node 節點 是 pod 運行的地方。node 節點上運行的 k8s 組件有 kubelet、kube-proxy 和 pod 網絡。後端

  • kubelet
    • kubelet 是 node 節點的代理,當 master 節點中 kube-scheduler 肯定在某個 node 節點上運行 pod 後,會將 pod 的具體配置信息發送給該節點的 kubelet,kubelet 根據這些信息建立和運行容器,並向 master 節點報告運行狀態。
  • kube-proxy
    • 每一個 node 節點都會運行 kube-proxy 服務,它負責將訪問 service 的請求轉發到後端的容器。若是有多個副本,kube-proxy 會實現負載均衡。
  • pod 網絡
    • pod 要可以相互通訊,k8s cluster 必須部署 pod 網絡,flannel 是其中一個可選方案。

Pod

每個 pod 包含一個或多個 container,pod 中的容器做爲一個總體被 master 調度到 node 節點上運行。api

爲何 k8s 使用 pod 管理容器,而不是直接操做容器?安全

一、由於有些容器天生就是須要緊密的聯繫,放在一個 pod 中表示一個完整的服務,k8s 同時會在每一個 pod 中加入 pause 容器,來管理內部容器組的狀態。

二、Pod 中的全部容器共享 ip,共享 volume,方便進行容器之間通訊和數據共享。

何時須要在 pod 中定義多個容器?

答:這些容器聯繫很是緊密,並且須要直接共享資源,例如一個爬蟲程序,和一個 web server 程序。web server 強烈依賴爬蟲程序提供數據支持。

Label

Label 是一個 key = value 的鍵值對,其中 key 和 value 都由用戶本身指定。

Label 的使用場景:

  • kube-controller
    • 經過 label selecter 篩選出要監控的 pod 副本,使 pod 副本數量符合預期。
  • kube-proxy
    • 經過 label selecter 選擇對應的 pod,自動創建每一個 service 到對應 pod 的請求轉發路由表,從而實現 service 的智能負載均衡機制。
  • 經過對 node 定義 label,在 pod 中使用 node selector 匹配,kube-scheduler 進程能夠實現 pod 定向調度的特性。

總之,label 能夠給對象建立多組標籤,label 和 label selecter 共同構建了 k8s 系統中核心的應用模型,使得被管理對象可以被精細地分組管理,同時實現了整個集羣的高可用性。

Controller

k8s 一般不會直接建立 pod,而是經過 controller 來管理 pod。controller 中定義了 pod 的部署特性,好比有幾個副本,在什麼樣的 node 上運行等。爲了知足不一樣的業務場景,k8s 提供了多種類型的 controller。

  • Deployment
    • 最常使用,能夠管理 pod 多個副本,並確保 pod 按照指望的狀態運行,底層調用 ReplicaSet。
  • ReplicaSet
    • 實現 pod 的多副本管理,一般使用 Deployment 就夠了。
  • DaemonSet
    • 用於每一個 node 最多隻運行一個 pod 副本的場景。
    • 使用場景
      • 在集羣的每一個節點上運行存儲 Daemon,好比 glusterd 或 ceph。
      • 在每一個節點上運行日誌蒐集 Daemon,好比 flunentd 或 logstash。
      • 在每一個節點上運行監控,好比 Prometheus Node Exporter 或 collected。
  • StatefuleSet
    • 可以保證 pod 的每一個副本在整個生命週期中名稱是不變的,而其餘 controller 不提供這個功能。
  • Job
    • 用於運行結束就刪除的應用,而其餘 controller 中的 pod 一般是長期持續運行。

提示 使用 deployment controller 建立的用例,若是出現有 pod 掛掉的狀況,會自動新建一個 pod,來維持配置中的 pod 數量。

Job

容器按照持續運行時間可分爲兩類:服務類容器和工做類容器。

服務類容器一般持續提供服務,須要一直運行,好比 http server。工做類容器則是一次性任務,好比批處理程序,完成後容器就退出。

Controller 中 deployment、replicaSet 和 daemonSet 類型都用於管理服務類容器,對於工做類容器,咱們使用 job。

Service

Service 是能夠訪問一組 pod 的策略,一般稱爲微服務。具體訪問哪一組 pod 是經過 label 匹配出來的。service 爲 pod 提供了負載均衡,原理是使用 iptables。

爲何要用 service ?

  • pod 是有生命週期的,它們能夠被建立,也能夠被銷燬,然而一旦被銷燬生命就永遠結束。而 pod 在一個 k8s 集羣中可能常常性的建立,銷燬,每一次重建都會產生一個新的 ip 地址。
  • service 從邏輯上表明瞭一組 pod,具體是哪些 pod 是由 label 來挑選的。service 有本身的 ip,並且這個 ip 是不變的,客戶端只須要訪問 service 的 ip,k8s 則負責創建和維護 service 與 pod 的映射關係,不管 pod 如何變化,對客戶端不會有任何影響,由於 service 沒有變。

外網如何訪問 service?

  • ClusterIP:經過集羣的內部 ip 暴露服務,選擇該值,服務只可以在集羣內部能夠訪問,這也是默認的 ServiceType。
  • NodePort:經過每一個 node 上的 ip 和端口(NodePort)暴露服務。NodePort 服務會路由到 ClusterIP 服務,這個 ClusterIP 服務會自動建立。經過請求 nodeip:nodeport,能夠從集羣的外部訪問一個 service 服務。
  • LoadBalancer:使用雲提供商的負載局衡器,能夠向外部暴露服務。外部的負載均衡器能夠路由到 NodePort 服務和 ClusterIP 服務。
  • ExternalName:經過返回 CNAME 和它的值,能夠將服務映射到 externalName 字段的內容(例如, foo.bar.example.com)。 沒有任何類型代理被建立,這隻有 k8s 1.7 或更高版本的 kube-dns 才支持。

Namespace

若是有多個用戶使用同一個 k8s cluster,如何將他們建立的 controller,pod 等資源分開呢?

答:使用 namespace。

若是將物理的 cluster 邏輯上劃分紅多個虛擬 cluster,每一個 cluster 就是一個 namespace,不一樣 namespace 裏的資源是徹底隔離的。

k8s 默認建立了兩個 namespace。

  • default 建立資源時若是不指定,將會放到這個 namespace 中。
  • kube-system 存放 k8s 本身建立的系統資源。

Kubernetes 優化

  • 健康檢查
  • 數據管理
  • 密碼管理
  • 集羣監控

健康檢查

強大的自愈能力是 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 支持多種類型。

  • emptyDir
    • 數據存放在 pod 中,對 pod 中的容器來講,是持久的,只要 pod 還在數據就還在。
  • hostPath
    • 數據存在主機上,主機在數據就在。
  • AWS Elastic Block Store
    • 數據存在雲服務器上。
  • Persistent Volume
    • 自定義一塊外部存儲空間 Persistent Volume,而後在建立 pod 時使用 PersistentVolumeClaim(PVC)去申請空間,並進行存儲。

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 集羣並部署容器化應用只是第一步。一旦集羣運行起來,咱們須要確保集羣一切都是正常的,這就須要對集羣進行監控。

經常使用的可視化監控工具以下。

  • Weave Scope
  • Heapster
  • Prometheus Operator

具體的使用步驟就直接看文檔了,這裏不詳細說明。

經過集羣監控咱們可以及時發現集羣出現的問題,但爲了方便進一步排查問題,咱們還須要進行進行日誌記錄。

經常使用的日誌管理工具以下。

  • Elasticsearch 負責存儲日誌並提供查詢接口。
  • Fluentd 負責從 k8s 蒐集日誌併發送給 Elasticsearch。
  • Kibana 提供一個可視化頁面,用戶能夠瀏覽和搜索日誌。

Kubernetes 實戰

咱們來實戰部署一個 k8s 記事本項目,項目使用 yhlben/notepad 鏡像進行構建,該鏡像在部署後會在 8083 端口上提供一個 web 版記事本服務,查看演示

爲了不安裝 k8s 出現的各類坑,這裏使用 Play with Kubernetes進行演示。

首先在 Play with Kubernetes 上建立 3 臺服務器,node1 做爲 master 節點,node2 和 node3 做爲工做節點。接下來進行如下操做;

  • 建立一個集羣 cluster
  • 加入 node 節點
  • 初始化 cluster 網絡
  • 建立 controller
  • 建立 service
  • 執行部署

建立一個集羣 cluster

在 node1 上運行 kubeadm init 便可建立一個集羣。

kubeadm init --apiserver-advertise-address $(hostname -i)
複製代碼

執行完成後會生成 token,這樣其餘節點就能夠憑藉這個 token 加入該集羣。

Kubernetes 實戰步驟1

加入 node 節點

在 node2 和 node3 機器上,分別執行如下命令,加入到 node1 的集羣。

kubeadm join 192.168.0.8:6443 --token nfs9d0.z7ibv3xokif1mnmv \
    --discovery-token-ca-cert-hash sha256:6587f474ae1543b38954b0e560832ff5b7c67f79e1d464e7f59e33b0fefd6548
複製代碼

命令執行完畢後,便可看到 node2,node3 已經加入成功。

Kubernetes 實戰步驟2

查看集羣狀態

在 node1 節點上,執行如下命令。

kubectl get node
複製代碼

Kubernetes 實戰步驟3

能夠看到,集羣中存在 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 狀態了。

Kubernetes 實戰步驟4

建立 Deployment

咱們經過配置文件來建立 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。

Kubernetes 實戰步驟4.1

能夠看到,咱們使用 deployment 類型的 controller 建立了 3 個 pod,分別運行在 node2 和 node3 機器上,若是有更多的機器,也會自動負載均衡,合理地分配到每一個機器上。

建立 Service

建立 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 查看建立結果。

Kubernetes 實戰步驟5

能夠看到,deployment 和 service 均建立成功,而且已知 service 暴露的 ip 地址爲:10.106.74.65,端口號爲 80,因爲設置了 targetPort,service 在接收到請求時,會自動轉發到 pod 對應的 8083 端口上。

訪問部署結果

部署成功後,咱們能夠經過兩種方式進行訪問:

  • 集羣內:經過 service 的 clusterIp + port 端口進行訪問。
  • 集羣外:經過任意一個 node 節點 + nodePort 端口進行訪問。
# 集羣內訪問
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
複製代碼

集羣內訪問。

Kubernetes 實戰步驟6

集羣外訪問。

Kubernetes 實戰步驟6

到這裏,已經算部署成功了,你們確定有疑問,部署一個如此簡單的 web 應用就這麼麻煩,到底 k8s 好在哪裏?咱們接着往下看。

K8s 運維

項目已經部署,接下來來實戰一個運維,感覺一下 k8s 帶給咱們的便利。

案例 1

公司要作雙 11 活動,須要至少 100 個容器才能知足用戶要求,應該怎麼作?

首先,應該儘量利用當前擁有的服務器資源,建立更多的容器來參與負載均衡,經過 docker stats 能夠查看容器佔用的系統資源狀況。若是充分利用後仍然不能知足需求,就根據剩餘須要的容器,計算出須要購買多少機器,實現資源的合理利用。

  • 購買服務器,將服務器做爲 node 節點,join 到集羣中。
  • 執行擴容命令。

執行如下命令就能將容器擴展到 100 個。

kubectl scale deployments/notepad --replicas=100
複製代碼

如圖,擴展 10 個 pod 的狀況,node2 和 node3 節點共同分擔負載。

Kubernetes 實戰步驟8

也能夠經過修改 deployment.yaml 中的 replicas 字段,執行 kubectl apply -f deployment.yaml去執行擴展。若是活動結束了,只須要將多餘的服務器刪除,縮減容器數量便可還原到以前的效果。

案例 2

雙 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,經過一個簡單的集羣來實現這一點,但其中也踩了好多坑,具體以下:

  • 使用 minikube 搭建項目
    • 能快速搭建一個單節點 k8s 環境。
    • 但在本地使用 minikube 搭建一套 k8s 集羣,不少包裝不上,全局代理也不行。
  • 使用 google clould 上的服務器
    • 在 gogole clould 上,解決了網絡問題,須要安裝的包都能裝上。
    • 但因爲是新服務器,須要各類安裝環境,docker,kubeadm,kubectl 等,安裝過程繁瑣,還可能會遇到報錯。
    • 不知道哪天手滑了一下,試用帳號變成了付費帳號,贈金 $300 就這樣沒了 😭。
  • 使用 play with kubernetes
    • k8s 所需環境不用配,已經裝好了,這一點很不錯。
    • 單臺機器容量過小,稍微大一點的鏡像就安裝不進去了。
    • 偶爾使用 play with kubernetes 時,剛新建完一個實例,就被踢下線,遇到這種狀況就無法玩。
    • 沒有提供公網 ip,沒法驗證外網訪問狀況。

最後,推薦一下《天天 5 分鐘玩轉 Kubernetes》這本書,一本很是適合入門 k8s 的實戰書。書中經過大量的簡單實戰,從易到難,讓我真正理解了 k8s,本文中的大量理論知識也都來自這本書。

更多文章,歡迎 Star 和 訂閱 個人博客

相關文章
相關標籤/搜索