k8s架構及服務詳解


1.容器及其三要素

1.1.容器是什么

容器的本質是一種特殊的進程。

在linux容器中有三個重要的概念:Namespace、Cgroups、rootfs。

Namespace做隔離,讓進程只能看到Namespace中的世界;

Cgroups     做限制,讓這個“世界”圍着一個看不見的牆。

rootfs          做文件系統,rootfs 只是一個操作系統所包含的文件、配置和目錄,並不包括操作系統內核。

這樣就實現了進程在我們所看到的一個與世隔絕的房間,這個房間就是Pass項目賴以生存的"沙盒"。

1.2.Namespace

進入容器后,ps命令看到的容器的應用進程都是1號進程,這個其實是pid  namespace導致,他其實就是個障眼法,

讓你看到的是類似於一個新的虛擬機新環境,其實是不一樣的,容器就是一個運行的進程,而容器中的其他進程則是pid為1的子進程。

除了剛剛pid namespace,還有其它的namespace如下:

 容器是怎么新建namespace的?

 docker創建容器,其實就是linux系統的一次fork的調用,

在進行fork調用時,會傳入一些flag參數,這個參數可以控制對linux內核調用新的namespace。

1.3.rootfs

掛載在容器根目錄上、用來為容器進程提供隔離后執行環境的文件系統,就是所謂的“容器鏡像”。它還有一個更為專業的名字,叫作:rootfs(根文件系統)。

容器的rootfs由三部分組成,1:只讀層、2:可讀寫層、3:init層

1.只讀層:都以增量的方式分別包含了  操作系統的一部分。

2.可讀寫:就是專門用來存放你修改 rootfs 后產生的增量,無論是增、刪、改,都發生在這里。而當我們使用完了這個被修改過的容器之后,還可以使用 docker commit 和 push 指令,保存這個被修改過的可讀寫層,並上傳到 Docker Hub 上,供其他人使用;而與此同時,原先的只讀層里的內容則不會有任何變化。這,就是增量 rootfs 的好處。

3.Init 層:是 Docker 項目單獨生成的一個內部層,專門用來存放 /etc/hosts、/etc/resolv.conf 等信息。

5.kubernetes Service:讓客戶端發現pod並與之通信

5.1.Service介紹

5.1.1.Serice簡介

5.1.1.1什么是Service

  service是k8s中的一個重要概念,主要是提供負載均衡和服務自動發現。

  Service 是由 kube-proxy 組件,加上 iptables 來共同實現的。

5.1.1.2.Service的創建

   創建Service的方法有兩種:

  1.通過kubectl expose創建

#kubectl expose deployment nginx --port=88 --type=NodePort --target-port=80 --name=nginx-service

這一步說是將服務暴露出去,實際上是在服務前面加一個負載均衡,因為pod可能分布在不同的結點上。
–port:暴露出去的端口
–type=NodePort:使用結點+端口方式訪問服務
–target-port:容器的端口
–name:創建service指定的名稱

  2.通過yaml文件創建

  創建一個名為hostnames-yaohong的服務,將在端口80接收請求並將鏈接路由到具有標簽選擇器是app=hostnames的pod的9376端口上。

  使用kubectl creat來創建serivice

apiVersion: v1
kind: Service
metadata:
  name: hostnames-yaohong
spec:
  selector:
    app: hostnames
  ports:
  - name: default
    protocol: TCP
    port: 80     //該服務的可用端口
    targetPort: 9376    //具有app=hostnames標簽的pod都屬於該服務

5.1.1.3.檢測服務

  使用如下命令來檢查服務:

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.187.0.1   <none>        443/TCP   18d

5.1.1.4.在運行的容器中遠程執行命令

  使用kubectl exec 命令來遠程執行容器中命令

$ kubectl -n kube-system exec coredns-7b8dbb87dd-pb9hk -- ls /
bin
coredns
dev
etc
home
lib
media
mnt
proc
root
run
sbin
srv
sys
tmp
usr
var

雙橫杠(--)代表kubectl命令項的結束,在雙橫杠后面的內容是指pod內部需要執行的命令。

  

5.2.連接集群外部的服務

5.2.1.介紹服務endpoint

服務並不是和pod直接相連的,介於他們之間的就是Endpoint資源。

Endpoint資源就是暴露一個服務的IP地址和端口列表。

通過service查看endpoint方法如下:

$ kubectl -n kube-system get svc kube-dns
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterI
P   10.187.0.2   <none>        53/UDP,53/TCP   19d

$ kubectl -n kube-system describe svc kube-dns
Name:              kube-dns
Namespace:         kube-system
Labels:            addonmanager.kubernetes.io/mode=Reconcile
                   k8s-app=kube-dns
                   kubernetes.io/cluster-service=true
                   kubernetes.io/name=CoreDNS
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{"prometheus.io/scrape":"true"},"labels":{"addonmanager.kubernetes.io/mode":...
                   prometheus.io/scrape: true
Selector:          k8s-app=kube-dns
Type:              ClusterIP
IP:                10.187.0.2
Port:              dns  53/UDP
TargetPort:        53/UDP
Endpoints:         10.186.0.2:53,10.186.0.3:53     //代表服務endpoint的pod的ip和端口列表
Port:              dns-tcp  53/TCP
TargetPort:        53/TCP
Endpoints:         10.186.0.2:53,10.186.0.3:53
Session Affinity:  None
Events:            <none>

  

直接查看endpoint信息方法如下:

#kubectl -n kube-system get endpoints kube-dns
NAME       ENDPOINTS                                               AGE
kube-dns   10.186.0.2:53,10.186.0.3:53,10.186.0.2:53 + 1 more...   19d

#kubectl -n kube-system describe  endpoints kube-dns
Name:         kube-dns
Namespace:    kube-system
Labels:       addonmanager.kubernetes.io/mode=Reconcile
              k8s-app=kube-dns
              kubernetes.io/cluster-service=true
              kubernetes.io/name=CoreDNS
Annotations:  <none>
Subsets:
  Addresses:          10.186.0.2,10.186.0.3
  NotReadyAddresses:  <none>
  Ports:
    Name     Port  Protocol
    ----     ----  --------
    dns      53    UDP
    dns-tcp  53    TCP

Events:  <none>

  

5.2.2.手動配置服務的endpoint

 如果創建pod時不包含選擇器,則k8s將不會創建endpoint資源。這樣就需要創建endpoint來指的服務的對應的endpoint列表。

service中創建endpoint資源,其中一個作用就是用於service知道包含哪些pod。

 

 

 

5.2.3.為外部服務創建別名

 除了手動配置來訪問外部服務外,還可以使用完全限定域名(FQDN)訪問外部服務。

apiVersion: v1
kind: Service
metadata:
  name: Service-yaohong
spec:
  type: ExternalName                     //代碼的type被設置成了ExternalName  
externalName: someapi.somecompany.com    // 實際服務的完全限定域名(FQDN)
port: - port: 80

  服務創建完成后,pod可以通過external-service.default.svc.cluster.local域名(甚至是external-service)連接外部服務。

5.3.將服務暴露給外部客戶端

有3種方式在外部訪問服務:

  1.將服務的類型設置成NodePort;

  2.將服務的類型設置成LoadBalance;

  3.創建一個Ingress資源。

5.3.1.使用nodeport類型的服務

NodePort 服務是引導外部流量到你的服務的最原始方式。NodePort,正如這個名字所示,在所有節點(虛擬機)上開放一個特定端口,任何發送到該端口的流量都被轉發到對應服務。

 

 

 YAML 文件類似如下:

apiVersion: v1
kind: Service
metadata:
  name: Service-yaohong
spec:
  type: NodePort  //為NodePort設置服務類型
  ports:
  - port: 80  
    targetPort: 8080
    nodeport: 30123    //通過集群節點的30123端口可以訪問服務
  selector:
    app: yh

這種方法有許多缺點:

  1.每個端口只能是一種服務

  2.端口范圍只能是 30000-32767

如果節點/VM 的 IP 地址發生變化,你需要能處理這種情況

基於以上原因,我不建議在生產環境上用這種方式暴露服務。如果你運行的服務不要求一直可用,或者對成本比較敏感,你可以使用這種方法。這樣的應用的最佳例子是 demo 應用,或者某些臨時應用。

5.3.2.通過Loadbalance將服務暴露出來

LoadBalancer 服務是暴露服務到 internet 的標准方式。在 GKE 上,這種方式會啟動一個 Network Load Balancer[2],它將給你一個單獨的 IP 地址,轉發所有流量到你的服務。

 

 

通過如下方法來定義服務使用負載均衡

apiVersion: v1
kind: Service
metadata:
  name: loadBalancer-yaohong
spec:
  type: LoadBalancer  //該服務從k8s集群的基礎架構獲取負載均衡器
  ports:
  - port: 80  
    targetPort: 8080
  selector:
    app: yh

何時使用這種方式?

如果你想要直接暴露服務,這就是默認方式。所有通往你指定的端口的流量都會被轉發到對應的服務。它沒有過濾條件,沒有路由等。這意味着你幾乎可以發送任何種類的流量到該服務,像 HTTP,TCP,UDP,Websocket,gRPC 或其它任意種類。

這個方式的最大缺點是每一個用 LoadBalancer 暴露的服務都會有它自己的 IP 地址,每個用到的 LoadBalancer 都需要付費,這將是非常昂貴的。

5.4.通過Ingress暴露服務

為什么使用Ingress,一個重要的原因是LoadBalancer服務都需要創建自己的負載均衡器,以及獨有的公有Ip地址,而Ingress只需要一個公網Ip就能為許多服務提供訪問。

 

5.4.1.創建Ingress資源

Ingress 事實上不是一種服務類型。相反,它處於多個服務的前端,扮演着“智能路由”或者集群入口的角色。

你可以用 Ingress 來做許多不同的事情,各種不同類型的 Ingress 控制器也有不同的能力。

 

 

編寫如下ingress.yml文件

kind: Ingress
metadata:
  name: ingressyaohong
spec:
  rules:
  - host: kubia.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: kubia-nodeport
          servicePort: 80

通過如下命令進行查看ingress

# kubectl create  -f ingress.yml

 

5.4.2.通過Ingress訪問服務

通過kubectl get ing命令進行查看ingress

# kubectl get ing
NAME             HOSTS               ADDRESS   PORTS   AGE
ingressyaohong   kubia.example.com             80      2m

了解Ingress的工作原理

 

 

 

何時使用這種方式?

Ingress 可能是暴露服務的最強大方式,但同時也是最復雜的。Ingress 控制器有各種類型,包括 Google Cloud Load Balancer, Nginx,Contour,Istio,等等。它還有各種插件,比如 cert-manager[5],它可以為你的服務自動提供 SSL 證書。

如果你想要使用同一個 IP 暴露多個服務,這些服務都是使用相同的七層協議(典型如 HTTP),那么Ingress 就是最有用的。如果你使用本地的 GCP 集成,你只需要為一個負載均衡器付費,且由於 Ingress是“智能”的,你還可以獲取各種開箱即用的特性(比如 SSL、認證、路由等等)。

 

5.4.3.通過相同的Ingress暴露多少服務

1.將不同的服務映射到相同的主機不同的路徑

apiVersion: v1
kind: Ingress
metadata:
  name: Ingress-yaohong
spec:
  rules:
  - host: kubia.example.com
    http:
      paths:
      - path: /yh                //對kubia.example.com/yh請求轉發至kubai服務
        backend:
          serviceName: kubia
          servicePort:80
      - path: /foo              //對kubia.example.com/foo請求轉發至bar服務
        backend:
          serviceName: bar
          servicePort:80

  

2.將不同的服務映射到不同的主機上

apiVersion: v1
kind: Ingress
metadata:
  name: Ingress-yaohong
spec:
  rules:
  - host: yh.example.com
    http:
      paths:
      - path: /                //對yh.example.com請求轉發至kubai服務
        backend:
          serviceName: kubia
          servicePort:80
  - host: bar.example.com
    http:
      paths:
      - path: /                //對bar.example.com請求轉發至bar服務
        backend:
          serviceName: bar
          servicePort:80

 

5.4.4.配置Ingress處理TLS傳輸

客戶端和控制器之間的通信是加密的,而控制器和后端pod之間的通信則不是。

apiVersion: v1
kind: Ingress
metadata:
  name: Ingress-yaohong
spec:
 tls:                     //在這個屬性中包含所有的TLS配置
 - hosts:
   - yh.example.com       //將接收來自yh.example.com的TLS連接
   serviceName: tls-secret     //從tls-secret中獲得之前創立的私鑰和證書
  rules:
  - host: yh.example.com
    http:
      paths:
      - path: /                //對yh.example.com請求轉發至kubai服務
        backend:
          serviceName: kubia
          servicePort:80

5.5.pod就緒后發出信號

5.5.1.介紹就緒探針

就緒探針有三種類型:

1.Exec探針,執行進程的地方。容器的狀態由進程的退出狀態代碼確定。

2.HTTP GET探針,向容器發送HTTP GET請求,通過響應http狀態碼判斷容器是否准備好。

3.TCP socket探針,它打開一個TCP連接到容器的指定端口,如果連接建立,則認為容器已經准備就緒。

 

啟動容器時,k8s設置了一個等待時間,等待時間后才會執行一次准備就緒檢查。之后就會周期性的進行調用探針,並根據就緒探針的結果采取行動。

如果某個pod未就緒成功,則會從該服務中刪除該pod,如果pod再次就緒成功,則從新添加pod。

 

與存活探針區別:

就緒探針如果容器未准備就緒,則不會終止或者重啟啟動。

存活探針通過殺死異常容器,並用新的正常的容器來替代他保證pod正常工作。

就緒探針只有准備好處理請求pod才會接收他的請求。

 

重要性;

確保客戶端只與正常的pod進行交互,並且永遠不會知道系統存在問題。

 

5.5.2.向pod添加就緒探針

添加的yml文件如下

apiVersion: v1
kind: deployment
...
spec:
  ...
  port:
    containers:
    - name: kubia-yh
        imgress: luksa/kubia
        readinessProbe:
          failureThreshold: 2
          httpGet:
            path: /ping
            port: 80
            scheme: HTTP
          initialDelaySeconds: 30
          periodSeconds: 5
          successThreshold: 1
          timeoutSeconds: 3

相關參數解釋如下:

  • initialDelaySeconds:容器啟動和探針啟動之間的秒數。
  • periodSeconds:檢查的頻率(以秒為單位)。默認為10秒。最小值為1。
  • timeoutSeconds:檢查超時的秒數。默認為1秒。最小值為1。
  • successThreshold:失敗后檢查成功的最小連續成功次數。默認為1.活躍度必須為1。最小值為1。
  • failureThreshold:當Pod成功啟動且檢查失敗時,Kubernetes將在放棄之前嘗試failureThreshold次。放棄生存檢查意味着重新啟動Pod。而放棄就緒檢查,Pod將被標記為未就緒。默認為3.最小值為1。

HTTP探針在httpGet上的配置項:

  • host:主機名,默認為pod的IP。
  • scheme:用於連接主機的方案(HTTP或HTTPS)。默認為HTTP。
  • path:探針的路徑。
  • httpHeaders:在HTTP請求中設置的自定義標頭。 HTTP允許重復的請求頭。
  • port:端口的名稱或編號。數字必須在1到65535的范圍內

 

模擬就緒探針

# kubectl   exec <pod_name> -- curl http://10.187.0.139:80/ping
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0

5.6.使用headless服務發現獨立的pod

5.6.1.創建headless服務

Headless Service也是一種Service,但不同的是會定義spec:clusterIP: None,也就是不需要Cluster IPService

顧名思義,Headless Service就是沒頭的Service。有什么使用場景呢?

  • 第一種:自主選擇權,有時候client想自己來決定使用哪個Real Server,可以通過查詢DNS來獲取Real Server的信息。

  • 第二種:Headless Services還有一個用處(PS:也就是我們需要的那個特性)。Headless Service的對應的每一個Endpoints,即每一個Pod,都會有對應的DNS域名;這樣Pod之間就可以互相訪問。

6.kubernetes  磁盤、PV、PVC

6.1.介紹卷

6.1.1.卷的類型

emptyDir-用於存儲臨時數據的簡單空目錄

hostPath-用於將目錄從工作節點的文件系統掛載到pod

nfs-掛載到pod中的NFS共享卷。

還有其他的如gitRepo、gcepersistenDisk

 

6.2.通過卷在容器間共享數據

6.2.1.使用emptyDir卷

卷的生命周期與pod的生命周期項關聯,所以當刪除pod時,卷的內容就會丟失。

使用empty示例代碼如下:

apiVersion: v1
kind: Pod
metadata:
  name: fortune
spec:
  containers:
  - image: luksa/fortune
    name: html-gener
    volumeMounts:
    - name: html
      mountPath: /usr/share/nginx
      readOnly: true
  - image: nginx/aplin
    name: web-service
    volumeMounts:
    - name: html
      mountPath: /usr/share
      readOnly: true
  volumes:
  - name: html                        //一個名為html的單獨emptyDir卷,掛載在上面的兩個容器中
    emptyDir: {}

  

6.3.訪問工作節點文件系統上的文件

6.3.1.hostPath卷

hostPath是持久性存儲,emptyDir卷的內容隨着pod的刪除而刪除。

使用hostPath會發現當刪除一個pod,並且下一個pod使用了指向主機上相同路徑的hostPath卷,則新pod將會發現上一個pod留下的數據,但前提是必須將其調度到與第一個pod相同的節點上。

所以當你使用hostPath時請務必考慮清楚,當重新起一個pod時候,必須要保證pod的節點與之前相同。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

 

6.4.使用持久化存儲

怎樣保證pod重新啟動后調度到任意一個節點都有相同的數據可用,這就需要做到持久化存儲。

因此必須要將數據存儲在某種類型的網絡存儲(NAS)中。

各種支持的方式不盡相同,例如 GlusterFS 需要創建 Endpoint,Ceph/NFS 之流就沒這么麻煩了。

6.4.1.使用NFS存儲

以NFS為例,yml代碼如下:

 

 

 

6.4.2.configmap和secert

secret和configmap可以理解為特殊的存儲卷,但是它們不是給Pod提供存儲功能的,而是提供了從集群外部向集群內部的應用注入配置信息的功能。ConfigMap扮演了K8S集群中配置中心的角色。ConfigMap定義了Pod的配置信息,可以以存儲卷的形式掛載至Pod中的應用程序配置文件目錄,從configmap中讀取配置信息;也可以基於環境變量的形式,從ConfigMap中獲取變量注入到Pod容器中使用。但是ConfigMap是明文保存的,如果用來保存數據庫賬號密碼這樣敏感信息,就非常不安全。一般這樣的敏感信息配置是通過secret來保存。secret的功能和ConfigMap一樣,不過secret是通過Base64的編碼機制保存配置信息。

從ConfigMap中獲取配置信息的方法有兩種:

  • 一種是利用環境變量將配置信息注入Pod容器中的方式,這種方式只在Pod創建的時候生效,這就意味着在ConfigMap中的修改配置信息后,更新的配置不能被已經創建Pod容器所應用。
  • 另一種是將ConfigMap做為存儲卷掛載至Pod容器內,這樣在修改ConfigMap配置信息后,Pod容器中的配置也會隨之更新,不過這個過程會有稍微的延遲。

ConfigMap當作存儲卷掛載至Pod中的用法:

apiVersion: v1
kind: Pod
metadata:
  name: pod-configmap-vol-2
  labels:
    name: pod-configmap-vol-2
spec:
  containers:
  - name: myapp
    image: ikubernetes/myapp:v1
    volumeMounts:
    - name: my-cm-www
      mountPath: /etc/nginx/conf.d/       # 將名為my-www的configmap掛載至Pod容器的這個目錄下。
  volumes:
  - name: my-cm-www
    configMap:               # 存儲卷類型選configMap

  secert的方法類似,只是secert對數據進行了加密

 

6.5.從底層存儲技術解耦pod

6.5.1.介紹持久卷和持久卷聲明

  當集群用戶需要在其pod中使用持久化存儲時,他們首先創建持久化聲明(PVC)清單,指定所需要的最低容量要求,和訪問模式,然后用戶將持久卷聲明清單提交給kubernetes API服務器,kubernetes將找到可以匹配的持久卷並將其綁定到持久卷聲明。

  持久卷聲明可以當做pod中的一個卷來使用,其他用戶不能使用相同的持久卷,除非先通過刪除持久卷聲明綁定來釋放。

6.5.2.創建持久卷

下面創建一個 PV mypv1,配置文件pv1.yml 如下:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: yh_pv1
spec:
  capacity:
    storage: 1Gi                //capacity 指定 PV 的容量為 1G
  accessModes:                 //accessModes 指定訪問模式為 ReadWriteOnce
    - ReadWriteOnce            
  persistentVolumeReclaimpolicy: Recycle  //persistentVolumeReclaimPolicy 指定當 PV 的回收策略為 Recycle
  storageClassName: nfs         //storageClassName 指定 PV 的 class 為 nfs。相當於為 PV 設置了一個分類,PVC 可以指定 class 申請相應 class 的 PV。
  nfs:
    path: /nfs/data             //指定 PV 在 NFS 服務器上對應的目錄
    server: 10.10.0.11

1.accessModes 指定訪問模式為 ReadWriteOnce,支持的訪問模式有:

  ReadWriteOnce – PV 能以 read-write 模式 mount 到單個節點。
  ReadOnlyMany – PV 能以 read-only 模式 mount 到多個節點。
  ReadWriteMany – PV 能以 read-write 模式 mount 到多個節點。

2.persistentVolumeReclaimPolicy 指定當 PV 的回收策略為 Recycle,支持的策略有:
  Retain – 需要管理員手工回收。
  Recycle – 清除 PV 中的數據,效果相當於執行 rm -rf /thevolume/*
  Delete – 刪除 Storage Provider 上的對應存儲資源,例如 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。

 

創建 pv

# kubectl apply -f pv1.yml 
persistentvolume/yh-pv1 created

 

查看pv:

# kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE
yh-pv1   1Gi        RWO            Recycle          Available           nfs                     17m

  

STATUS 為 Available,表示 yh-pv1就緒,可以被 PVC 申請。

6.5.3.通過持久卷聲明來獲取持久卷

 

接下來創建 PVC mypvc1,配置文件 pvc1.yml 如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: yh-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: nfs

  

PVC 就很簡單了,只需要指定 PV 的容量,訪問模式和 class。

執行命令創建 mypvc1

# kubectl apply -f pvc1.yml 
persistentvolumeclaim/yh-pvc created

查看pvc

# kubectl get pvc
NAME     STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
yh-pvc   Bound    yh-pv1   1Gi        RWO            nfs            64s

 

從 kubectl get pvc 和 kubectl get pv 的輸出可以看到 yh-pvc1 已經 Bound 到yh- pv1,申請成功。

 

# kubectl get pv
NAME     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS   REASON   AGE
yh-pv1   1Gi        RWO            Recycle          Bound    default/yh-pvc   nfs                     47m

  

6.5.4.在pod中使用持久卷聲明

上面已經創建好了pv和pvc,pod中直接使用這個pvc即可

與使用普通 Volume 的格式類似,在 volumes 中通過 persistentVolumeClaim 指定使用 mypvc1 申請的 Volume。

 通過命令創建mypod1

可見,在 Pod 中創建的文件 /mydata/hello 確實已經保存到了 NFS 服務器目錄 /nfsdata中。

如果不再需要使用 PV,可用刪除 PVC 回收 PV。

 

6.5.5.回收持久卷

當 PV 不再需要時,可通過刪除 PVC 回收。

未刪除pvc之前  pv的狀態是Bound

刪除pvc之后pv的狀態變為Available,,此時解除綁定后則可以被新的 PVC 申請。

/nfsdata文件中的文件被刪除了

 

因為 PV 的回收策略設置為 Recycle,所以數據會被清除,但這可能不是我們想要的結果。如果我們希望保留數據,可以將策略設置為 Retain

通過 kubectl apply 更新 PV:

 

回收策略已經變為 Retain,通過下面步驟驗證其效果:

 

① 重新創建 mypvc1

② 在 mypv1 中創建文件 hello

③ mypv1 狀態變為 Released

④ PV 中的數據被完整保留。

雖然 mypv1 中的數據得到了保留,但其 PV 狀態會一直處於 Released,不能被其他 PVC 申請。為了重新使用存儲資源,可以刪除並重新創建 mypv1。刪除操作只是刪除了 PV 對象,存儲空間中的數據並不會被刪除。

 

新建的 mypv1 狀態為 Available,已經可以被 PVC 申請。

PV 還支持 Delete 的回收策略,會刪除 PV 在 Storage Provider 上對應存儲空間。NFS 的 PV 不支持 Delete,支持 Delete 的 Provider 有 AWS EBS、GCE PD、Azure Disk、OpenStack Cinder Volume 等。

 

6.6.持久卷的動態配置

6.6.1.通過StorageClass資源定義可用存儲類型

前面的例子中,我們提前創建了 PV,然后通過 PVC 申請 PV 並在 Pod 中使用,這種方式叫做靜態供給(Static Provision)。

與之對應的是動態供給(Dynamical Provision),即如果沒有滿足 PVC 條件的 PV,會動態創建 PV。相比靜態供給,動態供給有明顯的優勢:不需要提前創建 PV,減少了管理員的工作量,效率高。

動態供給是通過 StorageClass 實現的,StorageClass 定義了如何創建 PV,下面是兩個例子。

StorageClass standard

StorageClass slow

這兩個 StorageClass 都會動態創建 AWS EBS,不同在於 standard 創建的是 gp2 類型的 EBS,而 slow 創建的是 io1 類型的 EBS。不同類型的 EBS 支持的參數可參考 AWS 官方文檔。

StorageClass 支持 Delete 和 Retain 兩種 reclaimPolicy,默認是 Delete

與之前一樣,PVC 在申請 PV 時,只需要指定 StorageClass 和容量以及訪問模式,比如:

 

除了 AWS EBS,Kubernetes 支持其他多種動態供給 PV 的 Provisioner,完整列表請參考 https://kubernetes.io/docs/concepts/storage/storage-classes/#provisioner

 

6.6.2.PV&&PVC在應用在mysql的持久化存儲

下面演示如何為 MySQL 數據庫提供持久化存儲,步驟為:

  1. 創建 PV 和 PVC。

  2. 部署 MySQL。

  3. 向 MySQL 添加數據。

  4. 模擬節點宕機故障,Kubernetes 將 MySQL 自動遷移到其他節點。

  5. 驗證數據一致性。

 

首先創建 PV 和 PVC,配置如下:

mysql-pv.yml

 

mysql-pvc.yml

創建 mysql-pv 和 mysql-pvc

 

接下來部署 MySQL,配置文件如下:

 

 PVC mysql-pvc Bound 的 PV mysql-pv 將被 mount 到 MySQL 的數據目錄 var/lib/mysql

MySQL 被部署到 k8s-node2,下面通過客戶端訪問 Service mysql

kubectl run -it --rm --image=mysql:5.6 --restart=Never mysql-client -- mysql -h mysql -ppassword

 

更新數據庫:

① 切換到數據庫 mysql。

② 創建數據庫表 my_id。

③ 插入一條數據。

④ 確認數據已經寫入。

 關閉 k8s-node2,模擬節點宕機故障。

 

驗證數據的一致性:

 由於node2節點已經宕機,node1節點接管了這個任務。

通過kubectl run 命令 進入node1的這個pod里,查看數據是否依舊存在

 

MySQL 服務恢復,數據也完好無損。

7.kubernetes ConfigMap和Secret:配置應用程序

7.1.配置容器化應用程序

7.2.向容器傳遞命令行參數

7.2.1.待Docker中定義命令與參數

1.了解ENTRYPOINT與CMD

  ENTRYPOINT定義容器啟動時被調用的可以執行程序

  CMD指定傳遞給ENTRYP的參數

dockerfile 內容如下

FROM daocloud.io/centos:latest

ADD aaa /usr/local/aaa

CMD ["-f","/var/log/aa.log"]
ENTRYPOINT ["tail"]

當啟動鏡像時,容器啟動時執行如下命令:tail -f /var/log/aa.log

或者在docker run <images> <arguments> 中指定,arguments會覆蓋CMD中內容

7.2.2.在kubernetes中覆蓋命令行和參數

 在k8s中定義容器時,鏡像的ENTRYPOINT和CMD都可以被覆蓋,僅需在容器定義中設置熟悉command和args的值

對應參數如下:

Docker kubernetes 描述
ENTRYPOINT command 容器中運行的可執行文件
CMD args 傳給可執行文件的參數

 

 

 

 

相關yml代碼如下:

kind: pod
spec:
  containers:
  - image: some/image
    command: ["/bin/command"]
    args: ["args1","args2","args3"]

  

7.3.為容器設置環境變量

7.3.1.在容器定義中指定環境變量

與容器的命令和參數設置相同,環境變量列表無法在pod創建后被修改。

在pod的yml文件中設置容器環境變量代碼如下:

kind: pod
spec:
  containers:
  - image: luksa/fortune:env
    env:
    - name: INTERVAL
      value: "30"
    name: value-test-yh

  

7.3.2.在環境變量值中引用其他環境變量

使用$( VAR )引用環境變量,

相關ym代碼如下:

env:
- name: FIRST_VAR
  value: "foo"
- name: SECOND_VAR
  value: "$(FIRST_VAR)bar"   //最終變量值為foobar

 

7.4.利用ConfigMap解耦配置

7.4.1.ConfigMap介紹

kubernetes允許將配置選項分離到獨立的資源對象ConfigMap中,本質上就是一個鍵/值對映射,值可以是短字面變量,也可以是完整的配置文件。

應用無須直接讀取ConfigMap,甚至根本不需要知道其是否存在。

映射的內容通過環境變量或者卷文件的形式傳遞給容器,而並非直接傳遞給容器,命令行參數的定義中也是通過$(ENV_VAR)語法變量

7.4.2.創建ConfigMap

使用kubectl creat configmap創建ConfigMap中間不用加-f。

1.使用指令創建ConfigMap

#kubectl creat configmap configmap-yaohong --from-literal=foo=bar --from-literal=sleep-interval=25

2.從文件內容創建ConfigMap條目

#kubectl create configmap my-conf-yh --from-file=config-file.conf

使用如下命令,會將文件內容存儲在自定義的條目下。與字面量使用相同

#kubectl create configmap my-conf-yh --from-file=customkey=config-file.conf 

3.從文件夾創建ConfigMap

#kubectl create configmap my-conf-yh --from-file=/path/to/dir

4.合並不同選項

#kubectl create configmap my-conf-yh 
  --from-file=/path/to/dir/
  --from-file=bar=foobar.conf
  --from-literal=some=thing

  

5.獲取ConfigMap

#kubectl -n <namespace> get configmap

  

7.4.3.給容器傳遞ConfigMap條目作為環境變量

引用環境變量中的參數值給當前變量

apiVersion: v1
kind: pod
metadata:
  name: fortune-env-from-configmap
spec:
  containers:
  - image: luksa/fortune:env
    env:
    - name: INTERVAL                  //設置環境變量
      valueFrom:
        configMapkeyRef:
          name: fortune-configmap     
          key: sleep-interval         //變量的值取自fortune-configmap的slee-interval對應的值

  

7.4.4.一次性傳遞ConfigMap的所有條目作為環境變量

apiVersion: v1
kind: pod
metadata:
  name: fortune-env-from-configmap
spec:
  containers:
  - image: luksa/fortune:env
    envFrom:
    - prefix: CONFIG_
      confgMapRef:
        name: my-confg-map    //引用my-config-map的ConfigMap並在變量前面都加上CONFIG_

  

7.4.5.使用ConfigMap卷將條目暴露為文件

apiVersion: v1
kind: pod
metadata:
  name: configmap-volume-yh
spec:
  containers:
  - image: nginx:aplin
    name: web-server
    volumeMounts:
    ...
    - name: config
defaultMode: "6600" //設置文件的權限為rw-rw mountPath: /etc/nginx/con.conf
subPath: my.conf //subPath字段可以用於掛載卷中某個獨立的文件或者文件夾,而且不覆蓋該卷下其他文件 ... volume: ... - name: config configMap: name: fortune-config //引用fortune-config configMap的卷,然后掛載在/etc/nginx/conf.d

  可以使用如下命令查看到/etc/nginx/conf.d文件下面包含fortune-config

#kubectl exec config-volume-yh -c web-server ls /etc/nginx/conf.d

   

7.5.使用Secert給容器傳遞敏感數據

7.5.1.介紹Secert

Secret結構和ConfigMap類似,均是鍵/值對的映射。

使用方法也和ConfigMap一樣,可以:

  1.將Secret條目作為環境變量傳遞給容器,

  2.將Secret條目暴露為卷中文件

 ConfigMap存儲非敏感的文本配置數據,采用Secret存儲天生敏感的數據

7.5.2.默認令牌Secret

1.查看secret

# kubectl get secrets 
NAME                  TYPE                                  DATA   AGE
default-token-x9cjb   kubernetes.io/service-account-token   3      78d

2.描述secret

# kubectl describe secrets default-token-x9cjb 
Name:         default-token-x9cjb
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: 64a41a09-98ce-11e9-9fa5-fa163e6fdb6b

Type:  kubernetes.io/service-account-token

Data
====
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5lduaW8vc2VydmljZTxCv6HdtP-ZW3ZC2IKKR5YBhaokFIl35mix79pU4Ia2pJ_fuPTBGNyrCHyNQYH4ex5DhND3_b2puQmn8RSErQ
ca.crt:     1298 bytes
namespace:  7 bytes

 

7.5.3.創建Secret

1.創建一個名為https-yh的generic secret

#kubectl create secret generic https-yh --from-file=https.key  --from-file=https.cert  --from-file=foo

 

2.創建一個secret.yaml文件,內容用base64編碼

$ echo -n 'admin' | base64
YWRtaW4=
$ echo -n '1f2d1e2e67df' | base64
MWYyZDFlMmU2N2Rm

 

yaml文件內容:

復制代碼
apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm
復制代碼

 

創建:

$ kubectl create -f ./secret.yaml
secret "mysecret" created

 

解析Secret中內容

復制代碼
$ kubectl get secret mysecret -o yaml
apiVersion: v1
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm
kind: Secret
metadata:
  creationTimestamp: 2016-01-22T18:41:56Z
  name: mysecret
  namespace: default
  resourceVersion: "164619"
  selfLink: /api/v1/namespaces/default/secrets/mysecret
  uid: cfee02d6-c137-11e5-8d73-42010af00002
type: Opaque
復制代碼

 

base64解碼:

$ echo 'MWYyZDFlMmU2N2Rm' | base64 --decode
1f2d1e2e67df

  

7.5.4.對比ConfigMap與Secret

Secret的條目內容會進行Base64格式編碼,而ConfigMap直接以純文本展示。

1.為二進制數據創建Secret

  Base64可以將二進制數據轉換為純文本,並以YAML或Json格式進行展示

  但要注意Secret的大小限制是1MB

2.stringDate字段介紹

  Secret可以通過StringDate字段設置條目的純文本

kind: Secret
apiVersion: v1
stringDate:
  foo: plain txt
date:
  https.cert: HGIOPUPSDF63456BJ3BBJL34563456BLKJBK634563456BLBKJBLKJ63456BLK3456LK
  http.key: OHOPGPIU42342345OIVBGOI3456345OVB6O3456BIPO435B6IPU345UI

  

7.5.5.在pod中使用Secret

secret可以作為數據卷掛載或者作為環境變量暴露給Pod中的容器使用,也可以被系統中的其他資源使用。比如可以用secret導入與外部系統交互需要的證書文件等。

在Pod中以文件的形式使用secret

  1. 創建一個Secret,多個Pod可以引用同一個Secret
  2. 修改Pod的定義,在spec.volumes[]加一個volume,給這個volume起個名字,spec.volumes[].secret.secretName記錄的是要引用的Secret名字
  3. 在每個需要使用Secret的容器中添加一項spec.containers[].volumeMounts[],指定spec.containers[].volumeMounts[].readOnly = truespec.containers[].volumeMounts[].mountPath要指向一個未被使用的系統路徑。
  4. 修改鏡像或者命令行使系統可以找到上一步指定的路徑。此時Secret中data字段的每一個key都是指定路徑下面的一個文件名

下面是一個Pod中引用Secret的列子:

復制代碼
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
復制代碼

 

每一個被引用的Secret都要在spec.volumes中定義

如果Pod中的多個容器都要引用這個Secret那么每一個容器定義中都要指定自己的volumeMounts,但是Pod定義中聲明一次spec.volumes就好了。

映射secret key到指定的路徑

可以控制secret key被映射到容器內的路徑,利用spec.volumes[].secret.items來修改被映射的具體路徑

復制代碼
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username
復制代碼

 

發生了什么呢?

  • username被映射到了文件/etc/foo/my-group/my-username而不是/etc/foo/username
  • password沒有變

Secret文件權限

可以指定secret文件的權限,類似linux系統文件權限,如果不指定默認權限是0644,等同於linux文件的-rw-r--r--權限

設置默認權限位

復制代碼
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      defaultMode: 256
復制代碼

 

上述文件表示將secret掛載到容器的/etc/foo路徑,每一個key衍生出的文件,權限位都將是0400

由於JSON不支持八進制數字,因此用十進制數256表示0400,如果用yaml格式的文件那么就很自然的使用八進制了

同理可以單獨指定某個key的權限

復制代碼
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mypod
    image: redis
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
  volumes:
  - name: foo
    secret:
      secretName: mysecret
      items:
      - key: username
        path: my-group/my-username
        mode: 511
復制代碼

 

從volume中讀取secret的值

值得注意的一點是,以文件的形式掛載到容器中的secret,他們的值已經是經過base64解碼的了,可以直接讀出來使用。

復制代碼
$ ls /etc/foo/
username
password
$ cat /etc/foo/username
admin
$ cat /etc/foo/password
1f2d1e2e67df
復制代碼

 

被掛載的secret內容自動更新

也就是如果修改一個Secret的內容,那么掛載了該Secret的容器中也將會取到更新后的值,但是這個時間間隔是由kubelet的同步時間決定的。最長的時間將是一個同步周期加上緩存生命周期(period+ttl)

特例:以subPath形式掛載到容器中的secret將不會自動更新

以環境變量的形式使用Secret

  1. 創建一個Secret,多個Pod可以引用同一個Secret
  2. 修改pod的定義,定義環境變量並使用env[].valueFrom.secretKeyRef指定secret和相應的key
  3. 修改鏡像或命令行,讓它們可以讀到環境變量
復制代碼
apiVersion: v1
kind: Pod
metadata:
  name: secret-env-pod
spec:
  containers:
  - name: mycontainer
    image: redis
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password
  restartPolicy: Never
復制代碼

 

容器中讀取環境變量,已經是base64解碼后的值了:

$ echo $SECRET_USERNAME
admin
$ echo $SECRET_PASSWORD
1f2d1e2e67df

 

使用imagePullSecrets

創建一個專門用來訪問鏡像倉庫的secret,當創建Pod的時候由kubelet訪問鏡像倉庫並拉取鏡像,具體描述文檔在 這里

設置自動導入的imagePullSecrets

可以手動創建一個,然后在serviceAccount中引用它。所有經過這個serviceAccount創建的Pod都會默認使用關聯的imagePullSecrets來拉取鏡像,

 

9.deployment:聲明式的升級應用

 

Deployment 的控制器,實際上控制的是 ReplicaSet 的數目,以及每個 ReplicaSet 的屬性。

而一個應用的版本,對應的正是一個 ReplicaSet;這個版本應用的 Pod 數量,則由 ReplicaSet 通過它自己的控制器(ReplicaSet Controller)來保證。

通過這樣的多個 ReplicaSet 對象,Kubernetes 項目就實現了對多個“應用版本”的描述。

                               

 

 

9.1.使用RC實現滾動升級

#kubectl rolling-update kubia-v1 kubia-v2 --image=luksa/kubia:v2

  使用kubia-v2版本應用來替換運行着kubia-v1的RC,將新的復制控制器命名為kubia-v2,並使用luksa/kubia:v2最為鏡像。

  升級完成后使kubectl describe rc kubia-v2查看升級后的狀態。
  為什么現在不再使用rolling-update?
  1.直接更新pod和RC的標簽並不是一個很的方案;
  2.kubectl只是執行升級中的客戶端,但如果執行kubectl過程中是去了網絡連接,升級將會被中斷,pod和RC將會處於一個中間的狀態,所以才有了Deployment資源的引入。
 

9.2.使用Deployment聲明式的升級應用

  Rs替代Rc來復制個管理pod。
  創建Deployment前確保刪除所有的RC和pod,但是暫時保留Service,
  kubectl delete rc --all
  創建Deployment
#kubectl create -f kubectl.depl-v1.yaml --record  //--record可以記錄歷史版本

#查看Deployment的相關信息
#kubectl get deployment
#kubectl describe deployment

#查看部署狀態:
#kubectl rollout status deployment kubia

  

9.3.觸發deployment升級

#kubectl edit deployment kubia //修改完后資源對象會被更新
#kubectl patch deployment kubia -p '{...}' //只能包含想要更新的字段
#kubectl apply -f kubia-deploy-v2.yml //如果yml中定義的資源不存在,會自動被創建
#kubectl replace -f kubia-deploy-v2.yml //如果yml中定義的資源不存在,則會報錯
  修改configmap並不會觸發升級,如果想要觸發,可以創建新的configmap並修改pod模板引用新的configmap。
 

9.4.回滾deployment

  在上述升級deployment過程中可以使用如下命令來觀察升級的過程
#kubectl rollout status deployment kubia
  如果出現報錯,如何進行停止?可以使用如下命令進行回滾到先前部署的版本
#kubectl rollout undo deployment kubia
  如何顯示deployment的歷史版本?
#kubectl rollout history deployment kubia
  如何回滾到特定的版本?
#kubectl rollout undo deployment kubia --to-revision=1

  

9.5.控制滾動升級的速率

  在deployment的滾動升級過程中,有兩個屬性決定一次替換多少個pod:maxSurge、maxUnavailable,可以通過strategy字段下的rollingUpdate的屬性來配置,
  maxSurge:決定期望的副本數,默認值為25%,如果副本數設置為4個,則在滾動升級過程中,不會運行超過5個pod。
  maxUnavaliable: 決定允許多少個pod處於不可用狀態,默認值為25%,如果副本數為4,那么只能有一個pod處於不可用狀態,
       默認情況下如果10分鍾內沒有升級完成,將被視為失敗,如果要修改這個參數可以使用kubectl describe deploy kubia 查看到一條ProgressDeadline-Exceeded的記錄,可以修改此項參數修改判斷時間。

9.6.金絲雀發布和藍綠發布

金絲雀部署:優先發布一台或少量機器升級,等驗證無誤后再更新其他機器。優點是用戶影響范圍小,不足之處是要額外控制如何做自動更新。
藍綠部署:2組機器,藍代表當前的V1版本,綠代表已經升級完成的V2版本。通過LB將流量全部導入V2完成升級部署。優點是切換快速,缺點是影響全部用戶。

10.Statefulset:部署有狀態的多副本應用

10.1.什么是Statefulset

  StatefulSet是Kubernetes提供的管理有狀態應用的負載管理控制器API。
       特點:
  1.具有固定的網絡標記(主機名)
  2.具有持久化存儲
  3.需要按順序部署和擴展
  4.需要按順序終止和刪除
  5.需要按順序滾動和更新
 
  有狀態應用:這種實例之間有不對等關系,以及實例對外部數據有依賴關系的應用,就被稱為“有狀態應用”(Stateful Application)。

10.2.statefulset的創建

  statefulset的創建順序從0到N-1,終止順序則相反,如果需要對satateful擴容,則之前的n個pod必須存在,如果要終止一個pod,則他的后續pod必須全部終止。

       創建statefulset

#kubectl create -f ss-nginx.yml

  查看statefulset

#kubectl get statefulset

  

  statefulset會使用一個完全一致的pod來替換被刪除的pod。

  statefulset擴容和縮容時,都會刪除最高索引的pod,當這個pod完全被刪除后,才回刪除擁有次高索引的pod。

10.3.statefulset中發現伙伴的節點

  通過DNS中SRV互相發現。

10.4.更新statefulset

#kuebctl edit statefulset kubia

  但修改后pod不會自動 被更新,需要手動delete pod后會重新調度更新。

10.5.statefulset如何處理節點失效

 

11.Kubernetes架構及相關服務詳解

11.1.了解架構

K8s分為兩部分:
  1.Master節點
  2.node節點
Master節點組件:
  1.etcd分布式持久化存儲
  2.api服務器
  3.scheduler
  4.controller
Node節點:
  1.kubelet
  2.kube-proxy
  3.容器運行時(docker、rkt及其它)
附加組件:
  1.Dns服務器
  2.儀表板
  3.ingress控制器
  4.Heapster控制器
  5.網絡容器接口插件

11.2.k8s組件分布式特性

  k8s系統組件之間通信只能通過API服務器通信,他們之間不會之間進行通信。

   API服務器是和etcd通信的唯一組件,其他組件不會直接和etcd進行通信。
  API服務器與其他組件之間的通信基本上是由其他組件發起的,
       
//獲取Master節點服務健康狀況
#kubectl get componentstatuses   

11.3.k8s如何使用etcd

   etcd是一個響應快,分布式,一致的key-value存儲。
  是k8s存儲集群狀態和元數據唯一的地方,具有樂觀鎖特性,
   
關於樂觀鎖並發控制
    樂觀鎖並發控制(有時候指樂觀鎖),是指一段數據包含一個版本數字,而不是鎖住該段數據並阻止讀寫操作。每當更新數據,版本數就會增加。當更新數據時,版本就會增加。當更新數據時,就會檢查版本值是否在客戶端讀取數據時間和提交時間之間被增加過。如果增加過,那么更新會被拒絕,客戶端必須重新讀取新數據,重新嘗試更新。
    兩個客戶端嘗試更新同一個數據條目,只有一個會被更新成功。

資源如何存儲在etcd中

  flannel操作etcd使用的是v2的API,而kubernetes操作etcd使用的v3的API,所以在下面我們執行etcdctl的時候需要設置ETCDCTL_API環境變量,該變量默認值為2。

  etcd是使用raft一致性算法實現的,是一款分布式的一致性KV存儲,主要用於共享配置和服務發現。
  Etcd V2和V3之間的數據結構完全不同,互不兼容。
 
確保etcd集群一致性

  一致性算法要求大部分節點參與,才能進行到下一個狀態,需要有過半的節點參與狀態的更新,所以導致etcd的節點必須為奇數個。

11.4.API服務器

  Kubernetes API服務器為API對象驗證和配置數據,這些對象包含Pod,Service,ReplicationController等等。API Server提供REST操作以及前端到集群的共享狀態,所有其它組件可以通過這些共享狀態交互。

  1.API提供RESTful API的形式,進行CRUD(增刪查改)集群狀態

  2.進行校驗

  3.處理樂觀鎖,用於處理並發問題,

  4.認證客戶端

    (1)通過認證插件認證客戶端

    (2)通過授權插件認證客戶端

    (3)通過准入插件驗證AND/OR修改資源請求

  API服務器如何通知客戶端資源變更

    API服務器不會去創建pod,同時他不會去管理服務的端點,

    它做的是,啟動控制器,以及一些其他的組件來監控一鍵部署的資源變更,是得組件可以再集群元數據變化時候執行任何需要做的任務,

11.5.了解調度器

  調度器指定pod運行在哪個集群節點上。

  調度器不會命令選中節點取運行pod,調度器做的就是通過api服務器更新pod的定義。然后api服務器再去通知kubelet該pod已經被調用。當目標節點的kubelet發現該pod被調度到本節點,就會創建並運行pod容器。

  

調度方法:

  1.通過算法過濾所有節點,找到最優節點

   2.查找可用節點

    (1)是否滿足對硬件的資源要求

    (2)節點是否資源耗盡

    (3)pod是否要求被調度到指定的節點、

    (4)是否和要求的lable一致

    (5)需求端口是否被占用

    (6)是否有符合要求的存儲卷

    (7)是否容忍節點的污染

11.6.介紹控制器管理器中運行的控制器

(1)RC控制器

  啟動RC資源的控制器叫做Replication管理器。

  RC的操作可以理解為一個無限的循環,每次循環,控制器都會查找符合其pod選擇器的pod數量,並且將該數值和期望的復制集數量做比較。

(2)RS控制器

  與RC類似

(3)DaemonSet以及job控制器

  從他們各自資源集中定義pod模板創建pod資源,

(4)Deployment控制器

  Deployment控制器負責使deployment的實際狀態與對應的Deployment API對象期望狀態同步。

  每次Deployment對象修改后,Deployment控制器會滾動升級到新的版本。通過創建ReplicaSet,然后按照Deployment中定義的策略同時伸縮新、舊RelicaSet,直到舊pod被新的替代。

(5)StatefulSet控制器

  StatefulSet控制器會初始化並管理每個pod實例的持久聲明字段。

(6)Node控制器

  Node控制器管理Node資源,描述了集群的工作節點。

(7)Service控制器

  Service控制器就是用來在loadBalancer類型服務被創建或刪除,從基礎設施服務請求,釋放負載均衡器的。

  當Endpoints監聽到API服務器中Aervice資源和pod資源發生變化時,會對應修改、創建、刪除Endpoints資源。

 (8)Endpoint資源控制器

  Service不會直接連接到pod,而是通過一個ip和端口的列表,EedPoint管理器就是監聽service和pod的變化,將ip和端口更新endpoint資源。

 

(9)Namespace控制器

  當收到刪除namespace對象的通知時,控制器通過API服務群刪除后所有屬於該命名空間的資源。

(10)PV控制器

  創建一個持久卷聲明時,就找到一個合適的持久卷進行綁定到聲明。

 

11.7.kubelet做了什么

了解kubelet的工作內容

  簡單來說,就是負責所有運行在工作節點上的全部內容。

  第一個任務,在api服務器中創建一個node資源來注冊該節點;第二任務,持續監控api服務器是否把該節點分配給pod;第三任務,啟動pod;第四任務,持續監控運行的容器,向api服務器報告他們的狀態,事件和資源消耗。

  第五任務,kubelet也是運行容器的存活探針的組件,當探針報錯時,他會重啟容器;第六任務,當pod從api服務器刪除時,kubelet終止容器,並通知服務器pod已經終止。

 

11.1.7.kube-proxy的作用

  service是一組pod的服務抽象,相當於一組pod的LB,負責將請求分發給對應的pod。service會為這個LB提供一個IP,一般稱為cluster IP。
  kube-proxy的作用主要是負責service的實現,具體來說,就是實現了內部從pod到service和外部的從node port向service的訪問。

   kube-proxy有兩種代理模式,userspace和iptables,目前都是使用iptables。

12.kubernetes API服務器的安全防護

12.1.了解認證機制

  啟動API服務器時,通過命令行選項可以開啟認證插件。

12.1.1.用戶和組

了解用戶:

  分為兩種連接到api服務器的客戶端:

  1.真實的人

  2.pod,使用一種稱為ServiceAccount的機制

了解組:

  認證插件會連同用戶名,和用戶id返回組,組可以一次性給用戶服務多個權限,不用單次賦予,

  system:unauthenticated組:用於所有認證插件都不會認證客戶端身份的請求。

  system:authenticated組:會自動分配給一個成功通過認證的用戶。

  system:serviceaccount組:包含 所有在系統中的serviceaccount。

  system:serviceaccount:<namespace>組:包含了所有在特定命名空間中的serviceAccount。

12.1.2 ServiceAccount介紹

  每個pod中都包含/var/run/secrets/kubernetes.io/serviceaccount/token文件,如下圖所示,文件內容用於對身份進行驗證,token文件持有serviceaccount的認證token。

  

  應用程序使用token去連接api服務器時,認證插件會對serviceaccount進行身份認證,並將serviceaccount的用戶名傳回到api服務器內部。

       serviceaccount的用戶名格式如下:

  system:serviceaccount:<namespace>:<service account name>

  ServiceAccount是運行在pod中的應用程序,和api服務器身份認證的一中方式。

了解ServiceAccount資源

  ServiceAcount作用在單一命名空間,為每個命名空間創建默認的ServiceAccount。

       

  多個pod可以使用相同命名空間下的同一的ServiceAccount,

 ServiceAccount如何與授權文件綁定

   在pod的manifest文件中,可以指定賬戶名稱的方式,將一個serviceAccount賦值給一個pod,如果不指定,將使用該命名空間下默認的ServiceAccount.

   可以 將不同的ServiceAccount賦值給pod,讓pod訪問不同的資源。

12.1.3創建ServiceAccount

  為了集群的安全性,可以手動創建ServiceAccount,可以限制只有允許的pod訪問對應的資源。

        創建方法如下:

$ kubectl get sa
NAME      SECRETS   AGE
default   1         21h


$ kubectl create serviceaccount yaohong
serviceaccount/yaohong created


$ kubectl get sa
NAME      SECRETS   AGE
default   1         21h
yaohong   1         3s

  使用describe來查看ServiceAccount。

$ kubectl describe sa yaohong
Name:                yaohong
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   yaohong-token-qhbxn   //如果強制使用可掛載秘鑰。那么使用這個serviceaccount的pod只能掛載這個秘鑰
Tokens:              yaohong-token-qhbxn
Events:              <none>

  查看該token,

$ kubectl describe secret yaohong-token-qhbxn
Name:         yaohong-token-qhbxn
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: yaohong
              kubernetes.io/service-account.uid: a3d0d2fe-bb43-11e9-ac1e-005056870b4d

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1342 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6Inlhb2hvbmctdG9rZW4tcWhieG4iLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoieWFvaG9uZyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6ImEzZDBkMmZlLWJiNDMtMTFlOS1hYzFlLTAwNTA1Njg3MGI0ZCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0Onlhb2hvbmcifQ.BwmbZKoM95hTr39BuZhinRT_vHF-typH4anjkL0HQxdVZEt_eie5TjUECV9UbLRRYIqYamkSxmyYapV150AQh-PvdcLYPmwKQLJDe1-7VC4mO2IuVdMCI_BnZFQBJobRK9EdPdbZ9uxc9l0RL5I5WyWoIjiwbrQvtCUEIkjT_99_NngdrIr7QD9S5SxHurgE3HQbmzC6ItU911LjmxtSvBqS5NApJoJaztDv0cHKvlT67ZZbverJaStQdxr4yiRbpSycRNArHy-UZKbNQXuzaZczSjVouo5A5hzgSHEBBJkQpQ6Tb-Ko5XGjjCgV_b9uQvhmgdPAus8GdFTTFAbCBw

  

12.1.4將ServiceAccount分配給pod

  在pod中定義的spec.serviceAccountName字段上設置,此字段必須在pod創建時設置后續不能被修改。

  自定義pod的ServiceAccount的方法如下圖

      

 

12.2通過基於角色的權限控制加強集群安全

12.2.1.介紹RBAC授權插件

  RBAC授權插件將用戶角色作為決定用戶能否執行操作的關機因素。

12.2.2介紹RBAC授權資源

  RBAC授權規則通過四種資源來進行配置的,他們可以分為兩組:

    Role和ClusterRole,他們決定資源上可執行哪些動詞。

         RoleBinding和ClusterRoleBinding,他們將上述角色綁定到特定的用戶,組或者ServiceAccounts上。

  Role和RoleBinding是namespace級別資源

  ClusterRole和ClusterRoleBinding是集群級別資源

12.2.3使用Role和RoleBinding

   Role資源定義了哪些操作可以在哪些資源上執行,

創建Role

  service-reader.yml

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: kube-system
  name: service-reader
rules:
- apiGroups: [""]
  verbs: ["get", "list"]
  resources: ["services"]

  在kube-system中創建Role

#kubectl -n kube-system create -f service-reader.yml

  查看該namespace下的role

$ kubectl -n kube-system get role
NAME                                             AGE
extension-apiserver-authentication-reader        41h
kube-state-metrics-resizer                       41h
service-reader                                   2m17s
system::leader-locking-kube-controller-manager   41h
system::leader-locking-kube-scheduler            41h
system:controller:bootstrap-signer               41h
system:controller:cloud-provider                 41h
system:controller:token-cleaner                  41h

綁定角色到ServiceAccount

   將service-reader角色綁定到default ServiceAccount

$ kubectl  create rolebinding test --role=service-reader
rolebinding.rbac.authorization.k8s.io/test created

  

$ kubectl  get rolebinding test -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  creationTimestamp: 2019-08-11T03:40:51Z
  name: test
  namespace: default
  resourceVersion: "239323"
  selfLink: /apis/rbac.authorization.k8s.io/v1/namespaces/default/rolebindings/test
  uid: d0aff243-bbe9-11e9-ac1e-005056870b4d
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: service-reader

  

12.2.4使用ClusterRole和ClusterRoleBinding

 查看集群ClusterRole

# kubectl get clusterrole 
NAME                                                                   AGE
admin                                                                  42h
cluster-admin                                                          42h
edit                                                                   42h
flannel                                                                42h
kube-state-metrics                                                     42h
system:aggregate-to-admin                                              42h
...

創建ClusterRole

kubectl create clusterrole flannel --verb=get,list -n kube-system 

查看yaml文件

# kubectl get  clusterrole flannel -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{},"name":"flannel"},"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["get"]},{"apiGroups":[""],"resources":["nodes"],"verbs":["list","watch"]},{"apiGroups":[""],"resources":["nodes/status"],"verbs":["patch"]}]}
  creationTimestamp: 2019-08-09T09:58:42Z
  name: flannel
  resourceVersion: "360"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/flannel
  uid: 45100f6f-ba8c-11e9-8f57-005056870608
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - nodes/status
  verbs:
  - patch

 

創建clusterRoleBinding

$ kubectl  create clusterrolebinding  cluster-tetst  --clusterrole=pv-reader  --serviceaccount=kuebsystem:yaohong 
clusterrolebinding.rbac.authorization.k8s.io/cluster-tetst created

  

 

12.2.5了解默認的ClusterRole和ClusterRoleBinding

如下所示使用kubectl get clusterroles和kubectl get clusterrolesbinding可以獲取k8s默認資源。

用edit ClusterRole允許對資源進行修改

用admin ClusterRole賦予一個命名空間全部的權限

$ kubectl get clusterroles
NAME                                                                   AGE
admin                                                                  44h
cluster-admin                                                          44h
edit                                                                   44h
flannel                                                                44h
kube-state-metrics                                                     44h
system:aggregate-to-admin                                              44h
system:aggregate-to-edit                                               44h
system:aggregate-to-view                                               44h
system:auth-delegator                                                  44h
system:aws-cloud-provider                                              44h
system:basic-user                                                      44h
system:certificates.k8s.io:certificatesigningrequests:nodeclient       44h
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient   44h
system:controller:attachdetach-controller                              44h
system:controller:certificate-controller                               44h
system:controller:clusterrole-aggregation-controller                   44h
。。。

  

$ kubectl get clusterrolebindings
NAME                                                   AGE
clust-tetst                                            17m
cluster-admin                                          44h
cluster-tetst                                          13m
flannel                                                44h
kube-state-metrics                                     44h
kubelet-bootstrap                                      44h
system:aws-cloud-provider                              44h
system:basic-user                                      44h
system:controller:attachdetach-controller              44h
system:controller:certificate-controller               44h
。。。

 

13.Kubernetes-保障集群內節點和網絡安全

13.1.在pod中使用宿主節點的Linux命名空間

13.1.1.在pod中使用宿主節點的網絡命名空間

   在pod的yaml文件中就設置spec.hostNetwork: true  

   這個時候pod使用宿主機的網絡,如果設置了端口,則使用宿主機的端口。

apiVersion: v1
kind: pod
metadata:
    name: pod-host-yaohong
spec:
    hostNetwork: true  //使用宿主節點的網絡命名空間
    containers: 
    - image: luksa/kubia
      command: ["/bin/sleep", "9999"]

13.1.2.綁定宿主節點上的端口而不使用宿主節點的網絡命名空間

   在pod的yaml文件中就設置spec.containers.ports字段來設置

   在ports字段中可以使用

  containerPorts設置通過pod 的ip訪問的端口

  container.hostPort設置通過所在節點的端口訪問

apiVersion: v1
kind: pod
metadata:
    name: kubia-hostport-yaohong
spec:
    containers: 
    - image: luksa/kubia
    - name: kubia
      ports:
      - containerport: 8080 //該容器通過pod IP訪問該端口
        hostport: 9000  //該容器可以通過它所在節點9000端口訪問
        protocol: Tcp

13.1.3.使用宿主節點的PID與IPC

   PID是進程ID,PPID是父進程ID

   在linux下的多個進程間的通信機制叫做IPC(Inter-Process Communication),它是多個進程之間相互溝通的一種方法。

apiVersion: v1
kind: pod
metadata:
    name: pod-with-host-pid-and-ipc-yaohong
spec:
    hostPID: true //你希望這個pod使用宿主節點的PID命名空間
    hostIPC: true //你希望pod使用宿主節點的IPC命名空間
    containers:
    - name: main
      image: alpine
      command: ["/bin/sleep", "99999"]

 

13.2.配置節點的安全上下文

13.2.1.使用指定用戶運行容器

  查看某個pod運行的用戶

$ kubectl -n kube-system exec coredns-7b8dbb87dd-6ll7z id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)

  容器的運行用戶再DockerFile中指定,如果沒有指定則為root

  指定pod的運行的用戶方法如下

apiVersion: v1
kind: pod
metadata:
    name: pod-as-user
spec:
    containers:
    - name: main
      image: alpine
      command: ["/bin/sleep", "99999"]
      securityContext:
        runAsUser: 405   //你需要指定的用戶ID,而不是用戶名

13.2.2.阻止容器以root用戶運行

  runAsNonRoot來設置
apiVersion: v1
kind: pod
metadata:
    name: pod-as-user
spec:
    containers:
    - name: main
      image: alpine
      command: ["/bin/sleep", "99999"]
      securityContext:
        runAsNonRoot: true   //這個容器只允許以非root用戶運行

13.2.3.使用特權模式運行pod

  為了獲得宿主機內核完整的權限,該pod需要在特權模式下運行。需要添加privileged參數為true。

apiVersion: v1
kind: pod
metadata:
    name: pod-as-privileged
spec:
    containers:
    - name: main
      image: alpine
      command: ["/bin/sleep", "99999"]
      securityContext:
        privileged: true   //這個容器將在特權模式下運行

13.2.4.為容器單獨添加內核功能

apiVersion: v1
kind: pod
metadata:
    name: pod-as-capability
spec:
    containers:
    - name: main
      image: alpine
      command: ["/bin/sleep", "99999"]
      securityContext:
        capabilities:    //該參數用於pod添加或者禁用某項內核功能
          add:
          - SYS_TIME      //添加修改系統時間參數

13.2.5.在容器中禁止使用內核功能

apiVersion: v1
kind: pod
metadata:
    name: pod-as-capability
spec:
    containers:
    - name: main
      image: alpine
      command: ["/bin/sleep", "99999"]
      securityContext:
        capabilities:    //該參數用於pod添加或者禁用某項內核功能
          drop:
          - CHOWN      //禁用容器修改文件的所有者

13.2.6.阻止對容器根文件系統的寫入

  securityContext.readyOnlyFilesystem設置為true來實現阻止對容器根文件系統的寫入。
apiVersion: v1
kind: pod
metadata:
    name: pod-with-readonly-filesystem
spec:
    containers:
    - name: main
      image: alpine
      command: ["/bin/sleep", "99999"]
      securityContext:
         readyOnlyFilesystem: true  //這個容器的根文件系統不允許寫入
      volumeMounts:
      - name: my-volume
        mountPath: /volume    //volume寫入是允許的,因為這個目錄掛載一個存儲卷
        readOnly: false 

13.3.限制pod使用安全相關的特性

13.3.1.PodSecurityPolicy資源介紹

   PodSecurityPolicy是一種集群級別(無命名空間)的資源,它定義了用戶能否在pod中使用各種安全相關的特性。

13.3.2.了解runAsUser、fsGroups和supplementalGroup策略

 

runAsUser:
  runle: MustRunAs
  ranges:
  - min: 2             //添加一個max=min的range,來指定一個ID為2的user
    max: 2
  fsGroup:
    rule: MustRunAs
    ranges:
    - min: 2
      max: 10         //添加多個區間id的限制,為2-10 或者20-30
    - min: 20
      max: 30 
  supplementalGroups:
    rule: MustRunAs
    ranges:
    - min: 2
      max: 10
    - min: 20
      max: 30

13.3.3.配置允許、默認添加、禁止使用的內核功能

   三個字段會影響容器的使用

  allowedCapabilities:指定容器可以添加的內核功能
  defaultAddCapabilities:為所有容器添加的內核功能
  requiredDropCapabilities:禁止容器中的內核功能
apiVersion: v1
kind: PodSecurityPolicy
spec:
  allowedCapabilities:
  - SYS_TIME                 //允許容器添加SYS_time功能
  defaultAddCapabilities:
  - CHOWN                    //為每個容器自動添加CHOWN功能
  requiredDropCapabilities:
  - SYS_ADMIN                //要求容器禁用SYS_ADMIN和SYS_MODULE功能

13.4.隔離pod網絡

13.4.1.在一個命名空間中使用網絡隔離

  podSelector進行對一個命名空間下的pod進行隔離

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: postgres-netpolicy
spec:
  podSelector:         //這個策略確保了對具有app=databases標簽的pod的訪問安全性
    matchLabels:
      app: database
  ingress:
  - from:
    - podSelector:    //它只允許來自具有app=webserver標簽的pod的訪問
      matchLabels:
        app: webserver
    ports:
    - port: 5432      //允許對這個端口的訪問

13.4.2.在 不同的kubernetes命名空間之間進行網絡隔離

   namespaceSelector進行對不同命名空間間進行網絡隔離

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: postgres-netpolicy
spec:
  podSelector:         //這個策略確保了對具有app=databases標簽的pod的訪問安全性
    matchLabels:
      app: database
  ingress:
  - from:
    - namespaceSelector:    //只允許tenant: manning標簽的命名空間中運行的pod進行互相訪問
      matchLabels:
        tenant: manning   
    ports:
    - port: 5432      //允許對這個端口的訪問

13.4.3.使用CIDR網絡隔離

  ingress:
  - from:
    - ipBlock:
        cidr: 192.168.1.0/24    //指明允許訪問的ip段

13.4.4.限制pod對外訪問流量

   使用egress進行限制

spec:
  podSelector:         //這個策略確保了對具有app=databases標簽的pod的訪問安全性
    matchLabels:
      app: database
  egress:              //限制pod的出網流量
  - to:
    - podSelector:
        matchLables:   //database的pod只能與有app: webserver的pod進行通信
          app: webserver

  

 

 

 

 

 

 

 

   

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM