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)負載均衡,通過反向代理服務器來優化網站的負載