NodePort存在的不足
Ingress Controller有很多实现方法,我们这里采用官方维护的基于nginx实现的控制器
项目地址:http://github.com/kubernetes/ingress-nginx
文档:https://kubernetes.github.io/ingress-nginx/deploy
其他控制器:https://kubenetes.io/docs/concepts/services-networking/ingress-controllers
我用的是0.49这个版本安装的,以下是对应支持版本
我这里用的是0.49的
[root@k8s-master ~]# ls
anaconda-ks.cfg calico.yaml.bak ingress-controller-0.49.yaml pod.yaml
calico.yaml deployment.yaml pod-nodeaffinity.yaml services.yaml
# 直接构建即可,这个文件中所需的镜像在国外,所以修改了下镜像的获取地址,其他的没变
[root@k8s-master ~]# kubectl apply -f ingress-controller-0.49.yaml
# 运行后他有两个是job
[root@k8s-master ~]# kubectl get job -A
NAMESPACE NAME COMPLETIONS DURATION AGE
ingress-nginx ingress-nginx-admission-create 1/1 48s 32m
ingress-nginx ingress-nginx-admission-patch 1/1 73s 32m
# controller运行即可,其他两个是job
[root@k8s-master ~]# kubectl get pods -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-vq9hb 0/1 Completed 0 32m
ingress-nginx-admission-patch-b52mc 0/1 Completed 0 32m
ingress-nginx-controller-78b4958f8f-5plrv 1/1 Running 0 32m
将web服务由Ingress代理出去,使用之前创建的3个nginx的pod测试即可
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
web-586db47859-5vqr4 1/1 Running 0 21h
web-586db47859-ljvm4 1/1 Running 0 21h
web-586db47859-tkp2k 1/1 Running 0 21h
# 创建一个Ingress
[root@k8s-master ingress]# vim ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
# 设置名称
name: web1
spec:
rules:
# 设置域名
- host: web1.aaa.cn
http:
paths:
- pathType: Prefix
# 设置的访问路径为/,也就是说在访问"web1.aaa.cn"域名时直接访问的是对应Service的/路径
path: "/"
backend:
# 与service关联,这里与service的name为web的相关联
service:
name: web
port:
number: 80
# 创建好之后查看svc,可以看到80这个端口被映射为31636了,等等访问域名的时候就加入这个端口
[root@k8s-master ingress]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.106.183.203 <none> 80:31636/TCP,443:32389/TCP 63m
ingress-nginx-controller-admission ClusterIP 10.100.222.247 <none> 443/TCP 63m
# 查看ingress
[root@k8s-master ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
web1 <none> web1.aaa.cn 192.168.113.146 80 13m
# 在创建一个用来测试的Pod
[root@k8s-master ingress]# kubectl create deployment java-demo --image=lizhenliang/java-demo
deployment.apps/java-demo created
# 创建Pod对应的service
[root@k8s-master ingress]# kubectl expose deployment java-demo --port=8088 --target-port=8080 --type=NodePort
service/java-demo exposed
# PORT中前面的8088是service的IP,后面的30939是容器中8080对外暴露的IP
[root@k8s-master ingress]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
java-demo NodePort 10.104.210.67 <none> 8088:30939/TCP 3s
创建ingress
# 编写yaml
[root@k8s-master ingress]# vim ingress2.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web2
spec:
rules:
- host: web2.aaa.cn
http:
paths:
- path: /foo
pathType: Prefix
backend:
service:
name: web
port:
number: 80
- path: /bar
pathType: Prefix
backend:
service:
name: java-demo
port:
number: 8088
# 这样就创建了一个名称为web2的ingress
[root@k8s-master ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
web1 <none> web1.aaa.cn 192.168.113.146 80 164m
web2 <none> web2.aaa.cn 192.168.113.146 80 64s
# 查看该ingress的信息
[root@k8s-master ingress]# kubectl describe ingress web2
# 其中有这样一条信息,可以看到代理的PodIP
Rules:
Host Path Backends
---- ---- --------
web2.aaa.cn
/foo web:80 (10.244.36.75:80,10.244.36.76:80,10.244.36.77:80)
/bar java-demo:80 (10.244.169.140:8080)
在本地hosts加入主机映射后访问下试试,当然nginx的需要提前创建一个foo目录并且写入index.html文件,原理就和nginx一样
看到是可以访问的
Java-demo这个因为是tomcat,所以添加上页面后使用绝对路径访问
# 有之前创建的web和Java-demo两个Pod,那么直接通过不通的路径去访问
[root@k8s-master ingress]# vim ingress3.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web3
spec:
rules:
- host: web3-1.aaa.cn
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: java-demo
port:
number: 8088
- host: web3-2.aaa.cn
http:
paths:
- pathType: Prefix
path: "/"
backend:
service:
name: web
port:
number: 80
配置HTTPS步骤:
1、准备域名证书文件(来自:openssl/cfssl工具自签或者权威机构颁发)
2、将证书文件保存到Secret
kubectl create secret tls blog-aliangedu-cn --cert=blog.aliangedu.cn.pem --key=blog.aliangedu.cn-key.pem
3、Ingress规则配置tls
# 示例
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog-https
annotations:
nginx.ingress.kubernetes.io/rewrite-target: https://www.baidu.com
spec:
tls:
- hosts:
- blog.aliangedu.cn
secretName: blog-aliangedu-cn
rules:
- host: blog.aliangedu.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
cfssl方式生成证书
# 先使用cfssl.sh获取cfssl命令
[root@k8s-master ssl]# cat cfssl/cfssl.sh
# 脚本内容如下
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
chmod +x cfssl*
mv cfssl_linux-amd64 /usr/bin/cfssl
mv cfssljson_linux-amd64 /usr/bin/cfssljson
mv cfssl-certinfo_linux-amd64 /usr/bin/cfssl-certinfo
# 使用certs.sh生成证书,其中那个"blog.zzz.com"可以修改,为该域名签发一个证书
[root@k8s-master ssl]# cat certs/certs.sh
cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
EOF
cat > ca-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "Beijing",
"ST": "Beijing"
}
]
}
EOF
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
cat > blog.zzz.com-csr.json <<EOF
{
"CN": "blog.zzz.com",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"ST": "BeiJing"
}
]
}
EOF
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes blog.zzz.com-csr.json | cfssljson -bare blog.zzz.com
生成证书
# 执行该命令获取命令
[root@k8s-master ssl]# bash cfssl.sh
# 执行该命令生成证书
[root@k8s-master ssl]# bash certs.sh
[root@k8s-master ssl]# ls
blog.zzz.com.csr blog.zzz.com-key.pem ca-config.json ca-csr.json ca.pem cfssl.sh
blog.zzz.com-csr.json blog.zzz.com.pem ca.csr ca-key.pem certs.sh
# 主要使用以上的"blog.zzz.com-key.pem"和"blog.zzz.com.pem"来去配置域名的
使用证书
# 将生成的证书保存到k8s中
# 保存到k8s中并且名称为blog-zzz-com的证书
[root@k8s-master ssl]# kubectl create secret tls blog-zzz-com --cert=blog.zzz.com.pem --key=blog.zzz.com-key.pem
secret/blog-zzz-com created
# 查看保存到k8s中的证书
[root@k8s-master ssl]# kubectl get secret
NAME TYPE DATA AGE
blog-zzz-com kubernetes.io/tls 2 49s
default-token-rmp55 kubernetes.io/service-account-token 3 9d
# 配置Ingress文件
[root@k8s-master ingress]# vim ingress-blog.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: blog
spec:
tls:
- hosts:
# 使用的域名,就是在生成证书时配置的域名,需要与下方rules中的host保持一致
- blog.zzz.com
# 创建时设置的secret名称
secretName: blog-zzz-com
rules:
# 需要与上面的的tls中的host保持一致
- host: blog.zzz.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: java-demo
port:
number: 8088
# 查看ingress
[root@k8s-master ingress]# kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
blog <none> blog.zzz.com 80, 443 18s
# 查看svc
[root@k8s-master ingress]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.106.183.203 <none> 80:31636/TCP,443:32389/TCP 20h
ingress-nginx-controller-admission ClusterIP 10.100.222.247 <none> 443/TCP 20h
根据以上信息可以知道域名为blog.zzz.com,https的端口为32389,所以访问即可
点击"不安全"打开证书,可以看到证书信息
这个证书是自己签发的,所以显示"不安全",如果有买下的证书,按照以上流程签发即可
Ingress Contronler怎么工作的
Ingress Contronler通过与Kubernetes API交互,动态的去感知集群中Ingress规则变化,然后读取它,按照自定义的规则,规则就是写明了那个域名对应那个service,生成一段Nginx配置,应用到管理的Nginx服务,然后热加载生成
以此来达到Nginx负载均衡器配置及动态更新的问题
流程包流程:客户端 -> Ingress Controller(Nginx) -> 分布在各节点Pod
一般Ingress Controller会以DaemonSet+nodeSelector部署到几台特定的Node,然后将这几台挂载到公网负载均衡器对外提供服务
Ingress-Controller这个Pod其实里面运行着的是一个ingress的控制器和nginx,如下
[root@k8s-master ingress]# kubectl exec -it ingress-nginx-controller-78b4958f8f-5plrv -n ingress-nginx -- bash
bash-5.1$ ps -ef
PID USER TIME COMMAND
1 www-data 0:00 /usr/bin/dumb-init -- /nginx-ingress-controller --election-id=ingress-controller-leade
7 www-data 0:22 /nginx-ingress-controller --election-id=ingress-controller-leader --ingress-class=ngin
26 www-data 0:00 nginx: master process /usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf
我们可以在创建Ingress时,加入一些对nginx的配置,如下
配置的这些规则是直接在nginx的配置文件中的server块生效的,配置完成后可以在nginx中查看
更多使用方法
# 以下是一段示例
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web1
annotations:
# 使用的控制器的标识,如果时多个控制器的话可以指定使用不同的控制器
kubernetes.io/ingress.class: "nginx"
# 增加链接超时
nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
# 设置客户端上传文件大小
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
# 重定向
nginx.ingress.kubernetes.io/rewrite-target: https://www.baidu.com
# 自定义规则
nginx.ingress.kubernetes.io/server-snippet: |
if ($http_user_agent ~* '(Android|iPhone)') {
rewrite ^/(.*) http://m.baidu.com break;
}
spec:
rules:
- host: web1.aliangedu.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx
port:
number: 80
在上诉内容中,我们可以知道Ingress是通过NodePort对外暴露的(在查看命名空间为ingress-nginx的svc时可以看到type类型为NodePort)
这样子会在用户访问时中间的转发在多一层,我们可以使用hostnetwork的方式,将Ingress-Controller使用的网络与宿主机公用,并且通过DaemonSet的方式进行高可用
让Ingress共享宿主机网络
# 修改Ingress-controller的yaml配置文件
# 先拷贝一个yaml文件
root@k8s-master ingress]# cp ingress-controller-0.49.yaml ingress-controller-0.49-1.yaml
# 卸载之前的ingress
[root@k8s-master ingress]# kubectl delete -f ingress-controller-0.49.yaml
# 修改cp后的yaml文件,如下,加入"hostNetwork: True"
[root@k8s-master ingress]# vim ingress-controller-0.49-1.yaml
spec:
dnsPolicy: ClusterFirst
# pod使用宿主机网络
hostNetwork: True
containers:
- name: controller
image: lizhenliang/ingress-nginx-controller:v0.49.0
[root@k8s-master ingress]# kubectl apply -f ingress-controller-0.49-1.yaml
# 查看ingress在那个节点
[root@k8s-master ingress]# kubectl get pods -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-admission-create-xp5mk 0/1 Completed 0 36m 10.244.36.79 k8s-node1 <none> <none>
ingress-nginx-admission-patch-lvjf4 0/1 Completed 1 36m 10.244.36.80 k8s-node1 <none> <none>
ingress-nginx-controller-6dbdc4dcbb-smtgq 1/1 Running 0 36m 192.168.113.149 k8s-node1 <none> <none>
# 查看该节点的监听端口,可以看到该节点监听80和443端口了
[root@k8s-node1 ~]# netstat -antp |grep 80
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 36992/nginx: master
tcp 0 0 0.0.0.0:30805 0.0.0.0:* LISTEN 63547/kube-proxy
tcp6 0 0 :::80 :::* LISTEN 36992/nginx: master
[root@k8s-node1 ~]# netstat -antp |grep 443
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 36992/nginx: master
上诉可知,Ingress已经共享宿主机网络,那么访问的话也直接访问即可,不需要在通过NodePort映射的端口进行访问了
当然,如果我们去把host映射改为其他的IP是访问不到的,因为他只是在149这个节点上运行的,其他节点没有Ingress-controller也不进行监听,如果想要实现多个节点都可以访问,可以将deployment改为DaemonSet方式部署
# 删除Ingress
[root@k8s-master ingress]# kubectl delete -f ingress-controller-0.49-1.yaml
# 修改Ingress-controller的yaml文件,将控制器Deployment改为DaemonSet
apiVersion: apps/v1
kind: Deployment
metadata:
...
# 查看Pod情况,之所以没有在master节点部署是因为master1有污点,可以去掉污点或者是在yaml中加入污点容忍即可
[root@k8s-master ingress]# kubectl get pods -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
ingress-nginx-admission-create-xhb68 0/1 Completed 0 115s 10.244.36.81 k8s-node1 <none> <none>
ingress-nginx-admission-patch-nf9mv 0/1 Completed 0 115s 10.244.36.82 k8s-node1 <none> <none>
ingress-nginx-controller-rk9b7 1/1 Running 0 52s 192.168.113.146 k8s-node2 <none> <none>
ingress-nginx-controller-smc8h 1/1 Running 0 52s 192.168.113.149 k8s-node1 <none> <none>
这里在说下用户访问的流程吧,两种方式
1、通过NodePort暴露Ingress controller pod:
浏览器 -> 负载均衡器 -> 域名:端口 -> service NodePort(HTTP:对应端口,HTTPS:对应端口) -> Ingress-nginx-controller(80,443 /他会基于域名去分流,正如之前的配置) -> 分布在各个节点的Pod
2、通过Hostnetwork让Ingress controller Pod共享宿主机网络
浏览器 -> 负载均衡器 -> 域名:端口 -> Ingress-nginx-controller(80,443 /基于域名分流) -> 分布在各个节点上的Pod