上一节中我们学习了Service以及对应的负载均衡,但是这些负载均衡都是基于IP和端口的四层负载均衡。那么如果想要实现七层负载均衡,也就是根据请求的内容来,有没有办法实现呢?这就需要用到本节即将学习的Ingress了。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
在k8s里,Ingress是一个可以允许集群外部访问集群内布服务的控制器。通过配置一条条规则(rules)来规定进来的连接被分配到后端的哪个服务。
Ingress相当于一个集中的路由中心,例如,可以将xiaofu.com/api/v1/
路由到后端的service-v1
服务,而将xiaofu.com/api/v2/
路由到service-v2
服务。
同样是将集群内部的服务暴露给集群外,很有必要对比下Ingress和上一节学习的NodePort。
NodePort是Service的一个类型,并没有额外加入组件。其会在每个Node上开一个端口,对应到后端的Service。所有访问集群任意节点IP上该端口都可以访问到后端的Service。这样的优点就是简单快速,但是只是起了简单的Node端口到Service端口的映射功能,功能很单一。
Ingress是区别于上一节学的那些Service的另一个单独组件,可以被单独声明,创建和销毁。引入Ingress的好处就是有一个集中的路由配置项,同时可实现基于内容的七层负载均衡。小小的麻烦就是引入了额外组件,但是其实创建一个Ingress和创建一个k8s中的其他资源的步骤没什么两样。
Ingress的实现方式有很多,下面着重学习下官方推荐的基于Nginx的Ingress-Nginx控制器。
可以将上面的Ingress理解为在集群的前端加了一个Nginx,用来提供Nginx所能提供的一切功能,例如基于url的反向代理,https认证,用户鉴权,域名重定向等等
需要注意几点:
更详细的介绍可以查看官方网站。
直接按照官方文档进行安装即可。官方提供了minikube以及各厂商的云机器上安装的方式,我们是用kubeadm自己搭建的k8s环境,所以选择Bare-metal
部分的安装脚本,只有下面一句脚本即可
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.32.0/deploy/static/provider/baremetal/deploy.yaml
官方默认采用NodePort的方式安装Nginx,其实还有其余方式,参考https://kubernetes.github.io/ingress-nginx/deploy/baremetal/
推荐先将上面这个yaml文件下载到本地,因为以后还可以根据这个文件批量删除或者修改ingress-nginx的资源。
kubectl apply -f xxx.yaml 也可以用来修改资源配置
kubectl delete -f xxx.yaml 则可以批量删除资源
[root@k8s-master ~]# kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-0.32.0/deploy/static/provider/baremetal/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
configmap/ingress-nginx-controller created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
service/ingress-nginx-controller-admission created
service/ingress-nginx-controller created
deployment.apps/ingress-nginx-controller created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
serviceaccount/ingress-nginx-admission created
所有的资源都在ingress-nginx
这个namespace下
[root@k8s-master ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.96.130.237 80:31958/TCP,443:30265/TCP 54m
ingress-nginx-controller-admission ClusterIP 10.109.76.229 443/TCP 54m
[root@k8s-master ~]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-g5sfq 0/1 Completed 0 57m
ingress-nginx-admission-patch-tzv5x 0/1 Completed 0 57m
ingress-nginx-controller-5575c6cd9d-8xwmt 1/1 Running 0 57m
[root@k8s-master ~]# kubectl get deployment -n ingress-nginx
NAME READY UP-TO-DATE AVAILABLE AGE
ingress-nginx-controller 1/1 1 1 57m
从上面的svc可以看到,有个以NodePort暴露出来的ingress-nginx-controller
的service,对外暴露31958端口对应nginx的80端口,暴露30265端口对应nginx的443端口。下面在实际操作的时候会用到这两个外部端口。
以下所有yaml文件托管在我的Github仓库
事先删除上一节的所有service和pod,以免引起混淆。
首先用yaml文件http-mynginx.yaml
创建mynginx:v2
版的deployment的常规的ClusterIP的service
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: mynginx-deployment
spec:
replicas: 3
template:
metadata:
labels:
app: mynginx
version: v2
spec:
containers:
- name: mynginx
image: mynginx:v2
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: mynginx-service
namespace: default
spec:
type: ClusterIP
selector:
app: mynginx
version: v2
ports:
- name: http
port: 8080
targetPort: 80
这里其实就是上一节ClusterIP中的两个yaml文件,用---
在同一个yaml文件中进行了合并。
成功创建出pod和service
[root@k8s-master k8s-ingress]# kubectl apply -f http-mynginx.yaml
deployment.extensions/mynginx-deployment created
service/mynginx-service created
[root@k8s-master k8s-ingress]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 10d
mynginx-service ClusterIP 10.97.205.233 8080/TCP 10s
[root@k8s-master k8s-ingress]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mynginx-deployment-b66f59f66-bbkgg 1/1 Running 0 14s 10.244.1.77 k8s-node1
mynginx-deployment-b66f59f66-vfvsw 1/1 Running 0 14s 10.244.1.76 k8s-node1
mynginx-deployment-b66f59f66-xtk4c 1/1 Running 0 14s 10.244.0.15 k8s-master
下面需要将上述mynginx-service
这个service的8080端口通过nginx暴露出去,用yaml文件http-mynginx-ingress.yaml
在nginx中创建一条规则
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mynginx-http
spec:
rules:
- host: mynginx.xiaofu.com
http:
paths:
- path: /
backend:
serviceName: mynginx-service
servicePort: 8080
这个规则中将域名mynginx.xiaofu.com
的根目录映射到后端的mynginx-service。这里用根目录是因为后端pod中只有一个index.html
文件,没有别的路径,实际中这个路径可以根据自己需求去灵活指定。
然后修改本地hosts文件,绑定mynginx.xiaofu.com
到k8s集群中任意一个node的外网ip即可。Windows下的hosts文件路径为C:\Windows\System32\drivers\etc\hosts
。此后访问该域名的31958端口,数据流就会被导到nginx的80端口,然后到后端服务mynginx-service
的8080端口,最后走负载均衡到其中一个pod。
这样就成功完成了nginx中的http反向代理功能,对nginx配置感兴趣的话,可以用类似下面的方法进去nginx的pod中看一下配置文件
[root@k8s-master k8s-ingress]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-g5sfq 0/1 Completed 0 4h18m
ingress-nginx-admission-patch-tzv5x 0/1 Completed 0 4h18m
ingress-nginx-controller-5575c6cd9d-7b8zt 1/1 Running 0 36m
[root@k8s-master k8s-ingress]# kubectl exec ingress-nginx-controller-5575c6cd9d-7b8zt -n ingress-nginx -it -- /bin/bash
bash-5.0$ cat /etc/nginx/nginx.conf
如果想达到开头时候说的类似访问svc1.mynginx.xiaofu.com
和svc2.mynginx.xiaofu.com
映射到后端的不同service,只需要将这两个域名都指向集群中node的ip,并在Ingress中添加两条不同规则即可,这里就不演示了。
如果想要网站访问更安全,可以考虑带加密和认证的https协议。在客户端和nginx之间走https协议,nginx反向代理到后端的时候因为是内网,还是走未加密的http协议即可。Https的工作方式这里不额外介绍了,要完成配置,我们需要服务端的自签名CA证书和私钥文件。
生成所需的两个文件
[root@k8s-master ssl]# openssl genrsa -out ca.key 1024
Generating RSA private key, 1024 bit long modulus
..................++++++
.....................++++++
e is 65537 (0x10001)
[root@k8s-master ssl]# openssl req -new -key ca.key -out ca.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:SG
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:
Organization Name (eg, company) [Default Company Ltd]:xiaofu
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:
root@k8s-master ssl]# openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt
Signature ok
subject=/C=SG/L=Default City/O=xiaofu
Getting Private key
[root@k8s-master ssl]# ll
total 12
-rw-r--r--. 1 root root 713 May 8 20:07 ca.crt
-rw-r--r--. 1 root root 582 May 8 20:02 ca.csr
-rw-r--r--. 1 root root 887 May 8 20:01 ca.key
将刚才生成的两个文件存储到了k8s的secret里面,这里可以暂时不用管下面的命令具体含义,后面讲到k8s的存储的时候我们再详细学习。
[root@k8s-master ssl]# kubectl create secret tls tls-secret --key ca.key --cert ca.crt
secret/tls-secret created
通过yaml文件https-mynginx.yaml
创建一个https加密的域名,采用刚才存储的tls-secret
进行认证和加密,映射到后面和刚才一样的service
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mynginx-https
spec:
tls:
- hosts:
- secret.xiaofu.com
secretName: tls-secret
rules:
- host: secret.xiaofu.com
http:
paths:
- path: /
backend:
serviceName: mynginx-service
servicePort: 8080
成功创建出来
[root@k8s-master k8s-ingress]# kubectl apply -f https-mynginx.yaml
ingress.extensions/mynginx-https created
之后修改hosts文件,将secret.xiaofu.com
绑定到集群中任一node的ip。
然后就可以用https访问域名了,端口是前面ingress暴露出来的443对用的外网端口
因为是我们自己签名的证书,所以需要用户认证一下
之后就可以正常访问了
更多tls的相关使用参考k8s的官方文档
以下案例参考了k8s官方文档
因为要通过apache的htpasswd模块去验证,所以要先安装apache,centos7中就是httpd
yum install httpd
之后创建用户xiaofu及密码文件auth,并根据auth文件存储到k8s的secret中
[root@k8s-master ~]# htpasswd -c auth xiaofu
New password:
Re-type new password:
Adding password for user xiaofu
[root@k8s-master ~]# kubectl create secret generic basic-auth --from-file=auth
secret/basic-auth created
通过yaml文件basicauth-mynginx.yaml
来添加一条需要认证的域名
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mynginx-basicauth
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - xiaofu'
spec:
rules:
- host: auth.xiaofu.com
http:
paths:
- path: /
backend:
serviceName: mynginx-service
servicePort: 8080
这里和前面http部分的yaml文件就只是annotations
部分。
成功创建
[root@k8s-master k8s-ingress]# kubectl apply -f basicauth-mynginx.yaml
ingress.extensions/mynginx-basicauth created
同时在hosts文件中将basicauth.xiaofu.com
映射到任意node的ip,之后再访问声明了的域名就要密码了
输入刚才创建的用户和密码即可成功访问。
上面yaml文件中的annotations
除了可以声明认证方式外,还有很多重写功能。
下面的表格取自官方文档
Name | Description | Values |
---|---|---|
nginx.ingress.kubernetes.io/rewrite-target | Target URI where the traffic must be redirected | string |
nginx.ingress.kubernetes.io/ssl-redirect | Indicates if the location section is accessible SSL only (defaults to True when Ingress contains a Certificate) | bool |
nginx.ingress.kubernetes.io/force-ssl-redirect | Forces the redirection to HTTPS even if the Ingress is not TLS Enabled | bool |
nginx.ingress.kubernetes.io/app-root | Defines the Application Root that the Controller must redirect if it’s in ‘/’ context | string |
nginx.ingress.kubernetes.io/use-regex | Indicates if the paths defined on an Ingress use regular expressions | bool |
下面利用重写功能将访问redirect.xiaofu.com
的请求都重定向到mynginx.xiaofu.com
。yaml文件mynginx-rewrite.yaml
如下
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: mynginx-rewrite
annotations:
nginx.ingress.kubernetes.io/rewrite-target: http://mynginx.xiaofu.com:31958
spec:
rules:
- host: redirect.xiaofu.com
http:
paths:
- path: /
backend:
serviceName: mynginx-service
servicePort: 8080
跳转的目的地址要写完整
成功创建
[root@k8s-master k8s-ingress]# kubectl apply -f mynginx-rewrite.yaml
ingress.extensions/mynginx-rewrite configured
同时在hosts文件中将redirect.xiaofu.com
映射到任意node的ip,此时访问redirect.xiaofu.com:31958/
会自动跳转到mynginx.xiaofu.com:31958
这一节我们学习了通过Ingress将k8s集群内部服务暴露给集群外的方法,到这里Service的部分就全部结束了,下一节我们开始学习k8s中的存储,其中就会学习到这一节中已经用到了的secret。