1. 程式人生 > >Kubernetes 資源物件

Kubernetes 資源物件

[TOC] >在k8s中所有的物件都叫做資源,例如:pod,service等 #Pod 資源 pod是在k8s中最小單元,前面有提到,k8s支援自愈,彈性擴容等高階特性,那麼如果單純的在k8s節點中跑業務docker是沒有辦法支援這些高階特性,必須要有定製化的容器,那麼,pod就是這個官方已經定製化好的支援高階特性的容器,當啟動一個pod時,至少會有兩個容器,`pod容器``和業務容器`,多個業務容器會共享一個pod容器(一組容器的集合),那麼一個Pod中的容器共享網路名稱空間, **Pod容器分類** * Infrastructure Container:基礎容器,維護整個Pod網路空間 * InitContainers:初始化容器,先於業務容器開始執行 * Containers:業務容器,並行啟動 **Pod存在的意義:為親密性應用而存在** * 兩個應用之間發生檔案互動 * 兩個應用需要通過127.0.0.1或socker通訊 * 兩個應用需要發生頻繁的呼叫 **映象拉取策略** ```js imagePullPolicy 1、ifNotPresent:預設值,映象在宿主機上不存在時才拉取 2、Always:每次建立Pod都會重新拉取一次映象 3、Never:Pod永遠不會主動拉取這個映象 ``` **1.pod基本操作** ```bash // 指定yaml檔案建立pod kubectl create -f [yaml檔案路徑] // 檢視pod基本資訊 kubectl get pods // 檢視pod詳細資訊 kubectl describe pod [pod名] // 更新pod(修改了yaml內容) kubectl apply -f [yaml檔案路徑] // 刪除指定pod kubectl delete pod [pod名] // 強制刪除指定pod kubectl delete pod [pod名] --foce --grace-period=0 ``` **2.pod yaml配置檔案** ```yaml apiVersion: v1 kind: Pod metadata: name: nginx01 labels: app: web spec: containers: - name: nginx01 image: reg.betensh.com/docker_case/nginx:1.13 ports: - containerPort: 80 ``` **Pod與controllers的關係** * controllers:在叢集上管理和執行容器的物件 * 通過`label-selector`相關聯 * Pod通過控制器實現應用的運維,如伸縮,滾動升級等。 #RC 副本控制器 Replication Controller 副本控制器,應用託管在Kubernetes之後,Kubernetes需要保證應用能夠持續執行,這是RC的工作內容,它會確保任何時間Kubernetes中都有指定數量的Pod正在執行。在此基礎上,RC還提供了一些高階的特性,比如滾動升級、升級回滾等。 >在新版本的 Kubernetes 中建議使用 ReplicaSet(簡稱為RS )來取代 ReplicationController **1.建立一個rc** ```yaml apiVersion: v1 kind: ReplicationController metadata: name: myweb spec: replicas: 2 selector: app: myweb template: metadata: labels: app: myweb spec: containers: - name: nginx01 image: reg.betensh.com/docker_case/nginx:1.13 ports: - containerPort: 80 ``` 預設情況下`pod名`會以`rc名+隨機值`組成,如下: ```bash [root@k8s-master01 rc]# kubectl get pods NAME READY STATUS RESTARTS AGE myweb-0lp57 1/1 Running 0 45s myweb-zgfcf 1/1 Running 0 45s ``` RC通過標籤選擇器(labels)來控制pod,RC名稱必須要和標籤選擇器名稱一致 ```bash [root@k8s-master01 rc]# kubectl get rc -o wide NAME DESIRED CURRENT READY AGE CONTAINER(S) IMAGE(S) SELECTOR myweb 2 2 2 12m nginx01 reg.betensh.com/docker_case/nginx:1.13 app=myweb ``` **2.RC滾動升級** 前面我們已經建立一個v1版本的http-server的pod在k8s環境中,如果我們想要做一次版本升級該怎麼辦呢?難道把原來的pod停掉,再使用新的映象拉起來一個新的pod嗎,這樣做明顯是不合適的。 ![](https://img2020.cnblogs.com/blog/1679739/202003/1679739-20200313000029241-111715661.png) ```bash kubectl rolling-update myweb -f nginx_rc_v2.yaml --update-period=30s ``` **3.RC滾動回退** 假設現在myweb升級到myweb2,出現了bug,那麼先強制中斷升級 ```bash kubectl rolling-update myweb -f nginx_rc_v2.yaml --update-period=50s ``` 然後將myweb2的版本回滾到myweb ```bash kubectl rolling-update myweb myweb2 --rollback ``` #deployment 資源 deployment也是保證pod高可用的一種方式,明明有RC為何還要引入deployment呢? 因為deployment解決了RC的一個痛點,當使用RC升級容器版本後,標籤會發生變化,那麼svc的標籤還是原來的,這樣就需要手動修改svc配置檔案。 Deployment 為`Pod`和`ReplicaSet`之上,提供了一個宣告式定義(declarative)方法,用來替代以前的`ReplicationController`來方便的管理應用。 你只需要在`Deployment`中描述您想要的`目標狀態`是什麼,`Deployment controller `就會幫您將`Pod`和`ReplicaSet`的實際狀態改變到您的`目標狀態`。您可以定義一個全新的`Deployment`來,建立`ReplicaSet`或者刪除已有的 `Deployment`並建立一個新的來替換。也就是說`Deployment`是可以`管理多個ReplicaSet`的,如下圖: ![](https://img2020.cnblogs.com/blog/1679739/202003/1679739-20200313000056145-439475136.png) >雖然也 ReplicaSet 可以獨立使用,但建議使用 Deployment 來自動管理 ReplicaSet,這樣就無需擔心跟其他機制的不相容問題 **1.建立一個deployment** ```bash apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.13 ports: - containerPort: 80 // 啟動 [root@k8s-master01 deploy]# kubectl create -f nginx_deploy.yaml ``` 檢視deployment啟動狀態 >deployment會先啟動一個rs,而後在啟動pod ```bash [root@k8s-master01 deploy]# kubectl get all NAME READY STATUS RESTARTS AGE pod/nginx-deployment-fcfcc984f-t2bk4 1/1 Running 0 33s pod/nginx-deployment-fcfcc984f-vg7qt 1/1 Running 0 33s pod/nginx-deployment-fcfcc984f-zhwxg 1/1 Running 0 33s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/kubernetes ClusterIP 10.96.0.1 443/TCP 16h NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/nginx-deployment 3/3 3 3 33s NAME DESIRED CURRENT READY AGE replicaset.apps/nginx-deployment-fcfcc984f 3 3 3 33s ``` **2.關聯service** ```bash kubectl expose deployment nginx-deployment --port=80 --type=NodePort ``` 檢視svc ```bash [root@k8s-master01 deploy]# kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 443/TCP 16h nginx-deployment NodePort 10.96.171.141 80:31873/TCP 25s ``` 訪問svc地址以及埠 ```bash [root@k8s-master01 deploy]# curl -I 10.0.0.33:31873 HTTP/1.1 200 OK Server: nginx/1.13.12 Date: Thu, 14 Nov 2019 05:44:51 GMT Content-Type: text/html Content-Length: 612 Last-Modified: Mon, 09 Apr 2018 16:01:09 GMT Connection: keep-alive ETag: "5acb8e45-264" Accept-Ranges: bytes ``` **3.deployment升級** ```bash // 直接編輯對應deployment,並修改映象版本 kubectl edit deployment nginx-deployment // 通過 set image 釋出新的映象 kubectl set image deploy nginx-deployment nginx-deployment=nginx:1.17 ``` **4.deployment回滾** ```bash // 回滾到上一級版本 kubectl rollout undo deployment nginx-deployment // 回滾到指定版本 kubectl rollout undo deployment nginx-deployment --to-revision=1 // 檢視當前deploy歷史版本 kubectl rollout history deployment nginx-deployment ``` **5.命令列方式實現釋出版本** ```bash # kubectl run nginx --image=nginx:1.13 --replicas=3 --record # kubectl rollout history deployment nginx deployment.extensions/nginx # kubectl set image deployment nginx nginx=nginx:1.15 ``` #Headless Service 在K8S裡,我們想要通過name來訪問服務的方式就是在`Deployment`上面新增一層`Service`,這樣我們就可以通過`Service name`來訪問服務了,那其中的原理就是和`CoreDNS`有關,它將`Service name`解析成`Cluster IP`,這樣我們訪問`Cluster IP`的時候就通過Cluster IP作負載均衡,把流量分佈到`各個POD`上面。我想的問題是`CoreDNS`是否會直接解析POD的name,在Service的服務裡,是不可以的,因為Service有Cluster IP,直接被CoreDNS解析了,那怎麼才能讓它解析POD呢,有大牛提出了可以使用`Headless Service`,所以我們就來探究一下什麼是`Headless Service`。 `Headless Service`也是一種`Service`,但不同的是會定義`spec:clusterIP: None`,也就是不需要`Cluster IP的Service`。 我們首先想想`Service的Cluster IP`的工作原理:一個`Service`可能對應多個`EndPoint(Pod)`,`client`訪問的是`Cluster IP`,通過`iptables`規則轉到`Real Server`,從而達到負載均衡的效果。具體操作如下所示: 1、web-demo.yaml ```yaml #deploy apiVersion: apps/v1beta1 kind: StatefulSet metadata: name: web-demo namespace: dev spec: # 指定svc名稱 serviceName: web-demo-svc replicas: 3 template: metadata: labels: app: web-demo spec: containers: - name: web-demo image: 10.0.0.33/base_images/web-demo:v1.0 ports: - containerPort: 8080 resources: requests: memory: 1024Mi cpu: 500m limits: memory: 2048Mi cpu: 2000m livenessProbe: tcpSocket: port: 8080 initialDelaySeconds: 20 periodSeconds: 10 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 5 readinessProbe: httpGet: path: /hello port: 8080 scheme: HTTP initialDelaySeconds: 20 periodSeconds: 10 failureThreshold: 1 successThreshold: 1 timeoutSeconds: 5 --- #service apiVersion: v1 kind: Service metadata: name: web-demo-svc namespace: dev spec: ports: - port: 80 targetPort: 8080 protocol: TCP clusterIP: None selector: app: web-demo ``` 檢視svc,發現`ClusterIP`為`None` ``` $ kubectl get svc -n dev | grep "web-demo-svc" web-demo-svc ClusterIP None 80/TCP 12s ``` pod會按照順序建立 ```bash $ kubectl get pod -n dev NAME READY STATUS RESTARTS AGE web-demo-0 1/1 Running 0 7m2s web-demo-1 1/1 Running 0 6m39s web-demo-2 1/1 Running 0 6m15s ``` 登入到Cluster的內部pod ```bash $ kubectl exec -it web-demo-0 sh -n dev / # nslookup web-demo-svc Name: web-demo-svc Address 1: 10.244.2.67 web-demo-0.web-demo-svc.dev.svc.cluster.local Address 2: 10.244.3.12 web-demo-2.web-demo-svc.dev.svc.cluster.local Address 3: 10.244.1.214 web-demo-1.web-demo-svc.dev.svc.cluster.local ``` >總結:通過dns訪問,會返回後端pods的列表 #StatefulSet 首先`Deployment`只是用於無狀態服務,無差別並且沒有順序的Pod,而StatefulSet支援多個Pod之間的順序性,用於 每個Pod中有自己的編號,需要互相訪問,以及持久儲存區分 **Pod順序性** 1、headless-service.yaml ```yaml apiVersion: v1 kind: Service metadata: name: springboot-web-svc spec: ports: - port: 80 targetPort: 8080 protocol: TCP clusterIP: None selector: app: springboot-web ``` 2、statefulset.yaml ```yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: springboot-web spec: # serviceName 該欄位是告訴statefulSet用那個headless server去保證每個的解析 serviceName: springboot-web-svc replicas: 2 selector: matchLabels: app: springboot-web template: metadata: labels: app: springboot-web spec: containers: - name: springboot-web image: 10.0.0.33/base_images/web-demo:v1.0 ports: - containerPort: 8080 livenessProbe: tcpSocket: port: 8080 initialDelaySeconds: 20 periodSeconds: 10 failureThreshold: 3 successThreshold: 1 timeoutSeconds: 5 ``` 檢視pod ```bash $ kubectl get pod NAME READY STATUS RESTARTS AGE springboot-web-0 1/1 Running 0 118s springboot-web-1 1/1 Running 0 116s ``` 進入一個pod,而通過pod name訪問另外一個pod ```bash $ kubectl exec -it springboot-web-0 sh / # ping springboot-web-1.springboot-web-svc PING springboot-web-1.springboot-web-svc (10.244.2.68): 56 data bytes 64 bytes from 10.244.2.68: seq=0 ttl=62 time=1.114 ms 64 bytes from 10.244.2.68: seq=1 ttl=62 time=0.698 ms ``` **持久化儲存** 自動根據pod建立pvc ```yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: nginx-demo spec: serviceName: springboot-web-svc replicas: 2 selector: matchLabels: app: nginx-demo template: metadata: labels: app: springboot-web spec: containers: - name: springboot-web image: 10.0.0.33/base_images/nginx:1.13 ports: - containerPort: 8080 volumeMounts: - name: data mountPath: / volumeClaimTemplates: - metadata: name: data spec: accessModes: - ReadWriteOnce storageClassName: glusterfs-storage-class resources: requests: storage: 1Gi ``` #DaemonSet DaemonSet是在Kubernetes1.2 版本新增的一種資源物件 `DaemonSet`能夠讓`所有(或者一些特定)的Node`節點`僅執行一份Pod`。當節點加入到kubernetes叢集中,Pod會被(DaemonSet)排程到該節點上執行,當節點從kubernetes叢集中被移除,被(DaemonSet)排程的Pod會被移除,如果刪除DaemonSet,所有跟這個DaemonSet相關的pods都會被刪除。 在使用kubernetes來執行應用時,很多時候我們需要在一個`區域(zone)`或者`所有Node`上執行`同一個守護程序(pod)`,例如如下場景: * 每個Node上執行一個分散式儲存的守護程序,例如glusterd,ceph * 執行日誌採集器在每個Node上,例如fluentd,logstash * 執行監控的採集端在每個Node,例如prometheus node exporter,collectd等 DaemonSet的Pod排程策略與RC很類似,除了使用系統內建的排程演算法在每個Node上進行排程,也可以在Pod定義中使用NodeSelector或NodeAffinity來指定滿足條件的Node範圍進行排程 DaemonSet 資原始檔格式 ```yaml apiVersion: extensions/v1beta1 kind: DaemonSet metadata: ``` 下面例子定義為在每個Node上都啟動一個`filebeat`容器,其中掛載了宿主機目錄"/var/log/messages" ```yaml $ vi k8s-log-filebeat.yaml apiVersion: v1 kind: ConfigMap # 定義一個config檔案內容 metadata: name: k8s-logs-filebeat-config namespace: kube-system data: # 填寫filebeat讀取日誌相關資訊 filebeat.yml: |- filebeat.prospectors: - type: log paths: - /messages fields: app: k8s type: module fields_under_root: true output.logstash: # specified logstash port (by default 5044) hosts: ['10.0.0.100:5044'] --- apiVersion: apps/v1 kind: DaemonSet # DaemonSet 物件,保證在每個node節點執行一個副本 metadata: name: k8s-logs namespace: kube-system spec: selector: matchLabels: project: k8s app: filebeat template: metadata: labels: project: k8s app: filebeat spec: containers: - name: filebeat image: docker.elastic.co/beats/filebeat:6.8.1 args: [ "-c", "/etc/filebeat.yml", "-e", ] resources: requests: cpu: 100m memory: 100Mi limits: cpu: 500m memory: 500Mi securityContext: runAsUser: 0 # 進行實際掛載操作 volumeMounts: # 將configmap裡的配置掛載到 /etc/filebeat.yml 檔案中 - name: filebeat-config mountPath: /etc/filebeat.yml subPath: filebeat.yml # 將宿主機 /var/log/messages 路徑掛載到 /messages中 - name: k8s-logs mountPath: /messages # 定義卷 volumes: - name: k8s-logs hostPath: path: /var/log/messages type: File - name: filebeat-config configMap: name: k8s-logs-filebeat-config ``` 使用kubectl create 命令建立該DeamonSet ```bash $ kubectl create -f k8s-log-filebeat.yaml configmap/k8s-logs-filebeat-config created daemonset.apps/k8s-logs created ``` 檢視建立好的DeamonSet和Pod,可以看到在每個Node上都建立了一個Pod ```bash $ kubectl get ds -n kube-system | grep "k8s-logs" NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE k8s-logs 2 2 0 2 0 2m15s $ kubectl get pods -n kube-system -o wide | grep "k8s-logs" k8s-logs-gw4bs 0/1 Running 0 87s k8s-node01 k8s-logs-p6r6t 0/1 Running 0 87s k8s-node02 ``` 在kubernetes 1.6以後的版本中,DaemonSet也能執行滾動升級了,即在更新一個DaemonSet模板的時候,舊的Pod副本會被自動刪除,同時新的Pod副本會被自動建立,此時DaemonSet的更新策略(updateStrategy)為RollingUpdate,如下: ```yaml apiVersion: apps/v1 kind: DaemonSet metadata: name: k8s-logs namespace: kube-system spec: updateStrategy: type: RollingUpdate ``` >updateStrategy 的另外一個值是OnDelete,即只有當手工刪除了DaemonSet建立的Pod副本,新的Pod副本才會被創建出來,如果不設定updateStrategy的值,則在kubernetes 1.6之後的版本中會被預設設定為RollingUpdate(滾動升級)。 #Service 資源 我們都知道在kubernetes中以Pod為最小排程單位,並且它的特性就是不確定性,即隨時會被銷燬和重新建立、不確定性會導致每個Pod會通過排程器將其部署到不同的N個Node節點,這樣會導致Pod ip地址會發生變化; 舉個例子,web場景,分為前端後端,前端需要去呼叫後端資源,如果後端的Pod天生的不確定性導致IP地址不同,那麼前端肯定是無法做到自動切換連線後端的IP地址,所以需要通過Service去發現Pod並獲取其IP地址。 **Pod與Service的關係** * 防止Pod失聯.,獲取Pod資訊(通過label-selector關聯) * 定義一組Pod的訪問策略(負載均衡 TCP/UDP 4層) * 支援ClusterIP,NodePort以及LoadBalancer 三種類型 * Server的底層實現主要有iptables和IPVS二種網路模式 >每個Service關聯一個應用 **Service型別** * ClusterIP:預設,分配一個叢集內部可以訪問的虛擬IP(vip) * NodePort:在每個Node上分配一個埠作為外部訪問入口 * LoadBalancer:工作在特定的Cloud Provider上,例如Google Cloud, AWS,OpenStack **1.Cluster IP詳解:** >Cluster IP,也叫VIP,主要實現不同Pod之間互相訪問 ![](https://img2020.cnblogs.com/blog/1679739/202003/1679739-20200313000227406-384482100.png) ```yaml type: NodePort ports: - port: 80 targetPort: 80 protocol: TCP ``` **開啟proxy訪問** kubectl proxy 讓外部網路訪問K8S service的ClusterIP ```bash kubectl proxy --address='0.0.0.0' --accept-hosts='^*$' --port=8009 http://[k8s-master]:8009/api/v1/namespaces/[namespace-name]/services/[service-name]/proxy ``` 詳細:https://blog.csdn.net/zwqjoy/article/details/87865283 **2.Node Port詳解:** >實現外部使用者可以訪問節點Node,節點Node會將其流量轉發到內部Pod 訪問流程:使用者 -> 域名 -> 負載均衡器 -> NodeIP:Port ->PodIP:Port ![](https://img2020.cnblogs.com/blog/1679739/202003/1679739-20200313000244615-1717635191.png) 還可以直接在Node前面部署一個LB負載均衡,如圖: ![](https://img2020.cnblogs.com/blog/1679739/202003/1679739-20200313000316121-1623663324.png) ```yaml type: NodePort ports: - port: 80 targetPort: 80 nodePort: 30008 protocol: TCP ``` 引數解釋 ```yaml spec.ports.port:vip埠(cluster ip) spec.ports.nodePort:對映到宿主機的埠,即提供外部訪問的埠 spec.ports.targetPort:pod 埠 spec.selector: 標籤選擇器 ``` >建立nodePort型別的時候也會分配一個Cluster IP,方便提供給Pod之間訪問 **3、LoadBalancer詳解:** 訪問流程:使用者 -> 域名 -> 負載均衡器 -> NodeIP:Port ->PodIP:Port ![](https://img2020.cnblogs.com/blog/1679739/202003/1679739-20200313000333819-1779240660.png) **常規的docker對映場景:** >訪問 --> node IP:10.0.0.12 --> docker 容器: 172.16.48.2 如果當docker掛掉了後,在重新啟動一個docker,容器IP地址就會發生變化,那麼之前做的node和docker的對映就無效,就需要手動修改對映,這樣就顯得很麻煩 so,在k8s中新增了cluster IP,網段 10.254.0.0/16,series會自動建立這個cluster IP,也叫vip,當pod建立完成後會自動註冊到service裡,並且實現負載均衡(規則預設為rr),如果某個pod掛了後,會自動被剔除 >訪問 --> node IP:10.0.0.13 --> cluster IP:10.254.0.0/16 (service) --> pod IP:172.16.48.2 **建立service** ```yaml apiVersion: v1 kind: Service metadata: name: myweb spec: type: NodePort ports: - port: 80 nodePort: 30000 targetPort: 80 selector: app: myweb // 啟動 kubectl create -f nginx-svc.yaml ``` 檢視Service是否正常接管pod網路服務: ```bash [root@k8s-master01 svc]# kubectl get endpoints NAME ENDPOINTS AGE kubernetes 10.0.0.31:6443 2d myweb 172.16.12.2:80,172.16.15.4:80 2m ``` #ipvs和iptables工作原理 service底層流量轉發與負載均衡實現: * iptables * ipvs 1、一個service會建立很多的iptables規則(更新,非增量式) 2、iptables規則是從上到下逐條匹配(延時大)。 救世主:IPVS(核心態) >LVS基於IPVS核心排程模組實現的負載均衡,如:阿里雲SLB,基於LVS實現四層負載均衡。 **iptables:** * 靈活,功能強大(可以在資料包不同階段對包進行操作) * 規則遍歷匹配和更新,呈線性時延 **IPVS:** * 工作在核心態,有更好的效能 * 排程演算法豐富:rr,wrr,lc,wlc,ip h