一、什么是Ingress
为什么需要 Ingress:
Ingress 也是为了解决在集群之外,访问集群内部Service服务的问题。
实际上,将service的type设置为nodePort或LoadBalancer,也能实现将集群内部的服务暴露给外部访问。那Ingress岂不是多余的?肯定不是。
先回顾下,LoadBalancer类型的Service访问流程,如下:
.————————————————————————————————————————————————
| 集群内部 .-------> Pod |
| / |
集群外部(用户) ----> LoadBalancer -----+--> node_ip:nodePort ----> Service |
| \ |
| '-------> Pod |
| |
'————————————————————————————————————————————————
LoadBalancer 类型的 Service 一般应用于云平台环境中,这个LoadBalancer是由云厂商提供的,位于k8s集群外部,当用户在云平台上创建一个该类型的Service时,同时会为该 Service 创建一个对应的负载均衡器。可以发现,每个 Service 都要有一个负载均衡器,这种做法实际上是成本比较高的。
Ingress除了能将集群内部的服务暴露给外界访问,它也是一个负载均衡器,专门是为集群内部的Service提供负载均衡服务。
Ingress 与 LoadBalancer 类型的 Service 有点类似,但区别在于,Ingress是一个k8s集群内部的全局的负载均衡器,作为k8s集群中的一个对象资源而存在,而LoadBalancer类型则是一个集群外部的负载均衡服务器。
Ingress 工作原理:
Ingress,是K8s的一个资源对象,定义了一系列路由转发规则(或反向代理规则)。它规定了外部进来的HTTP/HTTPS请求,应该被转发到哪个Service上。
具体说,Ingress就是一段nginx服务的反向代理配置,它能根据请求中不同的Host和URL路径,将请求转发到不同的Service所对应的后端Pod上。
Ingress Controller相当于是一个反向代理程序,负责解析Ingress的反向代理规则,并实时感知Ingress转发规则的变化。一旦Ingress规则有变化,Ingress Controller会及时更新自己相应的转发规则,并根据这些规则,将接收到的请求转发到对应的Service。
Ingress Controller是通过API Server获取Ingress资源的变化,并动态地生成Load Balancer(如Nginx程序)所需的配置文件(如nginx.conf),然后重新加载Load Balancer(如执行nginx -s load
重新加载Nginx)来生成新的路由转发规则。
Ingress与手动部署反向代理服务器的思路是一样的。如果反向代理服务器使用的是Nginx程序,Ingress Controller 会将 Ingress 规则变化生成一段Nginx的配置,然后将这个配置写到Ingress Controller管理下的Nginx Pod中,然后reload。
二、安装 Nginx Ingress
部署 Ingress :
Ingress组件需要提前部署到k8s集群中,它包括 Ingress Service 与 Ingress Controller,以及Ingress Controller管理下的两个Nginx Pod。反向代理服务器有Nginx、Apache、traefik等,这里选择用Nginx。
官网部署手册:https://github.com/kubernetes/ingress-nginx/blob/main/docs/deploy/index.md
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.1/deploy/static/provider/cloud/deploy.yaml
当部署好后,在环境中能看到有一个Ingress Service、Ingress Controller、Nginx Pod,如下:
查看Ingress Service:
# 该Service用的是LoadBalancer方式对外暴露服务(也可以用nodePort方式)
$ kubectl -n kube-system get svc nginx-ingress-lb
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-lb LoadBalancer 172.17.216.4 10.10.33.4 80:31366/TCP,443:30489/TCP 487d
$ kubectl -n kube-system describe svc nginx-ingress-lb
Name: nginx-ingress-lb
Namespace: kube-system
Labels: app=nginx-ingress-lb
Annotations: .....
Selector: app=ingress-nginx
Type: LoadBalancer
IP: 172.17.216.4
LoadBalancer Ingress: 10.10.33.4
Port: http 80/TCP
TargetPort: 80/TCP
NodePort: http 31366/TCP
Endpoints: 172.16.1.130:80,172.16.1.131:80
Port: https 443/TCP
TargetPort: 443/TCP
NodePort: https 30489/TCP
Endpoints: 172.16.1.130:443,172.16.1.131:443
Session Affinity: None
External Traffic Policy: Local
HealthCheck NodePort: 30135
Events: <none>
查看Ingress Controller:
$ kubectl -n kube-system get deploy nginx-ingress-controller
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-ingress-controller 2 2 2 2 487d
查看Ingress Controller下的两个Nginx Pod:
$ kubectl -n kube-system get pods -owide -l app=ingress-nginx
NAME READY STATUS RESTARTS AGE IP NODE
nginx-ingress-controller-787644849b-nmsbm 1/1 Running 0 487d 172.16.1.130 node1
nginx-ingress-controller-787644849b-tn7bq 1/1 Running 0 487d 172.16.1.131 node1
Ingress 使用:
Ingress能将来自于不同域名的请求转发到不同的Service上,也可以将同一个域名下的请求,根据不同URI接口转发到不同的Service上。
Ingress对象字段说明:
spec:
backend: 全局默认后端。若不匹配任何规则的请求,都会代理到默认后端
serviceName: service的名称。要将请求代理到哪个Service上
servicePort: service的端口
rules: 定义入站流量的转发规则
host: 第一条规则,检查入站流量是否匹配此处定义的域名
http:
paths:
path: 第二条规则,检查入站流量的URI路径,是否匹配此处定义的path(省略即为"/")
backend: 自定义后端。所有入站流量都会先匹配host与path规则,当两个都匹配时,才会将流量代理到下面定义的service上,否则会发送到默认backend
serviceName:
servicePort:
tls: 配置HTTPS
示例1:根据不同域名转发到不同的service上
1)先创建两个service,分别是my-service-1和my-service-2。
my-service-1:
apiVersion: v1
kind: Service
metadata:
name: my-service-1
namespace: default
spec:
selector:
app: my-nginx-web1
ports:
- name: nginx
port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx-deploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: my-nginx-web1
template:
metadata:
labels:
app: my-nginx-web1
spec:
containers:
- name: nginx-web1
image: nginx:1.7.9
ports:
- name: http
containerPort: 80
my-service-2:
apiVersion: v1
kind: Service
metadata:
name: my-service-2
namespace: default
spec:
selector:
app: my-nginx-web2
ports:
- name: nginx
port: 8080
targetPort: 80
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx-deploy-2
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: my-nginx-web2
template:
metadata:
labels:
app: my-nginx-web2
spec:
containers:
- name: nginx-web2
image: nginx:1.7.9
ports:
- name: http
containerPort: 80
2)创建一个Ingress规则,根据不同域名转发到不同的service上
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-demo-0915
namespace: default
spec:
rules:
- host: my.test.com #将来自于my.test.com域名的请求代理到my-service-1上
http:
paths:
- backend:
serviceName: my-service-1
servicePort: 80
- host: a.test.com #将来自于a.test.com域名的请求代理到my-service-2上
http:
paths:
- backend:
serviceName: my-service-2
servicePort: 8080
3)查看service与pod信息
#查看两个service
$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
my-service-1 ClusterIP 172.17.171.2 <none> 80/TCP 35m app=my-nginx-web1
my-service-2 ClusterIP 172.17.101.107 <none> 8080/TCP 59m app=my-nginx-web2
$ kubectl describe svc my-service-1
Name: my-service-1
Namespace: default
Labels: <none>
Annotations: ...
Selector: app=my-nginx-web1
Type: ClusterIP
IP: 172.17.171.2
Port: nginx 8080/TCP
TargetPort: 80/TCP
Endpoints: 172.16.1.150:80,172.16.2.156:80
Session Affinity: None
Events: <none>
$ kubectl describe svc my-service-2
Name: my-service-2
Namespace: default
Labels: <none>
Annotations: ...
Selector: app=my-nginx-web2
Type: ClusterIP
IP: 172.17.101.107
Port: nginx 8080/TCP
TargetPort: 80/TCP
Endpoints: 172.16.1.151:80
Session Affinity: None
Events: <none>
#查看service的Pod
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE
my-nginx-deploy-6fb9f56545-8k4mk 1/1 Running 0 87s 172.16.2.156 node1 <none>
my-nginx-deploy-6fb9f56545-c7gcx 1/1 Running 0 87s 172.16.1.150 node2 <none>
my-nginx-deploy-2-7f47d46589-mnpbl 1/1 Running 0 67m 172.16.1.151 node2 <none>
查看Ingress信息:
# 查看ingress
$ kubectl get ingress ingress-demo-0915
NAME HOSTS ADDRESS PORTS AGE
ingress-demo-0915 my.test.com,a.test.com 10.10.33.4 80 3h14m
$ kubectl describe ingress ingress-demo-0915
Name: ingress-demo-0915
Namespace: default
Address: 10.10.33.4
Default backend: default-http-backend:80 (<none>)
Rules:
Host Path Backends
---- ---- --------
my.test.com
my-service-1:80 (<none>)
a.test.com
my-service-2:8080 (<none>)
Annotations:
nginx.ingress.kubernetes.io/service-weight: my-service-1: 100, my-service-2: 100
....
Events: <none>
4)访问测试
因为我这里的Ingress Service使用的是LoadBalancer类型,所以需要通过LoadBalancer的IP访问Ingress,这个要先在本地/etc/hosts文件中绑定好域名解析后再访问。
$ echo '10.10.33.4 a.test.com my.test.com' >> /etc/hosts
$ curl a.test.com
$ curl my.test.com
示例2:根据同一个域名的不同请求接口转发到不同的service上
Ingress的yaml如下:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/service-weight: 'my-service-1: 100, my-service-2: 100'
name: ingress-demo-0915-2
namespace: default
spec:
rules:
- host: b.test.com
http:
paths:
- backend:
serviceName: my-service-1
servicePort: 80
path: /web1
- backend:
serviceName: my-service-2
servicePort: 8080
path: /web2
查看Ingress信息:
$ kubectl get ingress ingress-demo-0915-2
NAME HOSTS ADDRESS PORTS AGE
ingress-demo-0915-2 b.test.com 10.10.33.4 80 70m
$ kubectl describe ingress ingress-demo-0915-2
Name: ingress-demo-0915-2
Namespace: default
Address: 10.10.33.4
Default backend: default-http-backend:80 (<none>)
Rules:
Host Path Backends
---- ---- --------
b.test.com
/web1 my-service-1:80 (<none>)
/web2 my-service-2:8080 (<none>)
Annotations:
nginx.ingress.kubernetes.io/service-weight: my-service-2: 100, my-service-1: 100
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal UPDATE 53m (x8 over 71m) nginx-ingress-controller Ingress default/ingress-demo-0915-2
Normal UPDATE 53m (x8 over 71m) nginx-ingress-controller Ingress default/ingress-demo-0915-2
访问测试:
因为我这里的Ingress Service使用的是LoadBalancer类型,所以需要通过LoadBalancer的IP访问Ingress,这个要先在本地/etc/hosts文件中绑定好域名解析后再访问。
$ echo '10.10.33.4 b.test.com' >> /etc/hosts
$ curl b.test.com/web1
$ curl b.test.com/web2
示例3:配置HTTPS
1、登录master节点, 创建私钥(tls.key)和证书(tls.crt)
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/O=DevOps/CN=my.test.com"
2、创建一个Secret
$ kubectl create secret tls my-ingress-secret --cert=tls.crt --key=tls.key
$ kubectl get secret my-ingress-secret
NAME TYPE DATA AGE
my-ingress-secret kubernetes.io/tls 2 13s
$ kubectl describe secret my-ingress-secret
Name: my-ingress-secret
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1302 bytes
tls.key: 1679 bytes
3、创建Deployment和Service
apiVersion: apps/v1
kind: Deployment
metadata:
name: test-web1
labels:
app: test-web1
spec:
replicas: 1
selector:
matchLabels:
app: test-web1
template:
metadata:
labels:
app: test-web1
spec:
containers:
- name: test-web1
image: nginx:1.7.9
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: web1-service
spec:
type: ClusterIP
selector:
app: test-web1
ports:
- port: 8080
targetPort: 8080
4、创建一个ingress,并配置tls
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress-tls
namespace: default
spec:
tls:
- hosts:
- my.test.com
secretName: my-ingress-secret
rules:
- host: my.test.com
http:
paths:
- path:
backend:
serviceName: web1-service
servicePort: 8080
查看ingress
$ kubectl get ingress my-ingress-tls
NAME HOSTS ADDRESS PORTS AGE
my-ingress-tls my.test.com 10.10.33.4 80, 443 53s
$ kubectl describe ingress my-ingress-tls
Name: my-ingress-tls
Namespace: default
Address: 10.10.33.4
Default backend: default-http-backend:80 (<none>)
TLS:
my-ingress-secret terminates my.test.com
Rules:
Host Path Backends
---- ---- --------
my.test.com
web1-service:8080 (<none>)
Annotations:
.....
5、用https协议访问测试
$ curl https://my.test.com