9.1 Ingress为弥补NodePort不足而生

1 单独用service暴露服务的方式,在实际生产环境中不太合适 ClusterIP 只能在集群内部访问。 NodePort 方式的话,测试环境使用还行,当有几十上百的服务在集群中运行时,NodePort的端口管理是灾难。 LoadBalance 方式受限于云平台,且通常在云平台部署ELB还需要额外的费用。

2 ingress与ingress-controller

ingress对象: 指的是k8s中的一个api对象,一般用yaml配置。作用是定义请求如何转发到service的规则,可以理解为配置模板。

ingress-controller:具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发。​​简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请求,比如哪些域名哪些path要转发到哪些服务等等。Ingress Controller会动态感知集群中的Ingress的规则变化,然后读取,动态生成Nginx的配置文件,最后注入到运行nginx的pod的中,然后会自动reload,配置生效。​

3 k8s真正提供服务的是pod,为了负载均衡,为了使用域名, service 诞生了,然后 ingress 诞生了,那么Ingress的作用如下

(1) 帮助位于集群中的Service能够有一个对外可达的url,即让集群外的客户端也可以访问到自己。(对于这一点,​​NodePort类型的Service可以实现,只支持4层负载均衡,一个端口只能一个服务使用,端口需提前规划​​)

(2) 做专业的负载均衡,毕竟Service的负载均衡还是很初级的

4 Ingress工作流程

如下图所示,流量到达外部负载均衡器(​​externalLB​​)后,首先转发至​​Service​​资源​​Ingres-nginx​​上,然后通过​​Ingress​​控制器基于​​Ingress​​资源定义的规则将客户端请求流量直接转发至与​​Service​​对应的后端​​Pod​​资源之上。这种转发机制会绕过​​Service​​资源(​​app Service​​;​​api Service​​),从而省去了由​​kube-proxy​​实现的端口代理开销。​​Ingress​​规则需要由一个​​Service​​资源对象辅助识别相关的所有​​Pod​​资源。如下​​Ingress​​通过​​app service​​资源去匹配后端的​​pod1​​和​​pod2​​;这个​​app service​​只是起到一个辅助识别功能。​​user -> 域名 -> node ip:80/443 -> ingress controller -> 域名分流 –> pod​

ingress controller pod -> 获取service(apiserver)关联的pod -> 应用到本地nginx(提供七层负载均衡)

因为是DaemoSet方式部署的ingress controller, 所以除master节点外每个从node节点都会部署一个ingress controller的pod并监听80和443端口。

第9章:Ingress_nginx

9.2 Pod与Ingress的关系

  • 通过Service相关联
  • 通过Ingress Controller实现Pod的负载均衡
  • 支持TCP/UDP 4层和HTTP 7层

第9章:Ingress_负载均衡_02

9.3 Ingress Controller

为了使Ingress资源正常工作,集群必须运行一个Ingress Controller(负载均衡实现)。

所以要想通过ingress暴露你的应用,大致分为两步:

  1. 部署Ingress Controller
  2. 创建Ingress规则

整体流程如下:

第9章:Ingress_json_03

Ingress Controller有很多实现,我们这里采用官方维护的Nginx控制器。

其他主流控制器:(1) Traefik: HTTP反向代理、负载均衡工具 (2) Istio:服务治理,控制入口流量

部署文档:​​https://github.com/kubernetes/ingress-nginx/blob/nginx-0.30.0/docs/deploy/index.md​

# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml

注意事项:
- 镜像地址修改成国内的(国外的源无法连接):lizhenliang/nginx-ingress-controller:0.20.0
- 使用宿主机网络(提高网络转发性能):hostNetwork: true

# vim mandatory.yaml
......
spec:
hostNetwork: true
serviceAccountName: nginx-ingress-serviceaccount
containers:
- name: nginx-ingress-controller
image: lizhenliang/nginx-ingress-controller:0.20.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
......

# kubectl apply -f mandatory.yaml
# kubectl get pods -n ingress-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-ingress-controller-9r25t 1/1 Running 0 5m16s 172.16.1.71 k8s-node1
nginx-ingress-controller-p8g66 1/1 Running 0 5m16s 172.16.1.72 k8s-node2

# 查看ingress-controller中的nginx配置文件
# kubectl exec -it nginx-ingress-controller-9r25t sh -n ingress-nginx
$ ls /etc/nginx/nginx.conf
/etc/nginx/nginx.conf
$

# 此时在任意Node上就可以看到该控制监听的80和443端口,80和443端口就是接收来自外部访问集群中应用流量,转发对应的Pod上
# netstat -natp |egrep ":80|:443"
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 83841/nginx: master
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 83841/nginx: master

9.4 Ingress

接下来,就可以创建ingress规则了。

在ingress里有三个必要字段:

  • host: 访问该应用的域名,也就是域名解析
  • serverName: 应用的service名称
  • serverPort: service端口

1、HTTP访问

# vim http.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-lc-ingress
spec:
rules:
- host: example.lc.com
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80

# kubectl apply -f http.yaml
# kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
example-lc-ingress example.lc.com 80 47s

# 添加windows本地hosts解析
C:\Windows\System32\drivers\etc\hosts
172.16.1.71 example.lc.com

# 使用域名访问
http://example.lc.com/

注意:

(1) 生产环境:example.lc.com 域名是在你购买域名的运营商上进行解析,A记录值为K8S Node的公网IP(该Node必须运行了Ingress controller)。

(2) 测试环境:可以绑定hosts模拟域名解析("C:\Windows\System32\drivers\etc\hosts"),对应IP是K8S Node的内网IP。例如:

172.16.1.71 example.lc.com

2、HTTPS访问

# vim sslhttp.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: sslexample-lc-ingress
spec:
tls:
- hosts:
- sslexample.lc.com
secretName: sslexample-lc-com
rules:
- host: sslexample.lc.com
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80

# kubectl apply -f sslhttp.yaml
# kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
sslexample-lc-ingress sslexample.lc.com 80, 443 8s

里面用到了secret名为example-lc-com,用于保存https证书。这里使用cfssl工具自签证书用于测试,先下载cfssl工具:

# cat 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

# cat 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 > sslexample.lc.com-csr.json <<EOF
{
"CN": "sslexample.lc.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 sslexample.lc.com-csr.json | cfssljson -bare sslexample.lc.com

执行certs.sh脚本,生成证书:
# sh certs.sh
# ls *.pem
ca-key.pem ca.pem sslexample.lc.com-key.pem sslexample.lc.com.pem

将证书保存在secret里:

# kubectl create secret tls sslexample-lc-com --cert=sslexample.lc.com.pem --key=sslexample.lc.com-key.pem
# kubectl get secret
NAME TYPE DATA AGE
default-token-wc5fp kubernetes.io/service-account-token 3 13d
sslexample-lc-com kubernetes.io/tls 2 16s

# kubectl describe secrets sslexample-lc-com

这样,ingress就能通过secret名称拿到要用的证书了,然后绑定本地hosts,就可以https访问了:​​https://sslexample.lc.com/​

3、根据URL路由到多个服务

# vim url.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: url-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: foobar.lc.com
http:
paths:
- path: /foo
backend:
serviceName: service1
servicePort: 80
- host: foobar.lc.com
http:
paths:
- path: /bar
backend:
serviceName: service2
servicePort: 80

工作流程:
foobar.lc.com -> 172.16.1.71 -> /foo   service1:80
/bar service2:80

4、基于名称的虚拟主机

# vim vhost.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.lc.com
http:
paths:
- backend:
serviceName: service1
servicePort: 80
- host: bar.lc.com
http:
paths:
- backend:
serviceName: service2
servicePort: 80

工作流程:
foo.lc.com --|                 |-> service1:80
| 172.16.1.71 |
bar.lc.com --| |-> service2:80

9.5 Annotations对Ingress个性化配置

参考文档 :​​https://github.com/kubernetes/ingress-nginx/blob/master/docs/user-guide/nginx-configuration/annotations.md​

HTTP:配置Nginx常用参数

# vim nginx_parameter.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: example-ingress
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: "10m"
spec:
rules:
- host: example.lc.com
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80

HTTPS:禁止访问HTTP强制跳转到HTTPS(创建https后通过http访问默认会跳转到https)
# vim redirect_https.yaml

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: tls-example-ingress
annotations:
kubernetes.io/ingress.class: "nginx“
nginx.ingress.kubernetes.io/ssl-redirect: 'true'
spec:
tls:
- hosts:
- sslexample.lc.com
secretName: secret-tls
rules:
- host: sslexample.lc.com
http:
paths:
- path: /
backend:
serviceName: nginx
servicePort: 80

9.6 Ingress Controller高可用方案

如果域名只解析到一台Ingress controller,是存在单点的,挂了就不能提供服务了。这就需要具备高可用,有两种常见方案:

第9章:Ingress_nginx_04

左边:双机热备,选择两台Node专门跑Ingress controller,然后通过keepalived对其做主备。用户通过VIP访问。

固定ingress controller到两个node上(daemonset+nodeselector),user -> 域名 -> vip(keepalived) ha -> pod

右边:高可用集群(推荐),前面加一个负载均衡器,转发请求到后端多台Ingress controller。

固定ingress controller到两个node上(daemonset+nodeselector),user -> 域名 -> LB(nginx、lvs、haproxy)-> ingress controller -> pod