- Kubernetes權威指南:從Docker到Kubernetes實踐全接觸(第4版)
- 龔正等編著
- 2756字
- 2019-09-23 11:04:27
1.3 從一個簡單的例子開始
考慮到Kubernetes提供的PHP+Redis留言板的Hello World例子對于絕大多數剛接觸Kubernetes的人來說比較復雜,難以順利上手和實踐,所以在此將其替換成一個簡單得多的Java Web應用例子,可以讓新手快速上手和實踐。
此Java Web應用的結構比較簡單,是一個運行在Tomcat里的Web App,如圖1.1所示,JSP頁面通過JDBC直接訪問MySQL數據庫并展示數據。出于演示和簡化的目的,只要程序正確連接到了數據庫,就會自動完成對應的Table的創建與初始化數據的準備工作。所以,當我們通過瀏覽器訪問此應用時,就會顯示一個表格的頁面,數據則來自數據庫。

圖1.1 Java Web應用的結構
此應用需要啟動兩個容器:Web App容器和MySQL容器,并且Web App容器需要訪問MySQL容器。在Docker時代,假設我們在一個宿主機上啟動了這兩個容器,就需要把MySQL容器的IP地址通過環境變量注入Web App容器里;同時,需要將Web App容器的8080端口映射到宿主機的8080端口,以便在外部訪問。在本章的這個例子里,我們介紹在Kubernetes時代是如何達到這個目標的。
1.3.1 環境準備
首先,安裝Kubernetes和下載相關鏡像,本書建議采用VirtualBox或者VMware Workstation在本機虛擬一個64位的CentOS 7虛擬機作為學習環境。虛擬機采用NAT的網絡模式以便連接外網,然后使用kubeadm快速安裝一個Kubernetes集群(安裝步驟詳見2.2節的說明)。之后就可以在這個Kubernetes集群中進行練習了。
注:本書示例中的Docker鏡像下載地址為https://hub.docker.com/u/kubeguide/。
1.3.2 啟動MySQL服務
首先,為MySQL服務創建一個RC定義文件mysql-rc.yaml,下面給出了該文件的完整內容和解釋:
apiVersion: v1 kind: ReplicationController # 副本控制器RC metadata: name: mysql # RC的名稱,全局唯一 spec: replicas: 1 # Pod副本的期待數量 selector: app: mysql # 符合目標的Pod擁有此標簽 template: # 根據此模板創建Pod的副本(實例) metadata: labels: app: mysql # Pod副本擁有的標簽,對應RC的Selector spec: containers: # Pod內容器的定義部分 - name: mysql # 容器的名稱 image: mysql # 容器對應的Docker Image ports: - containerPort: 3306 # 容器應用監聽的端口號 env: # 注入容器內的環境變量 - name: MYSQL_ROOT_PASSWORD value: "123456"
以上YAML定義文件中的kind屬性用來表明此資源對象的類型,比如這里的值為ReplicationController,表示這是一個RC;在spec一節中是RC的相關屬性定義,比如spec.selector是RC的Pod標簽選擇器,即監控和管理擁有這些標簽的Pod實例,確保在當前集群中始終有且僅有replicas個Pod實例在運行,這里設置replicas=1,表示只能運行一個MySQL Pod實例。當在集群中運行的Pod數量少于replicas時,RC會根據在spec.template一節中定義的Pod模板來生成一個新的Pod實例,spec.template.metadata. labels指定了該Pod的標簽,需要特別注意的是:這里的labels必須匹配之前的spec.selector,否則此RC每創建一個無法匹配Label的Pod,就會不停地嘗試創建新的Pod,陷入惡性循環中。
在創建好mysql-rc.yaml文件后,為了將它發布到Kubernetes集群中,我們在Master上執行命令:
# kubectl create -f mysql-rc.yaml replicationcontroller "mysql" created
接下來,用kubectl命令查看剛剛創建的RC:
# kubectl get rc NAME DESIRED CURRENT AGE mysql 1 1 1m
查看Pod的創建情況時,可以運行下面的命令:
# kubectl get pods NAME READY STATUS RESTARTS AGE mysql-c95jc 1/1 Running 0 2m
我們看到一個名為mysql-xxxxx的Pod實例,這是Kubernetes根據mysql這個RC的定義自動創建的Pod。由于Pod的調度和創建需要花費一定的時間,比如需要一定的時間來確定調度到哪個節點上,以及下載Pod里的容器鏡像需要一段時間,所以我們一開始看到Pod的狀態顯示為Pending。在Pod成功創建完成以后,狀態最終會被更新為Running。
我們通過docker ps指令查看正在運行的容器,發現提供MySQL服務的Pod容器已經創建并正常運行了,此外會發現MySQL Pod對應的容器還多創建了一個來自谷歌的pause容器,這就是Pod的“根容器”,詳見1.4.3節的說明。
# docker ps | grep mysql 72ca992535b4 mysql "docker-entrypoint.sh" 12 minutes ago Up 12 minutes k8s_mysql.86dc506e_mysql-c95jc_default_511d6705-5051-11e6-a9d8-000c29ed42c1_9f89 d0b4 76c1790aad27 gcr.io/google_containers/pause-amd64:3.0 "/pause" 12 minutes ago Up 12 minutes k8s_POD.16b20365_mysql-c95jc_default_511d6705-5051-11e6-a9d8-000c29ed42c1_28520a ba
最后,創建一個與之關聯的Kubernetes Service——MySQL的定義文件(文件名為mysql-svc.yaml),完整的內容和解釋如下:
apiVersion: v1 kind: Service # 表明是Kubernetes Service metadata: name: mysql # Service的全局唯一名稱 spec: ports: - port: 3306 # Service提供服務的端口號 selector: # Service對應的Pod擁有這里定義的標簽 app: mysql
其中,metadata.name是Service的服務名(ServiceName);port屬性則定義了Service的虛端口;spec.selector確定了哪些Pod副本(實例)對應本服務。類似地,我們通過kubectl create命令創建Service對象。
運行kubectl命令,創建Service:
# kubectl create -f mysql-svc.yaml service "mysql" created
運行kubectl命令查看剛剛創建的Service:
# kubectl get svc NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE mysql 169.169.253.143 <none> 3306/TCP 48s
可以發現,MySQL服務被分配了一個值為169.169.253.143的Cluster IP地址。隨后,Kubernetes集群中其他新創建的Pod就可以通過Service的Cluster IP+端口號3306來連接和訪問它了。
通常,Cluster IP是在Service創建后由Kubernetes系統自動分配的,其他Pod無法預先知道某個Service的Cluster IP地址,因此需要一個服務發現機制來找到這個服務。為此,最初時,Kubernetes巧妙地使用了Linux環境變量(Environment Variable)來解決這個問題,后面會詳細說明其機制。現在只需知道,根據Service的唯一名稱,容器可以從環境變量中獲取Service對應的Cluster IP地址和端口,從而發起TCP/IP連接請求。
1.3.3 啟動Tomcat應用
上面定義和啟動了MySQL服務,接下來采用同樣的步驟完成Tomcat應用的啟動過程。首先,創建對應的RC文件myweb-rc.yaml,內容如下:
apiVersion: v1 kind: ReplicationController metadata: name: myweb spec: replicas: 2 selector: app: myweb template: metadata: labels: app: myweb spec: containers: - name: myweb image: kubeguide/tomcat-app:v1 ports: - containerPort: 8080
注意:在Tomcat容器內,應用將使用環境變量MYSQL_SERVICE_HOST的值連接MySQL服務。更安全可靠的用法是使用服務的名稱mysql,詳見本章Service的概念和第4章的說明。運行下面的命令,完成RC的創建和驗證工作:
#kubectl create -f myweb-rc.yaml replicationcontroller "myweb" created # kubectl get pods NAME READY STATUS RESTARTS AGE mysql-c95jc 1/1 Running 0 2h myweb-g9pmm 1/1 Running 0 3s
最后,創建對應的Service。以下是完整的YAML定義文件(myweb-svc.yaml):
apiVersion: v1 kind: Service metadata: name: myweb spec: type: NodePort ports: - port: 8080 nodePort: 30001 selector: app: myweb
type=NodePort和nodePort=30001的兩個屬性表明此Service開啟了NodePort方式的外網訪問模式。在Kubernetes集群之外,比如在本機的瀏覽器里,可以通過30001這個端口訪問myweb(對應到8080的虛端口上)。
運行kubectl create命令進行創建:
# kubectl create -f myweb-svc.yaml You have exposed your service on an external port on all nodes in your cluster. If you want to expose this service to the external internet, you may need to set up firewall rules for the service port(s) (tcp:30001) to serve traffic. See http://releases.k8s.io/release-1.3/docs/user-guide/services-firewalls.md for more details. service "myweb" created
我們看到上面有提示信息,意思是需要把30001這個端口在防火墻上打開,以便外部的訪問能穿過防火墻。
運行kubectl命令,查看創建的Service:
# kubectl get services NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE mysql 169.169.253.143 <none> 3306/TCP 2m myweb 169.169.149.215 <nodes> 8080/TCP 1m kubernetes 169.169.0.1 <none> 443/TCP 10m
至此,我們的第1個Kubernetes例子便搭建完成了,我們將在下一節中驗證結果。
1.3.4 通過瀏覽器訪問網頁
經過上面的幾個步驟,我們終于成功實現了Kubernetes上第1個例子的部署搭建工作。現在一起來見證成果吧!在你的筆記本上打開瀏覽器,輸入http://虛擬機IP:30001/demo/。
比如虛擬機IP為192.168.18.131(可以通過#ip a命令進行查詢),在瀏覽器里輸入地址http://192.168.18.131:30001/demo/后,可以看到如圖1.2所示的網頁界面。

圖1.2 通過瀏覽器訪問Tomcat應用
如果看不到這個網頁界面,那么可能有幾個原因,比如因為防火墻的問題無法訪問30001端口,或者因為是通過代理上網的,瀏覽器錯把虛擬機的IP地址當作遠程地址了。可以在虛擬機上直接運行curl 192.168.18.131:30001來驗證此端口能否被訪問,如果還是不能訪問,就肯定不是機器的問題了。
接下來,可以嘗試單擊“Add…”按鈕添加一條記錄并提交,如圖1.3所示,在提交以后,數據就被寫入MySQL數據庫中了。

圖1.3 在留言板網頁添加新的留言
至此,我們終于完成了Kubernetes上的Tomcat例子,這個例子并不是很復雜。我們也看到,相對于傳統的分布式應用的部署方式,在Kubernetes之上我們僅僅通過一些很容易理解的配置文件和相關的簡單命令就完成了對整個集群的部署,這讓我們驚詫于Kubernetes的創新和強大。
下一節,我們將對Kubernetes中的基本概念和術語進行全面學習,在這之前,讀者可以繼續研究這個例子里的一些拓展內容,如下所述。
◎ 研究RC、Service等配置文件的格式。
◎ 熟悉kubectl的子命令。
◎ 手工停止某個Service對應的容器進程,然后觀察有什么現象發生。
◎ 修改RC文件,改變副本數量,重新發布,觀察結果。