# 一. 简介
Ingress 服务是为全局的、代理不同后端 Service 而设置的负载均衡服务。所以,所谓 Ingress,就是 Service 的 “Service”。
## 1.2 场景
Kubernetes 的 Service 只有四层代理,暂时只支持这样的格式:`IP:Port`访问。
而 `Ingress api` 支持实现七层代理,可以用来绑定外部域名。
## 1.2 组成
Ingress 由两部分组成:
- Ingress Controller
这是一个标准,可以有很多实现,例如下文的 `ingress-nginx`,它以pod形式运行的。
- IngressRule
以 yaml 形式为载体的一组声明式的策略,`ingress-controller` 会动态地按照策略生成配置文件(如:nginx.conf)
关于本文的项目的代码,都放于链接:[GitHub资源](https://github.com/wyattup/devops/tree/main/demos/kubernetes/Ingress)
# 二. IngressRule
`IngressRule` 的 `Key`,就叫做:`host`。它必须是一个标准的域名格式`(Fully Qualified Domain Name)`的字符串,而不能是 IP 地址。
## 2.1 案例
“demo-ingress.yaml” 文件内容如下:
```
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: demo-ingress
annotations:
# use the shared ingress-nginx
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- ingress.wyatt.plus
secretName: demo-secret
rules:
- host: ingress.wyatt.plus
http:
paths:
- path: /svc1
backend:
serviceName: svc-1
servicePort: 80
- path: /svc2
backend:
serviceName: svc-2
servicePort: 80
```
## 2.2 分析
上面的` IngressRule` 规则的定义,则依赖于 `path` 字段。这里的每一个 `path` 都对应一个后端 `Service`。所以在我们的例子里,定义了两个 `path`,它们分别对应 `svc-1` 和 `svc-2` 这两个 `Deployment` 的 `Service` 。
# 三. Ingress Controller
`Ingress Controller` 会根据定义的 Ingress 对象,提供对应的代理能力。目前,业界常用的各种反向代理项目,比如 Nginx、HAProxy、Envoy、Traefik 等,都已经为 Kubernetes 专门维护了对应的 Ingress Controller。
当一个新的 Ingress 对象由用户创建后,`nginx-ingress-controller` 就会根据 `Ingress` 对象里定义的内容,生成一份对应的 Nginx 配置文件`(/etc/nginx/nginx.conf)`,并使用这个配置文件启动一个` Nginx` 服务。而一旦 `Ingress` 对象被更新,`nginx-ingress-controller` 就会更新这个配置文件。
## 3.1 案例(mac平台)
对于 “deploy.yaml” 文件,我截取核心内容如下:
```yaml
# Source: ingress-nginx/templates/controller-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
helm.sh/chart: ingress-nginx-3.23.0
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/version: 0.44.0
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/component: controller
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
revisionHistoryLimit: 10
minReadySeconds: 0
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/component: controller
spec:
dnsPolicy: ClusterFirst
containers:
- name: controller
image: k8s.gcr.io/ingress-nginx/controller:v0.44.0@sha256:3dd0fac48073beaca2d67a78c746c7593f9c575168a17139a9955a82c63c4b9a
imagePullPolicy: IfNotPresent
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
args:
- /nginx-ingress-controller
- --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
- --election-id=ingress-controller-leader
- --ingress-class=nginx
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --validating-webhook=:8443
- --validating-webhook-certificate=/usr/local/certificates/cert
- --validating-webhook-key=/usr/local/certificates/key
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 101
allowPrivilegeEscalation: true
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: LD_PRELOAD
value: /usr/local/lib/libmimalloc.so
livenessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 5
readinessProbe:
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 3
ports:
- name: http
containerPort: 80
protocol: TCP
- name: https
containerPort: 443
protocol: TCP
- name: webhook
containerPort: 8443
protocol: TCP
volumeMounts:
- name: webhook-cert
mountPath: /usr/local/certificates/
readOnly: true
resources:
requests:
cpu: 100m
memory: 90Mi
nodeSelector:
kubernetes.io/os: linux
serviceAccountName: ingress-nginx
terminationGracePeriodSeconds: 300
volumes:
- name: webhook-cert
secret:
secretName: ingress-nginx-admission
```
# 3.2 分析
以看到,在上述 YAML 文件中,我们定义了一个使用 `nginx-ingress-controller` 镜像的 Pod。需要注意的是,这个 Pod 的启动命令需要使用该 Pod 所在的 `Namespace` 作为参数。而这个信息,当然是通过 `Downward API` 拿到的,即:Pod 的 `env` 字段里的定义`(env.valueFrom.fieldRef.fieldPath)`。
# 四. Demo
我们做一个如下的场景站点:https://ingress.wyatt.plus ,这是一个微服务系统。
- https://ingress.wyatt.plus/svc1,对应的是“微服务-1”
- https://ingress.wyatt.plus/svc2,对应的是“微服务-2”
这两个系统,分别由名叫 “svc-1” 和 “svc-2” 这样两个 Deployment 来提供服务。
由于每个平台配置文件有区别,所以我将按照mac平台的方式按照配置文件。[各平台的nginx controller配置](https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md)
## 4.1 按照 Ingress Controller
我使用了`Docker for mac`相关的配置,执行如下指令:
```shell
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.44.0/deploy/static/provider/cloud/deploy.yaml
```
## 4.2 配置Service与Deployment
### 4.2.1 demo-svc-1
该文件包了 Deployment 和 Service 这俩个配置,内容如下:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: svc-1
spec:
replicas: 2
selector:
matchLabels:
app: svc-1
template:
metadata:
labels:
app: svc-1
spec:
containers:
- name: svc-1
image: nginxdemos/hello:plain-text
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-1
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: svc-1
```
该配置通过 `label selector` 来进行匹配 `app: svc-1` 的 Pod。
### 4.2.2 demo-svc-2
该文件包了 Deployment 和 Service 这俩个配置,内容如下:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: svc-2
spec:
replicas: 3
selector:
matchLabels:
app: svc-2
template:
metadata:
labels:
app: svc-2
spec:
containers:
- name: svc-2
image: nginxdemos/hello:plain-text
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-2
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: svc-2
```
该配置通过 `label selector` 来进行匹配 `app: svc-2` 的 Pod。
## 4.3 创建Secret
创建 Secret 有2种方式,如下:
### 4.3.1 命令行创建
```
kubectl create secret tls demo-secret --key tls.key --cert tls.crt
```
### 4.3.2 以 `YAML`方式创建
```yaml
apiVersion: v1
kind: Secret
metadata:
name: demo-secret
type: Opaque
data:
tls.crt: **************************
tls.key: **************************
```
## 4.4 配置Ingress
注意 Ingress 的版本,最新的 API 变动较多,最新的为 `networking.k8s.io/v1`。
“demo-ingress.yaml” 配置如下:
```yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: demo-ingress
annotations:
# use the shared ingress-nginx
kubernetes.io/ingress.class: "nginx"
spec:
tls:
- hosts:
- ingress.wyatt.plus
secretName: demo-secret
rules:
- host: ingress.wyatt.plus
http:
paths:
- path: /svc1
backend:
serviceName: svc-1
servicePort: 80
- path: /svc2
backend:
serviceName: svc-2
servicePort: 80
```
当我们执行 `kubectl apply` 创建成功后,我们可以再通过如下指令查看 Ingress 情况。
```shell
kubectl get ingress
# result
NAME CLASS HOSTS ADDRESS PORTS AGE
demo-ingress <none> ingress.wyatt.plus localhost 80, 443 110m
```
## 4.5 检查
关于当前的 “demo-ingress” ,我们可以通过更详细的指令查看里面内容:
```shell
kubectl describe ingress demo-ingress
# result
Name: demo-ingress
Namespace: default
Address: localhost
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
demo-secret terminates ingress.wyatt.plus
Rules:
Host Path Backends
---- ---- --------
ingress.wyatt.plus
/svc1 svc-1:80 (10.1.3.124:80,10.1.3.126:80)
/svc2 svc-2:80 (10.1.3.123:80,10.1.3.125:80,10.1.3.127:80)
Annotations: kubernetes.io/ingress.class: nginx
```
可以看到,这个` Ingress` 对象最核心的部分,正是` Rules` 字段。其中,我们定义的 Host 是 `ingress.wyatt.plus`,它有两条转发规则`(Path)`,分别转发给 `/svc1` 和 `/svc2`。
实际执行结果如下:

## 4.6 验证
我们可以通过访问这个 Ingress 的地址和端口,访问到我们前面部署的应用,
我们使用浏览器可以访问 `https://ingress.wyatt.plus/svc1` 和 `https://ingress.wyatt.plus/svc2` 这俩个 url 。
我们将分别看到如下的俩个页面:
- svc1
```text
Server address: 10.1.3.124:80
Server name: svc-1-67c6fdbf4d-bwwlp
Date: 31/Mar/2021:22:53:06 +0000
URI: /svc1
Request ID: d37a5c0b9aaae3c71865ff7fcb3c615b
```
我们可以看到,访问这个 URL 得到的返回信息是:`Server name: svc-1-67c6fdbf4d-bwwlp` 。这正是 `svc-1` 这个 Deployment 的名字。
- svc2
```text
Server address: 10.1.3.127:80
Server name: svc-2-77f44d76b-bx77x
Date: 31/Mar/2021:22:54:13 +0000
URI: /svc2
Request ID: 17645ccb197fe3fb6daf6200aba289bf
```
我们可以看到,访问这个 URL 得到的返回信息是:`Server name: svc-2-77f44d76b-bx77x` 。这正是 `svc-2` 这个 Deployment 的名字。
可以确认 `Nginx Ingress Controller` 创建的 Nginx 负载均衡器,已经成功地将请求转发给了对应的后端 Service。
我们也可以在 Kubernetes Dashboard 里面查看 Ingress 对于的路径匹配规则,如下图:

# 五. Ingress部署方式
## 5.1 Deployment + LoadBalancer 模式的 Service
### 5.1.1 细节
如果要把 Ingress 部署在公有云,那可以选择这种方式。用 Deployment 部署 ingress-controller ,创建一个 type 为 `LoadBalancer` 的 Service 关联这组 pod 。大部分公有云,都会为 `LoadBalancer` 的 Service 自动创建一个负载均衡器,通常还绑定了公网地址。只要把域名解析指向该地址,就实现了集群服务的对外暴露。
缺点:
- 需要额外购买公有云的服务,与平台挂钩。
### 5.1.2 架构图

## 5.2 Deployment + NodePort 模式的 Service
### 5.2.1 细节
同样用 Deployment 模式部署 ingress-controller ,并创建对应的服务,但是 type 为 `NodePort`。这样,ingress 就会暴露在集群节点ip的特定端口上。由于 NodePort 暴露的端口是随机端口,一般会在前面再搭建一套负载均衡器来转发请求。该方式一般用于宿主机是相对固定的环境ip地址不变的场景。
缺点:
- NodePort 方式暴露 ingress 虽然简单方便,但是 NodePort 多了一层 NAT ,在请求量级很大时可能对性能会有一定影响。
- 请求节点会是类似 https://www.wyatt.plus:30076, 其中 `30076` 是 `kubectl get svc -n ingress-nginx` 的 svc 暴露出来的 NodePort 端口。
### 5.2.2 架构

## 5.3 DaemonSet + HostNetwork + nodeSelector
### 5.3.1 细节
用 `DaemonSet` 结合 `nodeSelector` 来部署 ingress-controller 到特定的node上,然后使用 `HostNetwork` 直接把该 pod 与宿主机 node 的网络打通,直接使用宿主机的 `80/433` 端口就能访问服务。这时,ingress-controller 所在的node机器就很类似传统架构的**边缘节点**,比如机房入口的nginx服务器。
优点:
- 该方式整个请求链路最简单,性能相对 NodePort 模式更好。
缺点:
- 由于直接利用宿主机节点的网络和端口,一个 node 只能部署一个 `ingress-controller` pod。
### 5.2.3 架构

# 六. 总结
`Ingress` 只能工作在七层,而 `Service` 只能工作在四层。所以当我们想要在 Kubernetes 里为应用进行 `TLS` 配置等 `HTTP` 相关的操作时,都必须通过` Ingress` 来进行。
有了 Ingress 这个抽象,我们就可以根据自己的需求来自由选择 Ingress Controller。我们只需要做很少的编程工作,就可以实现一个自己的` Ingress Controller`。
Ingress 带来的灵活度和自由度,对于使用容器时代来说,其实是非常有意义的。
欢迎收藏个人博客: [Wyatt's Blog](https://blog.wyatt.plus) ,非常感谢~
| 个人博客: [Wyatt's Blog](https://blog.wyatt.plus) | 个人公众号:Wyatt的成长之路 |
| :----------------------------------------------------------: | :----------------------------------------------------------: |
| <img src="https://os.wyatt.plus/blog/image_1616376288212.png" width = "200" height = "200" alt="微信号" align=center /> | <img src="https://os.wyatt.plus/blog/43_768_56a21dedcd68e50f951ed74cbe9ff627_26644997eca03ec8600b492ab6935dba_1616376836829.png" width = "200" height = "200" alt="公众号" align=center /> |
# Reference
https://kubernetes.github.io/ingress-nginx/
https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md#docker-for-mac
https://time.geekbang.org/column/article/69214?utm_campaign=guanwang&utm_source=baidu-ad&utm_medium=ppzq-pc&utm_content=title&utm_term=baidu-ad-ppzq-title
https://www.cnblogs.com/baoshu/p/13255909.html
https://github.com/kubernetes/ingress-nginx/

Kubernetes:Ingress剖析