容器技術是微服務技術的核心技術之一,並隨着微服務的流行而迅速成爲主流。Docker是容器技術的先驅和奠定者,它出現以後迅速佔領市場,幾乎成了容器的代名詞。但它在開始的時候並無很好地解決容器的集羣問題。Kubernetes抓住了這個機遇,以容器編排者(Container Orchestration)的身份出現,對容器集羣進行管理和調度,如今已經戰勝了Docker成爲了容器技術事實上的標準。固然K8s內部仍是須要Docker的,但它的功能範圍被大大壓縮了,只是負責底層的容器引擎和鏡像(Docker Image)管理,成爲了容器體系中不可缺乏, 但沒有存在感的一部分。而絕大部分的對外接口都是由k8s來負責。node
K8s核心對象:
相對於簡單易學的Docker來講,k8s系統龐雜並且概念衆多,同一個功能有不少不一樣方法來完成,讓你無所適從,學習起來要困難的多。對於普通碼農來說不須要創建完整的生產環境,只須要搭建一個本地開發環境,這時你只須要了解k8s的核心概念就夠了,這樣能夠大大縮短學習時間。k8s的一切都是對象(Object),它的核心概念一共只有4個,Pod,部署(Deployment),服務(Service)和節點(Node)。另外再加上一個容器鏡像(Docker Image),這個是Docker引擎的核心。掌握了這五個核心概念,就對容器技術有了基本瞭解,打下了紮實的基礎。nginx
Windows安裝環境:
Windows10的Windows 10企業版, 專業版, 和教育版是能夠支持直接安裝K8s的,但電腦是要支持Hyper-V的, 詳見這裏。因爲個人Windows是家庭版,只能先安裝虛擬機(VirtualBox),再在虛擬機上安裝k8s。我用的k8s是Minikube,是k8s的簡化版。另外還安裝了Vagrant(它是管理虛擬機的一個軟件)做爲界面來管理VirtualBox。git
容器鏡像(Docker Image):
任何程序都在容器中運行,k8s支持多種容器,其中Docker是最流行的。容器鏡像(Docker Image)是一個以文件形式存在的運行環境,它的裏面是分層的,每一層都在上一層的基礎上不斷疊加新的功能。容器鏡像是由Dockerfile建立的。Dockerfile是一個文件,裏面包含一組已經定義好的Docker命令(與Linux命令比較類似)。當運行Dockerfile時,裏面的命令被依次執行,最後生成須要的容器鏡像。你再調用Docker命令(Docker run)運行容器鏡像來生成Docker容器,完成以後,應用程序就已經在容器裏部署好了。這種方式可以保證每次獲得的環境都是同樣的。容器比虛擬機強的地方在於,它佔用系統資源更少,生成時間更短。建立一個容器的耗時通常是秒級的,而虛擬機是分鐘級的。容器鏡像的建立效率取決於它的大小,通常來說容器鏡像越小,它的生成時間越短。 下面就是一個「nginx」的Dockerfile示例。github
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的運行環境。通常有如下幾種:docker
- FROM ubuntu:18.04: 按照Linux的具體版原本建立鏡像,這樣的Linux運行環境是比較完整的,鏡像的大小是百兆級別的。
- FROM alpine:latest : alpine是一個 精簡了的Linux運行環境,它的大小是十兆級別的。
- FROM scratch : scratch是最小Linux運行環境,建立很是快,但它的問題是你不能經過shell登陸到容器內部,所以我通常不用它。
當用Vagrant管理虛擬機時,能夠先用Vagrant命令啓動虛擬機,而後敲入vagrant ssh,進入虛擬機,系統顯示:shell
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),但也能夠湊活着用。命令運行以後,顯示:ubuntu
vagrant@ubuntu-xenial:~$ docker run --name docker-nginx -p 8001:80 nginx
這時Nginx已經運行,但尚未任何請求,控制檯沒有輸出。若是名字爲「docker-nginx」的容器之前已經被運行,那麼你須要刪除原來的,再運行上面命令。能夠先敲入「docker ps -a」找到全部運行過的容器,再敲入「docker rm 1ec2e3d63537」進行刪除,其中「 1ec2e3d63537」是容器ID.windows
切換到另外一個虛擬機窗口,敲入curl localhost:8001,顯示:api
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的官網有一個練習環境,能夠直接用來運行命令,詳見這裏