Z.S.K.'s Records

Kubernetes(Nginx Ingress实践)

kubernetes集群内的服务, 在集群外是无法访问的, 想访问的话, 必须通过一种方式将请求代理进集群内部, ingress就是最为常用的手段, ingress也有许多开源方案,这里以ingress-nginx为例.Nginx Ingress 也是本人最常用集群外对访问方式.

首先会将nginx ingress部署到集群中,然后会聊了聊它的使用方式.

原理

Nginx ingress 本质上就是nginx+Lua, 所以使用过nginx的,其实对它的原理一看就明白.

首先看一下Nginx 的 反向代理模式

在 k8s 系统中,后端服务的变化是十分频繁的,单纯依靠人工来更新nginx 的配置文件几乎不可能,nginx-ingress 由此应运而生。Nginx-ingress 通过监视 k8s 的资源状态变化实现对 nginx 配置文件的自动更新

同样一看便知, 对于使用nginx ingress来说,知道它是怎么样的工作方式感觉就可以了,源码之类的其实没必要过多去解读.

总结 : Ingress-nginx分为3部分:

  • nginx-ingress-controller: 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段 Nginx 配置,再写到 Nginx-ingress-control的 Pod 里,这个 Ingress Contronler 的pod里面运行着一个nginx服务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中,然后 reload 一下 使用配置生效。以此来达到动态更新问题
  • Nginx: 反向代理服务
  • Ingress: ingress资源对象

部署

DaemonSet

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
labels:
app: ingress-nginx
name: nginx-ingress-controller
namespace: ingress-nginx
spec:
revisionHistoryLimit: 10
selector:
matchLabels:
app: ingress-nginx
template:
metadata:
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
labels:
app: ingress-nginx
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/os
operator: NotIn
values:
- windows
containers:
- args:
- /nginx-ingress-controller
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --annotations-prefix=nginx.ingress.kubernetes.io
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
image: rancher/nginx-ingress-controller:0.21.0-rancher3
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
name: nginx-ingress-controller
ports:
- containerPort: 80
hostPort: 80
name: http
protocol: TCP
- containerPort: 443
hostPort: 443
name: https
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
volumeMounts:
- mountPath: /etc/localtime
name: host-time
readOnly: true
dnsPolicy: ClusterFirst
hostNetwork: true
serviceAccount: nginx-ingress-serviceaccount
serviceAccountName: nginx-ingress-serviceaccount
terminationGracePeriodSeconds: 30
volumes:
- hostPath:
path: /etc/localtime
name: host-time

Default http backend

在新版的nginx ingress中,强制需要部署一个默认的后端, 用于处理无法匹配上的请求,就是一个nginx即可.

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
52
53
54
55
56
57
58
apiVersion: v1
kind: Service
metadata:
labels:
app: default-http-backend
name: default-http-backend
namespace: ingress-nginx
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8080
selector:
app: default-http-backend
sessionAffinity: None
type: ClusterIP
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
annotations:
labels:
app: default-http-backend
name: default-http-backend
namespace: ingress-nginx
spec:
selector:
matchLabels:
app: default-http-backend
template:
metadata:
labels:
app: default-http-backend
spec:
containers:
- image: rancher/nginx-ingress-controller-defaultbackend:1.4-rancher1
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 30
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
name: default-http-backend
ports:
- containerPort: 8080
protocol: TCP
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi

Ingress Rule

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: fxxk
namespace: default
spec:
rules:
- host: fxxk.com # 1. 指定绑定的域名
http:
paths:
- backend:
serviceName: auth # 3. 则转发到这个service的8082端口
servicePort: 8082
path: /fxxk # 2. 如果匹配到该条规则
- backend:
serviceName: auth2
servicePort: 8899
path: /client/v1

规则使用还是比较清晰的, 一看就明白, ingress支持很多个域名, 最终都会转换成nginx里的server的标准配置.

最后将上述资源apply 到集群中即可.

支持TCP/UDP

其实在Nginx 1.9之后的版本就已经支持了TCP转发了, 因此Nginx Ingress默认也是可以直接使用TCP转发的, 使用了Nginx Ingress这么久,从来都没有关注过这个点

查看了官方文档,使用也比较简单

首先,需要在nginx ingress controller 的daemonset中容器的启动参数增加对TCP/UDP的配置

1
2
3
4
5
6
7
8
9
spec:  
containers:
- args:
- /nginx-ingress-controller
- --default-backend-service=$(POD_NAMESPACE)/default-http-backend
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --annotations-prefix=nginx.ingress.kubernetes.io

--tcp-services-configmap 指定包含tcp的规则的cm, tcp-services这个名字也可以自定义,具体格式下面再说

--udp-services-configmap指定包含udp的规则的cm

当然,这个configmap可以在其它的namespace下, 比如放在 default的话,则是defalt/tcp-services

来看一下这个tcp-services的cm的内容,非常简单:

1
2
3
4
5
6
7
8
apiVersion: v1
data:
"9000": clickhouse-operator/clickhouse-1box:9000
# "9001": clickhouse-operator/clickhouse-1box:9001 # 可以同时暴露多个tcp端口
kind: ConfigMap
metadata:
name: tcp-services
namespace: ingress-nginx

要关注的只有data字段,格式为: out_port: namespaces/svc_name:port, 即out_port为需要对外暴露的tcp端口, 这个例子表示,需要对外暴露9000端口,通过9000端口进来的TCP请求请转发到ns: clickhouse-operator下的svc: clickhouse-1box的9000端口上

总共就需要这2步,就能够实现对外接收tcp请求了,是不是非常方便.

最后来看看在nginx pod中生成的配置,其实就是转换成了nginx 的stram

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 省略...
# TCP services
server {
preread_by_lua_block {
ngx.var.proxy_upstream_name="tcp-clickhouse-operator-clickhouse-1box-9000";
}

listen 9000;

proxy_timeout 600s;
proxy_pass upstream_balancer;

}

# UDP services

Annotation/Configmap

nginx本身强大的原因之一在于它的很多参数都是可以设置的, 在nginx ingress中也是如此,通过增加configmap及annotation可以自定义非常多的选项, 甚至可以使用自定义模板

比如: 我需要自定义nginx的日志,或者调整一些参数配置,那么就可以使用一个nginx-config的cm, 然后在daemonset中指定使用这个cm即可.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
data:
client-body-buffer-size: 10m
error-log-level: error
http-redirect-code: "301"
log-format-upstream: '{"time": "$time_iso8601", "remote_addr": "$proxy_protocol_addr",
"x-forward-for": "$proxy_add_x_forwarded_for", "request_id": "$request_id", "remote_user":
"$remote_user", "bytes_sent": $bytes_sent, "request_time": $request_time, "status":
$status, "vhost": "$host", "request_proto": "$server_protocol", "path": "$uri",
"request_query": "$args", "request_length": $request_length, "request_method":
"$request_method", "http_referrer": "$http_referer", "http_user_agent": "$http_user_agent"
}'
proxy-body-size: 10m
kind: ConfigMap
metadata:
labels:
app: ingress-nginx
name: nginx-configuration
namespace: ingress-nginx

其它的一些常用配置可参考以下:

annotations列表

configmap列表

问题

  1. controller启动时提示以下提示:

解决:

nginx-ingress-controller的daemonset中的启动命令中指定了ingress-class, 所以需要相对应

所以最好不要指定ingress-class, 不然以后想增加的时候都会需要与这个对应

  1. 关于--annotations-prefix=nginx.ingress.kubernetes.io

在编写ingress规则时经常需要定义一些annotations, annotations-prefix支持使用自定义前缀, 默认值为nginx.ingress.kubernetes.io,一般情况下不建议修改, 官方支持的annotations列表可参考

参考文章:

转载请注明出处https://izsk.me

Z.S.K. wechat
Scan Me To Read on Phone
I know you won't do this,but what if you did?