本文內容已經基於k8s v1.8.8進行了驗證測試。
k8s的Service定義了一個服務的訪問入口地址,前端的應用通過這個入口地址訪問其背后的一組由Pod副本組成的集群實例,來自外部的訪問請求被負載均衡到后端的各個容器應用上。Service與其后端Pod副本集群之間則是通過Label Selector來實現對接的。而RC的作用相當於是保證Service的服務能力和服務質量始終處於預期的標准。Service 定義可以基於 POST 方式,請求 apiserver 創建新的實例。一個 Service 在 Kubernetes 中是一個 REST 對象。本文對Service的使用進行詳細說明,包括Service的負載均衡、外網訪問、DNS服務的搭建、Ingress7層路由機制等。
1、Service定義詳解
1.1 yaml格式的Service定義文件的完整內容
apiVersion: v1
kind: Service
matadata:
name: string
namespace: string
labels:
- name: string
annotations:
- name: string
spec:
selector: []
type: string
clusterIP: string
sessionAffinity: string
ports:
- name: string
protocol: string
port: int
targetPort: int
nodePort: int
status:
loadBalancer:
ingress:
ip: string
hostname: string
1.2 對Service定義文件中各屬性的說明表
屬性名稱
|
取值類型
|
是否必選
|
取值說明
|
version
|
string
|
Required
|
v1
|
kind
|
string
|
Required
|
Service
|
metadata
|
object
|
Required
|
元數據
|
metadata.name
|
string
|
Required
|
Service名稱
|
metadata.namespace
|
string
|
Required
|
命名空間,默認為default
|
metadata.labels[]
|
list
|
|
自定義標簽屬性列表
|
metadata.annotation[]
|
list
|
|
自定義注解屬性列表
|
spec
|
object
|
Required
|
詳細描述
|
spec.selector[]
|
list
|
Required
|
Label Selector配置,將選擇具有指定Label標簽的Pod作為管理范圍
|
spec.type
|
string
|
Required
|
Service的類型,指定Service的訪問方式,默認值為ClusterIP。取值范圍如下:
ClusterIP: 虛擬服務的IP,用於k8s集群內部的pod訪問,在Node上kube-proxy通過設置的Iptables規則進行轉發。
NodePort:使用宿主機的端口,使用能夠訪問各Node的外部客戶端通過Node的IP地址和端口就能訪問服務。
LoadBalancer: 使用外接負載均衡器完成到服務的負載分發,需要在spec.status.loadBalancer字段指定外部負載均衡器的IP地址
,並同時定義nodePort和clusterIP,用於公有雲環境。
|
spec.clusterIP
|
string
|
|
虛擬服務的IP地址,當type=clusterIP時,如果不指定,則系統進行自動分配。也可以手工指定。當type=LoadBalancer時,則需要指定。
|
spec.sessionAffinity
|
string
|
|
是否支持Session,可選值為ClientIP,表示將同一個源IP地址的客戶端訪問請求都轉發到同一個后端Pod。默認值為空。
|
spec.ports[]
|
list
|
|
Service需要暴露的端口列表
|
spec.ports[].name
|
string
|
|
端口名稱
|
spec.ports[].protocol
|
string
|
|
端口協議,支持TCP和UDP,默認值為TCP
|
spec.ports[].port
|
int
|
|
服務監聽的端口號
|
spec.ports[].targetPort
|
int
|
|
需要轉發到后端Pod的端口號
|
spec.ports[].nodePort
|
int
|
|
當spec.type=NodePort時,指定映射到物理機的端口號
|
status
|
object
|
|
當spec.type=LoadBalancer時,設置外部負載均衡器的地址,用於公有雲環境
|
status.loadBalancer
|
object
|
|
外部負載均衡器
|
status.loadBalancer.ingress
|
object
|
|
外部負載均衡器
|
status.loadBalancer.ingress.ip
|
string
|
|
外部負載均衡器的IP地址
|
status.loadBalancer.ingress.hostname
|
string
|
|
外部負載均衡器的主機名
|
2、Service,RC,Pod架構層次關系

3、VIP 和 Service 代理
運行在每個Node上的kube-proxy進程其實就是一個智能的軟件負載均衡器,它會負責把對Service的請求轉發到后端的某個Pod實例上並在內部實現服務的負載均衡與會話保持機制。Service不是共用一個負載均衡器的IP,而是被分配了一個全局唯一的虛擬IP地址,稱為Cluster IP。在Service的整個生命周期內,它的Cluster IP不會改變。 kube-proxy 負責為 Service 實現了一種 VIP(虛擬 IP)的形式,而不是 ExternalName 的形式。在k8s v1.2版本之前默認使用userspace提供vip代理服務,從 Kubernetes v1.2 起,默認是使用 iptables 代理。
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。

4、 發布服務 —— type類型
對一些應用希望通過外部(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 才支持。
k8s中有3種IP地址:
-
Node IP: Node節點的IP地址,這是集群中每個節點的物理網卡的IP地址;
-
Pod IP: Pod的IP地址,這是Docker Engine根據docker0網橋的IP地址段進行分配的,通常是一個虛擬的二層網絡;
-
Cluster IP:Service 的IP地址,這也是一個虛擬的IP,但它更像是一個“偽造”的IP地址,因為它沒有一個實體網絡對象,所以無法響應ping命令。它只能結合Service Port組成一個具體的通信服務端口,單獨的Cluster IP不具備TCP/IP通信的基礎。在k8s集群之內,Node IP網、Pod IP網與Cluster IP網之間的通信采用的是k8s自己設計的一種編程實現的特殊的路由規則,不同於常見的IP路由實現。
5、 服務發現
Kubernetes 支持2種基本的服務發現模式 —— 環境變量和 DNS。
環境變量
當 Pod 運行在 Node 上,kubelet 會為每個活躍的 Service 添加一組環境變量。 它同時支持 Docker links兼容 變量、簡單的 {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 變量,這里 Service 的名稱需大寫,橫線被轉換成下划線。
舉個例子,一個名稱為 "redis-master" 的 Service 暴露了 TCP 端口 6379,同時給它分配了 Cluster IP 地址 10.0.0.11,這個 Service 生成了如下環境變量:
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
一個強烈推薦的集群插件 是 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。
Kubernetes 從 1.3 版本起, DNS 是內置的服務,通過插件管理器
集群插件 自動被啟動。Kubernetes DNS 在集群中調度 DNS Pod 和 Service ,配置 kubelet 以通知個別容器使用 DNS Service 的 IP 解析 DNS 名字。
6、Service的基本用法
一般來說,對外提供服務的應用程序需要通過某種機制來實現,對於容器應用最簡便的方式就是通過TCP/IP機制及監聽IP和端口號來實現。
創建一個基本功能的Service
(1)例如,我們定義一個提供web服務的RC,由兩個tomcat容器副本組成,每個容器通過containerPort設置提供服務號為8080:
webapp-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: webapp
spec:
replicas: 2
template:
metadata:
name: webapp
labels:
app: webapp
spec:
containers:
- name: webapp
image: tomcat
ports:
- containerPort: 8080
創建該RC:
#kubectl create -f webapp-rc.yaml
獲取Pod的IP地址:
#kubectl get pods -l app=webapp -o yaml|grep podIP
podIP:172.17.0.2
podIP:172.17.0.3
直接通過這兩個Pod的IP地址和端口號訪問Tomcat服務:
#curl 172.17.0.2:8080
直接通過Pod的IP地址和端口號可以訪問容器內的應用服務,但是Pod的IP地址是不可靠的,例如Pod所在的Node發生故障,Pod將被k8s重新調度到另一台Node。Pod的IP地址將發生變化,更重要的是,如果容器應用本身是分布式的部署方式,通過多個實例共同提供服務,就需要在這些實例的前端設置一個負載均衡器來實現請求的分發。kubernetes中的Service就是設計出來用於解決這些問題的核心組件。
(2)為了讓客戶端應用能夠訪問到兩個Tomcat Pod 實例,需要創建一個Service來提供服務
k8s提供了一種快速的方法,即通過kubectl expose命令來創建:
#kubectl expose rc webapp
查看新創建的Service可以看到系統為它分配了一個虛擬的IP地址(clusterIP),而Service所需的端口號則從Pod中的containerPort復制而來:
[root@bogon ~]# kubectl expose rc webapp
service "webapp" exposed
[root@bogon ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.10.10.1 <none> 443/TCP 19d
mysql ClusterIP 10.10.10.200 <none> 3306/TCP 19d
webapp ClusterIP 10.10.10.58 <none> 8080/TCP 9s
接下來,我們就可以通過Service的IP地址和Service的端口號訪問該Service了:
#curl 10.10.10.58:8080
這里,對Service地址10.10.10.58:8080的訪問被自動負載分發到了后端兩個Pod之一。
(3)除了使用kubectl expose命令創建Service,我們也可以通過配置文件定義Service,再通過kubectl create命令進行創建。
例如前面的webapp就用,我們可以設置一個Service:
webapp-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- port: 8081
targetPort: 8080
selector:
app: webapp
Service定義中的關鍵字段是ports和selector。
本例中ports定義部分指定了Service所需的虛擬端口號為8081,由於與Pod容器端口號8080不一樣,所以需要在通過targetPort來指定后端Pod的端口。
selector定義部分設置的是后端Pod所擁有的label: app=webapp
(4)目前kubernetes提供了兩種負載分發策略:RoundRobin和SessionAffinity
-
RoundRobin:輪詢模式,即輪詢將請求轉發到后端的各個Pod上
-
SessionAffinity:基於客戶端IP地址進行會話保持的模式,第一次客戶端訪問后端某個Pod,之后的請求都轉發到這個Pod上
默認是RoundRobin模式。
7、多端口Service
有時候,一個容器應用提供多個端口服務,可以按下面這樣定義:
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
ports:
- name: web
port: 8080
targetPort: 8080
- name: management
port: 8005
targetPort: 8005
selector:
app: webapp
另一個例子是兩個端口使用了不同的4層協議,即TCP和UDP
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "KubeDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.10.10.100
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
8、Headless Service
有時不需要或不想要負載均衡,以及單獨的 Service IP。 遇到這種情況,可以通過指定 Cluster IP(spec.clusterIP)的值為 "None" 來創建 Headless Service。
這個選項允許開發人員自由尋找他們自己的方式,從而降低與 Kubernetes 系統的耦合性。 應用仍然可以使用一種自注冊的模式和適配器,對其它需要發現機制的系統能夠很容易地基於這個 API 來構建。
對這類 Service 並不會分配 Cluster IP,kube-proxy 不會處理它們,而且平台也不會為它們進行負載均衡和路由。僅依賴於Label Selector將后端的Pod列表返回給調用的客戶端。
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
clusterIP: None
selector:
app: nginx
這樣,Service就不再具有一個特定的ClusterIP地址,對其進行訪問將獲得包含Label"app=nginx"的全部Pod列表,然后客戶端程序自行決定如何處理這個Pod列表。
例如, StatefulSet就是使用Headless Service為客戶端返回多個服務地址。
Lable Secector:
-
配置 Selector:對定義了 selector 的 Headless Service,Endpoint 控制器在 API 中創建了 Endpoints 記錄,並且修改 DNS 配置返回 A 記錄(地址),通過這個地址直接到達 Service 的后端 Pod上。
-
不配置 Selector:對沒有定義 selector 的 Headless Service,Endpoint 控制器不會創建 Endpoints 記錄。
9、外部服務Service——沒有 selector 的 Service
在某些環境中,應用系統需要將一個外部數據庫用為后端服務進行連接,或將另一個集群或Namespace中的服務作為服務的后端,這時可以通過創建一個無Label Selector的Service實現:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 80
Servcie 抽象了該如何訪問 Kubernetes Pod,但也能夠抽象其它類型的 backend,例如:
-
希望在生產環境中使用外部的數據庫集群。
-
希望服務指向另一個 Namespace 中或其它集群中的服務。
-
正在將工作負載同時轉移到 Kubernetes 集群和運行在 Kubernetes 集群之外的 backend。
由於這個 Service 沒有 selector,就不會創建相關的 Endpoints 對象。可以手動將 Service 映射到指定的 Endpoints:
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。 相反地,對於運行在集群外部的服務,它通過返回該外部服務的別名這種方式來提供服務。
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。
10、集群外部訪問Pod或Service的方法
由於Pod和Service是k8s集群范圍內的虛擬概念,所以集群外的客戶端系統無法通過Pod的IP地址或者Service的虛擬IP地址和虛擬端口號訪問到它們。
為了讓外部客戶端可以訪問這些服務,可以將Pod或Service的端口號映射到宿主機,以使得客戶端應用能夠通過物理機訪問容器應用。
10.1 將容器應用的端口號映射到物理機
(1)通過設置容器級別的hostPort,將容器應用的端口號映射到物理機上
文件pod-hostport.yaml
apiVersion: v1
kind: Pod
metadata:
name: webapp
labels:
app: webapp
spec:
containers:
- name: webapp
image: tomcat
ports:
- containerPort: 8080
hostPort: 8081
通過kubectl create創建這個Pod:
#kubectl create -f pod-hostport.yaml
通過物理機的IP地址和8081端口號訪問Pod內的容器服務:
#curl 10.0.2.6:8081
(2)通過設置Pod級別的hostNetwork=true,該Pod中所有容器的端口號都將被直接映射到物理機上
設置hostWork=true是需要注意,在容器的ports定義部分如果不指定hostPort,則默認hostPort等於containerPort,如果指定了hostPort,則hostPort必須等於containerPort的值。
文件pod-hostnetwork.yaml
apiVersion: v1
kind: Pod
metadata:
name: webapp-hostnetwork
labels:
app: webapp-hostnetwork
spec:
hostNetwork: true
containers:
- name: webapp-hostnetwork
image: tomcat
imagePullPolicy: Never
ports:
- containerPort: 8080
創建這個Pod:
#kubectl create -f pod-hostnetwork.yaml
[root@bogon ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
dapi-test-pod-volume 1/1 Running 4 12d 172.17.0.7 10.0.2.6
pod-affinity 1/1 Running 3 11d 172.17.0.4 10.0.2.6
webapp 1/1 Running 0 6m 172.17.0.2 10.0.2.6
webapp-hostnetwork 1/1 Running 0 37s 10.0.2.10 10.0.2.10
通過物理機的IP地址和8080端口訪問Pod的容器服務
#curl 10.0.2.10:8080
10.2 將Service的端口號映射到物理機
(1)通過設置nodePort映射到物理機,同時設置Service的類型為NodePort
文件webapp-svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp-nodeport
spec:
type: NodePort
ports:
- port: 8090
targetPort: 8080
nodePort: 8090
selector:
app: webapp
創建這個Service:
[root@bogon ~]# kubectl create -f webapp-svc-nodeport.yaml
service "webapp-nodeport" created
[root@bogon ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.10.10.1 <none> 443/TCP 22d
mysql ClusterIP 10.10.10.200 <none> 3306/TCP 22d
webapp-nodeport NodePort 10.10.10.191 <none> 8090:8090/TCP 11s
[root@bogon ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
dapi-test-pod-volume 1/1 Running 4 12d 172.17.0.7 10.0.2.6
pod-affinity 1/1 Running 3 11d 172.17.0.4 10.0.2.6
webapp 1/1 Running 0 15m 172.17.0.2 10.0.2.6
webapp-hostnetwork 1/1 Running 0 8m 10.0.2.10 10.0.2.10
通過物理機的IP和端口訪問:
[root@bogon ~]# curl 10.0.2.6:8090
如果訪問不通,查看下物理機的防火牆設置
同樣,對該Service的訪問也將被負載分發到后端多個Pod上
(2)通過設置LoadBalancer映射到雲服務商提供的LoadBalancer地址
這種用法僅用於在公有雲服務提供商的雲平台上設置Service的場景。
status.loadBalancer.ingress.ip設置的146.148.47.155為雲服務商提供的負載均衡器的IP地址。對該Service的訪問請求將會通過LoadBalancer轉發到后端的Pod上,負載分發的實現方式依賴雲服務商提供的LoadBalancer的實現機制。
apiVersion: v1
kind: Service
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:
ingree:
- ip: 146.148.47.155
11、DNS服務搭建指南
作為服務發現機制的基本功能,在集群內需要能夠通過服務名對服務進行訪問,這就需要一個集群范圍的DNS服務來完成服務名到ClusterIP的解析。
kubernetes提供的虛擬DNS服務名為skydns,由四個組件組成。
1)etcd:DNS存儲
2)kube2sky:將kubernetes Master中的Service(服務)注冊到etcd
3)skyDNS:提供NDS域名解析服務
4)healthz:提供對skydns服務的健康檢查功能
k8s DNS服務的總體架構:

11.1 skydns配置文件說明
skydns服務由一個RC和一個Service的定義組成,分別由配置文件skydns-rc.yaml和skydns-svc.yaml定義。
skydns的RC配置文件skydns-rc.yaml的內容如下,包含4個容器的定義:
apiVersion: v1
kind: ReplicationController
metadata:
name: kube-dns-v11
namespace: kube-system
labels:
k8s-app: kube-dns
version: v11
kubernetes.io/cluster-service: "true"
spec:
replicas: 1
selector:
k8s-app: kube-dns
version: v11
template:
metadata:
labels:
k8s-app: kube-dns
version: v11
kubernetes.io/cluster-service: "true"
spec:
containers:
- name: etcd
image: gcr.io/google_containers/etcd-amd64:2.2.1
resources:
limits:
cpu: 100m
memory: 50Mi
requests:
cpu: 100m
memory: 50Mi
command:
- /usr/local/bin/etcd
- -data-dir
- /tmp/data
- -listen-client-urls
- http://127.0.0.1:2379,http://127.0.0.1:4001
- -advertise-client-urls
- http://127.0.0.1:2379,http://127.0.0.1:4001
- -initial-cluster-token
- skydns-etcd
volumeMounts:
- name: etcd-storage
mountPath: /tmp/data
- name: kube2sky
image: gcr.io/google_containers/kube2sky-amd64:1.15
resources:
limits:
cpu: 100m
memory: 50Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /readiness
port: 8081
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
args:
# command = "/kube2sky"
- --kube-master-url=http://10.0.2.5:8080
- --domain=cluster.local
- name: skydns
image: gcr.io/google_containers/skydns:2015-10-13-8c72f8c
resources:
limits:
cpu: 100m
memory: 50Mi
requests:
cpu: 100m
memory: 50Mi
args:
# command = "/skydns"
- -machines=http://127.0.0.1:4001
- -addr=0.0.0.0:53
- -ns-rotate=false
- -domain=cluster.local
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
- name: healthz
image: gcr.io/google_containers/exechealthz:1.0
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
args:
- -cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1 > /dev/null
- -port=8080
ports:
- containerPort: 8080
protocol: TCP
volumes:
- name: etcd-storage
emptyDir: {}
dnsPolicy: Default #Don't use cluster DNS.
需要修改的幾個配置參數如下:
-
kube2sky容器需要訪問K8s Master,需要配置Master所在物理主機的IP地址和端口號,本例中設置參數--kube_master_url的值為http://10.0.2.5:8080 。
-
kube2sky容器和skydns容器的啟動參數--domain,設置k8s集群中Service所屬的域名,本例中為“cluster.local”。啟動后,kube2sky會通過API Server監控集群中全部Service的定義,生成相應的記錄並保存到etcd中。kube2sky為每個Service生成以下兩條記錄:
-
<service_name>.<namespace_name>.<domain>
-
<service_name>.<namespace_name>.svc.<domain>
-
-
skydns的啟動參數-addr=0.0.0.0:53表示使用本機TCP和UDP的53端口提供服務。
skydns的Service配置文件skydns-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: kube-dns
namespace: kube-system
labels:
k8s-app: kube-dns
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "kubeDNS"
spec:
selector:
k8s-app: kube-dns
clusterIP: 10.10.10.2
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP
注意:skydns服務使用的clusterIP需要指定一個固定IP,每個Node的kubelet進程都將使用這個IP地址,不能通過kubernetes自動分配。
另外,這個IP地址需要在kube-apiserver啟動參數--service-cluster-ip-range指定IP地址范圍。
在創建skydns容器之前,先修改每個Node上kubelet啟動參數。
故障排查:以上RC中包含4個容器,需要全部能創建成功。如果遇到報錯,可以查看相應pod的詳情,再使用下面方法查看容器日志,定位錯誤原因。
kubectl logs -f kube-dns-v11-cndzm --namespace=kube-system -c skydns
11.2 關於獲取gcr.io/google_containers網站上的容器鏡像的方法
本方法是參照以下文章提供的方法所實現的:
https://mritd.me/2016/10/29/set-up-kubernetes-cluster-by-kubeadm/
這個從谷歌網站上獲取需要的容器鏡像的處理思路是同時利用了github和dockerhub提供的服務。前者提供了項目托管,后者提供了根據Dockerfile自動構建容器鏡像的服務,而且是支持基於github項目的容器鏡像集成構建服務。
(1)我們就可以先在自己的github項目下為需要使用的docker容器鏡像定義出Dockerfile文件,文件的內容簡單到只需要兩行內容。
例如skydns容器鏡像的Dockerfile文件內容如下所示:
FROM gcr.io/google_containers/skydns:2015-10-13-8c72f8c
MAINTAINER watermelonbig <watermelonbig@163.com>
(2)登錄
https://hub.docker.com/
按照上面提供的參考文章的指導,為你自己構建出一份需要使用的docker容器鏡像。
或者使用我已經構建好的:
(3)在k8s node本地使用下面的shell腳本完成下載容器鏡像、重新打tag以及清除沒用的鏡像
images=(etcd-amd64:2.2.1 kube2sky-amd64:1.15 skydns:2015-10-13-8c72f8c exechealthz:1.0 )
for imageName in ${images[@]} ;
do
docker pull watermelonbig/$imageName
docker tag watermelonbig/$imageName gcr.io/google_containers/$imageName
docker rmi watermelonbig/$imageName
done
查看下載到node本地的容器鏡像:
[root@bogon ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
gcr.io/google_containers/exechealthz 1.0 41ff19891ee8 About an hour ago 7.12MB
gcr.io/google_containers/kube2sky-amd64 1.15 0ea1b8538ec1 About an hour ago 29.2MB
gcr.io/google_containers/skydns 2015-10-13-8c72f8c 5f97362942c4 About an hour ago 40.6MB
gcr.io/google_containers/etcd-amd64 2.2.1 e26c5ece7b6b About an hour ago 28.2MB
11.3 修改每台Node上的kubelet啟動參數
添加上以下兩個參數:
-
--cluster_dns=10.10.10.2 為DNS服務的ClusterIP地址
-
--cluster_dns=cluster.local 為DNS服務中設置的域名
然后重啟kubelet服務。
11.4 創建skydns RC和Service
通過kubectl create完成skydns的RC和Service的創建:
#kubectl create -f skydns-rc.yaml
#kubectl create -f skydns-svc.yaml
查看RC、Pod和Service,確保容器成功啟動:
[root@localhost ~]# kubectl get rc --namespace=kube-system
NAME DESIRED CURRENT READY AGE
kube-dns-v11 1 1 1 7m
[root@localhost ~]# kubectl get svc --namespace=kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.10.10.2 <none> 53/UDP,53/TCP 1m
[root@localhost ~]# kubectl get pods --namespace=kube-system
NAME READY STATUS RESTARTS AGE
fluentd-cloud-logging-8rkvk 1/1 Running 5 11d
fluentd-cloud-logging-rr45f 1/1 Running 4 11d
kube-dns-v11-cndzm 4/4 Running 0 7m
至此,k8s集群內的虛擬DNS服務搭建完成,在需要訪問k8s Service的應用中,僅需要配置上k8s Service的名字和服務的端口號,就能可以訪問,如redis-master:6379 。
通過服務名進行配置,能夠極大地簡化客戶端應用對后端服務變化的感知,包括服務虛擬IP地址的變化、服務后端Pod的變化等,對應用程序的微服務架構實現提供了強有力的支撐。
11.5 通過DNS查找Service
使用一個帶有nslookup工具的Pod來驗證DNS服務。
文件busybox.yaml
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- name: busybox
image: busybox
command:
- sleep
- "3600"
運行kubectl create -f busybox.yaml完成創建。
測試nslookup
查看下當前有哪些k8s Services:
[root@localhost ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.10.10.1 <none> 443/TCP 23d
mysql ClusterIP 10.10.10.200 <none> 3306/TCP 23d
webapp-nodeport NodePort 10.10.10.191 <none> 8090:8090/TCP 1d
[root@localhost ~]#
通過服務名字測試dns解析是否正確:
[root@localhost ~]# kubectl exec busybox -- nslookup mysql
Server: 10.10.10.2
Address 1: 10.10.10.2
Name: mysql
Address 1: 10.10.10.200 bogon
[root@localhost ~]# kubectl exec busybox -- nslookup webapp-nodeport
Server: 10.10.10.2
Address 1: 10.10.10.2
Name: webapp-nodeport
Address 1: 10.10.10.191 bogon
如果某個Service屬於不同的命名空間,需要帶上namespace名字:
[root@localhost ~]# kubectl get svc --namespace kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.10.10.2 <none> 53/UDP,53/TCP 16m
不指定namespace時,顯示kube-dns的域名解析失敗:
[root@localhost ~]# kubectl exec busybox -- nslookup kube-dns
nslookup: can't resolve 'kube-dns'
Server: 10.10.10.2
Address 1: 10.10.10.2
command terminated with exit code 1
指定了namespace后,可以正確解析出來了:
[root@localhost ~]# kubectl exec busybox -- nslookup kube-dns.kube-system
Server: 10.10.10.2
Address 1: 10.10.10.2
Name: kube-dns.kube-system
Address 1: 10.10.10.2
11.6 DNS服務的工作原理解析
我們看看DNS服務背后的工作原理。
(1)kube2sky容器應用通過調用kubernetes Master的API獲得集群中所有Service的信息,並持續監控新Service的生成,然后寫入etcd中。
查看etcd中存儲的Service信息:
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster
/skydns/local/cluster/svc
/skydns/local/cluster/pod
可以看到在skydns鍵下面,根據我們配置的域名(cluster.local)生成了local/cluster子鍵,接下來是pod(default和kube-system)和svc(下面也按namespace生成子鍵)。
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster
/skydns/local/cluster/svc
/skydns/local/cluster/pod
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/svc
/skydns/local/cluster/svc/default
/skydns/local/cluster/svc/kube-system
[root@localhost ~]#
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/pod
/skydns/local/cluster/pod/default
/skydns/local/cluster/pod/kube-system
[root@localhost ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
busybox 1/1 Running 0 26m 172.17.0.7 10.0.2.6
dapi-test-pod-volume 1/1 Running 6 13d 172.17.0.4 10.0.2.6
pod-affinity 1/1 Running 5 12d 172.17.0.3 10.0.2.6
webapp 1/1 Running 2 1d 172.17.0.2 10.0.2.6
webapp-hostnetwork 1/1 Running 2 1d 10.0.2.10 10.0.2.10
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/pod/default
/skydns/local/cluster/pod/default/172-17-0-8
/skydns/local/cluster/pod/default/172-17-0-7
/skydns/local/cluster/pod/default/172-17-0-4
/skydns/local/cluster/pod/default/172-17-0-3
/skydns/local/cluster/pod/default/172-17-0-2
/skydns/local/cluster/pod/default/10-0-2-10
[root@localhost ~]#
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/pod/kube-system
/skydns/local/cluster/pod/kube-system/172-17-0-6
/skydns/local/cluster/pod/kube-system/172-17-0-2
/skydns/local/cluster/pod/kube-system/172-17-0-5
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/svc/default
/skydns/local/cluster/svc/default/kubernetes
/skydns/local/cluster/svc/default/webapp-nodeport
/skydns/local/cluster/svc/default/mysql
從etcd數據庫中查看"mysql"服務的域名解析數據:
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl ls /skydns/local/cluster/svc/default/mysql
/skydns/local/cluster/svc/default/mysql/7e5750b4
[root@localhost ~]# kubectl exec kube-dns-v11-cndzm -c etcd --namespace=kube-system etcdctl get /skydns/local/cluster/svc/default/mysql/7e5750b4
{"host":"10.10.10.200","priority":10,"weight":10,"ttl":30,"targetstrip":0}
可以看到,mysql服務對應的完整域名為mysql.default.svc.cluster.local,並且其IP地址為10.10.10.200 。
(2)根據kubelet啟動參數的設置(--cluster_dns),kubelet會在每個新創建的Pod中設置DNS域名解析配置文件/etc/resolv.conf文件,在其中增加了一條nameserver配置和一條search配置
[root@localhost ~]# kubectl exec busybox -c busybox -- cat /etc/resolv.conf
nameserver 10.10.10.2
search default.svc.cluster.local svc.cluster.local cluster.local localdomain
options ndots:5
通過名字服務器10.10.10.2訪問的實際就是skydns在53端口上提供的DNS解析服務。
(3)最后應用程序就能夠像訪問網站域名一樣,僅僅通過服務的名字就能夠訪問到服務了。
11.7 DNS服務的演進
在后續的版本中,skydns將被更新為kubedns(也不再使用etcd數據庫),同時增加DNS專用的HPA控制器,以自動擴展DNS容器的副本數量。
DNS服務是k8s集群中服務發現的最核心組件,建議將其作為標准配置,在安裝集群時部署。
12、自定義DNS和上游DNS服務器
在實際環境中,很多用戶都有自己的私有域名區域,並且希望能夠集成到k8s DNS的命名空間中,例如混合雲用戶可能希望能在集群內解析其內部的".corp"域名;用戶也可能已存在一個未被k8s管理的服務發現系統(例如Consul)來完成域名解析。從k8s v1.6版本起,用戶可以在k8s集群內配置私有DNS區域(通常稱為存根域Stub Domain)和外部的上游域名服務了。下面講解如何使用這一功能。
12.1 k8s默認的域名解析流程
Kubernetes 目前在Pod定義中支持兩種 DNS 策略: Default和ClusteFirst。 如果 dnsPolicy 的 Flag 沒有特別指明,則默認使用ClusterFirst。 如果 dnsPolicy 設置為 Default ,則名稱解析配置將完全從 pod所在的節點(/etc/resolv.conf)繼承而來。
k8s默認的域名解析流程如下圖所示:

如果 dnsPolicy 設置為ClusterFirst ,則 DNS 查詢將被發送到 kube-dns (skydns)服務。 kube-dns服務負責以集群域名為后綴(例如.cluster.local)進行服務名的解析。其他域名查詢(例如 www.kubernetes.io )將被轉發給節點上定義的上游域名服務器 。
在此之前,通常使用自定義解析器替換上游 DNS 來引入存根域。 然而,這會導致自定義解析器本身成為 DNS 解析的關鍵路徑,其中可擴展性和可用性的問題成為一個高風險因素。 現在,本功能讓用戶無須對整個DNS路徑進行改造就完成自定義DNS解析的過程。
12.2 自定義DNS的方式
從 Kubernetes 1.6 開始,集群管理員可以使用 ConfigMap 來指定自定義存根域和上游DNS Server 。
例如,下面的配置包含一個存根域和兩個上游域名服務器 。對域名后綴為.acme.local的查詢請求將被發送到地址為 1.2.3.4 上的 DNS 服務。同時,設置8.8.8.8和8.8.4.4為上游DNS服務器地址。
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-dns
namespace: kube-system
data:
stubDomains: |
{"acme.local": ["1.2.3.4"]}
upstreamNameservers: |
["8.8.8.8", "8.8.4.4"]
主要參數說明:
-
stubDomains (可選):JSON格式的存根域定義,以 DNS 后綴(如acme.local)為key, 值是一個JSON數組,表示一組DNS服務的IP地址。 注意,目標域名服務器也可以是 Kubernetes 服務名。 例如,可以運行自己的 dnsmasq 副本,將自定義 DNS 導出到 ClusterDNS的命名空間中。
-
upstreamNameservers :一個 DNS IP組成的 JSON 數組。 注意:如果指定了這個值,那么從節點的域名服務設置( /etc/resolv.conf )繼承過來的值就會被覆蓋。本字段限制最多可指定3個IP地址。
下圖顯示了上述配置中指定的 DNS 域名解析流程。

1)查詢首先被發送到 kube-dns 中的 DNS 緩存層。
2)從緩存層,檢查請求的后綴,並根據下面的情況轉發到對應的 DNS 上:
-
具有集群后綴的名字(例如 “.cluster.local”):請求被發送到 kube-dns。
-
具有存根域后綴的名字(例如 “.acme.local”):請求被發送到配置的自定義 DNS 解析器(例如:監聽在 1.2.3.4)。
-
未能匹配上后綴的名字(例如 “widget.com”):請求被轉發到上游 DNS(例如:Google 公共 DNS 服務器,8.8.8.8 和 8.8.4.4)。
域名解析的順序:
域名
|
響應查詢的服務器
|
kubernetes.default.svc.cluster.local
|
kube-dns
|
foo.acme.local
|
自定義 DNS (1.2.3.4)
|
widget.com
|
上游 DNS (8.8.8.8, 8.8.4.4,其中之一)
|
12.3 自定義DNS的示例
1)新增加一個節點10.0.2.12,安裝一個dnsmasq服務器,並創建一條主機記錄供dnsmasq使用。
生成一個自定義的DNS記錄文件/tmp/hosts:
# echo "192.168.10.2 server.acme.local" > /tmp/hosts
啟動DNS服務:
# dnsmasq -q -d -h -R -H /tmp/hosts
參數說明:
-
-d: 以debug模式啟動,在前后運行,便於觀察日志
-
-q: 輸出查詢記錄
-
-h: 不使用/etc/hosts
-
-R: 不使用/etc/resolve.conf
-
-H: 使用自定義的文件作為DNS記錄
2)創建自定義DNS的Configmap
配置文件dns-configmap.yaml的內容如下:
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-dns
namespace: kube-system
data:
stubDomains: |
{"acme.local": ["10.0.2.12"]}
upstreamNameservers: |
["8.8.8.8", "8.8.4.4"]
[root@bogon ~]# kubectl create -f dns-configmap.yaml
configmap "kube-dns" created
3)運行一個容器,進入容器內部查看自定義的DNS配置是否生效
文件dns-tester.yaml
apiVersion: v1
kind: Pod
metadata:
name: dns-tester
spec:
dnsPolicy: ClusterFirst
containers:
- name: busybox
image: busybox
command:
- sleep
- "3600"
[root@bogon ~]# kubectl create -f dns-tester.yaml
pod "dns-tester" created
[root@bogon ~]# kubectl exec -it dns-tester -- sh
# nslookup kubernetes.default
Server: 10.10.10.2
Address 1: 10.10.10.2 bogon
Name: kubernetes.default
Address 1: 10.10.10.1 bogon
#
理論上,從容器中執行ping server.acme.local應該返回解析出的地址192.168.10.2 。而實際上,上面的試驗失敗了,自定義DNS服務器未能生效,原因暫未找到!
13、Ingress:HTTP 7層路由機制
根據前面對Service的使用說明,我們知道Service的表現形式為IP:Port,即工作在TCP/IP層,而對於基於HTTP的服務來說,不同的URL地址經常對應到不同的后端服務或者虛擬服務器,這些應用層的轉發機制僅通過kubernetes的Service機制是無法實現的。kubernetes v1.1版本中新增的Ingress資源對象將不同URL的訪問請求轉發到后端不同的Service,以實現HTTP層的業務路由機制。
使用 Ingress 進行負載分發時,Ingress Controller 將基於 Ingress 規則將客戶端請求直接轉發到 Service 對應的后端 Endpoint(即 Pod)上,這樣會跳過 kube-proxy 的轉發功能,kuber-proxy 不在起作用。如果 Ingress Controller 提供的是對外服務,則實際上實現的是邊緣路由器的功能。
下圖顯示了一個典型的 HTTP 層路由的例子:

- 對 http://mywebsite.com/api 的訪問將被路由到后端為 “api” 的 Service。
- 對 http://mywebsite.com/web 的訪問將被路由到后端為 “web” 的 Service。
- 對 http://mywebsite.com/docs 的訪問將被路由到后端為 “docs” 的 Service。
為使用Ingress,需要創建Ingress Controller(帶一個默認backend服務)和Ingress策略設置來共同完成。
下面通過一個例子說明Ingress Controller和Ingress策略的配置方法和客戶端如何訪問Ingress提供的服務。
在定義 Ingress 策略之前,需要先部署 Ingress Controller,以實現為所有后端 Service 提供一個統一的入口。Ingress Controller 需要實現基於不同 HTTP URL 向后轉發的負載分發規則,並可以靈活設置 7 層的負載分發策略。如果公有雲服務商能夠提供該類型的 HTTP 路由 LoadBalancer,則也可以設置其為 Ingress Controller。
在 Kubernetes 中,Ingress Controller 將以 Pod 的形式運行,監控 apiservice 的 /ingress 接口后端的 backend services,如果 service 發生變化,則 Ingress Controller 應自動更新其轉發規則。
下面的例子使用 Nginx 來實現一個 Ingress Controller,需要實現的基本邏輯如下:
(1)監聽 apiserver,獲取全部 ingress 的定義。
(2)基於 ingress 的定義,生成 Nginx 所需的配置文件 /etc/nginx/nginx.conf。
(3)執行 nginx -s reload 命令,重新加載 nginx.conf 配置文件的內容。
基於 Go 語言的代碼核心實現如下:
for {
rateLimiter.Accept()
ingresses, err := ingClient.List(Labels.Everything(), fields.Everything())
if err != nil || reflect.Deepqual(ingresses.Items, known.Items) {
contiune
}
if w, err :- os.Create("/etc/nginx/nginx.conf"); err != nil {
log.Fatalf("Failed to open %v: %v", nginxConf, err)
} else if err := tmpl.Execute(w, ingresses); err != nil {
log.Fatalf("Failed to write template %v", err)
}
shellOut("nginx -s reload")
}
本例使用 Google 提供的 nginx-ingress-controller 鏡像來創建 Ingress Controller。該 Ingress Controller 以 daemonset 的形式進行創建,在每個Node上都將啟動一個Nginx服務。
這里為Nginx容器設置了hostPort,將容器應用監聽的80和443端口號映射到物理機上,使得客戶應用可以通過URL地址"http://物理機IP:80"或"https://物理機IP:443"來訪問該Ingress Controller。這使得Nginx類似於通過NodePort映射到物理機的Service,成為代替kube-proxy的HTTP層的Load Balancer。
默認情況下,Kubernetes服務的Pod無法從外部網絡訪問,但只能由Kubernetes集群內的其他Pod訪問。 Kubernetes具有用於HTTP負載平衡的內置配置,稱為Ingress,該配置定義了與Kubernetes服務的外部連接的規則。 需要提供對其Kubernetes服務的外部訪問的用戶創建一個定義規則的Ingress資源,其中包括URI路徑,支持服務名稱和其他信息。 然后,Ingress控制器可以自動編寫前端負載平衡器以啟用Ingress配置。 NGINX Ingress控制器使Kubernetes能夠配置NGINX和NGINX Plus來負載均衡Kubernetes服務。
13.1 創建一個專用的Namespace、Service Account和Default Secret
文件ns-and-sa.yaml,創建命名空間和服務賬號
apiVersion: v1
kind: Namespace
metadata:
name: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress
namespace: kube-system
[root@bogon ~]# kubectl create -f ns-and-sa.yaml
namespace "kube-system" created
serviceaccount "nginx-ingress" created
注:以上配置需要依賴於ServiceAccount認證,請確認你的kube-apiserver服務已經啟用了對ServiceAccount認證的支持。
在NGINX中為默認服務器創建一個帶有TLS證書和密鑰的Secret.
文件default-server-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: default-server-secret
namespace: kube-system
type: Opaque
data:
tls.crt: MIICoTCCAYkCCQCTK3tHyCzQOTANBgkqhkiG9w0BAQsFADAQMQ4wDAYDVQQDDAVib2dvbjAeFw0xODAyMjgwMDU4NTlaFw0zMTExMDcwMDU4NTlaMBUxEzARBgNVBAMMCmt1YmUtYWRtaW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCauNxlQU6xaDhjH77x6zwQeF77acYW9EkBfbEd8MM1ZcWm1agplI1M90uWJLwcS72qFeQ+zCxzBv6aQ5/LEziJLX3ETacSvNa+WoWKjhaJh7DuWybRHHC4731jwuYAbrjGrljSxmrBRWlUygTYobjp19CBEBU5PbsbzO5GMYFWRwPZXGyPR3XN0AjRFqoxj4wsuN/4UvMsPn7CVWJqv4is2hpRcoBZMTkBIaWal/TQkNRCg6PTufSfjOLa2U9P4UDGtcl/FHOotGRwP0Ouf34NKxtlCweeSr2dr+L92eymTde5EyTyiF6qexbTOouAxtzkQNgraRacm1vMmJNeLv/PAgMBAAEwDQYJKoZIhvcNAQELBQADggEBABdhlVenHx1ifsUV7TAAoh9dVYmTS+RLrlcEKnEMZOgm9qFYIZiG/Imo4hcy2sVsbRtl6AsZuFCpPtVo7CFW+9FGCrJfrxmBUwz5lrTvsHmvqMakT8lUv14fBgBkeNDycqNZZODwASn9Ycd//i1XlwGdKPZ52X+ixr184Clg6MUtYhkDQ7NwNEetwe7ajWFyMwrSNm/weFG4jPvX4e3ZwdRKhj2rv4DnuUIwRXAoY9b2mvrWRlx7mzzIvg3eu7WwBDCst1LNjsIVanI2tHLtCcuxqTCpitPRgRa+9/o8AarO6MOLN0aJucQ6THTUv0pcCwZ4RQXUgnaCBsxWQL862CY=
tls.key: MIIEpAIBAAKCAQEAmrjcZUFOsWg4Yx++8es8EHhe+2nGFvRJAX2xHfDDNWXFptWoKZSNTPdLliS8HEu9qhXkPswscwb+mkOfyxM4iS19xE2nErzWvlqFio4WiYew7lsm0RxwuO99Y8LmAG64xq5Y0sZqwUVpVMoE2KG46dfQgRAVOT27G8zuRjGBVkcD2Vxsj0d1zdAI0RaqMY+MLLjf+FLzLD5+wlViar+IrNoaUXKAWTE5ASGlmpf00JDUQoOj07n0n4zi2tlPT+FAxrXJfxRzqLRkcD9Drn9+DSsbZQsHnkq9na/i/dnspk3XuRMk8oheqnsW0zqLgMbc5EDYK2kWnJtbzJiTXi7/zwIDAQABAoIBABfjDnO7f3C4TGxRTGOBE8bfLprWG7UThenrA9tBfoHR8o/tUIcK3j3RuCO4DCq6LtABjTl8wCgKHmimpBbwIrxa/52891xXNzgwNYnMogIdpt1FyVpjb6u9nbg3MNdEQNa7uA7ifzzTgI2Mmu7vGONRZGlomD48H5UNMIQ5xGQTWTeXbdCNM3bdLMLRjJ3Fd4V5BlzOFYSXBcZV2aOIfXizJaamvdHdEXDQUC9/g5RcOncqzzy6uGR6PRJMwBWyV3AILzGvp/JJoj+68hppNNjsJhfs1ic5VaZ55gkPZnKRK8wJZUl8brbFNCbt49uT9tAYPN/VLFppGJsTVGn9/aECgYEAyyMy56S2Dp9zlsSBHqMetEXTuBW01Vl7+rcFM7yBlwWq+I0MCoOJAMWbitLoBBxfHwERLXJRoEPmA+fuITG8a1qr1rjPwXuC6FST81HjlX0aD/DvEySzHr/F/jHtwnMthXsx0c3Zx5E68KkMkOTgm3qkVsONWJfqO4m6yTdG1+kCgYEAwvxIDDqYlpgn0/dFwjmG7JMiL+KBULZ1iiH+J91mjXrXH+ZlO+EFMdwK3H4eRyME9EWDgV98VwJ5PQ6Acu0mRcBAaqfvimdypdIp4D32DQtPXJ5jwiyWb0EbUd9EYM0R4+WZO5c0E1lQ4BOPHdeQky9xKjcTc4qZ36MQZ8wQfvcCgYEAtcszdGFQ2PdUP7puf5emE9ll55ntcv4ZWT/PtQRfyWM2Jig8fXBH/NvcHrovD/bAHgQbdluXt2DsidRXpXdYU48auBg/Pb2mYvvGSHyhuxfxahDKNIykjME9lQbj7CCdvZaJ+GRMITDU2anAaC+c3yg7yLZLWdzisBPLiOYiTXkCgYEAtTpo+LzJsEbx1cihlNmFN4O1pc4gJVXBP8dCg+j7dYp0QpkRBKur+WCs062BFNtOsn+dr/SBDBond4FwD+GtzezXsoouUXS/EbKZ62uLsyoM9GffDs4EcJmR+/HJizNvamOvnbx20XkVe3+SaMk//h19UCBqutxbEopsKB16Q5cCgYA4vQj2OOjkRAux/lHhkamjVFK1LVQn3br4jvhStVfWvxPReDTxMQHS9KmPdZF8A1sFAPkDQt95z/kJy0Tvg8P9ZRXe2wuGo6lSTT9Hj8XiYMwaLIAhChxagBr7VJ4dvf65/j/QSjH4YzHztPvzLy9T0xeBP3jnY3t4/gCtOCmNgA==
[root@bogon ~]# kubectl create -f default-server-secret.yaml
secret "default-server-secret" created
注:這里建議自行創建一個自簽的證書和密鑰。
可選的。 還可以創建一個用於定制NGINX配置的configmap(在
這里閱讀更多關於定制的信息)
13.2 配置RBAC
基於角色的訪問控制(“RBAC”)使用“rbac.authorization.k8s.io”API 組來實現授權控制,允許管理員通過Kubernetes API動態配置策略。
截至1.6 RBAC模式是beta版。
啟用RBAC:apiserver --authorization-mode=RBAC。
如果群集中啟用了RBAC,請創建群集角色並將其綁定到在步驟1中創建的服務帳戶:
文件rbac.yaml:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: nginx-ingress
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- configmaps
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- pods
verbs:
- list
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: nginx-ingress
subjects:
- kind: ServiceAccount
name: nginx-ingress
namespace: nginx-ingress
roleRef:
kind: ClusterRole
name: nginx-ingress
apiGroup: rbac.authorization.k8s.io
$ kubectl apply -f rbac.yaml
13.3 部署Ingress Controller
有兩種可先的部署方法,各不有同的用途:
- deployment, 如果計划要動態更改Ingress控制器副本的數量,請使用deployment。
- DaemonSet, 使用DaemonSet在每個節點或節點的子集上部署Ingress控制器。
使用deployment方式:
文件nginx-ingress.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ingress
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: nginx-ingress
template:
metadata:
labels:
app: nginx-ingress
spec:
serviceAccountName: nginx-ingress
containers:
- image: nginxdemos/nginx-ingress:1.1.1
name: nginx-ingress
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
#- -v=3 # Enables extensive logging. Useful for trooublshooting.
$ kubectl apply -f nginx-ingress.yaml
Kubernetes將創建一個Ingress控制器Pod。
使用DaemonSet方式:
文件nginx-ingress.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: nginx-ingress
namespace: kube-system
spec:
selector:
matchLabels:
app: nginx-ingress
template:
metadata:
labels:
app: nginx-ingress
spec:
serviceAccountName: nginx-ingress
containers:
- image: nginxdemos/nginx-ingress:1.1.1
name: nginx-ingress
ports:
- name: http
containerPort: 80
hostPort: 80
- name: https
containerPort: 443
hostPort: 443
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
args:
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
#- -v=3 # Enables extensive logging. Useful for trooublshooting.
創建和查看Ingress控制器:
[root@bogon ~]# kubectl apply -f nginx-ingress.yaml
daemonset "nginx-ingress" created
[root@bogon ~]# kubectl get ds --namespace=kube-system
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
nginx-ingress 2 2 2 2 2 <none> 4s
[root@bogon ~]# kubectl get pods --namespace=kube-system
NAME READY STATUS RESTARTS AGE
nginx-ingress-2w89p 1/1 Running 0 19s
nginx-ingress-8d9js 1/1 Running 0 19s
[root@bogon ~]# kubectl logs nginx-ingress-2w89p --namespace=kube-system
I0325 12:05:27.883237 1 main.go:65] Starting NGINX Ingress controller Version=1.1.1 GitCommit=8fc772d
2018/03/25 12:05:27 [notice] 18#18: signal process started
I0325 12:05:27.928047 1 event.go:218] Event(v1.ObjectReference{Kind:"Secret", Namespace:"kube-system", Name:"default-server-secret", UID:"0cf6ec9b-3024-11e8-9fed-080027cf1a4c", APIVersion:"v1", ResourceVersion:"2151163", FieldPath:""}): type: 'Normal' reason: 'Updated' the default server Secret nginx-ingress/default-server-secret was updated
Kubernetes將在集群的每個節點上創建一個Ingress控制器Pod。 閱讀
本文以了解如何在節點的子集上運行Ingress控制器,而不是在集群的每個節點上運行。
13.4 訪問Ingress控制器
如果是創建的DaemonSet,則Ingress控制器容器的端口80和443將映射到容器正在運行的節點的相同端口。 要訪問Ingress控制器,請使用Ingress控制器正在運行的群集中任何節點的IP地址。
[root@bogon ~]# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
10.0.2.10 Ready <none> 17d v1.8.8 <none> CentOS Linux 7 (Core) 3.10.0-693.17.1.el7.x86_64 docker://17.12.0-ce
10.0.2.6 Ready <none> 28d v1.8.8 <none> CentOS Linux 7 (Core) 3.10.0-693.17.1.el7.x86_64 docker://17.12.0-ce
[root@bogon ~]# curl 10.0.2.6
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.13.8</center>
</body>
</html>
[root@bogon ~]# curl 10.0.2.10
<html>
<head><title>404 Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.13.8</center>
</body>
</html>
進入一個nginx ingress controller pod中查看:
# kubectl exec -it nginx-ingress-2w89p --namespace=kube-system -c nginx-ingress -- /bin/bash
如果是創建的deployment,則有兩種方法可以訪問到Ingress controller pods。
1)使用NodePort類型的Service
文件nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-ingress
namespace: kube-system
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
- port: 443
targetPort: 443
protocol: TCP
name: https
selector:
app: nginx-ingress
$ kubectl create -f nodeport.yaml
2)使用LoadBalancer類型的Service
使用LoadBalancer類型創建一個服務。 Kubernetes將分配和配置一個雲負載均衡器,以負載平衡Ingress控制器Pods。
13.5 刪除Ingress控制器
刪除kube-system命名空間可以卸載Ingress控制器以及創建的所有輔助資源:
$ kubectl delete namespace kube-system
13.6 為Ingress Controller配置一個默認的 backend
這個 backend 服務用任何應用實現都可以,只要滿足默認對 /路徑 的訪問返回 404 應答,並且提供 /healthz 路徑以使 kubelet 完成對它的健康檢查。另外,由於 Nginx 通過 default-backend-service 的服務名稱(Service Name)去訪問它,所以需要 DNS 服務正確運行。
文件:default-backend.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: default-http-backend
labels:
k8s-app: default-http-backend
namespace: kube-system
spec:
replicas: 1
template:
metadata:
labels:
k8s-app: default-http-backend
spec:
terminationGracePeriodSeconds: 60
containers:
- name: default-http-backend
# Any image is permissable as long as:
# 1. It serves a 404 page at /
# 2. It serves 200 on a /healthz endpoint
image: gcr.io/google_containers/defaultbackend:1.4
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
ports:
- containerPort: 8080
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: default-http-backend
namespace: kube-system
labels:
k8s-app: default-http-backend
spec:
ports:
- port: 80
targetPort: 8080
selector:
k8s-app: default-http-backend
# kubectl create -f default-backend.yaml
[root@bogon ~]# kubectl get svc --namespace=kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default-http-backend ClusterIP 10.10.10.22 <none> 80/TCP 18h
kube-dns ClusterIP 10.10.10.2 <none> 53/UDP,53/TCP 5d
[root@bogon ~]# kubectl get deploy --namespace=kube-system
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
default-http-backend 1 1 1 1 18h
[root@bogon ~]# kubectl get pod --namespace=kube-system
NAME READY STATUS RESTARTS AGE
default-http-backend-8788bb498-z9cxl 1/1 Running 1 13h
kube-dns-v11-cndzm 4/4 Running 20 5d
nginx-ingress-k62b5 1/1 Running 0 8m
nginx-ingress-l4rhx 1/1 Running 0 8m
13.7 定義Ingress策略
為mywebsite.com定義Ingress,設置到后端Service的轉發規則。
文件my-ingress.yaml:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mywebsite-ingress
spec:
rules:
- host:
mywebsite.com
http:
paths:
- path: /healthz
backend:
serviceName: default-http-backend
servicePort: 80
這個Ingress的定義說明對目標http://mywebsite.com/healthz的訪問將被轉發到kubernetes的一個Service上 default-http-backend:80/healthz上。
創建該Ingress
[root@bogon ~]# kubectl create -f my-ingress.yaml
ingress "mywebsite-ingress" created
[root@bogon ~]# kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
mywebsite-ingress
mywebsite.com 80 33s
創建后登陸nginx-ingress Pod,查看自動生成的nginx負載均衡配置內容:
[root@bogon ~]# kubectl exec -it nginx-ingress-k62b5 --namespace=kube-system -c nginx-ingress -- /bin/bash
root@nginx-ingress-k62b5:/# cd etc
root@nginx-ingress-k62b5:/etc# cd nginx
root@nginx-ingress-k62b5:/etc/nginx# ls
conf.d koi-utf mime.types nginx.conf secrets win-utf
fastcgi_params koi-win modules scgi_params uwsgi_params
root@nginx-ingress-k62b5:/etc/nginx# cd conf.d
root@nginx-ingress-k62b5:/etc/nginx/conf.d# ls
kube-system-mywebsite-ingress.conf
onf @nginx-ingress-k62b5:/etc/nginx/conf.d# more kube-system-mywebsite-ingress.co
server 172.17.0.5:8080;
}
server {
listen 80;
server_tokens on;
server_name
mywebsite.com;
location /healthz {
proxy_http_version 1.1;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
client_max_body_size 1m;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering on;
proxy_pass http://kube-system-mywebsite-ingress-mywebsite.com-default-http-backend;
}
}
訪問http://mywebsite.com/web
我們可以通過物理機對其進行訪問。通過curl --resolve進行指定
[root@bogon ~]# curl -I --resolve
mywebsite.com:80:10.0.2.6 mywebsite.com/healthz
HTTP/1.1 200 OK
Server: nginx/1.13.8
Date: Sun, 25 Mar 2018 14:09:48 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive
13.8 Ingress的策略配置技巧
為展開下面的測試,我們需要准備一些測試環境。
文件:webapp-rc.yaml,提供測試用的backend pods
apiVersion: v1
kind: ReplicationController
metadata:
name: webapp
namespace: kube-system
spec:
replicas: 2
template:
metadata:
name: webapp
labels:
app: webapp
spec:
containers:
- name: webapp
image: tomcat
ports:
- containerPort: 8080
生成一個Service服務,服務端口將默認與容器端口一致:
# kubectl expose rc webapp --namespace=kube-system
[root@bogon ~]# kubectl get svc --namespace=kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default-http-backend ClusterIP 10.10.10.22 <none> 80/TCP 1d
kube-dns ClusterIP 10.10.10.2 <none> 53/UDP,53/TCP 6d
webapp ClusterIP 10.10.10.206 <none> 8080/TCP 2h
[root@bogon ~]# kubectl get pod --namespace=kube-system
NAME READY STATUS RESTARTS AGE
default-http-backend-8788bb498-z9cxl 1/1 Running 3 1d
kube-dns-v11-cndzm 4/4 Running 28 6d
nginx-ingress-k62b5 1/1 Running 2 1d
nginx-ingress-l4rhx 1/1 Running 4 1d
webapp-ms8z2 1/1 Running 0 2h
webapp-t6jlm 1/1 Running 0 2h
1)轉發到單個后端服務上
客戶端到Ingress Controller的訪問請求都將被轉發到后端的唯一Service上。這種情況下Ingress無須定義任何rule。
例如,通過以下設置,對Ingress Controller的訪問請求都將被轉發到"myweb:8080"這個服務上。
文件my-web-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
spec:
backend:
serviceName: webapp
servicePort: 8080
2)同一域名下,不同的URL路徑被轉發到不同的服務上
例如/examples表示訪問examples web頁面,/healthz表示訪問健康檢查接口。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mywebsite-ingress
namespace: kube-system
spec:
rules:
- host:
mywebsite.com
http:
paths:
- path: /healthz
backend:
serviceName: default-http-backend
servicePort: 80
- path: /examples
backend:
serviceName: webapp
servicePort: 8080
創建並查看結果:
[root@bogon ~]# kubectl get ingress --namespace=kube-system
NAME HOSTS ADDRESS PORTS AGE
mywebsite-ingress
mywebsite.com 80 4m
查看此時的pods信息:
[root@bogon ~]# kubectl get pod --namespace=kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE
default-http-backend-8788bb498-z9cxl 1/1 Running 3 1d 172.17.0.4 10.0.2.6
kube-dns-v11-cndzm 4/4 Running 28 6d 172.17.0.6 10.0.2.6
nginx-ingress-k62b5 1/1 Running 2 1d 172.17.0.7 10.0.2.6
nginx-ingress-l4rhx 1/1 Running 4 1d 172.17.0.2 10.0.2.10
webapp-ms8z2 1/1 Running 0 2h 172.17.0.5 10.0.2.6
webapp-t6jlm 1/1 Running 0 2h 172.17.0.3 10.0.2.10
我們選擇使用節點10.0.2.6做些測試驗證:
[root@bogon ~]# curl -I --resolve
mywebsite.com:80:10.0.2.6 mywebsite.com/healthz
HTTP/1.1 200 OK
Server: nginx/1.13.8
Date: Mon, 26 Mar 2018 18:16:56 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive
[root@bogon ~]# curl -I --resolve
mywebsite.com:80:10.0.2.6 mywebsite.com/examples/
HTTP/1.1 200
Server: nginx/1.13.8
Date: Mon, 26 Mar 2018 18:17:00 GMT
Content-Type: text/html
Content-Length: 1126
Connection: keep-alive
Accept-Ranges: bytes
ETag: W/"1126-1520255626000"
Last-Modified: Mon, 05 Mar 2018 13:13:46 GMT
查看節點10.0.2.6上面的Ingress Controller的日志信息:
[root@bogon ~]# kubectl logs nginx-ingress-k62b5 --namespace=kube-system
10.0.2.5 - - [26/Mar/2018:18:16:56 +0000] "HEAD /healthz HTTP/1.1" 200 0 "-" "curl/7.29.0" "-"
10.0.2.5 - - [26/Mar/2018:18:17:00 +0000] "HEAD /examples/ HTTP/1.1" 200 0 "-" "curl/7.29.0" "-"
查看下節點10.0.2.6上面的Ingress Controller的nginx負載均衡配置信息:
[root@bogon ~]# kubectl exec -it nginx-ingress-k62b5 --namespace=kube-system -c nginx-ingress -- /bin/bash
root@nginx-ingress-k62b5:/# cd /etc/nginx/conf.d
root@nginx-ingress-k62b5:/etc/nginx/conf.d# ls
kube-system-mywebsite-ingress.conf
root@nginx-ingress-k62b5:/etc/nginx/conf.d# more kube-system-mywebsite-ingress.conf
server 172.17.0.4:8080;
}
server 172.17.0.3:8080;
server 172.17.0.5:8080;
}
server {
listen 80;
server_tokens on;
server_name
mywebsite.com;
location /healthz {
proxy_http_version 1.1;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
client_max_body_size 1m;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering on;
}
location /examples {
proxy_http_version 1.1;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
client_max_body_size 1m;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering on;
}
}
root@nginx-ingress-k62b5:/etc/nginx/conf.d#
3)不同的域名(虛擬主機名)被轉發到不同的服務上
如下所示,對"a.ourdomain.com"的訪問請求將被轉發到"webapp:8080"服務上,對"b.ourdomain.com"的訪問請求將被轉發到"default-http-backend:80"的服務上。
文件my-ingress-vhost.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test-ingress
namespace: kube-system
spec:
rules:
- host: a.ourdomain.com
http:
paths:
- path: /examples
backend:
serviceName: webapp
servicePort: 8080
- host:
b.ourdomain.com
http:
paths:
- path: /healthz
backend:
serviceName: default-http-backend
servicePort: 80
創建:
[root@bogon ~]# kubectl create -f my-ingress-vhost.yaml
ingress "ourdomain-ingress" created
[root@bogon ~]# kubectl get ingress --namespace=kube-system
NAME HOSTS ADDRESS PORTS AGE
ourdomain-ingress
a.ourdomain.com,
b.ourdomain.com 80 13s
[root@bogon ~]# kubectl get ingress --namespace=kube-system -o wide
NAME HOSTS ADDRESS PORTS AGE
ourdomain-ingress
a.ourdomain.com,
b.ourdomain.com 80 17s
測試:
[root@bogon ~]# curl -I --resolve a.ourdomain.com:80:10.0.2.6 a.ourdomain.com/examples/
HTTP/1.1 200
Server: nginx/1.13.8
Date: Tue, 27 Mar 2018 00:48:52 GMT
Content-Type: text/html
Content-Length: 1126
Connection: keep-alive
Accept-Ranges: bytes
ETag: W/"1126-1520255626000"
Last-Modified: Mon, 05 Mar 2018 13:13:46 GMT
[root@bogon ~]# curl -I --resolve b.ourdomain.com:80:10.0.2.6 b.ourdomain.com/healthz
HTTP/1.1 200 OK
Server: nginx/1.13.8
Date: Tue, 27 Mar 2018 00:48:59 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive
進入nginx ingress controller pod容器中查看nginx虛擬主機的配置:
[root@bogon ~]# kubectl exec -it nginx-ingress-k62b5 --namespace=kube-system -c nginx-ingress -- /bin/bash
root@nginx-ingress-k62b5:/# cd /etc/nginx/conf.d
root@nginx-ingress-k62b5:/etc/nginx/conf.d# ls
kube-system-ourdomain-ingress.conf
root@nginx-ingress-k62b5:/etc/nginx/conf.d# more kube-system-ourdomain-ingress.conf
server 172.17.0.3:8080;
server 172.17.0.5:8080;
}
server 172.17.0.4:8080;
}
server {
listen 80;
server_tokens on;
server_name
a.ourdomain.com;
location /examples {
proxy_http_version 1.1;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
client_max_body_size 1m;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering on;
}
}
server {
listen 80;
server_tokens on;
server_name
b.ourdomain.com;
location /healthz {
proxy_http_version 1.1;
proxy_connect_timeout 60s;
proxy_read_timeout 60s;
client_max_body_size 1m;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering on;
}
}
root@nginx-ingress-k62b5:/etc/nginx/conf.d#
4)不使用域名的轉發規則
用於一個網站不使用域名直接提供服務的場景。
下面的配置將<ingress-controller-ip>/examples/的訪問請求轉發到"webapp:8080/examples/"服務上。
文件:my-nonedomain-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: none-domain-ingress
namespace: kube-system
spec:
rules:
- http:
paths:
- path: /examples
backend:
serviceName: webapp
servicePort: 8080
注意:使用無域名的Ingress轉發,將默認為禁用非安全HTTP,強制啟用HTTPS,此時全部的HTTP訪問請求將直接返回301錯誤。
[root@bogon ~]# kubectl get ingress --namespace=kube-system
NAME HOSTS ADDRESS PORTS AGE
none-domain-ingress * 80 2m
test-ingress
a.ourdomain.com,
b.ourdomain.com 80 8m
curl http://10.0.2.6/examples/
curl -k https://10.0.2.6/examples/
可以在Ingress的定義中設置一個annotation "ingress.kubernetes.io/ssl-redirect=false"來關閉強制啟用HTTPS的設置。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: none-domain-ingress
annotations:
ingress.kubernetes.io/ssl-redirect: "false"
namespace: kube-system
spec:
rules:
- http:
paths:
- path: /examples
backend:
serviceName: webapp
servicePort: 8080
curl http://10.0.2.6/examples/
問題:在實驗中發現按上面的不使用域名的轉發規則去配置Ingress,雖然可以正常生成對應的Ingress資源對象,但是進入ingress controller中卻發現始終無法生成對應的nginx配置文件信息。網上的相關資料很少,具體原因推測不排除是與軟件版本迭代、功能更新有關。因此,不使用域名的轉發規則未得到成功的驗證!
13.9 Ingress的TLS安全設置
為了使得Ingress提供HTTPS的安全訪問,可以為Ingress中的域名進行TLS安全證書的設置。
設置的步驟如下:
1)創建自簽名的密鑰和SSL證書文件。
2)將證書保存到Kubernetes中的一個Secret資源對象上。
3)將該Secret對象設置到Ingress中。
根據提供服務的網站是一個還是多個,可以使用不同的操作完成前兩步SSL證書和Secret對象的創建,在只有一個域名的情況下設置相對簡單。
第3步對於這兩種場景來說是相同的。
只有一個域名時的配置方法
對於只有一個域名的場景來說,可以通過OpenSSL工具直接生成密鑰和證書文件,將命令行參數-subj中的/CN設置為網站的域名:
[root@bogon ~]# openssl req -x509 -nodes -days 5000 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=
mywebsite.com"
Generating a 2048 bit RSA private key
.................................................+++
.+++
writing new private key to 'tls.key'
-----
[root@bogon ~]#
上述命令將在一個命令中生成自簽名的tls.crt證書和tls.key密鑰兩個文件。
根據tls.key和tls.crt創建secret資源對象,有以下兩種方法。
方法一:通過kubectl create secret tls命令直接通過tls.key和tls.crt文件創建secret對象。
[root@bogon ~]# kubectl create secret tls mywebsite-ingress-secret --key tls.key --cert tls.crt --namespace kube-system
secret "mywebsite-ingress-secret" created
方法二:編輯mywebsite-ingress-secret.yaml文件,將tls.key和tls.crt文件的內容復制進去,使用kubectl create命令進行創建。
文件mywebsite-ingress-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mywebsite-ingress-secret
namespace: kube-system
type: kubernetes.io/tls
data:
tls.crt:
MIIDAzCCAeugAwIBAgIJAO6WRm6qQzW/MA0GCSqGSIb3DQEBCwUAMBgxFjAUBgNVBAMMDW15d2Vic2l0ZS5jb20wHhcNMTgwMzI3MTIzNjQ2WhcNMzExMjA0MTIzNjQ2WjAYMRYwFAYDVQQDDA1teXdlYnNpdGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwNUw4kvWdA9GK2CCuEYAShJhfh3etUv8PjSXMqb6E5TKipwEI7ui0dh1T6GOm4ZH81myDyy1hmcpv1z1Pcx2D+EYada3c9Ea2YsFj5wdknsTP1BlO0env2pH0jCm2o+AR91gABVolw3u5gMvRuS7xJQQVzebStu0jK/20DtWbhVsuKvp/Bfdi+idKqaQXdleFgDeaLjUOPEAVCU8DOAvnmfQ639d05UMmqujMd7tisDA4p4D3e6jBQ2YOIq8DH0dxKtNRS70LudHBKa3xxofD/IVNnwaBdMi4t5txdTuvrS0Q5gWCMDgF4Ow0uJFKqcP3y83u3nxxCYk5RwRlY4myQIDAQABo1AwTjAdBgNVHQ4EFgQU3NpXIpRH/HUun+/DzOAGrXnR9uwwHwYDVR0jBBgwFoAU3NpXIpRH/HUun+/DzOAGrXnR9uwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAfyKX4RVf190Ywy29Wreq+gzpo5yPZzMNf0EF7yw64EJq4Gf8Tue0P+HO2xVKK8+RsoG3QsZh2/mLX9Q4g/kaPBK/i4PFOewOWglHvG1j8rW3zOQ2DlTa1fDhGgAnz39c/9L7AMy55xkT6fPLRGELBD/K6KyvzLIgpnHN5gRHqJVgsa+RXzk1pMctpgIdllxST1Wm4qGqWwyMqxqwoypmLcHFjEXmyezzXQ42S2FMi5KbwAY6JvbrC9ovIpxOGExoiIZPn/Ll7O66J1CDfLCVEgKFGwAI0csi8inFMpjlpLaBBs0O3thBRole08+bs6BAnWQBoQ7eUG98xnGjHStOdg==
tls.key:
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDA1TDiS9Z0D0YrYIK4RgBKEmF+Hd61S/w+NJcypvoTlMqKnAQju6LR2HVPoY6bhkfzWbIPLLWGZym/XPU9zHYP4Rhp1rdz0RrZiwWPnB2SexM/UGU7R6e/akfSMKbaj4BH3WAAFWiXDe7mAy9G5LvElBBXN5tK27SMr/bQO1ZuFWy4q+n8F92L6J0qppBd2V4WAN5ouNQ48QBUJTwM4C+eZ9Drf13TlQyaq6Mx3u2KwMDingPd7qMFDZg4irwMfR3Eq01FLvQu50cEprfHGh8P8hU2fBoF0yLi3m3F1O6+tLRDmBYIwOAXg7DS4kUqpw/fLze7efHEJiTlHBGVjibJAgMBAAECggEAXQi+i/+w2M/ht93ZU5SaYSJbHSDGKUwex9hRtmNvNfFMgvFg4eiOVpYLiz7T8d8oaZU13jJg5hZxwtzTiVxS/bhJMg8g9WTGgnybUz0r21c74wkjviUFHsnlGtjUN5CSgJmizoeFrZNQVnLk5VxH50DUXW/7oXM8ub/P4YkvWu5tJVquRmxlgzfsb+A2OAefDg+6hVRfrRT5yuAynTMTzocxDWIKX5qT2kWR2w3oOw1PxB+XIPeJYiWbM2Js49X9vnIoV5Ep4JsdfP7ViK7zD1qIk4K47L/LlfriKlW4o0iF7eTxqV4s1VsBzq6mU24waucUk2bTh0U1tVpMyA0DcQKBgQD8RU7roMhZKEPibv9FtsU6AmGXbdOzNchRzKAJUc7+MtYx79LfgE7Z6menaOAvQopjYf3iha5fuK+0qpD/tPvSTDlEZmSRbUjBzknVJRT3LLcT5BdRuOCKa+9oRDOvRF5KeVJp4UPcFcuX0HYCpKeffW6Bp6GCC90QPw2oPoJkdQKBgQDDrvIwW/PwdouHM3mUHg55BtC/6EpVeSquvbe5TDjM8nRv2WzfulNXxXE7t6Np4rQUWRRu1sSetY4j/nABc1doTmr33KpL0CDGQgbyIiAilP+P3d4vM7SQ/D7DAkZCx/sPU2CpT1UAamxMQx4UusQV5VCxlpU0WSODd7RQTZlehQKBgGm5ekvSBGUpNNoO/rju0lVvbgsSoih3H874XRHK0V9DWvVutweoNGOrk2lb0Ki90FW6Wba3o87GDg0/dqlT5j3KaC28Y+V20Yk3tUW41sKIYOnbK9K6EpCwj5M5EhggJQw9MEbje5cfI0YqvWp6Ky3yWX/JCyz/X9RROJ4MJIWJAoGAN/IfmgXgI2KxF5F29Ar31lBFhGh0QCWpN1ZUXazWHHpBIe1GvFw4EcMIUat6E/7Vv2+McPgENN/xaIcJHNEOL2skGANFyPETPbuI4tcw2tJAkrA7IsGkoUMM6pIO5m7ob4RGAYIF6l2C4vbZddHD2AK85FkKBXJqvd6OddsJu0UCgYEA34vgqzIC2EyvqdmGO3fPpiEyATRVJhBtMUY/RL/NsXA+bweZ7r+77SpOHXn3cYrF+HRRGjQTJY4ndNrKrpErlNoCEs5TQUMH1YxvDmHcSUOMcQuQa03MUVvSCu9vR2JmM7JAx89H0ccm45UcuSAClttBYol4/wRnXBm5hZ4Q9bk=
# kubectl create -f mywebsite-ingress-secret.yaml
secret "mywebsite-ingress-secret" created
有多個域名時的配置方法
如果提供服務的網站不止一個域名,例如前面的Ingress策略配置方式三,則SSL證書需要使用額外的一個x509 v3配置文件輔助完成,在[alt_names]段中完成多個DNS域名的設置。
編寫openssl.cnf配置文件:
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[req_distinguished_name]
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 =
mywebsite.com
DNS.2 = mywebsite2.com
1)首先生成自簽名的CA證書
# openssl genrsa -out ca.key 2048
# openssl req -x509 -new -nodes -key ca.key -days 5000 -out ca.crt -subj "/CN=
mywebsite.com"
2)基於openssl.cnf和ca證書生成ingress SSL證書
# openssl genrsa -out ingress.key 2048
# openssl req -new -key ingress.key -out ingress.csr -subj "/CN=
mywebsite.com" -config openssl.cnf
# openssl x509 -req -in ingress.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out ingress.crt -days 5000 -extensions v3_req -extfile openssl.cnf
3)根據ingress.key和ingress.crt文件創建secret資源對象
# kubectl create secret tls mywebsite-ingress-secret --key ingress.key --cert ingress.crt
至此,就成功完成了多個域名條件下的Ingress TLS證書與Secret對象的創建了。
最后,創建Ingress對象並在tls段中引用剛剛創建好的Secret對象
文件mywebsite-ingress-tls.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mywebsite-ingress-tls
namespace: kube-system
spec:
tls:
- hosts:
secretName: mywebsite-ingress-secret
rules:
- host:
mywebsite.com
http:
paths:
- path: /healthz
backend:
serviceName: default-http-backend
servicePort: 80
- path: /examples
backend:
serviceName: webapp
servicePort: 8080
之后,就可以通過HTTPS來訪問mywebsite.com了。
[root@bogon ~]# curl -H 'Host:
mywebsite.com' -k https://10.0.2.6/healthz
ok
[root@bogon ~]#
[root@bogon ~]# curl -H 'Host:
mywebsite.com' -k https://10.0.2.6/examples/
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE HTML><html lang="en"><head>
<meta charset="UTF-8">
<title>Apache Tomcat Examples</title>
</head>
<body>
<p>
<h3>Apache Tomcat Examples</H3>
<p></p>
<ul>
<li><a href="servlets">Servlets examples</a></li>
<li><a href="jsp">JSP Examples</a></li>
<li><a href="websocket/index.xhtml">WebSocket Examples</a></li>
</ul>
</body></html>
[root@bogon ~]#
13.10 Ingress的發展路線
當前的Ingress還是Beta版本,在Kubernetes的后續版本中將增加以下功能。
- 支持更多TLS選項,例如SNI、重加密等。
- 支持L4和L7負載均衡策略(目前只支持HTTP層的規則)。
- 支持更多類型的Ingress Controller,除了Nginx,還有HAProxy、Linkerd、traefik、AWS Application Load Balancer Ingress Controller等已經實現了的Ingress Controller,詳情可參考https://github.com/kubernetes/ingress-nginx/blob/master/docs/catalog.md的說明。
參考資料:
《Kubernetes權威指南——從Docker到Kubernetes實踐全接觸》第2章
https://github.com/kubernetes/ingress-nginx