Kubernetes 資源物件
阿新 • • 發佈:2020-03-13
[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