Kubernetes之Ingress

[TOC]

参考

1 k8s 对外暴露服务的方法

向 k8s 集群外部暴露服务的方式有三种: nodePort,LoadBalancer 和本文要介绍的 Ingress。每种方式都有各自的优缺点,nodePort 方式在服务变多的情况下会导致节点要开的端口越来越多,不好管理。而 LoadBalancer 更适合结合云提供商的 LB 来使用,但是在 LB 越来越多的情况下对成本的花费也是不可小觑。Ingress 是 k8s 官方提供的用于对外暴露服务的方式,也是在生产环境用的比较多的方式,一般在云环境下是 LB + Ingress Ctroller 方式对外提供服务,这样就可以在一个 LB 的情况下根据域名路由到对应后端的 Service,有点类似于 Nginx 反向代理,只不过在 k8s 集群中,这个反向代理是集群外部流量的统一入口。

2 Ingress 及 Ingress Controller 简介

Ingress 是 k8s 资源对象,用于对外暴露服务,该资源对象定义了不同主机名(域名)及 URL 和对应后端 Service(k8s Service)的绑定,根据不同的路径路由 http 和 https 流量。而 Ingress Contoller 是一个 pod 服务,封装了一个 web 前端负载均衡器,同时在其基础上实现了动态感知 Ingress 并根据 Ingress 的定义动态生成 前端 web 负载均衡器的配置文件,比如 Nginx Ingress Controller 本质上就是一个 Nginx,只不过它能根据 Ingress 资源的定义动态生成 Nginx 的配置文件,然后动态 Reload。个人觉得 Ingress Controller 的重大作用是将前端负载均衡器和 Kubernetes 完美地结合了起来,一方面在云、容器平台下方便配置的管理,另一方面实现了集群统一的流量入口,而不是像 nodePort 那样给集群打多个孔。
image

所以,总的来说要使用 Ingress,得先部署 Ingress Controller 实体(相当于前端 Nginx),然后再创建 Ingress (相当于 Nginx 配置的 k8s 资源体现),Ingress Controller 部署好后会动态检测 Ingress 的创建情况生成相应配置。Ingress Controller 的实现有很多种:有基于 Nginx 的,也有基于 HAProxy的,还有基于 OpenResty 的 Kong Ingress Controller 等,更多 Controller 见:https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/,本文使用基于 Nginx 的 Ingress Controller:ingress-nginx

3 helm 部署 Nginx Ingress Controller

基于 Nginx 的 Ingress Controller 有两种,一种是 k8s 社区提供的 ingress-nginx,另一种是 Nginx 社区提供的 kubernetes-igress,关于两者的区别见 这里

在这里我们部署 k8s 社区提供的 ingress-nginx,Ingress Controller 对外暴露方式采用 hostNetwork,在裸机环境下更多其他暴露方式见:https://kubernetes.github.io/ingress-nginx/deploy/baremetal/
image

使用 Helm 官方提供的 Chart stable/nginx-ingress,修改 values 文件:

  • 使用 DaemonSet 控制器,默认是 Deployment:controller.kind 设为 DaemonSet;
  • pod 使用主机网络:controller.hostNetwork 设为 true;
  • 在hostNetwork 下 pod 使用集群提供 dns 服务:controller.dnsPolicy 设为 ClusterFirstWithHostNet;
  • Service 类型设为 ClusterIP,默认是 LoadBalancer:controller.service.type 设为 ClusterIP;
  • 默认后端镜像使用 docker hub 提供的镜像,Google 国内无法访问;

helm 部署(国内可以使用微软的helm源)

1
helm install stable/nginx-ingress --name nginx-ingress -f myValues.yaml

部署完成后我们可以看到 Kubernetes 服务中增加了 nginx-ingress-controllernginx-ingress-default-backend 两个服务。nginx-ingress-controllerIngress Controller,主要做为一个七层的负载均衡器来提供 HTTP 路由、粘性会话、SSL 终止、SSL直通、TCP 和 UDP 负载平衡等功能。nginx-ingress-default-backend 为默认的后端,当集群外部的请求通过 Ingress 进入到集群内部时,如果无法负载到相应后端的 Service 上时,这种未知的请求将会被负载到这个默认的后端上。nginx-ingress-default-backend 默认提供了两个 URL 进行访问,其中的 /healthz 用作健康检查返回 200,而 / 返回 404 错误。

4 使用 Ingress 对外暴露服务-7层

4.1 HTTP类型服务

为了快速体验 Ingress,下面部署一个 nginx 服务,然后通过 Ingress 对外暴露 nginx service 进行访问。
首先部署 nginx 服务:
Deployment + Service

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
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
---
kind: Service
apiVersion: v1
metadata:
name: nginx
spec:
selector:
app: nginx
ports:
- port: 80
targetPort: 80

接下来创建 Ingress 对外暴露 nginx service 80 端口:
ingress.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-nginx
annotations:
# use the shared ingress-nginx
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: nginx.kube.com
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80

说明:

  • kubernetes.io/ingress.class: “nginx”:Nginx Ingress Controller 根据该注解自动发现 Ingress;
  • host: nginx.kube.com:对外访问的域名;
  • serviceName: nginx:对外暴露的 Service 名称;
  • servicePort: 80:nginx service 监听的端口;

创建的 Ingress 必须要和对外暴露的 Service 在同一命名空间下!

4.2 HTTPS类型服务

通过 Ingress 访问 kubernetes dashboard(支持 HTTPS 访问)
首先,练习使用,先用自签名证书来代替吧:

1
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout kube-dashboard.key -out kube-dashboard.crt -subj "/CN=dashboard.kube.com/O=dashboard.kube.com"

使用生成的证书创建 k8s Secret 资源,下一步创建的 Ingress 会引用这个 Secret:

1
kubectl create secret tls kube-dasboard-ssl --key kube-dashboard.key --cert kube-dashboard.crt -n kube-system

创建 Ingress 资源对象(支持 HTTPS 访问):

kube-dashboard-ingress.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress-kube-dashboard
annotations:
# use the shared ingress-nginx
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
tls:
- hosts:
- dashboard.kube.com
secretName: kube-dasboard-ssl
rules:
- host: dashboard.kube.com
http:
paths:
- path: /
backend:
serviceName: kubernetes-dashboard
servicePort: 443

说明:

  • kubernetes.io/ingress.class: “nginx”:Inginx Ingress Controller 根据该注解自动发现 Ingress;
  • nginx.ingress.kubernetes.io/backend-protocol: Controller 向后端 Service 转发时使用 HTTPS 协议,这个注解必须添加,否则访问会报错
  • secretName: kube-dasboard-ssl:https 证书 Secret;
  • host: dashboard.kube.com:对外访问的域名;
  • serviceName: kubernetes-dashboard:集群对外暴露的 Service 名称;
  • servicePort: 443:service 监听的端口;

注意:创建的 Ingress 必须要和对外暴露的 Service 在同一命名空间下!

将域名 dashboard.kube.com绑定到 k8s 任意节点 ip 即可访问.

4.3 basic-auth认证

4.3.1 创建用户名密码

首先需要安装htpasswd二进制文件,通过htpasswd生成一个“auth”文件;用来存取我们创建的用户及加密之后的密码。

1
2
3
4
5
6
7
8
9
10
11
12
13
htpasswd -c auth user1
New password: <bar>
New password:
Re-type new password:
Adding password for user user1

htpasswd auth user2
2nd user:
htpasswd auth user2
New password: <bar>
New password:
Re-type new password:
Adding password for user user2

4.3.2 创建kubernetes secret来存储user/pass pairs

1
2
3
4
5
6
7
8
9
10
11
12
13
kubectl -n <namespace> create secret generic basic-auth --from-file=auth
secret "basic-auth" created


kubectl get secret basic-auth -o yaml
apiVersion: v1
data:
auth: Zm9vOiRhcHIxJE9DRzZYeWJcJGNrKDBGSERBa29YWUlsSDkuY3lzVDAK
kind: Secret
metadata:
name: basic-auth
namespace: default
type: Opaque

4.3.3 创建Ingress

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: prometheus
namespace: monitoring
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth #此secret需和ingress在用一namespace下
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required" #提示符
spec:
rules:
- host: prom.xxxxx.im
http:
paths:
- path: /
backend:
serviceName: prometheus-svc
servicePort: 9090

4.4 多路径及虚拟主机例子

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
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
rules:
- host: api.mydomain.com
http:
paths:
- backend:
serviceName: api
servicePort: 80
- host: domain.com
http:
paths:
- path: /web/*
backend:
serviceName: web
servicePort: 8080
- host: backoffice.domain.com
http:
paths:
- backend:
serviceName: backoffice
servicePort: 8080

5 使用 Ingress 对外暴露服务-4层

我们可以看到ingress nginx的args里有这两行:

1
2
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services

从选项和值可以猜测出,要想代理四层(tcp/udp),得写同namespace里一个名为tcp-serviceudp-service的两个configmap的数据 四层的话这边我们创建一个mysql的pod,来代理3306端口到集群外面,则需要写tcp-services这个configmap:

1
2
3
4
5
6
7
kind: ConfigMap
apiVersion: v1
metadata:
name: tcp-services
namespace: ingress-nginx
data:
3306: "default/mysql:3306"

四层写这两个ConfigMap的data即可,按照这样去写即可out_port: namespaces/svc_name:port,要给每个ingress加一些nginx里的配置可以查看官方的annotation字段以及值(traefik同理)。

如果我们用如上helm方式部署ingress-nginx的话

直接编辑它的vales.yaml文件,在TCP/UDP字段之后添加out_port: namespaces/svc_name:port即可,helm会自动帮你生成对应的configmap,并暴露对应的四层端口

其他例子

1
2
3
4
5
6
7
8
9
10
kind: ConfigMap
apiVersion: v1
metadata:
name: udp-services
namespace: ingress-nginx
data:
2200: "default/gitlab:22"
3306: "kube-public/mysql:3306"
2202: "kube-public/centos:22"
2203: "kube-public/mongodb:27017"
1
2
3
4
5
6
7
kind: ConfigMap
apiVersion: v1
metadata:
name: udp-services
namespace: ingress-nginx
data:
53: "kube-system/kube-dns:53"
--------------------本文结束,感谢您的阅读--------------------