最近一直在看Kubernetes的相关问题, 对于本地搭建的集群, 我们需要Ingress 将服务暴露出去的. 在使用中我遇到了一些问题, 但是使用Google搜索并没有得到答案. 许多内容可能是自己探索得来, 使用方式有问题还请各位读者指出. 在调试时, 其实也阅读了Ingres相关的一部分实现代码, 感觉一篇文章可能写不下, 我会再开一篇.

!! 本文不会介绍怎么安装kubernetes集群, 请先安装好!!

Ingress在K8s中的作用

Ingress官方介绍中介绍了Ingress在K8s集群中的作用.

Ingres暴露了HTTP或是HTTPS路由, 允许集群外部访问集群内部的服务. 这部分的路由策略 是由Ingress资源来定义的.

如果大家之前有配置过Nginx多域名, 可以了解到, 对于不同的域名, 可以通过server_name来确认后端, Ingress的基本原理也是通过不同的server_name来选择对应的后端位置, 不过, 各种Ingress具体实现可能不太相同

1
2
3
4
5
6
7
8
9
10
server {
listen 80;
server_name app1.example.org;
...
}
server {
listen 80;
server_name app2.example.org;
...
}

我用过的两种Ingress实现

当我们平时讲到Ingress时, 我们讲到应该是Ingress服务, 或是Ingress API. 可以依照 这一套API有不同的实现, 我最近调研使用的实现有两种, 一种是Nginx公司(Nginx Inc)实现, 另一种是开源社区(Kubernetes)实现, 在使用时, 请一定搞清楚两种Ingress实现上的不同, 合理的选择Ingress.

Nginx公司的实现

https://github.com/nginxinc/kubernetes-ingress

Kubernetes社区的实现

https://github.com/kubernetes/ingress-nginx

几种Ingress的比较

原始表格来自nginx-ingress-controllers.md.

这是2019-11-05版本, 未来可能会不同, 请大家链接去看最新版.

Aspect or Feature kubernetes/ingress-nginx nginxinc/kubernetes-ingress with NGINX nginxinc/kubernetes-ingress with NGINX Plus
Fundamental
Authors Kubernetes community NGINX Inc and community NGINX Inc and community
NGINX version Custom NGINX build that includes several third-party modules NGINX official mainline build NGINX Plus
Commercial support N/A N/A Included
Load balancing configuration via the Ingress resource
Merging Ingress rules with the same host Supported Supported via Mergeable Ingresses Supported via Mergeable Ingresses
HTTP load balancing extensions - Annotations See the supported annotations See the supported annotations See the supported annotations
HTTP load balancing extensions – ConfigMap See the supported ConfigMap keys See the supported ConfigMap keys See the supported ConfigMap keys
TCP/UDP Supported via a ConfigMap Supported via a ConfigMap with native NGINX configuration Supported via a ConfigMap with native NGINX configuration
Websocket Supported Supported via an annotation Supported via an annotation
TCP SSL Passthrough Supported via a ConfigMap Not supported Not supported
JWT validation Not supported Not supported Supported
Session persistence Supported via a third-party module Not supported Supported
Canary testing (by header, cookie, weight) Supported via annotations Supported via custom resources Supported via custom resources
Configuration templates *1 See the template See the templates See the templates
Load balancing configuration via Custom Resources
HTTP load balancing Not supported See VirtualServer and VirtualServerRoute resources. See VirtualServer and VirtualServerRoute resources.
Deployment
Command-line arguments *2 See the arguments See the arguments See the arguments
TLS certificate and key for the default server Required as a command-line argument/ auto-generated Required as a command-line argument Required as a command-line argument
Helm chart Supported Supported Supported
Operational
Reporting the IP address(es) of the Ingress controller into Ingress resources Supported Supported Supported
Extended Status Supported via a third-party module Not supported Supported
Prometheus Integration Supported Supported Supported
Dynamic reconfiguration of endpoints (no configuration reloading) Supported with a third-party Lua module Not supported Supported

Ingress使用实践

下面的部分介绍了我使用Ingress的一些实践, 虽然比较简单, 但我觉得这是最普遍的一些用法了.

创建Ingress时选择集群中的部分机器

我们在创建Ingress的Pod时, 一个集群可能需要2-4个对外服务就可以了, 没有必要每个节点 上全都新建. 下面是创建该Pod时的部分配置, 可以注意到, 它是以DaemonSet形式部署的, 而这种形式的部署可以允许我们对应的机器的.

1
2
3
4
5
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ingress
namespace: nginx-ingress

节点的选择是通过nodeSelector这项配置进行的, 下面的配置中, 就是选择了run_ingress=true 的部分节点.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: nginx-ingress
namespace: nginx-ingress
spec:
selector:
matchLabels:
app: nginx-ingress
template:
metadata:
labels:
app: nginx-ingress
#prometheus.io/port: "9113"
spec:
nodeSelector:
run_ingress: "true"
serviceAccountName: nginx-ingress
...

节点的标记可以这样来做:

1
kubectl label node <node name> run_ingress="true"

基础的Ingress配置

在上述的结构中, 一个简单的服务, 一般会有几个pod+1个service+1个ingres组成

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 启动python3.7容器监听6000端口
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: arya-app
spec:
replicas: 2
selector:
matchLabels:
app: arya-app
template:
metadata:
labels:
app: arya-app
spec:
containers:
- name: myapp-container
image: python:3.7.4-stretch
command: ['python3', '-m', 'http.server', '6000']

# 创建对应的服务
---
apiVersion: v1
kind: Service
metadata:
name: arya-svc
spec:
selector:
app: arya-app
ports:
- protocol: TCP
port: 80
targetPort: 6000
name: http

# 创建服务相应的Ingress配置
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: arya-prod
spec:
rules:
- host: arya.example.com
http:
paths:
- path: /
backend:
serviceName: arya-svc
servicePort: 80

运行结果如下:

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
35
~ ❤  curl arya.example.com
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href=".dockerenv">.dockerenv</a></li>
<li><a href="bin/">bin/</a></li>
<li><a href="boot/">boot/</a></li>
<li><a href="dev/">dev/</a></li>
<li><a href="etc/">etc/</a></li>
<li><a href="home/">home/</a></li>
<li><a href="lib/">lib/</a></li>
<li><a href="lib64/">lib64/</a></li>
<li><a href="media/">media/</a></li>
<li><a href="mnt/">mnt/</a></li>
<li><a href="opt/">opt/</a></li>
<li><a href="proc/">proc/</a></li>
<li><a href="root/">root/</a></li>
<li><a href="run/">run/</a></li>
<li><a href="sbin/">sbin/</a></li>
<li><a href="srv/">srv/</a></li>
<li><a href="sys/">sys/</a></li>
<li><a href="tmp/">tmp/</a></li>
<li><a href="usr/">usr/</a></li>
<li><a href="var/">var/</a></li>
</ul>
<hr>
</body>
</html>

使用Ingress做灰度发布(canary)

平时我们的使用中也会需要灰度发布这一功能, 在调查之后发现, Nginx公司的Ingress实现 并不能直接进行灰度发布, 需要自定义资源的支持. 因此选择了Kubernetes社区的Ingress. 经过逐步的使用才发现, 这两套Ingress的实现是不同的, Nginx公司使用了普通的Nginx, 而Kubernetes使用的是OpenResty, 具体的不同我会在下一篇博客中介绍, 本此仅介绍canary的使用.

详细配置请查看canary官方文档

灰度发布需要两个版本的程序, 并且其中一个版本为线上版本, 另一个版本还未上线. 这两个版本分别对应不同的service与ingress配置, 我们先来看线上版本.

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
# prod_arya.yaml 线上版本对应的是753
---
apiVersion: v1
kind: Service
metadata:
name: arya-prod
spec:
selector:
app: arya-v753
ports:
- protocol: TCP
port: 80
targetPort: 6000
name: http

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: arya-prod
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: arya.example.com
http:
paths:
- path: /
backend:
serviceName: arya-prod
servicePort: 80

需要灰度的版本, 请注意: 两者的host也就是对外服务的域名需要是一致的, 不一致的地方是 annotations中增加了相关配置.

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
35
# canary_arya.yaml
---
apiVersion: v1
kind: Service
metadata:
name: arya-develop-canary
spec:
selector:
app: arya-v754
ports:
- protocol: TCP
port: 80
targetPort: 6000
name: http

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: arya-prod-canary
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20"
nginx.ingress.kubernetes.io/ssl-redirect: "false"
nginx.ingress.kubernetes.io/canary-by-header: canary
spec:
rules:
- host: arya.example.com
http:
paths:
- path: /
backend:
serviceName: arya-develop-canary
servicePort: 80

如果依照此配置, 那么可以用一下命令测试:

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
35
36
37
38
39
# 直接请求, 请求随机到达两个版本的环境中
~ ❤ for i in `seq 10`;do curl -s arya.example.com/version;done
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}

# 修改http header
# 754为新版, 表明所有流量均指向新环境
~ ❤ for i in `seq 10`;do curl -sL -H "canary: always" arya.example.com/version;done
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}
{"result":{"version_id":"754"}}

# 753为当前在线的版本, 下面表明流量全部到旧的环境中
~ ❤ for i in `seq 10`;do curl -sL -H "canary: never" arya.example.com/version;done
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}
{"result":{"version_id":"753"}}

总结

本篇博客主要就Ingress在Kubernetes中的作用, 以及一些实践工作做了介绍, 下一篇有关Ingress 的博客将会介绍两种Ingress实现(Nginx公司与Kubernetes社区)的不同, 并针对OpenResty 的使用做更为细致的探索.