ingress和ingressController
ingress是一个API,是一个规则,是个虚拟的东西
kubernetes提供service我们常用两种方式ClusterIP和NodePort,另外还有LoadBalance类型和ExternalName类型(目前这两种没用过,不做阐述)。ClusterIP是在集群内部用service的IP加端口来访问服务;而NodePort可以跨主机访问,只要能ping通k8s节点就能访问而不必局限在本集群环境,使用集群内部的节点IP加端口来访问,这种方式非常方便,但当有几十上百的服务在集群中运行时,NodePort的端口管理就比较困难。k8s还提供了一种集群维度暴露服务的方式,也就是ingress。Ingress 不是一种服务类型,ingress可以简单理解为service的service,它充当集群的入口点。 它可以将路由规则整合到一个资源中,因为它可以在同一IP地址下公开多个服务。
需要知道的是,ingress只是个规则,你必须具有 IngressController 才能满足 Ingress 的要求。仅创建 Ingress资源本身没有任何效果。下面是一个官网上的ingress示例的修改版,我把其中host可选项去掉了,如果没有host的话就适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了 host(例如 foo.bar.com),则 rules 适用于该 host.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-fanout-example
spec:
rules:
- http:
paths:
- path: /foo
pathType: Prefix
backend:
service:
name: service1
port:
number: 4200
- path: /bar
pathType: Prefix
backend:
service:
name: service2
port:
number: 8080
这个表示访问curl http://foo访问的是服务service1:4200端口,curl http://bar访问的是服务service2:8080端口。
Ingress Controller是需要用户自己实现的
Ingress是一个非常“极客”并需要DIY的产物,kubernetes只负责提供一个API定义,具体的IngressController需要用户自己实现!官方提供了Nginx Ingress Controller等其他几种Controller示例共开发者参考。实现一个IngressController大致框架是:List/Watch Kubernetes 的Service、Endpoints、Ingress对象,并根据这些信息刷新外部Load Balancer(例如nginx)的规则和配置。值得注意的是,如果想要通过域名访问Ingress,则需要用户自己配置域名和IngressIP的配置关系,例如host文件、自己的DNS(不是kube-dns). 下图是nginx官网给出的nginx-ingress-controller架构图
ingress和ingressController创建实例
Talk is cheap,show me the code.下面说一下我创建ingress和ingressController创建的过程,以及在这个过程中看到的一些思考和问题。
创建ingress
我使用如下YAML文件创建了一个ingress, 创建完之后除了可以kubectl get ingress有输出之外,不会有任何服务或者容器启动。只是创建了一个规则而已。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
spec:
ingressClassName: nginx-ingress-controller
rules:
- host: test-ko-mix-master-1
http:
paths:
- path: /index.html
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
- path: /bar
pathType: Prefix
backend:
service:
name: nginx2
port:
number: 80
注意,这里的http前面没有横杠,这里没有namespace,ingressClassName我暂且理解为指明你想用哪个ingressController,如果你没有定义一个class,那么你的云provider会使用缺省的ic.用这个创建之后查看ingress,得到如下结果:
[root@test-ko-mix-master-1 20211109]# kubectl get ingress -A -o wide
NAMESPACE NAME CLASS HOSTS ADDRESS PORTS AGE
default nginx-ingress nginx-ingress-controller test-ko-mix-master-1 80 2s
这里,ADDRESS一栏是空的(后面设定了service后会更新为service的IP),如果没有设定ingressClassName那么CLASS这一栏是none.
创建nginx-ingress-controller
这个nginx-ingress-controller是官方写的一个ic示例,如果你需要controller其他的资源需要自己去开发,这个资源可以是daemonset,也可以是deployment.启动的容器会自动拉起一个nginx进程并且会根据ingress的规则配置容器内nginx的配置。我
用如下的mandatory.yaml 文件来创建nginx-ingress-controller:
点击查看代码
[root@test-ko-mix-master-1 20211109]# cat mandatory.yaml
apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
---
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
---
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:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- "extensions"
- "networking.k8s.io"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- "extensions"
- "networking.k8s.io"
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:
# Defaults to "<election-id>-<ingress-class>"
# Here: "<ingress-controller-leader>-<nginx>"
# This has to be adapted if you change either parameter
# when launching the nginx-ingress-controller.
- "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
---
apiVersion: apps/v1
kind: DaemonSet
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:
# wait up to five minutes for the drain of connections
terminationGracePeriodSeconds: 300
serviceAccountName: nginx-ingress-serviceaccount
nodeSelector:
kubernetes.io/hostname: test-ko-mix-master-1
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.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:
allowPrivilegeEscalation: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
# www-data -> 101
runAsUser: 101
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
hostPort: 80
protocol: TCP
- name: https
containerPort: 443
hostPort: 443
protocol: TCP
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 10254
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 10
lifecycle:
preStop:
exec:
command:
- /wait-shutdown
---
apiVersion: v1
kind: LimitRange
metadata:
name: ingress-nginx
namespace: ingress-nginx
labels:
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
spec:
limits:
- min:
memory: 90Mi
cpu: 100m
type: Container
找到创建出来的nginx-ingress-controller容器,kubectl exec -ti -n ingress-nginx pod/nginx-ingress-controller-vt5lq cat /etc/nginx/nginx.conf ,可以看到ingress设定的一些规则:
## start server test-ko-mix-master-1
server {
server_name test-ko-mix-master-1 ;
listen 80 ;
listen 443 ssl http2 ;
set $proxy_upstream_name "-";
...
location /foo {
set $namespace "default";
set $ingress_name "nginx-ingress";
set $service_name "";
set $service_port "";
set $location_path "/foo";
这个时候你外集群外节点去curl ngin-ingress-controller所在主机的80 端口可以得到404网页返回。另外,我们查看nginx-ingress-controller的log,发现它一直在报错:err services "ingress-nginx" not found.这个是说我们创建nginx-ingress-controller中启动参数制定了publish-service为ingress-nginx:
- /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
因此我们要创建一个nodeport类型的service,name是ingress-nginx,创建该service的YAML文件如下:
点击查看代码
[root@test-ko-mix-master-1 20211109]# cat service-nodeport.yaml
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
创建完ingress-nginx service之后再次查看ingress,ADDRESS栏位有了值,也就是service IP Address:
[root@test-ko-mix-master-1 20211109]# kubectl get ingress -A -o wide
NAMESPACE NAME CLASS HOSTS ADDRESS PORTS AGE
default nginx-ingress nginx-ingress-controller test-ko-mix-master-1 192.168.255.207 80 48m
[root@test-ko-mix-master-1 20211109]# kubectl describe ingress -n default nginx-ingress
Name: nginx-ingress
Namespace: default
Address: 192.168.255.207
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
Rules:
Host Path Backends
---- ---- --------
test-ko-mix-master-1
/index.html nginx:80 (10.8.188.31:80)
/bar nginx2:80 (10.8.188.36:80)
Annotations: <none>
Events: <none>
正是有了这个nodeport类型的service我们才能在集群外的节点curl test-ko-mix-master-1:
因为我们ingress制定的url foo后端的service是nginx,所以我们要创建这个service及其后面的endpoints.否则nginx-ingress-controller会报错说找不到nginx这个service. 创建完成之后我的理解就是在集群群外的节点输入curl test-ko-mix-master-1 /foo 就访问到那边呢?nginx service对应的endpoint pod上吗?还是负载到nginx-ingress-controller里面的nginx进程上呢?根据官网的架构图理解应该是先到nginx-ingress-controller的容器,由它负载到后面的service,也就是nginx service对应的endpoints我看到过有的文档说流量不经过service由nginx-ingress-controller直接负载到service后面的endpoint也就是pod上,但是我没有找到controller所在主机的80监听端口也没有找到ipvs转发主机80端口的规则。那么我不敢妄评说controller的流量到底是怎么转发的。有知道的大神可以在评论区指教!
另外,我在集群主机上看到这样一条IPVS转发规则:
TCP 172.18.8.203:32629 rr
-> 10.8.188.30:80 Masq 1 0 0
意思就是访问本机(172.18.8.203)32629 端口的转发到10.8.188.30:80,其中10.8.188.30是nginx-ingress-controller 容器的IP Address.
ingressClass
当集群中存在多于一个的 Ingress Controller 时,就需要为 Ingress 指定对应的 Controller 是哪个,在 Kubernetes 1.18 之前,通常是在 Ingress 资源上通过 kubernetes.io/ingress.class 注解来指定使用的 Ingress Controller。虽然这个注解从未被正式定义过,但确是被各个 Ingress Controller 所广泛支持的。Kubernetes 1.18 起,正式提供了一个 IngressClass 资源,作用与 kubernetes.io/ingress.class 注解类似,例如:
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: external-lb
spec:
controller: nginx-ingress-internal-controller
parameters:
apiGroup: k8s.example.com
kind: IngressParameters
name: external-lb
其中重要的属性是 metadata.name 和 spec.controller,前者是这个 IngressClass 的名称,需要设定在 Ingress 中,后者是 Ingress Controller 的名称。Ingress 中的 spec.ingressClassName 属性,可以用来指定对应的 IngressClass,并进而由 IngressClass 关联到对应的 Ingress Controller,如:
kind: Ingress
metadata:
name: spring-k8s
spec:
ingressClassName: external-lb
defaultBackend:
service:
name: spring-k8s
port:
number: 80
注意,spec.ingressClassName 与 metadata.annotations.kubernetes.io/ingress.class 的作用并不完全相同,因为 ingressClassName 字段引用的是 IngressClass 资源的名称,IngressClass 资源中,除了指定了 Ingress Controller 的名称之外,还可能会通过 spec.parameters 属性定义一些额外的配置。
题外话,正向代理和反向代理
- 正向代理类似一个跳板机,代理访问外部资源。比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了。正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端.
正向代理的用途:
(1)访问原来无法访问的资源,如google
(2)可以做缓存,加速访问资源
(3)对客户端访问授权,上网进行认证
(4)代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息 - 反向代理(Reverse Proxy)实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器。反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端。
反向代理的作用:
(1)保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网
(2)负载均衡,通过反向代理服务器来优化网站的负载