learn-tech/专栏/Kubernetes从上手到实践/22服务增强:Ingress.md
2024-10-16 06:37:41 +08:00

491 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

因收到Google相关通知网站将会择期关闭。相关通知内容
22 服务增强Ingress
整体概览
通过前面的学习,我们已经知道 K8S 中有 Service 的概念,同时默认情况下还有 CoreDNS 完成集群内部的域名解析等工作,以此完成基础的服务注册发现能力。
在第 7 节中,我们介绍了 Service 的 4 种基础类型,在前面的介绍中,我们一般都在使用 ClusterIP 或 NodePort 等方式将服务暴露在集群内或者集群外。
本节,我们将介绍另一种处理服务访问的方式 Ingress。
Ingress 是什么
通过 kubectl explain ingress 命令,我们来看下对 Ingress 的描述。
Ingress is a collection of rules that allow inbound connections to reach the endpoints defined by a backend. An Ingress can be configured to give services externally-reachable urls, load balance traffic, terminate SSL, offer name based virtual hosting etc.
Ingress 是一组允许外部请求进入集群的路由规则的集合。它可以给 Service 提供集群外部访问的 URL负载均衡SSL 终止等。
直白点说Ingress 就类似起到了智能路由的角色,外部流量到达 Ingress ,再由它按已经制定好的规则分发到不同的后端服务中去。
看起来它很像我们使用的负载均衡器之类的。那你可能会问Ingress 与 LoadBalancer 类型的 Service 的区别是什么呢?
Ingress 不是一种 Service 类型
Ingress 是 K8S 中的一种资源类型,我们可以直接通过 kubectl get ingress 的方式获取我们已有的 Ingress 资源。
Ingress 可以有多种控制器(实现)
通过之前的介绍,我们知道 K8S 中有很多的 Controller (控制器),而这些 Controller 已经打包进了 kube-controller-manager 中,通过 --controllers 参数控制启用哪些。
但是 Ingress 的 Controller 并没有包含在其中,而且有多种选择。
由社区维护(或是说官方支持的)有两个:适用于 Google Cloud 的 GLBC当你使用 GKE 的时候,便会看到它;和 NGINX Ingress Controller 它是使用 ConfigMap 存储 NGINX 配置实现的。
第三方的实现还有:基于 Envoy 的 Contour; F5 的 F5 BIG-IP Controller; 基于 HAProxy 的 haproxy-ingress; 基于 Istio 的 Control Ingress Traffic; 现代化的反向代理服务器 Traefik; 以及 Kong 支持的 Kong Ingress Controller for Kubernetes 和 NGINX 官方支持的 NGINX Ingress Controller。
这里可以看到 K8S 社区和 NGINX 都有 NGINX Ingress Controller很多人在一开始接触 Ingress 的时候便陷入了选择的苦恼中,除去前面的那些选择外,单 NGINX 的控制器就有两个,到底应该怎么选。
这里提供两点建议:
可能多数人使用的都是 NGINX 而非 NGINX Plus如果你需要会话保持Session persistence的话那你应该选择 K8S 社区维护的版本
即使我们平时使用 NGINX 的时候,也常常会有动态配置的需求,如果你仍然有这样的需求,那你还是继续使用 K8S 社区维护的版本(其中内置了 Lua 支持)。
如何使用
前面也已经说到了,单纯的创建一个 Ingress 资源没什么意义,我们需要先配置一个 Controller ,才能让它正常工作。国内使用 GKE 的可能不是很多,为了更加通用,这里我们选择 K8S 社区维护的 NGINX Ingress Controller。
安装
整个安装过程其实也比较简单,具体步骤如下(以下步骤中都将直接展示该步骤所需的 YAML 配置文件):
创建 Namespace
apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
将以上内容保存为 namespace.yaml 文件,然后执行 kubectl apply -f namespace.yaml 即可。以下步骤均类似,不再赘述。 注意:这里创建 Namespace 只是为了保持集群相对规范,非强制,但推荐此做法。
创建 ConfigMap
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: tcp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: udp-services
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
这里创建了几个 ConfigMap主要是给 Controller 使用。
由于我们的集群使用 kubeadm 创建时,默认开启了 RBAC ,所以这里需要相应的创建对应的 Role 和 RoleBinding。
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: nginx-ingress-clusterrole
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- nodes
- pods
- secrets
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- apiGroups:
- "extensions"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- "extensions"
resources:
- ingresses/status
verbs:
- update
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
name: nginx-ingress-role
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
rules:
- apiGroups:
- ""
resources:
- configmaps
- pods
- secrets
- namespaces
verbs:
- get
- apiGroups:
- ""
resources:
- configmaps
resourceNames:
- "ingress-controller-leader-nginx"
verbs:
- get
- update
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
name: nginx-ingress-role-nisa-binding
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: nginx-ingress-role
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: nginx-ingress-clusterrole-nisa-binding
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-clusterrole
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
关于 RBAC 相关的内容,可查看第 8 节 《安全重点: 认证和授权》,了解此处的配置及其含义。
部署 NGINX Ingress Controller
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
template:
metadata:
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
annotations:
prometheus.io/port: "10254"
prometheus.io/scrape: "true"
spec:
serviceAccountName: nginx-ingress-serviceaccount
containers:
- name: nginx-ingress-controller
image: taobeier/nginx-ingress-controller:0.21.0
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/nginx-configuration
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
- --publish-service=$(POD_NAMESPACE)/ingress-nginx
- --annotations-prefix=nginx.ingress.kubernetes.io
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# www-data -> 33
runAsUser: 33
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
注意,这里的镜像是我从官方镜像直接同步的,为了解决国内无法下载镜像的情况。
另外,在启动参数中,指定了我们第二步中创建的 ConfigMap 。以及,在此部署中,用到了之前尚未详细说明的 readinessProbe 和 livenessProbe我们之前在详解 kubelet 时,大致提到过关于它所具备的职责,这两个配置主要是用于做探针,用户检查 Pod 是否已经准备好接受请求流量和是否存活。
这里还进行了 annotations 里面标注了关于 Prometheus 的相关内容,我们会在下节中描述。
master $ kubectl -n ingress-nginx get all
NAME READY STATUS RESTARTS AGE
pod/nginx-ingress-controller-6f647f7866-659ph 1/1 Running 0 75s
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
deployment.apps/nginx-ingress-controller 1 1 1 1 75s
NAME DESIRED CURRENT READY AGE
replicaset.apps/nginx-ingress-controller-6f647f7866 1 1 1 75s
可以看到 NGINX Ingress Controller 已经部署成功。
将 NGINX Ingress Controller 暴露至集群外
经过前面的介绍,我们已经知道 Ingress 的作用在于将集群外的请求流量转向集群内的服务,而我们知道,默认情况下集群外和集群内是不互通的,所以必须将 NGINX Ingress Controller 暴露至集群外,以便让其能接受来自集群外的请求。
将其暴露的方式有很多种,这里我们选择我们之前已经介绍过的 NodePort 的方式。选择它主要有以下原因:
我们可以使用纯的 LB 实现完成服务暴露,比如 MetalLB但它还处于 Beta 阶段,尚未有大规模生产环境使用的验证。
我们可以直接使用宿主机的网络,只需设置 hostNetwork: true 即可,但这个方式可能会带来安全问题。
我们可以选择 External IPs 的方式,但这种方式无法保留请求的源 IP所以并不是很好。
其实我们一般会选择自己提供边缘节点的方式,不过这种方式是建立在 NodePort 的方式之上,并且需要提供额外的组件,此处就暂不做展开了。
我们使用以下的配置,将 NGINX Ingress Controller 暴露至集群外
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
selector:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
创建该 Service。
master $ kubectl -n ingress-nginx get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.0.38.53 <none> 80:30871/TCP,443:30356/TCP 11s
现在,我们直接访问 Node:Port 便可访问到该 Controller。
master $ curl 172.17.0.3:30871
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.15.6</center>
</body>
</html>
由于我们并没有设置任何的默认响应后端,所以当直接请求时,便返回 404 。
实践
将我们的示例项目 SayThx 通过 Ingress 的方式进行访问。
该示例项目的部署,不再进行赘述。可在 ingress 分支 查看此处所需配置。
在我们将 NGINX Ingress Controller 及 SayThx 项目部署好之后,我们使用以下的配置创建 Ingress 资源。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: saythx-ing
namespace: work
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: saythx.moelove.info
http:
paths:
- path: /
backend:
serviceName: saythx-frontend
servicePort: 80
创建
master $ kubectl apply -f ingress.yaml
ingress.extensions/saythx-ing created
master $ kubectl -n work get ing
NAME HOSTS ADDRESS PORTS AGE
saythx-ing saythx.moelove.info 80 23s
验证
这里来解释下刚才的配置文件。首先,指定了 host: saythx.moelove.info 表示我们想要以 saythx.moelove.info 这个域名来访问它。path 直接写 / 表示所有的请求都转发至名为 saythx-frontend 的服务。
与我们平时使用 NGINX 基本一致。 现在编辑本地的 HOSTS 文件绑定 Node 的IP 与 saythx.moelove.info 这个域名。使用浏览器进行访问 saythx.moelove.info:刚才 Controller 使用 NodePort 暴露服务时的端口:
可以看到已经成功访问。
总结
在本节中,我们介绍了 Ingress 的基本情况,了解了它是 K8S 中的一种资源对象,主要负责将集群外部流量与集群内服务的通信。但它的正常工作离不开 Ingress Controller ,当前官方团队维护的主要有两个 GLBC 和 NGINX Ingress Controller。
我们大致介绍了现有的 Controller 实现,也实践了如何部署 NGINX Ingress Controller 以及如何使用 Ingress 将我们的示例项目暴露至集群外。
NGINX Ingress Controller 的使用,比较符合我们平时使用 NGINX 的习惯,相对来说也比较灵活,后续可看实际情况再进行更多的实践。
至此K8S 集群的管理,相关原理以及服务的部署等内容就基本介绍完毕。下节,我们将介绍生产实践中至关重要的一环 监控 相关的实践。