在本文中,我們從技術細節上對kubernetes進行簡單運用介紹,利用一些yaml指令碼層面上例項告訴大家kubernetes基本概念。Kubernetes以及它呈現出的程式設計正規化值得你去使用和整合到自己的技術棧中。
kubernetes簡單介紹
1 kubernetes起源
Kubernetes最初認為是谷歌開源的容器叢集管理系統,是Google多年大規模容器管理技術Borg或Omega的開源版本。準確來說的話,kubernetes更是一個全新的平臺,一個全新的平臺管理工具,它是專門為job和service設計。完全開放,2014年6月開始接受公開的commit,任何人都可以提供意見。由於kubernetes簡化了開發、運維和管理負荷,越來越多的企業開始在生產環境使用,因此kubernetes得到了迅速的發展。
2 kubernetes功能
基於容器的應用部署、維護和滾動升級
負載均衡和服務發現
跨機器和跨地區的叢集排程
自動伸縮
無狀態服務和有狀態服務
廣泛的Volume支援
外掛機制保證擴充套件性
3 kubernetes核心元件
Kubernetes主要由以下幾個核心元件組成:
etcd儲存了整個叢集的狀態;
apiserver提供了資源操作的唯一入口,並提供認證、授權、訪問控制、API註冊和發現等機制;
controller manager負責維護叢集的狀態,比如故障檢測、自動擴充套件、滾動更新等;
scheduler負責資源的排程,按照預定的排程策略將Pod排程到相應的機器上;
kubelet負責維護容器的生命週期,同時也負責Volume(CVI)和網路(CNI)的管理;
Container runtime負責映象管理以及Pod和容器的真正執行(CRI);
kube-proxy負責為Service提供cluster內部的服務發現和負載均衡
kubernetes環境部署
使用kubeadm方式安裝kubernetes 1.10.0
Kubernetes叢集新增/刪除Node
Kubernetes Dashboard1.8.3部署
k8s原生的叢集監控方案(Heapster+InfluxDB+Grafana)
請注意:上述環境只是測試環境,生產環境部署大同小異。
kubernetes基本概念
1 Container
Container(容器)是一種行動式、輕量級的作業系統級虛擬化技術。它使用namespace隔離不同的軟體執行環境,並透過映象自包含軟體的執行環境,從而使得容器可以很方便的在任何地方執行。由於容器體積小且啟動快,因此可以在每個容器映象中打包一個應用程式。這種一對一的應用映象關係擁有很多好處。
使用容器,不需要與外部的基礎架構環境繫結,因為每一個應用程式都不需要外部依賴,更不需要與外部的基礎架構環境依賴,完美解決了從開發到生產環境的一致性問題。容器同樣比虛擬機器更加透明,這有助於監測和管理。尤其是容器程式的生命週期由基礎設施管理,而不是由容器內的程式對外隱藏時更是如此。最後,每個應用程式用容器封裝,管理容器部署就等同於管理應用程式部署。在Kubernetes必須要使用Pod來管理容器,每個Pod可以包含一個或多個容器。
2 Pod
關於Pod的概念主要有以下幾點:
Pod是kubernetes中你可以建立和部署的最小也是最簡的單位。一個Pod代表著叢集中執行的一個程式;
在Kubrenetes叢集中Pod的使用方式;
Pod中如何管理多個容器
理解Pod:
上面已經說了“Pod是kubernetes中你可以建立和部署的最小也是最簡的單位。一個Pod代表著叢集中執行的一個程式。”Pod中封裝著應用的容器(有的情況下是好幾個容器),儲存、獨立的網路IP,管理容器如何執行的策略選項。
Pod代表著部署的一個單位:kubernetes中應用的一個例項,可能由一個或者多個容器組合在一起共享資源。
請注意:Docker是kubernetes中最常用的容器執行時,但是Pod也支援其他容器執行時。
Kubrenetes叢集中Pod的兩種使用方式:
(1)一個Pod中執行一個容器“每個Pod中一個容器”的模式是最常見的用法;在這種使用方式中,你可以把Pod想象成是單個容器的封裝,kuberentes管理的是Pod而不是直接管理容器。
實戰:建立一個nginx容器 apiVersion: v1 kind: Pod metadata: name:nginx-test labels: app: web spec: containers: - name:front-end image:nginx:1.7.9 ports: -containerPort: 80
建立Pod: kubectl create -f ./pod1-deployment\ 檢視Pod: kubectl get po 檢視Pod詳細情況: kubectl describe po nginx-test 進入到Pod(容器)內部: kubectl exec -it nginx-test /bin/bash
(2)在一個Pod中同時執行多個容器
說明:在一個Pod中同時執行多個容器是一種比較高階的用法。只有當你的容器需要緊密配合協作的時候才考慮用這種模式。
一個Pod中也可以同時封裝幾個需要緊密耦合互相協作的容器,它們之間共享資源。這些在同一個Pod中的容器可以互相協作成為一個service單位——一個容器共享檔案,另一個“sidecar”容器來更新這些檔案。Pod將這些容器的儲存資源作為一個實體來管理。
實戰:在一個pod裡放置兩個容器:nginx與redis apiVersion: v1 kind: Pod metadata: name: rss-site labels: app: web spec: containers: - name:front-end image:nginx:1.7.9 ports: -containerPort: 80 - name:rss-reader image: redis ports: -containerPort: 88
建立Pod: kubectl create -f ./test-deployment 檢視pod kubectl get po 檢視Pod詳細情況 kubectl describe po rss-site 進入front-end內部: kubectl exec -it rss-site -c front-end /bin/bash 進入rss-reade內部: kubectl exec -it rss-site -c rss-reader /bin/bash
以上是關於Pod的簡單介紹。
3 Node
Node是Pod真正執行的主機,可以物理機,也可以是虛擬機器。為了管理Pod,每個Node節點上至少要執行container runtime(比如docker或者rkt)、kubelet和kube-proxy服務。
4 Namespace
Namespace是對一組資源和物件的抽象集合,比如可以用來將系統內部的物件劃分為不同的專案組或使用者組。常見的pods, services, replication controllers和deployments等都是屬於某一個namespace的(預設是default),而node, persistentVolumes等則不屬於任何namespace。
5 Deployment
我們既然有Pod了,為什麼還要使用Deployment呢?這是因為實際工作中,我們很少會直接在kubernetes中建立單個Pod。因為Pod的生命週期是短暫的,用後即焚的實體。Deployment為Pod和ReplicaSet提供了一個宣告式定義(declarative)方法,用來替代以前的ReplicationController來方便的管理應用。你只需要在Deployment中描述想要的目標狀態是什麼,Deploymentcontroller就會幫你將Pod和ReplicaSet的實際狀態改變到你的目標狀態。你可以定義一個全新的Deployment來建立ReplicaSet或者刪除已有的Deployment並建立一個新的來替換。
什麼是複製控制器(ReplicationController,RC)
RC是K8s叢集中最早的保證Pod高可用的API物件。透過監控執行中的Pod來保證叢集中執行指定數目的Pod副本。指定的數目可以是多個也可以是1個;少於指定數目,RC就會啟動執行新的Pod副本;多於指定數目,RC就會殺死多餘的Pod副本。即使在指定數目為1的情況下,透過RC執行Pod也比直接執行Pod更明智,因為RC也可以發揮它高可用的能力,保證永遠有1個Pod在執行。RC是K8s較早期的技術概念,只適用於長期伺服型的業務型別,比如控制小機器人提供高可用的Web服務。
什麼是副本集(Replica Set,RS)
RS是新一代RC,提供同樣的高可用能力,區別主要在於RS後來居上,能支援更多種類的匹配模式。副本集物件一般不單獨使用,而是作為Deployment的理想狀態引數使用。
Deployment典型的應用場景
定義Deployment來建立Pod和ReplicaSet
滾動升級和回滾應用;如果當前狀態不穩定,回滾到之前的Deployment revision。每次回滾都會更新Deployment的revision
擴容和縮容,擴容Deployment以滿足更高的負載
暫停和繼續Deployment,暫停Deployment來應用PodTemplateSpec的多個修復,然後恢復上線
實戰Deployment
比如,我們這裡定義一個簡單的nginx應用:
apiVersion:extensions/v1beta1 kind: Deployment metadata: name:nginx-test namespace:test spec: replicas: 3 template: metadata: labels: app: nginx spec: containers: -name: nginx image: nginx:1.7.9 ports: - containerPort: 80
建立deploy kubectl create -f ./nginx-deployment 檢視deploy kubectl get deploy --namespace=test 檢視rs(副本集) kubectl get rs --namespace=test 檢視pods(容器組) kubectl get po --namespace=test
關於Deployment的應用還有很多,如:擴容、縮容、滾動升級、回滾應用等,這裡由於篇幅的問題不再一一介紹。
5 Label
Label是識別Kubernetes物件的標籤,以key/value的方式附加到物件上(key最長不能超過63位元組,value可以為空,也可以是不超過253位元組的字串)。Label不提供唯一性,並且實際上經常是很多物件(如Pods)都使用相同的label來標誌具體的應用。Label定義好後其他物件可以使用Label Selector來選擇一組相同label的物件(比如ReplicaSet和Service用label來選擇一組Pod)。Label Selector支援以下幾種方式:
等式,如app=nginx和env!=production
集合,如env in(production, qa)
多個label(它們之間是AND關係),如app=nginx,env=test
6 Service Account
Service account作用
Service account是為了方便Pod裡面的程式呼叫Kubernetes API或其他外部服務。
Serviceaccount使用場景
執行在pod裡的程式需要呼叫Kubernetes API以及非Kubernetes API的其它服務。Service Account它並不是給kubernetes叢集的使用者使用的,而是給pod裡面的程式使用的,它為pod提供必要的身份認證。
與User account區別
User account是為人設計的,而service account則是為了Pod中的程式
User account是跨namespace的,而service account則是僅侷限它所在的namespace
實戰名稱空間
apiVersion: v1 kind: Namespace metadata: name:datagrand labels: name: test
建立namespace:test kubectl create -f ./test.yaml 檢視名稱空間test的sa kubectl get sa -n test 檢視名稱空間test生成的default kubectl get sa default -o yaml -n test 我們可以建立Deployment時,使用這個test名稱空間了,如上例Deployment實戰。
Service Account鑑權
Service Account為服務提供了一種方便的認知機制,但它不關心授權的問題。可以配合RBAC來為Service Account鑑權:
配置--authorization-mode=RBAC和--runtime-config=rbac.authorization.k8s.io/v1alpha1
配置--authorization-rbac-super-user=admin
定義Role、ClusterRole、RoleBinding或ClusterRoleBinding
實戰鑑權
我們在Kubernetes Dashboard1.8.3部署中,碰到首次登入出現訪問許可權報錯的問題,原因就是ServiceAccount的建立問題。
apiVersion:rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name:kubernetes-dashboard labels: k8s-app: kubernetes-dashboard roleRef: apiGroup:rbac.authorization.k8s.io kind:ClusterRole name:cluster-admin subjects: - kind:ServiceAccount name:kubernetes-dashboard namespace:kube-system
7 Secret
Secret介紹
Secret解決了密碼、token、金鑰等敏感資料的配置問題,而不需要把這些敏感資料暴露到映象或者Pod Spec中。Secret可以以Volume或者環境變數的方式使用。
Secret型別
Opaque(default):任意字串,base64編碼格式的Secret,用來儲存密碼、金鑰等
kubernetes.io/service-account-token:作用於ServiceAccount,就是kubernetes的Service Account中所說的。即用來訪問Kubernetes API,由Kubernetes自動建立,並且會自動掛載到Pod的/run/secrets/kubernetes.io/serviceaccount目錄中
kubernetes.io/dockercfg:作用於Docker registry,使用者下載docker映象認證使用。用來儲存私有docker registry的認證資訊
實戰Opaque Secret型別
Opaque型別的資料是一個map型別,要求value是base64編碼格式: 建立admin賬戶 echo -n "admin" | base64 YWRtaW4= echo -n "1f2d1e2e67df" | base64 MWYyZDFlMmU2N2Rm
建立secret.yaml cat >> secrets.yml << EOF apiVersion: v1 kind: Secret metadata: name:mysecret type: Opaque data: password:MWYyZDFlMmU2N2Rm username:YWRtaW4=
建立secret kubectl create -f secrets.yml
檢視secret執行狀態 kubectl get secret --all-namespaces
Secret使用方式
以Volume方式
以環境變數方式
實戰Secret使用Volume方式
apiVersion: v1 kind: Pod metadata: name: mypod labels: name: wtf spec: volumes: - name:secrets secret: secretName: mysecret containers: - image:nginx:1.7.9 name: nginx volumeMounts: - name:secrets mountPath:"/etc/secrets" readOnly:true ports: - name: cp containerPort: 5432 hostPort:5432
說明:這樣就可以透過檔案的方式掛載到容器內,在/etc/secrets目錄下回生成這個檔案。
實戰Secret使用環境變數方式
apiVersion:extensions/v1beta1 kind: Deployment metadata: name:wordpress-deployment spec: replicas: 2 template: metadata: labels: app:wordpress spec: containers: - name:"wordpress" image:"wordpress:latest" ports: -containerPort: 80 env: - name:WORDPRESS_DB_USER valueFrom: secretKeyRef: name: mysecret key: username - name:WORDPRESS_DB_PASSWORD valueFrom: secretKeyRef: name: mysecret key: password
檢視Pod執行狀態 kubectl get po NAME READY STATUS RESTARTS AGE wordpress-deployment-6b569fbb7d-8qcpg 1/1 Running 0 2m wordpress-deployment-6b569fbb7d-xwwkg 1/1 Running 0 2m
進入容器內部檢視環境變數 kubectl exec -itwordpress-deployment-694f4c79b4-cpsxw /bin/bash root@wordpress-deployment-694f4c79b4-cpsxw:/var/www/html#env WORDPRESS_DB_USER=admin WORDPRESS_DB_PASSWORD=1f2d1e2e67df
8 ConfigMap
Configure說明
ConfigMaps允許你將配置檔案、命令列引數或環境變數中讀取的配置資訊與docker image分離,以保持集裝箱化應用程式的便攜性。即ConfigMapAPI給我們提供了向容器中注入配置資訊的機制。
理解ConfigMaps和Pods
ConfigMap API資源用來儲存key-value pair配置資料,這個資料可以在pods裡使用,或者被用來為像controller一樣的系統元件儲存配置資料。雖然ConfigMap跟Secrets類似,但是ConfigMap更方便的處理不含敏感資訊的字串。
注意:ConfigMaps不是屬性配置檔案的替代品。ConfigMaps只是作為多個properties檔案的引用。你可以把它理解為Linux系統中的/etc目錄,專門用來儲存配置檔案的目錄。
實戰建立ConfigMap
kind: ConfigMap apiVersion: v1 metadata: creationTimestamp: 2016-02-18T19:14:38Z name:example-config namespace:default data: example.property.1: hello example.property.2: world example.property.file: |- property.1=value-1 property.2=value-2 property.3=value-3
data一欄包括了配置資料,ConfigMap可以被用來儲存單個屬性,也可以用來儲存一個配置檔案。配置資料可以透過很多種方式在Pods裡被使用。ConfigMaps可以被用來:
設定環境變數的值
在容器裡設定命令列引數
在資料卷裡面建立config檔案
9 Volum
為什麼需要Volume
容器磁碟上檔案的生命週期是短暫的,這就使得在容器中執行重要應用時出現一些問題。比如,當容器崩潰時,kubelet會重啟它,但是容器中的檔案將丟失--容器以乾淨的狀態(映象最初的狀態)重新啟動。
其次,在Pod中同時執行多個容器時,這些容器之間通常需要共享檔案。Kubernetes中的Volume抽象就很好的解決了這些問題。
Volume背景
Docker中也有一個volume的概念,儘管它稍微寬鬆一些,管理也很少。在Docker中,卷就像是磁碟或是另一個容器中的一個目錄。它的生命週期不受管理,直到最近才有了local-disk-backed卷。Docker現在提供了卷驅動程式,但是功能還非常有限(例如Docker1.7只允許每個容器使用一個卷驅動,並且無法給卷傳遞引數)。
Kubernetes中的卷有明確的壽命——與封裝它的Pod相同。所以,卷的生命比Pod中的所有容器都長,當這個容器重啟時資料仍然得以儲存。當然,當Pod不再存在時,卷也將不復存在。也許更重要的是,Kubernetes支援多種型別的卷,Pod可以同時使用任意數量的卷。要使用卷,需要為pod指定為卷(spec.volumes欄位)以及將它掛載到容器的位置(spec.containers.volumeMounts欄位)。
Volume型別
Kubernetes支援以下型別的卷:
awsElasticBlockStore、azureDisk、azureFile、cephfs、csi、downwardAPI、emptyDir、fc (fibre channel)、flocker、gcePersistentDisk、gitRepo、glusterfs、hostPath、iscsi、local、nfs、persistentVolumeClaim、projected、portworxVolume、quobyte、rbd、scaleIO、secret、storageos、vsphereVolume等
K8S的儲存系統分類
K8S的儲存系統從基礎到高階大致分為三個層次:普通Volume,Persistent Volume和動態儲存供應。
普通Volume
最簡單的普通Volume是單節點Volume。它和Docker的儲存卷類似,使用的是Pod所在K8S節點的本地目錄。
persistent volume
它和普通Volume的區別是什麼呢?
普通Volume和使用它的Pod之間是一種靜態繫結關係,在定義Pod的檔案裡,同時定義了它使用的Volume。Volume是Pod的附屬品,我們無法單獨建立一個Volume,因為它不是一個獨立的K8S資源物件。而Persistent Volume簡稱PV是一個K8S資源物件,所以我們可以單獨建立一個PV。它不和Pod直接發生關係,而是透過Persistent Volume Claim,簡稱PVC來實現動態繫結。Pod定義裡指定的是PVC,然後PVC會根據Pod的要求去自動繫結合適的PV給Pod使用。
PV的訪問模式有三種:
ReadWriteOnce:是最基本的方式,可讀可寫,但只支援被單個Pod掛載
ReadOnlyMany:可以以只讀的方式被多個Pod掛載
ReadWriteMany:這種儲存可以以讀寫的方式被多個Pod共享。比較常用的是NFS
一般來說,PV和PVC的生命週期分為5個階段:
Provisioning,即PV的建立,可以直接建立PV(靜態方式),也可以使用StorageClass動態建立
Binding,將PV分配給PVC
Using,Pod透過PVC使用該Volume
Releasing,Pod釋放Volume並刪除PVC
Reclaiming,回收PV,可以保留PV以便下次使用,也可以直接從雲端儲存中刪除
根據這5個階段,Volume的狀態有以下4種:
Available:可用
Bound:已經分配給PVC
Released:PVC解綁但還未執行回收策略
Failed:發生錯誤
變成Released的PV會根據定義的回收策略做相應的回收工作。有三種回收策略:
Retain就是保留現場,K8S什麼也不做,等待使用者手動去處理PV裡的資料,處理完後,再手動刪除PV
Delete K8S會自動刪除該PV及裡面的資料
Recycle K8S會將PV裡的資料刪除,然後把PV的狀態變成Available,又可以被新的PVC繫結使用
在實際使用場景裡,PV的建立和使用通常不是同一個人。這裡有一個典型的應用場景:管理員建立一個PV池,開發人員建立Pod和PVC,PVC裡定義了Pod所需儲存的大小和訪問模式,然後PVC會到PV池裡自動匹配最合適的PV給Pod使用。
前面在介紹PV的生命週期時,提到PV的供給有兩種方式,靜態和動態。其中動態方式是透過StorageClass來完成的,這是一種新的儲存供應方式。
使用StorageClass有什麼好處呢?除了由儲存系統動態建立,節省了管理員的時間,還有一個好處是可以封裝不同型別的儲存供PVC選用。在StorageClass出現以前,PVC繫結一個PV只能根據兩個條件,一個是儲存的大小,另一個是訪問模式。在StorageClass出現後,等於增加了一個繫結維度。
比如這裡就有兩個StorageClass,它們都是用谷歌的儲存系統,但是一個使用的是普通磁碟,我們把這個StorageClass命名為slow。另一個使用的是SSD,我們把它命名為fast。在PVC裡除了常規的大小、訪問模式的要求外,還透過annotation指定了Storage Class的名字為fast,這樣這個PVC就會繫結一個SSD,而不會繫結一個普通的磁碟。限於篇幅問題,這裡簡單說一下emptyDir、nfs、PV和PVC。
實戰--emptyDir
emptyDir說明
EmptyDir型別的volume建立於pod被排程到某個宿主機上的時候,而同一個pod內的容器都能讀寫EmptyDir中的同一個檔案。一旦這個pod離開了這個宿主機,EmptyDir中的資料就會被永久刪除。所以目前EmptyDir型別的volume主要用作臨時空間,比如Web伺服器寫日誌或者tmp檔案需要的臨時目錄。
apiVersion: v1 kind: Pod metadata: labels: name:test-emptypath role: master name: test-emptypath spec: containers: - name:test-emptypath image:nginx:1.7.9 volumeMounts: - name:log-storage mountPath:/tmp/ volumes: - name:log-storage emptyDir: {}
實戰使用共享卷的標準多容器Pod
apiVersion: v1 kind: Pod metadata: name:datagrand spec: containers: - name: test1 image:nginx:1.7.9 volumeMounts: - name:log-storage mountPath:/usr/share/nginx/html - name: test2 image:centos volumeMounts: - name:log-storage mountPath:/html command:["/bin/sh","-c"] args: - whiletrue;do data>> /html/index.html; sleep 1; done volumes: - name:log-storage emptyDir:{}
簡單解釋下上面的內容:在這個例子中,我們定義了一個名為HTML的卷。它的型別是emptyDir,這意味著當一個Pod被分配到一個節點時,卷先被建立,並只要Pod在節點上執行時,這個卷仍存在。正如名字所說,它最初是空的。
第一容器執行nginx的伺服器並將共享卷掛載到目錄/ usr /share/ nginx /html。第二容器使用centos的映象,並將共享卷掛載到目錄/HTML。每一秒,第二容器新增當前日期和時間到index.html檔案中,它位於共享卷。當使用者發出一個HTTP請求到Pod,nginx的伺服器讀取該檔案並將其傳遞給響應請求的使用者。
實戰--NFS卷
NFS卷說明
nfs卷允許將現有的NFS(網路檔案系統)共享掛載到你的容器中。不像emptyDir,當刪除Pod時,nfs卷的內容被保留,卷僅僅是被解除安裝。這意味著NFS卷可以預填充資料,並且可以在pod之間“切換”資料。NFS可以被多個寫入者同時掛載。
NFS卷使用注意
請先部署好自己的NFS服務
在使用共享之前,必須執行自己的NFS伺服器並執行共享
實戰pod內的檔案共享
apiVersion: v1 kind: Pod metadata: name:nginx-test labels: app: nginx spec: containers: - name: nginx image:nginx:1.7.9 ports: -containerPort: 80 volumeMounts: #Mount thepath to the container - mountPath:"/tmp/" name:pv0003 volumes: - name: pv0003 nfs: #fixed:This ip is the addressof the nfs server server:192.168.246.169 #fixed:This path is sharedexternally by the nfs server path:"/data"
實戰PV和PVC
nfs作為k8s的網路儲存驅動,可以滿足持久儲存業務的需求,支援多節點讀寫。下面是兩個Pod同時使用一個永續性volume例項。
#建立PV apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv spec: capacity: storage: 4Gi accessModes: -ReadWriteMany nfs: server:192.168.246.168 ##NFS伺服器的ip地址 path:"/data" ##NFS伺服器上的共享目錄
#建立PVC apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc spec: accessModes: - ReadWriteMany storageClassName: "" resources: requests: storage:3Gi
#建立Deployment apiVersion: apps/v1 kind: Deployment metadata: name:nginx-deployment labels: app: nginx spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app:nginx spec: containers: - name:nginx image:nginx:1.7.9 volumeMounts: -mountPath: "/wtf" name: datadir volumes: - name:datadir persistentVolumeClaim: claimName: nfs-pvc -containerPort: 80
10 Service
Kubernetes Pod是有生命週期的,它們可以被建立,也可以被銷燬,然而一旦被銷燬生命就永遠結束。透過ReplicaSets能夠動態地建立和銷燬Pod(例如,需要進行擴縮容,或者執行滾動升級)。每個Pod都會獲取它自己的IP地址,即使這些IP地址不總是穩定可依賴的。這會導致一個問題:在Kubernetes叢集中,如果一組Pod(稱為backend)為其它Pod(稱為frontend)提供服務,那麼那些frontend該如何發現,並連線到這組Pod中的哪些backend呢?
什麼是Service
Kubernetes Service定義了這樣一種抽象:一個Pod的邏輯分組,一種可以訪問它們的策略——通常稱為微服務。這一組Pod能夠被Service訪問到,通常是透過Label Selector(檢視下面瞭解,為什麼可能需要沒有selector的Service)實現的。
舉個例子,考慮一個圖片處理backend,它執行了3個副本。這些副本是可互換的—— frontend不需要關心它們呼叫了哪個backend副本。然而組成這一組backend程式的Pod實際上可能會發生變化,frontend客戶端不應該也沒必要知道,而且也不需要跟蹤這一組backend的狀態。Service定義的抽象能夠解耦這種關聯。
對Kubernetes叢集中的應用,Kubernetes提供了簡單的Endpoints API,只要Service中的一組Pod發生變更,應用程式就會被更新。對非Kubernetes叢集中的應用,Kubernetes提供了基於VIP的網橋的方式訪問Service,再由Service重定向到backend Pod。
定義Service
有selector的單埠Service
一個Service在Kubernetes中是一個REST物件,和Pod類似。像所有的REST物件一樣,Service定義可以基於POST方式,請求apiserver建立新的例項。例如,假定有一組Pod,它們對外暴露了9376埠,同時還被打上"app=MyApp"標籤。
kind: Service apiVersion: v1 metadata: name:my-service spec: selector: app: MyApp ports: - protocol:TCP port: 80 targetPort: 9376
上述配置將建立一個名稱為“my-service”的Service物件,它會將請求代理到使用TCP埠9376,並且具有標籤"app=MyApp"的Pod上。這個Service將被指派一個IP地址(通常稱為“Cluster IP”),它會被服務的代理使用(見下面)。該Service的selector將會持續評估,處理結果將被POST到一個名稱為“my-service”的Endpoints物件上。
需要注意的是,Service能夠將一個接收埠對映到任意的targetPort。預設情況下,targetPort將被設定為與port欄位相同的值。可能更有趣的是,targetPort可以是一個字串,引用了backend Pod的一個埠的名稱。但是,實際指派給該埠名稱的埠號,在每個backend Pod中可能並不相同。對於部署和設計Service,這種方式會提供更大的靈活性。例如,可以在backend軟體下一個版本中,修改Pod暴露的埠,並不會中斷客戶端的呼叫。
Kubernetes Service能夠支援TCP和UDP協議,預設TCP協議。
有selector的多埠Service
很多Service需要暴露多個埠。對於這種情況,Kubernetes支援在Service物件中定義多個埠。當使用多個埠時,必須給出所有的埠的名稱,這樣Endpoint就不會產生歧義,例如:
kind: Service apiVersion: v1 metadata: name:my-service spec: selector: app: MyApp ports: - name:http protocol: TCP port: 80 targetPort: 9376 - name:https protocol: TCP port:443 targetPort: 9377
沒有selector的Service
Service抽象了該如何訪問Kubernetes Pod,但也能夠抽象其它型別的backend,例如:
希望在生產環境中使用外部的資料庫叢集,但測試環境使用自己的資料庫。
希望服務指向另一個Namespace中或其它叢集中的服務。
正在將工作負載轉移到Kubernetes叢集,和執行在Kubernetes叢集之外的backend。
根據以上的應用場景,我們都能夠定義沒有selector的Service,如下:
kind: Service apiVersion: v1 metadata: name:my-service spec: ports: - protocol:TCP port: 80 targetPort: 9376
由於這個Service沒有selector,就不會建立相關的Endpoints物件。可以手動將Service對映到指定的Endpoints:
kind: Endpoints apiVersion: v1 metadata: name:my-service subsets: - addresses: - ip:10.0.0.3 ##Endpoint IP = PodIP +ContainerPort ports: - port:9376
注意:Endpoint IP地址不能是loopback(127.0.0.0/8)、link-local(169.254.0.0/16)、或者link-local多播(224.0.0.0/24)。訪問沒有selector的Service,與有selector的Service的原理相同。請求將被路由到使用者定義的Endpoint(該示例中為10.0.0.3:9376)。
釋出服務——服務型別
對一些應用(如Frontend)的某些部分,可能希望透過外部(Kubernetes叢集外部)IP地址暴露Service。Kubernetes ServiceTypes允許指定一個需要的型別的Service,預設是ClusterIP型別。Type的取值以及行為如下:
ClusterIP透過叢集的內部IP暴露服務,選擇該值,服務只能夠在叢集內部可以訪問,這也是預設的ServiceType。
NodePort透過每個Node上的IP和靜態埠(NodePort)暴露服務。NodePort服務會路由到ClusterIP服務,這個ClusterIP服務會自動建立。透過請求:,可以從叢集的外部訪問一個NodePort服務。
LoadBalancer使用雲提供商的負載均衡器,可以向外部暴露服務。外部的負載均衡器可以路由到NodePort服務和ClusterIP服務。
ExternalName透過返回CNAME和它的值,可以將服務對映到externalName欄位的內容(例如,foo.bar.example.com)。沒有任何型別代理被建立,這隻有Kubernetes 1.7或更高版本的kube-dns才支援。
NodePort型別
如果設定type的值為"NodePort",Kubernetes master將從給定的配置範圍內(預設:30000-32767)分配埠,每個Node將從該埠(每個Node上的同一埠)代理到Service。該埠將透過Service的spec.ports[*].nodePort欄位被指定。如果需要指定的埠號,可以配置nodePort的值,系統將分配這個埠,否則呼叫API將會失敗(比如,需要關心埠衝突的可能性)。
kubernetes實戰--edusoho平臺建立
1 檔案目錄結構
# pwd /data # tree -L 3 . ├── mysql │ ├── conf │ │ └── my.cnf │ └── data │ ├── auto.cnf │ ├── edusoho │ ├── ibdata1 │ ├── ib_logfile0 │ ├── ib_logfile1 │ ├── mysql │ └── performance_schema ├── nginx │ ├── conf │ │ └── nginx.conf │ ├── edusoho │ │ ├── api │ │ ├── app │ │ ├── bootstrap │ │ ├── plugins │ │ ├── src │ │ ├── vendor │ │ ├── vendor_user │ │ └── web │ └── log │ └── error.log ├── php │ ├── log │ │ └── php-fpm.log │ ├── php-fpm.conf │ ├── php.ini │ └── www.conf
2 Pod的yaml檔案
apiVersion: v1 kind: Pod metadata: name:lamp-edusoho labels: app:lamp-edusoho restartPolicy: Always spec: containers: - name: nginx abels: app:lamp-nginx image:dockerhub.datagrand.com/global/nginx:v1 ports: -containerPort: 80 volumeMounts: - name:datadir mountPath: "/var/log/nginx/error.log" subPath:./nginx/log/error.log - name:datadir mountPath: "/etc/nginx/nginx.conf" subPath:./nginx/conf/nginx.conf - name:datadir mountPath: "/usr/share/nginx/html" subPath:./nginx/edusoho - name: php image:dockerhub.datagrand.com/global/php:v1 ports: -containerPort: 9000 volumeMounts: -mountPath: /usr/local/php/etc/php-fpm.conf name:datadir subPath:./php/php-fpm.conf -mountPath: /usr/local/php/etc/php-fpm.d/www.conf name:datadir subPath:./php/www.conf -mountPath: /usr/local/php/etc/php.ini name:datadir subPath:./php/php.ini -mountPath: /usr/local/php/var/log/php-fpm.log name:datadir subPath:./php/log/php-fpm.log -mountPath: /usr/share/nginx/html name:datadir subPath:./nginx/edusoho - name: mysql image:dockerhub.datagrand.com/global/mysql:5.6 ports: -containerPort: 3306 env: - name:MYSQL_ROOT_PASSWORD value:"123456" - name:MYSQL_DATABASE value:"edusoho" - name:MYSQL_USER value:"edusoho" - name:MYSQL_PASSWORD value:"edusoho" args:['--character-set-server=utf8'] volumeMounts: - name:datadir mountPath: "/var/lib/mysql" subPath:./mysql/data - name:datadir mountPath: "/etc/my.cnf" subPath:./mysql/conf/my.cnf volumes: - name:datadir persistentVolumeClaim: claimName:nfs-pvc
3 PV的yaml檔案
apiVersion: v1 kind: PersistentVolume metadata: name: nfs-pv spec: capacity: storage: 4Gi accessModes: -ReadWriteMany nfs: server:192.168.246.168 ##NFS伺服器的ip地址 path:"/data" ##NFS伺服器上的共享目錄
4 PVC的yaml檔案
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: nfs-pvc spec: accessModes: -ReadWriteMany storageClassName: "" resources: requests: storage:3Gi
5 Service的yaml檔案
apiVersion: v1 kind: Service metadata: name: edusoho labels: app: edusoho spec: type: NodePort ports: - port: 80 nodePort:32756 selector: app:lamp-edusoho
6 命令彙總
檢視Pod kubectl get po -o wide 檢視Service kubectl get svc 進入容器內部某個應用,如這裡的nginx kubectl exec -it lamp-edusoho -c nginx /bin/bash
7 訪問安裝Edusoho平臺
http://192.168.246.168:32756/install/start-install.php 說明:這裡的192.168.246.168是kubernetes的node節點IP,32756是Service中定義的nodePort。
kubernetes概念--Service:https://kubernetes.io/docs/concepts/services-networking/service/
kubernetes官網教程:https://kubernetes.io/docs/tutorials/
Kubernetes中的Persistent Volume解析:https://jimmysong.io/posts/kubernetes-persistent-volume/
關於作者
吳騰飛:達觀資料運維工程師 ,負責達觀資料系統平臺和應用業務的快速部署、監控、最佳化及維護,設計並研發自動化運維工具和平臺,資料庫的日常維護。
對自動化運維,Docker容器、虛擬化技術和容器編排kubernetes相關領域有濃厚興趣。