1. 程式人生 > >通過例項快速掌握k8s(Kubernetes)核心概念

通過例項快速掌握k8s(Kubernetes)核心概念

容器技術是微服務技術的核心技術之一,並隨著微服務的流行而迅速成為主流。Docker是容器技術的先驅和奠基者,它出現之後迅速佔領市場,幾乎成了容器的代名詞。但它在開始的時候並沒有很好地解決容器的叢集問題。Kubernetes抓住了這個機遇,以容器編排者(Container Orchestration)的身份出現,對容器叢集進行管理和排程,現在已經打敗了Docker成為了容器技術事實上的標準。當然K8s內部還是需要Docker的,但它的功能範圍被大大壓縮了,只是負責底層的容器引擎和映象(Docker Image)管理,成為了容器體系中不可缺少, 但沒有存在感的一部分。而絕大部分的對外介面都是由k8s來負責。

K8s核心物件:

相對於簡單易學的Docker來說,k8s系統龐雜而且概念眾多,同一個功能有很多不同方法來完成,讓你無所適從,學習起來要困難的多。對於普通碼農來講不需要建立完整的生產環境,只需要搭建一個本地開發環境,這時你只需要瞭解k8s的核心概念就夠了,這樣可以大大縮短學習時間。k8s的一切都是物件(Object),它的核心概念一共只有4個,Pod,部署(Deployment),服務(Service)和節點(Node)。另外再加上一個容器映象(Docker Image),這個是Docker引擎的核心。掌握了這五個核心概念,就對容器技術有了基本瞭解,打下了紮實的基礎。

Windows安裝環境:

Windows10的Windows 10企業版, 專業版, 和教育版是可以支援直接安裝K8s的,但電腦是要支援Hyper-V的, 詳見這裡。由於我的Windows是家庭版,只能先安裝虛擬機器(VirtualBox),再在虛擬機器上安裝k8s。我用的k8s是Minikube,是k8s的簡化版。另外還安裝了Vagrant(它是管理虛擬機器的一個軟體)作為介面來管理VirtualBox。

容器映象(Docker Image):

任何程式都在容器中執行,k8s支援多種容器,其中Docker是最流行的。容器映象(Docker Image)是一個以檔案形式存在的執行環境,它的裡面是分層的,每一層都在上一層的基礎上不斷疊加新的功能。容器映象是由Dockerfile建立的。Dockerfile是一個檔案,裡面包含一組已經定義好的Docker命令(與Linux命令比較相似)。當執行Dockerfile時,裡面的命令被依次執行,最後生成需要的容器映象。你再呼叫Docker命令(Docker run)執行容器映象來生成Docker容器,完成之後,應用程式就已經在容器裡部署好了。這種方式能夠保證每次得到的環境都是一樣的。容器比虛擬機器強的地方在於,它佔用系統資源更少,生成時間更短。建立一個容器的耗時一般是秒級的,而虛擬機器是分鐘級的。容器映象的建立效率取決於它的大小,一般來講容器映象越小,它的生成時間越短。
下面就是一個“nginx”的Dockerfile示例。

FROM alpine:3.2
EXPOSE 80 443
RUN apk add --update nginx && \
rm -rf /var/cache/apk/* && \
mkdir -p /tmp/nginx/client-body
COPY ./nginx.conf /etc/nginx/nginx.conf
VOLUME ["/etc/nginx/sites-enabled", "/etc/nginx/certs", "/etc/nginx/conf.d"]
CMD ["nginx", "-g", "daemon off;"]

任何Dockerfile的第一句總是“FROM 。。。”,就是要建立一個Linux的執行環境。一般有以下幾種:

  • FROM ubuntu:18.04: 按照Linux的具體版本來建立映象,這樣的Linux執行環境是比較完整的,映象的大小是百兆級別的。
  • FROM alpine:latest : alpine是一個 精簡了的Linux執行環境,它的大小是十兆級別的。
  • FROM scratch : scratch是最小Linux執行環境,建立非常快,但它的問題是你不能通過shell登入到容器內部,因此我一般不用它。

當用Vagrant管理虛擬機器時,可以先用Vagrant命令啟動虛擬機器,然後敲入vagrant ssh,進入虛擬機器,系統顯示:

PS E:\app2\kub> vagrant ssh
Last login: Sat Sep 28 06:56:11 2019 from 10.0.2.2

然後鍵入“docker run --name docker-nginx -p 8001:80 nginx”執行Nginx映象。"docker-nginx"是容器的名字,“--name”是名字的引數選項。“-p”表示埠對映,把虛擬機器的“8001”埠對映到容器的“80”埠(Nginx的預設埠)。“nginx”是映象的名字,如果本地沒有找到“Nginx”映象,系統會自動從Docker映象庫裡下載Nginx映象到本地,然後再執行,這個映象有比較完整的Linux系統,因此檔案比較大(100M),但也可以湊活著用。命令執行之後,顯示:

vagrant@ubuntu-xenial:~$ docker run --name docker-nginx -p 8001:80 nginx

這時Nginx已經執行,但還沒有任何請求,控制檯沒有輸出。如果名字為“docker-nginx”的容器以前已經被執行,那麼你需要刪除原來的,再執行上面命令。可以先敲入“docker ps -a”找到所有執行過的容器,再敲入“docker rm 1ec2e3d63537”進行刪除,其中“ 1ec2e3d63537”是容器ID.

切換到另一個虛擬機器視窗,敲入curl localhost:8001,顯示:

vagrant@ubuntu-xenial:/usr/bin$ curl localhost:8001
...
<h1>Welcome to nginx!</h1>
...

這時就出現了Nginx的首頁,表示Docker容器中的Nginx已經正常執行。

換回原容器顯示視窗,這時有了請求,控制檯輸出Nginx日誌。

vagrant@ubuntu-xenial:~$ docker run --name docker-nginx -p 8001:80 nginx
172.17.0.1 - - [28/Sep/2019:07:02:25 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"

這時已驗證Docker映象是好的,敲入“CTRL-C”退出。

Pod:

Pod是k8是的最基本概念,你可以把它看成是對容器(container)的一個封裝,用來管理容器。一個Pod裡可以管理有一個或多個容器,但一般是一個。Pod裡的所有容器都共享Pod的資源和網路。當一個Pod不能滿足使用者需求時,你可以把Pod作為複製的最小單元來複製出一個同樣的Pod來處理使用者請求。Pod支援多種容器,不過一般是用Docker。你可以用單獨的Pod配置檔案建立Pod, 也可以把Pod的配置資訊放在其他的物件(例如Deployment)的配置檔案裡面,並與其他物件一起建立,後者更為常見。每一個Pod都有一個唯一的IP地址,Pod一旦生成就可以通過IP地址進行訪問。但一般不這麼做,而是通過服務(Service)去間接地去訪問。下面就是Pod的配置檔案。它的解釋放在後面的Deployment裡面。

kind: Pod
apiVersion: v1
metadata:
  labels:
        app: nginx-app
spec:
  containers:
    - name: nginx-container
      image: nginx:latest
  restartPolicy: Never  

Pod模板(Pod Templates)

Pod模板是嵌入在其他K8s物件(Object)中的Pod的配置說明,例如Replication Controllers, Jobs, 和DaemonSets. 這時,Pod不是單獨建立的,而是由其它物件來建立,其中最常用的是Deployment。

部署(Deployment):

Deployment是比Pod更高一層的物件,它的主要作用是管理Pod叢集,它裡面可以有一個或多個Pod, 每一個Pod在功能上都是等同的。一般在Deployment裡配置多個Pod以實現負載均衡和容錯。在配置Deployment時,你需要指定Pod拷貝的個數,Deployment會自動管理它裡面的Pod。當某個Pod宕機時,Deployment能自動複製一個新的Pod並替換宕機的Pod,

下面就是Deployment的配置檔案(nginx-deployment.yaml)。在正方形灰框內(從template開始)的是嵌入在Deployment裡的Pod的設定,灰框上面的是部署(Deployment)的設定。當你執行這個配置檔案時,它會建立一個Deployment,同時也會建立嵌入在裡面的Pod。這就是為什麼我們一般不需要單獨的Pod的配置檔案,因為已經把它嵌入在了Deployment裡了。

鍵入“kubectl create -f nginx-deployment.yaml”來執行這個部署,顯示:

vagrant@ubuntu-xenial:~/dockerimages/kubernetes/nginx$ kubectl create -f nginx-deployment.yaml
deployment.apps/nginx-deployment created

這時部署已經成功,現在就可以訪問它了。每個Pod都有自己的k8s叢集內部IP地址,我們這時只能在K8s內部用IP地址進行訪問。鍵入下面命令檢視Pod地址,裡面有兩個“Nginx”Pod,因為Deployment裡面是兩個Pod的叢集。

vagrant@ubuntu-xenial:~/nginx$ kubectl get pods -o=custom-columns=NAME:.metadata.name,IP:.status.podIP
NAME                                IP
hello-minikube-856979d68c-74c65     172.17.0.3
nginx-deployment-77fff558d7-bhbbt   172.17.0.10
nginx-deployment-77fff558d7-v6zqw   172.17.0.9

開啟另一個視窗,連入虛擬機器,然後鍵入以下命令“kubectl exec -ti hello-minikube-856979d68c-74c65 -- /bin/sh”登入到k8s叢集內部,就能訪問Nginx了。這裡“hello-minikube-856979d68c-74c65”是"Minikube"Pod的名字。“172.17.0.10”是其中一個Pod的內部IP地址。

vagrant@ubuntu-xenial:~$ kubectl exec -ti hello-minikube-856979d68c-74c65 -- /bin/sh
# curl 172.17.0.10

服務(Service):

Service是最上層的k8s的物件,可以看成我們平常說的微服務。對服務來講最重要的就是服務註冊和發現。在k8s中,Service就是用來實現這個功能的。下面就是Service的配置檔案(nginx-service.yaml)。一般來說呼叫服務需要知道三個東西,IP地址,協議和埠,例如"http://10.0.2.1:80". 但我們不想用IP,而是用名字來定址,這就需要DNS。DNS在k8s叢集內部實現了基於服務名的定址。下面是Service的配置檔案。Service通過“selector”來與Pod進行繫結,這裡它把請求轉發給標籤“app”是“nginx-app”的Pod。“nodePort”給服務建立了一個外部可以訪問的埠,這樣在虛擬機器上就可以直接訪問服務,而不必登入到k8s叢集裡。

執行以下命令“kubectl create -f nginx-service.yaml”建立服務。

vagrant@ubuntu-xenial:~/$ kubectl create -f nginx-service.yaml
service/nginx-service created

服務建立完成之後,呼叫以下命令顯示當前的所有服務, 現在就有了“nginx-service”服務。“80”是服務的內部埠,“30163”是服務的外部埠。

vagrant@ubuntu-xenial:~/$ kubectl get services
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.96.0.1      <none>        443/TCP        35d
nginx-service   NodePort    10.109.7.249   <none>        80:30163/TCP   18s

因為已經通過“NodePort”對外開放了埠,現在不必登入到k8s內部,在虛擬機器上就可以訪問服務了. 你可以鍵入“localhost”

curl localhost:30163

Service 和Deployment的區別:

Deployment是用來管理叢集的,與Pod繫結,但你不能直接訪問Deployment。服務(Service)是為了方便用名字(而不是IP地址)訪問。但這也只是在叢集內部。你可以登入k8s叢集內部,然後鍵入下面命令訪問服務,但在虛擬機器上(k8s外面)就不行, 這時只能用IP地址。因為DNS只是在k8s內部才起作用。

# curl nginx-service

節點(Node):

Node剛開始接觸時容易和Pod搞混,但它相當於虛擬機器或物理機,而Pod相當於容器。所有的的Pod或容器都是部署在Node上面。Minikube只支援單Node,但你可以在它裡面部署多個Pod。下面是Node示意圖。

圖片來源

k8s核心概念之間的關係:

物件間關係:

下面是k8s的一個簡單結構圖:

圖片來源

圖中每個菱形是一個Node,中間的Node是Master Node,其餘三個是Worker Node。Master Node負責管理Worker Node。 Worker Node是真正幹活的Node。Master Node裡有各種控制器,Deployment就是由控制器來管理的,它在中間的管理Node裡,他裡面有兩個部署,A和B,分別對應服務A和服務B。“服務A”部署在最下面的Node裡,它裡面只有一個Pod。“服務B”部署在上面的兩個Node裡,左邊的Node只有一個Pod,右邊的Node有兩個Pod,這是一個有三個Pod的叢集。當一個部署裡有多個Pod時,一般是把它們部署在不同的Node上,這樣即使一個Node宕機,Deployment仍然可用。

物件繫結:

下圖是介紹物件之間如何繫結的關係圖。

圖片來源

每個物件都有多個標籤(Label),“app”就是一個用來標識Pod物件的標籤。圖裡有兩個Pod,它們的“app”標籤的值分別為“A”和“B”,其中Pod “B”是叢集,而Pod “A”不是。服務(Service)和部署(Deployment)都通過標籤選擇器(Label Selector)來繫結與之匹配的Pod。

附錄:

你最好是已經安裝了k8s和Docker環境,那就可以依次執行本文中的示例。如果你沒有環境,新建一個也不難。如果你不願意建立環境, k8s的官網有一個練習環境,可以直接用來執行命令,詳見這裡

索引:

  1. Kubernetes
  2. Docker
  3. Windows
  4. Minikube
  5. Kubernetes Basics