官网链接-ingress概念讲解
K8s集群搭建以及镜像源配置
搭建好一套k8s集群,可以参考我写的这篇教程:搭建k8s集群
k8s官方的镜像站在国内是拉不下来的,有几种方法解决:
- 在拉取镜像的虚拟机/服务器上科学上网
- 配置k8s的镜像源,目前国内只有阿里云支持改版后的k8s镜像源(registry.k8s.io)。
- 需要拉取镜像的时候,指定拉取策略为本地拉取(
imagePullPolicy:Never),每次需要拉取镜像前都手动拉取/上传一份镜像到服务器上再导入镜像
这里给出阿里云镜像源的配置教程:
旧版的k8s直接修改/etc/containerd/config.toml里的mirror信息,添加上阿里云的镜像站就行。但是新版的不支持inline或者说暂时兼容,未来不支持。所以这里就只给出新版k8s镜像源配置教程。
修改/etc/containerd/config.yaml,填入下列信息(如果你已经有了config.yaml且这个配置文件是从containerd默认配置里生成的,那直接备份,然后使用下面的内容)。sudo vim /etc/containerd/config.yaml
1
2
3
4
5
6
7
8
9
10
|
version = 2
[plugins."io.containerd.grpc.v1.cri"]
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.9"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".registry]
config_path = "/etc/containerd/certs.d"
|
创建/etc/containerd/certs.d目录,在这个目录填入docker.io和registry.k8s.io的镜像源。
注意:k8s里修改镜像源之后,使用kubectl describe pod <pod_name> 查看时还是显示的docker.io和registry.k8s.io。配置镜像源只物理修改从哪里修改,不改镜像拉取的逻辑源。所以改好镜像源之后也不太好验证成功,随便拉个镜像sudo crictl pull nginx:1.14.2,能拉下来就是成了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# Docker Hub 加速
sudo mkdir -p /etc/containerd/certs.d/docker.io
sudo tee /etc/containerd/certs.d/docker.io/hosts.toml << 'EOF'
server = "https://registry-1.docker.io"
[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]
EOF
# K8s 镜像加速
sudo mkdir -p /etc/containerd/certs.d/registry.k8s.io
sudo tee /etc/containerd/certs.d/registry.k8s.io/hosts.toml << 'EOF'
server = "https://registry.k8s.io"
[host."https://registry.cn-hangzhou.aliyuncs.com/google_containers"]
capabilities = ["pull", "resolve"]
override_path = true
EOF
|
到这里,镜像源就配置好了,如果不出意外,文件目录应该是下面这样:
1
2
3
4
5
6
7
|
rust@k8s1:/etc/containerd$ ll
总计 28
drwxr-xr-x 3 root root 4096 2月 4 10:31 ./
drwxr-xr-x 144 root root 12288 2月 2 17:01 ../
drwxr-xr-x 4 root root 4096 2月 2 16:44 certs.d/
-rw-r--r-- 1 root root 423 2月 2 19:02 config.toml
-rw-r--r-- 1 root root 886 12月 19 02:48 config.toml.dpkg-dist
|
修改完配置文件后需要重启containerd:
1
2
|
sudo systemctl restart containerd
sudo systemctl status containerd
|
Ingress原理介绍与对比
Service是“后端+负载均衡”的抽象,解决“怎么找到 Pod”和“在多个Pod间做负载均衡”。
Ingress是“前置网关+路由规则”,在HTTP(S)这一层,做“HTTP路由+TLS+虚拟主机”,根据域名、路径等把流量分到不同的Service。
Gateway API是Ingress的“升级版”,把“网关实例”和“路由规则”拆开,更标准、更强大、更适合生产。
没有ingress时,部署一套无状态应用
每个应用都需要一个NodePort或者LoadBalancer;同时需要记住"哪个端口对应哪个服务";
想加域名、HTTPS、路径重写,要自己写反向代理配置(Nginx/HAProxy 等)。
在学习/小集群场景下完全没问题,但是如果应用多起来(微服务架构),维护成本就变得很高了。
如果加上一个ingress,内容如下,就可以通过path+pathType(Prefix或者Exact)来区分不同的服务。
对外访问变成了http://demo.example.com/ → svc-a(前端),http://demo.example.com/api → svc-b(后端)。
而 svc-a / svc-b 可以只是普通 ClusterIP Service,不需要 NodePort/LoadBalancer。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: demo.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-a
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: svc-b
port:
number: 80
|
Service与Ingress都有负载均衡的作用,但这两个工作的网络层次不同,面向的对象也不同。
Service工作在L4传输层(IP:port),给一组 Pod 一个统一的VIP(ClusterIP),把TCP/UDP连接均匀分发到后端Pod。
NodePort/LoadBalancer类型Service,本质上也是在节点上开一个端口(NodePort)或云厂商给你分配一个外部 LB;
再把这个L4流量转发到Service的ClusterIP,最后到 Pod。
Ingress工作在L7应用层(HTTP/HTTPS),它本身是个“HTTP 反向代理”(Nginx、Envoy、Traefik 等);
根据域名、路径、Header等把请求路由到不同 Service;在同一个 Ingress 内部,它会对后端Service的多个Pod做L7负载均衡(轮询、权重、健康检查等)。
- Service:解决“Pod 挂了怎么办、怎么在多个 Pod 间轮询”,是“后端池”。
- Ingress:解决“一个入口怎么统一路由给很多后端服务”,是“前端网关”。
Service是怎么通过VIP实现负载均衡的?
在k8s里,当创建一个type: ClusterIP的Service 时:
控制平面会从某个预先保留的 IP 段(–service-cluster-ip-range)里分配一个 IP 地址,这就是.spec.clusterIP。
这个 IP 不是一个真正绑定在某个网卡上的 IP,而是一个 “虚拟 IP(Virtual IP)”,只存在于 kube-proxy / CNI 的规则里。
kube-proxy(或替代组件)在每个节点上运行,监听 Service 和 EndpointSlice。
对每个 ClusterIP:Port,它在节点上配置一套规则(iptables / IPVS / eBPF 等),规则内容类似:
“所有发往 10.96.0.1:80的包,都改成一个后端 Pod 的 IP:Port,并做负载均衡轮询”。
当一个 Pod(或集群外通过NodePort进来的包)访问10.96.0.1:80时:
实际上不会真正有“机器”监听在这个 IP 上;
而是经过这些规则,通过iptables/IPVS被DNAT(目标地址转换) 成某个真实 Pod IP:Port。
反向代理和路由的区别:
反向代理:是一个“服务器角色”,站在客户端和后端服务器之间,帮服务器收请求、再转发给后端,通常在“应用层”(HTTP 等)做这件事。
路由:是一种“规则/机制”,用来决定“一个请求(或包)该往哪儿走”,可以在网络层(IP 路由)、也可以在应用层(URL 路由)做。
关系:反向代理是“做代理的那台机器”,路由是它内部决定怎么转发的“规则”。
类比就像:反向代理是一个“门卫”,路由是它手里拿的“路线图”,门卫根据路线图把人(请求)带到对应的办公室(后端服务)。
既然 NodePort 就能用,为什么还要 Ingress?
类别一下:
- NodePort:像给每栋楼开一个单独的大门,每栋楼自己有门牌号(端口)。
- Ingress:像建一个“小区大门”,所有访客先到小区大门,再根据地址(域名/路径)分到具体楼栋
-
端口与协议限制:官网中明确说:Ingress 不暴露任意端口或协议。把 HTTP/HTTPS 以外的服务暴露到互联网,通常用Service.Type=NodePort或Service.Type=LoadBalancer。(补充:Gateway API支持更多的协议,包括TCP/UDP)
-
多服务、多域名场景:如果只有一栋楼(一个应用),那用NodePort确实够了;但是在有一个小区(多个应用)的情况下,再给每栋楼开个对外的门,会造成管理混乱的问题。如果硬要用NodePort,那还需要自己做路由(Nginx)来分发请求。
-
TLS、虚拟主机、路径重写等能力:Ingress支持终止TLS(配置证书,统一 HTTPS);虚拟主机(根据 Host头路由到不同服务);路径重写、限流、白名单等(通过注解或 Gateway API)。这些如果用 NodePort,就要自己在外面再搭一套反向代理,反而更麻烦。
Ingress和Gateway API区别
官网说:Kubernetes 项目推荐使用 Gateway 代替 Ingress。Ingress API 已冻结。
也就是说Ingress 不会再有新功能,只做维护;新特性、新标准都会放在 Gateway API 里。
| 维度 |
Ingress |
Gateway API |
| 目标协议 |
主要 HTTP/HTTPSkubernetes.io |
HTTP、HTTPS、gRPC、TCP、UDP 等多协议 |
| API 风格 |
单一 Ingress 资源,把“网关 + 路由规则”写在一起 |
拆分为 GatewayClass、Gateway、HTTPRoute 等角色化资源 |
| 角色/职责 |
没有明确区分基础设施和开发者 |
明确区分:Gateway 归集群管理员,HTTPRoute 归应用开发者 |
| 高级路由 |
仅主机 + 路径,高级功能依赖各控制器注解 |
原生支持 Header 匹配、流量分割、镜像、超时、重试等 |
| 可移植性 |
各控制器实现差异大,注解各不相同 |
标准化 API,跨实现更可移植 |
| 状态 |
已冻结,不再演进 |
活跃开发中,未来会持续增加新能力 |
无状态应用Ingress改造
前置内容:无状态应用
改造前客户端通过NodePort直连前端,改造后用ingress统一入口,客户端访问ingress。
前端 Service:从 type: NodePort 改成 type: ClusterIP。
后端 Service:保持不变(仍然 ClusterIP)。
新增:Ingress Controller(这里用NGINX Ingress);一个Ingress资源,把hello.example.com/转发到 frontend:80
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
curl -O https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.14.3/deploy/static/provider/baremetal/deploy.yaml
# 官方仓库的镜像拉不下来,修改为阿里云的镜像源
sed -i 's|registry.k8s.io/ingress-nginx/controller:v1.14.3@.*|registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingress-controller:v1.14.3|' deploy.yaml
sed -i 's|registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.6.7@.*|registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.6.7|' deploy.yaml
# 部署
kubectl apply -f deploy.yaml
rust@k8s1:~/k8s_try$ curl -O https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.14.3/deploy/static/provider/baremetal/deploy.yaml
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 16279 100 16279 0 0 22100 0 --:--:-- --:--:-- --:--:-- 22088
rust@k8s1:~/k8s_try$ kubectl apply -f deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
|
这个yaml将创建一个 ingress-nginx namespace;部署 NGINX Ingress Controller;
创建一个 ingress-nginx-controller Service,类型为 NodePort,在 80/443 端口上暴露。
应用deloy.yaml创建了好多东西,如果过程中出现了一些问题,比如镜像拉不下来,想重做,用kubectl delete -f deploy.yaml即可删除用这个文件创建的所有资源。
查看安装状态,如果pod有三个ingress-nginx-admission-create-xx、ingress-nginx-admission-patch-xx、 ingress-nginx-controller-74d74b7998-xx。属于正常情况,creat和patch是两个job,在执行完成后会自动删除。
1
2
3
4
5
6
7
8
9
10
|
kubectl get pod -n ingress-nginx
kubectl get svc -n ingress-nginx
rust@k8s1:~/k8s_try$ kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-65b88c9cd4-vc4qf 1/1 Running 0 8m56s
rust@k8s1:~/k8s_try$ kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller NodePort 10.99.59.37 <none> 80:31238/TCP,443:32350/TCP 8m33s
ingress-nginx-controller-admission ClusterIP 10.98.81.151 <none> 443/TCP 8m33s
|
HTTP NodePort:比如80:31238/TCP,这里的31238;
HTTPS NodePort:比如443:32350/TCP,这里的32350。
访问方式:http://任意节点IP:31238,https://任意节点IP:32350。返回结果目前应该是HTTP ERROR 503。
把前端 Service 改成 ClusterIP
新建/修改 frontend-service.yaml,去掉 type: NodePort 和 nodePort。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
selector:
app: hello
tier: frontend
ports:
- protocol: TCP
port: 80
targetPort: 80
# 不写 type,默认就是 ClusterIP
|
应用yaml文件。
1
2
|
vim frontend-service.yaml
kubectl apply -f frontend-service.yaml
|
后端 Service 保持不变。
创建 Ingress 资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-ingress
annotations:
# 如果后面要用路径重写,可以开这个注解(按需)
# nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx # NGINX Ingress Controller 默认使用 nginx 这个 class
rules:
# - host: hello.example.com # 改成你自己的域名,或者先删掉这行用 IP 访问
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend # 对应前端Service 名字
port:
number: 80
|
新建并应用frontend-ingress.yaml。
1
2
|
vim frontend-ingress.yaml
kubectl apply -f frontend-ingress.yaml
|
查看ingress状态
1
2
3
4
5
6
|
kubectl get ingress
kubectl describe ingress frontend-ingress
rust@k8s1:~/k8s_try$ kubectl get ingress
NAME CLASS HOSTS ADDRESS PORTS AGE
frontend-ingress nginx * 192.168.146.202 80 86s
|
验证nginx服务状态
1
2
3
4
|
curl <Host>:<Port>
rust@k8s1:~/k8s_try$ curl k8s1:31238
{"message":"Hello"}
|
进阶玩法:把 NGINX Ingress 改成 HostNetwork
把 NGINX Ingress 改成 HostNetwork后有下面的好处:
- 让 Ingress Controller 的 Pod 直接监听节点的 80/443;
- 不再依赖 NodePort Service;
- 访问方式变成标准的 http(s)://hello.example.com,不用再带 :31234 之类的端口
暂且搁置,以后有时间再研究