簡介
kubernetes 使用service和ingress共同構(gòu)建了,外部訪問k8s內(nèi)部容器的通道。
Service
概念
Service是定義一系列Pod以及訪問這些Pod的策略的一層抽象。
Pods是短暫的,那么重啟時(shí)IP地址可能會(huì)改變,為了能從前端容器正確可靠地指向后臺(tái)容器
Service通過Label找到Pod組。因?yàn)镾ervice是抽象的,所以在圖表里通常看不到它們的存在,這也就讓這一概念更難以理解。
現(xiàn)在,假定有2個(gè)后臺(tái)Pod,并且定義后臺(tái)Service的名稱為‘backend-service’,lable選擇器為(tier=backend, app=myapp)。backend-service 的Service會(huì)完成如下兩件重要的事情:
- 會(huì)為Service創(chuàng)建一個(gè)本地集群的DNS入口,因此前端Pod只需要DNS查找主機(jī)名為 ‘backend-service’,就能夠解析出前端應(yīng)用程序可用的IP地址。
- 現(xiàn)在前端已經(jīng)得到了后臺(tái)服務(wù)的IP地址,但是它應(yīng)該訪問2個(gè)后臺(tái)Pod的哪一個(gè)呢?Service在這2個(gè)后臺(tái)Pod之間提供透明的負(fù)載均衡,會(huì)將請求分發(fā)給其中的任意一個(gè)(如下面的動(dòng)畫所示)。通過每個(gè)Node上運(yùn)行的代理(kube-proxy)完成。
下述動(dòng)畫展示了Service的功能。注意該圖作了很多簡化。如果不進(jìn)入網(wǎng)絡(luò)配置,那么達(dá)到透明的負(fù)載均衡目標(biāo)所涉及的底層網(wǎng)絡(luò)和路由相對先進(jìn)。

在實(shí)際生產(chǎn)環(huán)境中,對Service的訪問可能會(huì)有兩種來源:Kubernetes集群內(nèi)部的程序(Pod)和Kubernetes集群外部,為了滿足上述的場景,Kubernetes service有以下三種類型:
- ClusterIP:提供一個(gè)集群內(nèi)部的虛擬IP以供Pod訪問。
- NodePort:在每個(gè)Node上打開一個(gè)端口以供外部訪問。
- LoadBalancer:通過外部的負(fù)載均衡器來訪問。
ClusterIP
此模式會(huì)提供一個(gè)集群內(nèi)部的虛擬IP(與Pod不在同一網(wǎng)段),以供集群內(nèi)部的pod之間通信使用。
ClusterIP也是Kubernetes service的默認(rèn)類型。

為了實(shí)現(xiàn)圖上的功能主要需要以下幾個(gè)組件的協(xié)同工作
- apiserver
用戶通過kubectl命令向apiserver發(fā)送創(chuàng)建service的命令,apiserver接收到請求以后將數(shù)據(jù)存儲(chǔ)到etcd中。 - kube-proxy
kubernetes的每個(gè)節(jié)點(diǎn)中都有一個(gè)叫做kube-proxy的進(jìn)程,這個(gè)進(jìn)程負(fù)責(zé)感知service,pod的變化,并將變化的信息寫入本地的
iptables中。 - iptables
使用NAT等技術(shù)將virtualIP的流量轉(zhuǎn)至endpoint中。
下面我們實(shí)際發(fā)布一個(gè)Service,能夠更清晰的了解到Service是如何工作的。
發(fā)布一個(gè)rc,并指定replices count為3.
yancey@ yancey-macbook kubernetes-1$kubectl create -f rc_nginx.yaml
yancey@ yancey-macbook kubernetes-1$kubectl get pods
NAME READY STATUS RESTARTS AGE
k8s-master-core-v2-01 4/4 Running 0 8m
k8s-proxy-core-v2-01 1/1 Running 0 8m
nginx-controller-6bovu 1/1 Running 0 4m
nginx-controller-iowux 1/1 Running 0 4m
nginx-controller-o7m6u 1/1 Running 0 4m
yancey@ yancey-macbook kubernetes-1$kubectl describe pod nginx-controller-6bovu
Name: nginx-controller-6bovu
Namespace: default
Node: core-v2-01/172.17.8.101
Start Time: Fri, 17 Jun 2016 15:22:20 +0800
Labels: app=nginx
Status: Running
IP: 10.1.13.3
Controllers: ReplicationController/nginx-controller
發(fā)布一個(gè)service,并指定步驟1中的label
yancey@ yancey-macbook kubernetes-1$kubectl create -f service_nginx.yaml
yancey@ yancey-macbook kubernetes-1$kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.0.0.1 <none> 443/TCP 9m
nginx-service 10.0.0.252 <none> 8000/TCP 4m
kubernetes為nginx-server這個(gè)service創(chuàng)建了一個(gè)10.0.0.252這個(gè)ClusterIP,也可以稱為VirtualIP.
yancey@ yancey-macbook kubernetes-1$kubectl get ep
NAME ENDPOINTS AGE
kubernetes 172.17.8.101:6443 9m
nginx-service 10.1.13.2:80,10.1.13.3:80,10.1.13.4:80 4m
查看endpoint信息,發(fā)現(xiàn)nginx-service一共有三個(gè)endpoint地址,分別對應(yīng)nginx的三個(gè)pod。
查看iptables,觀察其NAT表中的信息(只截取了部分和這個(gè)service有關(guān)的信息)
查看iptables中NAT表的命令: iptables -L -v -n -t nat
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
37 2766 KUBE-SERVICES all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service portals */
33 2112 DOCKER all -- * * 0.0.0.0/0 0.0.0.0/0 ADDRTYPE match dst-type LOCAL
在PREROUTING鏈中會(huì)先匹配到KUBE-SERVICES這個(gè)Chain。
Chain KUBE-SERVICES (2 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SVC-GKN7Y2BSGW4NJTYL tcp -- * * 0.0.0.0/0 10.0.0.252 /* default/nginx-service: cluster IP */ tcp dpt:8000
18 1080 KUBE-NODEPORTS all -- * * 0.0.0.0/0 0.0.0.0/0 /* kubernetes service nodeports; NOTE: this must be the last rule in this chain */ ADDRTYPE match dst-type LOCAL
所有destinationIP為10.0.0.252的包都轉(zhuǎn)到KUBE-SVC-GKN7Y2BSGW4NJTYL這個(gè)Chain
Chain KUBE-SVC-GKN7Y2BSGW4NJTYL (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-SEP-7ROBBXFV7SD4AIRW all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.33332999982
0 0 KUBE-SEP-XY3F6VJIZ7ELIF4Z all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ statistic mode random probability 0.50000000000
0 0 KUBE-SEP-JIDZHFC4A3T535AK all -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */
這里能看到請求會(huì)平均執(zhí)行KUBE-SEP-7ROBBXFV7SD4AIRW,KUBE-SEP-XY3F6VJIZ7ELIF4Z,KUBE-SEP-XY3F6VJIZ7ELIF4Z這三個(gè)Chain.最后我們看下KUBE-SEP-7ROBBXFV7SD4AIRW這個(gè)Chain做了什么
Chain KUBE-SEP-7ROBBXFV7SD4AIRW (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ all -- * * 10.1.13.2 0.0.0.0/0 /* default/nginx-service: */
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp to:10.1.13.2:80
這個(gè)Chain使用了DNAT規(guī)則將流量轉(zhuǎn)發(fā)到10.1.13.2:80這個(gè)地址。至此從Pod發(fā)出來的流量通過本地的iptables將流量轉(zhuǎn)至了service背后的pod上。
NodePort
Kubernetes將會(huì)在每個(gè)Node上打開一個(gè)端口并且每個(gè)Node的端口都是一樣的,通過<NodeIP>:NodePort的方式Kubernetes集群外部的程序可以訪問Service。
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
ports:
- port: 8000
targetPort: 80
protocol: TCP
# just like the selector in the replication controller,
# but this time it identifies the set of pods to load balance
# traffic to.
selector:
app: nginx
發(fā)布這個(gè)service,可以看到已經(jīng)隨機(jī)分配了一個(gè)NodePort端口。
yancey@ yancey-macbook kubernetes-1$kubectl get svc nginx-service -o yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: 2016-06-18T03:29:42Z
name: nginx-service
namespace: default
resourceVersion: "1405"
selfLink: /api/v1/namespaces/default/services/nginx-service
uid: e50ba23a-3504-11e6-a94f-080027a75e9e
spec:
clusterIP: 10.0.0.229
ports:
- nodePort: 30802
port: 8000
protocol: TCP
targetPort: 80
selector:
app: nginx
sessionAffinity: None
type: NodePort
status:
loadBalancer: {}
觀察Iptables中的變化,KUBE-NODEPORTS這個(gè)Chain中增加了如下內(nèi)容
Chain KUBE-NODEPORTS (1 references)
pkts bytes target prot opt in out source destination
0 0 KUBE-MARK-MASQ tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp dpt:30802
0 0 KUBE-SVC-GKN7Y2BSGW4NJTYL tcp -- * * 0.0.0.0/0 0.0.0.0/0 /* default/nginx-service: */ tcp dpt:30802
可以看到流量會(huì)轉(zhuǎn)至KUBE-SVC-GKN7Y2BSGW4NJTYL這個(gè)Chain中處理一次,也就是ClusterIP中提到的通過負(fù)載均衡將流量平均分配到3個(gè)endpoint上。
LoadBalancer
Ingress
概念
通常情況下,service和pod僅可在集群內(nèi)部網(wǎng)絡(luò)中通過IP地址訪問。所有到達(dá)邊界路由器的流量或被丟棄或被轉(zhuǎn)發(fā)到其他地方。從概念上講,可能像下面這樣:
internet
|
------------
[ Services ]
Ingress是授權(quán)入站連接到達(dá)集群服務(wù)的規(guī)則集合。
internet
|
[ Ingress ]
--|-----|--
[ Services ]
你可以給Ingress配置提供外部可訪問的URL、負(fù)載均衡、SSL、基于名稱的虛擬主機(jī)等。用戶通過POST Ingress資源到API server的方式來請求ingress。 Ingress controller負(fù)責(zé)實(shí)現(xiàn)Ingress,通常使用負(fù)載平衡器,它還可以配置邊界路由和其他前端,這有助于以HA方式處理流量。
你需要一個(gè)Ingress Controller來實(shí)現(xiàn)Ingress,單純的創(chuàng)建一個(gè)Ingress沒有任何意義。
GCE/GKE會(huì)在master節(jié)點(diǎn)上部署一個(gè)ingress controller。你可以在一個(gè)pod中部署任意個(gè)自定義的ingress controller。你必須正確地annotate每個(gè)ingress,比如 運(yùn)行多個(gè)ingress controller 和 關(guān)閉glbc
為了使Ingress正常工作,集群中必須運(yùn)行Ingress controller。 這與其他類型的控制器不同,其他類型的控制器通常作為kube-controller-manager二進(jìn)制文件的一部分運(yùn)行,在集群啟動(dòng)時(shí)自動(dòng)啟動(dòng)。 你需要選擇最適合自己集群的Ingress controller或者自己實(shí)現(xiàn)一個(gè)。
實(shí)踐
單Service Ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
spec:
backend:
serviceName: testsvc
servicePort: 80
使用kubectl create -f命令創(chuàng)建,然后查看ingress:
$ kubectl get ing
NAME RULE BACKEND ADDRESS
test-ingress - testsvc:80 107.178.254.228
107.178.254.228就是Ingress controller為了實(shí)現(xiàn)Ingress而分配的IP地址。RULE列表示所有發(fā)送給該IP的流量都被轉(zhuǎn)發(fā)到了BACKEND所列的Kubernetes service上。
簡單展開
如前面描述的那樣,kubernete pod中的IP只在集群網(wǎng)絡(luò)內(nèi)部可見,我們需要在邊界設(shè)置一個(gè)東西,讓它能夠接收ingress的流量并將它們轉(zhuǎn)發(fā)到正確的端點(diǎn)上。這個(gè)東西一般是高可用的loadbalancer。使用Ingress能夠允許你將loadbalancer的個(gè)數(shù)降低到最少,例如,嫁入你想要?jiǎng)?chuàng)建這樣的一個(gè)設(shè)置:
foo.bar.com -> 178.91.123.132 -> / foo s1:80
/ bar s2:80
你需要一個(gè)這樣的ingress:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
backend:
serviceName: s1
servicePort: 80
- path: /bar
backend:
serviceName: s2
servicePort: 80
使用kubectl create -f創(chuàng)建完ingress后:
$ kubectl get ing
NAME RULE BACKEND ADDRESS
test -
foo.bar.com
/foo s1:80
/bar s2:80
只要服務(wù)(s1,s2)存在,Ingress controller就會(huì)將提供一個(gè)滿足該Ingress的特定loadbalancer實(shí)現(xiàn)。 這一步完成后,您將在Ingress的最后一列看到loadbalancer的地址。
基于名稱的虛擬主機(jī)
Name-based的虛擬主機(jī)在同一個(gè)IP地址下?lián)碛卸鄠€(gè)主機(jī)名。
foo.bar.com --| |-> foo.bar.com s1:80
| 178.91.123.132 |
bar.foo.com --| |-> bar.foo.com s2:80
下面這個(gè)ingress說明基于 Host header的后端loadbalancer的路由請求:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: s1
servicePort: 80
- host: bar.foo.com
http:
paths:
- backend:
serviceName: s2
servicePort: 80
默認(rèn)backend:一個(gè)沒有rule的ingress,如前面章節(jié)中所示,所有流量都將發(fā)送到一個(gè)默認(rèn)backend。你可以用該技巧通知loadbalancer如何找到你網(wǎng)站的404頁面,通過制定一些列rule和一個(gè)默認(rèn)backend的方式。如果請求header中的host不能跟ingress中的host匹配,并且/或請求的URL不能與任何一個(gè)path匹配,則流量將路由到你的默認(rèn)backend。
TLS
你可以通過指定包含TLS私鑰和證書的 secret 來加密Ingress。 目前,Ingress僅支持單個(gè)TLS端口443,并假定TLS termination。 如果Ingress中的TLS配置部分指定了不同的主機(jī),則它們將根據(jù)通過SNI TLS擴(kuò)展指定的主機(jī)名(假如Ingress controller支持SNI)在多個(gè)相同端口上進(jìn)行復(fù)用。 TLS secret中必須包含名為tls.crt和tls.key的密鑰,這里面包含了用于TLS的證書和私鑰,例如:
apiVersion: v1
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
kind: Secret
metadata:
name: testsecret
namespace: default
type: Opaque
在Ingress中引用這個(gè)secret將通知Ingress controller使用TLS加密從將客戶端到loadbalancer的channel:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: no-rules-map
spec:
tls:
- secretName: testsecret
backend:
serviceName: s1
servicePort: 80
各種Ingress controller支持的TLS功能之間存在差距。 請參閱有關(guān)
nginx,gce 或任何其他平臺(tái)特定Ingress controller的文檔,以了解TLS在你的環(huán)境中的工作原理。
Ingress controller啟動(dòng)時(shí)附帶一些適用于所有Ingress的負(fù)載平衡策略設(shè)置,例如負(fù)載均衡算法,后端權(quán)重方案等。更高級(jí)的負(fù)載平衡概念(例如持久會(huì)話,動(dòng)態(tài)權(quán)重)尚未在Ingress中公開。 你仍然可以通過 service loadbalancer獲取這些功能。 隨著時(shí)間的推移,我們計(jì)劃將適用于跨平臺(tái)的負(fù)載平衡模式加入到Ingress資源中。
還值得注意的是,盡管健康檢查不直接通過Ingress公開,但Kubernetes中存在并行概念,例如準(zhǔn)備探查,可以使你達(dá)成相同的最終結(jié)果。 請查看特定控制器的文檔,以了解他們?nèi)绾翁幚斫】禉z查。