K8s实践练习9_流量入口Ingress

官网链接-ingress概念讲解

K8s集群搭建以及镜像源配置

搭建好一套k8s集群,可以参考我写的这篇教程:搭建k8s集群

k8s官方的镜像站在国内是拉不下来的,有几种方法解决:

  1. 在拉取镜像的虚拟机/服务器上科学上网
  2. 配置k8s的镜像源,目前国内只有阿里云支持改版后的k8s镜像源(registry.k8s.io)。
  3. 需要拉取镜像的时候,指定拉取策略为本地拉取(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.ioregistry.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:像建一个“小区大门”,所有访客先到小区大门,再根据地址(域名/路径)分到具体楼栋
  1. 端口与协议限制:官网中明确说:Ingress 不暴露任意端口或协议。把 HTTP/HTTPS 以外的服务暴露到互联网,通常用Service.Type=NodePort或Service.Type=LoadBalancer。(补充:Gateway API支持更多的协议,包括TCP/UDP)

  2. 多服务、多域名场景:如果只有一栋楼(一个应用),那用NodePort确实够了;但是在有一个小区(多个应用)的情况下,再给每栋楼开个对外的门,会造成管理混乱的问题。如果硬要用NodePort,那还需要自己做路由(Nginx)来分发请求。

  3. 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

安装 NGINX Ingress Controller(bare-metal)

 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后有下面的好处:

  1. 让 Ingress Controller 的 Pod 直接监听节点的 80/443;
  2. 不再依赖 NodePort Service;
  3. 访问方式变成标准的 http(s)://hello.example.com,不用再带 :31234 之类的端口

暂且搁置,以后有时间再研究

网站总访客数:Loading
网站总访问量:Loading
使用 Hugo 构建
主题 StackJimmy 设计