1. 簡介
kubernets service 是將執行一組pods上的應用程式公開為網路服務的抽象方法。
有了 kubernets service,你就無需修改應用程式即可使用服務發現機制,kubernets 為 pods 提供自己的ip地址,併為一組pod提供相同的DNS名,並且可以在它們之間進行負載均衡。
2. 為什麼要用services
建立和銷燬 kubernets pod 以匹配叢集狀態。pod 是非永久性資源。如果你使用 Deployment 來執行你的應用程式,則它可以動態建立和銷燬 Pod。
每個 Pod 都有自己的 IP 地址,但是在 Deployment 中,在同一時刻執行的 Pod 集合可能與稍後執行該應用程式的 Pod 集合不同。
這導致了一個問題: 如果一組 Pod(稱為“後端”)為叢集內的其他 Pod(稱為“前端”)提供功能, 那麼前端如何找出並跟蹤要連線的 IP 地址,以便前端可以使用提供工作負載的後端部分?
3. quick start
3.1 建立svc
資源模板 svc-deploy-nginx.yaml
資訊如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deploy-test
name: nginx-test-1
spec:
replicas: 3
selector:
matchLabels:
app: nginx-test
template:
metadata:
labels:
app: nginx-test
spec:
containers:
- image: nginx:latest
name: nginx
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
# must be match pod template .spec.template.labels
app: nginx-test
ports:
- protocol: TCP
port: 8000
targetPort: 80
建立一個deploy,和一個 svc
注意 svc selector 對應的是 deploy 的 .spec.template.labels
建立資源
$ kubectl create -f svc-deploy-nginx.yaml
3.2 檢視svc
-
使用資原始檔檢視
$ kubectl get -f svc-deploy-nginx.yaml -o wide # 輸出內容如下 # deploy 資訊 NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR deployment.apps/nginx-test-1 3/3 3 3 42m nginx nginx:latest app=nginx-test # svc 資訊 NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR service/my-service ClusterIP 10.96.112.5 <none> 8000/TCP 42m app=nginx-test
-
使用命令檢視
檢視svc 資訊
$ kubectl get svc/my-service -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR my-service ClusterIP 10.96.112.5 <none> 8000/TCP 45m app=nginx-test
檢視endpoints資訊
可以看到成功的繫結了三個pod endpoint
$ kubectl get ep/my-service NAME ENDPOINTS AGE my-service 10.100.132.133:80,10.100.132.139:80,10.100.132.140:80 46m
檢視svc 詳細資訊
$ kubectl describe svc/my-service Name: my-service Namespace: default Labels: <none> Annotations: <none> Selector: app=nginx-test Type: ClusterIP IP: 10.96.112.5 Port: <unset> 8000/TCP TargetPort: 80/TCP Endpoints: 10.100.132.133:80,10.100.132.139:80,10.100.132.140:80 Session Affinity: None Events: <none>
3.3 訪問svc
在上面的例項中svc 並沒有指定 svc type,其預設為ClusterIP
型別(從上面的詳情資訊中也能看出)。
我們現在訪問下例項中的svc
-
在叢集內部訪問
在叢集機器上可以使用ClusterIp 訪問服務,但是無法通過 svc name訪問
在叢集pod中可以使用任意方式訪問服務
-
在叢集外部訪問
叢集外部 沒有辦法直接訪問 svc type=ClusterIp 的svc
4. svc type
svc 有以下的幾種型別:
-
ClusterIP
:通過叢集的內部 IP 暴露服務,選擇該值時服務只能夠在叢集內部訪問。 這也是預設的ServiceType
。 -
NodePort
:通過每個節點上的 IP 和靜態埠(NodePort
)暴露服務。NodePort
服務會路由到自動建立的ClusterIP
服務。 通過請求<節點 IP>:<節點埠>
,你可以從叢集的外部訪問一個NodePort
服務。 -
LoadBalancer
:使用雲提供商的負載均衡器向外部暴露服務。 外部負載均衡器可以將流量路由到自動建立的NodePort
服務和ClusterIP
服務上。 -
ExternalName
:通過返回CNAME
和對應值,可以將服務對映到externalName
欄位的內容(例如,foo.bar.example.com
)。 無需建立任何型別代理。說明: 需使用 kube-dns 1.7 及以上版本或者 CoreDNS 0.0.8 及以上版本才能使用
ExternalName
型別。
4.1 ClusterIp
ClusterIp
是預設的svc型別,在建立該資源時,kubernetes會預設分配一個 cluster ip。- svc埠為
.spec.ports.port
,其代理的後端服務的目標埠為.spec.ports.targetPort
。 - 其cluster ip 只能在叢集內部訪問,如果想通過svc name 訪問其代理的服務,只能在pod中訪問。其驗證過程可參考3.3 訪問svc。
- 叢集外部的資源無法訪問
ClusterIp
型別的svc。
4.2 NodePort
node(節點)port(埠),顧名思義 NodePort
型別的svc是通過在叢集的宿主機上開放訪問的埠,並通過 <節點 IP>:<節點埠>
(叢集任意節點ip:nodeport 都可以訪問)來實現從叢集外部訪問其svc的。
nodePort 的原理在於在 node 上開了一個埠,將向該埠的流量匯入到 kube-proxy,然後由 kube-proxy 進一步到給對應的 pod。(能夠將內部服務暴露給外部的一種方式)
-
建立資源
一個deploy 和 一個 nodeport svc,且指定了node 埠為
30007
(如果不指定,k8s會預設分配一個)apiVersion: apps/v1 kind: Deployment metadata: labels: app: nginx-deploy-test name: nginx-test-2 spec: replicas: 3 selector: matchLabels: app: nginx-test-02 template: metadata: labels: app: nginx-test-02 spec: containers: - image: nginx:latest name: nginx imagePullPolicy: IfNotPresent --- apiVersion: v1 kind: Service metadata: name: my-service-2 spec: type: NodePort selector: app: nginx-test-02 ports: - protocol: TCP port: 8000 targetPort: 80 # 可選欄位 # 預設情況下,為了方便起見,Kubernetes 會從範圍內分配一個埠號(預設:30000-32767) nodePort: 30007
-
訪問svc
4.3 LoadBalancer
使用雲提供商的負載均衡器向外部暴露服務。 外部負載均衡器可以將流量路由到自動建立的 NodePort
服務和 ClusterIP
服務上。
4.4 ExternalName
型別為 ExternalName 的服務將服務對映到 DNS 名稱,而不是典型的選擇器,例如 其他svc 或者 公網域名。 你可以使用 spec.externalName
引數指定這些服務。
-
建立服務
例如,以下 Service 定義將
default
名稱空間中的my-service-2
服務對映到my-service-1.test.svc.cluster.local
(test 名稱空間中的 my-service-1 service):apiVersion: v1 kind: Service metadata: name: my-service-2 spec: type: ExternalName externalName: my-service-1.test.svc.cluster.local
服務資訊如下:
服務是在default namespace 下
沒有分配CLUSTER-IP,但是有我們指定的 EXTERNAL-IP
$ kubectl get svc -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 170d <none> my-service-2 ExternalName <none> my-service-1.test.svc.cluster.local <none> 41s <none>
為了驗證對映效果,我們再建立一個
my-service-1.test
service注意 服務都是在test namespace 下
apiVersion: apps/v1 kind: Deployment metadata: labels: app: nginx-deploy-test name: nginx-test-1 namespace: test spec: replicas: 3 selector: matchLabels: app: nginx-test template: metadata: labels: app: nginx-test spec: containers: - image: nginx:latest name: nginx imagePullPolicy: IfNotPresent --- apiVersion: v1 kind: Service metadata: name: my-service-1 namespace: test spec: selector: app: nginx-test ports: - protocol: TCP port: 8000 targetPort: 80
服務資訊如下:
$ kubectl get -f app-nginx.yaml -owide NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR deployment.apps/nginx-test-1 3/3 3 3 48s nginx nginx:latest app=nginx-test NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR service/my-service-1 ClusterIP 10.96.188.235 <none> 8000/TCP 47s app=nginx-test
-
訪問svc
首先我們建立一個pod 用來訪問 驗證 svc
服務資訊如下:
$ kubectl get po -owide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES helloworld 1/1 Running 0 7d23h 10.100.132.161 k8s-woker-01 <none> <none>
ok,到這裡 資源都以就緒,如果我們進入到
pod/helloworld
去訪問svc/my-service-2
,如果能成功訪問到naginx 服務,則證明ExternalName svc
這種方式是可行的:
4.5 Headless Services
準確來說 Headless Services 並不屬於svc的type,而是 clusterip型別的變種
有時不需要或不想要負載均衡,以及單獨的 Service IP。 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP
)的值為 "None"
來建立 Headless
Service。
對這無頭 Service 並不會分配 Cluster IP,kube-proxy 不會處理它們, 而且平臺也不會為它們進行負載均衡和路由。 DNS 如何實現自動配置,依賴於 Service 是否定義了選擇算符。
**無選擇算符的服務 **
對沒有定義選擇算符的無頭服務,Endpoint 控制器不會建立 Endpoints
記錄。 然而 DNS 系統會查詢和配置,無論是:
- 對於 ExternalName型別的服務,查詢其 CNAME 記錄
- 對所有其他型別的服務,查詢與 Service 名稱相同的任何
Endpoints
的記錄
關於Headless Services 在k8s-statefulset博文中 有詳細的記錄,感興趣的可以訪問了解下。
5. externalIPs
Service 的 externalIPs(spec.externalIPs
)可以設定一組外部的 IP 地址,並且將流量匯入到叢集內部。
如圖:
啟動一個springboot專案 並初始化一個介面,訪問後返回
Hello World~
在本機上訪問:
接下來我們就使用externalIPs
將 192.168.0.101
流量匯入到叢集內部
目標:叢集pod中訪問
192.168.0.101:8080
時不是訪問此時的這個springboot專案,而是訪問叢集內的externalIPs
svc
-
首先到叢集master節點訪問一下springboot專案
訪問成功
$ curl 192.168.0.101:8080 Hello World~
-
建立
externalIPs
svc,和想匯入到叢集的目標pod本例項主要是為了展示流量的切換,但其實
spec.externalIPs
可以指定符合ip 規則的任意地址(即使設定的ip不可達),k8s都會幫你 在訪問設定地址的服務時,將流量匯入到spec.selector
的服務中去實際上就是 如果你把
spec.externalIPs
地址設定成123.123.123.123
,當你在服務中訪問123.123.123.123
時,其實訪問的是spec.selector
指定的服務其實更像是 給
spec.selector
到服務指定了一個 ipv4 的別名apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 8080 targetPort: 80 name: web externalIPs: - "192.168.0.101" # 將 192.168.0.101 流量匯入到 下面的Deployment中 selector: app: nginx --- apiVersion: apps/v1 kind: Deployment metadata: name: web spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 name: web
服務資訊如下:
$ kubectl get deploy -owide NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR web 2/2 2 2 3h25m nginx nginx app=nginx $ kubectl get svc/nginx -owide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR nginx ClusterIP 10.96.41.170 192.168.0.101 8080/TCP 4m app=nginx
-
建立一個pod 並進入pod ,在pod中訪問
192.168.0.101:8080
檢視訪問的是外部的springboot
專案還是叢集內的deploy/web
很顯然:k8s成功的將流量匯入到了叢集內部
如果在叢集節點上訪問
192.168.0.101:8080
,是不能將結果匯入到deploy上的,只有在叢集服務內部訪問才有效 -
此時我們將svc/nginx 刪除掉,看下訪問的結果
符合預期
6. 管理外部的服務
場景:如果我們的中介軟體或者資料庫服務不是在k8s叢集內的,而是在其他的伺服器上,但是我們還是想通過訪問svc的方式去訪問這些外部的服務,我們應該怎麼做呢?
- 宣告一個沒有選擇符的svc
- 建立一個同名(和第一步中的svc name 相同)的
endpoints
服務去管理這些外部的服務地址
例項:
例項通過建立svc 和 endpoints,實現訪問svc-name,svc 將流量匯出到外部的springboot(專案資訊如5.0中提到的)專案中
-
建立服務
apiVersion: v1 kind: Service metadata: name: my-service spec: ports: - protocol: TCP port: 80 targetPort: 8080 --- apiVersion: v1 kind: Endpoints metadata: # must be match svc-name name: my-service subsets: - addresses: # 指向外部的springboot 服務 - ip: 192.168.0.101 ports: - port: 8080
-
訪問測試
7. 選擇自己的 IP 地址
在 Service
建立的請求中,可以通過設定 spec.clusterIP
欄位來指定自己的叢集 IP 地址。