1. 程式人生 > 程式設計 >kubernetes 上手指南:概念篇

kubernetes 上手指南:概念篇

大家好,我叫謝偉,是一名程式設計師。

今天的主題:kubernetes 概念篇,通過一些示例,學習 kubernetes(k8s) 的一些核心概念。

這些知識不可避免的受筆者經驗的限制,希望大家辯證的看待

kubernetes 角色是“作業系統”

上一節介紹了容器相關的知識,引申出單節點部署多個容器可以使用 docker-compose。而針對多節點的部署方案,docker-compose 無能為力,轉而介紹今天的角色:kubernetes。

kubernetes 底層操作的具體資源是容器(學習者需要優先掌握容器的相關知識)。容器的本質是程式,相當於在作業系統上執行的例項。

具體來講:k8s: 是一個跨主機叢集的開源容器排程平臺,可以自動化的應用容器的部署、擴充套件和操作,提供以容器為中心的基礎架構。

先拋開 k8s 涉及的具體的知識,就我們熟知的作業系統,你覺得會包含哪些知識?

  • 計算:資源管理
  • 儲存:外部的、內部的儲存
  • 網路:應用之間互聯
  • 擴充套件:相當於如何安裝各種各樣的軟體
  • 配置:配置檔案
  • 許可權:對使用者操作資源進行許可權認證
  • ...

沒錯,k8s 提供這些能力。

那麼 k8s 如何提供的這些能力?

這得從 k8s 架構說起。

k8s 的架構借鑑了 Google 內部的大規模叢集管理系統 Borg,負責對 Google 內部很多核心服務的排程和管理。

k8s

整體上來說: k8s 包含兩個部分:Master 節點, Node 節點。各節點完成不同的任務。

就 Master 而言包含,其包含4個部分:

  • APIServer: 內部的 web 服務,資源操作的入口
  • Scheduler: 排程器:負責資源的排程,比如把部署的應用部署在哪個節點上
  • Controller manager 負責叢集的狀態,比如故障診斷、自動擴充套件等
  • etcd 儲存叢集的整個資訊

這四個服務在 Master 節點部署完成之後,自動的創建出來:

>> docker ps -a --format "table {{.Image}}\t{{.Names}}"
2c4adeb21b4f            k8s_etcd_etcd-node1_kube-system_119fa7d9bc8bb1c755b9e8f2086d43e6_0
5811259ed0c9            k8s_kube-apiserver_kube-apiserver-node1_kube-system_f92ad4b0d2a33f76b05b6fcbe43e172a_0
07193a77f264            k8s_kube-controller-manager_kube-controller-manager-node1_kube-system_e3f0b6817c856cdb3e54f471dcbddf77_0
0f036524b7a2            k8s_kube-scheduler_kube-scheduler-node1_kube-system_0b5f93df7ddfe3fad5529c9f7f253717_0
k8s.gcr.io/pause:3.1    k8s_POD_kube-scheduler-node1_kube-system_0b5f93df7ddfe3fad5529c9f7f253717_0
k8s.gcr.io/pause:3.1    k8s_POD_kube-controller-manager-node1_kube-system_e3f0b6817c856cdb3e54f471dcbddf77_0
k8s.gcr.io/pause:3.1    k8s_POD_kube-apiserver-node1_kube-system_f92ad4b0d2a33f76b05b6fcbe43e172a_0
k8s.gcr.io/pause:3.1    k8s_POD_etcd-node1_kube-system_119fa7d9bc8bb1c755b9e8f2086d43e6_0
複製程式碼

其中每個服務起來,看上去都有個奇怪的容器:pause ? 後面介紹

就 Node 節點而言,主要包含:

  • kubelet : 節點代理,維護容器的生命週期(整個操作過程中,我們都不太會顯式的操作 kubelet)
  • kube_proxy: 轉發代理,負責服務的發現和負載均衡
  • docker: 容器

憑藉著 k8s 的開放能力,社群存在很多的外掛,完善 k8s 這個“作業系統”的能力,比如有聚焦在網路層面,有聚焦在儲存層面的。

我們的演示都聚焦在 k8s 的原生服務的能力基礎之上,不使用其他外掛

叢集部署

之前說過: k8s 的部署由於各種各樣的原因,對於初學者來說,整個的部署比較困難,比如對硬體的要求,對網路的要求(拉取基礎映象)。

如果你想嘗試試使用 k8s, 可以使用下面這個玩具:

Play With k8s

1. master 節點

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

執行結束,可以看到如何新增其他節點的命令

2. 網路初始化

kubectl apply -n kube-system -f  "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
複製程式碼

3. 獲取配置檔案

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
複製程式碼

甚至你可以將配置檔案拷貝至本地,在本地使用 kubectl 操作叢集資源

安裝部署依賴這幾個軟體:(不過這個玩具都內建了)

  • kubeadm: 一鍵式的安裝部署叢集的工具,社群推薦
  • kubelet: 維護容器生命週期
  • docker: 操作容器
  • kubectl: 操作叢集的命令列工具

後續主要圍繞:kubectl 工具的使用,操作叢集

安裝部署後其中有幾個目錄值得我們關注下:

// master 節點
>> ls /etc/kubernetes

admin.conf  controller-manager.conf  kubelet.conf  manifests  pki  scheduler.conf

>> ls manifests
etcd.yaml  kube-apiserver.yaml  kube-controller-manager.yaml  kube-scheduler.yaml

>> ls pki
apiserver-etcd-client.crt
apiserver-etcd-client.key
apiserver-kubelet-client.crt
apiserver-kubelet-client.key
apiserver.crt
apiserver.key
ca.crt
ca.key
etcd
front-proxy-ca.crt
front-proxy-ca.key
front-proxy-client.crt
front-proxy-client.key
sa.key
sa.pub
複製程式碼

主要是自動生成的配置檔案,包括元件:apiserver,etcd,controllermanager,scheduler 的配置檔案,和一些金鑰認證資訊。

事實上 k8s 安裝元件,是使用配置檔案的形式,一般選擇 yaml 的形式(雖然也支援 json 形式,但 yaml 的表達能力更佳,推薦使用)。

檢視節點資訊:

包含兩個節點,其中一個角色是 master 節點,另一個是普通 node 節點。

>> kubectl get nodes
NAME    STATUS   ROLES    AGE   VERSION
node1   Ready    <none>   15m   v1.14.9
node2   Ready    master   16m   v1.14.9
複製程式碼

如何部署其他元件

一般是通過執行 kubectl 命令:

>> kubectl apply -f 1namespace.yml
複製程式碼

作為開發者,在叢集部署完成之後,想要部署自己的服務,要點在於編寫配置檔案,那麼配置檔案一般都是什麼形式的?

apiVersion: v1
kind: Namespace
metadata:
  name: k8s-example
  labels:
    app: k8s-example
    name: k8s-example
    project: k8s-example

複製程式碼

各欄位表示如下:

  • apiVersion: 版本資訊,比如上文 v1
  • kind: 表示資源型別,比如上文 Namespace
  • metadata: 元資訊,一般來定義資源的名稱等

根據資源型別的不同,還給不同的資源定義了特殊欄位。

整體的 yaml 檔案包含四個部分:

apiVersion: 表示版本
kind: 表示資源
metadata: 表示元資訊
spec: 資源規範欄位
複製程式碼

那麼如何知道部署的 k8s 支援哪些資源和版本?這些東西又是哪裡來的?

// 檢視所有支援的資源
>> kubectl api-resources

// 檢視所有支援的版本
>> kubectl api-versions
複製程式碼

還記得 master 節點有個 api-server 嗎?是的,定義的 yaml 最終轉換成了相應的物件完成資源的增刪改查,這就是 api-server 主要的作用,也是 k8s 操作資源的入口。

>> kubectl get namespace
NAME           STATUS   AGE
default        Active   264d
k8s-example    Active   7s
kube-public    Active   264d
kube-system    Active   264d
production     Active   263d
複製程式碼

除自身剛剛建立的 namespace 之外,叢集搭建過程中自動的生成預設的 namespace。

namespace 是用來進行隔離的,是一個邏輯概念,真實的叢集環境中,可以根據使用場景建立不同的 namespace,用來進行隔離。

當然除命令列之外,也可以通過訪問 API 路由的形式操作資源。

>> kubectl proxy
Starting to serve on 127.0.0.1:8001

>> curl http://127.0.0.1:8001/api/v1/namespaces/k8s-example | jq .

{
  "kind": "Namespace","apiVersion": "v1","metadata": {
    "name": "k8s-example","selfLink": "/api/v1/namespaces/k8s-example","uid": "d7f3d7d3-1907-11ea-9746-fa163ed7ee23","resourceVersion": "57568285","creationTimestamp": "2019-12-07T15:40:06Z","labels": {
      "project": "k8s-example"
    },"annotations": {
      "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Namespace\",\"metadata\":{\"annotations\":{},\"labels\":{\"project\":\"k8s-example\"},\"name\":\"k8s-example\"}}\n"
    }
  },"spec": {
    "finalizers": [
      "kubernetes"
    ]
  },"status": {
    "phase": "Active"
  }
}

複製程式碼

這個訪問到的比我們之前定義的更詳細,k8s 會自動新增一些輔助資訊。

簡單的說:叢集搭建結束,開發者需要編寫對應服務的 yaml 配置檔案,這些配置檔案區分不同的物件有不同的規範要求。

具體不同資源的規範有哪些?讀者一方面可以熟悉 k8s 的相關概念瞭解相應的規範。也可以閱讀原始碼從源頭明確規範的欄位。

// Use of multiple namespaces is optional.
type Namespace struct {
	metav1.TypeMeta `json:",inline"`
	// Standard object's metadata.
	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
	// +optional
	metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

	// Spec defines the behavior of the Namespace.
	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
	// +optional
	Spec NamespaceSpec `json:"spec,2,name=spec"`

	// Status describes the current status of a Namespace.
	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status
	// +optional
	Status NamespaceStatus `json:"status,3,name=status"`
}
複製程式碼

其中各資源規範欄位即 Spec

kubernetes原始碼: github.com/kubernetes/…

那麼如何更新資源?

// 修改 yaml 檔案

>> kubectl apply -f 1namespace.yml

複製程式碼

那麼如何刪除資源?

>> kubectl delete -f 1namespace.yml
複製程式碼

kubectl 命令列還支援各種引數進行資源的操作,但我仍然建議:顯式的定義資源,再進行資源的操作。好處是:1. yaml 的表達能力,能知道具體的執行內容是什麼 2. 配置檔案可管理

相關概念

比如想部署個 nginx 服務。

我們已經知道需要編寫 yaml 檔案,再使用 kubectl 部署。

# 2nginxpod.yml

apiVersion: v1
kind: Pod
metadata:
  name: k8s-example-pod
  namespace: k8s-example
  labels:
    app: k8s-example-pod
spec:
  containers:
    - name: nginx
      image: nginx
      imagePullPolicy: IfNotPresent
      ports:
        - containerPort: 80

複製程式碼
  • 顯式的約定了 namespace: k8s-example,即會部署在 k8s-example 隔離環境下,不影響已有服務
  • spec 約定了部分容器的相關操作,比如映象,埠

檢視服務部署情況:

>> kubectl get pods --namespace k8s-example
NAME              READY   STATUS    RESTARTS   AGE
k8s-example-pod   1/1     Running   0          3m
複製程式碼

查詢指令中,顯式的指定了 namespace,否則查詢預設(default)的 namespace

再已有的知識中,加入 nginx 按照容器的方案部署,我們應該可以在本地訪問到其預設主頁。

那在 k8s 中如何訪問呢?

答案:搭配 service

# 3service.yml
apiVersion: v1
kind: Service
metadata:
  namespace: k8s-example
  name: k8s-example-nginx-service
  labels:
    app: k8s-example-nginx-service
spec:
  selector:
    app: k8s-example-pod
  ports:
    - port: 80
      targetPort: 80
  type: NodePort

複製程式碼
  • 顯式的指定 namespace: k8s-example
  • 指定了選擇器: app: k8s-example-pod
  • 約定了埠對映關係:80:80
  • 約定了型別:NodePort

迴歸頭去看 2nginxpod.yml 配置檔案中,定義了元資訊:app: k8s-example-pod,定義了容器埠:80。

所以 service 的角色是和 POD 進行繫結關係,POD 本身不提供對外訪問的能力,需要藉助 service 進行繫結,再對外服務。

>> kubectl get service --namespace k8s-example
NAME                        TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
k8s-example-nginx-service   NodePort   10.247.105.43   <none>        80:32511/TCP   1m
複製程式碼

master 或者 node 節點:訪問:

>> curl  http://10.247.105.43:80

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma,Verdana,Arial,sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page,the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
複製程式碼

這種方式其實是叢集分配給 cluserIP,通過 clusterIP 和對應的埠,可以訪問到服務。

那麼 NodePort 什麼意思?意思是可以使用 該節點的 IP 和對應的埠。

演示的這個節點繫結的對外IP: 119.3.198.221 curl http://119.3.198.221:32511/ 也可以訪問到服務

概念整理:

  • 什麼是 pod ?
  • pod 如何使其被外部訪問到?

POD

2nginxpod.yml 檔案中可以瞭解到 POD 資源的定義主要圍繞著 容器進行。沒錯,POD 表示容器組(包含一個或者多個容器),一般把相關的容器定義在一個 POD 內,這樣的組合關係,使其共同服務於叢集。

POD 是 k8s 排程的最基本的單位。

還記得 docker-compose.yml 是如何編寫的嗎?

version: 版本
services: 具體服務
volumes: 資料卷
networks: 網路
複製程式碼

一般我都會顯式的定義 volumes,networks 這樣定義的所有的服務都共享 volume 和 network。 POD 就是對標的這種思想。使其組合的形式對外服務,各容器之間共享網路,資料卷等。

還記得之前叢集部署有個奇怪的 pause 容器嗎?

這是個永遠暫停的容器,體積非常小,它的作用就是將 POD 內的容器關聯起來。

那麼如何記住 POD 內欄位的定義?記住 POD 是容器組,那麼關於容器的欄位基本都會出現。

比如:

  • container
  • image
  • volume
  • port
  • ...

service

開發者編寫的服務,如果需要提供對外訪問能力,需要 service 進行繫結,這種繫結關係是自動,具體的如何繫結主要根據的 selector 選擇器,選擇器內定義的欄位,在 某個 POD 內出現完全吻合的,那麼進行繫結。否則處於監聽狀態,等待符合的物件。

為什麼不直接讓 POD 可以直接訪問?訪問需要繫結 ip 和 埠吧?但是 POD 在叢集內是可以隨時刪除、升級、回滾的。鑑於此不直接提供訪問能力,轉而通過 service 進行繫結。

總結:

  • POD 是容器組,開發者在會製造映象的基礎上定義自己 POD 的配置檔案
  • Service 和 POD 進行繫結,包括訪問型別和埠對映關係

控制器

ReplicaSet

叢集提供高可用服務的一個重要手段是部署多個相同的服務,應用負載均衡的能力,使其對外服務。意思是,一般開發者都不會單獨的定義一個 POD,也很少單獨寫資源型別是 POD 的配置檔案。轉而是另一個概念:ReplicaSet (副本)

很明顯,副本的意思是多個, ReplicaSet 這種資源物件簡單的說是管理 POD 的,比如指定 2 副本,那麼管理的 POD , 會有2個一摸一樣的。

# 4replicaset.yml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  namespace: k8s-example
  labels:
    app: k8s-example-replicaset
    name: k8s-example-replicaset
  name: k8s-example-replicaset
spec:
  selector:
    matchLabels:
      app: k8s-example-nginx-pod
  replicas: 2
  template:
    metadata:
      labels:
        app: k8s-example-nginx-pod
    spec:
      containers:
        - name: k8s-example-nginx-pod
          image: nginx
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80

複製程式碼
  • 顯式的指定 namespace: k8s-example
  • replicat: 2 表示相應的服務有兩套
  • selector: 選擇器,將符合條件的 POD 納入麾下
  • template: 沒錯 POD 哪些 spec 欄位都成為了模版內的內容

同樣是 nginx 容器。定義的元資訊:app: k8s-example-nginx-pod 和之前的不一樣,意味著之前定義的 service 沒法和現在的服務進行繫結。那麼怎麼辦?

新增一個 service:

# 3service.yml
apiVersion: v1
kind: Service
metadata:
  namespace: k8s-example
  name: k8s-example-nginx-service
  labels:
    app: k8s-example-nginx-service
spec:
  selector:
    app: k8s-example-pod
  ports:
    - port: 80
      targetPort: 80
  type: NodePort
---
apiVersion: v1
kind: Service
metadata:
  namespace: k8s-example
  name: k8s-example-replicaset-service
  labels:
    app: k8s-example-replicaset-service
spec:
  selector:
    app: k8s-example-nginx-pod
  type: NodePort
  ports:
    - port: 80
      nodePort: 31234
      targetPort: 80

複製程式碼
  • 多個配置檔案可以寫在同一個檔案內,使用 --- 分割開
  • 顯示的指定了 nodeport: 31234

預設的會自動的分配:30000-32767 之間的任意一個埠

檢視 replicaset:

>> kubectl get replicaset --namespace k8s-example
NAME                     DESIRED   CURRENT   READY   AGE
k8s-example-replicaset   2         2         2       11m

# 也可以檢視 pod 

>> kubectl get pods --namespace k8s-example
NAME                           READY   STATUS    RESTARTS   AGE
k8s-example-pod                1/1     Running   0          1h
k8s-example-replicaset-b72k4   1/1     Running   0          9m
k8s-example-replicaset-jd7g6   1/1     Running   0          12m
複製程式碼
  • 預期 2個,當前2個,服務中2個,使用時長 11min。

  • 副本兩個,為區分開來,自動的在名稱後加上了隨機字元。

檢視 service:

>> kubectl get service --namespace k8s-example
NAME                             TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
k8s-example-nginx-service        NodePort   10.247.105.43   <none>        80:32511/TCP   1h
k8s-example-replicaset-service   NodePort   10.247.134.39   <none>        80:31234/TCP   14m
複製程式碼
>> curl http://119.3.198.221:31234/
>> curl http://49.4.54.222:31234/
複製程式碼

可以看到都對外提供服務,有人問 119.3.198.22149.4.54.222 是哪裡的 IP, 嚯,忘記說了,我這個叢集有三個節點:1個 master (不部署除預設之外的元件),2個 node,上面的兩個 IP 是這兩個節點對外的 ip。且均勻的分佈在節點上,每個節點一個(當然有規則可以使其只部署在一個節點上,甚至都不部署在我這個兩個節點上,直接失敗)。

現在明白 type: NodePort 的意思吧?

沒錯,ReplicaSet 的資源物件比 POD 上一級,叢集始終根據副本的個數在調控著,比如,你刪掉一個,立馬給你啟動一個,比如你新增一個,立馬給你刪除一個等。

Deployment

Deployment 稱作無狀態工作負載,適合在生產環境中使用。其具備 ReplicaSet 的所有能力,且支援事件和狀態檢視、回滾、版本記錄等能力。

看各種檔案,貌似都在弱化 ReplicaSet 的概念,轉而直接介紹 Deployment

同樣部署 nginx,資源型別是 Deployment 的配置檔案如何編寫?

# 5deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-example-deployment
  labels:
    app: k8s-example-deployment
    name: k8s-example-deployment
  namespace: k8s-example
spec:
  selector:
    matchLabels:
      app: k8s-example-nginx-deployment-pod
  replicas: 2
  template:
    metadata:
      labels:
        app: k8s-example-nginx-deployment-pod
    spec:
      containers:
        - name: k8s-example-nginx-deployment-pod
          imagePullPolicy: IfNotPresent
          image: nginx
          ports:
            - containerPort: 80


複製程式碼

除資源型別不同之外,幾乎和 ReplicaSet 配置一致。

檢視部署情況:

>> kubectl get deployment --namespace k8s-example
NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
k8s-example-deployment   2         2         2            2           4s
複製程式碼

比 ReplicaSet 多了個欄位 UP-TO-DATE.

>> kubectl describe deployment k8s-example-deployment --namespace k8s-example

Name:                   k8s-example-deployment
Namespace:              k8s-example
CreationTimestamp:      Sun,08 Dec 2019 10:06:32 +0800
Labels:                 app=k8s-example-deployment
                        name=k8s-example-deployment
Annotations:            deployment.kubernetes.io/revision: 1
                        kubectl.kubernetes.io/last-applied-configuration:
                          {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"labels":{"app":"k8s-example-deployment","name":"k8s-example-depl...
Selector:               app=k8s-example-nginx-deployment-pod
Replicas:               2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable,25% max surge
Pod Template:
  Labels:  app=k8s-example-nginx-deployment-pod
  Containers:
   k8s-example-nginx-deployment-pod:
    Image:        nginx
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   k8s-example-deployment-6fb599d4b8 (2/2 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  99s   deployment-controller  Scaled up replica set k8s-example-deployment-6fb599d4b8 to 2
複製程式碼

這種檢視詳情的命令主要用來排除問題,檢視的欄位是 Events, 如何有問題,會顯示出來,比如是拉取映象出問題,還是排程出問題等。

deployment 管理的 POD 如何提供對外服務能力,是的,搭配 service.

# 3service.yml
apiVersion: v1
kind: Service
metadata:
  name: k8s-example-deployment-service
  labels:
    app: k8s-example-deployment-service
  namespace: k8s-example
spec:
  selector:
    app: k8s-example-nginx-deployment-pod
  clusterIP: None
  ports:
    - port: 80
      targetPort: 80

複製程式碼

這時我們設定的 clusterIP: None,表示服務以 Headless Service 的形式對外訪問,例項間通過服務發現訪問。

檢視下 service:

>> kubectl get service --namespace k8s-example

NAME                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
k8s-example-deployment-service   ClusterIP   None             <none>        80/TCP         2m
k8s-example-nginx-service        NodePort    10.247.69.248    <none>        80:31519/TCP   2m
k8s-example-replicaset-service   NodePort    10.247.252.231   <none>        80:31234/TCP   2m
複製程式碼

可以看到 k8s-example-deployment-service 沒有顯式的提供內部叢集IP,那怎麼訪問到被管理的 POD?

k8s-example-deployment-service.k8s-example.svc.cluster.local ..svc.cluster.local

意思是如果你在 service 中定義了 clusterIP: None,那麼該服務的訪問的 DNS 地址為:<service-name>.<namespace>.svc.cluster.local,提供內部服務訪問。

具體服務是選擇何種形式的訪問,需要讀者根據自己實際的需求進行設定。

配置檔案:configmap,sercret

編寫程式的過程中,不可避免的會使用到配置檔案,自己的程式中一般有兩種方式:

  • 本地配置檔案
  • 環境變數

同樣為說明用法:以部署 mysql 為例。

  • 定義配置檔案
# 6setting.yml
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: k8s-example
  name: k8s-example-configmap
  labels:
    app: k8s-example-configmap
    name: k8s-example-configmap
data:
  mysql.Database: "root"
  mysql.User: "k8s-example"

---
apiVersion: v1
kind: Secret
metadata:
  name: k8s-example-secret
  labels:
    app: k8s-example-secret
  namespace: k8s-example
data:
  mysql.Port: "MzMwNg=="
  mysql.Password: "cm9vdA=="


複製程式碼

兩種資源:configMap 、secret 的配置欄位幾乎一致,區別在於:secret 中的值都需要 bas64轉 碼。

>> kubectl apply -f 6setting.yml
>> kubectl get configmap --namespace k8s-example
NAME                    DATA   AGE
k8s-example-configmap   2      11m

>> kubectl get secret --namespace k8s-example

NAME                  TYPE                                  DATA   AGE
default-secret        kubernetes.io/dockerconfigjson        1      15h
default-token-64tfz   kubernetes.io/service-account-token   3      15hs
k8s-example-secret    Opaque                                2      11m
複製程式碼

當然如何你想看具體的配置檔案的詳情,都可以使用 kubectl describe 進行檢視。

  • 配置 service
# 3service.yml
apiVersion: v1
kind: Service
metadata:
  name: k8s-example-mysql-service
  labels:
    app: k8s-example-mysql-service
    name: k8s-example-mysql-service
  namespace: k8s-example
spec:
  selector:
    app: k8s-example-mysql-pod-with-setting
  type: NodePort
  ports:
    - port: 3306
      targetPort: 3306
      nodePort: 32345

複製程式碼

檢視部署情況:

>> kubectl get service --namespace k8s-example

NAME                             TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
k8s-example-deployment-service   ClusterIP   None             <none>        80/TCP           5h
k8s-example-mysql-service        NodePort    10.247.1.4       <none>        3306:32345/TCP   17m
k8s-example-nginx-service        NodePort    10.247.69.248    <none>        80:31519/TCP     5h
k8s-example-replicaset-service   NodePort    10.247.252.231   <none>        80:31234/TCP     5h
複製程式碼
  • 配置 無狀態工作負載

deployment 中如何引用相應的配置?

# 7deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8s-example-deployment-with-setting
  labels:
    app: k8s-example-deployment-with-setting
    name: k8s-example-deployment-with-setting
  namespace: k8s-example
spec:
  selector:
    matchLabels:
      app: k8s-example-mysql-pod-with-setting
  replicas: 2
  template:
    metadata:
      labels:
        app: k8s-example-mysql-pod-with-setting
    spec:
      containers:
        - name: k8s-example-mysql-pod-with-setting
          image: mysql
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 3306
          resources:
            limits:
              cpu: "1"
              memory: "1024Mi"
            requests:
              cpu: "0.5"
              memory: "500Mi"
          envFrom:
            - configMapRef:
                name: k8s-example-configmap
            - secretRef:
                name: k8s-example-secret
          env:
            - name: MYSQL_DEPLOYMENT
              value: "k8s-example"
            - name: MYSQL_USER
              value: ${mysql.User}
            - name: MYSQL_DATABASE
              value: ${mysql.Database}
            - name: MYSQL_ROOT_PASSWORD
              value: ${mysql.Password}
            - name: METADATA_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: RESOURCE
              valueFrom:
                resourceFieldRef:
                  resource: limits.cpu
複製程式碼

其中:resource 欄位,可以用來表明服務申請和限制 記憶體和CPU,具體可以用數字來表示,也可以用百分比表示。這種限制有什麼用?其中一個比較厲害的功能是動態的調整 POD,比如你設定某種策略,在服務請求壓力比較大的時候,多開幾個副本,緩解上游請求壓力。在系統比較閒的時候,刪除幾個副本,釋放資源。

>> kubectl apply -f 7deployment.yml
>> kubectl get deployment --namespace k8s-example

NAME                                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
k8s-example-deployment                2         2         2            2           5h
k8s-example-deployment-with-setting   2         2         2            2           13m


>> kubectl get pods --namespace k8s-example

NAME                                                   READY   STATUS    RESTARTS   AGE
k8s-example-deployment-6fb599d4b8-bmrnz                1/1     Running   0          5h
k8s-example-deployment-6fb599d4b8-rcc2w                1/1     Running   0          5h
k8s-example-deployment-with-setting-56c7dd7ccc-9k98p   1/1     Running   0          6m
k8s-example-deployment-with-setting-56c7dd7ccc-htwvz   1/1     Running   0          6m
k8s-example-pod                                        1/1     Running   0          15h
k8s-example-replicaset-b72k4                           1/1     Running   0          15h
k8s-example-replicaset-jd7g6                           1/1     Running   0          15h
複製程式碼

進入容器內檢視環境變數和登入 mysql,檢視資料庫試試:

kubectl 對容器的操作幾乎和 docker 的命令一致,這意味著,如果你熟悉 docker,使用 kubectl 操作容器幾乎沒有學習成本

>> kubectl exec -it k8s-example-deployment-with-setting-56c7dd7ccc-htwvz --namespace k8s-example -- bash

>> env | grep METADATA
METADATA_NAME=k8s-example-deployment-with-setting-56c7dd7ccc-htwvz

>> env | grep MYSQL_DEPLOYMENT
MYSQL_DEPLOYMENT=k8s-example

>> mysql -u root -p

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| k8s-example        |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)

複製程式碼

除這些概念之外,k8s 還包括:

  • StatefulSet 有狀態工作負載,主要應用於工作負載存在先後,主從依賴等服務
  • job 單次執行負載
  • cronjob 定時任務負載
  • daemonSet 守護程式,一個節點上只存在一個,一般用於網路或者日誌處理等
  • persistenVolume/persistentVolumeClaim 檔案掛載相關

希望這些簡單的示例能夠讓你感受到 k8s 的基本使用方法。

一般我部署元件抽象出這麼幾個步驟:

  • 定義 namespace : 根據場景定義不同的 namepace 起到邏輯上隔離的作用
  • 定義 configmap/secret 等配置相關的資源
  • 定義 service 用於繫結 pod,提供對外服務
  • 定義 persistentVolume/persistentVolumeClaim 檔案掛載相關
  • 定義 deployment 用於主要的服務,即使副本只有一個,也不採用 POD 的形式
  • 定義 statefulSet 用於有狀態的負載
  • 定義 job 服務
  • 定義 cronjob 服務

事實上,規劃整套系統的元件的部署的時候,我會採用清單的形式,按照上面的步驟進行操作。儘管各資源配置檔案的定義先後關係不影響整體服務,但我依然建議讀者總結出一套自己的“清單”。

<完>

參考檔案: