一、導讀
對於基於HTTP的服務來說,不同的URL地址經常對應不同的后端服務或者虛擬服務器,通常的做法是在應用前添加一個反向代理服務器Nginx,進行請求的負載轉發,在Spring Cloud這個微服務框架中,使用zuul網關實現此功能。
而對於k8s集群來說,當然也是可以用Nginx實現請求的轉發,但對於一個成熟的容器編排工具,k8s內置了一個HTTP請求負載分發的組件,就是Ingress Controll。另外,k8s的Service也是具有負載均衡能力的組件。

二、用法
在定義Ingress之前,需要先部署Ingress Controller,以實現所有后端Service提供一個統一的入口。Ingress Controller需要實現基於不同Http URL向后轉發的負載分發規則 。
在K8s中,Ingress Controller將以Pod的形式運行,監控apiserver的/ingress接口后端的backend services,如果service發生變化,則Ingress Controller應自動更新其轉發規則。
1、創建Ingress Controller
其實Ingress底層就可以用Nginx實現,Ingress Controller會監聽ApiServer,獲取全部的Ingress定義,然后根據定義生成Nginx的配置文件。
下面使用nginx-ingress-controller鏡像來創建Ingress Controller。
apiVersion: v1 kind: ReplicationController metadata: name: nginx-ingress namespace: ingress labels: app: nginx-ingress spec: replicas: 1 template: metadata: name: nginx-ingress labels: app: nginx-ingress spec: serviceAccountName: ingress-sc containers: - name: nginx-ingress image: imagia/nginx-ingress-controller:0.32.0 imagePullPolicy: IfNotPresent env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - containerPort: 80 hostPort: 80 - containerPort: 443 hostPort: 443
這里為Nginx容器設置了hostPort,將容器應用監聽的80和443端口 號映射到物理機上,使得客戶端應用可以通過URL地址“http://物理機 IP:80”或“https://物理機IP:443”來訪問該Ingress Controller。這使得Nginx 類似於通過NodePort映射到物理機的Service,成為代替kube-proxy的 HTTP層的Load Balancer:
2、創建Ingress
下面的Ingress定義了將/user的請求轉發至user-svc的Service上,將/order的請求轉發至order-svc的Service上。
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: myweb-ingress namespace: ingress spec: rules: - host: myweb.com http: paths: - path: /api/user backend: serviceName: user-svc servicePort: 8081 - path: /api/order backend: serviceName: order-svc servicePort: 8082
Ingress的策略配置:
(1)所有請求都轉發到單個Service上
此時不用配置rules
spec: backend: serviceName: user servicePort: 8080
(2)同一域名,不同url轉發到不同的服務上
比如域名都是myweb.com,/api/user轉發到user服務,/api/order轉發到 order服務。
spec: rules: - host: myweb.com http: paths: - path: /api/user backend: serviceName: user-svc servicePort: 8081 - path: /api/order backend: serviceName: order-svc servicePort: 8082
(3)不同域名
域名為myweb1.com的請求轉發到user服務,域名為myweb2.com的請求轉發到order服務
spec: rules: - host: myweb1.com http: paths: - backend: serviceName: user-svc servicePort: 8081 - host: myweb2.com http: paths: - backend: serviceName: order-svc servicePort: 8082
(4)不使用域名
這種配置用於一個網站不使用域名直接提供服務的場景,此時通過 任意一台運行ingress-controller的Node都能訪問到后端的服務。
spec: rules: - http: paths: - path: /api/user backend: serviceName: user-svc servicePort: 8081
【注】使用無域名的Ingress轉發規則時,將默認禁用非安全 HTTP,強制啟用HTTPS。
三、案例
1、簡介
創建一個命名空間:ingress,啟動兩個服務,一個是user,一個是order,利用Ingress-controller將請求/api/user轉發到user服務,將請求/api/order轉發到order服務。
2、創建服務
(1)創建簡單springboot應用
只有兩個api接口,分別是/api/order和/api/user,然后打成jar包上傳至服務器


(2)將上述jar包創建為鏡像
將jar包、jdk安裝包和Dockerfile放在同一個目錄

Dockerfile文件內容如下:
FROM centos:7 LABEL author=lsy ENV path=/usr/soft RUN mkdir ${path} WORKDIR ${path} ADD jdk-8u191-linux-x64.tar.gz ${path} ENV JAVA_HOME=${path}/jdk1.8.0_191 ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar ENV PATH=$JAVA_HOME/bin:$PATH COPY service-1.0.jar ${path} EXPOSE 8080 CMD java -jar service-1.0.jar
使用命令創建鏡像:
docker build ./ -t cnode-1:5000/ingress-service:v1.0
鏡像創建完成后記得上傳到docker私有倉庫
(3)在ingress命名空間啟動相應的ReplicationController和Service
user-rc.yaml
apiVersion: v1 kind: ReplicationController metadata: name: user-rc namespace: ingress labels: name: user-rc spec: replicas: 1 selector: name: user-rc template: metadata: name: user-rc labels: name: user-rc spec: containers: - name: user-rc image: cnode-1:5000/ingress-service:v1.0 imagePullPolicy: IfNotPresent ports: - containerPort: 8080
user-svc.yaml
apiVersion: v1 kind: Service metadata: name: user-svc namespace: ingress spec: selector: name: user-rc ports: - port: 8081 targetPort: 8080
order-rc.yaml
apiVersion: v1 kind: ReplicationController metadata: name: order-rc namespace: ingress labels: name: order-rc spec: replicas: 1 selector: name: order-rc template: metadata: name: order-rc labels: name: order-rc spec: containers: - name: order-rc image: cnode-1:5000/ingress-service:v1.0 imagePullPolicy: IfNotPresent ports: - containerPort: 8080
order-svc.yaml
apiVersion: v1 kind: Service metadata: name: order-svc namespace: ingress spec: selector: name: order-rc ports: - port: 8082 targetPort: 8080
分別使用kubectl create 命令創建上述資源
(4)在ingress命名空間中創建ServiceAccount
創建ServiceAccount和ClusterRoleBinding,如果不創建的話,Ingress-controller會使用默認名為default的ServiceAccount,default權限很弱,獲取不到相應的資源信息,這樣Ingress-controller會啟動失敗。
--- apiVersion: v1 kind: ServiceAccount metadata: labels: name: ingress-sc name: ingress-sc namespace: ingress --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: ingress labels: name: ingress-crb roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: ingress-sc namespace: ingress
(5)創建Ingress Controller
apiVersion: v1 kind: ReplicationController metadata: name: nginx-ingress namespace: ingress labels: app: nginx-ingress spec: replicas: 1 template: metadata: name: nginx-ingress labels: app: nginx-ingress spec: serviceAccountName: ingress-sc containers: - name: nginx-ingress image: imagia/nginx-ingress-controller:0.32.0 imagePullPolicy: IfNotPresent env: - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace ports: - containerPort: 80 hostPort: 80 - containerPort: 443 hostPort: 443
創建完后查看:

【注】環境變量POD_NAME和POD_NAMESPACE是必須要設置的,不然會報錯。
(6)創建Ingress
也就是規則設置,將請求地址為/api/user轉發至user服務,請求地址為/api/order的轉發至order服務。
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: myweb-ingress namespace: ingress spec: rules: - host: myweb.com http: paths: - path: /api/user backend: serviceName: user-svc servicePort: 8081 - path: /api/order backend: serviceName: order-svc servicePort: 8082
創建完后查看:

可以看到,它的Hosts是myweb.com,物理機地址是192.168.197.120,端口為80.
如果ADDRESS列為空, 則通常說明Nginx未能正確連接到后端Service,需要排錯。
【注】為什么只有一個ip,是因為這個RC只有一個Pod,調度到cnode-2這台機運行,如果想要每台機器都有一個,建議使用DaemonSet類型的Controller
此時查看ingress-controller的Pod的日志:
先獲取Pod的名字

然后查看日志:


從上圖可以看到,當ingress創建之后,ingress-controller會自動去加載,然后生成對應的nginx的conf文件。
進入容器查看nginx的配置文件:文件是/etc/nginx/nginx.conf

(7)測試效果
由於並沒有上述設置的myweb.com這個域名,所以需要進行解析。
curl --resolve myweb.com:80:192.168.197.120 http://myweb.com:80/api/order
訪問order服務:

查看日志:可以看到是轉發到order-svc這個Service上了
192.168.197.100 - - [31/Oct/2020:07:40:46 +0000] "GET /api/order HTTP/1.1" 200 21 "-" "curl/7.29.0" 82 0.004 [ingress-order-svc-8082] [] 10.36.0.4:8080 21 0.004 200 0f7dfb1134644ee2ceb7a7d364ddfe45
訪問user服務:

查看日志:可以看到是轉發到user-svc這個Service上了
192.168.197.100 - - [31/Oct/2020:07:42:16 +0000] "GET /api/user HTTP/1.1" 200 20 "-" "curl/7.29.0" 81 0.011 [ingress-user-svc-8081] [] 10.44.0.3:8080 20 0.010 200 46f66ab540c41590a74bbea66bd65ea8
===============================
我是Liusy,一個喜歡健身的程序員。
歡迎關注【上古偽神】,一起交流Java技術及健身,一起成為Java大神。
來都來了,關注一波再溜唄。