本文是對於 Kubernetes 實戰系列文章的提煉。javascript
Kubernetes [koo-ber-nay'-tice] 是 Google 基於 Borg 開源的容器編排調度引擎,其支持多種底層容器虛擬化技術,具備完備的功能用於支撐分佈式系統以及微服務架構,同時具有超強的橫向擴容能力;它提供了自動化容器的部署和複製,隨時擴展或收縮容器規模,將容器組織成組,而且提供容器間的負載均衡,提供容器彈性等特性。做爲 CNCF(Cloud Native Computing Foundation)最重要的組件之一,可謂雲操做系統;它的目標不只僅是一個編排系統,而是提供一個規範,可讓你來描述集羣的架構,定義服務的最終狀態。html
與通常的 PaaS 平臺相比,K8s 也是支持服務部署、自動運維、資源調度、擴縮容、自我修復、負載均衡,服務發現等功能,而其獨特之處就是其對於基礎設施層進行了較好的能力抽象。K8s 並無處理具體的存儲、網絡這些差別性極大的部分,而是作雲無關,開始實現各種 interface,作各類抽象。好比容器運行時接口(CRI)、容器網絡接口(CNI)、容器存儲接口(CSI)。這些接口讓 Kubernetes 變得無比開放,而其自己則能夠專一於內部部署及容器調度。前端
Kubernetes 有相似於 Linux 的分層架構,以下圖所示:java
Kubernetes 中全部的配置都是經過 API 對象的 spec 去設置的,也就是用戶經過配置系統的理想狀態來改變系統,這是 Kubernetes 重要設計理念之一,即全部的操做都是聲明式(Declarative)的而不是命令式(Imperative)的。聲明式操做在分佈式系統中的好處是穩定,不怕丟操做或運行屢次,例如設置副本數爲 3 的操做運行屢次也仍是一個結果,而給副本數加 1 的操做就不是聲明式的,運行屢次結果就錯了。python
相對於命令式操做,聲明式操做會更穩定且更容易被用戶接受,由於該 API 中隱含了用戶想要操做的目標對象,而這些對象恰好都是名詞性質的,好比 Service、Deployment、PV 等;且聲明式的配置文件更貼近「人類語言」,好比 YAML、JSON。聲明式的設計理念有助於實現控制閉環,持續觀測、校訂,最終將運行狀態達到用戶指望的狀態;感知用戶的行爲並執行。好比修改 Pod 數量,應用升級/回滾等等。調度器是核心,但它只是負責從集羣節點中選擇合適的 Node 來運行 Pods,顯然讓調度器來實現上訴的功能不太合適,而須要有專門的控制器組件來實現。mysql
Kubernetes 的各類功能都離不開它定義的資源對象,這些對象均可以經過 API 被提交到集羣的 Etcd 中。API 的定義和實現都符合 HTTP REST 的格式,用戶能夠經過標準的 HTTP 動詞(POST、PUT、GET、DELETE)來完成對相關資源對象的增刪改查。經常使用的資源對象,好比 Deployment、DaemonSet、Job、PV 等。API 的抽象也意在這部分資源對象的定義。Kubernetes 有新的功能實現,通常會建立新的資源對象,而功能也依託於該對象進行實現。nginx
類別 | 名稱 |
---|---|
資源對象 | Pod、ReplicaSet、ReplicationController、Deployment、StatefulSet、DaemonSet、Job、CronJob、HorizontalPodAutoscaling、Node、Namespace、Service、Ingress、Label、CustomResourceDefinition |
存儲對象 | Volume、PersistentVolume、Secret、ConfigMap |
策略對象 | SecurityContext、ResourceQuota、LimitRange |
身份對象 | ServiceAccount、Role、ClusterRole |
這裏咱們選擇幾個關鍵對象進行介紹。git
部署表示用戶對 Kubernetes 集羣的一次更新操做。部署是一個比 RS 應用模式更廣的 API 對象,能夠是建立一個新的服務,更新一個新的服務,也能夠是滾動升級一個服務。滾動升級一個服務,實際是建立一個新的 RS,而後逐漸將新 RS 中副本數增長到理想狀態,將舊 RS 中的副本數減少到 0 的複合操做;這樣一個複合操做用一個 RS 是不太好描述的,因此用一個更通用的 Deployment 來描述。以 Kubernetes 的發展方向,將來對全部長期伺服型的的業務的管理,都會經過 Deployment 來管理。程序員
RC、RS 和 Deployment 只是保證了支撐服務的微服務 Pod 的數量,可是沒有解決如何訪問這些服務的問題。若是說 Deployment 是負責保證 Pod 組的正常運行,那麼 Service 就是用於保證以合理的網絡來鏈接到該組 Pod。github
一個 Pod 只是一個運行服務的實例,隨時可能在一個節點上中止,在另外一個節點以一個新的 IP 啓動一個新的 Pod,所以不能以肯定的 IP 和端口號提供服務。要穩定地提供服務須要服務發現和負載均衡能力。服務發現完成的工做,是針對客戶端訪問的服務,找到對應的的後端服務實例。在 K8 集羣中,客戶端須要訪問的服務就是 Service 對象。每一個 Service 會對應一個集羣內部有效的虛擬 IP,集羣內部經過虛擬 IP 訪問一個服務。Service 有三種類型:
<NodeIP>:NodePort
來訪問該服務。<NodeIP>:NodePort
。在 Kubernetes 集羣中微服務的負載均衡是由 Kube-proxy 實現的。Kube-proxy 是 Kubernetes 集羣內部的負載均衡器。它是一個分佈式代理服務器,在 Kubernetes 的每一個節點上都有一個;這一設計體現了它的伸縮性優點,須要訪問服務的節點越多,提供負載均衡能力的 Kube-proxy 就越多,高可用節點也隨之增多。與之相比,咱們平時在服務器端作個反向代理作負載均衡,還要進一步解決反向代理的負載均衡和高可用問題。
Kubernetes 實戰系列中咱們介紹了 Docker 本地搭建,基於 Ubuntu 手動搭建集羣以及基於 Rancher 快速搭建集羣等方式。使用 Rancher 能夠自動和可視化的完成 Kubernetes 集羣的安裝工做,省去的繁瑣的人工安裝過程,然您快速投入的業務開發中。
$ docker run -d --restart=unless-stopped -p 80:80 -p 443:443 rancher/rancher
先在 Master 節點安裝 Rancher server、control、etcd 和 worker。選擇網絡組件爲 Flannel,同時在自定義主機運行命令中選擇主機角色、填寫主機的內網和外網 IP。
咱們須要將腳本複製到對應的機器上運行,而後 Rancher 將自動建立 Kubernetes 集羣,並默認在 80 端口運行 Web Server。添加 Node 節點時只須要在 Rancher 的 Web 界面上找到您剛安裝的集羣並選擇【編輯集羣】並選擇節點角色爲 Worker 便可增長一臺 Kubenretes 集羣節點。
Helm 是由 Deis 發起的一個開源工具,有助於簡化部署和管理 Kubernetes 應用。在本章的實踐中,咱們也會使用 Helm 來簡化不少應用的安裝操做。
在 Linux 中可使用 Snap 安裝 Heml:
$ sudo snap install helm --classic # 經過鍵入以下命令,在 Kubernetes 羣集上安裝 Tiller $ helm init --upgrade
在缺省配置下, Helm 會利用 "gcr.io/kubernetes-helm/tiller" 鏡像在 Kubernetes 集羣上安裝配置 Tiller;而且利用 "https://kubernetes-charts.storage.googleapis.com" 做爲缺省的 stable repository 的地址。因爲在國內可能沒法訪問 "gcr.io", "storage.googleapis.com" 等域名,阿里雲容器服務爲此提供了鏡像站點。請執行以下命令利用阿里雲的鏡像來配置 Helm:
$ helm init --upgrade -i registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.5.1 --stable-repo-url https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts # 刪除默認的源 $ helm repo remove stable # 增長新的國內鏡像源 $ helm repo add stable https://burdenbear.github.io/kube-charts-mirror/ $ helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts # 查看 Helm 源添加狀況 $ helm repo list
Helm 的常見命令以下:
# 查看在存儲庫中可用的全部 Helm Charts $ helm search # 更新 Charts 列表以獲取最新版本 $ helm repo update # 查看某個 Chart 的變量 $ helm inspect values stable/mysql # 查看在羣集上安裝的 Charts 列表 $ helm list # 刪除某個 Charts 的部署 $ helm del --purge wordpress-test # 爲 Tiller 部署添加受權 $ kubectl create serviceaccount --namespace kube-system tiller $ kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller $ kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
get 命令用於獲取集羣的一個或一些 resource 信息。使用--help 查看詳細信息。kubectl 的幫助信息、示例至關詳細,並且簡單易懂。建議你們習慣使用幫助信息。kubectl 能夠列出集羣全部 resource 的詳細。resource 包括集羣節點、運行的 pod,ReplicationController,service 等。
$ kubectl get [(-o|--output=)json|yaml|wide|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...] (TYPE [NAME | -l label] | TYPE/NAME ...) [flags] [flags]
kubectl run 和 docker run 同樣,它能將一個鏡像運行起來,咱們使用 kubectl run 來將一個 sonarqube 的鏡像啓動起來。
$ kubectl run sonarqube --image=sonarqube:5.6.5 --replicas=1 --port=9000 deployment "sonarqube" created # 該命令爲咱們建立了一個 Deployment $ kubectl get deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE sonarqube 1 1 1 1 5m
咱們也能夠直接以交互方式運行某個鏡像:
$ kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il
K8s 將鏡像運行在 Pod 中以方便實施卷和網絡共享等管理,使用 get pods 能夠清楚的看到生成了一個 Pod:
$ kubectl get pods NAME READY STATUS RESTARTS AGE sonarqube-1880671902-s3fdq 1/1 Running 0 6m $ 交互式運行 Pod 中的某個命令 $ kubectl exec -it sonarqube-1880671902-s3fdq -- /bin/bash
kubectl 能夠用於刪除建立好的 Deployment 與 Pod:
$ kubectl delete pods sonarqube-1880671902-s3fdq $ kubectl delete deployment sonarqube
kubectl 通用能夠基於 Yaml 文件進行應用的生命週期管理:
# 建立 $ kubectl create -f yamls/mysql.yaml # 刪除 $ kubectl delete -f yamls/mysql.yaml # 同時建立多個 $ kubectl create -f yamls/ # 同時刪除多個 $ kubectl delete -f yamls/
在 K8s 集羣安裝完畢以後,能夠下載集羣的配置文件到本地 kubectl 配置中:
mkdir $HOME/.kube scp root@<master-public-ip>:/etc/kubernetes/kube.conf $HOME/.kube/config
而後能夠來查看當前的上下文
$ unset KUBECONFIG $ kubectl config current-context # 查看當前載入的上下文 $ kubectl config get-contexts # 瀏覽可用的上下文 $ kubectl config use-context context-name # 切換到指定上下文
Kubernetes 實戰/典型應用一節中,咱們介紹了許多常見的中間件的配置部署方式。這裏以簡單的 HTTP 服務器爲例,介紹常見的服務配置流程。
在 K8s Boilerplates 中咱們定義了簡單的 Nginx 的部署與服務,分別用於集羣構建與對外的服務暴露:
# nginx-deployment-service.yaml --- apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2 kind: Deployment metadata: name: nginx spec: strategy: type: Recreate selector: matchLabels: app: nginx replicas: 3 # tells deployment to run 1 pods matching the template template: # create pods using pod definition in this template metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx namespace: default labels: app: nginx spec: externalTrafficPolicy: Local ports: - name: http port: 80 selector: app: nginx type: NodePort
$ kubectl create -f https://raw.githubusercontent.com/wx-chevalier/Backend-Boilerplates/master/K8s/Base/nginx-deployment-service.yaml $ kubectl get pod NAME READY STATUS RESTARTS AGE nginx-56db997f77-2q6qz 1/1 Running 0 3m21s nginx-56db997f77-fv2zs 1/1 Running 0 3m21s nginx-56db997f77-wx2q5 1/1 Running 0 3m21s $ kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE nginx 3/3 3 3 3m36s $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 21h nginx NodePort 10.43.8.50 <none> 80:32356/TCP 4m5s
Ingress 是一種 Kubernetes 資源,也是將 Kubernetes 集羣內服務暴露到外部的一種方式。ngress 只是一個統稱,其由 Ingress 和 Ingress Controller 兩部分組成。Ingress 用做將原來須要手動配置的規則抽象成一個 Ingress 對象,使用 YAML 格式的文件來建立和管理。Ingress Controller 用做經過與 Kubernetes API 交互,動態的去感知集羣中 Ingress 規則變化。
目前可用的 Ingress Controller 類型有不少,好比:Nginx、HAProxy、Traefik 等,Nginx Ingress 使用 ConfigMap 來管理 Nginx 配置。
$ helm install --name nginx-ingress --set "rbac.create=true,controller.service.externalIPs[0]=172.19.157.1,controller.service.externalIPs[1]=172.19.157.2,controller.service.$ xternalIPs[2]=172.19.157.3" stable/nginx-ingress NAME: nginx-ingress LAST DEPLOYED: Tue Aug 20 14:50:13 2019 NAMESPACE: default STATUS: DEPLOYED RESOURCES: ==> v1/ConfigMap NAME DATA AGE nginx-ingress-controller 1 0s ==> v1/Pod(related) NAME READY STATUS RESTARTS AGE nginx-ingress-controller-5f874f7bf4-nvsvv 0/1 ContainerCreating 0 0s nginx-ingress-default-backend-6f598d9c4c-vj4v8 0/1 ContainerCreating 0 0s ==> v1/Service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx-ingress-controller LoadBalancer 10.43.115.59 172.19.157.1,172.19.157.2,172.19.157.3 80:32122/TCP,443:32312/TCP 0s nginx-ingress-default-backend ClusterIP 10.43.8.65 <none> 80/TCP 0s ==> v1/ServiceAccount NAME SECRETS AGE nginx-ingress 1 0s ==> v1beta1/ClusterRole NAME AGE nginx-ingress 0s ==> v1beta1/ClusterRoleBinding NAME AGE nginx-ingress 0s ==> v1beta1/Deployment NAME READY UP-TO-DATE AVAILABLE AGE nginx-ingress-controller 0/1 1 0 0s nginx-ingress-default-backend 0/1 1 0 0s ==> v1beta1/PodDisruptionBudget NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE nginx-ingress-controller 1 N/A 0 0s nginx-ingress-default-backend 1 N/A 0 0s
部署完成後咱們能夠看到 Kubernetes 服務中增長了 nginx-ingress-controller 和 nginx-ingress-default-backend 兩個服務。nginx-ingress-controller 爲 Ingress Controller,主要作爲一個七層的負載均衡器來提供 HTTP 路由、粘性會話、SSL 終止、SSL 直通、TCP 和 UDP 負載平衡等功能。nginx-ingress-default-backend 爲默認的後端,當集羣外部的請求經過 Ingress 進入到集羣內部時,若是沒法負載到相應後端的 Service 上時,這種未知的請求將會被負載到這個默認的後端上。
$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.43.0.1 <none> 443/TCP 20h nginx-ingress-controller LoadBalancer 10.43.115.59 172.19.157.1,172.19.157.2,172.19.157.3 80:32122/TCP,443:32312/TCP 77m nginx-ingress-default-backend ClusterIP 10.43.8.65 <none> 80/TCP 77m $ kubectl --namespace default get services -o wide -w nginx-ingress-controller NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR nginx-ingress-controller LoadBalancer 10.43.115.59 172.19.157.1,172.19.157.2,172.19.157.3 80:32122/TCP,443:32312/TCP 77m app=nginx-ingress,component=controller,release=nginx-ingress
因爲咱們採用了 externalIP 方式對外暴露服務, 因此 nginx-ingress-controller 會在三臺節點宿主機上的 暴露 80/443 端口。咱們能夠在任意節點上進行訪問,由於咱們尚未在 Kubernetes 集羣中建立 Ingress 資源,因此直接對 ExternalIP 的請求被負載到了 nginx-ingress-default-backend 上。nginx-ingress-default-backend 默認提供了兩個 URL 進行訪問,其中的 /healthz 用做健康檢查返回 200,而 / 返回 404 錯誤。
$ curl 127.0.0.1/ # default backend - 404 $ curl 127.0.0.1/healthz/ # 返回的是 200
後續咱們若是須要建立自身的 Ingress 配置,能夠參考以下方式:
apiVersion: extensions/v1beta1 kind: Ingress metadata: annotations: kubernetes.io/ingress.class: nginx name: example namespace: foo spec: rules: - host: www.example.com http: paths: - backend: serviceName: exampleService servicePort: 80 path: / # This section is only required if TLS is to be enabled for the Ingress tls: - hosts: - www.example.com secretName: example-tls
若是但願使用 TLS,那麼須要建立包含證書與 Key 的 Secret:
apiVersion: v1 kind: Secret metadata: name: example-tls namespace: foo data: tls.crt: <base64 encoded cert> tls.key: <base64 encoded key> type: kubernetes.io/tls
Helm 安裝完畢後,咱們來測試部署一個 WordPress 應用:
$ helm install --name wordpress-test --set "ingress.enabled=true,persistence.enabled=false,mariadb.persistence.enabled=false" stable/wordpress NAME: wordpress-test ...
這裏咱們使用 Ingress 負載均衡進行訪問,能夠經過以下方式訪問到服務:
$ kubectl get ingress NAME HOSTS ADDRESS PORTS AGE wordpress.local-wordpress-test wordpress.local 172.19.157.1,172.19.157.2,172.19.157.3 80 59m $ curl -I http://wordpress.local -x 127.0.0.1:80 HTTP/1.1 200 OK Server: nginx/1.15.6 Date: Tue, 20 Aug 2019 07:55:21 GMT Content-Type: text/html; charset=UTF-8 Connection: keep-alive Vary: Accept-Encoding X-Powered-By: PHP/7.0.27 Link: <http://wordpress.local/wp-json/>; rel="https://api.w.org/"
也能夠根據 Charts 的說明,利用以下命令得到 WordPress 站點的管理員用戶和密碼:
echo Username: user echo Password: $(kubectl get secret --namespace default wordpress-test-wordpress -o jsonpath="{.data.wordpress-password}" | base64 --decode) ==> v1beta1/Role NAME AGE nginx-ingress 0s ==> v1beta1/RoleBinding NAME AGE nginx-ingress 0s
某熊的技術之路指北 ☯ 就是對筆者不一樣領域方面沉澱下的知識倉庫的導航與索引,便於讀者快速地尋找到本身須要的內容。路漫漫其修遠兮,吾正上下而求索,也但願能給全部碰見過筆者痕跡的同窗些許幫助,在浩瀚銀河間能順利達到一個又一個彼岸。
您能夠經過如下導航來在 Gitbook 中閱讀筆者的系列文章,涵蓋了技術資料概括、編程語言與理論、Web 與大前端、服務端開發與基礎架構、雲計算與大數據、數據科學與人工智能、產品設計等多個領域:
此外,你還可前往 xCompass 交互式地檢索、查找須要的文章/連接/書籍/課程;或者在 MATRIX 文章與代碼索引矩陣中查看文章與項目源代碼等更詳細的目錄導航信息。最後,你也能夠關注微信公衆號:『某熊的技術之路』以獲取最新資訊。