# Service
Kubernetes [`Pod`](https://kubernetes.io/docs/user-guide/pods) 是有生命周期的,它們可以被創建,也可以被銷毀,然而一旦被銷毀生命就永遠結束。
通過 [`ReplicationController`](https://kubernetes.io/docs/user-guide/replication-controller) 能夠動態地創建和銷毀 `Pod`。
每個 `Pod` 都會獲取它自己的 IP 地址,即使這些 IP 地址不總是穩定可依賴的。
這會導致一個問題:在 Kubernetes 集群中,如果一組 `Pod`(稱為 backend)為其它 `Pod` (稱為 frontend)提供服務,那么那些 frontend 該如何發現,并連接到這組 `Pod` 中的哪些 backend 呢?
關于 `Service`
Kubernetes `Service` 定義了這樣一種抽象:一個 `Pod` 的邏輯分組,一種可以訪問它們的策略 —— 通常稱為微服務。
這一組 `Pod` 能夠被 `Service` 訪問到,通常是通過 [`Label Selector`](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors)(查看下面了解,為什么可能需要沒有 selector 的 `Service`)實現的。
舉個例子,考慮一個圖片處理 backend,它運行了3個副本。這些副本是可互換的 —— frontend 不需要關心它們調用了哪個 backend 副本。
然而組成這一組 backend 程序的 `Pod` 實際上可能會發生變化,frontend 客戶端不應該也沒必要知道,而且也不需要跟蹤這一組 backend 的狀態。
`Service` 定義的抽象能夠解耦這種關聯。
對 Kubernetes 集群中的應用,Kubernetes 提供了簡單的 `Endpoints` API,只要 `Service` 中的一組 `Pod` 發生變更,應用程序就會被更新。
對非 Kubernetes 集群中的應用,Kubernetes 提供了基于 VIP 的網橋的方式訪問 `Service`,再由 `Service` 重定向到 backend `Pod`。
## 定義 Service
一個 `Service` 在 Kubernetes 中是一個 REST 對象,和 `Pod` 類似。
像所有的 REST 對象一樣, `Service` 定義可以基于 POST 方式,請求 apiserver 創建新的實例。
例如,假定有一組 `Pod`,它們對外暴露了 9376 端口,同時還被打上 `"app=MyApp"` 標簽。
```yaml
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
```
上述配置將創建一個名稱為 “my-service” 的 `Service` 對象,它會將請求代理到使用 TCP 端口 9376,并且具有標簽 `"app=MyApp"` 的 `Pod` 上。
這個 `Service` 將被指派一個 IP 地址(通常稱為 “Cluster IP”),它會被服務的代理使用(見下面)。
該 `Service` 的 selector 將會持續評估,處理結果將被 POST 到一個名稱為 “my-service” 的 `Endpoints` 對象上。
需要注意的是, `Service` 能夠將一個接收端口映射到任意的 `targetPort`。
默認情況下,`targetPort` 將被設置為與 `port` 字段相同的值。
可能更有趣的是,`targetPort` 可以是一個字符串,引用了 backend `Pod` 的一個端口的名稱。
但是,實際指派給該端口名稱的端口號,在每個 backend `Pod` 中可能并不相同。
對于部署和設計 `Service` ,這種方式會提供更大的靈活性。
例如,可以在 backend 軟件下一個版本中,修改 Pod 暴露的端口,并不會中斷客戶端的調用。
Kubernetes `Service` 能夠支持 `TCP` 和 `UDP` 協議,默認 `TCP` 協議。
### 沒有 selector 的 Service
Service 抽象了該如何訪問 Kubernetes `Pod`,但也能夠抽象其它類型的 backend,例如:
* 希望在生產環境中使用外部的數據庫集群,但測試環境使用自己的數據庫。
* 希望服務指向另一個 [`Namespace`](https://kubernetes.io/docs/user-guide/namespaces) 中或其它集群中的服務。
* 正在將工作負載轉移到 Kubernetes 集群,和運行在 Kubernetes 集群之外的 backend。
在任何這些場景中,都能夠定義沒有 selector 的 `Service` :
```yaml
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
```
由于這個 `Service` 沒有 selector,就不會創建相關的 `Endpoints` 對象。可以手動將 `Service` 映射到指定的 `Endpoints`:
```yaml
kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 9376
```
注意:Endpoint IP 地址不能是 loopback(127.0.0.0/8)、 link-local(169.254.0.0/16)、或者 link-local 多播(224.0.0.0/24)。
訪問沒有 selector 的 `Service`,與有 selector 的 `Service` 的原理相同。請求將被路由到用戶定義的 Endpoint(該示例中為 `1.2.3.4:9376`)。
ExternalName `Service` 是 `Service` 的特例,它沒有 selector,也沒有定義任何的端口和 Endpoint。
相反地,對于運行在集群外部的服務,它通過返回該外部服務的別名這種方式來提供服務。
```yaml
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
```
當查詢主機 `my-service.prod.svc.CLUSTER`時,集群的 DNS 服務將返回一個值為 `my.database.example.com` 的 `CNAME` 記錄。
訪問這個服務的工作方式與其它的相同,唯一不同的是重定向發生在 DNS 層,而且不會進行代理或轉發。
如果后續決定要將數據庫遷移到 Kubernetes 集群中,可以啟動對應的 Pod,增加合適的 Selector 或 Endpoint,修改 `Service` 的 `type`。
## VIP 和 Service 代理
在 Kubernetes 集群中,每個 Node 運行一個 `kube-proxy` 進程。`kube-proxy` 負責為 `Service` 實現了一種 VIP(虛擬 IP)的形式,而不是 `ExternalName` 的形式。
在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默認的運行模式。
從 Kubernetes v1.2 起,默認就是 iptables 代理。
在Kubernetes v1.8.0-beta.0中,添加了ipvs代理。
在 Kubernetes v1.0 版本,`Service` 是 “4層”(TCP/UDP over IP)概念。
在 Kubernetes v1.1 版本,新增了 `Ingress` API(beta 版),用來表示 “7層”(HTTP)服務。
### userspace 代理模式
這種模式,kube-proxy 會監視 Kubernetes master 對 `Service` 對象和 `Endpoints` 對象的添加和移除。
對每個 `Service`,它會在本地 Node 上打開一個端口(隨機選擇)。
任何連接到“代理端口”的請求,都會被代理到 `Service` 的backend `Pods` 中的某個上面(如 `Endpoints` 所報告的一樣)。
使用哪個 backend `Pod`,是基于 `Service` 的 `SessionAffinity` 來確定的。
最后,它安裝 iptables 規則,捕獲到達該 `Service` 的 `clusterIP`(是虛擬 IP)和 `Port` 的請求,并重定向到代理端口,代理端口再代理請求到 backend `Pod`。
網絡返回的結果是,任何到達 `Service` 的 IP:Port 的請求,都會被代理到一個合適的 backend,不需要客戶端知道關于 Kubernetes、`Service`、或 `Pod` 的任何信息。
默認的策略是,通過 round-robin 算法來選擇 backend `Pod`。
實現基于客戶端 IP 的會話親和性,可以通過設置 `service.spec.sessionAffinity` 的值為 `"ClientIP"` (默認值為 `"None"`)。

### iptables 代理模式
這種模式,kube-proxy 會監視 Kubernetes master 對 `Service` 對象和 `Endpoints` 對象的添加和移除。
對每個 `Service`,它會安裝 iptables 規則,從而捕獲到達該 `Service` 的 `clusterIP`(虛擬 IP)和端口的請求,進而將請求重定向到 `Service` 的一組 backend 中的某個上面。
對于每個 `Endpoints` 對象,它也會安裝 iptables 規則,這個規則會選擇一個 backend `Pod`。
默認的策略是,隨機選擇一個 backend。
實現基于客戶端 IP 的會話親和性,可以將 `service.spec.sessionAffinity` 的值設置為 `"ClientIP"` (默認值為 `"None"`)。
和 userspace 代理類似,網絡返回的結果是,任何到達 `Service` 的 IP:Port 的請求,都會被代理到一個合適的 backend,不需要客戶端知道關于 Kubernetes、`Service`、或 `Pod` 的任何信息。
這應該比 userspace 代理更快、更可靠。然而,不像 userspace 代理,如果初始選擇的 `Pod` 沒有響應,iptables 代理不能自動地重試另一個 `Pod`,所以它需要依賴 [readiness probes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#defining-readiness-probes)。

### ipvs 代理模式
這種模式,kube-proxy會監視Kubernetes `Service`對象和`Endpoints`,調用`netlink`接口以相應地創建ipvs規則并定期與Kubernetes `Service`對象和`Endpoints`對象同步ipvs規則,以確保ipvs狀態與期望一致。訪問服務時,流量將被重定向到其中一個后端Pod。
與iptables類似,ipvs基于netfilter 的 hook 功能,但使用哈希表作為底層數據結構并在內核空間中工作。這意味著ipvs可以更快地重定向流量,并且在同步代理規則時具有更好的性能。此外,ipvs為負載均衡算法提供了更多選項,例如:
- `rr`:輪詢調度
- `lc`:最小連接數
- `dh`:目標哈希
- `sh`:源哈希
- `sed`:最短期望延遲
- `nq`:?不排隊調度
**注意:** ipvs模式假定在運行kube-proxy之前在節點上都已經安裝了IPVS內核模塊。當kube-proxy以ipvs代理模式啟動時,kube-proxy將驗證節點上是否安裝了IPVS模塊,如果未安裝,則kube-proxy將回退到iptables代理模式。

## 多端口 Service
很多 `Service` 需要暴露多個端口。對于這種情況,Kubernetes 支持在 `Service` 對象中定義多個端口。
當使用多個端口時,必須給出所有的端口的名稱,這樣 Endpoint 就不會產生歧義,例如:
```yaml
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
```
## 選擇自己的 IP 地址
在 `Service` 創建的請求中,可以通過設置 `spec.clusterIP` 字段來指定自己的集群 IP 地址。
比如,希望替換一個已經存在的 DNS 條目,或者遺留系統已經配置了一個固定的 IP 且很難重新配置。
用戶選擇的 IP 地址必須合法,并且這個 IP 地址在 `service-cluster-ip-range` CIDR 范圍內,這對 API Server 來說是通過一個標識來指定的。
如果 IP 地址不合法,API Server 會返回 HTTP 狀態碼 422,表示值不合法。
### 為何不使用 round-robin DNS?
一個不時出現的問題是,為什么我們都使用 VIP 的方式,而不使用標準的 round-robin DNS,有如下幾個原因:
* 長久以來,DNS 庫都沒能認真對待 DNS TTL、緩存域名查詢結果
* 很多應用只查詢一次 DNS 并緩存了結果
* 就算應用和庫能夠正確查詢解析,每個客戶端反復重解析造成的負載也是非常難以管理的
我們盡力阻止用戶做那些對他們沒有好處的事情,如果很多人都來問這個問題,我們可能會選擇實現它。
## 服務發現
Kubernetes 支持2種基本的服務發現模式 —— 環境變量和 DNS。
### 環境變量
當 `Pod` 運行在 `Node` 上,kubelet 會為每個活躍的 `Service` 添加一組環境變量。
它同時支持 [Docker links 兼容](https://docs.docker.com/userguide/dockerlinks/) 變量(查看 makeLinkVariables)、簡單的 `{SVCNAME}_SERVICE_HOST` 和 `{SVCNAME}_SERVICE_PORT` 變量,這里 `Service` 的名稱需大寫,橫線被轉換成下劃線。
舉個例子,一個名稱為 `"redis-master"` 的 Service 暴露了 TCP 端口 6379,同時給它分配了 Cluster IP 地址 10.0.0.11,這個 Service 生成了如下環境變量:
```bash
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
```
*這意味著需要有順序的要求* —— `Pod` 想要訪問的任何 `Service` 必須在 `Pod` 自己之前被創建,否則這些環境變量就不會被賦值。DNS 并沒有這個限制。
### DNS
一個可選(盡管強烈推薦)[集群插件](http://releases.k8s.io/master/cluster/addons/README.md) 是 DNS 服務器。
DNS 服務器監視著創建新 `Service` 的 Kubernetes API,從而為每一個 `Service` 創建一組 DNS 記錄。
如果整個集群的 DNS 一直被啟用,那么所有的 `Pod` 應該能夠自動對 `Service` 進行名稱解析。
例如,有一個名稱為 `"my-service"` 的 `Service`,它在 Kubernetes 集群中名為 `"my-ns"` 的 `Namespace` 中,為 `"my-service.my-ns"` 創建了一條 DNS 記錄。
在名稱為 `"my-ns"` 的 `Namespace` 中的 `Pod` 應該能夠簡單地通過名稱查詢找到 `"my-service"`。
在另一個 `Namespace` 中的 `Pod` 必須限定名稱為 `"my-service.my-ns"`。
這些名稱查詢的結果是 Cluster IP。
Kubernetes 也支持對端口名稱的 DNS SRV(Service)記錄。
如果名稱為 `"my-service.my-ns"` 的 `Service` 有一個名為 `"http"` 的 `TCP` 端口,可以對 `"_http._tcp.my-service.my-ns"` 執行 DNS SRV 查詢,得到 `"http"` 的端口號。
Kubernetes DNS 服務器是唯一的一種能夠訪問 `ExternalName` 類型的 Service 的方式。
更多信息可以查看 [DNS Pod 和 Service](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/)。
## Headless Service
有時不需要或不想要負載均衡,以及單獨的 Service IP。
遇到這種情況,可以通過指定 Cluster IP(`spec.clusterIP`)的值為 `"None"` 來創建 `Headless` Service。
這個選項允許開發人員自由尋找他們自己的方式,從而降低與 Kubernetes 系統的耦合性。
應用仍然可以使用一種自注冊的模式和適配器,對其它需要發現機制的系統能夠很容易地基于這個 API 來構建。
對這類 `Service` 并不會分配 Cluster IP,kube-proxy 不會處理它們,而且平臺也不會為它們進行負載均衡和路由。
DNS 如何實現自動配置,依賴于 `Service` 是否定義了 selector。
### 配置 Selector
對定義了 selector 的 Headless Service,Endpoint 控制器在 API 中創建了 `Endpoints` 記錄,并且修改 DNS 配置返回 A 記錄(地址),通過這個地址直接到達 `Service` 的后端 `Pod` 上。
### 不配置 Selector
對沒有定義 selector 的 Headless Service,Endpoint 控制器不會創建 `Endpoints` 記錄。
然而 DNS 系統會查找和配置,無論是:
* `ExternalName` 類型 Service 的 CNAME 記錄
* 記錄:與 Service 共享一個名稱的任何 `Endpoints`,以及所有其它類型
## 發布服務 —— 服務類型
對一些應用(如 Frontend)的某些部分,可能希望通過外部(Kubernetes 集群外部)IP 地址暴露 Service。
Kubernetes `ServiceTypes` 允許指定一個需要的類型的 Service,默認是 `ClusterIP` 類型。
`Type` 的取值以及行為如下:
* `ClusterIP`:通過集群的內部 IP 暴露服務,選擇該值,服務只能夠在集群內部可以訪問,這也是默認的 `ServiceType`。
* `NodePort`:通過每個 Node 上的 IP 和靜態端口(`NodePort`)暴露服務。`NodePort` 服務會路由到 `ClusterIP` 服務,這個 `ClusterIP` 服務會自動創建。通過請求 `<NodeIP>:<NodePort>`,可以從集群的外部訪問一個 `NodePort` 服務。
* `LoadBalancer`:使用云提供商的負載均衡器,可以向外部暴露服務。外部的負載均衡器可以路由到 `NodePort` 服務和 `ClusterIP` 服務。
* `ExternalName`:通過返回 `CNAME` 和它的值,可以將服務映射到 `externalName` 字段的內容(例如, `foo.bar.example.com`)。
沒有任何類型代理被創建,這只有 Kubernetes 1.7 或更高版本的 `kube-dns` 才支持。
### NodePort 類型
如果設置 `type` 的值為 `"NodePort"`,Kubernetes master 將從給定的配置范圍內(默認:30000-32767)分配端口,每個 Node 將從該端口(每個 Node 上的同一端口)代理到 `Service`。該端口將通過 `Service` 的 `spec.ports[*].nodePort` 字段被指定。
如果需要指定的端口號,可以配置 `nodePort` 的值,系統將分配這個端口,否則調用 API 將會失敗(比如,需要關心端口沖突的可能性)。
這可以讓開發人員自由地安裝他們自己的負載均衡器,并配置 Kubernetes 不能完全支持的環境參數,或者直接暴露一個或多個 Node 的 IP 地址。
需要注意的是,Service 將能夠通過 `<NodeIP>:spec.ports[*].nodePort` 和 `spec.clusterIp:spec.ports[*].port` 而對外可見。
### LoadBalancer 類型
使用支持外部負載均衡器的云提供商的服務,設置 `type` 的值為 `"LoadBalancer"`,將為 `Service` 提供負載均衡器。
負載均衡器是異步創建的,關于被提供的負載均衡器的信息將會通過 `Service` 的 `status.loadBalancer` 字段被發布出去。
```yaml
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
nodePort: 30061
clusterIP: 10.0.171.239
loadBalancerIP: 78.11.24.19
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 146.148.47.155
```
來自外部負載均衡器的流量將直接打到 backend `Pod` 上,不過實際它們是如何工作的,這要依賴于云提供商。
在這些情況下,將根據用戶設置的 `loadBalancerIP` 來創建負載均衡器。
某些云提供商允許設置 `loadBalancerIP`。如果沒有設置 `loadBalancerIP`,將會給負載均衡器指派一個臨時 IP。
如果設置了 `loadBalancerIP`,但云提供商并不支持這種特性,那么設置的 `loadBalancerIP` 值將會被忽略掉。
### AWS 內部負載均衡器
在混合云環境中,有時從虛擬私有云(VPC)環境中的服務路由流量是非常有必要的。
可以通過在 `Service` 中增加 `annotation` 來實現,如下所示:
```yaml
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
[...]
```
在水平分割的 DNS 環境中,需要兩個 `Service` 來將外部和內部的流量路由到 Endpoint 上。
### AWS SSL 支持
對運行在 AWS 上部分支持 SSL 的集群,從 1.3 版本開始,可以為 `LoadBalancer` 類型的 `Service` 增加兩個 annotation:
```
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012
```
第一個 annotation 指定了使用的證書。它可以是第三方發行商發行的證書,這個證書或者被上傳到 IAM,或者由 AWS 的證書管理器創建。
```yaml
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: (https|http|ssl|tcp)
```
第二個 annotation 指定了 `Pod` 使用的協議。
對于 HTTPS 和 SSL,ELB 將期望該 `Pod` 基于加密的連接來認證自身。
HTTP 和 HTTPS 將選擇7層代理:ELB 將中斷與用戶的連接,當轉發請求時,會解析 Header 信息并添加上用戶的 IP 地址(`Pod` 將只能在連接的另一端看到該 IP 地址)。
TCP 和 SSL 將選擇4層代理:ELB 將轉發流量,并不修改 Header 信息。
### 外部 IP
如果外部的 IP 路由到集群中一個或多個 Node 上,Kubernetes `Service` 會被暴露給這些 `externalIPs`。
通過外部 IP(作為目的 IP 地址)進入到集群,打到 `Service` 的端口上的流量,將會被路由到 `Service` 的 Endpoint 上。
`externalIPs` 不會被 Kubernetes 管理,它屬于集群管理員的職責范疇。
根據 `Service` 的規定,`externalIPs` 可以同任意的 `ServiceType` 來一起指定。
在下面的例子中,`my-service` 可以在 80.11.12.10:80(外部 IP:端口)上被客戶端訪問。
```yaml
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10
```
## 不足之處
為 VIP 使用 userspace 代理,將只適合小型到中型規模的集群,不能夠擴展到上千 `Service` 的大型集群。
查看 [最初設計方案](http://issue.k8s.io/1107) 獲取更多細節。
使用 userspace 代理,隱藏了訪問 `Service` 的數據包的源 IP 地址。
這使得一些類型的防火墻無法起作用。
iptables 代理不會隱藏 Kubernetes 集群內部的 IP 地址,但卻要求客戶端請求必須通過一個負載均衡器或 Node 端口。
`Type` 字段支持嵌套功能 —— 每一層需要添加到上一層里面。
不會嚴格要求所有云提供商(例如,GCE 就沒必要為了使一個 `LoadBalancer` 能工作而分配一個 `NodePort`,但是 AWS 需要 ),但當前 API 是強制要求的。
## 未來工作
未來我們能預見到,代理策略可能會變得比簡單的 round-robin 均衡策略有更多細微的差別,比如 master 選舉或分片。
我們也能想到,某些 `Service` 將具有 “真正” 的負載均衡器,這種情況下 VIP 將簡化數據包的傳輸。
我們打算為 L7(HTTP)`Service` 改進我們對它的支持。
我們打算為 `Service` 實現更加靈活的請求進入模式,這些 `Service` 包含當前 `ClusterIP`、`NodePort` 和 `LoadBalancer` 模式,或者更多。
## VIP 的那些駭人聽聞的細節
對很多想使用 `Service` 的人來說,前面的信息應該足夠了。
然而,有很多內部原理性的內容,還是值去理解的。
### 避免沖突
Kubernetes 最主要的哲學之一,是用戶不應該暴露那些能夠導致他們操作失敗、但又不是他們的過錯的場景。
這種場景下,讓我們來看一下網絡端口 —— 用戶不應該必須選擇一個端口號,而且該端口還有可能與其他用戶的沖突。
這就是說,在彼此隔離狀態下仍然會出現失敗。
為了使用戶能夠為他們的 `Service` 選擇一個端口號,我們必須確保不能有2個 `Service` 發生沖突。
我們可以通過為每個 `Service` 分配它們自己的 IP 地址來實現。
為了保證每個 `Service` 被分配到一個唯一的 IP,需要一個內部的分配器能夠原子地更新 etcd 中的一個全局分配映射表,這個更新操作要先于創建每一個 `Service`。
為了使 `Service` 能夠獲取到 IP,這個映射表對象必須在注冊中心存在,否則創建 `Service` 將會失敗,指示一個 IP 不能被分配。
一個后臺 Controller 的職責是創建映射表(從 Kubernetes 的舊版本遷移過來,舊版本中是通過在內存中加鎖的方式實現),并檢查由于管理員干預和清除任意 IP 造成的不合理分配,這些 IP 被分配了但當前沒有 `Service` 使用它們。
### IP 和 VIP
不像 `Pod` 的 IP 地址,它實際路由到一個固定的目的地,`Service` 的 IP 實際上不能通過單個主機來進行應答。
相反,我們使用 `iptables`(Linux 中的數據包處理邏輯)來定義一個虛擬IP地址(VIP),它可以根據需要透明地進行重定向。
當客戶端連接到 VIP 時,它們的流量會自動地傳輸到一個合適的 Endpoint。
環境變量和 DNS,實際上會根據 `Service` 的 VIP 和端口來進行填充。
#### Userspace
作為一個例子,考慮前面提到的圖片處理應用程序。
當創建 backend `Service` 時,Kubernetes master 會給它指派一個虛擬 IP 地址,比如 10.0.0.1。
假設 `Service` 的端口是 1234,該 `Service` 會被集群中所有的 `kube-proxy` 實例觀察到。
當代理看到一個新的 `Service`, 它會打開一個新的端口,建立一個從該 VIP 重定向到新端口的 iptables,并開始接收請求連接。
當一個客戶端連接到一個 VIP,iptables 規則開始起作用,它會重定向該數據包到 `Service代理` 的端口。
`Service代理` 選擇一個 backend,并將客戶端的流量代理到 backend 上。
這意味著 `Service` 的所有者能夠選擇任何他們想使用的端口,而不存在沖突的風險。
客戶端可以簡單地連接到一個 IP 和端口,而不需要知道實際訪問了哪些 `Pod`。
#### Iptables
再次考慮前面提到的圖片處理應用程序。
當創建 backend `Service` 時,Kubernetes master 會給它指派一個虛擬 IP 地址,比如 10.0.0.1。
假設 `Service` 的端口是 1234,該 `Service` 會被集群中所有的 `kube-proxy` 實例觀察到。
當代理看到一個新的 `Service`, 它會安裝一系列的 iptables 規則,從 VIP 重定向到 per-`Service` 規則。
該 per-`Service` 規則連接到 per-`Endpoint` 規則,該 per-`Endpoint` 規則會重定向(目標 NAT)到 backend。
當一個客戶端連接到一個 VIP,iptables 規則開始起作用。一個 backend 會被選擇(或者根據會話親和性,或者隨機),數據包被重定向到這個 backend。
不像 userspace 代理,數據包從來不拷貝到用戶空間,kube-proxy 不是必須為該 VIP 工作而運行,并且客戶端 IP 是不可更改的。
當流量打到 Node 的端口上,或通過負載均衡器,會執行相同的基本流程,但是在那些案例中客戶端 IP 是可以更改的。
## API 對象
在 Kubernetes REST API 中,Service 是 top-level 資源。
## 更多信息
- [使用 Service 連接 Frontend 到 Backend](https://kubernetes.io/docs/tutorials/connecting-apps/connecting-frontend-backend/)
- 序言
- 云原生
- 云原生(Cloud Native)的定義
- CNCF - 云原生計算基金會簡介
- CNCF章程
- 云原生的設計哲學
- Play with Kubernetes
- 快速部署一個云原生本地實驗環境
- Kubernetes與云原生應用概覽
- 云原生應用之路——從Kubernetes到Cloud Native
- 云原生編程語言
- 云原生編程語言Ballerina
- 云原生編程語言Pulumi
- 云原生的未來
- Kubernetes架構
- 設計理念
- Etcd解析
- 開放接口
- CRI - Container Runtime Interface(容器運行時接口)
- CNI - Container Network Interface(容器網絡接口)
- CSI - Container Storage Interface(容器存儲接口)
- Kubernetes中的網絡
- Kubernetes中的網絡解析——以flannel為例
- Kubernetes中的網絡解析——以calico為例
- 具備API感知的網絡和安全性管理開源軟件Cilium
- Cilium架構設計與概念解析
- 資源對象與基本概念解析
- Pod狀態與生命周期管理
- Pod概覽
- Pod解析
- Init容器
- Pause容器
- Pod安全策略
- Pod的生命周期
- Pod Hook
- Pod Preset
- Pod中斷與PDB(Pod中斷預算)
- 集群資源管理
- Node
- Namespace
- Label
- Annotation
- Taint和Toleration(污點和容忍)
- 垃圾收集
- 控制器
- Deployment
- StatefulSet
- DaemonSet
- ReplicationController和ReplicaSet
- Job
- CronJob
- Horizontal Pod Autoscaling
- 自定義指標HPA
- 準入控制器(Admission Controller)
- 服務發現
- Service
- Ingress
- Traefik Ingress Controller
- 身份與權限控制
- ServiceAccount
- RBAC——基于角色的訪問控制
- NetworkPolicy
- 存儲
- Secret
- ConfigMap
- ConfigMap的熱更新
- Volume
- Persistent Volume(持久化卷)
- Storage Class
- 本地持久化存儲
- 集群擴展
- 使用自定義資源擴展API
- 使用CRD擴展Kubernetes API
- Aggregated API Server
- APIService
- Service Catalog
- 資源調度
- QoS(服務質量等級)
- 用戶指南
- 資源對象配置
- 配置Pod的liveness和readiness探針
- 配置Pod的Service Account
- Secret配置
- 管理namespace中的資源配額
- 命令使用
- Docker用戶過度到kubectl命令行指南
- kubectl命令概覽
- kubectl命令技巧大全
- 使用etcdctl訪問kubernetes數據
- 集群安全性管理
- 管理集群中的TLS
- kubelet的認證授權
- TLS bootstrap
- 創建用戶認證授權的kubeconfig文件
- IP偽裝代理
- 使用kubeconfig或token進行用戶身份認證
- Kubernetes中的用戶與身份認證授權
- Kubernetes集群安全性配置最佳實踐
- 訪問Kubernetes集群
- 訪問集群
- 使用kubeconfig文件配置跨集群認證
- 通過端口轉發訪問集群中的應用程序
- 使用service訪問群集中的應用程序
- 從外部訪問Kubernetes中的Pod
- Cabin - Kubernetes手機客戶端
- Kubernetic - Kubernetes桌面客戶端
- Kubernator - 更底層的Kubernetes UI
- 在Kubernetes中開發部署應用
- 適用于kubernetes的應用開發部署流程
- 遷移傳統應用到Kubernetes中——以Hadoop YARN為例
- 最佳實踐概覽
- 在CentOS上部署Kubernetes集群
- 創建TLS證書和秘鑰
- 創建kubeconfig文件
- 創建高可用etcd集群
- 安裝kubectl命令行工具
- 部署master節點
- 安裝flannel網絡插件
- 部署node節點
- 安裝kubedns插件
- 安裝dashboard插件
- 安裝heapster插件
- 安裝EFK插件
- 生產級的Kubernetes簡化管理工具kubeadm
- 使用kubeadm在Ubuntu Server 16.04上快速構建測試集群
- 服務發現與負載均衡
- 安裝Traefik ingress
- 分布式負載測試
- 網絡和集群性能測試
- 邊緣節點配置
- 安裝Nginx ingress
- 安裝配置DNS
- 安裝配置Kube-dns
- 安裝配置CoreDNS
- 運維管理
- Master節點高可用
- 服務滾動升級
- 應用日志收集
- 配置最佳實踐
- 集群及應用監控
- 數據持久化問題
- 管理容器的計算資源
- 集群聯邦
- 存儲管理
- GlusterFS
- 使用GlusterFS做持久化存儲
- 使用Heketi作為Kubernetes的持久存儲GlusterFS的external provisioner
- 在OpenShift中使用GlusterFS做持久化存儲
- GlusterD-2.0
- Ceph
- 用Helm托管安裝Ceph集群并提供后端存儲
- 使用Ceph做持久化存儲
- 使用rbd-provisioner提供rbd持久化存儲
- OpenEBS
- 使用OpenEBS做持久化存儲
- Rook
- NFS
- 利用NFS動態提供Kubernetes后端存儲卷
- 集群與應用監控
- Heapster
- 使用Heapster獲取集群和對象的metric數據
- Prometheus
- 使用Prometheus監控kubernetes集群
- Prometheus查詢語言PromQL使用說明
- 使用Vistio監控Istio服務網格中的流量
- 分布式跟蹤
- OpenTracing
- 服務編排管理
- 使用Helm管理Kubernetes應用
- 構建私有Chart倉庫
- 持續集成與發布
- 使用Jenkins進行持續集成與發布
- 使用Drone進行持續集成與發布
- 更新與升級
- 手動升級Kubernetes集群
- 升級dashboard
- 領域應用概覽
- 微服務架構
- 微服務中的服務發現
- 使用Java構建微服務并發布到Kubernetes平臺
- Spring Boot快速開始指南
- Service Mesh 服務網格
- 企業級服務網格架構
- Service Mesh基礎
- Service Mesh技術對比
- 采納和演進
- 定制和集成
- 總結
- Istio
- 安裝并試用Istio service mesh
- 配置請求的路由規則
- 安裝和拓展Istio service mesh
- 集成虛擬機
- Istio中sidecar的注入規范及示例
- 如何參與Istio社區及注意事項
- Istio教程
- Istio免費學習資源匯總
- 深入理解Istio Service Mesh中的Envoy Sidecar注入與流量劫持
- 深入理解Istio Service Mesh中的Envoy Sidecar代理的路由轉發
- Linkerd
- Linkerd 使用指南
- Conduit
- Condiut概覽
- 安裝Conduit
- Envoy
- Envoy的架構與基本術語
- Envoy作為前端代理
- Envoy mesh教程
- SOFAMesh
- SOFAMesh中的Dubbo on x-protocol
- SOFAMosn
- 使用 SOFAMosn 構建 SOFAMesh
- 大數據
- Spark standalone on Kubernetes
- 運行支持Kubernetes原生調度的Spark程序
- Serverless架構
- 理解Serverless
- FaaS-函數即服務
- OpenFaaS快速入門指南
- 邊緣計算
- 人工智能