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 IP
的Service
。
顧名思義,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 數據庫提供持久化存儲,步驟為:
-
創建 PV 和 PVC。
-
部署 MySQL。
-
向 MySQL 添加數據。
-
模擬節點宕機故障,Kubernetes 將 MySQL 自動遷移到其他節點。
-
驗證數據一致性。
首先創建 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
- 創建一個Secret,多個Pod可以引用同一個Secret
- 修改Pod的定義,在
spec.volumes[]
加一個volume,給這個volume起個名字,spec.volumes[].secret.secretName
記錄的是要引用的Secret名字 - 在每個需要使用Secret的容器中添加一項
spec.containers[].volumeMounts[]
,指定spec.containers[].volumeMounts[].readOnly = true
,spec.containers[].volumeMounts[].mountPath
要指向一個未被使用的系統路徑。 - 修改鏡像或者命令行使系統可以找到上一步指定的路徑。此時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
- 創建一個Secret,多個Pod可以引用同一個Secret
- 修改pod的定義,定義環境變量並使用
env[].valueFrom.secretKeyRef
指定secret和相應的key - 修改鏡像或命令行,讓它們可以讀到環境變量
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最為鏡像。
9.2.使用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中定義的資源不存在,則會報錯
9.4.回滾deployment
#kubectl rollout status deployment kubia
#kubectl rollout undo deployment kubia
#kubectl rollout history deployment kubia
#kubectl rollout undo deployment kubia --to-revision=1
9.5.控制滾動升級的速率
9.6.金絲雀發布和藍綠發布
藍綠部署:2組機器,藍代表當前的V1版本,綠代表已經升級完成的V2版本。通過LB將流量全部導入V2完成升級部署。優點是切換快速,缺點是影響全部用戶。
10.Statefulset:部署有狀態的多副本應用
10.1.什么是Statefulset
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.了解架構
11.2.k8s組件分布式特性

k8s系統組件之間通信只能通過API服務器通信,他們之間不會之間進行通信。
//獲取Master節點服務健康狀況 #kubectl get componentstatuses
11.3.k8s如何使用etcd
關於樂觀鎖並發控制 樂觀鎖並發控制(有時候指樂觀鎖),是指一段數據包含一個版本數字,而不是鎖住該段數據並阻止讀寫操作。每當更新數據,版本數就會增加。當更新數據時,版本就會增加。當更新數據時,就會檢查版本值是否在客戶端讀取數據時間和提交時間之間被增加過。如果增加過,那么更新會被拒絕,客戶端必須重新讀取新數據,重新嘗試更新。 兩個客戶端嘗試更新同一個數據條目,只有一個會被更新成功。
資源如何存儲在etcd中
flannel操作etcd使用的是v2的API,而kubernetes操作etcd使用的v3的API,所以在下面我們執行etcdctl
的時候需要設置ETCDCTL_API
環境變量,該變量默認值為2。
一致性算法要求大部分節點參與,才能進行到下一個狀態,需要有過半的節點參與狀態的更新,所以導致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
在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用戶運行
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.阻止對容器根文件系統的寫入
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.配置允許、默認添加、禁止使用的內核功能
三個字段會影響容器的使用
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