k8s之HTTP请求负载分发
一、导读
对于基于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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
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上。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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
1
2
3
4
|
spec: backend: serviceName: user servicePort: 8080 |
(2)同一域名,不同url转发到不同的服务上
比如域名都是myweb.com,/api/user转发到user服务,/api/order转发到 order服务。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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都能访问到后端的服务。
1
2
3
4
5
6
7
8
|
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文件内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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 |
使用命令创建镜像:
1
|
docker build ./ -t cnode-1:5000 /ingress-service :v1.0 |
镜像创建完成后记得上传到docker私有仓库
(3)在ingress命名空间启动相应的ReplicationController和Service
user-rc.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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
1
2
3
4
5
6
7
8
9
10
11
|
apiVersion: v1 kind: Service metadata: name: user-svc namespace: ingress spec: selector: name: user-rc ports: - port: 8081 targetPort: 8080 |
order-rc.yaml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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
1
2
3
4
5
6
7
8
9
10
11
|
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会启动失败。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
--- 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
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服务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
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这个域名,所以需要进行解析。
1
|
curl --resolve myweb.com:80:192.168.197.120 http: //myweb .com:80 /api/order |
访问order服务:
查看日志:可以看到是转发到order-svc这个Service上了
1
|
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上了
1
|
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,一个喜欢健身的程序员。