文章目錄
- 一、Service詳解
- Service介紹
- Service類型
- Service使用
- 實驗環境準備
- ClusterIP類型的Service
- HeadLiness類型的Service
- NodePort類型的Service
- LoadBalancer類型的Service
- ExternalName類型的Service
- Ingress介紹
- Ingress使用
- 環境準備
- Http代理
- Https代理
- 二、資料存儲
- 基本存儲
- EmptyDir
- HostPath
- NFS
- 進階存儲
- PV和PVC
- PV
- PVC
- 生命周期
- 配置存儲
- ConfigMap
- Secret
- 三、安全認證
- 通路控制概述
- 認證管理
- 授權管理
- 準入控制
- 四、DashBoard
- 部署Dashboard
- 使用DashBoard
一、Service詳解
kubernetes的流量負載元件:Service和Ingress
Service介紹
在kubernetes中,pod是應用程式的載體,我們可以通過pod的ip來通路應用程式,但是pod的ip位址不是固定的,這也就意味着不友善直接采用pod的ip對服務進行通路。
為了解決這個問題,kubernetes提供了Service資源,Service會對提供同一個服務的多個pod進行聚合,并且提供一個統一的入口位址。通過通路Service的入口位址就能通路到後面的pod服務。
Service在很多情況下隻是一個概念,真正起作用的其實是kube-proxy服務程序,每個Node節點上都運作着一個kube-proxy服務程序。
當建立Service的時候會通過api-server向etcd寫入建立的service的資訊,而kube-proxy會基于監聽的機制發現這種Service的變動,然後**它會将最新的Service資訊轉換成對應的通路規則**。
# 10.97.97.97:80 是service提供的通路入口
# 當通路這個入口的時候,可以發現後面有三個pod的服務在等待調用,
# kube-proxy會基于rr(輪詢)的政策,将請求分發到其中一個pod上去
# 這個規則會同時在叢集内的所有節點上都生成,是以在任何一個節點上通路都可以。
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
kube-proxy目前支援三種工作模式:
userspace 模式
userspace模式下,kube-proxy會為每一個Service建立一個監聽端口,發向Cluster IP的請求被Iptables規則重定向到kube-proxy監聽的端口上,kube-proxy根據LB算法選擇一個提供服務的Pod并和其建立連結,以将請求轉發到Pod上
該模式下,kube-proxy充當了一個四層負責均衡器的角色。由于kube-proxy運作在userspace中,在進行轉發處理時會增加核心和使用者空間之間的資料拷貝,雖然比較穩定,但是效率比較低。
iptables 模式
iptables模式下,kube-proxy為service後端的每個Pod建立對應的iptables規則,直接将發向Cluster IP的請求重定向到一個Pod IP
該模式下kube-proxy不承擔四層負責均衡器的角色,隻負責建立iptables規則。該模式的優點是較userspace模式效率更高,但不能提供靈活的LB政策,當後端Pod不可用時也無法進行重試
ipvs 模式
ipvs模式和iptables類似,kube-proxy監控Pod的變化并建立相應的ipvs規則。ipvs相對iptables轉發效率更高。除此以外,ipvs支援更多的LB算法。
# 此模式必須安裝ipvs核心子產品,否則會降級為iptables
# 開啟ipvs
[root@master ~]# kubectl edit cm kube-proxy -n kube-system
[root@master ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system
[root@node1 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
Service類型
Service的資源清單檔案:
kind: Service # 資源類型
apiVersion: v1 # 資源版本
metadata: # 中繼資料
name: service # 資源名稱
namespace: dev # 命名空間
spec: # 描述
selector: # 标簽選擇器,用于确定目前service代理哪些pod
app: nginx
type: # Service類型,指定service的通路方式
clusterIP: # 虛拟服務的ip位址
sessionAffinity: # session親和性,支援ClientIP、None兩個選項
ports: # 端口資訊
- protocol: TCP
port: 3017 # service端口
targetPort: 5003 # pod端口
nodePort: 31122 # 主機端口
- ClusterIP:預設值,它是Kubernetes系統自動配置設定的虛拟IP,隻能在叢集内部通路
- NodePort:将Service通過指定的Node上的端口暴露給外部,通過此方法,就可以在叢集外部通路服務
- LoadBalancer:使用外接負載均衡器完成到服務的負載分發,注意此模式需要外部雲環境支援
- ExternalName: 把叢集外部的服務引入叢集内部,直接使用
Service使用
實驗環境準備
在使用service之前,首先利用Deployment建立出3個pod,注意要為pod設定
app=nginx-pod
的标簽
建立deployment.yaml,内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
name: pc-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
[root@master ~]# kubectl create -f deployment.yaml
deployment.apps/pc-deployment created
# 檢視pod詳情
[root@master ~]# kubectl get pods -n dev -o wide --show-labels
NAME READY STATUS IP NODE LABELS
pc-deployment-66cb59b984-8p84h 1/1 Running 10.244.1.40 node1 app=nginx-pod
pc-deployment-66cb59b984-vx8vx 1/1 Running 10.244.2.33 node2 app=nginx-pod
pc-deployment-66cb59b984-wnncx 1/1 Running 10.244.1.39 node1 app=nginx-pod
# 為了友善後面的測試,修改下三台nginx的index.html頁面(三台修改的IP位址不一緻)
# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
# echo "10.244.1.40" > /usr/share/nginx/html/index.html
#修改完畢之後,通路測試
[root@master ~]# curl 10.244.1.40
10.244.1.40
[root@master ~]# curl 10.244.2.33
10.244.2.33
[root@master ~]# curl 10.244.1.39
10.244.1.39
ClusterIP類型的Service
建立service-clusterip.yaml檔案
apiVersion: v1
kind: Service
metadata:
name: service-clusterip
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: 10.97.97.97 # service的ip位址,如果不寫,預設會生成一個
type: ClusterIP
ports:
- port: 80 # Service端口
targetPort: 80 # pod端口
# 建立service
[root@master ~]# kubectl create -f service-clusterip.yaml
service/service-clusterip created
# 檢視service
[root@master ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-clusterip ClusterIP 10.97.97.97 <none> 80/TCP 13s app=nginx-pod
# 檢視service的詳細資訊
# 在這裡有一個Endpoints清單,裡面就是目前service可以負載到的服務入口
[root@master ~]# kubectl describe svc service-clusterip -n dev
Name: service-clusterip
Namespace: dev
Labels: <none>
Annotations: <none>
Selector: app=nginx-pod
Type: ClusterIP
IP: 10.97.97.97
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity: None
Events: <none>
# 檢視ipvs的映射規則
[root@master ~]# ipvsadm -Ln
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
# 通路10.97.97.97:80觀察效果
[root@master ~]# curl 10.97.97.97:80
10.244.2.33
Endpoint
Endpoint是kubernetes中的一個資源對象,存儲在etcd中,用來記錄一個service對應的所有pod的通路位址,它是根據service配置檔案中selector描述産生的。
一個Service由一組Pod組成,這些Pod通過Endpoints暴露出來,**Endpoints是實作實際服務的端點集合**。換句話說,service和pod之間的聯系是通過endpoints實作的。
負載分發政策
對Service的通路被分發到了後端的Pod上去,目前kubernetes提供了兩種負載分發政策:
- 如果不定義,預設使用kube-proxy的政策,比如随機、輪詢
- 基于用戶端位址的會話保持模式,即來自同一個用戶端發起的所有請求都會轉發到固定的一個Pod上
此模式可以使在spec中添加`sessionAffinity:ClientIP`選項
# 檢視ipvs的映射規則【rr 輪詢】
[root@master ~]# ipvsadm -Ln
TCP 10.97.97.97:80 rr
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
# 循環通路測試
[root@master ~]# while true;do curl 10.97.97.97:80; sleep 5; done;
10.244.1.40
10.244.1.39
10.244.2.33
10.244.1.40
10.244.1.39
10.244.2.33
# 修改分發政策----sessionAffinity:ClientIP
# 檢視ipvs規則【persistent 代表持久】
[root@master ~]# ipvsadm -Ln
TCP 10.97.97.97:80 rr persistent 10800
-> 10.244.1.39:80 Masq 1 0 0
-> 10.244.1.40:80 Masq 1 0 0
-> 10.244.2.33:80 Masq 1 0 0
# 循環通路測試
[root@master ~]# while true;do curl 10.97.97.97; sleep 5; done;
10.244.2.33
10.244.2.33
10.244.2.33
# 删除service
[root@master ~]# kubectl delete -f service-clusterip.yaml
service "service-clusterip"
HeadLiness類型的Service
在某些場景中,開發人員可能不想使用Service提供的負載均衡功能,而希望自己來控制負載均衡政策,針對這種情況,kubernetes提供了HeadLiness Service,這類Service不會配置設定Cluster IP,如果想要通路service,隻能通過service的域名進行查詢。
建立service-headliness.yaml
apiVersion: v1
kind: Service
metadata:
name: service-headliness
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None # 将clusterIP設定為None,即可建立headliness Service
type: ClusterIP
ports:
- port: 80
targetPort: 80
# 建立service
[root@master ~]# kubectl create -f service-headliness.yaml
service/service-headliness created
# 擷取service, 發現CLUSTER-IP未配置設定
[root@master ~]# kubectl get svc service-headliness -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service-headliness ClusterIP None <none> 80/TCP 11s app=nginx-pod
# 檢視service詳情
[root@master ~]# kubectl describe svc service-headliness -n dev
Name: service-headliness
Namespace: dev
Labels: <none>
Annotations: <none>
Selector: app=nginx-pod
Type: ClusterIP
IP: None
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.39:80,10.244.1.40:80,10.244.2.33:80
Session Affinity: None
Events: <none>
# 檢視域名的解析情況
[root@master ~]# kubectl exec -it pc-deployment-66cb59b984-8p84h -n dev /bin/sh
/ # cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local
[root@master ~]# dig @10.96.0.10 service-headliness.dev.svc.cluster.local
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.40
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.1.39
service-headliness.dev.svc.cluster.local. 30 IN A 10.244.2.33
NodePort類型的Service
在之前的樣例中,建立的Service的ip位址隻有叢集内部才可以通路,如果希望将Service暴露給叢集外部使用,那麼就要使用到另外一種類型的Service,稱為NodePort類型。NodePort的工作原理其實就是将service的端口映射到Node的一個端口上,然後就可以通過 NodeIp:NodePort
來通路service了
建立service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: service-nodeport
namespace: dev
spec:
selector:
app: nginx-pod
type: NodePort # service類型
ports:
- port: 80
nodePort: 30002 # 指定綁定的node的端口(預設的取值範圍是:30000-32767), 如果不指定,會預設配置設定
targetPort: 80
# 建立service
[root@master ~]# kubectl create -f service-nodeport.yaml
service/service-nodeport created
# 檢視service
[root@master ~]# kubectl get svc -n dev -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) SELECTOR
service-nodeport NodePort 10.105.64.191 <none> 80:30002/TCP app=nginx-pod
# 接下來可以通過電腦主機的浏覽器去通路叢集中任意一個nodeip的30002端口,即可通路到pod
LoadBalancer類型的Service
LoadBalancer和NodePort很相似,目的都是向外部暴露一個端口,差別在于LoadBalancer會在叢集的外部再來做一個負載均衡裝置,而這個裝置需要外部環境支援的,外部服務發送到這個裝置上的請求,會被裝置負載之後轉發到叢集中。
ExternalName類型的Service
ExternalName類型的Service用于引入叢集外部的服務,它通過 externalName
屬性指定外部一個服務的位址,然後在叢集内部通路此service就可以通路到外部的服務了。
apiVersion: v1
kind: Service
metadata:
name: service-externalname
namespace: dev
spec:
type: ExternalName # service類型
externalName: www.baidu.com #改成ip位址也可以
# 建立service
[root@master ~]# kubectl create -f service-externalname.yaml
service/service-externalname created
# 域名解析
[root@master ~]# dig @10.96.0.10 service-externalname.dev.svc.cluster.local
service-externalname.dev.svc.cluster.local. 30 IN CNAME www.baidu.com.
www.baidu.com. 30 IN CNAME www.a.shifen.com.
www.a.shifen.com. 30 IN A 39.156.66.18
www.a.shifen.com. 30 IN A 39.156.66.14
Ingress介紹
在前面課程中已經提到,Service對叢集之外暴露服務的主要方式有兩種:NotePort和LoadBalancer,但是這兩種方式,都有一定的缺點:
- NodePort方式的缺點是會占用很多叢集機器的端口,那麼當叢集服務變多的時候,這個缺點就愈發明顯
- LB方式的缺點是每個service需要一個LB,浪費、麻煩,并且需要kubernetes之外裝置的支援
基于這種現狀,kubernetes提供了Ingress資源對象,Ingress隻需要一個NodePort或者一個LB就可以滿足暴露多個Service的需求。工作機制大緻如下圖表示:
實際上,Ingress相當于一個7層的負載均衡器,是kubernetes對反向代理的一個抽象,它的工作原理類似于Nginx,可以了解成在Ingress裡建立諸多映射規則,Ingress Controller通過監聽這些配置規則并轉化成Nginx的反向代理配置 , 然後對外部提供服務。在這裡有兩個核心概念:
- ingress:kubernetes中的一個對象,作用是定義請求如何轉發到service的規則
- ingress controller:具體實作反向代理及負載均衡的程式,對ingress定義的規則進行解析,根據配置的規則來實作請求轉發,實作方式有很多,比如Nginx, Contour, Haproxy等等
Ingress(以Nginx為例)的工作原理如下:
- 使用者編寫Ingress規則,說明哪個域名對應kubernetes叢集中的哪個Service
- Ingress控制器動态感覺Ingress服務規則的變化,然後生成一段對應的Nginx反向代理配置
- Ingress控制器會将生成的Nginx配置寫入到一個運作着的Nginx服務中,并動态更新
- 到此為止,其實真正在工作的就是一個Nginx了,内部配置了使用者定義的請求轉發規則
Ingress使用
環境準備
搭建ingress環境
# 建立檔案夾
[root@master ~]# mkdir ingress-controller
[root@master ~]# cd ingress-controller/
# 擷取ingress-nginx,本次案例使用的是0.30版本
[root@master ingress-controller]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
[root@master ingress-controller]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
# 修改mandatory.yaml檔案中的倉庫
# 修改quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 為quay-mirror.qiniu.com/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
# 建立ingress-nginx
[root@master ingress-controller]# kubectl apply -f ./
# 檢視ingress-nginx
[root@master ingress-controller]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
pod/nginx-ingress-controller-fbf967dd5-4qpbp 1/1 Running 0 12h
# 檢視service
[root@master ingress-controller]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.98.75.163 <none> 80:32240/TCP,443:31335/TCP 11h
準備service和pod
為了後面的實驗比較友善,建立如下圖所示的模型
建立tomcat-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: tomcat-pod
template:
metadata:
labels:
app: tomcat-pod
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
namespace: dev
spec:
selector:
app: tomcat-pod
clusterIP: None
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
# 建立
[root@master ~]# kubectl create -f tomcat-nginx.yaml
# 檢視
[root@master ~]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP None <none> 80/TCP 48s
tomcat-service ClusterIP None <none> 8080/TCP 48s
Http代理
建立ingress-http.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-http
namespace: dev
spec:
rules:
- host: nginx.itheima.com
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.itheima.com
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080
# 建立
[root@master ~]# kubectl create -f ingress-http.yaml
ingress.extensions/ingress-http created
# 檢視
[root@master ~]# kubectl get ing ingress-http -n dev
NAME HOSTS ADDRESS PORTS AGE
ingress-http nginx.itheima.com,tomcat.itheima.com 80 22s
# 檢視詳情
[root@master ~]# kubectl describe ing ingress-http -n dev
...
Rules:
Host Path Backends
---- ---- --------
nginx.itheima.com / nginx-service:80 (10.244.1.96:80,10.244.1.97:80,10.244.2.112:80)
tomcat.itheima.com / tomcat-service:8080(10.244.1.94:8080,10.244.1.95:8080,10.244.2.111:8080)
...
# 接下來,在本地電腦上配置host檔案,解析上面的兩個域名到192.168.109.100(master)上
# 然後,就可以分别通路tomcat.itheima.com:32240 和 nginx.itheima.com:32240 檢視效果了
Https代理
建立證書
# 生成證書
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=itheima.com"
# 建立密鑰
kubectl create secret tls tls-secret --key tls.key --cert tls.crt
建立ingress-https.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-https
namespace: dev
spec:
tls:
- hosts:
- nginx.itheima.com
- tomcat.itheima.com
secretName: tls-secret # 指定秘鑰
rules:
- host: nginx.itheima.com
http:
paths:
- path: /
backend:
serviceName: nginx-service
servicePort: 80
- host: tomcat.itheima.com
http:
paths:
- path: /
backend:
serviceName: tomcat-service
servicePort: 8080
# 建立
[root@master ~]# kubectl create -f ingress-https.yaml
ingress.extensions/ingress-https created
# 檢視
[root@master ~]# kubectl get ing ingress-https -n dev
NAME HOSTS ADDRESS PORTS AGE
ingress-https nginx.itheima.com,tomcat.itheima.com 10.104.184.38 80, 443 2m42s
# 檢視詳情
[root@master ~]# kubectl describe ing ingress-https -n dev
...
TLS:
tls-secret terminates nginx.itheima.com,tomcat.itheima.com
Rules:
Host Path Backends
---- ---- --------
nginx.itheima.com / nginx-service:80 (10.244.1.97:80,10.244.1.98:80,10.244.2.119:80)
tomcat.itheima.com / tomcat-service:8080(10.244.1.99:8080,10.244.2.117:8080,10.244.2.120:8080)
...
# 下面可以通過浏覽器通路https://nginx.itheima.com:31335 和 https://tomcat.itheima.com:31335來檢視了
二、資料存儲
容器的生命周期可能很短,會被頻繁地建立和銷毀。那麼容器在銷毀時,儲存在容器中的資料也會被清除。這種結果對使用者來說,在某些情況下是不樂意看到的。為了持久化儲存容器的資料,kubernetes引入了Volume的概念。
Volume是Pod中能夠被多個容器通路的共享目錄,它被定義在Pod上,然後被一個Pod裡的多個容器挂載到具體的檔案目錄下,kubernetes通過Volume實作同一個Pod中不同容器之間的資料共享以及資料的持久化存儲。Volume的生命容器不與Pod中單個容器的生命周期相關,當容器終止或者重新開機時,Volume中的資料也不會丢失。
kubernetes的Volume支援多種類型,比較常見的有下面幾個:
- 簡單存儲:EmptyDir、HostPath、NFS
- 進階存儲:PV、PVC
- 配置存儲:ConfigMap、Secret
基本存儲
EmptyDir
EmptyDir是最基礎的Volume類型,一個EmptyDir就是Host上的一個空目錄。
EmptyDir是在Pod被配置設定到Node時建立的,它的初始内容為空,并且無須指定主控端上對應的目錄檔案,因為kubernetes會自動配置設定一個目錄,當Pod銷毀時, EmptyDir中的資料也會被永久删除。 EmptyDir用途如下:
- 臨時空間,例如用于某些應用程式運作時所需的臨時目錄,且無須永久保留
- 一個容器需要從另一個容器中擷取資料的目錄(多容器共享目錄)
接下來,通過一個容器之間檔案共享的案例來使用一下EmptyDir。
在一個Pod中準備兩個容器nginx和busybox,然後聲明一個Volume分别挂在到兩個容器的目錄中,然後nginx容器負責向Volume中寫日志,busybox中通過指令将日志内容讀到控制台。
建立一個volume-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-emptydir
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.14-alpine
ports:
- containerPort: 80
volumeMounts: # 将logs-volume挂在到nginx容器中,對應的目錄為 /var/log/nginx
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"] # 初始指令,動态讀取指定檔案中内容
volumeMounts: # 将logs-volume 挂在到busybox容器中,對應的目錄為 /logs
- name: logs-volume
mountPath: /logs
volumes: # 聲明volume, name為logs-volume,類型為emptyDir
- name: logs-volume
emptyDir: {}
# 建立Pod
[root@master ~]# kubectl create -f volume-emptydir.yaml
pod/volume-emptydir created
# 檢視pod
[root@master ~]# kubectl get pods volume-emptydir -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
volume-emptydir 2/2 Running 0 97s 10.244.1.100 node1 ......
# 通過podIp通路nginx
[root@master ~]# curl 10.244.1.100
......
# 通過kubectl logs指令檢視指定容器的标準輸出
[root@master ~]# kubectl logs -f volume-emptydir -n dev -c busybox
10.244.0.0 - - [13/Apr/2020:10:58:47 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.29.0" "-"
HostPath
EmptyDir中資料不會被持久化,它會随着Pod的結束而銷毀,如果想簡單的将資料持久化到主機中,可以選擇HostPath
HostPath就是将Node主機中一個實際目錄挂在到Pod中,以供容器使用,這樣的設計就可以保證Pod銷毀了,但是資料依據可以存在于Node主機上。
建立一個volume-hostpath.yaml:
apiVersion: v1
kind: Pod
metadata:
name: volume-hostpath
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
hostPath:
path: /root/logs
type: DirectoryOrCreate # 目錄存在就使用,不存在就先建立後使用
關于type的值的一點說明:
DirectoryOrCreate 目錄存在就使用,不存在就先建立後使用
Directory 目錄必須存在
FileOrCreate 檔案存在就使用,不存在就先建立後使用
File 檔案必須存在
Socket unix套接字必須存在
CharDevice 字元裝置必須存在
BlockDevice 塊裝置必須存在
# 建立Pod
[root@master ~]# kubectl create -f volume-hostpath.yaml
pod/volume-hostpath created
# 檢視Pod
[root@master ~]# kubectl get pods volume-hostpath -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-volume-hostpath 2/2 Running 0 16s 10.244.1.104 node1 ......
#通路nginx
[root@master ~]# curl 10.244.1.104
# 接下來就可以去host的/root/logs目錄下檢視存儲的檔案了
### 注意: 下面的操作需要到Pod所在的節點運作(案例中是node1)
[root@node1 ~]# ls /root/logs/
access.log error.log
# 同樣的道理,如果在此目錄下建立一個檔案,到容器中也是可以看到的
NFS
HostPath可以解決資料持久化的問題,但是一旦Node節點故障了,Pod如果轉移到了别的節點,又會出現問題了,此時需要準備單獨的網絡存儲系統,比較常用的用NFS、CIFS。
NFS是一個網絡檔案存儲系統,可以搭建一台NFS伺服器,然後将Pod中的存儲直接連接配接到NFS系統上,這樣的話,無論Pod在節點上怎麼轉移,隻要Node跟NFS的對接沒問題,資料就可以成功通路。
1)首先要準備nfs的伺服器,這裡為了簡單,直接是master節點做nfs伺服器
# 在master上安裝nfs服務
[root@master ~]# yum install nfs-utils -y
# 準備一個共享目錄
[root@master ~]# mkdir /root/data/nfs -pv
# 将共享目錄以讀寫權限暴露給192.168.109.0/24網段中的所有主機
[root@master ~]# vim /etc/exports
[root@master ~]# more /etc/exports
/root/data/nfs 192.168.109.0/24(rw,no_root_squash)
# 啟動nfs服務
[root@master ~]# systemctl start nfs
2)接下來,要在的每個node節點上都安裝下nfs,這樣的目的是為了node節點可以驅動nfs裝置
# 在node上安裝nfs服務,注意不需要啟動
[root@master ~]# yum install nfs-utils -y
3)接下來,就可以編寫pod的配置檔案了,建立volume-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
nfs:
server: 192.168.109.100 #nfs伺服器位址
path: /root/data/nfs #共享檔案路徑
4)最後,運作下pod,觀察結果
# 建立pod
[root@master ~]# kubectl create -f volume-nfs.yaml
pod/volume-nfs created
# 檢視pod
[root@master ~]# kubectl get pods volume-nfs -n dev
NAME READY STATUS RESTARTS AGE
volume-nfs 2/2 Running 0 2m9s
# 檢視nfs伺服器上的共享目錄,發現已經有檔案了
[root@master ~]# ls /root/data/
access.log error.log
進階存儲
PV和PVC
前面已經學習了使用NFS提供存儲,此時就要求使用者會搭建NFS系統,并且會在yaml配置nfs。由于kubernetes支援的存儲系統有很多,要求客戶全都掌握,顯然不現實。為了能夠屏蔽底層存儲實作的細節,友善使用者使用, kubernetes引入PV和PVC兩種資源對象。
PV(Persistent Volume)是持久化卷的意思,是對底層的共享存儲的一種抽象。一般情況下PV由kubernetes管理者進行建立和配置,它與底層具體的共享存儲技術有關,并通過插件完成與共享存儲的對接。
PVC(Persistent Volume Claim)是持久卷聲明的意思,是使用者對于存儲需求的一種聲明。換句話說,PVC其實就是使用者向kubernetes系統發出的一種資源需求申請。
使用了PV和PVC之後,工作可以得到進一步的細分:
- 存儲:存儲工程師維護
- PV: kubernetes管理者維護
- PVC:kubernetes使用者維護
PV
PV是存儲資源的抽象,下面是資源清單檔案:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
nfs: # 存儲類型,與底層真正存儲對應
capacity: # 存儲能力,目前隻支援存儲空間的設定
storage: 2Gi
accessModes: # 通路模式
storageClassName: # 存儲類别
persistentVolumeReclaimPolicy: # 回收政策
PV 的關鍵配置參數說明:
-
存儲類型
底層實際存儲的類型,kubernetes支援多種存儲類型,每種存儲類型的配置都有所差異
- 存儲能力(capacity)
目前隻支援存儲空間的設定( storage=1Gi ),不過未來可能會加入IOPS、吞吐量等名額的配置
-
通路模式(accessModes)
用于描述使用者應用對存儲資源的通路權限,通路權限包括下面幾種方式:
- ReadWriteOnce(RWO):讀寫權限,但是隻能被單個節點挂載
- ReadOnlyMany(ROX): 隻讀權限,可以被多個節點挂載
- ReadWriteMany(RWX):讀寫權限,可以被多個節點挂載
需要注意的是,底層不同的存儲類型可能支援的通路模式不同
-
回收政策(persistentVolumeReclaimPolicy)
當PV不再被使用了之後,對其的處理方式。目前支援三種政策:
- Retain (保留) 保留資料,需要管理者手工清理資料
- Recycle(回收) 清除 PV 中的資料,效果相當于執行 rm -rf /thevolume/*
- Delete (删除) 與 PV 相連的後端存儲完成 volume 的删除操作,當然這常見于雲服務商的存儲服務
需要注意的是,底層不同的存儲類型可能支援的回收政策不同
-
存儲類别
PV可以通過storageClassName參數指定一個存儲類别
- 具有特定類别的PV隻能與請求了該類别的PVC進行綁定
- 未設定類别的PV則隻能與不請求任何類别的PVC進行綁定
-
狀态(status)
一個 PV 的生命周期中,可能會處于4中不同的階段:
- Available(可用): 表示可用狀态,還未被任何 PVC 綁定
- Bound(已綁定): 表示 PV 已經被 PVC 綁定
- Released(已釋放): 表示 PVC 被删除,但是資源還未被叢集重新聲明
- Failed(失敗): 表示該 PV 的自動回收失敗
實驗
使用NFS作為存儲,來示範PV的使用,建立3個PV,對應NFS中的3個暴露的路徑。
- 準備NFS環境
# 建立目錄
[root@master ~]# mkdir /root/data/{pv1,pv2,pv3} -pv
# 暴露服務
[root@master ~]# more /etc/exports
/root/data/pv1 192.168.109.0/24(rw,no_root_squash)
/root/data/pv2 192.168.109.0/24(rw,no_root_squash)
/root/data/pv3 192.168.109.0/24(rw,no_root_squash)
# 重新開機服務
[root@master ~]# systemctl restart nfs
- 建立pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv1
server: 192.168.109.100
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv2
server: 192.168.109.100
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv3
server:
# 建立 pv
[root@master ~]# kubectl create -f pv.yaml
persistentvolume/pv1 created
persistentvolume/pv2 created
persistentvolume/pv3 created
# 檢視pv
[root@master ~]# kubectl get pv -o wide
PVC
PVC是資源的申請,用來聲明對存儲空間、通路模式、存儲類别需求資訊。下面是資源清單檔案:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
namespace: dev
spec:
accessModes: # 通路模式
selector: # 采用标簽對PV選擇
storageClassName: # 存儲類别
resources: # 請求空間
requests:
storage:
PVC 的關鍵配置參數說明:
- 通路模式(accessModes)
用于描述使用者應用對存儲資源的通路權限
-
選擇條件(selector)
通過Label Selector的設定,可使PVC對于系統中己存在的PV進行篩選
-
存儲類别(storageClassName)
PVC在定義時可以設定需要的後端存儲的類别,隻有設定了該class的pv才能被系統選出
-
資源請求(Resources )
描述對存儲資源的請求
實驗
- 建立pvc.yaml,申請pv
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc3
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage:
# 建立pvc
[root@master ~]# kubectl create -f pvc.yaml
persistentvolumeclaim/pvc1 created
persistentvolumeclaim/pvc2 created
persistentvolumeclaim/pvc3 created
# 檢視pvc
[root@master ~]# kubectl get pvc -n dev -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE VOLUMEMODE
pvc1 Bound pv1 1Gi RWX 15s Filesystem
pvc2 Bound pv2 2Gi RWX 15s Filesystem
pvc3 Bound pv3 3Gi RWX 15s Filesystem
# 檢視pv
[root@master ~]# kubectl get pv -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM AGE VOLUMEMODE
pv1 1Gi RWx Retain Bound dev/pvc1 3h37m Filesystem
pv2 2Gi RWX Retain Bound dev/pvc2 3h37m Filesystem
pv3 3Gi RWX Retain Bound dev/pvc3 3h37m Filesystem
- 建立pods.yaml, 使用pv
apiVersion: v1
kind: Pod
metadata:
name: pod1
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","while true;do echo pod1 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc1
readOnly: false
---
apiVersion: v1
kind: Pod
metadata:
name: pod2
namespace: dev
spec:
containers:
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","while true;do echo pod2 >> /root/out.txt; sleep 10; done;"]
volumeMounts:
- name: volume
mountPath: /root/
volumes:
- name: volume
persistentVolumeClaim:
claimName: pvc2
readOnly: false
# 建立pod
[root@master ~]# kubectl create -f pods.yaml
pod/pod1 created
pod/pod2 created
# 檢視pod
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod1 1/1 Running 0 14s 10.244.1.69 node1
pod2 1/1 Running 0 14s 10.244.1.70 node1
# 檢視pvc
[root@master ~]# kubectl get pvc -n dev -o wide
NAME STATUS VOLUME CAPACITY ACCESS MODES AGE VOLUMEMODE
pvc1 Bound pv1 1Gi RWX 94m Filesystem
pvc2 Bound pv2 2Gi RWX 94m Filesystem
pvc3 Bound pv3 3Gi RWX 94m Filesystem
# 檢視pv
[root@master ~]# kubectl get pv -n dev -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM AGE VOLUMEMODE
pv1 1Gi RWX Retain Bound dev/pvc1 5h11m Filesystem
pv2 2Gi RWX Retain Bound dev/pvc2 5h11m Filesystem
pv3 3Gi RWX Retain Bound dev/pvc3 5h11m Filesystem
# 檢視nfs中的檔案存儲
[root@master ~]# more /root/data/pv1/out.txt
node1
node1
[root@master ~]# more /root/data/pv2/out.txt
生命周期
PVC和PV是一一對應的,PV和PVC之間的互相作用遵循以下生命周期:
- 資源供應:管理者手動建立底層存儲和PV
-
資源綁定:使用者建立PVC,kubernetes負責根據PVC的聲明去尋找PV,并綁定
在使用者定義好PVC之後,系統将根據PVC對存儲資源的請求在已存在的PV中選擇一個滿足條件的
- 一旦找到,就将該PV與使用者定義的PVC進行綁定,使用者的應用就可以使用這個PVC了
- 如果找不到,PVC則會無限期處于Pending狀态,直到等到系統管理者建立了一個符合其要求的PV
PV一旦綁定到某個PVC上,就會被這個PVC獨占,不能再與其他PVC進行綁定了
-
資源使用:使用者可在pod中像volume一樣使用pvc
Pod使用Volume的定義,将PVC挂載到容器内的某個路徑進行使用。
-
資源釋放:使用者删除pvc來釋放pv
當存儲資源使用完畢後,使用者可以删除PVC,與該PVC綁定的PV将會被标記為“已釋放”,但還不能立刻與其他PVC進行綁定。通過之前PVC寫入的資料可能還被留在儲存設備上,隻有在清除之後該PV才能再次使用。
- 資源回收:kubernetes根據pv設定的回收政策進行資源的回收
對于PV,管理者可以設定回收政策,用于設定與之綁定的PVC釋放資源之後如何處理遺留資料的問題。隻有PV的存儲空間完成回收,才能供新的PVC綁定和使用
配置存儲
ConfigMap
ConfigMap是一種比較特殊的存儲卷,它的主要作用是用來存儲配置資訊的。
建立configmap.yaml,内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: configmap
namespace: dev
data:
info: |
username:admin
password:123456
接下來,使用此配置檔案建立configmap
# 建立configmap
[root@master ~]# kubectl create -f configmap.yaml
configmap/configmap created
# 檢視configmap詳情
[root@master ~]# kubectl describe cm configmap -n dev
Name: configmap
Namespace: dev
Labels: <none>
Annotations: <none>
Data
====
info:
----
接下來建立一個pod-configmap.yaml,将上面建立的configmap挂載進去
apiVersion: v1
kind: Pod
metadata:
name: pod-configmap
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 将configmap挂載到目錄
- name: config
mountPath: /configmap/config
volumes: # 引用configmap
- name: config
configMap:
name:
# 建立pod
[root@master ~]# kubectl create -f pod-configmap.yaml
pod/pod-configmap created
# 檢視pod
[root@master ~]# kubectl get pod pod-configmap -n dev
NAME READY STATUS RESTARTS AGE
pod-configmap 1/1 Running 0 6s
#進入容器
[root@master ~]# kubectl exec -it pod-configmap -n dev /bin/sh
# cd /configmap/config/
# ls
info
# more info
username:admin
password:123456
# 可以看到映射已經成功,每個configmap都映射成了一個目錄
# key--->檔案 value---->檔案中的内容
# 此時如果更新configmap的内容, 容器中的值也會動态更新
Secret
在kubernetes中,還存在一種和ConfigMap非常類似的對象,稱為Secret對象。它主要用于存儲敏感資訊,例如密碼、秘鑰、證書等等。
- 首先使用base64對資料進行編碼
[root@master ~]# echo -n 'admin' | base64 #準備username
YWRtaW4=
[root@master ~]# echo -n '123456' | base64 #準備password
- 接下來編寫secret.yaml,并建立Secret
apiVersion: v1
kind: Secret
metadata:
name: secret
namespace: dev
type: Opaque
data:
username: YWRtaW4=
password:
# 建立secret
[root@master ~]# kubectl create -f secret.yaml
secret/secret created
# 檢視secret詳情
[root@master ~]# kubectl describe secret secret -n dev
Name: secret
Namespace: dev
Labels: <none>
Annotations: <none>
Type: Opaque
Data
- 建立pod-secret.yaml,将上面建立的secret挂載進去:
apiVersion: v1
kind: Pod
metadata:
name: pod-secret
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
volumeMounts: # 将secret挂載到目錄
- name: config
mountPath: /secret/config
volumes:
- name: config
secret:
secretName:
# 建立pod
[root@master ~]# kubectl create -f pod-secret.yaml
pod/pod-secret created
# 檢視pod
[root@master ~]# kubectl get pod pod-secret -n dev
NAME READY STATUS RESTARTS AGE
pod-secret 1/1 Running 0 2m28s
# 進入容器,檢視secret資訊,發現已經自動解碼了
[root@master ~]# kubectl exec -it pod-secret /bin/sh -n dev
/ # ls /secret/config/
password username
/ # more /secret/config/username
admin
/ # more /secret/config/password
至此,已經實作了利用secret實作了資訊的編碼。
三、安全認證
Kubernetes的安全認證機制。
通路控制概述
Kubernetes作為一個分布式叢集的管理工具,保證叢集的安全性是其一個重要的任務。所謂的安全性其實就是保證對Kubernetes的各種用戶端進行認證和鑒權操作。
用戶端
在Kubernetes叢集中,用戶端通常有兩類:
- User Account:一般是獨立于kubernetes之外的其他服務管理的使用者賬号。
- Service Account:kubernetes管理的賬号,用于為Pod中的服務程序在通路Kubernetes時提供身份辨別。
認證、授權與準入控制
ApiServer是通路及管理資源對象的唯一入口。任何一個請求通路ApiServer,都要經過下面三個流程:
- Authentication(認證):身份鑒别,隻有正确的賬号才能夠通過認證
- Authorization(授權): 判斷使用者是否有權限對通路的資源執行特定的動作
- Admission Control(準入控制):用于補充授權機制以實作更加精細的通路控制功能。
認證管理
Kubernetes叢集安全的最關鍵點在于如何識别并認證用戶端身份,它提供了3種用戶端身份認證方式:
- HTTP Base認證:通過使用者名+密碼的方式認證
這種認證方式是把“使用者名:密碼”用BASE64算法進行編碼後的字元串放在HTTP請求中的Header Authorization域裡發送給服務端。
服務端收到後進行解碼,擷取使用者名及密碼,然後進行使用者身份認證的過程。
- HTTP Token認證:通過一個Token來識别合法使用者
這種認證方式是用一個很長的難以被模仿的字元串--Token來表明客戶身份的一種方式。
每個Token對應一個使用者名,當用戶端發起API調用請求時,需要在HTTP Header裡放入Token,API Server接到Token後會跟伺服器中儲存的token進行比對,
然後進行使用者身份認證的過程
- HTTPS證書認證:基于CA根證書簽名的雙向數字證書認證方式
這種認證方式是安全性最高的一種方式,但是同時也是操作起來最麻煩的一種方式。
HTTPS認證大體分為3個過程:
- 證書申請和下發
HTTPS通信雙方的伺服器向CA機構申請證書,CA機構下發根證書、服務端證書及私鑰給申請者
- 用戶端和服務端的雙向認證
1> 用戶端向伺服器端發起請求,服務端下發自己的證書給用戶端,
用戶端接收到證書後,通過私鑰解密證書,在證書中獲得服務端的公鑰,
用戶端利用伺服器端的公鑰認證證書中的資訊,如果一緻,則認可這個伺服器
2> 用戶端發送自己的證書給伺服器端,服務端接收到證書後,通過私鑰解密證書,
在證書中獲得用戶端的公鑰,并用該公鑰認證證書資訊,确認用戶端是否合法
- 伺服器端和用戶端進行通信
伺服器端和用戶端協商好加密方案後,用戶端會産生一個随機的秘鑰并加密,然後發送到伺服器端。
伺服器端接收這個秘鑰後,雙方接下來通信的所有内容都通過該随機秘鑰加密
注意: Kubernetes允許同時配置多種認證方式,隻要其中任意一個方式認證通過即可
授權管理
授權發生在認證成功之後,通過認證就可以知道請求使用者是誰, 然後Kubernetes會根據事先定義的授權政策來決定使用者是否有權限通路,這個過程就稱為授權。
每個發送到ApiServer的請求都帶上了使用者和資源的資訊:比如發送請求的使用者、請求的路徑、請求的動作等,授權就是根據這些資訊和授權政策進行比較,如果符合政策,則認為授權通過,否則會傳回錯誤。
API Server目前支援以下幾種授權政策:
- AlwaysDeny:表示拒絕所有請求,一般用于測試
- AlwaysAllow:允許接收所有請求,相當于叢集不需要授權流程(Kubernetes預設的政策)
- ABAC:基于屬性的通路控制,表示使用使用者配置的授權規則對使用者請求進行比對和控制
- Webhook:通過調用外部REST服務對使用者進行授權
- Node:是一種專用模式,用于對kubelet發出的請求進行通路控制
- RBAC:基于角色的通路控制(kubeadm安裝方式下的預設選項)
RBAC(Role-Based Access Control) 基于角色的通路控制,主要是在描述一件事情:給哪些對象授予了哪些權限
其中涉及到了下面幾個概念:
- 對象:User、Groups、ServiceAccount
- 角色:代表着一組定義在資源上的可操作動作(權限)的集合
- 綁定:将定義好的角色跟使用者綁定在一起
RBAC引入了4個頂級資源對象:
- Role、ClusterRole:角色,用于指定一組權限
- RoleBinding、ClusterRoleBinding:角色綁定,用于将角色(權限)賦予給對象
Role、ClusterRole
一個角色就是一組權限的集合,這裡的權限都是許可形式的(白名單)。
# Role隻能對命名空間内的資源進行授權,需要指定nameapce
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
namespace: dev
name: authorization-role
rules:
- apiGroups: [""] # 支援的API組清單,"" 空字元串,表示核心API群
resources: ["pods"] # 支援的資源對象清單
verbs: ["get", "watch", "list"] # 允許的對資源對象的操作方法清單
# ClusterRole可以對叢集範圍内資源、跨namespaces的範圍資源、非資源類型進行授權
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: authorization-clusterrole
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
需要詳細說明的是,rules中的參數:
- apiGroups: 支援的API組清單
"","apps", "autoscaling", "batch"
- resources:支援的資源對象清單
"services", "endpoints", "pods","secrets","configmaps","crontabs","deployments","jobs",
"nodes","rolebindings","clusterroles","daemonsets","replicasets","statefulsets",
"horizontalpodautoscalers","replicationcontrollers","cronjobs"
- verbs:對資源對象的操作方法清單
"get", "list", "watch", "create", "update", "patch", "delete", "exec"
RoleBinding、ClusterRoleBinding
角色綁定用來把一個角色綁定到一個目标對象上,綁定目标可以是User、Group或者ServiceAccount。
# RoleBinding可以将同一namespace中的subject綁定到某個Role下,則此subject即具有該Role定義的權限
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: authorization-role-binding
namespace: dev
subjects:
- kind: User
name: heima
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: authorization-role
apiGroup:
# ClusterRoleBinding在整個叢集級别和所有namespaces将特定的subject與ClusterRole綁定,授予權限
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: authorization-clusterrole-binding
subjects:
- kind: User
name: heima
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: authorization-clusterrole
apiGroup:
RoleBinding引用ClusterRole進行授權
RoleBinding可以引用ClusterRole,對屬于同一命名空間内ClusterRole定義的資源主體進行授權。
一種很常用的做法就是,叢集管理者為叢集範圍預定義好一組角色(ClusterRole),然後在多個命名空間中重複使用這些ClusterRole。這樣可以大幅提高授權管理工作效率,也使得各個命名空間下的基礎性授權規則與使用體驗保持一緻。
# 雖然authorization-clusterrole是一個叢集角色,但是因為使用了RoleBinding
# 是以heima隻能讀取dev命名空間中的資源
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: authorization-role-binding-ns
namespace: dev
subjects:
- kind: User
name: heima
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: authorization-clusterrole
apiGroup:
實戰:建立一個隻能管理dev空間下Pods資源的賬号
- 建立賬号
# 1) 建立證書
[root@master pki]# cd /etc/kubernetes/pki/
[root@master pki]# (umask 077;openssl genrsa -out devman.key 2048)
# 2) 用apiserver的證書去簽署
# 2-1) 簽名申請,申請的使用者是devman,組是devgroup
[root@master pki]# openssl req -new -key devman.key -out devman.csr -subj "/CN=devman/O=devgroup"
# 2-2) 簽署證書
[root@master pki]# openssl x509 -req -in devman.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out devman.crt -days 3650
# 3) 設定叢集、使用者、上下文資訊
[root@master pki]# kubectl config set-cluster kubernetes --embed-certs=true --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.109.100:6443
[root@master pki]# kubectl config set-credentials devman --embed-certs=true --client-certificate=/etc/kubernetes/pki/devman.crt --client-key=/etc/kubernetes/pki/devman.key
[root@master pki]# kubectl config set-context devman@kubernetes --cluster=kubernetes --user=devman
# 切換賬戶到devman
[root@master pki]# kubectl config use-context devman@kubernetes
Switched to context "devman@kubernetes".
# 檢視dev下pod,發現沒有權限
[root@master pki]# kubectl get pods -n dev
Error from server (Forbidden): pods is forbidden: User "devman" cannot list resource "pods" in API group "" in the namespace "dev"
# 切換到admin賬戶
[root@master pki]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".
2) 建立Role和RoleBinding,為devman使用者授權
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
namespace: dev
name: dev-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: authorization-role-binding
namespace: dev
subjects:
- kind: User
name: devman
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: dev-role
apiGroup:
[root@master pki]# kubectl create -f dev-role.yaml
role.rbac.authorization.k8s.io/dev-role created
rolebinding.rbac.authorization.k8s.io/authorization-role-binding created
- 切換賬戶,再次驗證
# 切換賬戶到devman
[root@master pki]# kubectl config use-context devman@kubernetes
Switched to context "devman@kubernetes".
# 再次檢視
[root@master pki]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
nginx-deployment-66cb59b984-8wp2k 1/1 Running 0 4d1h
nginx-deployment-66cb59b984-dc46j 1/1 Running 0 4d1h
nginx-deployment-66cb59b984-thfck 1/1 Running 0 4d1h
# 為了不影響後面的學習,切回admin賬戶
[root@master pki]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".
準入控制
通過了前面的認證和授權之後,還需要經過準入控制處理通過之後,apiserver才會處理這個請求。
準入控制是一個可配置的控制器清單,可以通過在Api-Server上通過指令行設定選擇執行哪些準入控制器:
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,
DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
隻有當所有的準入控制器都檢查通過之後,apiserver才執行該請求,否則傳回拒絕。
目前可配置的Admission Control準入控制如下:
- AlwaysAdmit:允許所有請求
- AlwaysDeny:禁止所有請求,一般用于測試
- AlwaysPullImages:在啟動容器之前總去下載下傳鏡像
- DenyExecOnPrivileged:它會攔截所有想在Privileged Container上執行指令的請求
- ImagePolicyWebhook:這個插件将允許後端的一個Webhook程式來完成admission controller的功能。
- Service Account:實作ServiceAccount實作了自動化
- SecurityContextDeny:這個插件将使用SecurityContext的Pod中的定義全部失效
- ResourceQuota:用于資源配額管理目的,觀察所有請求,確定在namespace上的配額不會超标
- LimitRanger:用于資源限制管理,作用于namespace上,確定對Pod進行資源限制
- InitialResources:為未設定資源請求與限制的Pod,根據其鏡像的曆史資源的使用情況進行設定
- NamespaceLifecycle:如果嘗試在一個不存在的namespace中建立資源對象,則該建立請求将被拒絕。當删除一個namespace時,系統将會删除該namespace中所有對象。
- DefaultStorageClass:為了實作共享存儲的動态供應,為未指定StorageClass或PV的PVC嘗試比對預設的StorageClass,盡可能減少使用者在申請PVC時所需了解的後端存儲細節
- DefaultTolerationSeconds:這個插件為那些沒有設定forgiveness tolerations并具有notready:NoExecute和unreachable:NoExecute兩種taints的Pod設定預設的“容忍”時間,為5min
- PodSecurityPolicy:這個插件用于在建立或修改Pod時決定是否根據Pod的security context和可用的PodSecurityPolicy對Pod的安全政策進行控制
四、DashBoard
之前在kubernetes中完成的所有操作都是通過指令行工具kubectl完成的。其實,為了提供更豐富的使用者體驗,kubernetes還開發了一個基于web的使用者界面(Dashboard)。使用者可以使用Dashboard部署容器化的應用,還可以監控應用的狀态,執行故障排查以及管理kubernetes中各種資源。
部署Dashboard
- 下載下傳yaml,并運作Dashboard
# 下載下傳yaml
[root@master ~]# wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml
# 修改kubernetes-dashboard的Service類型
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: NodePort # 新增
ports:
- port: 443
targetPort: 8443
nodePort: 30009 # 新增
selector:
k8s-app: kubernetes-dashboard
# 部署
[root@master ~]# kubectl create -f recommended.yaml
# 檢視namespace下的kubernetes-dashboard下的資源
[root@master ~]# kubectl get pod,svc -n kubernetes-dashboard
NAME READY STATUS RESTARTS AGE
pod/dashboard-metrics-scraper-c79c65bb7-zwfvw 1/1 Running 0 111s
pod/kubernetes-dashboard-56484d4c5-z95z5 1/1 Running 0 111s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/dashboard-metrics-scraper ClusterIP 10.96.89.218 <none> 8000/TCP 111s
service/kubernetes-dashboard NodePort 10.104.178.171 <none> 443:30009/TCP 111s
2)建立通路賬戶,擷取token
# 建立賬号
[root@master-1 ~]# kubectl create serviceaccount dashboard-admin -n kubernetes-dashboard
# 授權
[root@master-1 ~]# kubectl create clusterrolebinding dashboard-admin-rb --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:dashboard-admin
# 擷取賬号token
[root@master ~]# kubectl get secrets -n kubernetes-dashboard | grep dashboard-admin
dashboard-admin-token-xbqhh kubernetes.io/service-account-token 3 2m35s
[root@master ~]# kubectl describe secrets dashboard-admin-token-xbqhh -n kubernetes-dashboard
Name: dashboard-admin-token-xbqhh
Namespace: kubernetes-dashboard
Labels: <none>
Annotations: kubernetes.io/service-account.name: dashboard-admin
kubernetes.io/service-account.uid: 95d84d80-be7a-4d10-a2e0-68f90222d039
Type: kubernetes.io/service-account-token
Data
====
namespace: 20 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImJrYkF4bW5XcDhWcmNGUGJtek5NODFuSXl1aWptMmU2M3o4LTY5a2FKS2cifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4teGJxaGgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiOTVkODRkODAtYmU3YS00ZDEwLWEyZTAtNjhmOTAyMjJkMDM5Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmVybmV0ZXMtZGFzaGJvYXJkOmRhc2hib2FyZC1hZG1pbiJ9.NAl7e8ZfWWdDoPxkqzJzTB46sK9E8iuJYnUI9vnBaY3Jts7T1g1msjsBnbxzQSYgAG--cV0WYxjndzJY_UWCwaGPrQrt_GunxmOK9AUnzURqm55GR2RXIZtjsWVP2EBatsDgHRmuUbQvTFOvdJB4x3nXcYLN2opAaMqg3rnU2rr-A8zCrIuX_eca12wIp_QiuP3SF-tzpdLpsyRfegTJZl6YnSGyaVkC9id-cxZRb307qdCfXPfCHR_2rt5FVfxARgg_C0e3eFHaaYQO7CitxsnIoIXpOFNAR8aUrmopJyODQIPqBWUehb7FhlU1DCduHnIIXVC_UICZ-MKYewBDLw
ca.crt: 1025 bytes
3)通過浏覽器通路Dashboard的UI
在登入頁面上輸入上面的token
出現下面的頁面代表成功
使用DashBoard
示範DashBoard的使用
檢視
選擇指定的命名空間,然後點選
dev
,檢視dev空間下的所有deployment
Deployments
擴縮容
在上點選
Deployment
,然後指定
規模
,點選确定
目标副本數量
編輯
在上點選
Deployment
,然後修改
編輯
,點選确定
yaml檔案
檢視Pod
點選 Pods
, 檢視pods清單
操作Pod