2. 使用Kubernetes和Docker

  • 2019 年 10 月 10 日
  • 筆記

1. 內容

  • 使用Docker創建、運行及共享容器鏡像
  • 在本地部署單節點的Kubernetes集群
  • 配置和使用命令行客戶端——kubectl
  • 在Kubernetes上部署應用並進行水平伸縮

2. 創建、運行及共享容器鏡像

介紹

深入學習前,先看看如何創建一個簡單的應用、打包成容器鏡像、在遠程集群或本地集群運行

步驟簡介

  • 安裝Docker並運行第一個「Hello World」容器
  • 創建一個簡單的php應用並部署在Kubernetes
  • 把應用打包成可以獨立運行的容器鏡像
  • 基於鏡像運行容器
  • 把鏡像推送到Docker Hub,這樣任何人在任何地方都可以使用

安裝並運行Docker

  • 安裝:https://docs.docker.com/docker-for-mac/install/
  • 運行Hello World容器
    • busybox是一個集成最常用linux命令的linux系統容器鏡像
    • 使用docker run命令指定鏡像名字、執行命令(可選),如圖所示
    • 通過一個命令就運行了一個完整的「應用」,而不用做其它的事情
    • 重要的是應用是在容器內部執行的,完全獨立於基於其他主機上運行的進程
  • 背後原理
    • 執行docker run命令(docker run busybox echo "Hello World")後,docker會檢查busybox:latest鏡像是否已經存在本機,如果沒有會從http://docker.io的鏡像中心拉取
    • 鏡像下載到本機後,docker基於這個鏡像創建一個容器並在容器中運行命令
    • echo命令輸出後,進程終止,容器停止運行
  • 運行其它鏡像
    • 如果想運行其它鏡像可以在hub.docker.com網站搜索
    • 然後像這樣運行鏡像docker run <image>
  • 容器鏡像的版本管理
    • docker支援同一鏡像的多個版本,每個版本必須有唯一的tag名,當沒有顯式指定tag時,docker默認指定為latest
    • 運行別的版本鏡像docker run <image>:<tag>

創建一個簡單的php應用

  • 這個應用程式輸出當前時間
  • 應用運行在容器中,看到的是自己的主機名而不是宿主機名,即使它像其它進程一樣運行在宿主機上
  • 這在後面非常有用,當應用部署在Kubernetes上進行伸縮時(複製應用到多個節點),它的請求切換到了應用的不同實例上

為鏡像創建Dockerfile

  • 把應用打包成鏡像,首先需要創建名為Dockerfile的文件,它包含了一系列構建鏡像時執行的指令
  • Dockerfile和index.php在同一目錄
  • Dockerfile內容如下
  • FROM定義了基礎鏡像,使用了7-zts-alpine3.9版本
  • ADD是把index.php從本地文件夾添加到鏡像的/目錄
  • EXPOSE聲明暴露的埠號
  • WORKDIR指定默認工作目錄
  • CMD默認執行的命令,意思是用php啟動一個web伺服器,埠為8080

構建容器鏡像

  • 運行docker命令(docker build -t php:local .)來構建鏡像
  • 構建過程不是用Docker客戶端完成,而將整個目錄上傳到Docker守護進程(Docker客戶端和守護進程可以不在一台機器上)
  • 構建過程中,Docker首次會從公開鏡像倉庫(Docker Hub)拉取基礎鏡像(php:7-zts-alpine3.9)
  • 最後一個.是告訴Docker是基於當前目錄,構建名為php、標籤為local的鏡像;Docker會在目錄中尋找Dockerfile,然後基於其中指令構建鏡像
使用docker images命令查看生成的鏡像  $ docker images  php    local                b26e7de6427c        9 hours ago         64.7MB  php    7-zts-alpine3.9 0adbdb1b2250        2 weeks ago         64.7MB
  • 構建鏡像過程
  • 鏡像分層
    • 鏡像不是一個大的二進位塊,而由多層組成的,在busybox例子中,每一層有一行Pull complete,不同鏡像可能會共享分層,這會讓存儲和傳輸變得更高效
    • Dockerfile每一條單獨的指令都會創建一個新層

運行容器鏡像

  • 運行應用容器
    • 運行命令docker run --name php-container -p 8000:8000 -d php:local
    • 這條命令告知Docker是基於php:local鏡像創建一個名為php-container的容器,本機8000埠映射到容器的8000的埠,-d表示後台運行
  • 訪問應用
    • 運行命令$ curl http://localhost:8000
    • 通過http://localhost:8000訪問應用,輸出當前運行時間。應用是運行在容器中,與其他應用隔離
  • 列出所有運行中的容器
    • 運行命令docker ps
命令結果  CONTAINER ID   IMAGE    COMMAND   CREATED   STATUS   PORTS       NAMES  2b3eb8cbab22   php:local "docker-php-entrypoi…"   2 minutes ago   Up 2 minutes    0.0.0.0:8000->8000/tcp   php-container    
  • 有一個容器正在運行,顯示容器的id、名稱、運行的鏡像、命令等資訊
  • 獲取容器資訊
    • 運行命令docker inspect php-container會列印出包含容器底層資訊的長json

探索運行容器的內部

  • 在已有的容器內部運行shell
    • 在php-container容器執行ls -al /,命令和主容器進程有相同的命名空間
    • -i:確保標準輸入流保持開放,需要在shell中輸入命令
    • -t:分配一個偽終端(TTY)
    • 運行命令docker exec -it php-container ls -al /
輸出結果  $ docker exec -it php-container ls -al /  total 68  drwxr-xr-x    1 root     root          4096 May 28 23:13 .  drwxr-xr-x    1 root     root          4096 May 28 23:13 ..  -rwxr-xr-x    1 root     root             0 May 28 23:13 .dockerenv  drwxr-xr-x    1 root     root          4096 May 11 03:27 bin  drwxr-xr-x    5 root     root           340 May 28 23:13 dev  drwxr-xr-x    1 root     root          4096 May 28 23:13 etc  drwxr-xr-x    1 root     root          4096 May 11 03:04 home  -rw-r--r--    1 root     root            33 May 28 23:12 index.php
  • 參數介紹
  • 從內部探索容器
    • 進入容器命令docker exec -it php-container sh
查看容器里運行的進程  # ps -ef  PID   USER     TIME  COMMAND    1   root     0:00  /usr/local/bin/php -S 0.0.0.0:8000    7   root     0:00  sh   12   root     0:00  ps -ef
  • 容器的文件系統也是獨立的
執行ls -al /命令查看  # ls -al /  total 68  drwxr-xr-x    1 root     root          4096 May 29 03:58 .  drwxr-xr-x    1 root     root          4096 May 29 03:58 ..  -rwxr-xr-x    1 root     root             0 May 29 03:58 .dockerenv  drwxr-xr-x    1 root     root          4096 May 11 03:27 bin  drwxr-xr-x    5 root     root           340 May 29 03:58 dev  drwxr-xr-x    1 root     root          4096 May 29 03:58 etc  drwxr-xr-x    1 root     root          4096 May 11 03:04 home  -rw-r--r--    1 root     root            33 May 28 23:12 index.php  drwxr-xr-x    1 root     root          4096 May 11 03:27 lib  drwxr-xr-x    5 root     root          4096 May  9 20:49 media  drwxr-xr-x    2 root     root          4096 May  9 20:49 mnt  drwxr-xr-x    2 root     root          4096 May  9 20:49 opt  dr-xr-xr-x  262 root     root             0 May 29 03:58 proc  drwx------    1 root     root          4096 May 29 03:59 root  drwxr-xr-x    2 root     root          4096 May  9 20:49 run  drwxr-xr-x    2 root     root          4096 May  9 20:49 sbin  drwxr-xr-x    2 root     root          4096 May  9 20:49 srv  dr-xr-xr-x   13 root     root             0 May 29 03:58 sys  drwxrwxrwt    1 root     root          4096 May 11 03:27 tmp  drwxr-xr-x    1 root     root          4096 May 11 03:27 usr  drwxr-xr-x    1 root     root          4096 May 11 03:04 var
  • 容器擁有完整的文件系統、進程、用戶、主機名和網路介面

停止和刪除容器

  • 停止命令
    • 執行docker stop php-container
使用docker ps -a查看容器,發現狀態已經是Exited  $ docker ps -a  CONTAINER ID   IMAGE    COMMAND    CREATED  STATUS   PORTS     NAMES  c113cc387a6d   php:local "docker-php-entrypoi…"   8 minutes ago   Exited(137) 56 seconds ago   php-container
  • 刪除容器命令 docker rm php-container

向鏡像倉庫推送鏡像

  • 介紹
    • 目前構建的鏡像只能本機使用,為了在任何機器上都能使用,可以把鏡像推送到外部的鏡像倉庫,如Docker Hub(http://hub.docker.com)
  • 使用附加標籤標註鏡像
    • 在Docker Hub註冊個帳號,把鏡像重命名標籤
    • 重命名鏡像標籤docker tag php:local yeedom/php:v1
查看鏡像docker images  $ docker images  REPOSITORY     TAG       IMAGE ID            CREATED             SIZE  php            local     9ed8b05c3334        13 hours ago        64.7MB  yeedom/php     v1        9ed8b05c3334        13 hours ago        64.7MB
  • 兩個鏡像指向同一個鏡像id
  • 向Docker Hub推送鏡像
推送前先登錄  $ docker login  Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.  Username: yeedom  Password:  Login Succeeded
推送鏡像  國外網路可能不穩定,失敗了可以多試幾次  $ docker push yeedom/php:v1  The push refers to repository [docker.io/yeedom/php]  d281f172ef70: Pushed  8a1be1ed4590: Mounted from library/php  b39144a13ab7: Mounted from library/php  060efcf419e8: Mounted from library/php  f38edf0edb30: Mounted from library/php  0b0394abec4c: Mounted from library/php  21f626200b4c: Mounted from library/php  414e112bbb2c: Mounted from library/php  3575e617b5f4: Mounted from library/php  f1b5933fe4b5: Mounted from library/php  v1: digest: sha256:448c8eeca87506bbc6d41190073acdcab094aa0b0bd3c2551c4ced947f422ebb size: 2409
  • 在Docker Hub上查看鏡像
  • 在不同機器上運行鏡像
運行命令$ docker run -p 8000:8000 -d yeedom/php:v1  $ docker run -p 8000:8000 -d yeedom/php:v1  Unable to find image 'yeedom/php:v1' locally  v1: Pulling from yeedom/php  Digest: sha256:448c8eeca87506bbc6d41190073acdcab094aa0b0bd3c2551c4ced947f422ebb  Status: Downloaded newer image for yeedom/php:v1  a0d96dc65d6795ba5542754bd3a07e424da672960f545f5cf36e14fa02cac46f
  • 這非常簡單,最棒的是應用每次都運行在完全一致的環境中
刪除鏡像docker rmi yeedom/php:v1  $ docker rmi yeedom/php:v1  Untagged: yeedom/php:v1  Untagged: yeedom/php@sha256:448c8eeca87506bbc6d41190073acdcab094aa0b0bd3c2551c4ced947f422ebb
  • 刪除容器docker ps -aq | xargs docker rm -f
  • 推送完後,鏡像就可以給任何人用了,只需要在機器上運行一條命令就能啟動一個新容器了
  • 模擬一台新電腦
  • 在裝有docker機器上運行這條命令啟動新容器

3. 配置Kubernetes集群

用Minikube運行一個本地單節點Kubernetes集群

  • 介紹
    • 要在Kubernetes運行應用,首先要設置集群
    • 使用Minikube是運行Kubernetes集群最簡單、最快捷的方法,它是構建單節點集群的工具,對於測試Kubernetes和本地開發應用都非常有用
    • Minikube在VM中通過VirtualBox、KVM或hyperkit來運行Kubernetes,所以啟動Kubernetes集群之前,還需要安裝VM
  • 安裝文檔:因為minikube中國安裝有點特殊,請參考文檔
  • 使用Minikube啟動一個Kubernetes集群
    • 運行命令minikube start
  • 獲取集群概覽
    • 每個節點運行著Docker、kubelet、kube-proxy
    • Kubectl:向運行在主節點上的Kubernetes API伺服器發出REST請求以與集群交互
    • 交互圖
  • 列出集群節點
命令kubectl get nodes  $ kubectl get nodes  NAME       STATUS    ROLES     AGE       VERSION  minikube   Ready     <none>    43m       v1.14.2
  • 查看對象更多資訊查看對象更多資訊查看對象更多資訊查看對象更多資訊
  • 命令$ kubectl describe node minikube
  • 內容比較多,如輸出節點的狀態、CPU、記憶體數據、系統資訊、運行容器的節點
    • 如果不指定節點名稱則會列印所有節點資訊

為kubectl配置別名和命令行補齊

  • 配置別名
  • 使命令行補齊
    • 使用kube-shell,帶命令補齊和高亮

4. 在Kubernetes上運行第一個應用

介紹

正常來說,部署一個Kubernetes程式需要包含部署的所有組件描述的配置文件,因為第一次使用,所以用最簡單的方法運行Kubernetes程式

部署php應用

運行命令  kube-shell> kubectl run php-container --image=yeedom/php:v1 --port=8000 --generator=run/v1  replicationcontroller "php-container" created
  • 部署應用最簡單的方式是使用kubectl run命令,它會創建所有必要的組件而無需json或yaml文件,這樣不需要深入了解每個組件對象的結構
  • 參數解釋
    • —image:容器鏡像
    • —port:埠
    • —generator:創建一個ReplicationController
  • 介紹pod
    • 一個pod是一組緊密相關的容器,運行在同一個工作節點和linux命名空間中
    • 每個pod就像一個獨立的邏輯機器,有自己的ip、主機名、進程等,運行一個獨立的應用程式
    • 一個pod的所有容器運行在同一個邏輯機器上,也可以出現在不同的節點上
    • 和Docker啟動容器不同,Kubernetes不直接處理單個容器,它使用多個共存容器的理念,這組容器叫作pod
    • pod
    • 關係圖
  • 列出pod
命令  $ kubectl describe pod php-container-mdkmq  Name:               php-container-mdkmq  Namespace:          default  Priority:           0  PriorityClassName:  <none>  Node:               minikube/10.0.2.15  Start Time:         Thu, 30 May 2019 07:58:10 +0800  Labels:             run=php-container  Annotations:        <none>  Status:             Running  …………………………
  • 介紹:不能列出單個容器,因為它們不是獨立的Kubernetes對象,但可列出pod

命令

$ kubectl get pods  NAME                  READY     STATUS              RESTARTS   AGE  php-container-mdkmq   0/1       ContainerCreating   0          7
  • 列出所有pod
  • 查看pod詳情
  • 幕後發生的事情
    • ReplicationController對象創建一個新的pod
    • 調度器將其調度到一個工作節點上
    • kubectl看到pod被調度到節點上,就告知Docker拉取鏡像
    • 創建容器
    • 構建鏡像並推送到Docker Hub
    • 運行kubectl命令時,向Kubernetes API伺服器發送http請求在集群中創建一個新的ReplicationController對象

訪問web應用

  • 介紹:每個pod在集群內有自己的ip,要讓pod從外部訪問,需要通過服務對象公開它,要創建一個特殊的LoadBalance類型的服務
  • 創建一個服務對象
    • 命令
$ kubectl expose rc php-container --type=LoadBalancer --name php-service  service "php-service" exposed
  • rc是ReplicationController縮寫
  • 列出服務
    • 命令kubectl get service
$ kubectl get service  NAME         TYPE          CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE  kubernetes   ClusterIP     10.96.0.1      <none>        443/TCP         37d  php-service  LoadBalancer  10.105.206.79  localhost     8000:32040/TCP  6s
  • 使用外部ip訪問服務
    • 命令
$ curl http://localhost:8000
  • 查看服務
    • 命令
$ kubectl get service php-service  NAME          TYPE          CLUSTER-IP     EXTERNAL-IP  PORT(S)         AGE  php-service   LoadBalancer  10.105.206.79  localhost    8000:32040/TCP  3m

系統的邏輯部分

  • ReplicationController、pod和服務如何組合在一起
    • 如之前所說,Kubernetes沒有直接創建和使用容器,它的基本構件是pod
    • 但你也沒有直接創建pod,而是通過kubectl run命令創建了ReplicationController,它用於創建pod實例
    • 為了能使pod能從集群外部訪問,需要創建一個服務對外暴露
  • pod和它的容器
    • 在系統中最重要最基本的組件是pod
    • 它只包含一個容器,但是通常一個pod可包含任意數量的容器
    • pod有自己的私有ip和主機名
  • ReplicationController的角色
    • 通常,rc用於創建pod多個副本並讓它保持運行
    • 如果pod有任何原因消失或停止,那麼rc將拉起或重新創建新的pod
  • 為什麼需要服務
    • 解決不斷變化的pod ip地址,pod可能因為故障而停止,這時會有新pod替換
    • 固定的ip和埠對外提供服務
    • 服務表示一組或多組提供相同服務的pod,到達服務ip和埠的請求會轉發到該服務的一個容器ip和埠

水平伸縮應用

  • 增加期望的副本數
    • Kubernetes的一個主要好處是可簡單地處理部署,我們把運行實例數量增加到三個
    • 查看rc
kubectl get rc  NAME            DESIRED   CURRENT   READY     AGE  php-container   1         1         1         52m
  • 查看pod
kubectl get pod  NAME                  READY     STATUS    RESTARTS   AGE  php-container-xlhzh   1/1       Running   0          53m
  • 增加副本數
kubectl scale rc php-container --replicas=3  replicationcontroller "php-container" scaled
  • 查看擴容結果 已經由1個變成3個pod了,可以看到給應用擴容非常簡單
kubectl get pods  NAME                  READY     STATUS    RESTARTS   AGE  php-container-f879k   1/1       Running   0          5s  php-container-n2pwj   1/1       Running   0          5s  php-container-xlhzh   1/1       Running   0          54m
  • Kubernetes原則:不是告訴Kubernetes要執行什麼,而是聲明系統的期望狀態,Kubernetes世界都是這樣的
  • 當切換到服務時請求切換到所有pod上
    • 多次請求服務,會落到不同的pod上
$ curl http://localhost:8000  php-container-n2pwj  2019-05-30 13:23:38    $ curl http://localhost:8000  php-container-xlhzh  2019-05-30 13:23:50    $ curl http://localhost:8000  php-container-f879k  2019-05-30 13:23:54
  • 服務會做負載均衡擋在多個pod前面
  • 服務請求示例圖

查看應用運行在哪個節點上

  • 介紹:在Kubernetes世界中,pod運行在哪個節點上不重要,只要它被調度到一個可以提供pod正常運行所需的cpu和記憶體的節點就可以了
  • 列出pod時顯示pod IP和pod的節點:會顯示pod的ip和運行的節點
    kubectl get pod -o wide      NAME                  READY     STATUS    RESTARTS   AGE       IP           NODE      php-container-f879k   1/1       Running   0          50m       10.1.1.102   docker-for-desktop      php-container-n2pwj   1/1       Running   0          50m       10.1.1.101   docker-for-desktop      php-container-xlhzh   1/1       Running   0          1h        10.1.1.100   docker-for-desktop
  • 使用kubectl describe查看pod的其它細節
kubectl describe pod php-container-f879k  Name:           php-container-f879k  Namespace:      default  Node:           docker-for-desktop/192.168.65.3  Start Time:     Thu, 30 May 2019 21:10:32 +0800  Labels:         run=php-container  Annotations:    <none>  Status:         Running  IP:             10.1.1.102  Controlled By:  ReplicationController/php-container

5. 小結

  • 拉取、運行鏡像
  • 把應用打包到容器鏡像,並且推送到公開鏡像倉庫讓大家可以使用
  • 進入運行中的容器並檢查運行環境
  • 為kubectl命令行工具設置別名和tab補全
  • 在Kubernetes集群中列出查看節點、pod、服務和ReplicationController
  • 在Kubernetes中運行容器並可以在集群外部訪問
  • 了解pod、ReplicationController和服務是關聯的基礎場景
  • 通過改變ReplicationController的副本數對應用進行水平伸縮