Kubernetes 入門基礎篇

Kubernetes 1.4 基礎課程

Kubernetes 介紹

Kubernetes的發展歷史

Kubernetes是一個開源的用於管理大量異構主機組成的雲平臺中容器的應用,Kubernetes的目標是讓部署容器化的應用及微服務簡單且高效。Kubernetes提供了應用部署、規劃、更新和維護的軟件集合,它的核心特點之一就是保證雲平臺中的容器按照用戶的期望自動化的運行,雲平臺管理人員僅僅需要加載一個微型服務,規劃器會自動找到合適的位置高可用的運行這個微服務。

在Docker作爲高級容器引擎快速發展的之前,Google很早就致力於容器技術及集羣方面的積累。在Google內部容器技術已經應用了很多年,Borg系統運行管理着成千上萬的Google內部容器應用和微服務,在Borg的支持下,無論是谷歌搜索,還是Gmail,以及谷歌地圖和YouTube視頻,都可以從龐大的數據中心中自動化的獲取技術資源來支撐其服務高性能且穩定的運行。Borg項目是Kubernetes項目的前身,Kubernetes正是在Borg的基礎上發展、構建、創新而來的。

作爲集羣管理器出現的Borg系統,在其系統中運行着衆多集羣,而每個集羣又由上萬臺服務器聯接組成,Borg每時每刻都在處理來自衆多應用程序所提交的成百上千的工作請求(Job), Borg對這些工作請求(Job)進行接收、調度、啓動,並對服務器上的容器進行啓動、關閉、釋放和監控。Borg論文中提到的三大優勢:

  • 爲終端用戶隱藏環境部署、資源管理和錯誤處理過程,終端用戶僅需要關注應用的開發。
  • 全局服務高可用、高可靠和性能監控。
  • 自動化的將負載到成千上萬的異構的服務器組成的集羣中。

Kubernetes於2014年6月在舊金山發佈,其在希臘語中意思是船長或領航員,這也與它在容器集羣管理中的角色吻合,即作爲裝載了集裝箱(Container)的衆多貨船的指揮者,負擔着全局調度和運行監控的職責。Kubernetes也經常寫作k8s,8代表從k到s有“ubernete”8個字符。

Kubernetes對雲環境中的資源進行了更高層次的抽象,通過將容器進行細緻的組合,將最終的應用服務交給用戶。Kubernetes在模型建立之初就考慮了容器在異構服務器上連接的要求,支持多種網絡解決方案,並可以在服務層面上構建集羣範圍的軟件定義網絡(SDN)環境。其目的是將服務發現和負載均衡機制放置到容器可達的範圍內,這種透明的方式便利了各個服務之間的通信,併爲微服務架構提供了平臺基礎。

Kubernetes於2015年7月發佈了1.0版本,在2016年10月發佈了1.4版本。新的1.4版本的Kubernetes更加簡單高效,並且支持了最新的Docker 1.12中的新功能。

Kubernnetes 是什麼

Kubernetes是一種用於容器集羣的自動化部署、擴容以及運維的開源平臺。與其競爭的容器集羣管理開源平臺還包括Mesos這樣的重量級產品。

使用Kubernetes可以快速高效地響應客戶需求,其特點如下:

  • 動態地對應用進行擴容。
  • 無縫地發佈和更新應用程序及服務。
  • 按需分配資源以優化硬件使用。

Kubernetes的出現是爲了減輕系統運維人員在公有云及私有云上編配和運行應用的負擔。

Kubernetes從開發之初就致力於將其打造爲簡潔、可移植、可擴展且可自愈的系統平臺,具體說明如下:

  • 簡潔:輕量級,簡單,易上手
  • 可移植:公有,私有,混合,多重雲(multi-cloud)
  • 可擴展: 模塊化, 插件化, 可掛載, 可組合
  • 可自愈: 自動佈置, 自動重啓, 自動複製
  • 以應用程序爲中心的管理: 將抽象級別從在虛擬硬件上運行操作系統上升到了在使用特定邏輯資源的操作系統上運行應用程序。這在提供了Paas的簡潔性的同時擁有IssS的靈活性,並且相對於運行12-factor應用程序有過之而無不及。
  • 開發和運維的關注點分離: 提供構建和部署的分離;這樣也就將應用從基礎設施中解耦。
  • 敏捷的應用創建和部署: 相對使用虛擬機鏡像,容器鏡像的創建更加輕巧高效。
  • 持續開發,持續集成以及持續部署: 提供頻繁可靠地構建和部署容器鏡像的能力,同時可以快速簡單地回滾(因爲鏡像是固化的)。
  • 鬆耦合,分佈式,彈性,自由的微服務: 應用被分割爲若干獨立的小型程序,可以被動態地部署和管理 -- 而不是一個運行在單機上的超級臃腫的大程序。
  • 開發,測試,生產環境保持高度一致: 無論是再筆記本電腦還是服務器上,都採用相同方式運行。
  • 兼容不同的雲平臺或操作系統上: 可運行與Ubuntu,RHEL,on-prem或者Google Container Engine,覆蓋了開發,測試和生產的各種不同環境。
  • 資源分離: 帶來可預測的程序性能。
  • 資源利用: 高性能,大容量。

Kubernetes 不是什麼

Kubernetes不是平臺即服務(PaaS)。

  • Kubernetes並不對支持的應用程序類型有任何限制。 它並不指定應用框架,限制語言類型,也不僅僅迎合 12-factor模式. Kubernetes旨在支持各種多種多樣的負載類型:只要一個程序能夠在容器中運行,它就可以在Kubernetes中運行。
  • Kubernetes並不關注代碼到鏡像領域。它並不負責應用程序的構建。不同的用戶和項目對持續集成流程都有不同的需求和偏好,所以Kubernetes分層支持持續集成但並不規定和限制它的工作方式。
  • 確實有不少PaaS系統運行在Kubernetes之上,比如OpenshiftDeis。同樣你也可以將定製的PaaS系統,結合一個持續集成系統再Kubernetes上進行實施:只需生成容器鏡像並通過Kubernetes部署。
  • 由於Kubernetes運行再應用層而不是硬件層,所以它提供了一些一般PaaS提供的功能,比如部署,擴容,負載均衡,日誌,監控,等等。無論如何,Kubernetes不是一個單一應用,所以這些解決方案都是可選可插拔的。

Kubernetes並不是單單的"編排系統";它排除了對編排的需要:

  • “編排”的技術定義爲按照指定流程執行一系列動作:執行A,然後B,然後C。相反,Kubernetes有一系列控制進程組成,持續地控制從當前狀態到指定狀態的流轉。無需關注你是如何從A到C:只需結果如此。這樣將使得系統更加易用,強大,健壯和彈性。

Kubernetes的組織結構

Kubernetes組織結構主要是由Node、Pod、Replication Controller、Deployment、Service等多種資源對象組成的。其資源對象屬性均保存在etcd提供的鍵值對存儲庫中,通過kubectl工具完成對資源對象的增、刪、查、改等操作。我們可以將Kubernetes視爲一個高度自動化的資源對象控制系統,它通過跟蹤和對比etcd鍵值對庫中保存的“對象原始信息”和當前環境中“對象實時信息”的差異來實現自動化控制和自動化糾錯等功能的。

Kubernetes支持DockerRocket容器, 對其他的容器鏡像格式和容器會在未來加入。

Master

Kubernetes中的Master是一臺運行Kubernetes的主機,可以是物理機也可以是虛擬機,它是Kubernetes集羣中的控制節點,它負責完成整個Kubernetes集羣的創建、管理和控制,在Kubernetes集羣中必不可少。

我們默認只能在Master上使用kubectl工具完成Kubernetes具體的操作命令,如果Master宕機或是離線,我們所有的控制命令都會失效。因此Master非常重要,在生產環境中,Master一般會配置高可用集羣服務解決其單點故障問題。

Kubernetes 1.4 開始Master上的關鍵服務都是以Docker 容器實例的方式運行的,包括etcd服務。具體服務和其功能如下:

  • Kubernetes API Server ( kube-apiserver),爲kubernetes客戶端提供HTTP Rest API接口的關鍵服務,是kubernetes中對象資源增、刪、查、改等操作的唯一入口,同時也是kubernetes集羣控制的入口。
  • Kubernetes Controller Manager ( kube-controller-manager),爲Kubernetes提供統一的自動化控制服務。
  • Kubernetes Scheduler (kube-scheduler),爲Kubernetes提供資源調度的服務,統一分配資源對象。
  • Etcd Server(etcd),爲Kubernetes保存所有對象的鍵值對數據信息。

Node

在Kubernetes集羣中,除了Master之外其它運行Kubernetes服務的主機稱之爲Node,在早期Kubernetes中稱其爲Minion。Node也可以是物理機或虛擬機。Node是Kubernetes集羣中任務的負載節點,Master經過特殊設置後也可以作爲Node負載任務。Kubernetes一般是由多個Node組成的,我們建議Node的數量是N+2的。當某個Node宕機後,其上的負載任務將會被Master自動轉移到其它的Node上。之所以使用N+2的配置,是爲了在Node意外宕機時Kubernetes集羣的負載不會突然被撐滿,導致性能急劇下降。

Kubernetes 1.4 開始Node上的關鍵服務都是以Docker 實例的方式運行的,具體服務和功能如下:

  • kubelet,負責Pod對應的容器實例的創建、啓動、停止、刪除等任務,接受Master傳遞的指令實現Kubernetes集羣管理的基本功能。
  • kube-proxy,實現kubernetes service功能和負載均衡機制的服務。

Node節點通過kubelet服務向Master註冊,可以實現動態的在Kubernetes集羣中添加和刪除負載節點。已加入Kubernetes集羣中的Node節點還會通過kubelet服務動態的向Master提交其自身的資源信息,例如主機操作系統、CPU、內存、Docker版本和網絡情況。如果Master發現某Node超時未提交信息,Node會被判定爲“離線”並標記爲“不可用(Not Ready),隨後Master會將此離線Node上原有Pod遷移到其它Node上。

Pod

在Kubenetes中所有的容器均在Pod中運行,一個Pod可以承載一個或者多個相關的容器。同一個Pod中的容器會部署在同一個物理機器上並且能夠共享資源。一個Pod也可以包含0個或者多個磁盤卷組(volumes),這些卷組將會以目錄的形式提供給一個容器或者被所有Pod中的容器共享。對於用戶創建的每個Pod,系統會自動選擇那個健康並且有足夠資源的機器,然後開始將相應的容器在那裏啓動,你可以認爲Pod就是虛擬機。當容器創建失敗的時候,容器會被節點代理(node agent)自動重啓,這個節點代理(node agent)就是kubelet服務。在我們定義了副本控制器(replication controller)之後,如果Pod或者服務器故障的時候,容器會自動的轉移並且啓動。

Pod是Kubernetes的基本操作單元,把相關的一個或多個容器構成一個Pod,通常Pod裏的容器運行相同的應用。Pod包含的容器運行在同一個物理機器上,看作一個統一管理單元,共享相同的卷(volumes)和網絡名字空間(network namespace)、IP和端口(Port)空間。在Kubernetes集羣中,一個Pod中的容器可以和另一個Node上的Pod容器直接通訊。

用戶可以自己創建並管理Pod,但是Kubernetes可以極大的簡化管理操作,它能讓用戶指派兩個常見的跟Pod相關的活動:1) 基於相同的Pod配置,部署多個Pod副本;2)當一個Pod或者它所在的機器發生故障的時候創建替換的Pod。Kubernetes的API對象用來管理這些行爲,我們將其稱作副本控制器(Replication Controller),它用模板的形式定義了Pod,然後系統根據模板實例化出一些Pod。Pod的副本集合可以共同組成應用、微服務,或者在一個多層應用中的某一層。一旦Pod創建好,Kubernetes系統會持續的監控他們的健康狀態,和它們運行時所在的機器的健康狀況。如果一個Pod因爲軟件或者機器故障,副本控制器(Replication Controller)會自動在健康的機器上創建一個新的Pod,來保證pod的集合處於冗餘狀態。

Label

Label用來給Kubernetes中的對象分組。Label通過設置鍵值對(key-value)方式在創建Kubernetes對象的時候附屬在對象之上。一個Kubernetes對象可以定義多個Labels(key=value),並且key和value均由用戶自己指定,同一組Label(key=value)可以指定到多個Kubernetes對象,Label可以在創建Kubernetes對象時設置,也可以在對象創建後通過kubectl或kubernetes api添加或刪除。其他Kubernetes對象可以通過標籤選擇器(Label Selector)選擇作用對象。你可以把標籤選擇器(Lebel Selector)看作關係查詢語言(SQL)語句中的where條件限定詞。

Lable實現的是將指定的資源對象通過不同的Lable進行捆綁,靈活的實現多維度的資源分配、調度、配置和部署。

Replication Controller(RC)

Replication Controller確保任何時候Kubernetes集羣中有指定數量的Pod副本(replicas)在運行, 如果少於指定數量的Pod副本(replicas),Replication Controller會啓動新的容器(Container),反之會殺死多餘的以保證數量不變。Replication Controller使用預先定義的Pod模板創建pods,一旦創建成功,Pod模板和創建的Pod沒有任何關聯,可以修改Pod模板而不會對已創建Pod有任何影響。也可以直接更新通過Replication Controller創建的pod。對於利用pod模板創建的pod,Replication Controller根據標籤選擇器(Label Selector)來關聯,通過修改Pod的Label可以刪除對應的Pod。

Replication Controller的定義包含如下的部分:

  • Pod的副本數目(Replicas)
  • 用於篩選Pod的標籤選擇器(Label Selector)
  • 用於創建Pod的標準配置模板(Template)

Replication Controller主要有如下用法:

  • 編排(Rescheduling):Replication Controller會確保Kubernetes集羣中指定的pod副本(replicas)在運行, 即使在節點出錯時。
  • 縮放(Scaling):通過修改Replication Controller的副本(replicas)數量來水平擴展或者縮小運行的pod。
  • 滾動升級(Rolling updates): Replication Controller的設計原則使得可以一個一個地替換Pod來滾動升級(rolling updates)服務。
  • 多版本任務(Multiple release tracks): 如果需要在系統中運行多版本(multiple release)的服務,Replication Controller使用Labels來區分多版本任務(multiple release tracks)。

由於Replication Controller 與Kubernetes API中的模塊有同名衝突,所以從Kubernetes 1.2 開始並在Kubernetes 1.4 中它由另一個概念替換,這個新概念的名稱爲副本設置(Replica Set),Kubernetes官方將其稱爲”下一代RC“。Replicat Set 支持基於集合的Label Selector(set-based selector),而Replication Controller 僅僅支持基於鍵值對等式的Label Selector(equality-based selector)。此外,Replicat Set 在Kubernetes 1.4中也不再單獨使用,它被更高層次的資源對象Deployment 使用,所以在Kubernetes 1.4中我們使用Deployment定義替換了之前的Replication Controller定義。

Deployment

在Kubernetes 1.2開始,定義了新的概念Deployment用以管理Pod和替換Replication Controller。

你可以認爲Deployment是新一代的副本控制器。其工作方式和配置基本與Replication Controller差不多,後面我們主要使用的副本控制器是Deployment。

Horizontal Pod Autoscaler (HPA)

Horizontal Pod Autoscaler 爲Pod 橫向自動擴容,簡稱HPA。Kubernetes可以通過RC或其替代對象監控Pod的負載變化情況,以此制定針對性方案調整目標Pod的副本數以增加其性能。

HPA使用以下兩種方法度量Pod的指標:

  • CPUUtilizationPercentage,目標Pod所有副本自身的CPU利用率的平均值
  • 應用自定義度量指標,例如每秒請求數(TPS)

Service

Kubernetes中Pod是可創建可銷燬而且不可再生的。 Replication Controllers可以動態的創建、配置和銷燬Pod。雖然我們可以設置Pod的IP,但是Pod的IP並不能得到穩定和持久化的保證。這將會導致一個凸出的問題,如果在Kubernetes集羣中,有一些後端Pod(backends)爲另一些前端Pod(frontend)提供服務或功能驅動,如何能保證前端(frontend)能夠找到並且鏈接到後端(backends)。這就需要稱之爲服務(Service)的Kubernetes對象來完成任務了。

Services也是Kubernetes的基本操作單元,是Kubernetes運行用戶應用服務的抽象,每一個服務後面都有很多對應的容器來支持,通過Proxy的port和服務selector決定服務請求傳遞給後端提供服務的容器,對外表現爲一個單一訪問接口,外部不需要了解後端如何運行,這給擴展或維護後端帶來很大的好處。

Kubernetes中的每個Service其實就是我們稱之爲“微服務”的東西。

Service同時還是Kubernetes分佈式集羣架構的核心,Service具有以下特性:

  • 擁有唯一指定的名字
  • 擁有虛擬IP
  • 能夠提供某種網絡或Socket服務
  • 能夠將用戶請求映射到提供服務的一組容器的應用上

一般情況下,Service通常是由多個Pod及相關服務進程來提供服務的,每個服務進程都具有一個獨立的訪問點(Endpoint 一組IP和端口),Kubernetes可以通過內建的透明負載均衡和故障恢復機制,排除突發故障並提供我們不間斷的訪問。

Volume

Volume是Pod中能夠被多個容器訪問的共享目錄。Kubernetes中的Volume定義的Pod上,然後被一個Pod裏的多個容器掛載到具體的目錄上,Volume的生命週期與Pod的生命週期相同,但並不是與Pod中的容器相關,當Pod中的容器終止或重啓,Volume中的數據不會丟失。Kubernetes中的Volume支持GlusterFS和Ceph這樣的分佈式文件系統和本地EmptyDir和HostPath這樣的本地文件系統。

Persistent Volume

Persistent Volume 不同於前面提到的Volume ,Volume是分配給Pod的存儲資源,而Persistent Volume是Kubernetes集羣中的網絡存儲資源,我們可以在這個資源中劃分子存儲區域分配給Pod作爲Volume使用。Persistent Volume 簡稱 PV,作爲Pod 的Volume使用時,還需要分配Persistent Volume Claim 作爲Volume,Persistent Volume Claim簡稱PVC。

Persistent Volume 有以下特點:

  • Persistent Volume 只能是網絡存儲,並不掛接在任何Node,但可以在每個Node上訪問到
  • Persistent Volume 並不是第一在Pod上,而是獨立於Pod定義到Kubernetes集羣上
  • Persistent Volume 目前支持的類型如下:NFS、RDB、iSCSI 、AWS ElasticBlockStore、GlusterFS和Ceph等

Namespace

Namespace 是Linux 中資源管理的重要概念和應用,它實現了多用戶和多進程的資源隔離。Kubernetes中將集羣中的資源對象運行在不同的Namespace下,形成了相對獨立的不同分組、類型和資源羣。

在Kubernetes 集羣啓動後,會創建第一個默認Namespace 叫做”default“。用戶在創建自有的Pod、RC、Deployment 和Service 指定運行的Namespace,在不明確指定的情況下默認使用”default“。

Kubernetes 集羣的資源配額管理也是通過Namespace結合Linux 系統的Cgroup 來完成對不同用戶Cpu使用量、內存使用量、磁盤訪問速率等資源的分配。

Kubernetes 安裝和配置

課堂及實驗環境說明

我們的環境結構如下,請根據自己的Foundation號調整IP和設備名

up500課程結構圖

設備名 主機名 說明
master masterN Kubernetes Master Node 虛擬主機
nodea nodeaN Kubernetes Node 虛擬機
nodeb nodebN Kubernetes Node 虛擬機
nodec nodecN Kubernetes Node 虛擬機
sharestorage sharestorageN 共享存儲虛擬機

配置 Kubernetes 運行環境

安裝Kubernetes的方法有很多,你可以從源代碼編譯安裝,也可以通過kubernetes.io網站腳本安裝,但是我們推薦大家使用包管理工具安裝Kubernetes,這樣可以更好的做到環境更迭和升級。

在課程環境中,我們已經做好了基於RedHat YUM的網絡包管理環境,RHEL 7.2和Kubernetes 1.4的安裝包環境都已就位,課程環境下的所有設備都已設置RHEL 7.2的REPO,但Kubernetes 1.4的REPO沒有默認放入設備中,接下來我們將使用YUM RPM的方式安裝Kubernetes 1.4。

需要注意的是,演示中的環境是在Foundation 0 設備上,大家做實驗時請替換設備號爲你自己所在設備的Foundation 號。

Kubernetes的運行節點中有一個Master 節點的概念,Master節點上運行Kubernetes的管理服務和etcd數據中心服務。我們將使用master虛擬機安裝配置Kubernetes的管理、etcd數據中心、proxy代理等服務。同時選擇nodea和nodeb兩個虛擬機運行Kubernetes上的資源容器服務。後面將使用nodec來添加和剔除節點。

首先我們先初始化虛擬機設備:

[kiosk@foundation0 Desktop]$ rht-vmctl reset master
Are you sure you want to reset master? (y/n) y
Powering off master.
Resetting master.
Creating virtual machine disk overlay for up500-master-vda.qcow2
Starting master.
[kiosk@foundation0 Desktop]$ rht-vmctl reset nodea
Are you sure you want to reset nodea? (y/n) y
Powering off nodea.
Resetting nodea.
Creating virtual machine disk overlay for up500-nodea-vda.qcow2
Starting nodea.
[kiosk@foundation0 Desktop]$ rht-vmctl reset nodeb
Are you sure you want to reset nodeb? (y/n) y
Powering off nodeb.
Resetting nodeb.
Creating virtual m

確認所有虛擬機設備正常啓動後,通過 ssh 連接 master、nodea和nodeb 設備。你可以通過 view master 這樣的桌面工具連接。根據你自己的設備號替換0。

[kiosk@foundation0 Desktop]$ ssh root@master0
Last login: Sat Jun  4 14:39:46 2016 from 172.25.0.250
[root@master0 ~]# 
[kiosk@foundation0 Desktop]$ ssh root@nodea0
Last login: Sat Jun  4 14:39:46 2016 from 172.25.0.250
[root@nodea0 ~]# 
[kiosk@foundation0 Desktop]$ ssh root@nodeb0
Last login: Sat Jun  4 14:39:46 2016 from 172.25.0.250
[root@nodeb0 ~]# 

關閉防火牆

由於Kubernetes環境需要配置Iptables來完成端口映射和服務代理,所以我們需要關閉系統中的Firewalld防火牆。根據你自己的設備號替換0。

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl disable firewalld " ; done
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
Removed symlink /etc/systemd/system/basic.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
Removed symlink /etc/systemd/system/basic.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
Removed symlink /etc/systemd/system/basic.target.wants/firewalld.service.

因爲要操作三臺虛擬機,所以預先配置好ssh key是明智的選擇。如果你沒有配置,後續的操作會頻繁的輸入虛擬機密碼。

上一條命令我們僅僅是關閉了Firewalld防火牆的啓動開關,接下來我們需要將它停止運行並清除其配置的規則。根據你自己的設備號替換0。

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl stop firewalld " ; done

注意此處是沒有回顯的,你可以自行測試。

關閉NetworkManager服務

由於Kubernetes環境中將調用IProute2設置網絡,爲了不使系統的自適應網絡服務影響Kubernetes的網絡環境,建議關閉NetowrkManager服務。根據你自己的設備號替換0。

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl disable  NetworkManager " ; done
Removed symlink /etc/systemd/system/multi-user.target.wants/NetworkManager.service.
Removed symlink /etc/systemd/system/dbus-org.freedesktop.NetworkManager.service.
Removed symlink /etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service.
Removed symlink /etc/systemd/system/multi-user.target.wants/NetworkManager.service.
Removed symlink /etc/systemd/system/dbus-org.freedesktop.NetworkManager.service.
Removed symlink /etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service.
Removed symlink /etc/systemd/system/multi-user.target.wants/NetworkManager.service.
Removed symlink /etc/systemd/system/dbus-org.freedesktop.NetworkManager.service.
Removed symlink /etc/systemd/system/dbus-org.freedesktop.nm-dispatcher.service.

上一條命令我們僅僅是關閉了NetworkManager的啓動開關,接下來我們需要將它停止運行。根據你自己的設備號替換0。

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl stop  NetworkManager " ; done

關閉SELinux系統安全機制

由於Kubernetes 1.4與SELinux在運行環境下有已知的Bug,所以我們要在安裝Kubernetes 1.4之前關閉系統的SELinux安全機制。根據你自己的設備號替換0。

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "setenforce 0 " ; done
[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config " ; done

安裝附加軟件包

由於目前我們使用的虛擬化系統是最精簡的RHEL7.2環境,所以爲了後續操作比較方便,我們需要安裝wget、bzip2和net-tools軟件包。虛擬化系統的YUM環境默認支持RHEL7.2標準包的安裝。根據你自己的設備號替換0。

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "yum install wget bzip2 net-tools -y " ; done
Loaded plugins: product-id, search-disabled-repos, subscription-manager
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
Repodata is over 2 weeks old. Install yum-cron? Or run: yum makecache fast
Resolving Dependencies
--> Running transaction check
---> Package bzip2.x86_64 0:1.0.6-13.el7 will be installed
---> Package net-tools.x86_64 0:2.0-0.17.20131004git.el7 will be installed
---> Package wget.x86_64 0:1.14-10.el7_0.1 will be installed
--> Finished Dependency Resolution

-- 中間顯示較多,此處忽略 --

Complete!

配置Kubernetes YUM源環境

Kubernetes 是基於容器的應用平臺,所以需要首先安裝Docker。Kubernetes 1.4 支持Docker 1.12的新特性,所以我們建議安裝最新的Docker 1.12以發揮其最大的性能。安裝Docker1.12和Kubernetes 1.4在我們的實驗環境下非常簡單,下載環境中的YUM源配置就可以了:

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "wget http://cla***oom.example.com/materials/kubernetes-1.4.repo -O /etc/yum.repos.d/k8s.repo " ; done
--2016-10-27 23:33:26--  http://cla***oom.example.com/materials/kubernetes-1.4.repo
Resolving cla***oom.example.com (cla***oom.example.com)... 172.25.254.254
Connecting to cla***oom.example.com (cla***oom.example.com)|172.25.254.254|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 151 [application/x-troff-man]
Saving to: ‘/etc/yum.repos.d/k8s.repo’

     0K                                                       100% 13.1M=0s

2016-10-27 23:33:26 (13.1 MB/s) - ‘/etc/yum.repos.d/k8s.repo’ saved [151/151]

--2016-10-27 23:33:26--  http://cla***oom.example.com/materials/kubernetes-1.4.repo
Resolving cla***oom.example.com (cla***oom.example.com)... 172.25.254.254
Connecting to cla***oom.example.com (cla***oom.example.com)|172.25.254.254|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 151 [application/x-troff-man]
Saving to: ‘/etc/yum.repos.d/k8s.repo’

     0K                                                       100% 28.2M=0s

2016-10-27 23:33:26 (28.2 MB/s) - ‘/etc/yum.repos.d/k8s.repo’ saved [151/151]

--2016-10-27 23:33:25--  http://cla***oom.example.com/materials/kubernetes-1.4.repo
Resolving cla***oom.example.com (cla***oom.example.com)... 172.25.254.254
Connecting to cla***oom.example.com (cla***oom.example.com)|172.25.254.254|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 151 [application/x-troff-man]
Saving to: ‘/etc/yum.repos.d/k8s.repo’

     0K                                                       100% 33.8M=0s

2016-10-27 23:33:25 (33.8 MB/s) - ‘/etc/yum.repos.d/k8s.repo’ saved [151/151]

如果是公網線上環境,建議設置如下兩個源:

[kevinzou@hk ~]$ cat /etc/yum.repos.d/docker.repo 
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/7/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg

[kevinzou@hk ~]$ cat /etc/yum.repos.d/kubernetes.repo 
[kubernetes]
name=Kubernetes
baseurl=http://yum.kubernetes.io/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
       https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg

安裝 Kubernetes 1.4

安裝軟件包

我們需要安裝如下軟件包:

軟件包名 軟件包說明
docker-engine Docker Engine 軟件包,提供底層容器支持
kubeadm Kubernetes 1.4 新增 配置Cluster工具包
kubectl Kubernetes Master環境配置管理工具包
kubelet Kubernetes 主程序包
kubernetes-cni Kubernetes 容器網絡配置工具包

我們需要在所有節點上安裝這些軟件包,無論是Manager還是Node。

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "yum install docker-engine kubeadm kubectl kubelet kubernetes-cni -y " ; done
Loaded plugins: product-id, search-disabled-repos, subscription-manager
This system is not registered to Red Hat Subscription Management. You can use subscription-manager to register.
Resolving Dependencies
--> Running transaction check
---> Package docker-engine.x86_64 0:1.12.2-1.el7.centos will be installed
--> Processing Dependency: docker-engine-selinux >= 1.12.2-1.el7.centos for package: docker-engine-1.12.2-1.el7.centos.x86_64
--> Processing Dependency: libcgroup for package: docker-engine-1.12.2-1.el7.centos.x86_64
--> Processing Dependency: libseccomp.so.2()(64bit) for package: docker-engine-1.12.2-1.el7.centos.x86_64
--> Processing Dependency: libltdl.so.7()(64bit) for package: docker-engine-1.12.2-1.el7.centos.x86_64
---> Package kubeadm.x86_64 0:1.5.0-0.alpha.0.1534.gcf7301f will be installed
---> Package kubectl.x86_64 0:1.4.0-0 will be installed
---> Package kubelet.x86_64 0:1.4.0-0 will be installed
--> Processing Dependency: socat for package: kubelet-1.4.0-0.x86_64
---> Package kubernetes-cni.x86_64 0:0.3.0.1-0.07a8a2 will be installed
--> Running transaction check
---> Package docker-engine-selinux.noarch 0:1.12.2-1.el7.centos will be installed
-- 中間顯示較多,此處忽略 --
  libseccomp.x86_64 0:2.2.1-1.el7                                               
  libsemanage-python.x86_64 0:2.1.10-18.el7                                     
  libtool-ltdl.x86_64 0:2.4.2-20.el7                                            
  policycoreutils-python.x86_64 0:2.2.5-20.el7                                  
  python-IPy.noarch 0:0.75-6.el7                                                
  setools-libs.x86_64 0:3.3.7-46.el7                                            
  socat.x86_64 0:1.7.2.2-5.el7                                                  

Complete!

由於總所周知卻不可描述的原因,你可能無法在大陸網絡環境下安裝這些軟件包,請自備×××。

啓動Docker服務

設置docker隨系統啓動

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl enable docker kubelet " ; done
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service.
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service.
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service.

啓動docker 服務

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl start docker " ; done

預導入容器鏡像

從Kubernetes 1.4開始,Kubenetes實現了容器化運行其依賴服務,比如etcd、kube-proxy、kube-controller-manager等。因此,在Kubernetes應用啓動的時候首先會通過網絡下載所需要的容器鏡像,由於衆所周知且不可描述的原因,Kubernetes所需的Google容器鏡像文件大陸地區無法下載,我們訪問不到gcr.io網站。

我們通過其他技術手段可以訪問gcr.io網站,並下載了這些容器鏡像。如果你需要獨立下載安裝Kubernetes 1.4,請自備×××。

Master節點需要如下容器鏡像:

gcr.io/google_containers/etcd-amd64:2.2.5
gcr.io/google_containers/exechealthz-amd64:1.1
gcr.io/google_containers/kube-apiserver-amd64:v1.4.0
gcr.io/google_containers/kube-controller-manager-amd64:v1.4.0
gcr.io/google_containers/kube-discovery-amd64:1.0
gcr.io/google_containers/kubedns-amd64:1.7
gcr.io/google_containers/kube-dnsmasq-amd64:1.3
gcr.io/google_containers/kube-proxy-amd64:v1.4.0
gcr.io/google_containers/kube-scheduler-amd64:v1.4.0
gcr.io/google_containers/pause-amd64:3.0
weaveworks/weave-kube:1.7.2
weaveworks/weave-npc:1.7.2

Node節點需要如下容器鏡像:

gcr.io/google_containers/kube-proxy-amd64:v1.4.0
gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0
gcr.io/google_containers/pause-amd64:3.0
weaveworks/weave-kube:1.7.2
weaveworks/weave-npc:1.7.2

在後面的章節中會具體介紹這些容器鏡像的具體作用。

接下來在實驗環境中下載並導入容器鏡像:

  1. 下載k8s-1.4-master-img.tbz到Master節點
[kiosk@foundation0 Desktop]$ ssh root@master0 "wget http://cla***oom.example.com/materials/k8s-imgs/k8s-1.4-master-img.tbz "
  1. 下載k8s-1.4-node-img.tbz 到Node節點
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "wget http://cla***oom.example.com/materials/k8s-imgs/k8s-1.4-node-img.tbz " ; done
  1. 在Master節點上將k8s-1.4-master-img.tbz文件解包
[kiosk@foundation0 Desktop]$ ssh root@master0 "tar -jxf k8s-1.4-master-img.tbz" 
  1. 在Master節點上導入容器鏡像
[kiosk@foundation0 Desktop]$  ssh root@master0 'for i in ./k8s-1.4-master-img/*.img ; do docker load -i  $i ; done'
  1. 在Node節點上將k8s-1.4-node-img.tbz文件解包
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf k8s-1.4-node-img.tbz " ; done
  1. 在Node節點上導入容器鏡像
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./k8s-1.4-node-img/*.img ; do docker load -i $i  ; done' ; done

啓動 Kubernetes集羣

確認kubelet服務啓動

保證SELinux關閉的前提下在所有節點上啓動kubelet服務

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "hostname ; getenforce " ; done
master0.example.com
Permissive
nodea0.example.com
Permissive
nodeb0.example.com
Permissive

[kiosk@foundation0 Desktop]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "systemctl start kubelet " ; done

此時如果查看節點上的日誌文件/var/log/messages,此時會報錯出來,原因是沒有修改配置文件。

Oct 28 01:03:50 localhost systemd: Unit kubelet.service entered failed state.
Oct 28 01:03:50 localhost systemd: kubelet.service failed.
Oct 28 01:04:01 localhost systemd: kubelet.service holdoff time over, scheduling restart.
Oct 28 01:04:01 localhost systemd: Started Kubernetes Kubelet Server.
Oct 28 01:04:01 localhost systemd: Starting Kubernetes Kubelet Server...
Oct 28 01:04:01 localhost kubelet: error: failed to run Kubelet: invalid kubeconfig: stat /etc/kubernetes/kubelet.conf: no such file or directory
Oct 28 01:04:01 localhost systemd: kubelet.service: main process exited, code=exited, status=1/FAILURE

初始化Kubernetes 1.4 集羣

Kubernetes 1.4 受Docker 1.12的影響提供了kubeadm工具,使Kubernetes 1.4的集羣搭建非常簡單,僅僅兩個命令就可以完成集羣的初始化和節點的加入。

  • 初始化集羣環境
[kiosk@foundation0 Desktop]$ ssh root@master0 "kubeadm init"
<master/tokens> generated token: "279c9d.eb291600fc5d4f6f"
<master/pki> created keys and certificates in "/etc/kubernetes/pki"
<util/kubeconfig> created "/etc/kubernetes/kubelet.conf"
<util/kubeconfig> created "/etc/kubernetes/admin.conf"
<master/apiclient> created API client configuration
<master/apiclient> created API client, waiting for the control plane to become ready
<master/apiclient> all control plane components are healthy after 15.801121 seconds
<master/apiclient> waiting for at least one node to register and become ready
<master/apiclient> first node is ready after 3.007568 seconds
<master/discovery> created essential addon: kube-discovery, waiting for it to become ready
<master/discovery> kube-discovery is ready after 2.504817 seconds
<master/addons> created essential addon: kube-proxy
<master/addons> created essential addon: kube-dns

Kubernetes master initialised successfully!

You can now join any number of machines by running the following on each node:

kubeadm join --token 279c9d.eb291600fc5d4f6f 172.25.0.10

請記下最後一行“kubeadm join --token 279c9d.eb291600fc5d4f6f 172.25.0.10”,後面我們節點加入時會用到。

  • 查看初始化狀態
[kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl get pod --all-namespaces"
NAMESPACE     NAME                                          READY     STATUS              RESTARTS   AGE
kube-system   etcd-master0.example.com                      1/1       Running             0          6m
kube-system   kube-apiserver-master0.example.com            1/1       Running             1          6m
kube-system   kube-controller-manager-master0.example.com   1/1       Running             0          6m
kube-system   kube-discovery-982812725-l5uow                1/1       Running             0          6m
kube-system   kube-dns-2247936740-uxzfc                     0/3       ContainerCreating   0          6m
kube-system   kube-proxy-amd64-joq3k                        1/1       Running             0          6m
kube-system   kube-scheduler-master0.example.com            1/1       Running             0          6m
  • 加入新節點
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i " kubeadm join --token 279c9d.eb291600fc5d4f6f 172.25.0.10 "  ; done
<util/tokens> validating provided token
<node/discovery> created cluster info discovery client, requesting info from "http://172.25.0.10:9898/cluster-info/v1/?token-id=279c9d"
<node/discovery> cluster info object received, verifying signature using given token
<node/discovery> cluster info signature and contents are valid, will use API endpoints [https://172.25.0.10:443]
<node/csr> created API client to obtain unique certificate for this node, generating keys and certificate signing request
<node/csr> received signed certificate from the API server, generating kubelet configuration
<util/kubeconfig> created "/etc/kubernetes/kubelet.conf"

Node join complete:
* Certificate signing request sent to master and response
  received.
* Kubelet informed of new secure connection details.

Run 'kubectl get nodes' on the master to see this machine join.

<util/tokens> validating provided token
<node/discovery> created cluster info discovery client, requesting info from "http://172.25.0.10:9898/cluster-info/v1/?token-id=279c9d"
<node/discovery> cluster info object received, verifying signature using given token
<node/discovery> cluster info signature and contents are valid, will use API endpoints [https://172.25.0.10:443]
<node/csr> created API client to obtain unique certificate for this node, generating keys and certificate signing request
<node/csr> received signed certificate from the API server, generating kubelet configuration
<util/kubeconfig> created "/etc/kubernetes/kubelet.conf"

Node join complete:
* Certificate signing request sent to master and response
  received.
* Kubelet informed of new secure connection details.

Run 'kubectl get nodes' on the master to see this machine join.
  • 測試節點狀態
[kiosk@foundation0 Desktop]$  ssh root@master0 "kubectl get nodes"
NAME                  STATUS    AGE
master0.example.com   Ready     10m
nodea0.example.com    Ready     1m
nodeb0.example.com    Ready     1m

[kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl get pod --all-namespaces"
NAMESPACE     NAME                                          READY     STATUS              RESTARTS   AGE
kube-system   etcd-master0.example.com                      1/1       Running             0          13m
kube-system   kube-apiserver-master0.example.com            1/1       Running             1          13m
kube-system   kube-controller-manager-master0.example.com   1/1       Running             0          13m
kube-system   kube-discovery-982812725-l5uow                1/1       Running             0          13m
kube-system   kube-dns-2247936740-uxzfc                     0/3       ContainerCreating   0          13m
kube-system   kube-proxy-amd64-7oy3h                        1/1       Running             0          3m
kube-system   kube-proxy-amd64-joq3k                        1/1       Running             0          13m
kube-system   kube-proxy-amd64-wbyyx                        1/1       Running             0          3m
kube-system   kube-scheduler-master0.example.com            1/1       Running             0          13m

大家會發現kube-dns一直處在ContainerCreating狀態,這是因爲我們還未配置Kubernetes網絡。

[kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl apply -f http://cla***oom.example.com/materials/k8s-conf/weave-kube"
daemonset "weave-net" created
  • 測試節點狀態
[kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl get pod --all-namespaces"
NAMESPACE     NAME                                          READY     STATUS    RESTARTS   AGE
kube-system   etcd-master0.example.com                      1/1       Running   0          22m
kube-system   kube-apiserver-master0.example.com            1/1       Running   1          22m
kube-system   kube-controller-manager-master0.example.com   1/1       Running   0          22m
kube-system   kube-discovery-982812725-l5uow                1/1       Running   0          22m
kube-system   kube-dns-2247936740-uxzfc                     3/3       Running   0          22m
kube-system   kube-proxy-amd64-7oy3h                        1/1       Running   0          12m
kube-system   kube-proxy-amd64-joq3k                        1/1       Running   0          22m
kube-system   kube-proxy-amd64-wbyyx                        1/1       Running   0          12m
kube-system   kube-scheduler-master0.example.com            1/1       Running   0          22m
kube-system   weave-net-15j6s                               2/2       Running   0          3m
kube-system   weave-net-3ybbh                               2/2       Running   0          3m
kube-system   weave-net-8ilap                               2/2       Running   0          3m
  • 安裝kubectl擴展工具

默認情況下我們使用kubernetes控制工具kubectl時是沒有自動補全功能的,這樣在日常工作中要記憶大量繁瑣的命令參數和當前狀態值。我們需要生成系統支持的補全(completion)文件,並放置到系統相關目錄中。

[root@master0 ~]# kubectl completion bash >kubectl
[root@master0 ~]# cp kubectl /etc/bash_completion.d/

需要重新登錄系統此配置才能生效。重新登錄master0系統後使用kubectl工具就可以使用TAB鍵來補全命令了。

安裝kubernetes-dashboard

Kubernetes 1.2中引入了一個Web UI以方便用戶配置管理Kubernetes 集羣,這個Web UI就是kubernetes-dashboard。它是Kubernetes官方提供的可視化工具,在Kubernetes 1.4系統中可以完成大多數的管理操作。

在Kubernetes 1.4中安裝kubernetes-dashboard是非常簡單的,直接使用Kubernetes提供的yaml文件就可以安裝,公網yaml文件位置在https://rawgit.com/kubernetes/dashboard/master/src/deploy/kubernetes-dashboard.yaml。我們在實驗環境下爲大家提供了本地文件。

[kiosk@foundation0 Desktop]$ ssh root@master0 "kubectl apply -f http://cla***oom.example.com/materials/k8s-conf/kubernetes-dashboard.yaml "
deployment "kubernetes-dashboard" created
service "kubernetes-dashboard" created

kubernetes-dashboard.yaml文件的內容如下,具體的關鍵字說明會在後面的課程中具體描述:

# Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Configuration to deploy release version of the Dashboard UI.
#
# Example usage: kubectl create -f <this_file>

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  labels:
    app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kubernetes-dashboard
  template:
    metadata:
      labels:
        app: kubernetes-dashboard
    spec:
      containers:
      - name: kubernetes-dashboard
        image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0
        imagePullPolicy: IfNotPresent
        # 請注意這裏,imagePullPolicy關鍵字設置了獲取容器鏡像的策略,網絡文件中默認設置爲
        # Always 表示每次都下載鏡像,我將這裏設置爲IfNotPresent,這樣我們就可以使用之前預
        # 導入的鏡像
        ports:
        - containerPort: 9090
          protocol: TCP
        args:
          # Uncomment the following line to manually specify Kubernetes API server Host
          # If not specified, Dashboard will attempt to auto discover the API server and connect
          # to it. Uncomment only if the default does not work.
          # - --apiserver-host=http://my-address:port
        livenessProbe:
          httpGet:
            path: /
            port: 9090
          initialDelaySeconds: 30
          timeoutSeconds: 30
---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 9090
  selector:
    app: kubernetes-dashboard

正常創建kubernetes-dashboard服務後,我們需要查看其拋出的隨機端口,這個端口號30000~32767之間,Kubernetes 通過kube-proxy服務代理用戶請求,具體鏈接過程我們後面的課程具體介紹。

[root@master0 ~]# kubectl describe  service --namespace=kube-system
Name:           kube-dns
Namespace:      kube-system
Labels:         component=kube-dns
            k8s-app=kube-dns
            kubernetes.io/cluster-service=true
            name=kube-dns
            tier=node
Selector:       name=kube-dns
Type:           ClusterIP
IP:         100.64.0.10
Port:           dns 53/UDP
Endpoints:      10.46.0.1:53
Port:           dns-tcp 53/TCP
Endpoints:      10.46.0.1:53
Session Affinity:   None
No events.

Name:           kubernetes-dashboard
Namespace:      kube-system
Labels:         app=kubernetes-dashboard
Selector:       app=kubernetes-dashboard
Type:           NodePort
IP:         100.70.61.228
Port:           <unset> 80/TCP
NodePort:       <unset> 30054/TCP
Endpoints:      10.40.0.1:9090
Session Affinity:   None

我做實驗的時候kubernetes-dashboard的NodePort是30054/TCP,你看到的NodePort應該和我這裏不同。通過瀏覽器firefox訪問http://master0.example.com:30054就可以看到Kubernetes UI的界面了。生產環境下需要考慮安全性,應該在前端加認證代理。

001-kubernetes-dashboard

在這裏Kubernetes 1.4中有個Bug,本來我們是可以通過https://master0.example.com/ui訪問Kubernetes UI的,但是由於代碼的兼容問題,導致訪問的時候會回顯”Unauthorized“信息。

管理 Kubernetes 集羣服務

從簡單的WEB服務入手學習

kubectl命令介紹

我們後續的操作都是使用kubectl命令來完成的,並使用kubernetes-dashboard WEB UI驗證。我們看看如何使用kubectl命令。

首先kubectl命令默認只能在Master節點上運行

[root@master0 ~]# kubectl cluster-info
Kubernetes master is running at http://localhost:8080
kube-dns is running at http://localhost:8080/api/v1/proxy/namespaces/kube-system/services/kube-dns

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

如果要在Node節點上運行,默認會由於無法連接Kubernetes管理服務而報錯

[root@nodea0 ~]# kubectl cluster-info
The connection to the server localhost:8080 was refused - did you specify the right host or port?

如果你希望在Node節點上正確的執行kubectl,需要將Master節點上的admin.conf文件拷貝到Node節點上,並明確指定admin.conf的路徑。這種操作由於安全性問題不推薦。

[root@master0 ~]# scp /etc/kubernetes/admin.conf root@nodea0:~/
root@nodea0's password:
admin.conf                                    100% 9190     9.0KB/s   00:00

[root@nodea0 ~]# kubectl --kubeconfig=./admin.conf cluster-info
Kubernetes master is running at https://172.25.0.10:443
kube-dns is running at https://172.25.0.10:443/api/v1/proxy/namespaces/kube-system/services/kube-dns

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

請注意上例中的主機名,請替換爲你自己的設備號。

kubectl命令的子命令和操作參數比較多,在使用kubectl操作Kubernetes環境之前,我們先羅列出其常用的子命令和操作參數。

kubectl 常用子命令列表:

命令 說明
get 顯示資源信息
describe 詳細的描述資源信息
create 通過指定的文件或URL創建資源
update 通過指定的文件或URL修改更新資源
delete 通過指定的文件、URL、資源ID或標籤刪除資源
namespace 設置或顯示指定的命名空間(已經棄用)
logs 打印在某Pod中的容器日誌信息
rolling-update 對給定的副本控制器(ReplicationController)執行滾動更新
scale 調整副本管理器(ReplicationController)副本數
exec 在某容器中執行命令,類似Docker的exec命令
port-forward 爲某Pod設置一個或多個端口轉發
proxy 運行Kubernetes API Server代理
run 在機器中運行一個獨立的鏡像
stop 通過ID或資源名刪除一個資源(已經棄用)
expose 將資源對象暴露爲Kubernetes Server
label 修改某個資源上的標籤
config 修改集羣的配置信息
cluster-info 顯示集羣信息
api-versions 顯示API版本信息
version 打印kubectl和API Server版本信息
help 幫助命令

kubectl 常用可操作資源對象列表:

資源對象名稱 縮寫
deployments de
endpoints ep
horizontalpodautoscalers hpa
ingresses ing
limitranges limits
nodes no
namespaces ns
pods po
persistentvolumes pv
persistentvolumecails pvc
resourcequotas quota
replicationcontrollers rc
services svc
deployments null
jobs null
secrets null
serviceaccounts null

kubectl 常用命令行公共參數:

參數 說明
--alsologtostderr[=false] 同時輸出日誌到標準錯誤控制檯和文件
--certificate-authority="" 用以進行認證授權的.cert文件路徑
--client-certificate="" TLS使用的客戶端證書路徑
--client-key="" TLS使用的客戶端密鑰路徑
--cluster="" 指定使用的kubeconfig配置文件中的集羣名
--context="" 指定使用的kubeconfig配置文件中的環境名
--insecure-skip-tls-verify[=false]: 如果爲true,將不會檢查服務器憑證的有效性,這會導致你的HTTPS鏈接變得不安全
--kubeconfig="" 命令行請求使用的配置文件路徑
--log-backtrace-at=:0 當日志長度超過定義的行數時,忽略堆棧信息
--log-dir="" 如果不爲空,將日誌文件寫入此目錄
--log-flush-frequency=5s 刷新日誌的最大時間間隔
--logtostderr[=true] 輸出日誌到標準錯誤控制檯,不輸出到文件
--match-server-version[=false] 要求服務端和客戶端版本匹配
--namespace="" 如果不爲空,命令將使用指定namespace
--password="" API Server進行簡單認證使用的密碼
-s, --server="" Kubernetes API Server的地址和端口號
--stderrthreshold=2 高於此級別的日誌將被輸出到錯誤控制檯
--token="" 認證到API Server使用的令牌
--user="" 指定使用的kubeconfig配置文件中的用戶名
--username="" API Server進行簡單認證使用的用戶名
--v=0 指定輸出日誌的級別
--vmodule= 指定輸出日誌的模塊,格式如下:pattern=N,使用逗號分隔

創建簡單Pod

Kubernetes創建對象的時候一般是使用預定義文件編寫對象信息說明,然後使用kubectl工具或kubernetes-dashboard UI來創建。由於我們在內網完成此操作,所以需要預先將鏡像導入所有node節點,如果你是在公網環境下,則不需要預先導入鏡像。

  1. 下載鏡像文件壓縮包
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "wget http://cla***oom.example.com/materials/k8s-imgs/nginx-img.tbz " ; done
  1. 在節點上解開壓縮包
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf nginx-img.tbz " ; done
  1. 將鏡像導入節點系統
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./nginx/*.img ; do docker load -i $i  ; done' ; done

我們以nginx web應用爲例創建我們第一個Pod。以下是創建Pod的yaml文件內容:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80

將文件保存爲nginx-pod.yaml,後面我們將使用”kubectl create“命令來創建nginx Pod。文件中的標籤我們後面具體介紹。

# 查看配置文件內容
[root@master0 ~]# cat nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:latest
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80

# 創建Pod nginx
[root@master0 ~]# kubectl create -f nginx-pod.yaml
pod "nginx" created

# 查看Pod 狀態和信息
[root@master0 ~]# kubectl get pod
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   0          31s

# 查看Pod nginx的詳細信息
[root@master0 ~]# kubectl describe pod nginx
Name:       nginx
Namespace:  default
Node:       nodea0.example.com/172.25.0.11
Start Time: Fri, 28 Oct 2016 08:39:58 +0800
Labels:     <none>
Status:     Running
IP:     10.32.0.2
Controllers:    <none>
Containers:
  nginx:
    Container ID:   docker://76ff103f17a4024c94e3d28d2a98814bc943be8626216965e9be43726179d37c
    Image:      nginx:latest
    Image ID:       docker://sha256:e43d811ce2f4986aa69bc8ba6c92f0789537f604d1601e0b6ec024e1c38062b4
    Port:       80/TCP
    State:      Running
      Started:      Fri, 28 Oct 2016 08:40:28 +0800
    Ready:      True
    Restart Count:  0
    Volume Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-d6l71 (ro)
    Environment Variables:  <none>
Conditions:
  Type      Status
  Initialized   True
  Ready     True
  PodScheduled  True
Volumes:
  default-token-d6l71:
    Type:   Secret (a volume populated by a Secret)
    SecretName: default-token-d6l71
QoS Class:  BestEffort
Tolerations:    <none>
Events:
  FirstSeen LastSeen    Count   From                SubobjectPath       Type        Reason      Message
  --------- --------    -----   ----                -------------       --------    ------      -------
  1m        1m      1   {default-scheduler }            Normal      Scheduled   Successfully assigned nginx to nodea0.example.com
  55s       55s     1   {kubelet nodea0.example.com}    spec.containers{nginx}  Normal      Pulling     pulling image "nginx:latest"
  35s       35s     1   {kubelet nodea0.example.com}    spec.containers{nginx}  Normal      Pulled      Successfully pulled image "nginx:latest"
  32s       32s     1   {kubelet nodea0.example.com}    spec.containers{nginx}  Normal      Created     Created container with docker id 76ff103f17a4; Security:[seccomp=unconfined]
  31s       31s     1   {kubelet nodea0.example.com}    spec.containers{nginx}  Normal      Started     Started container with docker id 76ff103f17a4

目前創建的Pod是無法在外部訪問的,在配置文件中我們指定的80/tcp端口只能Kubernetes內指定運行的其他Pod訪問。爲能夠訪問這個nginx,我們還要創建一個基本容器(busybox),使用這個基本容器來測試Pod的訪問狀態。

[root@master0 ~]# kubectl run busybox --image=busybox:latest --restart=Never --tty -i --generator=run-pod/v1 --env="POD_IP=$(kubectl get pod nginx -o go-template='{{.status.podIP}}')"
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
Waiting for pod default/busybox to be running, status is Pending, pod ready: false
If you don't see a command prompt, try pressing enter.
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 9E:D6:D2:44:B5:9A
          inet addr:10.40.0.3  Bcast:0.0.0.0  Mask:255.240.0.0
          inet6 addr: fe80::9cd6:d2ff:fe44:b59a/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1410  Metric:1
          RX packets:10 errors:0 dropped:0 overruns:0 frame:0
          TX packets:4 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:816 (816.0 B)  TX bytes:300 (300.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # echo $POD_IP
10.32.0.3
/ # wget -qO- http://$POD_IP
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
/ # exit
Waiting for pod default/busybox to terminate, status is Running

以上操作中,我們通過kubectl的run命令創建了一個Pod busybox,並創建busybox容器的虛擬控制檯,通過虛擬控制檯查看busybox容器的IP,並連接nginx容器查看其狀態。具體的參數我們後面會逐一介紹。

操作完畢後刪除臨時創建的Pod busybox。

[root@master0 ~]# kubectl delete pod busybox
pod "busybox" deleted

Pod的狀態包括以下幾種:

狀態 說明
Pending 已經創建該Pod,但Pod中還有若干容器沒有完成運行,可能正在下載容器鏡像
Running Pod中所有容器已經運行,包括容器啓動狀態或重啓狀態
Succeeded Pod中所有容器成功退出,且不會繼續重啓
Failed Pod中所有容器已經退出,但容器爲失敗狀態,可能由於無法下載容器鏡像
Unknown 由於某種原因無法獲取Pod狀態,可能由於Master和Node網絡通信不暢

設置Pod Label

同樣的步驟我們可以創建更多的Pod,但如何去區分Pod呢? 我們使用之前的講過的概念—Label。

將原有的nginx-pod.yaml文件在本地拷貝爲web-pod.yaml,並添加label標籤項目。

apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
     app: nginx-web
spec:
  containers:
  - name: nginx
    image: nginx:latest
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80

接下來和前面創建Pod nginx一樣,我們可以創建Pod web ,和Pod nginx不同,初始化創建的時候,我們就設置了其Label,爲“app:nginx-web”。

創建Pod web
[root@master0 ~]# kubectl create -f web-pod.yaml
pod "web" created

查看Pod 的狀態和信息
[root@master0 ~]# kubectl get pod
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   1          7d
web       1/1       Running   0          13s

查看Pod web 的詳細信息
[root@master0 ~]# kubectl describe pod web
Name:       web
Namespace:  default
Node:       nodea0.example.com/172.25.0.11
Start Time: Fri, 04 Nov 2016 14:03:12 +0800
Labels:     app=nginx-web
Status:     Running
IP:     10.32.0.4
Controllers:    <none>
Containers:
  nginx:
    Container ID:   docker://06c006624e46532ae8daee0683b533225a4c69f26302a7387d062b0d3627522e
    Image:      nginx:latest
    Image ID:       docker://sha256:e43d811ce2f4986aa69bc8ba6c92f0789537f604d1601e0b6ec024e1c38062b4
    Port:       80/TCP
    State:      Running
      Started:      Fri, 04 Nov 2016 14:03:23 +0800
    Ready:      True
    Restart Count:  0
    Volume Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-d6l71 (ro)
    Environment Variables:  <none>
Conditions:
  Type      Status
  Initialized   True
  Ready     True
  PodScheduled  True
Volumes:
  default-token-d6l71:
    Type:   Secret (a volume populated by a Secret)
    SecretName: default-token-d6l71
QoS Class:  BestEffort
Tolerations:    <none>
Events:
  FirstSeen LastSeen    Count   From                SubobjectPath       Type        Reason      Message
  --------- --------    -----   ----                -------------       --------    ------      -------
  7m        7m      1   {default-scheduler }                    Normal      Scheduled   Successfully assigned web to nodea0.example.com
  7m        7m      1   {kubelet nodea0.example.com}    spec.containers{nginx}  Normal      Pulling     pulling image "nginx:latest"
  7m        7m      1   {kubelet nodea0.example.com}    spec.containers{nginx}  Normal      Pulled      Successfully pulled image "nginx:latest"
  7m        7m      1   {kubelet nodea0.example.com}    spec.containers{nginx}  Normal      Created     Created container with docker id 06c006624e46; Security:[seccomp=unconfined]
  7m        7m      1   {kubelet nodea0.example.com}    spec.containers{nginx}  Normal      Started     Started container with docker id 06c006624e46

可以發現,相對Pod nginx,在Pod web詳細信息中Lables欄目中有“app=nginx-web”,而Pod nginx詳細信息中Lables欄目爲"<none>"。

我們可以通過kubectl -l 參數通過指定Label 信息指定Pod。

[root@master0 ~]# kubectl get pod
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   1          7d
web       1/1       Running   0          15m

[root@master0 ~]# kubectl get pod  -l app=nginx-web
NAME      READY     STATUS    RESTARTS   AGE
web       1/1       Running   0          15m

我們要給已經創建的Pod nginx設置Label也很容易,使用kubectl lable 命令就可以了。

[root@master0 ~]# kubectl label pod nginx app=nginx-test
pod "nginx" labeled

如果要覆蓋一個已經設置的Label 需要在kubectl label 命令後加上—overwrite參數。

[root@master0 ~]# kubectl label --overwrite pod nginx app=nginx-foo
pod "nginx" labeled

如果要刪除一個Pod 的Label,需要的僅僅是在label的名字後加上”-“號。

[root@master0 ~]# kubectl label  pod nginx app-
pod "nginx" labeled

創建多容器Pod

在同一個Pod中,我們可以創建多個相互關聯的容器,在容器之間可以通過Volume共享數據。

接下來的例子中,我們將使用到nginx:latest和busybox:latest兩個鏡像,並建立一個emptyDir類型的Volume,用以共享存儲。

首先我們要創建一個多容器Pod配置文件:

apiVersion: v1
kind: Pod
metadata:
  name: multicontainer-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    imagePullPolicy: IfNotPresent
    ports:
    - containerPort: 80
    volumeMounts: # spec.volumeMounts[] 設置掛接卷
    - name: htmlroot # spec.volumeMounts[].name 掛接卷名稱,由spec.volumes[].name定義
      mountPath: /usr/share/nginx/html # 掛接到容器路徑
  - name: busybox
    image: busybox:latest
    imagePullPolicy: IfNotPresent
    command: ["sh","-c","while : ; do sleep 10 ; done"] # 容器執行命令
    volumeMounts:
    - name: htmlroot
      mountPath: /mnt
  volumes: # spec.volumes[] 定義卷類型
  - name: htmlroot # spec.volumes[].name 定義卷名稱,容器掛接卷時引用
    emptyDir: {} # 卷類型

將多容器Pod配置文件保存爲multicontainer_pod.yaml ,在Master上使用kubectl創建:

[root@master0 ~]# kubectl create -f multicontainer_pod.yaml
pod "multicontainer-pod" created

等待pod被創建成功後,我們可以查看當前多容器Pod的狀態:

[root@master0 ~]# kubectl get pod multicontainer-pod
NAME                 READY     STATUS    RESTARTS   AGE
multicontainer-pod   2/2       Running   0          1m
[root@master0 ~]# kubectl describe pod multicontainer-pod
Name:       multicontainer-pod
Namespace:  default
Node:       nodea0.example.com/172.25.0.11
Start Time: Tue, 22 Nov 2016 16:26:16 +0800
Labels:     <none>
Status:     Running
IP:     10.40.0.1
Controllers:    <none>
Containers:
  nginx:
    Container ID:   docker://4ee4004da7e92b15da43af86947a6a4d649207c5e9d95f0a1ffe88e9a0655306
    Image:      nginx:latest
    Image ID:       docker://sha256:05a60462f8bafb215ddc5c20a364b5fb637670200a74a5bb13a1b23f64515561
    Port:       80/TCP
    State:      Running
      Started:      Tue, 22 Nov 2016 16:26:27 +0800
    Ready:      True
    Restart Count:  0
    Volume Mounts:
      /usr/share/nginx/html from htmlroot (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-k5azo (ro)
    Environment Variables:  <none>
  busybox:
    Container ID:   docker://a3831df2aa83f55b90f0e54dbc75026d2d28721803e3e754f3c6f6076db53645
    Image:      busybox:latest
    Image ID:       docker://sha256:e02e811dd08fd49e7f6032625495118e63f597eb150403d02e3238af1df240ba
    Port:
    Command:
      sh
      -c
      while : ; do sleep 10 ; done
    State:      Running
      Started:      Tue, 22 Nov 2016 16:26:33 +0800
    Ready:      True
    Restart Count:  0
    Volume Mounts:
      /mnt from htmlroot (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-k5azo (ro)
    Environment Variables:  <none>
Conditions:
  Type      Status
  Initialized   True
  Ready     True
  PodScheduled  True
Volumes:
  htmlroot:
    Type:   EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
  default-token-k5azo:
    Type:   Secret (a volume populated by a Secret)
    SecretName: default-token-k5azo
QoS Class:  BestEffort
Tolerations:    <none>
Events:
  FirstSeen LastSeen    Count   From                SubobjectPath           Type        Reason      Message
  --------- --------    -----   ----                -------------           --------    ------      -------
  1m        1m      1   {default-scheduler }                        Normal      Scheduled   Successfully assigned multicontainer-pod to nodea0.example.com
  1m        1m      1   {kubelet nodea0.example.com}    spec.containers{nginx}      Normal      Pulled      Container image "nginx:latest" already present on machine
  1m        1m      1   {kubelet nodea0.example.com}    spec.containers{nginx}      Normal      Created     Created container with docker id 4ee4004da7e9; Security:[seccomp=unconfined]
  1m        1m      1   {kubelet nodea0.example.com}    spec.containers{nginx}      Normal      Started     Started container with docker id 4ee4004da7e9
  1m        1m      1   {kubelet nodea0.example.com}    spec.containers{busybox}    Normal      Pulled      Container image "busybox:latest" already present on machine
  1m        1m      1   {kubelet nodea0.example.com}    spec.containers{busybox}    Normal      Created     Created container with docker id a3831df2aa83; Security:[seccomp=unconfined]
  1m        1m      1   {kubelet nodea0.example.com}    spec.containers{busybox}    Normal      Started     Started container with docker id a3831df2aa83

我們可以通過kubectl exec 命令連接Pod中的容器,查看並測試共享卷。

# 連接multicontainer-pod中的busybox容器
[root@master0 ~]# kubectl exec multicontainer-pod -c busybox -it /bin/sh

# 使用df 查看磁盤掛接情況,請注意/mnt目錄掛接情況
/ # df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/mapper/docker-253:0-669894-c8bc33b61e5399bfb777d2d306ce707322234fa8217c188bacc3389b0cc3dbef
                      10474496     34672  10439824   0% /
tmpfs                   508396         0    508396   0% /dev
tmpfs                   508396         0    508396   0% /sys/fs/cgroup
/dev/mapper/rhel-root
                       9226240   4034764   5191476  44% /mnt
/dev/mapper/rhel-root
                       9226240   4034764   5191476  44% /dev/termination-log
/dev/mapper/rhel-root
                       9226240   4034764   5191476  44% /etc/resolv.conf
/dev/mapper/rhel-root
                       9226240   4034764   5191476  44% /etc/hostname
/dev/mapper/rhel-root
                       9226240   4034764   5191476  44% /etc/hosts
shm                      65536         0     65536   0% /dev/shm
tmpfs                   508396        12    508384   0% /var/run/secrets/kubernetes.io/serviceaccount
tmpfs                   508396         0    508396   0% /proc/kcore
tmpfs                   508396         0    508396   0% /proc/timer_list
tmpfs                   508396         0    508396   0% /proc/timer_stats
tmpfs                   508396         0    508396   0% /proc/sched_debug

# 我們進入 /mnt 目錄後創建 index.html文件,並寫入測試字符串
/ # cd /mnt
/mnt # ls
/mnt # echo "<h1>busybox</hi>" > index.html
/mnt # exit

退出busybox容器,再連接nginx容器:

[root@master0 ~]# kubectl exec multicontainer-pod -c nginx -it /bin/sh
# df
Filesystem                                                                                       1K-blocks    Used Available Use% Mounted on
/dev/mapper/docker-253:0-669894-985f5295e7838bcf7b60c163493cd010b1a5665a9ab46955ffdc6f7cadbf8f66  10474496  232540  10241956   3% /
tmpfs                                                                                               508396       0    508396   0% /dev
tmpfs                                                                                               508396       0    508396   0% /sys/fs/cgroup
/dev/mapper/rhel-root                                                                              9226240 4035728   5190512  44% /etc/hosts
shm                                                                                                  65536       0     65536   0% /dev/shm
tmpfs                                                                                               508396      12    508384   1% /run/secrets/kubernetes.io/serviceaccount
# cd /usr/share/nginx/html
# ls
index.html
# cat index.html
<h1>busybox</hi>
# exit

可以看出多容器Pod中的nginx和busybox容器共享了一個存儲卷。

測試成功後,不要忘記做清除操作:

[root@master0 ~]# kubectl delete  -f multicontainer_pod.yaml
pod "multicontainer-pod" deleted

創建簡單Deployment

我們可以通過一次配置啓動多個Pod ,並且保證當Pod損壞或銷燬時可以自動重建。這就需要Depolyment來完成。

首先創建nginx-deployment.yaml 文件,文件內容如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2 
  template: 
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

此文件指定了Deployment 的名字爲nginx-deployment,將創建兩個replicas,也就是將有兩個新的Pod。

運行kubectl create命令,創建Deloyment 。

[root@master0 ~]# kubectl create -f nginx-deployment.yaml
deployment "nginx-deployment" created

創建成功後可以看到:

[root@master0 ~]# kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            0           13s

同時有兩個新的Pod被創建出來:

root@master0 ~]# kubectl get pod
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-2947857529-3dpz6   1/1       Running   0          25s
nginx-deployment-2947857529-xt8gd   1/1       Running   0          25s

如果你沒有清除之前創建的Pod ,可以通過Label來分揀這兩個新的Pod:

[root@master0 ~]# kubectl get pod -l app=nginx
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-2947857529-3dpz6   1/1       Running   0          55s
nginx-deployment-2947857529-xt8gd   1/1       Running   0          55s

當然,我們可以是直接使用kubectl run 命令來創建Deployment。

[root@master0 ~]# kubectl run run-nginx --image=nginx:latest --replicas=2 --labels=app=nginx-run --port=80
deployment "run-nginx" created

查看Deployment信息:

[root@master0 ~]# kubectl get deployment run-nginx
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
run-nginx          2         2         2            2           55s

查看創建的Pod信息:

[root@master0 ~]# kubectl get pod 
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-2947857529-3dpz6   1/1       Running   0          5m
nginx-deployment-2947857529-xt8gd   1/1       Running   0          5m
run-nginx-1357102973-02c4z          1/1       Running   0          1m
run-nginx-1357102973-c7pkm          1/1       Running   0          1m

也可以根據Label分揀Pod:

[root@master0 ~]# kubectl get pod -l app=nginx-run
NAME                         READY     STATUS    RESTARTS   AGE
run-nginx-1357102973-02c4z   1/1       Running   0          2m
run-nginx-1357102973-c7pkm   1/1       Running   0          2m

爲了下次創建Deployment更加方便,我們可以將現在運行的Deployment的創建信息導出爲yaml格式文件:

[root@master0 ~]# kubectl --export  -o yaml get deployment run-nginx | tee nginx-run.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: null
  generation: 1
  labels:
    app: nginx-run
  name: run-nginx
  selfLink: /apis/extensions/v1beta1/namespaces//deployments/run-nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-run
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: nginx-run
    spec:
      containers:
      - image: nginx:latest
        imagePullPolicy: IfNotPresent
        name: run-nginx
        ports:
        - containerPort: 80
          protocol: TCP
        resources: {}
        terminationMessagePath: /dev/termination-log
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      securityContext: {}
      terminationGracePeriodSeconds: 30
status: {}

之後我們需要再次創建或在其他的集羣環境中創建此Deployment,就可以使用kubectl create命令來完成了。

我們可以完成以下步驟測試刪除Deployment run-nginx然後通過導出的yaml文件重新創建run-nginx。

[root@master0 ~]# kubectl delete deployment run-nginx
deployment "run-nginx" deleted

[root@master0 ~]# kubectl create  -f nginx-run.yaml
deployment "run-nginx" created

[root@master0 ~]# kubectl get deployment run-ngix
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
run-nginx          2         2         2            2           24s

創建簡單Service

在我們已經創建Pod和Deployment的基礎上,我們可以創建Service以使外部用戶可以訪問到Kubernetes內的應用服務。

首先創建nginx-server.yaml 文件,文件內容如下:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 8000
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx
  type: LoadBalancer

指定Service名爲nginx-service,設置集羣內部端口爲8000,對應到Pod中的80端口,協議類型爲TCP,選擇器指定Lable爲app=nginx的Deployment,使用負載均衡的方式訪問Pod。

運行kubectl create命令,創建Service:

[root@master0 ~]# kubectl create -f nginx-service.yaml

查看當前的Service狀態:

[root@master0 ~]# kubectl get service
NAME            CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes      100.64.0.1      <none>        443/TCP    12d
nginx-service   100.68.22.181   <pending>     8000/TCP   1m

查看nginx-service服務的具體情況:

[root@master0 ~]# kubectl describe service nginx-service
Name:           nginx-service
Namespace:      default
Labels:         <none>
Selector:       app=nginx
Type:           LoadBalancer
IP:         100.68.22.181
Port:           <unset> 8000/TCP
NodePort:       <unset> 32690/TCP
Endpoints:      10.32.0.13:80,10.40.0.9:80
Session Affinity:   None

NodePort就是我們可以通過外部訪問到的服務端口,可以通過curl或firefox訪問確認。Session Affinity是指定是否保持Session,後面的課程中我們會深入的介紹。

[root@foundation0 ~]# curl http://master0.example.com:32690
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

目前nginx-service是由nginx-deployment提供資源,而nginx-deployment是由兩個Pod組成的。

[root@master0 ~]# kubectl get deployment -l app=nginx
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            2           4d
[root@master0 ~]# kubectl get pods -l app=nginx
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-2947857529-3dpz6   1/1       Running   2          4d
nginx-deployment-2947857529-xt8gd   1/1       Running   2          4d

我們可以通過修改兩個Pod上nginx的主頁來查看Service輪訓負載均衡的狀態:

[root@master0 ~]# kubectl exec -it nginx-deployment-2947857529-3dpz6 /bin/bash
root@nginx-deployment-2947857529-3dpz6:/# echo "<h1>Pod A</h1>" > /usr/share/nginx/html/index.html
root@nginx-deployment-2947857529-3dpz6:/# exit
exit
[root@master0 ~]# kubectl exec -it nginx-deployment-2947857529-xt8gd /bin/bash
root@nginx-deployment-2947857529-xt8gd:/# echo "<h1>Pod B</h1>" > /usr/share/nginx/html/index.html
root@nginx-deployment-2947857529-xt8gd:/# exit
exit

在外部機器上使用curl或firefox你可以查看到訪問是輪訓的:

[root@foundation0 ~]# curl http://master0.example.com:32690
<h1>Pod B</h1>
[root@foundation0 ~]# curl http://master0.example.com:32690
<h1>Pod A</h1>
[root@foundation0 ~]# curl http://master0.example.com:32690
<h1>Pod A</h1>
[root@foundation0 ~]# curl http://master0.example.com:32690
<h1>Pod A</h1>
[root@foundation0 ~]# curl http://master0.example.com:32690
<h1>Pod B</h1>

爲方便後續內容的介紹和實驗,我們可以將創建的Service、Deployment和Pod盡數刪除。

[root@master0 ~]# kubectl delete service nginx-service
service "nginx-service" deleted

[root@master0 ~]# kubectl delete deployment nginx-deployment
deployment "nginx-deployment" deleted

[root@master0 ~]# kubectl delete deployment run-nginx
deployment "run-nginx" deleted

[root@master0 ~]# kubectl delete pod nginx
pod "nginx" deleted

[root@master0 ~]# kubectl delete pod web
pod "web" deleted

同時創建Deployment和Service

分別創建Pod、Deployment和Service既麻煩又不利於關聯性,所以在生產環境中,我們會將所以得對象創建整合在一個配置文件中定義,並通過統一的配置文件來創建Service。

在上一節中,我們按部就班的創建了nginx-service服務,並且在測試成功後刪除了所有自定對象。目前kubernetes環境下沒有任何自定義對象了:

[root@master0 ~]# kubectl get pod
[root@master0 ~]# kubectl get deployment
[root@master0 ~]# kubectl get service
NAME         CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   100.64.0.1   <none>        443/TCP   12d

我們可以將前面創建Deployment和Service的配置文件修改併合並起來成爲新的配置文件my-nginx.yaml:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 8000
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx
  type: LoadBalancer

注意:在兩個不同部分之間需要使用三個“-”號組成的間隔符號用以區分不同的設置區域。

使用my-nginx.yaml配置文件創建運行環境:

[root@master0 ~]# kubectl create -f my-nginx.yaml
deployment "nginx-deployment" created
service "nginx-service" created

可以看出kubectl還是根據配置文件分別創建Deployment和Service的。

我們仍然可以通過kubectl查看當前的Pod、Deployment和Service狀態,並獲得訪問Service的外部端口。其操作和之前單獨建立Service時沒有區別。

創建複雜的多Service微服務環境

部署鏡像

複雜的微服務環境需要多個鏡像來完成環境搭建,他們分爲前端web應用和後端數據庫應用。我們在此例子中我們將完成一個留言板應用微服務,它是由前端php應用和後端redis數據庫組成,並且爲了保證業務的正常運行,我們可以設置前端和後端運行多副本的方式。

前端服務鏡像爲kissingwolf/guestbook-frontend,後端服務鏡像爲kissingwolf/redis-mater和kissingwolf/redis-slave。這個微服務是建立在前端php應用鏈接redis數據庫的基礎上的。

  1. 下載鏡像文件壓縮包
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "wget http://cla***oom.example.com/materials/k8s-imgs/guestbook-img.tbz " ; done
  1. 在節點上解開壓縮包
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf guestbook-img.tbz " ; done
  1. 將鏡像導入節點系統
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./guestbook/*.img ; do docker load -i $i  ; done' ; done

如果在公網上實驗,可以跳過部署鏡像的步驟,所有鏡像均可以在hub.docker.com上找到並通過kubernetes自動下載。

編寫微服務部署配置文件

我們將要建立的是一個線上留言板微服務,此微服務包括三個kubernetes Service:redis-master、redis-slave和frontend。

apiVersion: v1
kind: Service
metadata:
  name: redis-master
  labels:
    app: redis
    tier: backend
    role: master
spec:
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: redis
    tier: backend
    role: master
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-master
spec:
  template:
    metadata:
      labels:
        app: redis
        role: master
        tier: backend
    spec:
      containers:
      - name: master
        image: kissingwolf/redis-master:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
  name: redis-slave
  labels:
    app: redis
    tier: backend
    role: slave
spec:
  ports:
  - port: 6379
  selector:
    app: redis
    tier: backend
    role: slave
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-slave
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: redis
        role: slave
        tier: backend
    spec:
      containers:
      - name: slave
        image: kissingwolf/redis-slave:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  type: LoadBalancer
  ports:
  - port: 80
  selector:
    app: guestbook
    tier: frontend
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: kissingwolf/guestbook-frontend:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 80

將以上內容保存爲guestbook.yaml文件,放置到master0主機上,請使用自己的foundation號替換0。

此文件由三個連續“-”符號分爲了六個部分:三個Service塊和三個Deployment塊,我們分別介紹其作用。

redis-mater Service塊:

apiVersion: v1 # api版本
kind: Service # 配置類型
metadata: # 元信息配置
  name: redis-master # metadata.name Service名稱,需符合命名規範
  labels: # metadata.labels[] 自定義標籤屬性列表
    app: redis # 自定義標籤屬性
    tier: backend # 自定義標籤屬性
    role: master # 自定義標籤屬性
spec: # 詳細描述,用以配置Service
  ports: # spec.ports[] Service 需要暴露的端口號列表
  - port: 6379 # spec.ports[].port Service 監聽的端口號,默認爲TCP
    targetPort: 6379 # spec.ports[].targetPort 需要轉發到後端的端口號,默認爲TCP,如果與port一致可以省略不寫。
  selector: # spec.selector[] 標籤選擇器,將符合標籤的Deployment關聯Service
    app: redis # 查找的目標標籤屬性
    tier: backend # 查找的目標標籤屬性
    role: master # 查找的目標標籤屬性

redis-master Deployment塊:

apiVersion: extensions/v1beta1 # api版本
kind: Deployment # 配置類型
metadata: # 元信息配置
  name: redis-master # metadata.name Deployment名稱,需符合命名規範
spec: # 詳細描述,用以配置Deployment
  template: # spec.template 容器的定義,此部分和Pod定義的內容基本一致
    metadata: # spec.template.metadata 元數據配置
      labels: # spec.template.metadata.labels[] 自定義標籤屬性列表
        app: redis # 自定義標籤屬性
        role: master # 自定義標籤屬性
        tier: backend # 自定義標籤屬性
    spec: # spec.template.spce 容器的詳細描述
      containers: # spec.template.spce.containers[] Pod中運行容器的詳細列表
      - name: master # spec.template.spce.containers[].name 容器名稱
        image: kissingwolf/redis-master:latest # .image 容器鏡像名稱
        imagePullPolicy: IfNotPresent # .imagePullPolicy 獲得鏡像的策略
        ports: # spec.template.spce.containers[].ports[] 容器需要暴露的端口號列表
        - containerPort: 6379 # .containerPort 容器需要監聽的端口號,默認爲TCP

redis-slave Service塊,與redis-master Service基本一致:

apiVersion: v1
kind: Service
metadata:
  name: redis-slave
  labels:
    app: redis
    tier: backend
    role: slave
spec:
  ports:
  - port: 6379
  selector:
    app: redis
    tier: backend
    role: slave

redis-slave Deployment塊,與redis-master Depolyment 定義相仿的地方我們就不再囉嗦了:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: redis-slave
spec:
  replicas: 2 # spec.replicas 設置Pod的副本數,如果設置爲0,則不創建Pod
  template:
    metadata:
      labels:
        app: redis
        role: slave
        tier: backend
    spec:
      containers:
      - name: slave
        image: kissingwolf/redis-slave:latest
        imagePullPolicy: IfNotPresent
        env: # spec.template.spec.containers[].env[] 容器運行前需要設置的環境變量列表
        - name: GET_HOSTS_FROM # 容器內環境變量名
          value: dns # 設置變量對應的值
        ports:
        - containerPort: 6379

容器化管理的理念是容器內運行的程序和結構與容器環境管理者無關!我們不用在乎容器內的配置和運行程序安裝、運行和處理的問題,容器對應容器環境的管理者應該是個黑盒子,容器管理者應該像執行一條命令一樣運行一個容器。在這個例子中,我們不用關心redis-master和redis-slave是如何通訊的,就好像我們不需要關心兩個程序是如何由管道符號“|”連接起輸入輸出的。

frontend Service 塊:

apiVersion: v1
kind: Service
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  type: LoadBalancer # spec.type 指定Service的訪問方式,LoadBalancer指定kube-proxy完成負載分發,默認會在每個node上指定一個相同的30000以上端口監聽。
  ports:
  - port: 80
  selector:
    app: guestbook
    tier: frontend    

frontend Deployment 塊:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: frontend
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: guestbook
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: kissingwolf/guestbook-frontend:latest
        imagePullPolicy: IfNotPresent
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 80

綜上,我們在配置中指定將創建三個服務,redis-master服務創建1個pod,redis-slave服務創建2個pod,而前端web服務frontend創建3個pod。可以充分做到冗餘和負載均衡。

啓動微服務

編寫好guestbook.yaml配置文件後,可以使用kubectl create命令創建這個由三個自服務組成的微服務。

[root@master0 ~]# kubectl create -f guestbook.yaml
service "redis-master" created
deployment "redis-master" created
service "redis-slave" created
deployment "redis-slave" created
service "frontend" created
deployment "frontend" created

我們可以同過kubectl get 命令查看Pod、Deployment和Service的狀態:

[root@master0 ~]# kubectl get pods
NAME                            READY     STATUS    RESTARTS   AGE
frontend-2027482420-7viu0       1/1       Running   0          2m
frontend-2027482420-ai4zb       1/1       Running   0          2m
frontend-2027482420-g8mfr       1/1       Running   0          2m
redis-master-2712522894-8t10c   1/1       Running   0          2m
redis-slave-2928339718-kkuio    1/1       Running   0          2m
redis-slave-2928339718-ysakf    1/1       Running   0          2m
[root@master0 ~]# kubectl get deployment
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
frontend       3         3         3            3           3m
redis-master   1         1         1            1           3m
redis-slave    2         2         2            2           3m
[root@master0 ~]# kubectl get service
NAME           CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
frontend       100.65.209.142   <pending>     80/TCP     4m
kubernetes     100.64.0.1       <none>        443/TCP    13d
redis-master   100.74.117.129   <none>        6379/TCP   4m
redis-slave    100.71.128.170   <none>        6379/TCP   4m

訪問微服務

當前我們Guestbook微服務中,負責處理用戶請求的前端服務frontend註冊到kubernetes後,kube-proxy將代理其監聽請求,並開啓一個30000以上端口的監聽。

[root@master0 ~]# kubectl describe service frontend
Name:           frontend
Namespace:      default
Labels:         app=guestbook
                tier=frontend
Selector:       app=guestbook,tier=frontend
Type:           LoadBalancer
IP:         100.65.209.142
Port:           <unset> 80/TCP
NodePort:       <unset> 31876/TCP
Endpoints:      10.32.0.18:80,10.32.0.19:80,10.40.0.15:80
Session Affinity:   None

NodePort 指定的就是kube-proxy監聽的端口,frontend服務目前的端口是31876/TCP,你機器上會有不同。在master和每個node上都有。

[root@foundation0 ~]# for i in master0 nodea0 nodeb0 ; do ssh root@$i "hostname ; netstat -natp |grep ':31876' " ; done 
master0.example.com
tcp6       0      0 :::31876                :::*                    LISTEN      3679/kube-proxy
nodea0.example.com
tcp6       0      0 :::31876                :::*                    LISTEN      2886/kube-proxy
nodeb0.example.com
tcp6       0      0 :::31876                :::*                    LISTEN      3060/kube-proxy

我們可以通過瀏覽器訪問master或任意節點,其訪問均通過kube-proxy均衡的分發到各個pod上。並且在Node或Pod損壞的情況下自動遷移和恢復服務。

創建帶共享存儲卷的Service環境

創建NFS共享存儲卷

我們可以使用很多種方法和軟件導出並共享NFS卷,在我們當前的環境中,比較方便的做法是使用sharestorage虛擬機,利用已經安裝好的openfiler系統導出NFS卷。

Openfiler是一種開源的共享存儲系統,可以支持NFS、CIFS和iSCSI等共享方式。在我們的現有環境中它充當共享存儲設備。

首先我們啓動本地的sharestorage虛擬機:

[kiosk@foundation0 ~]$ rht-vmctl start sharestorage

啓動正常以後我們就可以訪問共享存儲設備了,虛擬機爲sharestorage,主機名爲sharestorageN,域名爲sharestorageN.example.com。 N爲你的Foundation 號,我演示機的Foundation 號爲0。

打開瀏覽器訪問sharestorage的配置界面,登錄地址爲http://sharestorageN.example.com:446 , 用戶名爲openfiler,密碼爲uplooking。

001-openfiler-signin

登錄後看到如下界面

002-openfiler-index

點擊”Services",然後點擊“NFSv3 server"後的”Enable“,打開NFS服務003-openfiler-nfs-service-enable

打開NFS服務後,點擊”Volumes“創建一個新的卷組,“create new physical volumes"004-openfiler-create-pv

選擇”/dev/hdb"005-openfiler-create-pv

點擊“create”,創建物理卷006-openfiler-create-pv

創建好物理卷後,點擊“shares”,再點擊“create a new filesystem volume",創建文件系統卷007-openfiler-create-share

在”Volume group name“中填入”share“,並且勾選”/dev/hdb1"008-openfiler-create-share

009-openfiler-create-share

點擊“Add volume group”,後在“Volumes”中可以看到010-openfiler-create-lv

在“Shares”中可以看到我們建好的“Network Shares”011-openfiler-create-share-dir

點擊“Volumes”,可以在“Volumes Section”中選擇“Add Volume",創建卷”nfs“,設置”Required Space (MB)“爲5000,點擊”Create“創建

012-openfiler-create-share-dir

可以看到,”nfs“卷已經建立好013-openfiler-create-share-vg

由於安全方面的考慮,我們需要設置能夠訪問此共享設備的網絡,請點擊“System”,拉到頁面下方,在“Network Access Configuration”,填入name:"mynet",Netework:"172.25.0.0",Netmask:"255.255.0.0"。點擊”Update“014-openfiler-create-network-access

然後點擊”Shares“,點選Network Shares 中的”nfs“,創建導出 015-openfiler-create-share-dir-default

在”Folder name“中填入”k8s“,點擊”Create Sub-folder“016-openfiler-create-share-dir-vg

創建好後,點擊”k8s“,點擊”Make Share“017-openfiler-create-share-dir

進入共享設置中,在“Share Access Control Mode”中選“Plublic guest access”,點“Update”018-openfiler-create-share

然後下拉頁面,看到“Host access configuration",在NFS項目中選”RW“,然後“Update” 019-openfiler-create-share-nfs

至此,共享存儲設備端就配置了NFS導出設備及文件系統。

NFS導出的路徑爲:sharestorageN.example.com:/mnt/share/nfs/k8s ,你可以在其他系統中測試掛接。N爲你的機器號。

Master和Node設備上要掛接此共享設備需要安裝nfs客戶端相關軟件包,並且啓動相應服務:

[root@master0 ~]# yum install nfs-utils -y
[root@master0 ~]# systemctl start rpcbind
[root@nodea0 ~]# yum install nfs-utils -y
[root@nodea0 ~]# systemctl start rpcbind
[root@nodeb0 ~]# yum install nfs-utils -y
[root@nodeb0 ~]# systemctl start rpcbind

將sharestorageN.example.com:/mnt/share/nfs/k8s掛接到Master主機的/mnt目錄下,方便後期測試使用,其他Node無需掛接

[root@master0 ~]# mount sharestorage0.example.com:/mnt/share/nfs/k8s /mnt

編寫掛接共享存儲服務配置文件

我們創建一個配置文件nfs-nginx-service-pv.yaml,其中包括創建PV、PVC、Deployment和Service的配置項目

apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfspv001
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteMany
  nfs:
    path: /mnt/share/nfs/k8s
    server: 172.25.0.8

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim001
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 3Gi

---

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment-pv
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx-pv
    spec:
      containers:
      - name: nginx-pv
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: mypvc
      volumes:
      - name: mypvc
        persistentVolumeClaim:
          claimName: myclaim001

---

apiVersion: v1
kind: Service
metadata:
  name: nginx-service-pv
spec:
  ports:
  - port: 8000
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx-pv
  type: LoadBalancer

此文件由三個連續“-”符號分爲了四個部分:Persistent Volume、Persistent Volume Claim、Deployment 和Service。

Persistent Volume 部分用以定義網絡存儲卷:

apiVersion: v1 # Api 版本
kind: PersistentVolume # 配置類型
metadata: # 元數據
  name: nfspv001 # metadata.name 網絡存儲卷名稱,需符合命名規範
spec: # 詳細信息
  capacity: # 容量定義
    storage: 5Gi # 存儲容量
  accessModes: # 訪問方式 
    - ReadWriteMany # ReadWriteMany 可讀寫,可以被多個Node掛接
  nfs: # 卷類型爲nfs
    path: /mnt/share/storage1/k8s # 掛接目錄
    server: 172.25.0.8 # nfs 服務器IP

Persistent Volume Claim 部分用以定義網絡存儲卷區域,分配給Pod Volume的對象

apiVersion: v1 # Api 版本
kind: PersistentVolumeClaim # 配置類型
metadata: # 元數據 
  name: myclaim001 # metadata.name 網絡存儲卷對象名稱,需符合命名規範
spec: # 詳細信息
  accessModes: # 訪問模式
    - ReadWriteMany # ReadWriteMany 可讀寫,可以被多個Node掛接
  resources: # 資源定義
    requests: # 具體配置
      storage: 3Gi # 存儲容量

Deployment 部分用以定義Pod副本具體對象信息,包括掛接Volume對象信息

apiVersion: extensions/v1beta1 # Api 版本
kind: Deployment # 配置類型
metadata: # 元數據
  name: nginx-deployment-pv # metadata.name Deployment 對象名稱,需符合命名規範
spec: # 詳細信息
  replicas: 2 # 副本數
  template: # Pod 對象模板信息
    metadata: # 元數據
      labels: # 標籤
        app: nginx-pv # 自定義標籤信息
    spec: # spec.template.spce 容器的詳細描述
      containers: # 容器對象信息
      - name: nginx # 容器名
        image: nginx:latest # 鏡像信息
        imagePullPolicy: IfNotPresent # 鏡像下載方式
        ports: # 端口信息
        - containerPort: 80 # 容器暴露端口
        volumeMounts: # 容器掛接卷信息
        - mountPath: "/usr/share/nginx/html" # 容器掛接捲到本地路徑
          name: mypvc # 卷名稱
      volumes: # 卷定義
      - name: mypvc # 卷名定義
        persistentVolumeClaim: # 定義使用PVC設置卷
          claimName: myclaim001 # PVC卷名

Service 部分用以定義服務具體對象信息

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 8000
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx-pv
  type: LoadBalancer

啓動帶共享存儲卷的Service環境

編寫好nfs-nginx-service-pv.yaml配置文件後,可以通過kubectl create 命令創建Service及其相關對象。

[root@master0 ~]# kubectl create -f nfs-nginx-service-pv.yaml
persistentvolume "nfspv001" created
persistentvolumeclaim "myclaim001" created
deployment "nginx-deployment" created
service "nginx-service-pv" created

我們可以通過kubectl相關命令查看創建好的對象:

# 查看PV 對象
[root@master0 ~]# kubectl get pv
NAME       CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                REASON    AGE
nfspv001   5Gi        RWX           Retain          Bound     default/myclaim001             1m
[root@master0 ~]# kubectl describe pv
Name:       nfspv001
Labels:     <none>
Status:     Bound
Claim:      default/myclaim001
Reclaim Policy: Retain
Access Modes:   RWX
Capacity:   5Gi
Message:
Source:
    Type:   NFS (an NFS mount that lasts the lifetime of a pod)
    Server: 172.25.0.8
    Path:   /mnt/share/nfs/k8s
    ReadOnly:   false

# 查看PVC 對象
[root@master0 ~]# kubectl get pvc
NAME         STATUS    VOLUME     CAPACITY   ACCESSMODES   AGE
myclaim001   Bound     nfspv001   5Gi        RWX           2m
[root@master0 ~]# kubectl describe pvc
Name:       myclaim001
Namespace:  default
Status:     Bound
Volume:     nfspv001
Labels:     <none>
Capacity:   5Gi
Access Modes:   RWX

# 查看Deployment 對象
[root@master0 ~]# kubectl get deployment nginx-deployment-pv
NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment-pv   2         2         2            2           21s
[root@master0 ~]# kubectl describe deployment nginx-deployment-pv
Name:           nginx-deployment-pv
Namespace:      default
CreationTimestamp:  Mon, 21 Nov 2016 17:08:06 +0800
Labels:         app=nginx-pv
Selector:       app=nginx-pv
Replicas:       2 updated | 2 total | 2 available | 0 unavailable
StrategyType:       RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
OldReplicaSets:     <none>
NewReplicaSet:      nginx-deployment-pv-2162189116 (2/2 replicas created)
Events:
  FirstSeen LastSeen    Count   From                SubobjectPath   Type        Reason          Message
  --------- --------    -----   ----                -------------   --------    ------          -------
  32s       32s     1   {deployment-controller }        Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-pv-2162189116 to 2

# 查看Service 對象信息 
[root@master0 ~]# kubectl get service nginx-service-pv
NAME               CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
nginx-service-pv   100.68.143.130   <pending>     8000/TCP   3m
[root@master0 ~]# kubectl describe service nginx-service-pv
Name:           nginx-service-pv
Namespace:      default
Labels:         <none>
Selector:       app=nginx-pv
Type:           LoadBalancer
IP:         100.68.143.130
Port:           <unset> 8000/TCP
NodePort:       <unset> 31905/TCP
Endpoints:      10.32.0.31:80,10.40.0.26:80
Session Affinity:   None

我們還可以連接相應Pod 查看其Volume掛接情況,需要注意你的pod信息和這裏顯示的會不同:

# 首先查詢Pod信息
[root@master0 ~]# kubectl get pod |grep nginx-deployment-pv
nginx-deployment-pv-2162189116-bg1bw   1/1       Running   0          5m
nginx-deployment-pv-2162189116-yuozz   1/1       Running   0          5m

# 分別連接Pod,查看其內部掛接信息
[root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116-bg1bw /bin/bash
root@nginx-deployment-pv-2162189116-bg1bw:/# df
Filesystem                                                                                       1K-blocks    Used Available Use% Mounted on
/dev/mapper/docker-253:0-669928-284631bea446cc1ea5b0dfd7678a85b7875767bb43bac60762b3a2a73ca6bd3d  10474496  232560  10241936   3% /
tmpfs                                                                                               508396       0    508396   0% /dev
tmpfs                                                                                               508396       0    508396   0% /sys/fs/cgroup
/dev/mapper/rhel-root                                                                              9226240 6569852   2656388  72% /etc/hosts
shm                                                                                                  65536       0     65536   0% /dev/shm
172.25.0.8:/mnt/share/nfs/k8s                                                                      5134336    4416   5129920   1% /usr/share/nginx/html
tmpfs                                                                                               508396      12    508384   1% /run/secrets/kubernetes.io/serviceaccount
root@nginx-deployment-pv-2162189116-bg1bw:/# exit
exit

[root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116- /bin/bash
nginx-deployment-pv-2162189116-bg1bw  nginx-deployment-pv-2162189116-yuozz
[root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116-yuozz /bin/bash
root@nginx-deployment-pv-2162189116-yuozz:/# mount |grep k8s
172.25.0.8:/mnt/share/nfs/k8s on /usr/share/nginx/html type nfs (rw,relatime,vers=3,rsize=65536,wsize=65536,namlen=255,hard,proto=tcp,timeo=600,retrans=2,sec=sys,mountaddr=172.25.0.8,mountvers=3,mountport=677,mountproto=udp,local_lock=none,addr=172.25.0.8)
root@nginx-deployment-pv-2162189116-yuozz:/# exit
exit

可以看到,每個Pod都自動掛接了NFS共享目錄,並且掛接在本地的/usr/share/nginx/html目錄上。

我們可以進入Pod設置nginx的index.html,通過curl訪問Service的NodePort來測試。

[root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116-yuozz /bin/bash
root@nginx-deployment-pv-2162189116-yuozz:/# cd /usr/share/nginx/html/
<62189116-yuozz:/usr/share/nginx/html# echo "<h1>PV Test</h1>" > index.html
root@nginx-deployment-pv-2162189116-yuozz:/usr/share/nginx/html# exit
exit
# 另外一個Pod 上應該也可以看到相同的文件
[root@master0 ~]# kubectl exec -ti nginx-deployment-pv-2162189116-bg1bw /bin/bash
<9116-bg1bw:/# cat /usr/share/nginx/html/index.html
<h1>PV Test</h1>
root@nginx-deployment-pv-2162189116-bg1bw exit
exit
# 通過curl工具訪問Service的主頁, 請用你自己的NodePort替換31905
[root@master0 ~]# curl http://master0.example.com:31905
<h1>PV Test</h1>

我們之前在Master上掛接了相同的NFS目錄,本地掛節點是/mnt,我們現在應該可以看到此目錄下也存在文件index.html

[root@master0 ~]# cd /mnt
[root@master0 mnt]# ls
index.html
[root@master0 mnt]# cat index.html
<h1>PV Test</h1>

綜上就是我們創建帶共享存儲卷的Service環境的方法。此方法解決了微服務中多副本存儲共享問題。

管理Pod的調度

RC 和 Deployment 設置自動調度

RC和Deployment設計的目的之一就是設置和管理Pod的多副本化,以及維持並監控Pod副本的數量。Kubernetes 集羣通過RC和Deployment在集羣內部始終維護用戶指定的Pod副本數量。

之前我們使用my-nginx.yaml創建過一個Pod副本數爲2的基於nginx web 的服務,其配置文件內容如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2  # 指定副本數爲 2 
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 8000
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx
  type: LoadBalancer

我們在此重新創建這個服務:

[root@master0 ~]# kubectl create -f my-nginx.yaml
deployment "nginx-deployment" created
service "nginx-service" created

Pod 運行在Kubernetes 現有的Node上,我們可以通過kubectl get nodes 命令查看當前Node 的信息:

[root@master0 ~]# kubectl get nodes
NAME                  STATUS    AGE
master0.example.com   Ready     23h
nodea0.example.com    Ready     23h
nodeb0.example.com    Ready     23h

其中,masterN爲Kubernetes集羣中的Master節點,nodeaN和nodebN爲運行Pod資源的Node節點。

我們可以通過命令kubectl get pod -o wide 來查看Pod分別運行在那些Node上:

[root@master0 ~]# kubectl get pod -o wide
NAME                                READY     STATUS    RESTARTS   AGE       IP          NODE
nginx-deployment-2273492681-74lpo   1/1       Running   0          41m       10.46.0.4   nodeb0.example.com
nginx-deployment-2273492681-zgnzx   1/1       Running   0          41m       10.40.0.1   nodea0.example.com

在我們不做任何額外設置時,Pod在RC和Deployment 管理下會自動分佈到現有資源Node節點上,並且在Node節點損壞或離線狀態下自動遷移到正常的節點上。我們可以模擬節點nodeb損壞,驗證Pod遷移。

# 首先迫使nodebN 節點離線
[kiosk@foundation0 ~]$ ssh root@nodeb0 "reboot"

# 這時再查看Pod 信息
# 發現 nodebN 失效後,在正常的節點上創建新的 Pod
[root@master0 ~]# kubectl get pod -o wide
NAME                                READY     STATUS              RESTARTS   AGE       IP          NODE
nginx-deployment-2273492681-0bpic   0/1       ContainerCreating   0          16s       <none>      nodea0.example.com
nginx-deployment-2273492681-d9md3   1/1       Running             0          7m        10.40.0.1   nodea0.example.com
nginx-deployment-2273492681-g397i   1/1       Terminating         0          7m        10.46.0.4   nodeb0.example.com

# 新Pod 運行正常後,刪除失效的Pod
[root@master0 ~]# kubectl get pod -o wide
NAME                                READY     STATUS        RESTARTS   AGE       IP          NODE
nginx-deployment-2273492681-0bpic   1/1       Running       0          22s       10.40.0.2   nodea0.example.com
nginx-deployment-2273492681-d9md3   1/1       Running       0          7m        10.40.0.1   nodea0.example.com
nginx-deployment-2273492681-g397i   1/1       Terminating   0          7m        10.46.0.4   nodeb0.example.com
[root@master0 ~]# kubectl get pod -o wide
NAME                                READY     STATUS    RESTARTS   AGE       IP          NODE
nginx-deployment-2273492681-0bpic   1/1       Running   0          29s       10.40.0.2   nodea0.example.com
nginx-deployment-2273492681-d9md3   1/1       Running   0          8m        10.40.0.1   nodea0.example.com

# Node 正常恢復後並不將Pod資源切回
[root@master0 ~]# kubectl get node
NAME                  STATUS    AGE
master0.example.com   Ready     1d
nodea0.example.com    Ready     1d
nodeb0.example.com    Ready     1d
[root@master0 ~]# kubectl get pod -o wide
NAME                                READY     STATUS    RESTARTS   AGE       IP          NODE
nginx-deployment-2273492681-0bpic   1/1       Running   0          5m        10.40.0.2   nodea0.example.com
nginx-deployment-2273492681-d9md3   1/1       Running   0          13m       10.40.0.1   nodea0.example.com

Pod 副本數擴容和縮減

在生產環境中,我們經常會遇到由於負載的增大需要對某個服務進行擴容的場景,可以經常會遇到由於資源緊張需要將不太重要的或實際負載不高的服務進行縮減的場景。在Kubernetes 集羣環境中,我們可以很方便的利用Pod的副本數來控制服務容量的增減。

手動增減Pod副本數

我們在Kubernetes 集羣運行狀態下,可以使用kubectl scale 命令手動增減RC和Deployment中Pod的副本數。

增加Deployment 中的副本數,演示如下:

# 當前 nginx-deployment 狀態如下
[root@master0 ~]# kubectl get deployment nginx-deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            2           25m

# 當前 pod 狀態如下
[root@master0 ~]# kubectl get pod
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-2273492681-0bpic   1/1       Running   0          18m
nginx-deployment-2273492681-d9md3   1/1       Running   0          26m

# 我們將nginx-deployment 中的pod 數由2設置爲3
[root@master0 ~]# kubectl scale --replicas=3 deployment nginx-deployment
deployment "nginx-deployment" scaled

# 增加副本數後的nginx-deployment 狀態如下
[root@master0 ~]# kubectl get deployment nginx-deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           28m

# 增加副本數後的 pod 狀態如下
[root@master0 ~]# kubectl get pod
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-2273492681-0bpic   1/1       Running   0          21m
nginx-deployment-2273492681-d9md3   1/1       Running   0          28m
nginx-deployment-2273492681-m3qcj   1/1       Running   0          58s

# 爲後續實驗清理環境
[root@master0 ~]# kubectl delete -f my-nginx.yaml
deployment "nginx-deployment" deleted
service "nginx-service" deleted
自動增減Pod副本數

除了手動通過kubectl scale 命令來增減Pod副本數之外,我們還可以使用在前面介紹過的概念Horizontal Pod Autoscaler(HPA)來根據容器佔用的CPU使用率來自動進行增減Pod副本數。

Horizontal Pod Autoscaler (HPA) 基於Master節點上的kube-controller-manager服務定義的監測時長(默認爲30秒),週期性的檢測Pod的CPU使用率,當滿足預設條件時對RC或Deployment中的副本數進行調整。

要使用HPA就需要預設Pod的CPU使用條件,同時還需要Kubernetes Heapster組件的支持,需要安裝Heapster組件,否則無法獲取Pod的CPU使用情況。

首先我們需要裝載Heapster運行容器的鏡像到每個Node節點上:

# 在所有Node節點上下載heapster-img.tbz文件
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i " wget http://cla***oom.example.com/materials/k8s-imgs/heapster-img.tbz" ; done

# 然後將其解包
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf heapster-img.tbz " ; done

# 然後將其導入
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./heapster/*.img ; do docker load -i $i ; done' ; done

在所有Node節點導入所需的heapster容器鏡像後,需要在Master上運行Heapster腳本將其啓動起來:

# 首先下載Heapster運行環境包到Master節點上
[kiosk@foundation0 Desktop]$ ssh root@master0 "wget http://cla***oom.example.com/materials/k8s-conf/heapster.tar "

# 然後將其解包
[kiosk@foundation0 Desktop]$ ssh root@master0 "tar -xf heapster.tar "

# 在Master節點上執行heapster目錄下的kube.sh腳本
[root@master0 heapster]# ./kube.sh

# 耐心等待3~5分鐘,可以看到相應的Pod運行起來
[root@master0 heapster]# kubectl get pod --all-namespaces
NAMESPACE     NAME                                          READY     STATUS    RESTARTS   AGE
kube-system   etcd-master0.example.com                      1/1       Running   5          3d
kube-system   heapster-3901806196-8c2rj                     1/1       Running   3          1d
kube-system   kube-apiserver-master0.example.com            1/1       Running   10         3d
kube-system   kube-controller-manager-master0.example.com   1/1       Running   5          3d
kube-system   kube-discovery-982812725-nghjq                1/1       Running   5          3d
kube-system   kube-dns-2247936740-f32d9                     3/3       Running   15         3d
kube-system   kube-proxy-amd64-gzmv5                        1/1       Running   6          3d
kube-system   kube-proxy-amd64-px2ms                        1/1       Running   3          1d
kube-system   kube-proxy-amd64-tve1y                        1/1       Running   4          3d
kube-system   kube-scheduler-master0.example.com            1/1       Running   5          3d
kube-system   kubernetes-dashboard-1171352413-yuqpa         1/1       Running   3          2d
kube-system   monitoring-grafana-927606581-45lpl            1/1       Running   3          1d
kube-system   monitoring-influxdb-3276295126-ec2nf          1/1       Running   3          1d
kube-system   weave-net-cfenz                               2/2       Running   15         3d
kube-system   weave-net-kpvob                               2/2       Running   6          1d
kube-system   weave-net-xblek                               2/2       Running   10         3d

# 有三個服務正常運行
[root@master0 ~]# kubectl get service heapster --namespace=kube-system
NAME       CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
heapster   100.77.26.79   <none>        80/TCP    1d
[root@master0 ~]# kubectl get service monitoring-grafana --namespace=kube-system
NAME                 CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
monitoring-grafana   100.70.101.80   <nodes>       80/TCP    1d
[root@master0 ~]# kubectl get service monitoring-influxdb --namespace=kube-system
NAME                  CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
monitoring-influxdb   100.64.163.255   <none>        8086/TCP   1d

Kubernetes 的Heapster模塊正常部署後,我們就可以做自動增減Pod副本數的實驗了。

首先在所有Node節點上部署hpa-example容器鏡像,它是一個安裝了apache和php的測試環境,我們在後面的試驗中將訪問其服務使其產生工作負載。

# 在所有Node節點上下載hpa-example-img.tbz包
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i " wget http://cla***oom.example.com/materials/k8s-imgs/hpa-example-img.tbz" ; done

# 然後將其解開
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i "tar -jxf hpa-example-img.tbz " ; done

# 然後將hpa-example容器鏡像導入
[kiosk@foundation0 Desktop]$ for i in nodea0 nodeb0 ; do ssh root@$i 'for i in ./hpa-example/*.img ; do docker load -i $i ; done' ; done
Loaded image: kissingwolf/hpa-example:latest
Loaded image: kissingwolf/hpa-example:latest

# 如果你之前沒有注意清除Node節點環境下載的文件,由於磁盤空間限制可能會導致無法導入,你可以執行以下命令,清除下載文件並提出磁盤空間
[kiosk@foundation0 ~]$ for i in master0 nodea0 nodeb0 ; do ssh root@$i "rm -rf ~/*.tbz " ; done

接下來我們創建hpa-example.yaml配置文件,設置自動增減Pod副本數的Service環境:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hpa-example-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: hpa-example
    spec:
      containers:
      - name: php-apache
        image: kissingwolf/hpa-example:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
  name: hpa-example-service
spec:
  ports:
  - port: 8000
    targetPort: 80
    protocol: TCP
  selector:
    app: hpa-example
  type: LoadBalancer
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-example-deployment
spec:
  maxReplicas: 10
  minReplicas: 2
  scaleTargetRef:
    apiVersion: extensions/v1beta1
    kind: Deployment
    name: hpa-example-deployment
  targetCPUUtilizationPercentage: 50

配置文件分爲三個部分:Deployment、Service和HorizontalPodAutoscaler

Depolyment部分說明:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hpa-example-deployment
spec:
  replicas: 2 # 初始化副本數爲2
  template:
    metadata:
      labels:
        app: hpa-example
    spec:
      containers:
      - name: php-apache
        image: kissingwolf/hpa-example:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        resources: # spec.template.spce.containers[].resources 設置資源監控
          requests: # 資源監控項目
            cpu: 200m # CPU資源初始化限制,1 cpu core = 1000m ,200m = 0.2 cpu core

HorizontalPodAutoscaler部分說明:

apiVersion: autoscaling/v1 # 配置版本
kind: HorizontalPodAutoscaler # 配置類型
metadata:
  name: hpa-example-deployment
spec:
  maxReplicas: 10 # 設置最大副本數
  minReplicas: 2 # 設置最小副本數
  scaleTargetRef:
    apiVersion: extensions/v1beta1
    kind: Deployment
    name: hpa-example-deployment # 配置的Deployment名稱
  targetCPUUtilizationPercentage: 50 # 設置Pod Cpu使用率維持在50%

通過kubectl create 命令創建此服務:

[root@master0 ~]# kubectl create -f hpa-example.yaml
deployment "hpa-example-deployment" created
service "hpa-example-service" created
horizontalpodautoscaler "hpa-example-deployment" created

初始化狀態可以通過 kubectl get hpa 命令查看:

[root@master0 ~]# kubectl get hpa
NAME                     REFERENCE                           TARGET    CURRENT     MINPODS   MAXPODS   AGE
hpa-example-deployment   Deployment/hpa-example-deployment   50%       <waiting>   2         10        47s
# 需要等待1分鐘左右才能收集好資源信息
[root@master0 ~]# kubectl get hpa
NAME                     REFERENCE                           TARGET    CURRENT   MINPODS   MAXPODS   AGE
hpa-example-deployment   Deployment/hpa-example-deployment   50%       0%        2         10        2m

我們可以通過使用物理機foundationN發起請求,以增加負載的方式使Depolyment中的副本數自動增加:

# 首先確定hpa-example-service 對外暴露的端口(NodePort)
[root@master0 ~]# kubectl describe service hpa-example-service
Name:           hpa-example-service
Namespace:      default
Labels:         <none>
Selector:       app=hpa-example
Type:           LoadBalancer
IP:         100.69.106.140
Port:           <unset> 8000/TCP
NodePort:       <unset> 32445/TCP
Endpoints:      10.40.0.1:80,10.46.0.20:80
Session Affinity:   None
No events.

# 在foundationN上執行如下命令產生負載,請注意MasterN爲你自己的Master主機,訪問端口爲你即時查看到的端口
[kiosk@foundation0 ~]$ while : ; do curl http://master0.example.com:32445 >/dev/null 2>&1 ; done

# 初始化的Deployment狀態如下:
[root@master0 ~]# kubectl get deployment
NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hpa-example-deployment   2         2         2            2           15m

# 經過2分鐘左右後Deployment狀態如下
[root@master0 ~]# kubectl get deployment
NAME                     DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
hpa-example-deployment   6         6         6            6           18m

# hpa 狀態如下
[root@master0 ~]# kubectl get hpa
NAME                     REFERENCE                           TARGET    CURRENT   MINPODS   MAXPODS   AGE
hpa-example-deployment   Deployment/hpa-example-deployment   50%       55%       2         10        19m

# pod 狀態如下
[root@master0 ~]# kubectl get pod
NAME                                     READY     STATUS    RESTARTS   AGE
hpa-example-deployment-882509061-0vrl2   1/1       Running   0          36s
hpa-example-deployment-882509061-5qrzz   1/1       Running   0          18m
hpa-example-deployment-882509061-99a4s   1/1       Running   0          36s
hpa-example-deployment-882509061-iw037   1/1       Running   0          36s
hpa-example-deployment-882509061-vus9q   1/1       Running   0          36s
hpa-example-deployment-882509061-zejct   1/1       Running   0          18m

如果我們停下foundationN上運行的運行的負載,則Deployment中的Pod數會自動減少。

最後不要忘記清除環境:

[root@master0 ~]# kubectl delete -f hpa-example.yaml
deployment "hpa-example-deployment" deleted
service "hpa-example-service" deleted
horizontalpodautoscaler "hpa-example-deployment" deleted

Pod 中容器的滾動升級

當Kubernetes集羣中的某個Service由於某種原因需要升級相關Pod中的容器鏡像,我們會想當然的認爲正確的操作步驟是先停止Service上的所有相關Pod,然後從鏡像註冊中心拉取新的鏡像然後啓動。在Kubernetes集羣中Pod數量不多的情況下這樣的操作沒有問題,但是如果集羣規模比較大,Pod的數量比較多,並且用戶訪問又是持續化的,這樣的操作會帶來災難性的後果。你可以想象以下在大草原上,數以百萬計的野牛狂奔向你的時候,你和你的同伴所有的武器都突然啞火的感覺嗎?我們稱這種情況叫做“驚羣效應”。

Kubernetes 是通過滾動升級(rolling-update)功能來解決這個問題的,RC方式中操作命令爲 kubectl rolling-update,Deployment方式中操作命令爲 kubectl set image 。

RC 滾動升級

RC 滾動升級功能通過將原有RC和升級RC置於同一Service和Namespace下,然後自動控制原有RC中的Pod副本數量逐漸減少直至爲零,同時自動控制升級RC中的Pod副本數量從零逐漸增長至指定值。新舊RC中的Pod副本資源共享且均衡負載,有效的降低了驚羣效應的發生。

在前面的試驗中,我們在Node上導入了nginx三個版本的鏡像,之前使用的都是latest最新版本,在接下來的試驗中,我們會首先創建一個使用低版本nginx鏡像的Service ,然後使用配置文件更新這個Service的nginx鏡像到高版本。

創建舊有RC的配置文件命名爲test-rollingupdate-v1.yaml,內容如下:

apiVersion: v1
kind: ReplicationController
metadata:
  name: test-rollingupdate-v1
  labels:
    name: nginx
    version: v1
spec:
  replicas: 4  # 我們設置Pod副本數爲4
  selector:
    name: nginx
    version: v1
  template:
    metadata:
      labels:
        name: nginx
        version: v1
    spec:
      containers:
      - name: nginx
        image: nginx:1.10.2 # nginx容器鏡像版本爲1.10.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

使用配置文件生成RC:

[root@master0 ~]# kubectl create  -f test-rollingupdate-v1.yaml
replicationcontroller "test-rollingupdate-v1" create

查看當前RC信息:

[root@master0 ~]# kubectl get rc
NAME                    DESIRED   CURRENT   READY     AGE
test-rollingupdate-v1   4         4         4         56s

[root@master0 ~]# kubectl get pod
NAME                          READY     STATUS    RESTARTS   AGE
test-rollingupdate-v1-8nl1f   1/1       Running   0          1m
test-rollingupdate-v1-8y5fx   1/1       Running   0          1m
test-rollingupdate-v1-l6l03   1/1       Running   0          1m
test-rollingupdate-v1-mk1cr   1/1       Running   0          1m

[root@master0 ~]# kubectl describe replicationcontroller
Name:       test-rollingupdate-v1
Namespace:  default
Image(s):   nginx:1.10.2
Selector:   name=nginx,version=v1
Labels:     name=nginx
        version=v1
Replicas:   4 current / 4 desired
Pods Status:    4 Running / 0 Waiting / 0 Succeeded / 0 Failed
No volumes.
Events:
  FirstSeen LastSeen    Count   From                SubobjectPath   Type        Reason          Message
  --------- --------    -----   ----                -------------   --------    ------          -------
  1m        1m      1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v1-mk1cr
  1m        1m      1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v1-8y5fx
  1m        1m      1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v1-l6l03
  1m        1m      1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v1-8nl1f

創建新有RC的配置文件命名爲test-rollingupdate-v2.yaml,內容如下:

apiVersion: v1
kind: ReplicationController
metadata:
  name: test-rollingupdate-v2 # RC的名字不能與舊RC同名
  labels:
    name: nginx-rc
    version: v2 # 用於與舊版本區分
spec:
  replicas: 2 # v2 版本的副本數可以與v1 不同
  selector:
    name: nginx
    version: v2 # spec.selector中至少有一個Label不能與舊版本不同
  template:
    metadata:
      labels:
        name: nginx
        version: v2 # 用於與舊版本區分
    spec:
      containers:
      - name: nginx
        image: nginx:1.11.5 # nginx容器鏡像版本升級爲1.11.5
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

使用kubectl rolling-update命令滾動升級test-rollingupdate-v1 RC:

[rootmaster0 ~]# kubectl rolling-update test-rollingupdate-v1 -f test-rollingupdate-v2.yaml
Created test-rollingupdate-v2
Scaling up test-rollingupdate-v2 from 0 to 2, scaling down test-rollingupdate-v1 from 4 to 0 (keep 2 pods available, don't exceed 3 pods)
....
此處根據副本個數決定切換時間和顯示

同時打開另外一個終端,查看RC信息,可以看到v1和v2版本開始切換:

[root@master0 ~]# kubectl get rc
NAME                    DESIRED   CURRENT   READY     AGE
test-rollingupdate-v1   2         2         2         12m
test-rollingupdate-v2   1         1         1         1m

在另一個終端中,查看replicationcontroller的詳細信息,可以看到v1和v2版本具體操作信息:

[root@master0 ~]# kubectl describe replicationcontroller
Name:       test-rollingupdate-v1
Namespace:  default
Image(s):   nginx:1.10.2
Selector:   name=nginx,version=v1
Labels:     name=nginx
        version=v1
Replicas:   1 current / 1 desired
Pods Status:    1 Running / 0 Waiting / 0 Succeeded / 0 Failed
No volumes.
Events:
  FirstSeen LastSeen    Count   From                SubobjectPath   Type        Reason          Message
  --------- --------    -----   ----                -------------   --------    ------          -------
  13m       13m     1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v1-mk1cr
  13m       13m     1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v1-8y5fx
  13m       13m     1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v1-l6l03
  13m       13m     1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v1-8nl1f
  1m        1m      1   {replication-controller }       Normal      SuccessfulDelete    Deleted pod: test-rollingupdate-v1-8y5fx
  1m        1m      1   {replication-controller }       Normal      SuccessfulDelete    Deleted pod: test-rollingupdate-v1-8nl1f
  7s        7s      1   {replication-controller }       Normal      SuccessfulDelete    Deleted pod: test-rollingupdate-v1-mk1cr

Name:       test-rollingupdate-v2
Namespace:  default
Image(s):   nginx:1.11.5
Selector:   name=nginx,version=v2
Labels:     name=nginx-rc
        version=v2
Replicas:   2 current / 2 desired
Pods Status:    2 Running / 0 Waiting / 0 Succeeded / 0 Failed
No volumes.
Events:
  FirstSeen LastSeen    Count   From                SubobjectPath   Type        Reason          Message
  --------- --------    -----   ----                -------------   --------    ------          -------
  1m        1m      1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v2-6i9hm
  7s        7s      1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v2-tf47l

等待一段時間後,v1中的Pod均切換爲v2的Pod:

[root@master0 ~]# kubectl rolling-update test-rollingupdate-v1 -f test-rollingupdate-v2.yaml
Created test-rollingupdate-v2
Scaling up test-rollingupdate-v2 from 0 to 2, scaling down test-rollingupdate-v1 from 4 to 0 (keep 2 pods available, don't exceed 3 pods)
Scaling test-rollingupdate-v1 down to 2
Scaling test-rollingupdate-v2 up to 1
Scaling test-rollingupdate-v1 down to 1
Scaling test-rollingupdate-v2 up to 2
Scaling test-rollingupdate-v1 down to 0
Update succeeded. Deleting test-rollingupdate-v1
replicationcontroller "test-rollingupdate-v1" rolling updated to "test-rollingupdate-v2"

滾動升級後,僅保留v2 版本的RC:

[root@master0 ~]# kubectl get rc
NAME                    DESIRED   CURRENT   READY     AGE
test-rollingupdate-v2   2         2         2         8m

查看replicationcontroller 具體信息也是一樣:

[root@master0 ~]# kubectl describe replicationcontroller
Name:       test-rollingupdate-v2
Namespace:  default
Image(s):   nginx:1.11.5
Selector:   name=nginx,version=v2
Labels:     name=nginx-rc
        version=v2
Replicas:   2 current / 2 desired
Pods Status:    2 Running / 0 Waiting / 0 Succeeded / 0 Failed
No volumes.
Events:
  FirstSeen LastSeen    Count   From                SubobjectPath   Type        Reason          Message
  --------- --------    -----   ----                -------------   --------    ------          -------
  9m        9m      1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v2-6i9hm
  8m        8m      1   {replication-controller }       Normal      SuccessfulCreate    Created pod: test-rollingupdate-v2-tf47l

我們也可以不更新配置文件直接使用命令指定要滾動升級的nginx鏡像,命令如下:

[root@master0 ~]# kubectl rolling-update test-rollingupdate-v1 --image=nginx:1.11.5

我們如果發現滾動升級後新版本的鏡像有問題,還可以指定原有鏡像回滾。

[root@master0 ~]# kubectl rolling-update test-rollingupdate-v1 --image=nginx:1.10.2 --rollback
Deployment 滾動升級

Deployment 滾動升級比較簡單,首先我們創建一個測試用的Deployment,配置文件命名爲test-deployment-rollup.yaml ,內容如下:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 4
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.10.2
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

然後使用kubectl create 創建Deployment:

[root@master0 ~]# kubectl create -f  test-deployment-rollup.yaml
deployment "nginx-deployment" created

通過查看Deployment的詳細信息,可以獲悉其默認使用RollingUpdate方式:

[root@master0 ~]# kubectl describe deployment
Name:           nginx-deployment
Namespace:      default
CreationTimestamp:  Tue, 06 Dec 2016 16:29:01 +0800
Labels:         app=nginx
Selector:       app=nginx
Replicas:       4 updated | 4 total | 4 available | 0 unavailable
StrategyType:       RollingUpdate
MinReadySeconds:    0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
OldReplicaSets:     <none>
NewReplicaSet:      nginx-deployment-2612444508 (4/4 replicas created)
Events:
  FirstSeen LastSeen    Count   From                SubobjectPath   Type        Reason          Message
  --------- --------    -----   ----                -------------   --------    ------          -------
  3m        3m      1   {deployment-controller }        Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2612444508 to 4

我們只需要使用kubectl set image 命令就可以滾動升級其Pod容器鏡像:

[root@master0 ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.11.5
deployment "nginx-deployment" image updated

其中deployment/nginx-deployment 是deployment的名字,nginx=nginx:1.11.5 是容器名=容器鏡像名及版本。

滾動的方式也是將舊版本容器逐步停止,然後逐一生成新版本容器,但要比RC方式更快。

[root@master0 ~]# kubectl get pods
NAME                                READY     STATUS              RESTARTS   AGE
nginx-deployment-2612444508-xicfw   1/1       Running             0          6m
nginx-deployment-2937634144-bnyuz   0/1       ContainerCreating   0          7s
nginx-deployment-2937634144-eo6qb   0/1       ContainerCreating   0          9s
nginx-deployment-2937634144-mvyjb   1/1       Running             0          17s
nginx-deployment-2937634144-rzgcl   1/1       Running             0          17s
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章