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命令輸出後,進程終止,容器停止運行
- 執行docker run命令(
- 運行其它鏡像
- 如果想運行其它鏡像可以在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的副本數對應用進行水平伸縮