Ingress 介绍
在Kuberbetes中除了使用NodePort,LoadBalancer, Port Proxy(hostPort)等实现外部访问入口之外,还可以使用Kubernetes 自带的Ingress来实现服务的负载均衡和策略路由的工作,其本质相当于一个Nginx代理服务器,可以对请求(http)实现精细的控制。
Ingress 是可以利用 Nginx、Haproxy 等负载均衡器暴露集群内服务的工具,
使用Ingress可以配置一个用于外部访问的URL地址,同时实现流量负载,SSL,基于名称的虚拟主机(例如Nginx的upstream)等。用户通过发送POST请求到API server,来申请Ingress资源, Ingress控制器负责完成Ingress转发工作,通常会使用负载均衡,也可以配置边缘路由器或其他前端以帮助以HA方式处理流量。
在当前版本中,Ingress处于测试版本,在使用Ingress时,需要先创建Ingress Contronller.
创建Ingree Controller
在Kubernetes中,Ingress Controller将以Pod的形式运行,监控apiserver的 ingress接口后端的backend services,如果service发生变化,则Ingress Controller 应自动更新其转发规则。 Ingress Controller 需要实现基于不同HTTP URL向后转发的负载均衡分发规则,并可灵活设置7层的负载分发策略。如果公有云服务商能提供该类型的HTTP路由LoadBlanacer,也可以设置其为Ingress Controller。
在新的版本中Ingress Controller和具有负载均衡的软件已经捆绑到一起,目前支持多种类型, 如nginx ,haproxy 等。
创建Ingree Contoller和backend
使用如下的定义的文件,可以直接从官方下载,需要修改谷歌的镜像地址(https://kubernetes.github.io/ingress-nginx/deploy/):
---
apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: default-http-backend
labels:
app: default-http-backend
namespace: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app: default-http-backend
template:
metadata:
labels:
app: default-http-backend
spec:
terminationGracePeriodSeconds: 60
containers:
- name: default-http-backend
# Any image is permissible as long as:
# 1. It serves a 404 page at /
# 2. It serves 200 on a /healthz endpoint
image: mirrorgooglecontainers/defaultbackend:1.4
livenessProbe:
httpGet:
path: /healthz
port: 8080
scheme: HTTP
initialDelaySeconds: 30
timeoutSeconds: 5
ports:
- containerPort: 8080
resources:
limits:
cpu: 10m
memory: 20Mi
requests:
cpu: 10m
memory: 20Mi
---
apiVersion: v1
kind: Service
metadata:
name: default-http-backend
namespace: ingress-nginx
labels:
app: default-http-backend
spec:
ports:
- port: 80
targetPort: 8080
selector:
app: default-http-backend
---
kind: ConfigMap
apiVersion: v1
metadata:
name: nginx-configuration
namespace: ingress-nginx
labels:
app: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: tcp-services
namespace: ingress-nginx
---
kind: ConfigMap
apiVersion: v1
metadata:
name: udp-services
namespace: ingress-nginx
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: nginx-ingress-clusterrole
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
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
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
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: nginx-ingress-clusterrole
subjects:
- kind: ServiceAccount
name: nginx-ingress-serviceaccount
namespace: ingress-nginx
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx-ingress-controller
namespace: ingress-nginx
spec:
replicas: 1
selector:
matchLabels:
app: ingress-nginx
template:
metadata:
labels:
app: ingress-nginx
annotations:
prometheus.io/port: '10254'
prometheus.io/scrape: 'true'
spec:
serviceAccountName: nginx-ingress-serviceaccount
containers:
- name: nginx-ingress-controller
image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.16.2
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
- --publish-service=$(POD_NAMESPACE)/ingress-nginx
- --annotations-prefix=nginx.ingress.kubernetes.io
# - --report-node-internal-ip-address=true
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
# hostPort: 80 如果不使用NodePort,可以使用hostPort的方式直接映射到宿主端口
- name: https
containerPort: 443
# hostPort: 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
使用如下文件创建 Ingress的services:
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
- name: https
port: 443
targetPort: 443
protocol: TCP
selector:
app: ingress-nginx
这里使用的nodeport的方式,对于其他云平台可以使用官方提供的对应yaml文件创建。
查看配置是否成功:
# kubectl get pods --all-namespaces -l app=ingress-nginx --watch
NAMESPACE NAME READY STATUS RESTARTS AGE
ingress-nginx nginx-ingress-controller-6c9fcdf8d9-fvn57 1/1 Running 0 38m
# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
default-http-backend-7fb45cbc-kv24l 1/1 Running 0 39m
nginx-ingress-controller-6c9fcdf8d9-fvn57 1/1 Running 0 39m
# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default-http-backend ClusterIP 10.1.74.56 <none> 80/TCP 40m
ingress-nginx NodePort 10.1.28.135 <none> 80:34596/TCP,443:28411/TCP 24m
在安装有kube-proxy的节点上会显示Nodeport映射的端口:
[root@node-2 ~]# netstat -lntp|egrep "28411|34596"
tcp6 0 0 :::28411 :::* LISTEN 1164/kube-proxy
tcp6 0 0 :::34596 :::* LISTEN 1164/kube-proxy
[root@node-3 ~]# netstat -lntp|egrep "28411|34596"
tcp6 0 0 :::28411 :::* LISTEN 1164/kube-proxy
tcp6 0 0 :::34596 :::* LISTEN 1164/kube-proxy
访问任意kube-proxy节点的端口,会返回404(还未配置Ingress)说明Ingress安装完成:
# curl 10.0.0.3:34596
default backend - 404
当然,在指定NodePort时,也可以指定端口:
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 20080
protocol: TCP
- name: https
port: 443
targetPort: 443
nodePort: 20443
protocol: TCP
selector:
app: ingress-nginx
定义Ingress的访问策略
使用Ingress可以实现多种访问策略:
- 转发到单个后端服务
- 同一域名,不同的URL路径被转发到不同服务器上
- 不同域名(虚拟主机名)转发到不同服务器上
- 不使用域名的转发规则
转发到单个后端服务上
定义如下Ingress配置文件:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test1
spec:
rules:
- host: test.com
http:
paths:
- path: /demo
backend:
serviceName: php-apache
servicePort: 80
# curl test.com:20080/demo/index.html
demo page!
同一域名,不同的URL路径被转发到不同服务器上
定义如下文件:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test2
spec:
rules:
- host: test.com
http:
paths:
- path: /tomcat
backend:
serviceName: tomcat-service
servicePort: 8080
- path: /apache
backend:
serviceName: php-apache
servicePort: 80
这里会将路径带入到后端服务中,如果后端的服务没有此路径,将会找不到服务,从而返回404。
不同域名(虚拟主机名)转发到不同服务器上
这里使用示例定义一个Ingress 用于访问集群中的 tomcat和apache,当前的服务如下:
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
php-apache ClusterIP 10.1.175.166 <none> 80/TCP 4d
tomcat-service ClusterIP 10.1.228.9 <none> 8080/TCP 1h
# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
php-apache-56b5765b95-rhvg2 1/1 Running 2 5d 10.2.15.6 10.0.0.2
tomcat-deployment-65799d5fc4-5dx4h 1/1 Running 0 3h 10.2.22.7 10.0.0.3
创建一个Ingress:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: test
spec:
rules:
- host: tomcat.com
http:
paths:
- backend:
serviceName: tomcat-service
servicePort: 8080
- host: apache.com
http:
paths:
- path: /demo/index.html
backend:
serviceName: php-apache
servicePort: 80
创建成功后,会显示如下信息:
# kubectl get ing -o wide
NAME HOSTS ADDRESS PORTS AGE
test tomcat.com,apache.com 80 2h
在不使用kubernetes支持的公有云平台下,默认的 ADDRESS地址会显示为空,如果需要显示节点的IP,可以添加
--report-node-internal-ip-address=true
的参数,或者修改flags.go z中的源码,对应的useNodeInternalIP = flags.Bool("report-node-internal-ip-address", false,
中 false 修改为 true。
在本主机host 文件中配置上域名和映射20080端口的主机节点,通过不同的域名访问此节点:
[root@node-1 ingress]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
10.0.0.2 Ready <none> 25d v1.10.4
10.0.0.3 Ready <none> 24d v1.10.4
[root@node-1 ingress]# echo "10.0.0.2 tomcat.com apache.com" >> /etc/hosts
[root@node-1 ~]# curl apache.com:20080
OK!
[root@node-1 ~]# curl tomcat.com:20080
tomcat demo!
[root@node-1 ~]# curl 10.0.0.2:20080
default backend - 404 # 当找不到对应的后端Service时,会去访问指定的默认backend。
查看代理信息:
[root@node-1 ~]# curl --head tomcat.com:20080
HTTP/1.1 200
Server: nginx/1.13.12
Date: Tue, 03 Jul 2018 11:47:31 GMT
Content-Type: text/html;charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
[root@node-1 ~]# curl --head apache.com:20080
HTTP/1.1 200 OK
Server: nginx/1.13.12
Date: Tue, 03 Jul 2018 11:47:41 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.6.14
登录Ingress的Pod,我们可以看到已经添加了相应的转发规则:
# kubectl exec -it nginx-ingress-controller-6c9fcdf8d9-fvn57 -n ingress-nginx -- sh
$ cat nginx.conf
...
upstream default-php-apache-80 {
least_conn;
keepalive 32;
server 10.2.15.6:80 max_fails=0 fail_timeout=0;
}
upstream default-tomcat-service-8080 {
least_conn;
keepalive 32;
server 10.2.22.7:8080 max_fails=0 fail_timeout=0;
}
...
server {
server_name apache.com ;
listen 80;
listen [::]:80;
set $proxy_upstream_name "-";
...
## start server tomcat.com
server {
server_name tomcat.com ;
listen 80;
listen [::]:80;
set $proxy_upstream_name "-";
Ingress的TLS安全设置
对于使用https设置TLS的安全证书方面,Ingress 也可以支持。 通过以下步骤进行设置:
- 创建自签名的密钥和SSL证书文件
- 将证书保存到Kubernetes中的一个Secret资源对象上。
- 将该Secret对象设置到Ingress中。
目前,Ingress仅支持单个TLS端口443,并假定TLS termination。 如果Ingress中的TLS配置部分指定了不同的主机,则它们将根据通过SNI TLS扩展指定的主机名(假如Ingress controller支持SNI)在多个相同端口上进行复用。TLS secret中必须包含名为tls.crt和tls.key的密钥,这里面包含了用于TLS的证书和私钥,其格式如下:
apiVersion: v1
data:
tls.crt: base64 encoded cert # cert 文件内容
tls.key: base64 encoded key # key 文件内容
kind: Secret
metadata:
name: testsecret
namespace: default
type: Opaque
在Ingress中引用 Secret:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: no-rules-map
spec:
tls:
- secretName: testsecret
backend:
serviceName: s1
servicePort: 80
创建自签名的密钥和SSL证书文件并生成Secret
首先生成证书:
# openssl req -x509 -nodes -days 5000 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=myweb.com"
利用生成的key 和 crt文件创建Secret:
# kubectl create secret tls myweb-ingress-secret --key tls.key --cert tls.crt
secret "myweb-ingress-secret" created
# kubectl get secret
NAME TYPE DATA AGE
default-token-hmvnc kubernetes.io/service-account-token 3 26d
myweb-ingress-secret kubernetes.io/tls 2 13s
我们也可以使用YAML文件的方式创建,其格式和下面的类似:
# kubectl get secret myweb-ingress-secret -o yaml
apiVersion: v1
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMr
...
Gh0UlJJUkV5bUJpZjZNdmRlOERmUVRiT0x5OUF5Y0xVb2gyL2RlRnhOST0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2d0lCQURBTkJna3Foa2lHOXcwQ
...
MzekE9PQotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0tCg==
kind: Secret
metadata:
name: myweb-ingress-secret
namespace: default
type: kubernetes.io/tls
创建Ingress对象
TLS的证书创建到Secret之后,就可以利用他来创建Ingress对象了,使用如下文件:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myweb-ingress
spec:
tls:
- secretName: myweb-ingress-secret
rules:
- host: myweb.com
http:
paths:
- path: /demo
backend:
serviceName: php-apache
servicePort: 80
使用默认的443端口访问(本地使用的NodePort,映射到Ingress的443端口):
# curl -k https://myweb.com:20443/demo/index.html
demo test https page!
使用网页可以访问网页内容: https://myweb.com:20443/demo