基本
svc的NodePort解决了4层的对外服务提供。但7层的功能svc无法解决。例如https。
ingress controller 可以将多个域名转发到service,提供负载均衡/SSL管理/虚拟命名主机/path路由。
ingress controller 负责实现功能,不过路由配置规则由ingress对象实现。
ingress-controller 的实现有很多,具体可以查看页面,k8s官方维护了aws
,gce
,nginx
。
https://kubernetes.io/zh/docs/concepts/services-networking/ingress-controllers/
例如:
- nginx ingress 基于 nginx 的 ingress 控制器
- istio ingress 基于 istio 的 ingress 控制器
- traefik ingress 基于 traefik 的 ingress 控制器
特点
-
ingress 提供路由规则给 ingress controller。ingress 和后端业务位于同一个 namespace 中。另外,configMap 也可以给 ingress controller 提供配置。
-
ingress controller 可以单独位于一个ns,它会自动去解析 Ingress 对象。
- 需要 ingress 对象通过 ingressClass对象关联 ingress controller。
-
ingress 针对的主要是 http 和 https. 这些之外的端口暴漏,如果仅仅是4层,则通过 Service.Type=NodePort 或者 Service.Type=LoadBalancer 即可。
流量路径
internet => Ingress => Service
组件
之后的信息以 ingress-nginx 为基准。
Ingress controller
部署文档:
https://github.com/kubernetes/ingress-nginx/blob/master/docs/deploy/index.md
或者
https://kubernetes.github.io/ingress-nginx/deploy/
通过helm来安装ingress-nginx
当前 ingress-nginx 的仓库地址是: https://hub.helm.sh/charts/ingress-nginx/ingress-nginx
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
配置ingress-nginx
## 查看 repo:ingress-nginx 下 chart:ingress-nginx 的所有版本
➜ helm search repo ingress-nginx/ingress-nginx -l
NAME CHART VERSION APP VERSION DESCRIPTION
ingress-nginx/ingress-nginx 4.0.19 1.1.3 Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx 4.0.18 1.1.2 Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx 4.0.17 1.1.1 Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx 4.0.16 1.1.1 Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx 4.0.15 1.1.1 Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx 4.0.13 1.1.0 Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx 4.0.12 1.1.0 Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx 4.0.11 1.1.0 Ingress controller for Kubernetes using NGINX a...
ingress-nginx/ingress-nginx 4.0.10 1.1.0 Ingress controller for Kubernetes using NGINX a...
...
✨需要注意的是,CHART VERSION 和 APP VERSION 都不是 nginx 版本。
CHART VERSION 是这个图表的版本,APP VERSION 是 ingress-nginx 的版本。
对应关系看:
https://github.com/kubernetes/ingress-nginx#support-versions-table
✨建议安装倒数第二个chart版本。
➜ version=4.0.18
➜ # helm fetch ingress-nginx/ingress-nginx
➜ helm show values ingress-nginx/ingress-nginx --version=${version} > values.yaml
helm 通过 values.yaml 去修改 template。
修改values.yaml
,参考下列信息:
-
command 的示例配置是之前的一种控制器关闭时防止丢失链接的方法。非必须添加。其中
sleep 5
是必须的,但原因不明。没有它,控制器关闭的时候会丢失连接。- 不过现在官方已经加入了 /wait-shutdown 命令来解决这个问题。
-
ingressClass 单 ingress 可以不用指定。如果创建多个 ingress-nginx,则在一个集群中名称不可以相同。
- ingress 对象通过这个名称来将自身规则写入到对应的 ingress-controller。
-
metrics 开启 prometheus 指标。示例配置里,监控指标服务类型是 clusterip,如果你需要集群外访问,则需要设置为nodeport
controller:
......
#lifecycle:
#preStop:
#exec:
#command: ["/bin/sh", "-c", "sleep 5; /usr/local/openresty/nginx/sbin/nginx -c /etc/nginx/nginx.conf -s quit; while pgrep -x nginx; do sleep 1; done"]
ingressClass: nginx
metrics:
port: 10254
# if this port is changed, change healthz-port: in extraArgs: accordingly
enabled: true
service:
annotations: #{}
prometheus.io/scrape: "true"
prometheus.io/port: "10254"
✨应该保存好 values.yaml,便于之后优雅的0停机升级。
安装ingress-nginx
➜ helm install ingress-nginx-001 ingress-nginx/ingress-nginx --version ${version} -n ingress-nginx --create-namespace -f values.yaml
kubectl get all -n ingress-nginx
===
NAME READY STATUS RESTARTS AGE
pod/ingress-nginx-controller-5886685d54-hf6l6 1/1 Running 0 68m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/ingress-nginx-controller LoadBalancer 10.96.210.139 <pending> 80:32489/TCP,443:30936/TCP 68m
service/ingress-nginx-controller-admission ClusterIP 10.96.128.101 <none> 443/TCP 68m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/ingress-nginx-controller 1/1 1 1 68m
NAME DESIRED CURRENT READY AGE
replicaset.apps/ingress-nginx-controller-5886685d54 1 1 1 68m
我们可以看到 ingress-nginx 控制器通过deployment.apps/ingress-nginx-controller
创建了一个 pod/ingress-nginx-controller-5886685d54-hf6l6
。这个 pod 其实就是一个 nginx. 它通过 ingress 定义的路由规则,来动态的变更nginx配置,从而正确的将流量转发给后端应用的service对象.
kubectl describe pod/ingress-nginx exec -it pod/ingress-nginx-controller-5886685d54-hf6l6
===
Requests:
cpu: 100m
memory: 90Mi
当前配置默认没有做limit限制. 所以需要关注ingress-nginx创建的pod所在物理节点的性能是否可以满足. 建议强制绑定到多个node节点(通过污点), 这批节点专门跑用来进行ingress控制器.
通过登陆pod,可以查看转化后的nginx配置.
kubectl --namespace ingress-nginx exec -it pod/ingress-nginx-controller-5886685d54-hf6l6 -- /bin/bash
==
cat /etc/nginx/nginx.conf
ingress
https://kubernetes.io/docs/concepts/services-networking/ingress/
client => http://one.foo.com/one => Ingress = > Service(one):8001 => pod
client => http://*.foo.com/other => Ingress => Service(other):8002 => pod
ingress编写路由规则,并由ingress-controller进行转化。
下面是一个示例:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
namespace: default
# annotations:
# nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: one.foo.com
http:
paths:
- path: /one
pathType: Prefix
backend:
service:
name: one
port:
number: 8001
- host: "*.foo.com"
http:
paths:
- pathType: Prefix
path: "/other"
backend:
service:
name: other
port:
number: 8002
🌟请注意, networking.k8s.io/v1
必须是v1.19+才可以使用, 如果你不是,则应该使用networking.k8s.io/v1beta1
❤spec.ingressClassName
用来指定 ingressClass 对象。
为了方便理解 ingress 的配置项,我参考 nginx 配置,来进行了一些横向对比:
nginx.servername: 这里是 spec.rules[].host
nginx.location: 这里是 spec.rules.http.paths[].path
nginx.upstream: 这里是 spec.rules.http.paths[].backend
关于 spec.rules[].host
这里有一些注意事项. host 支持统配匹配. 不过 *
不能跨级匹配. 例如,
*.abc.com 可以匹配 one.abc.com 但是不能匹配 two.one.abc.com.
关于 spec.rules.http.paths[].path
这里有一些注意事项.如上例子所述. pathType 定义了 path 的类型. 它有三个类型:
-
ImplementationSpecific: 根据选用的 ingress class 来定性.这是默认类型.
-
Exact: 严格匹配. 不能有一点不同.
-
Prefix: 基于"/"分段进行前缀匹配. 例如,
- /aaa/bbb/ 可以匹配任何 /aaa/bbb/ 开头的, 或者 /aaa/bbb. 因此 /aaa/bbbb 是不能匹配的.
- /aaa/ 可以匹配任何 /aaa/ 开头的, 或者 /aaa. 因此, /aaaa 是不能匹配的.
- / 可以匹配任何 / 开头的. 因此, 匹配所有.
💥请注意, Prefix 类型下.你写的匹配字符串首尾都需要加
/
, 如果你尾部没有加/
, 那么 ingress 在匹配的时候, 也会帮你加上/
.
当Exact和Prefix混用的时候, 如果一个请求同时匹配了这两个类型下的path, 那么Exact 优先级更高.
构建TLS
https://kubernetes.io/zh/docs/concepts/services-networking/ingress/#tls
默认情况下,如果为 Ingress 启用了 TLS,ingress-controller 将使用 308 永久重定向响应将 HTTP 客户端重定向到 HTTPS 端口 443。
💥ingress.spec.tls.hosts
必须完全包含ingress.spec.rules.hosts
手动证书
-
创建 Secret 对象, 导入证书文件
apiVersion: v1 kind: Secret metadata: name: foo-com-tls namespace: default data: tls.crt: base64 encoded cert # 这里需要填写 base64 编码后的 cert 内容. tls.crt 不可更名 tls.key: base64 encoded key # 这里需要填写 base64 编码后的 key 内容. tls.key 不可更名 type: kubernetes.io/tls
可以通过
cat 证书文件|base64
获取编码后的内容 -
在Ingress对象中调用 Secret
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: tls-example-ingress spec: tls: - hosts: - https-example.foo.com secretName: foo-com-tls rules: - host: https-example.foo.com http: paths: - path: / pathType: Prefix backend: service: name: service1 port: number: 80
…backend.service.name svc对象名
…backend.service.port svc对象端口
不同的Ingress控制器对于TLS这块也有一定差异.具体以控制器本身说明为准.
自动证书
构建cert-manager证书管理器。具体安装参见证书管理器文档。
问题点
本地裸机解决svc lb类型的问题
我们可以看到通过helm默认安装的 ingress crotroller 的 Service 对象是LoadBalancer类型. 这种类型我们用于云端环境. 而我这里测试环境是裸机,因此你可以看到这里它一直无法获取到EXTERNAL-IP
.
这意味着,我们无法将流量通过service/ingress-nginx-controller
传递到pod/ingress-nginx-controller-5886685d54-hf6l6
.🙄
如何解决这个问题.k8s官方文档这里提供了多种裸机安装下的解决方案:
https://kubernetes.github.io/ingress-nginx/deploy/baremetal/
这里通过 https://kubernetes.github.io/ingress-nginx/deploy/baremetal/#a-pure-software-solution-metallb 纯软件方案来解决 LB 类型的 EXTERNAL-IP
获取问题. 其它方案并不是采用的 LB 类型, 因此这里不再叙说.
⚠️metallb 方案在当前并不是一个成熟的方案,当然它应该是可用的😄 所以用于本地测试环境是可以的. 线上环境建议直接用云服务的LB即可.
安装metallb模拟LB
https://metallb.universe.tf/installation/#installation-with-helm
metallb 模拟了云环境中的负载均衡器功能.
🌟If you’re using kube-proxy in IPVS mode, since Kubernetes v1.14.2 you have to enable strict ARP mode.
如果你是ipvs 且 k8s 版本在1.14.2以上, 那么你需要启动严格arp模式.
kubectl edit configmap -n kube-system kube-proxy
===
ipvs:
strictARP: true
开始安装
helm repo add metallb https://metallb.github.io/metallb
helm fetch metallb/metallb
修改 metalla/values.yaml, 添加地址池,用来给svc下发ip。请记住, 这些地址必须是没有被其它资源占用的.
configInline:
address-pools:
- name: default
protocol: layer2
addresses:
- 10.200.16.11 - 10.200.16.19
通过 helm 安装
kubectl create ns metallb-system
helm install metallb --namespace metallb-system -f metallb/values.yaml metallb/metallb
# 下面这个命令仅在你初始安装的时候需要运行
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
下面,让我们再看看 ingress crotroller 的 svc 对象信息.
kubectl get svc -n ingress-nginx
===
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.96.210.139 10.200.16.11 80:32489/TCP,443:30936/TCP 88m
ingress-nginx-controller-admission ClusterIP 10.96.128.101 <none> 443/TCP 88m
终于svc/ingress-nginx-controller
拿到了一个EXTERNAL-IP
, 现在你就可以将域名解析到EXTERNAL-IP
, 并通过这个域名来访问你的应用了.(而且它必须是能通过80访问的.)😄
假设已经设置编写一个 ingress,则应用后如下所示:
kubectl get ingress -n it
===
NAME CLASS HOSTS ADDRESS PORTS AGE
test-it-local-ingress <none> test.it.local 10.200.16.11 80 106m
可以看到 ingress 对象中也显示了 metallb 的下发的ip.
你可以在任意一个节点上通过 ipvs 规则来看到转发情况.
ipvsadm -L -n
===
TCP 10.200.16.11:80 rr
-> 10.97.2.57:80 Masq 1 0 0
TCP 10.200.16.11:443 rr
-> 10.97.2.57:443 Masq 1 0 0
这里 10.97.2.57 是 ingress 控制器的 pod ip.
暴漏非80和非443端口
https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/
默认你会发现ingress无法暴漏80和443之外的端口.这是因为默认ingress-nginx并没有开启4层转发.
开启ingress-nginx中pod的4层转发
检查 deployment: ingress-nginx 中 pod 模板是否开启了下列参数
deploymentName=`kubectl get all -n ingress-nginx | grep deployment | awk '{print $1}'`
kubectl edit ${deploymentName} -n ingress-nginx
===
- args:
- --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
- --udp-services-configmap=$(POD_NAMESPACE)/udp-services
创建暴漏端口的配置文件
apiVersion: v1
kind: ConfigMap
metadata:
name: tcp-services
namespace: ingress-nginx
data:
8080: "it/test-it-local-svc:8080"
这里8080: "it/test-it-local-svc:8080"第一个8080指的是ingress-nginx的pod对象里需要监听的端口,
it/test-it-local-svc:8080` 指的是ns:it下的svc对象test-it-local-svc的8080端口
添加暴漏端口到ingress-nginx中svc对象
kubectl get all -n ingress-nginx | grep service | awk '{print $1}'
kubectl edit service/ingress-nginx-controller -n ingress-nginx
===
- name: test-8080
port: 8080
protocol: TCP
targetPort: 8080
三步过后, 不出意外, 你将可以通过ingress-nginx的pod对象中的nginx.conf文件看到tcp的转发配置.
# TCP services
server {
preread_by_lua_block {
ngx.var.proxy_upstream_name="tcp-it-test-it-local-svc-8080";
}
listen 8080;
proxy_timeout 600s;
proxy_pass upstream_balancer;
}
ingress无法创建,报找不到控制器的svc
Error from server (InternalError): error when creating "cms-ingress.yaml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io": Post "https://ingress-nginx-controller-admission-nginx.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": service "ingress-nginx-controller-admission-nginx" not found
上述问题,常见于通过helm重建ingress-nginx之后,这是因为ValidatingWebhookConfiguration
陈旧配置没有被删除,以至于ingress找到了错误的配置.
➜ kubectl get ValidatingWebhookConfiguration
NAME WEBHOOKS AGE
ingress-nginx-admission 1 46m
ingress-nginx-admission-nginx 1 29d
ingress-nginx-doc-it-local-admission 1 448d
如上所示,我这里有两个陈旧的配置ingress-nginx-admission-nginx
和ingress-nginx-doc-it-local-admission
,删除即可.
优雅更新ingress控制器
启动临时控制器
临时控制器用于临时接收现有流量,创建临时控制器,并且版本和配置与原控制器一致。
kubectl create ns ingress-nginx-temp
# 原控制器版本
oldVersion=
# 查看原控制器的 values
helm get values ingress-nginx
# 通过原控制器的 values.yaml 创建原控制器版本的临时版本
helm install ingress-nginx-temp ingress-nginx/ingress-nginx --version ${oldVersion} -n ingress-nginx-temp -f values-${oldVersion}.yaml
切换DNS,指向临时控制器
✨通过观察原控制器日志,等待原控制器流量结束。
导出新控制器配置
# 新版本
newVersion=
# 导出新版本的 ingress-nginx 的 values.yaml
helm show values ingress-nginx/ingress-nginx --version=${newVersion} > values-${newVersion}.yaml
比对新旧配置
将旧的配置转移到新配置中,并确认新配置中有没有额外的参数
更新原控制器
# values.yaml 更新后的配置
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx --version ${newVersion} -n ingress-nginx -f values-${newVersion}.yaml
查看结果
kubectl get deployment nginx-ingress-controller -n ingress-nginx -o yaml
切换DNS,指向原控制器
✨通过观察临时控制器日志,等待临时控制器流量结束
删除临时控制器
helm delete --purge nginx-ingress-temp --namespace ingress-nginx-temp
helm delete ns ingress-nginx-temp
release 卸载
helm uninstall ingress-nginx
release 版本
helm history ingress-nginx
release 回滚
helm rollback ingress-nginx <history_num>