Service資源用於爲pod對象提供一個固定、統一的訪問接口及負載均衡的能力,並藉助新一代DNS系統的服務發現功能,解決客戶端發現並訪問容器化應用的問題。
1、service資源實現模型
(1)service資源
Service資源基於標籤選擇器將一組pod定義成一個邏輯組合,並通過自己的IP地址和端口調度代理請求至組內的對象上。並對客戶端隱藏了真實的處理用戶請求的pod資源。Service資源會通過API Service持續監視着標籤選擇器匹配到的後端pod對象,並實時跟蹤個對象的變動;但是service並不直接連接至pod對象,它們直接還有一箇中間層(Endpoint)資源對象,創建Service資源對象時,其關聯的Endpoint對象會自動創建。
(2)service實現方式
一個service對象就是工作節點上的一些iptables或ipvs規則,用於將到達service對象的IP地址的流量調度轉發至相應的Endpoint對象制定的IP地址和端口上。Service ip就是用於生成iptables或ipvs規則時使用的IP地址。Kube-proxy將請求代理至相應端點的方式主要有三種:
1)userspace代理模型
這種代理模型爲kubernetes1.1版本之前默認的代理模型;這種代理模型中,請求在內核空間和用戶空間來回轉發導致效率不高。
2)iptables代理模型
這種調度模型爲kubernetes1.2版本至1.11版本之間的默認調度模型;默認算法是隨機調度;iptables代理模型不會再背挑選中的後端pod資源無響應時自動進行重定向。
3)ipvs調度模型
Ipvs調度模型從1.11版本開始成爲默認的調度模型;這種模型中,ipvs構建於netfilter的鉤子函數之上,使用hash表作爲底層數據結構並工作與內核空間,具有流量轉發速度快,規則同步性能好等特性。IPvs支持支持衆多調度算法,如rr、lc、dh、sh、sed、nq等。
2、service資源的使用
Service資源的定義與其他資源的定義方式基本相同,spec字段經常嵌套的的字段有selector(定義標籤選擇器)ports(定義要暴露的端口)。
# 爲前面創建的deployment控制器創建定義service資源
[root@master01 test03]# cat test01.yaml
apiVersion: v1
kind: Service
metadata:
name: test-svc
spec:
selector:
app: test-deploy
ports:
- protocol: TCP
port: 80
targetPort: 80
# 創建service資源
[root@master01 test03]# kubectl apply -f test01.yaml
service/test-svc created
# 查看創建的service
[root@master01 test03]# kubectl get svc test-svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
test-svc ClusterIP 10.100.249.83 <none> 80/TCP 3m6s
# 查看endpoint資源端點列表
[root@master01 test03]# kubectl get endpoints test-svc
NAME ENDPOINTS AGE
test-svc 10.244.1.38:80,10.244.1.39:80,10.244.2.37:80 + 2 more... 4m24s
3、服務發現
在kubernetes集羣中,service爲pod中的服務類應用提供了一個穩定的訪問入口,pod中的應用是通過服務發現機制來得知某個特定service資源的IP和端口的。
(1)服務發現
根據服務發現的實現方式,可將服務發現分爲客戶端發現(由客戶端到服務註冊中心發現其依賴到的服務的相關信息)和服務端發現(通過中央路由器或者服務均衡器組件實現)兩類。
在kubernetes中,1.3版本開始服務發現是由kubeDNS實現的,從1.11版本後,服務發現功能是由CoreDNS組件實現的。
(2)服務發現的方式
1)環境變量方式
在創建pod資源時,kubelet會將其所屬名稱空間內的每個活動的Service對象以一系列環境變量的方式注入其中,支持使用kubernetes service環境變量及與docker的links兼容的變量。
2)DNS的方式
ClusterDNS是kubernetes系統中用於服務解析和名稱發現的服務,集羣中創建的每個service對象,都會由其生成相關的資源記錄;默認集羣內的各pod資源會自動配置其作爲名稱解析服務器,並在其DNS搜索列表中包含它所屬名稱空間的域名後綴。
在創建service資源對象時,ClusterDNS會爲它自動創建資源記錄用於名稱解析和服務註冊,pod資源可直接使用標準的DNS名稱來訪問這些“servie”資源。
4、服務暴露
Service的IP地址僅在集羣內可達,而有些服務需要暴露到外部網絡中接受各類客戶端的訪問,此時就需要在集羣的邊緣爲其添加一層轉發機制,以實現將外部請求接入到集羣service資源之上,及將集羣中的服務發佈到外部網絡中。Kubernetes中service共有四種類型,分別如下:
(1)ClusterIP
通過集羣內部的IP地址暴露服務,地址僅在集羣內部可達,無法被集羣外部的客戶端訪問。
(2)NodePort
NodePort類型建立在ClusterIP類型之上,用其所在的每個節點的IP地址的某靜態端口暴露服務,它依然會爲service分配集羣IP地址,並將此作爲NodePort的路由目標。
Kubernetes集羣在安裝部署時通常會預留一個端口範圍用於NodePort,默認爲30000-322767之間的端口。而在使用NodePort類型的service時,需要通過” service.spec.type”字段指定service類型名稱。
# 定義NodePort類型的service資源,並指定端口(一般不建議指定端口)
[root@master01 test03]# cat test02.yaml
apiVersion: v1
kind: Service
metadata:
name: test-nodportsvc
spec:
type: NodePort
selector:
app: test-deploy
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30033
# 查看創建好的service,依然創建了clusterIP,並將容器內的端口映射到了node節點上
[root@master01 test03]# kubectl get svc test-nodportsvc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
test-nodportsvc NodePort 10.111.192.243 <none> 80:30033/TCP 6m20s
(3)LoadBalancer
LoadBalancer類型建立在NodePort類型之上,其通過cloud provider提供的負載均衡器將服務暴露到集羣外部。LoadBalancer類型的service與NodePort類型的service的使用方法基本類似。
(4)ExternalName
ExternalNamel類型的service資源用於將集羣外部的服務發佈到集羣中以供Pod中的應用程序訪問,因此,不需要使用標籤選擇器關聯任何的pod對象,但必須要用spec.externalName屬性定義一個CNAME記錄用於返回外部真正提供服務的主機的別名,而後通過CHAME記錄獲取到相關主機的IP地址。
5、Headless類型的Service資源
如果客戶端需要直接訪問Service資源後端的所有Pod資源,這時就應該向客戶端暴露每個Pod資源的IP地址,而不再是中間層Service對象的ClusterIP,這種類型的service稱爲headless service。這種類型的service也沒有使用負載均衡代理它的需要。
如何爲Headless類型的Service資源配置IP地址取決於它的標籤選擇器的定義;如果具有標籤選擇器,則端點控制器會在API中爲其創建Endpoint記錄,並將ClusterDns服務中的A記錄直接解析到此Service後端的各Pod對象的IP地址對象上;如果沒有標籤選擇器,則端點控制器不會在API中爲其創建Endpoints記錄。
(1)創建Headless Service資源
定義Headless Service類型的資源時,只需將ClusterIP字段的至設置爲“None”即可。
# 定義Headless Service類型
[root@master01 test03]# cat test03.yaml
kind: Service
apiVersion: v1
metadata:
name: test-headless-svc
spec:
clusterIP: None
selector:
app: test-deploy
ports:
- name: httpport
port: 80
targetPort: 80
# 查看創建的headless service資源
[root@master01 test03]# kubectl get svc test-headless-svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
test-headless-svc ClusterIP None <none> 80/TCP 62s
[root@master01 test03]# kubectl describe test-headless-svc
error: the server doesn't have a resource type "test-headless-svc"
[root@master01 test03]# kubectl describe svc test-headless-svc
. . . . . .
Endpoints: 10.244.1.38:80,10.244.1.39:80,10.244.2.37:80 + 2 more...
(2)pod資源發現
Headless Service通過標籤選擇器關聯到所有pod資源的IP地址之上,客戶端向此service對象發起的請求將通過dns查詢時返回的IP地址(多個IP地址則以輪詢方式返回)直接接入到Pod資源中的應用之上,而不再由Service資源進行代理轉發。
# 查看dns對此service名稱的解析
[root@master01 test03]# kubectl exec -it cirros-58cc4cdc59-2f4s6 -- sh
/ # nslookup test-headless-svc
Server: 10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local
Name: test-headless-svc
Address 1: 10.244.2.39 10-244-2-39.test-headless-svc.default.svc.cluster.local
Address 2: 10.244.2.38 10-244-2-38.test-headless-svc.default.svc.cluster.local
Address 3: 10.244.2.37 10-244-2-37.test-headless-svc.default.svc.cluster.local
Address 4: 10.244.1.38 10-244-1-38.test-headless-svc.default.svc.cluster.local
Address 5: 10.244.1.39 10-244-1-39.test-headless-svc.default.svc.cluster.local
6、Ingress資源
Kubernetes提供了兩種負載均衡機制,一種是工作與傳輸層的Service資源,實現“TCP負載均衡器”,另一種是Ingress資源,實現“HTTP(S)負載均衡器”。
(1)Ingress和Ingress控制器
Ingress是kubernetes API的標準類型資源之一,它其實是一組基於DNS名稱或URL路勁把請求轉發至指定的Service資源的規則,用於將集羣外部的請求轉發至集羣內部完成服務的發佈。
Ingre資源並不能進行“能量穿透”,它僅是一組路由規則的集合,而能夠爲Ingress資源監聽套接字並轉發流量的組件稱爲Ingress控制器(Ingress Controller)。
Ingress控制器可以由任何具有反向代理功能的服務程序實現,Ingress也是運行於集羣中的Pod資源,應與被代理的pod運行與同一網絡中。
(2)Ingress資源的定義
Ingress資源是基於HTTP虛擬主機或URL的轉發規則,Ingress.spec資源是定義Ingress資源的核心組成部分,它主要由以下嵌套字段組成:
rules:用於定義當前Ingress資源的轉發規則列表,未定義的rule規則或者匹配不到 任何規則時,所有的流量都會轉發到backend定義的默認後端。
backend:默認的後端用於服務那些沒有匹配到任何規則的請求。Backend對象的 定義由兩個必選的內嵌字段serviceName和servicePort組成。
tls:目前僅支持通過默認端口443提供服務,tls也有以下兩個內嵌字段組成,僅 在定義TLS的轉發規則時才需要定義此類對象:
hosts:包含於使用的TLS證書之內的主機名稱字符串列表
secretName:用於引用SSL回話的secret對象名稱,在基於SNI實現多主機路由的場景中,此字段爲可選。
# 定義ingress資源
[root@master01 test03]# cat test04.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
annotations:
kubernetes.io/ingress: “nginx test”
spec:
rules:
- host: www.dayi123.com
http:
paths:
- backend:
serviceName: test-headless-svc
servicePort: 80
(3)Ingress資源的類型
1)單service資源型ingress
暴露單個服務的方法可以使用service的NodePort、LoadBalancer類型;也可使用Ingress,使用Ingress時,只需指定ingress的default backend即可。
# 定義一個單Service資源型的ingress
[root@master01 test03]# cat test05.yaml
apiVersion: extensions/v1beat1
kind: Ingress
metadata:
name: test-ingress02
spec:
backend:
serviceName: nginx-svc
serviceName: 80
2)基於URL路勁進行流量分發的ingress
基於URL路徑轉發是根據客戶端請求不同的URL路徑轉發到不同的後端服務中。
# 定義一個多路徑的ingress資源
[root@master01 test03]# cat test06.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress05
annotations:
kubernetes.io/ingress: “nginx test path”
spec:
rules:
- host: www.dayi123.com
http:
paths:
- path: /nginx
backend:
serviceName: nginx-svc
servicePort: 80
- path: /tomcat
backend:
serviceName: test-svc
servicePort: 80
3)基於主機名稱的ingress虛擬主機
基於主機名的ingress虛擬主機是將每個應用分別以獨立的FQDN主機名進行輸出。
# 定義一個基於主機名的service
[root@master01 test03]# cat test07.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress06
spec:
rules:
- host: blog.dayi123.com
http:
paths:
- backend:
serviceName: nginx-svc
servicePort: 80
- host: doc.dayi123.com
http:
paths:
- backend:
serviceName: test-svc
servicePort: 80
4)TLS類型的Ingress資源
TLS類型的Ingress資源用於以HTTPS發佈Service資源,基於一個含有私鑰和證書的Secret對象即可配置TLS協議的Ingress資源。Ingress資源目前僅支持單TLS端口,並且還會卸載TLS回話。
(4)部署基於nginx的Ingress控制器
Ingress是自身運行於Pod中的容器應用,一般是nginx或Envoy一類的具有代理及負載均衡功能的守護進程,它監視着來自於API Service的Ingress對象狀態,並以其規則生
相應的應用程序專有格式的配置文件並通過重載或重啓守護進程而使新配置生效。運行Pod資源的Ingress控制器接入外部請求有以下兩種方法:
1)以Deployment控制器管理Ingress控制器的Pod資源,並通過NodePort或LoadBalancer類型的Service對象爲其接入集羣外部的請求流量
2)藉助於DaemonSet控制器,將Ingress控制器的Pod資源各自以單一實力的方式運行於集羣的所有或部分工作節點,並配置該類pod以hostPort或hostNetwork的方式在當前節點接入外部流量。
# 在線創建nginx ingress控制器
[root@master01 ~]# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml
# 查看創建的nginx ingress控制器是否運行正常
[root@master01 ~]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
nginx-ingress-controller-c595c6896-6xcd5 1/1 Running 0 92m
在線的nginx ingress配置清單中採用了基於deployment控制器部署方式,因此接入外部流量之前需要通過NodePort或LoadBalancer類型的service資源對象。
# 爲nginx ingress定義service資源配置清單
[root@master01 test03]# cat test-nginx-ingress.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
- name: https
port: 443
selector:
app.kubernetes.io/name: ingress-nginx
# 爲nginx ingress控制器創建service
[root@master01 test03]# kubectl apply -f test-nginx-ingress.yaml
service/nginx-ingress-controller created
# 查看創建的service
[root@master01 test03]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-controller NodePort 10.105.134.97 <none> 80:30409/TCP,443:31235/TCP 16s
7、使用ingress發佈Tomcat
實驗的拓撲如下
(1)創建名稱空間
# 定義名稱空間的資源配置清單
[root@master01 ingress-tomcat]# cat ingress-tomcat.yaml
apiVersion: v1
kind: Namespace
metadata:
name: test-ingress
labels:
env: test-ingress
# 創建名稱空間
[root@master01 ingress-tomcat]# kubectl apply -f ingress-tomcat.yaml
namespace/test-ingress created
(2)部署tomcat實例
# 定義基於deployment的tomcat資源配置清單
[root@master01 ingress-tomcat]# cat tomcat-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
namespace: test-ingress
spec:
replicas: 2
selector:
matchLabels:
app: ingress-tomcat
template:
metadata:
labels:
app: ingress-tomcat
spec:
containers:
- name: tomcat
image: tomcat
ports:
- name: httport
containerPort: 8080
- name: ajpport
containerPort: 8009
# 創建tomcat的pod控制器
[root@master01 ingress-tomcat]# kubectl apply -f tomcat-deployment.yaml
deployment.apps/tomcat-deployment created
# 查看創建的tomcat pod
[root@master01 ingress-tomcat]# kubectl get pods -n test-ingress
NAME READY STATUS RESTARTS AGE
tomcat-deployment-67f5dcbdb4-7mmhz 1/1 Running 0 96s
tomcat-deployment-67f5dcbdb4-km5q8 1/1 Running 0 96s
(3)創建Service資源
Ingress資源僅能通過Service資源識別相應的Pod資源,獲取其IP地址和端口,然後Ingress控制器即可直接使用各Pod對象的IP地址與Pod內的服務直接進行通信,不經過Service的代理和調度,因此Service資源的ClusterIP對Ingress控制器來說存不存在無所謂。
# 定義service的資源配置清單文件
[root@master01 ingress-tomcat]# cat tomcat-service.yaml
apiVersion: v1
kind: Service
metadata:
name: tomcat-svc
namespace: test-ingress
labels:
app: tomcat-svc
spec:
selector:
app: ingress-tomcat
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
# 創建並查看service
[root@master01 ingress-tomcat]# kubectl apply -f tomcat-service.yaml
service/tomcat-svc created
[root@master01 ingress-tomcat]# kubectl get svc -n test-ingress
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
tomcat-svc ClusterIP 10.106.153.248 <none> 80/TCP 15s
(4)創建Ingress資源
# 定義ingress資源配置清單
[root@master01 ingress-tomcat]# cat tomcat-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: tomcat
namespace: test-ingress
annotations:
kubernetes.io/ingress: "nginx"
spec:
rules:
- host: tomcat.dayi123.com
http:
paths:
- path:
backend:
serviceName: tomcat-svc
servicePort: 80
# 創建並查看Ingress
[root@master01 ingress-tomcat]# kubectl apply -f tomcat-ingress.yaml
ingress.extensions/tomcat created
[root@master01 ingress-tomcat]# kubectl get ingress -n test-ingress
NAME HOSTS ADDRESS PORTS AGE
tomcat tomcat.dayi123.com 80 14s
上面的配置完成後,在本地主機的hosts中添加tomcat.dayi123.com對應node節點的解析後,就可在本地通過http://tomcat.dayi123.com:30409去訪問tomcat服務。而各node節點的30409端口是在前面爲nginx類型的ingress控制器創建的service中定義的。
(5)配置TLS Ingress資源
互聯網中的服務基本都是以https的方式提供服務的,如果希望ingress控制器接受客戶端的請求時又希望它能夠提供https服務,就應該配置tls類型的ingress資源。
# 生成用於測試的私鑰和自簽證書
[root@master01 ingress-tomcat]# openssl genrsa -out tls.key 2048
[root@master01 ingress-tomcat]# openssl req -new -x509 -key tomcat.key -out tomcat.crt -subj /C=CN/ST=Shanghai/L=Shanghai/O/dev/CN=tomcat.dayi123.com -days 736
在ingress控制器上配置HTTPS主機時,是不能直接使用私鑰和證書文件的,而是要使用Secret資源對象來傳遞相關的數據。
# 創建一個TLS類型名爲tomcat-ingress-secret的secret資源
[root@master01 ingress-tomcat]# kubectl create secret tls tomcat-ingress-secret --cert=tomcat.crt --key=tomcat.key -n test-ingress
secret/tomcat-ingress-secret created
# 查看創建的secret資源
[root@master01 ingress-tomcat]# kubectl get secrets tomcat-ingress-secret -n test-ingress
NAME TYPE DATA AGE
tomcat-ingress-secret kubernetes.io/tls 2 109s
secret資源創建完成後,就可以將創建的secret應用到ingress資源的配置清單中。
# 定義TLS類型的Ingress資源的配置清單
[root@master01 ingress-tomcat]# cat tomcat-ingress-tls.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: tomcat-ingress-tls
namespace: test-ingress
annotations:
kubernetes.io/ingress: "nginx"
spec:
tls:
- hosts:
- tomcat.dayi123.com
secretName: tomcat-ingress-secret
rules:
- host: tomcat.dayi123.com
http:
paths:
- path:
backend:
serviceName: tomcat-svc
servicePort: 80
# 創建並查看tls類型的ingress資源
[root@master01 ingress-tomcat]# kubectl apply -f tomcat-ingress-tls.yaml
ingress.extensions/tomcat-ingress-tls created
[root@master01 ingress-tomcat]# kubectl get ingress tomcat-ingress-tls -n test-ingress
NAME HOSTS ADDRESS PORTS AGE
tomcat-ingress-tls tomcat.dayi123.com 80, 443 3m35s
(6)測試
在前面爲基於nginx的ingress控制器創建的service中已將443端口映射到了node節點的31235端口,在本地對域名tomcat.dayi123.com做了解析就可以通過https://tomcat.dayi123.com: 31235進行測試。
# 查看ingress控制器中映射到本地的端口
[root@master01 ingress-tomcat]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-controller NodePort 10.105.134.97 <none> 80:30409/TCP,443:31235/TCP 167m
# 在linux客戶端進行測試(測試前要做hosts解析)
[root@master01 ingress-tomcat]# curl -k -v https://tomcat.dayi123.com:31235