kubernetes基礎篇(一)
??這是小編對kubernetes的第一篇文章,這篇文章介紹了以下內容:
- kubernetes介紹
- kubernetes 作用
- kubernetes的簡單案例實踐與分析
- kubernetes基礎概念詳細說明
- 部署中可能遇到的問題解決方案
1. Kubernetes介紹
??Kubernetes是一個全新的基於容器技術的分布式架構領先方案,與2015年被Google高調的正式開源。一經開源便迅速稱霸容器技術領域。
??Kubernetes是一個開放的平臺。不局限與任何一種語言,不論是C、java、Python都可以毫無阻礙的映射Kubernetes的service,並通過標準的TCP協議進行交互。
2. Kubernetes的優勢
??第一點就是跟docker相仿,部署簡單,大大節省了運維的成本,pod之間的資源是相互隔離的,不會各自的系統環境影響其他服務,只要輕松的修改image,並部署相應的service和deployment就可以實現服務部署應用、服務滾動升級等等。
??其次,Kubernetes全面擁抱微服務架構。將一個巨大的單體應用,分解成多個微服務,每一個微服務後又有多個實例,並且內嵌了負載均衡。讓我們直接應用微服務解決復雜業務系統的架構問題。
??最後,Kubernetes有超強的橫向擴展能力,學習過hadoop的生態的朋友們一定知道,hdfs好處之一就是部署在廉價的機器上,並實現了冗余備份,更是支持橫向的擴展,這些優點對於後起之秀的Kubernetes當然是統統繼承下來,並且與之完善。Kubernetes可以做到不用修改代碼就能平滑擴招到擁有上百個node的大規模集群。
新版本的架構:
3. Kubernetes案例演示
??在演示之前,由於大家可能對yaml語法有寫模糊,小編這裏先介紹一下yaml的基本語法,有助於大家理解下面案例的配置文件。
(1)yaml的語法介紹
??① Yaml的語法規則:
??? - 大小寫敏感
??? - 使用縮進表示層級關系
??? - 縮進時不允許使用 Tab 鍵,只允許使用空格
??? - 縮進的空格數目不重要,只要相同層級的元素左側對齊即可
??? - “#” 表示註釋
??? - 在 yaml 裏面,連續的項目(如:數組元素、集合元素)通過減號“-”來表示,map 結構 裏面的鍵值對(key/value)用冒號“:”來分割。
??② Yaml支持的三種數據結構:
???1. 對象:對象的一組鍵值對,使用冒號結構表示。
#案例演示
animal: pets
hash:
name: Steve
foo: bar
或者:hash: { name: Steve, foo: bar }
???2. 數組:一組連詞線開頭的行,構成一個數組。
#案例演示
- Cat
- Dog
- Goldfish
或者:[ ‘Cat‘, ‘Dog‘, ‘Goldfish‘ ]
???3. 復合結構:對象和數組可以結合使用,形成復合結構。
#案例演示
bat:
website: baidu: http://www.baidu.com
qq: http://www.qq.com
ali:
- http://www.taobao.com
- http://www.tmall.com
ceo:
yanhongli: 李彥宏
huatengma: 馬化騰
yunma: 馬雲
???4. 純量:純量是最基本的、不可再分的值。如:字符串、布爾值、整數、浮點數、Null、時間、日期
#案例演示
number: 12.30
??說了這麽多的概念,相比大家對Kubernetes也是有所動心,那麽小編這就帶大家簡單的部署一下Kubernetes,感受一下他的強大之處。
這裏我們使用一個MySQL+Tomcat實現一個簡單的web應用的部署。
(2)案例演示
- 單機版的kubernetes集群搭建:
#單機版的k8s安裝 [[email protected] ~]# systemctl disable firewalld [[email protected] ~]# systemctl stop firewalld [[email protected] ~]#yum install -y etcd kubernetes #按順序啟動以下服務 [[email protected] ~]#systemctl start etcd [[email protected] ~]#systemctl start docker [[email protected] ~]#systemctl start kube-apiserver [[email protected] ~]#systemctl start kube-controller-manager [[email protected] ~]#systemctl start kube-scheduler [[email protected] ~]#systemctl start kubelet [[email protected] ~]#systemctl start kube-proxy #批量啟動服務 for i in etcd docker kube-apiserver kube-controller-manager kube-scheduler kubelet kube-proxy ;do systemctl start $i ;done;
- 編寫相應的yaml文件:
#① MySQL服務的RC(mysql-rc.yaml)
apiVersion: v1
kind: ReplicationController #副本控制器RC
metadata:
name: mysql #RC的名稱,全局唯一
spec:
replicas: 1 #pod 期待的副本數
selector:
app: mysql #符合目標的pod擁有此標簽
template: #根據下面的模板創建pod的副本
metadata:
labels: #pod副本擁有的標簽,對應RC的selector
app: mysql
spec: #pod內容器的定義部分
containers:
- name: mysql #容器名稱
image: mysql #鏡像名稱
ports: #容器內應用監聽的端口號
- containerPort: 3306
env: #容器內容的環境變量
- name: MYSQL_ROOT_PASSWORD
value: "123456"
#② MySQL的service(mysql-svc.yaml)
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
#③ Tomcat服務的RC(myweb-rc.yaml)
apiVersion: v1
kind: ReplicationController #副本控制器RC
metadata:
name: myweb #RC的名稱,全局唯一
spec:
replicas: 2 #pod 期待的副本數
selector:
app: myweb #符合目標的pod擁有此標簽
template: #根據下面的模板創建pod的副本
metadata:
labels: #pod副本擁有的標簽,對應RC的selector
app: myweb
spec: #pod內容器的定義部分
containers:
- name: myweb #容器名稱
image: kubeguide/tomcat-app:v1 #鏡像名稱
ports: #容器內應用監聽的端口號
- containerPort: 8080
#④ Tomcat服務的service(myweb-svc.yaml)
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30001
selector:
app: myweb
- 創建相應的kubernetes資源對象:
#執行以下命令 [[email protected] ~]#kubelet create -f ./mysql-rc.yaml [[email protected] ~]#kubelet create -f ./mysql-svc.yaml [[email protected] ~]#kubelet create -f ./myweb-rc.yaml [[email protected] ~]#kubelet create -f ./ myweb-svc.yaml 註意:這裏可能會涉及到image的下載,需要聯網。
-
驗證是否部署成功:
#查看集群中的RC資源對象
[[email protected] ~]# kubectl get rc[[email protected] ~]# kubectl get svc #查看集群中的service資源對象
#訪問pod中的Tomcat服務: [[email protected] ~]# curl 192.168.130.130:30001
到此在單機版的kubernetes集群中,我們就部署成功一個Tomcat+MySQL服務。4. Kubernetes的基礎概念
??通過上面的案例大家也對Kubernetes有一定的了解,並且能簡單的實際的操作一下Kubernetes,但是可能對其中的細節並不是很了解,就像什麽是“rc”,為什麽就能通過service將容器中的服務映射到宿主機中供外部訪問等等,接下來小編就給大家介紹Kubernetes幾個重中之重的基礎概念。
(1) Master
??說道master大家一定不會陌生,分布式集群中沒有老大怎麽能行,群龍無首那豈不天下大亂。Kubernetes中的master就是集群的控制節點,基本上Kubernetes所有的控制命令都會發給它,他來負責具體的執行,如果它一旦宕機,集群內部的所有容器應用的管理都將失效。為了避免被斬首,最好設置多個master形成高可用。
??這裏介紹一下master中的一組進程:
?? - Kube-apiserver:提供了HTTP Rest接口的關鍵服務進程,是Kubernetes集群中增、刪、改、查等操作的唯一入口。如果它啟動失敗,那麽Kubernetes的所有操作均將無效。
?? - Kube-controller-manager:Kube-controller-manager:
?? - Kube-scheduler:負責資源調度的進程,當我們部署應用,創建pod時,這個進程就會根據每個node的資源情況,將pod安排的指定的node中運行。
?? - etcd:Kubernetes裏的所有資源對象的數據全部保存在etcd中,他就是一個Kubernetes的數據庫。比如,創建相應的pod時,就會在etcd中生成一個記錄,如果後期這個pod被刪除,同樣的存在etcd中這個記錄也會被刪除。(2) Node
??Kubernetes集群中除了master,其他的機器就是node,也就是所謂的從節點。Node是Kubernetes集群的工作負載節點,每個node都會被master分配一定的工作負載。就像hdfs一樣,如果有從節點宕機,master就會將此節點上的工作(對於hdfs是副本)轉義到其他的可用的node上,以保證我們設置的replica個數。
??這裏介紹一下node中的一組進程:
?? - Kubelet:負責node節點上的pod的創建、啟停、同時與master保持通信。
?? - Kube-proxy:實現service的通信與負載均衡機制的重要組件。
?? - Docker:負責node中的容器創建和管理。
??當然上面,說到了Kubernetes集群可以橫向擴展,也就是node可以動態的添加,當node節點上安裝了相應的配置,默認情況下kubelet會自動的向master註冊自己,kubelet會向master匯報自身情況(資源、系統…),這樣master會發現node並之後為其分配工作負載。當然這種類似心跳機制的通信,有助於master計算各個node的資源,更好的進行過負載,也有助於master對node的管理,當有node“失聯”時,master也會很快的將其node上的工作負載轉移到其他機器上,有利於集群的正常工作。(3) pod
??pod是Kubernetes最基本最重要的概念,pod也是Kubernetes集群的負責均衡的最小單位。
??上面的圖就是一個pod中的結構,這裏不論pod中運行多少container,都有一個pause container,它的作用就是代表這個容器組的狀態,為pod中的每一個container共享IP和數據目錄。
??Kubernetes為每一個pod分配了一個Pod IP,這個Pod IP被pod中的所有容器共享,默認的Kubernetes集群中任意的兩個pod之間可以直接的TCP通信。
??默認的當pod中有某個容器停止時,Kubernetes會自動檢測到這個問題並重啟這個pod(重啟pod中所有的container)。當前前面也說了,當node宕機時,會將工作負載移動到其他的node上,這個裏的工作負載就是pod。(4) Label
??當時小編學習k8s的label也是半知半解,k8s的中文官檔把label解釋的太過官方,小編我楞是讀了3遍沒有看懂到底label強大在哪裏。容小編我先介紹一下label,label是一個key=value的鍵值對,其中key與value由用戶自定義。Label可以附加在各種資源對象上,例如:node、pod、service、RC等。一個資源對象又可以定義多個label,同一個label也可以被添加到任意數量的資源對象上,label通常在資源對象定義時創建,同時也可以在資源對象創建後動態添加。
??是不是聽了上面一堆向繞口令式的解釋,有種想打人的沖突。年輕人,不要心浮氣躁,其實label很簡單,學過JavaScript小夥伴一定知道選擇器,有什麽類選擇器、id選擇器。其實label就和JavaScript的標簽一樣,當我們給某一個資源對象用label打上標簽的時候,可以在其他的資源對象中通過“selector”來選擇相應的標簽,這樣就能準確的定位當擁有相應標簽的資源對象了。
??Label selector有兩種類型:
?? - 基於等式: name=redis-slave #匹配具有name=redis-slave標簽的資源對象
?? - 基於集合:env!=production #匹配不具有env=production標簽的資源對象
?? Label selector在k8s集群中的重要使用場景:
?? - Kube-controller進程通過資源對象RC上定義的label selector來篩選要監控的pod副本的數量,從而實現pod副本的數量始終符合預期設定的全自動控制流程
?? - Kube-proxy進程通過service的label selector來選擇對應的pod,自動建立起每一個service到對應pod的請求轉發路由表,從而實現了service的智能復雜均衡。
?? - 通過對某些node定義特定的label,並且在pod定義文件中使用nodeselector這種標簽地調度,kube-scheduler進程可以實現pod“定向調度”的特性。
?? 總之,使用label可以對對象創建多組標簽,label和label selector共同構成了k8s系統中最核心的應用模型,使得被管理對象能夠被精確地分組管理。(5) replication controller—RC
??RC其實是一個定義了期望的場景,即聲明某種pod的副本數量在任意時刻都符合某個預期的值,所以RC的定義包括以下幾部分:
?? - pod的期待的副本數
?? - 用於篩選目標pod的label selector
?? - 當pod的副本數小於預期數量,用於創建新的pod的pod模板
??如上圖所示,RC就控制著,label為name=app的pod保持兩個副本,如果有pod出現問題,RC會在相應的其他node上,根據RC中的template啟動pod,如果pod的副本數對於期待的值,RC會立刻終結掉多余數量的pod,這一切都是自動化的。當然如果要下線pod,可以先將RC中期待的副本數設置為0,之後再刪除RC即可。
??在k8s v1.2是RC升級成為了一個新的概念---replica set,其實二者的區別就是,RC使用的是基於等式的label selector,而RS使用的是基於集合的label selector。
??RC的特性:
?? - 通過RC可以實現pod的創建以及副本數的自動控制
?? - 通過改變RC中定義的pod的副本數,可以實現pod的擴容和縮容
?? - 通過改變RC中template的image版本,可以實現pod的滾動升級(優雅)(6) deployment
??deployment也是kubernetes v1.2引入的概念,它的宗旨在於更好的解決pod的編排問題。Deployment內部是使用了replica set來實現,其相似度與replication controller有90%以上。Deployment相對於RC最大的優勢就是,我們可以隨時知道當前pod部署的進度,這是因為我們期待系統啟動N個pod的目標狀態,是一個連續變化的過程,而deployment可以實時的跟蹤這個過程,這相當有助於排查錯誤。
?? Deployment的典型使用場景:
?? - 創建deployment對象來生成RS,並完成pod副本的創建過程
?? - 創建deployment對象來生成RS,並完成pod副本的創建過程
?? - 當deployment不穩定時,可以回滾到先前的一個deployment版本
?? - 暫停deployment以便於一次性修改多個podtemplatespec的配置項,之後再恢復deployment進行新的發布。(7) statefulSet
??在k8s中,pod的管理對象RC、deployment、daemonset和job都是無狀態的服務。但是很多時候我們需要有狀態的服務,比如MySQL、akka、zookeeper集群,他們的節點都有固定的身份、集群規模比較固定,集群中的每個節點都是有狀態的通常會持久化數據到磁盤、如果磁盤損壞則集群的某個節點將無法正常運行。如果使用RC/deployment來控制這些pod,那麽我們發現這是不滿足要求的。因為,pod的名稱是隨機產生的,pod的IP是在運行期間才確定的,而且我們需要pod在失敗重啟後,仍然能掛載集群中的數據共享。
??根據以上的問題,kubernetes從v1.4引入了petSet,並在v1.5正式改名為statuefulSet。而它的特性是:
??- statuefulSet裏的每一個pod都有穩定的、唯一的網絡表示(通過某種DNS實現),可以發現集群中的其他成員
??- statuefulSet裏的每一個pod都有穩定的、唯一的網絡表示(通過某種DNS實現),可以發現集群中的其他成員
??- statuefulSet中的pod采用的是穩定的持久化存儲卷,通過PV/PVC來實現。刪除pod時默認不會刪除與statuefulSet相關的存儲卷。(8) service
??service也是kubernetes裏最核心的資源對象之一,kubernetes的每一個service其實就是微服務架構中的一個“微服務”,由下圖所示:
??Service定義了一個服務的訪問入口,前端的frontend pod通過這個入口地址訪問其背後的一組由pod副本組成的集群實例,service與其後端的pod通過label selector無縫對接。但是我們想到了,雖然客戶端訪問的是service,但是最終訪問的還是對應的service下的pod中的應用程序,由於pod在失敗重啟後,他的IP地址是動態變化的,那麽我們如何確定我們該如何訪問服務。這裏service設計了一種巧妙的方法,service一旦創建,集群會自動為它分配一個可用的clusterIP,而且在service的整個生命周期中,這個clusterIP不會改變,我們只要使用最基本的TCP網絡通信,就可以訪問具體的service服務,那之後service與pod的通信,是通過每個node節點上的kube-proxy,它負責把對service的請求轉發到後端的某個pod中。這樣我們就可以通過一個固定的IP去訪問服務啦。當然最終我們訪問的仍然是宿主機的某個端口,我們可以在定義service的yaml文件中,配置相應的宿主機的端口映射來實現外部訪問集群內部的服務。
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort #這裏就設置了service到宿主機端口得到映射
ports:
- port: 8080
nodePort: 30001
selector:
app: myweb
(9) volume
??volume是pod中能夠被多個容器訪問的共享目錄。K8s中的volume跟docker中的volume比較相似,但是兩者不能等價。K8s中的volume定義在pod上,然後被一個pod例的多個容器掛載到具體的文件目錄下,其次,kubernetes中的volume與pod的生命周期相同,但與容器的生命周期不相關,當容器終止或者重啟時,volume中的數據不會丟失。
??Volume的使用也比較簡單,我們先在pod中聲明一個volume,然後在容器裏引用該volume並mount到容器的目錄中即可。例:
template:
metadata:
labels:
app: app-demo
tier: frontend
spec:
volume:
- name: datavol
emptyDir: {}
containers:
- name: tomcat-deom
image: tomcat
volumeMounts:
- mountPath: /mydata-data
name: datavol
??這裏就是使用了一個emptyDir 類型的volume,掛載到container中的/mydata-data下。
??這裏介紹一下volume的常見的類型:
?? - emptyDir:臨時的空目錄,當pod在node中創建時自動分配,當pod從node中移除時,其emptyDir分配的目錄中的數據也將被刪除
?? - hostPath:在pod上掛載宿主機的文件或者目錄。
?? - gecPersistenDisk:表示使用谷歌公有雲提供的永久磁盤存放volume的數據。
?? - nfs:表示使用nfs網絡文件系統提供的共享目錄存儲數據,當然想用這種方式,還需要額外的部署一個nfs服務。
(10) persistent volume---PV
??volume是定義在pod上的,屬於“計算資源”的一部分,而實際上,“網絡存儲”是相對獨立與“計算資源”而存在的一種實體資源。類似於網盤,而kubernetes中的persistent volume 和與之關聯的persistent volume claim ,也起到了類似的作用。
??PV的特點:
??- PV只能是網絡存儲,不屬於任何一個node,但是可以在任意一個node中訪問
??- PV並不是定義在pod上的,而是獨立於pod之外定義的
例:
#定義一個PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
nfs:
path: /somedata
server: 172.17.0.2
#如果pod想使用上面的PV,就需要定義一個PVC
kind: PersistentVolumeCliam
apiVersion: v1
metadata:
name: myclaim
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 8Gi
#然後在pod的volume總引用上述的PVC即可
spec:
volume:
- name: mypd
PersistentVolumeCliam:
cliamName: myclaim
最後介紹一下PV的狀態:
? Availale:空閑狀態
? Bound:已經綁定到某個PVC上
? Released:對應的PVC已經刪除,但是資源還沒有被集群回收
? Failed:PV自動回收失敗
(11) namespace
??namespace是kubernetes系統中實現多租戶的資源隔離。Namespace通過將集群內部的資源對象“分配”到不同的namespce中,形成邏輯上分組的不同項目、小組或者用戶組,便於不同的分組在共享使用整個集群的資源的同時還能被分別管理。
??我們可以在定義資源對象是,在他的metadata中指定他屬於的namespace:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv003
namespace: xxxx
??同時在不同的namespace中,還能限定不同租戶能占用的資源,如有CPU使用量、內存等等。
(12) annotation
??annotation與label類似,也使用key/value鍵值對的形式進行定義。不同的是,label具有嚴格的命名規則,他定義的是k8s對象的元數據,並且同於label selector。而annotation則是用戶任意定義的“附加信息” ,以便於外部工具的查找。
??大體來說,annotation定義如下信息:
?? - build信息、release信息、dicker鏡像地址等等
?? - 日誌庫、監控庫、分析庫等等資源庫的地址信息
?? - 程序調試工具信息,例如:工具名、版本號
?? - 團隊信息,如:聯系電話、負責人名稱、網址等
5. 部署中可能遇到的問題解決方案
(1)安裝k8s報錯:
[[email protected] ~]# yum install -y etcd kubernetes
原因:機器上安裝了docker,因為安裝kubernetes會自動安裝docker。
解決:卸載docker
[[email protected] ~]# yum list installed | grep docke
[[email protected] ~]# yum remove -y docker-ce.x86_64
[[email protected] ~]# rm -rf /var/lib/docker
(2)創建rc報錯:
[[email protected] yaml_file]# kubectl create -f mysql-rc.yaml
原因:k8s的認證問題。
[[email protected] yaml_file]#systemctl restart kube-apiserver
(3)Error syncing pod, skipping: failed to "StartContainer" for "POD" with Image
原因:查看pod的創建的報錯信息,發現是pod-infrastructure鏡像下載失敗,導致pod啟動失敗。
解決:
pod-infrastructure鏡像下載配置:
[[email protected] yaml_file]# vim /etc/kubernetes/kubelet
發現:
發現國內是無法通過這個鏈接下載到pod-infrastructure鏡像。
首先我們:
[[email protected] yaml_file]# docker search pod-infrastructure
下載我們需要的鏡像:
[[email protected] yaml_file]# docker pull docker.io/w564791/pod-infrastructure
然後將下載的鏡像推送到自己的私有倉庫:
[[email protected] yaml_file]#docker tag docker.io/w564791/pod-infrastructure 127.0.0.1:5000/ pod-infrastructure:v1.0
[[email protected] yaml_file]#docker push 127.0.0.1:5000/ pod-infrastructure:v1.0
修改拉取的pod-infrastructure鏡像的位置:
[[email protected] yaml_file]# vim /etc/kubernetes/kubelet:
改為:
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=127.0.0.1:5000/ pod-infrastructure:v1.0"
最後重啟集群:
#!/bin/bash
for SERVICES in kube-apiserver kube-controller-manager kube-scheduler; do
systemctl restart $SERVICES
done
systemctl restart kubelet
(4)運行docker容器時:報錯oci runtime error: exec failed: container_linux.go
原因:環境是CentOS Linux release 7.6.1810 (Core),但是由於內核的版本和docker存在兼容性問題。
解決:升級內核:
#查看內核版本
[[email protected] yaml_file]# uname -r
#升級內核
#導入key
[[email protected] yaml_file]# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
#安裝elrepo的yum源
[[email protected] yaml_file]# rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
#安裝內核在yum的ELRepo源中,有mainline頒布的,可以這樣安裝:
[[email protected] yaml_file]# yum --enablerepo=elrepo-kernel install kernel-ml-devel kernel-ml -y
#重啟Linux
[[email protected] yaml_file]#reboot
以上文章內容,參考至:《kubernetes權威指南》(紀念版)
kubernetes基礎篇(一)