k8s架構及服務詳解

1.容器及其三要素

1.1.容器是什麼

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

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

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

Cgroups     作限制,讓這個「世界」圍着一個看不見的牆。mysql

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

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

1.2.Namespace

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

讓你看到的是相似於一個新的虛擬機新環境,實際上是不同的,容器就是一個運行的進程,而容器中的其餘進程則是pid爲1的子進程。web

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

 容器是怎麼新建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
相關文章
相關標籤/搜索