本文目的:
- 在K8S中安装部署ingress;
- 说明如何通过ingress实现http自动跳转到https;
关于ingress:
Ingress 是从Kubernetes集群外部访问集群内部服务的入口,为集群内的Service提供七层负载均衡能力。
一、部署Ingress Controller
为了使Ingress正常工作,集群内必须部署Ingress Controller。Kubernetes社区提供了以下Ingress Controller供选择,分别如下:
- Nginx
- HAProxy
- Envoy
- Traefik
这里我们选择Nginx作为Ingress Controller,执行以下命令即可。
kubectl apply -f https://gitee.com/uk8s/uk8s/raw/master/yaml/ingress_nginx/mandatory.yaml
这个yaml定义了一个使用nginx-ingress-controller作为镜像的pod副本集,这个Pod主要功能就是一个监听Ingress对象以及它所代理的后端Service变化的控制器。当一个新的Ingress对象由用户创建后,控制器会根据Ingress对象所定义的内容,生成一份对应的Nginx配置文件(也就是我们熟知的/etc/nginx/nginx.conf),并使用这个配置文件启动一个Nginx服务。而一旦Ingress对象被更新,控制器会更新这个配置文件。需要注意的是,如果只是被代理的Service对象被更新,控制器所管理的nginx服务不需要重新加载,这是因为nginx-ingress-controller通过nginx lua实现了upstream的动态配置。
另外,在这个yaml文件中,我们还看到定义了ConfigMap,nginx-ingress-controller可以通过ConfigMap对象来对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
data:
map-hash-bucket-size: "128"
ssl-protocols: SSLv2
需要注意的是,ConfigMap中的key和value只支持字符串,因此对于整数等类型,需要使用双引号,例如”100”,详细资料见Nginx-Ingress-ConfigMap。
本质上,这个Nginx-ingress-controller,其实是一个可以根据Ingress对象和被代理后端Service的变化,自动进行更新的Nginx负载均衡器。
容器默认使用UTC时间,如果要使用宿主机时区,参见Pod时区问题
二、将nginx ingress controller暴露出集群
上面我们已经在UK8S中部署了一个nginx ingress controller,但这个Nginx服务只能在集群内部可达,为了在集群外部可达,我们还需要为此创建一个外部可达的Service,这里我们选择创建一个LoadBalancer类型的Service,并将其暴露到外网。
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: LoadBalancer
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
这个 LoadBalancer的Service 就是将所有携带 ingress-nginx 标签的 Pod 的 80 和 433 端口暴露出去,我们可以通过以下方式获取到这个Service的外网访问入口。
bash-4.4# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx LoadBalancer 172.17.118.146 117.50.x.x 80:34947/TCP,443:48761/TCP 2d23h
部署完Ingress Controller 和它所需要的 Service后 ,我们就可以使用通过他来将集群内部的其他Service代理出去了。
三、创建两个应用
在下面的yaml中,我们定义了2个镜像名为echo-nginx的应用,主要是输出nginx应用自身的一些全局变量。
apiVersion: apps/v1
kind: Deployment
metadata:
name: uhost
spec:
replicas: 2
selector:
matchLabels:
app: uhost
template:
metadata:
labels:
app: uhost
spec:
containers:
- name: uhost
image: uhub.service.ucloud.cn/jenkins_k8s_cicd/echo_nginx:v11
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: uhost-svc
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: uhost
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: uk8s
spec:
replicas: 3
selector:
matchLabels:
app: uk8s
template:
metadata:
labels:
app: uk8s
spec:
containers:
- name: uk8s
image: uhub.service.ucloud.cn/jenkins_k8s_cicd/echo_nginx:v11
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: uk8s-svc
labels:
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: uk8s
我们将上述yaml保存为uk8s-ingrss-test-nginx.yaml,并执行如下命令创建应用。
kubectl apply -f uk8s-ingrss-test-nginx.yaml
四、定义Ingress对象
我们已经部署了nginx ingress controller并将其暴露到外网,并且创建好了两个应用,接下来,我们就可以定义一个ingress对象,来把两个应用代理出集群。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: uk8s-ingress
spec:
rules:
- host: uk8s.example.com
http:
paths:
- path: /uhost
backend:
serviceName: uhost-svc
servicePort: 80
- path: /uk8s
backend:
serviceName: uk8s-svc
servicePort: 80
上述yaml文件定义了一个Ingress对象,其中ingress.spec.rules便是ingress的代理规则集合。
我们先看host字段,他的值必须是一个标准的域名格式字符串,而不能是IP地址。而host字段定义的值,就是这个Ingress的入口。这样就意味着,当用户访问uk8s.example.com的时候,实际访问到的就是这个Ingress对象。这样,Kubernetes就能使用IngressRule来对你的请求进行下一步转发。
接下来是path字段。你可以简单理解为,这里的每个path都对应一个后端Service。所以,我们例子里,定义了两个path,他们分别对应uhost和uk8s这两个Deployment的Service(即:uhost-svc和uk8s-svc)。
每条http规则包含以下信息:一个host配置项(比如uk8s.example.com),path列表(比如:/uhost),每个path都关联一个backend(比如uhost:80)。在loadbalancer将流量转发到backend之前,所有的入站请求都要先匹配host和path。
我们将上述yaml保存为ingress.yaml,并通过以下命名直接创建ingress对象:
kubectl apply -f ingress.yaml
接下来,我们可以查看这个ingress对象
bash-4.4# kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
uk8s-ingress uk8s.example.com 117.50.x.x 80, 443 4h56m
bash-4.4# kubectl describe ingress uk8s-ingress
Name: uk8s-ingress
Namespace: default
Address: 117.50.x.x
Default backend: default-http-backend:80 (<none>)
TLS:
uk8s-secret terminates uk8s.example.com
Rules:
Host Path Backends
---- ---- --------
uk8s.example.com
/uhost uhost-svc:80 (<none>)
/uk8s uk8s-svc:80 (<none>)
Annotations:
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"extensions/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"uk8s-ingress","namespace":"default"},"spec":{"rules":[{"host":"uk8s.example.com","http":{"paths":[{"backend":{"serviceName":"uhost-svc","servicePort":80},"path":"/uhost"},{"backend":{"serviceName":"uk8s-svc","servicePort":80},"path":"/uk8s"}]}}],"tls":[{"hosts":["uk8s.example.com"],"secretName":"uk8s-secret"}]}}
从rule规则可以看到, 我们的定义的Host是uk8s.example.com,他有两条转发规则(Path),分别转发给uhost-svc和uk8s-svc。
接下来,我们就可以通过访问这个 Ingress 的地址和端口,访问到我们前面部署的应用了,当我们访问http://uk8s.example.com/uk8s 时,应该是uk8s这个Deployment负责响应请求,由于我们LoadBalance绑定了EIP,所以外网是可以访问的,我们在本地添加hosts即可直接从外网通过域名访问。
五、TLS支持,以及HTTP自动跳转到HTTPS
在上述的ingress对象中,我们没有给Host指定TLS证书,ingress controller支持通过指定一个包含TLS私钥以及证书的secret来加密站点。
我们先创建一个包含tls.crt以及tls.key的secret,在生成证书的时候,需要确保证书的CN包含”uk8s.example.com”,并将证书内容使用base64编码。
apiVersion: v1
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
kind: Secret
metadata:
name: uk8s-tls
namespace: default
type: kubernetes.io/tls
我们也可以通过以下命令来快速创建secret:
kubectl create secret tls uk8s-tls --key /tmp/tls.key --cert /tmp/tls.crt
然后在ingress对象中,通过ingress.spec.tls字段引用secret,ingress controller即会加密来保护客户端到ingress之间的通信管道。
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: uk8s-ingress
annotations:
ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- uk8s.example.com
secretName: uk8s-tls
rules:
- host: uk8s.example.com
http:
paths:
- path: /uhost
backend:
serviceName: uhost-svc
servicePort: 80
- path: /uk8s
backend:
serviceName: uk8s-svc
servicePort: 80