当前位置: 首页 > news >正文

kubernetes(4) 微服务

一、什么是微服务

在 Kubernetes 中,控制器负责维持业务副本,但真正把业务“暴露”出去的是 Service
一句话理解:

  • Service = 一组 Pod 的稳定访问入口 + 4 层负载均衡

  • Ingress = 7 层路由 + 统一入口 + 灰度 / 认证 / 重写等高级能力

默认情况下,Service 仅具备 4 层(TCP/UDP)能力,如需 7 层(HTTP/HTTPS)请使用 Ingress。


二、微服务(Service)的四种类型总览

类型作用与适用场景
ClusterIP默认值,集群内部虚拟 IP;仅供集群内部访问,自动 DNS 与服务发现。
NodePort在每个节点打开固定端口(30000-32767),外部通过 节点IP:端口 即可访问服务。
LoadBalancer基于 NodePort,再申请外部云负载均衡(或 MetalLB);适用于公有云或裸金属+MetalLB。
ExternalName将集群内请求通过 CNAME 转发到任意指定域名;常用于外部服务迁移或跨集群调用。

三、Service 的默认实现:iptables vs IPVS

  • iptables 模式(默认)

    • 规则多、刷新慢,万级 Pod 场景下 CPU 抖动明显。

  • IPVS 模式(推荐生产)

    • 内核级四层负载,支持 10w+ 连接,性能稳定。

    • 切换后自动生成虚拟网卡 kube-ipvs0,所有 ClusterIP 被绑定到该接口。

3.1 一键切换到 IPVS 模式

1.所有节点安装工具

yum install ipvsadm -y

2.修改 kube-proxy 配置

    kubectl -n kube-system edit cm kube-proxy
    # 找到 mode 字段,改为 "ipvs"

    如果什么都没有,说明是默认的使用iptables,这里我们加上ipvs

    修改配置后,要重启,这里可以删掉之前的网络配置pod,重新刷新新的pod出来,此时就是新策略的pod

    3.滚动重启 kube-proxy Pod

      kubectl -n kube-system get pods | awk '/kube-proxy/{system("kubectl -n kube-system delete pod "$1)
      }'

      4.验证

        ipvsadm -Ln | head
        # 出现 10.96.x.x:xx rr 即成功

        四、微服务类型详解

        4.1 ClusterIP —— 集群内默认访问方式

        4.1.1 标准 ClusterIP 示例
        apiVersion: v1
        kind: Service
        metadata:labels:app: timingleename: timinglee
        spec:ports:- port: 80          # Service 端口protocol: TCPtargetPort: 80    # Pod 端口selector:app: timinglee    # 绑定到标签一致的 Podtype: ClusterIP     # 可省略,默认即 ClusterIP
        kubectl apply -f clusterip.yml
        kubectl get svc timinglee
        # NAME      TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
        # timinglee ClusterIP  10.99.127.134 <none>        80/TCP    16s

        clusterip模式只能在集群内访问,并对集群内的pod提供健康检测和自动发现功能

        追加内容,此时微服务和控制器就在一个配置文件里了

        只有集群内部的IP,集群外部的不暴露

        4.1.2 DNS 自动解析验证
        # 集群内部可直接解析
        dig timinglee.default.svc.cluster.local @10.96.0.10
        # ;; ANSWER SECTION:
        # timinglee.default.svc.cluster.local. 30 IN A 10.99.127.134


        4.2 Headless —— 无 ClusterIP 的直连模式

        适用场景:StatefulSet 的稳定网络标识、客户端自己做负载均衡、自定义 DNS 策略。

        apiVersion: v1
        kind: Service
        metadata:name: timinglee
        spec:clusterIP: None          # 关键字段ports:- port: 80targetPort: 80selector:app: timinglee

        之前有了无头服务,要删掉,不然影响实验

        没有了IP以后,后端就没有调度了

        此时我们可以用dns来写,把要访问的server直接指定到后端的服务器中去

        #开启一个busyboxplus的pod测试

        kubectl apply -f headless.yml
        kubectl get svc timinglee
        # NAME      TYPE       CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
        # timinglee ClusterIP  None         <none>        80/TCP    6s

        DNS 结果直接返回所有 Pod IP:

        dig timinglee.default.svc.cluster.local @10.96.0.10
        # ANSWER SECTION:
        # timinglee.default.svc.cluster.local. 20 IN A 10.244.2.14
        # timinglee.default.svc.cluster.local. 20 IN A 10.244.1.18

        4.3 NodePort —— 节点端口暴露

        4.3.1 快速示例
        apiVersion: v1
        kind: Service
        metadata:name: timinglee-service
        spec:type: NodePortports:- port: 80targetPort: 80nodePort: 31771          # 可省略,自动分配 30000-32767selector:app: timinglee
        kubectl apply -f nodeport.yml
        kubectl get svc timinglee-service
        # NAME      TYPE      CLUSTER-IP    EXTERNAL-IP  PORT(S)        AGE
        # timinglee NodePort 10.98.60.22   <none>       80:31771/TCP   8s

        之前的服务设置了无头服务,这里要删除之前环境,重新运行

        多了一个端口

        这个端口用来直接对外暴露

        外部访问测试:

        curl 172.25.254.100:31771/hostname.html
        # timinglee-c56f584cf-fjxdk

        nodeport在集群节点上绑定端口,一个端口对应一个服务

        直接负载到下面两个

        用clusterip来访问后端的

        访问模式

        对应的端口是不固定的,但是我们可以直接指定,但是有范围限制最大30000

        但是想要超过限制也可以,修改配置文件就行。但是集群会挂掉,要等待自愈

        加上这句话- --service-node-port-range=30000-40000

        刚刚还不能超过的限制,现在就可以了

        NodePort 默认端口范围 30000-32767;如需自定义范围,请修改 kube-apiserver 参数 --service-node-port-range=30000-40000

        4.4 LoadBalancer —— 云或裸金属的外部 VIP

        4.4.1 公有云场景
        apiVersion: v1
        kind: Service
        metadata:name: timinglee-service
        spec:type: LoadBalancerports:- port: 80targetPort: 80selector:app: timinglee

        云厂商(AWS/GCP/阿里云) 上,提交 YAML 后会自动为 Service 分配一个公网 VIP:

        kubectl get svc timinglee-service
        # NAME      TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
        # timinglee LoadBalancer  10.107.23.134  203.0.113.10     80:32537/TCP   4s
        4.4.2 裸金属场景:MetalLB

        MetalLB 为裸金属或私有集群实现 LoadBalancer 功能。

        ① 安装 MetalLB
        # 1) 确保 kube-proxy 为 IPVS 模式(见第 3.1 节)
        kubectl edit cm -n kube-system kube-proxy   # mode: "ipvs"
        kubectl -n kube-system get pods | awk '/kube-proxy/{system("kubectl -n kube-system delete pod "$1)}'# 2) 下载官方清单
        wget https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml# 3) 修改镜像地址(私有仓库)
        sed -i 's#quay.io/metallb/controller:v0.14.8#reg.timinglee.org/metallb/controller:v0.14.8#' \metallb-native.yaml
        sed -i 's#quay.io/metallb/speaker:v0.14.8#reg.timinglee.org/metallb/speaker:v0.14.8#' \metallb-native.yaml# 4) 推送镜像到 Harbor
        docker pull quay.io/metallb/controller:v0.14.8
        docker pull quay.io/metallb/speaker:v0.14.8
        docker tag ... && docker push ...# 5) 部署
        kubectl apply -f metallb-native.yaml
        kubectl -n metallb-system wait --for=condition=ready pod -l app=metallb --timeout=120s
        ② 配置 IP 地址池
        # configmap.yml
        apiVersion: metallb.io/v1beta1
        kind: IPAddressPool
        metadata:name: first-poolnamespace: metallb-system
        spec:addresses:- 172.25.254.50-172.25.254.99     # 与本地网络同段
        ---
        apiVersion: metallb.io/v1beta1
        kind: L2Advertisement
        metadata:name: examplenamespace: metallb-system
        spec:ipAddressPools:- first-pool
        kubectl apply -f configmap.yml

        再次查看 Service:

        kubectl get svc timinglee-service
        # NAME      TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
        # timinglee LoadBalancer  10.109.36.123   172.25.254.50   80:31595/TCP   9m9s

        集群外直接访问 VIP:

        curl 172.25.254.50
        # Hello MyApp | Version: v1 | ...

        部署安装

        必须要把集群做成ipvs的模式

        并且重启网络方面的pod

        这个文档里的路径已经修改好了,如果是未修改的,记得把路径换成自己的软件仓库

        这个生效了之后,才能改配置

        之前这里还是正在生效,现在已经有了IP

        #通过分配地址从集群外访问服务

        已经自动分配对外IP


        4.5 ExternalName —— DNS CNAME 转发

        开启services后,不会被分配IP,而是用dns解析CNAME固定域名来解决ip变化问题

        一般应用于外部业务和pod沟通或外部业务迁移到pod内时

        在应用向集群迁移过程中,externalname在过度阶段就可以起作用了。

        集群外的资源迁移到集群时,在迁移的过程中ip可能会变化,但是域名+dns解析能完美解决此问题

        • 业务尚在集群外(如 RDS、COS、第三方 API)。

        • 迁移过程中保持调用方 域名不变,仅改 DNS 指向。

        apiVersion: v1
        kind: Service
        metadata:name: timinglee-service
        spec:type: ExternalNameexternalName: www.timinglee.org   # 目的域名
        kubectl apply -f externalname.yml
        kubectl get svc timinglee-service
        # NAME      TYPE           CLUSTER-IP   EXTERNAL-IP         PORT(S)   AGE
        # timinglee ExternalName   <none>       www.timinglee.org   <none>    2m58s

        集群内 Pod 访问 timinglee-service 时,DNS 会直接返回 www.timinglee.org 的地址,无需维护 IP 变化。

        集群内部的IP在访问时,做的是域名解析

        把真实的微服务转化成其他主机上

        没有IP时如何被访问的,通过dns的域名解析

        验证 DNS 解析

        这行命令测试 Kubernetes 集群内部 DNS(10.96.0.10是集群 DNS 服务的 IP)是否能正确解析ext-service对应的域名:

        结果显示ext-service.default.svc.cluster.local(集群内部服务域名)被解析为www.baidu.com
        最终解析到百度的实际 IP 地址(103.235.46.115和103.235.46.102)

        得到了集群内部的主机

        微服务把集群外部的资源映射到集群内部,让集群内部可以使用


        五、Ingress-Nginx 全景实战

        Ingress = 7 层路由 + 多域名 + 灰度 + 认证 + TLS + 重写

        在service前面在加一个nginx

        在集群暴露时,再加一个反向代理

        一种全局的、为了代理不同后端 Service 而设置的负载均衡服务,支持7层

        Ingress由两部分组成:Ingress controller和Ingress服务

        Ingress Controller 会根据你定义的 Ingress 对象,提供对应的代理能力。

        业界常用的各种反向代理项目,比如 Nginx、HAProxy、Envoy、Traefik 等,都已经为Kubernetes 专门维护了对应的 Ingress Controller。

        5.1 部署 Ingress-Nginx(裸金属)

        # 1) 下载官方清单
        wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.2/deploy/static/provider/baremetal/deploy.yaml# 2) 镜像同步到私有仓库
        docker tag registry.k8s.io/ingress-nginx/controller:v1.11.2 \reg.timinglee.org/ingress-nginx/controller:v1.11.2
        docker tag registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.4.3 \reg.timinglee.org/ingress-nginx/kube-webhook-certgen:v1.4.3
        docker push ...# 3) 修改清单中的镜像地址
        sed -i 's#registry.k8s.io/ingress-nginx/controller#reg.timinglee.org/ingress-nginx/controller#' deploy.yaml
        sed -i 's#registry.k8s.io/ingress-nginx/kube-webhook-certgen#reg.timinglee.org/ingress-nginx/kube-webhook-certgen#' deploy.yaml# 4) 部署 & 等待就绪
        kubectl apply -f deploy.yaml
        kubectl -n ingress-nginx wait --for=condition=ready pod -l app.kubernetes.io/name=ingress-nginx --timeout=120s

        在部署文件里

        上传ingress所需镜像到harbor

        运行配置文件,并且查看是否建立了新的命名空间

        此时查看还是没有对外开放的ip的,因为微服还没有修改,现在还是只能集群内部访问

        #修改微服务为loadbalancer

        此时就有对外开放的IP了

        在ingress-nginx-controller中看到的对外IP就是ingress最终对外开放的ip

        测试ingress

        生成一下模板

        上面是控制器,下面是微服务

        默认 Service 类型为 NodePort,如需 LoadBalancer

        kubectl -n ingress-nginx edit svc ingress-nginx-controller
        # 把 type: NodePort 改为 type: LoadBalancer
        kubectl -n ingress-nginx get svc
        # NAME                       TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)
        # ingress-nginx-controller   LoadBalancer   10.103.33.148   172.25.254.50   80:34512/TCP,443:34727/TCP

        5.2 基于路径的多版本分流

        5.2.1 部署两套版本业务
        # myapp-v1.yaml
        apiVersion: apps/v1
        kind: Deployment
        metadata:name: myapp-v1
        spec:replicas: 1selector:matchLabels: {app: myapp-v1}template:metadata:labels: {app: myapp-v1}spec:containers:- name: myappimage: myapp:v1
        ---
        apiVersion: v1
        kind: Service
        metadata:name: myapp-v1
        spec:selector:app: myapp-v1ports:- port: 80targetPort: 80
        kubectl apply -f myapp-v1.yaml
        # 同理再建 myapp-v2.yaml(镜像改为 v2,service 名为 myapp-v2)

         

        装了两台主机,两台主机呈现不同web页面

        核心动作都是nginx完成的

        调用nginx类,访问微服务的80端口

        当我们去访问我们刚刚设立的对外IP时,它带我们去看的时myappv1的80端口里的内容

        5.2.2 创建基于路径的 Ingress
        # ingress-path.yaml
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        metadata:name: ingress-pathannotations:nginx.ingress.kubernetes.io/rewrite-target: /
        spec:ingressClassName: nginxrules:- host: myapp.timinglee.orghttp:paths:- path: /v1pathType: Prefixbackend:service:name: myapp-v1port: {number: 80}- path: /v2pathType: Prefixbackend:service:name: myapp-v2port: {number: 80}
        kubectl apply -f ingress-path.yamlcurl http://myapp.timinglee.org/v1   # Version: v1
        curl http://myapp.timinglee.org/v2   # Version: v2

        此时直接访问是不行的,因为没有设定默认发布目录

        可以设定一下


        5.3 基于域名的多业务入口

        # ingress-domain.yaml
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        metadata:name: ingress-domain
        spec:ingressClassName: nginxrules:- host: myappv1.timinglee.orghttp:paths:- path: /pathType: Prefixbackend:service: {name: myapp-v1, port: {number: 80}}- host: myappv2.timinglee.orghttp:paths:- path: /pathType: Prefixbackend:service: {name: myapp-v2, port: {number: 80}}
        kubectl apply -f ingress-domain.yamlcurl http://myappv1.timinglee.org   # Version: v1
        curl http://myappv2.timinglee.org   # Version: v2

        子集写在最前面也行,写最后面也行

        此时我们


        5.4 HTTPS(TLS)一键启用

        5.4.1 自签证书 & Secret
        openssl req -x509 -newkey rsa:2048 -nodes -keyout tls.key -out tls.crt -days 365 \-subj "/CN=myapp-tls.timinglee.org"kubectl create secret tls web-tls-secret --cert=tls.crt --key=tls.key
        5.4.2 启用 TLS 的 Ingress
        # ingress-tls.yaml
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        metadata:name: ingress-tls
        spec:ingressClassName: nginxtls:- hosts: [myapp-tls.timinglee.org]secretName: web-tls-secretrules:- host: myapp-tls.timinglee.orghttp:paths:- path: /pathType: Prefixbackend:service: {name: myapp-v1, port: {number: 80}}
        kubectl apply -f ingress-tls.yamlcurl -k https://myapp-tls.timinglee.org   # HTTPS 成功

        生成证书去加密

        设置非交互式输入

        此时生成的证书与集群无关

        把证书变成资源,能被集群调用

        里面有两个文件

        查看资源信息,它们以键值的方式保存了

        要把加密的模式保存到配置文件中去

        一次性可以给多个设备加密 host

        最终要调用的资源里的证书

         

        查看新建的ingress的详细情况,是否加密成功

        此时直接访问已经不行了

        https:// 表示使用 HTTPS 协议 访问,符合 Ingress 配置中强制 HTTPS 的要求,因此不会被重定向。
        -k 参数的作用是 跳过 SSL 证书验证。如果你的 Ingress 使用的是自签名证书(而非可信 CA 颁发的证书),curl 会默认验证证书并报错(如 SSL certificate problem)。加上 -k 后会忽略证书验证,从而成功建立连接并获取响应。


        5.5 Basic Auth 用户认证

        5.5.1 生成密码文件并写入 Secret
        dnf install -y httpd-tools
        htpasswd -cm auth lee        # 输入两次密码
        kubectl create secret generic auth-web --from-file=auth
        5.5.2 在 Ingress 中开启认证
        # ingress-auth.yaml
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        metadata:name: ingress-authannotations:nginx.ingress.kubernetes.io/auth-type: basicnginx.ingress.kubernetes.io/auth-secret: auth-webnginx.ingress.kubernetes.io/auth-realm: "Please input username and password"
        spec:ingressClassName: nginxtls:- hosts: [myapp-tls.timinglee.org]secretName: web-tls-secretrules:- host: myapp-tls.timinglee.orghttp:paths:- path: /pathType: Prefixbackend:service: {name: myapp-v1, port: {number: 80}}

        用户级别的访问限制

        此时的文件还是没有关系和集群

        要通过这个命令,把这个叫做htpasswd的文件抽象成集群中的资源

        编辑配置文件,要用到参数

        在调用时,也会验证这些参数

        错误情况:

        这里会错误,是因为他默认调用auth这个名字,而之前创建用户密码时,储存的文件名不是它,系统此时访问不了

        修改名字:

        删除之前储存的资源

        重新建立资源

        删除之前运行的配置文件

        重新运行一次就行了

        kubectl apply -f ingress-auth.yamlcurl -k https://myapp-tls.timinglee.org          # 401 Unauthorized
        curl -k -u lee:lee https://myapp-tls.timinglee.org   # 200 OK


        5.6 URL 重写(Rewrite)与正则

        5.6.1 根路径重定向
        nginx.ingress.kubernetes.io/app-root: /hostname.html
        5.6.2 正则捕获与重写
        # ingress-rewrite.yaml
        metadata:annotations:nginx.ingress.kubernetes.io/use-regex: "true"nginx.ingress.kubernetes.io/rewrite-target: /$2
        spec:rules:- host: myapp-tls.timinglee.orghttp:paths:- path: /lee(/|$)(.*)          # 匹配 /lee 或 /lee/xxxpathType: ImplementationSpecificbackend:service: {name: myapp-v1, port: {number: 80}}
        curl -k -u lee:lee https://myapp-tls.timinglee.org/lee/hostname.html
        # 实际返回 /hostname.html 内容

        匹配正则表达式

        测试:


        六、金丝雀(Canary)发布

        6.1 核心思路

        • 先少量、后全量:降低新版本全量故障风险

        • Ingress-Nginx 支持 Header / Cookie / 权重 三种灰度策略

        • 发布过程 只增不减:Pod 总数 ≥ 期望值,业务无中断

        6.2 基于 Header 的灰度

        6.2.1 正式流量 Ingress(v1)
        # myapp-v1-ingress.yaml
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        metadata:name: myapp-v1-ingress
        spec:ingressClassName: nginxrules:- host: myapp.timinglee.orghttp:paths:- path: /pathType: Prefixbackend:service: {name: myapp-v1, port: {number: 80}}

        部署老版本的

        升级后的访问是要基于什么情况下访问

        要写参数来设置了

        当携带timinglee的值是6时,就访问new的

        6.2.2 灰度流量 Ingress(v2)
        # myapp-v2-canary-header.yaml
        apiVersion: networking.k8s.io/v1
        kind: Ingress
        metadata:name: myapp-v2-canaryannotations:nginx.ingress.kubernetes.io/canary: "true"nginx.ingress.kubernetes.io/canary-by-header: "version"nginx.ingress.kubernetes.io/canary-by-header-value: "2"
        spec:ingressClassName: nginxrules:- host: myapp.timinglee.orghttp:paths:- path: /pathType: Prefixbackend:service: {name: myapp-v2, port: {number: 80}}
        kubectl apply -f myapp-v1-ingress.yaml
        kubectl apply -f myapp-v2-canary-header.yaml# 测试
        curl http://myapp.timinglee.org                # v1
        curl -H "version: 2" http://myapp.timinglee.org # v2

        6.3 基于权重(Weight)的灰度

        # myapp-v2-canary-weight.yaml
        metadata:annotations:nginx.ingress.kubernetes.io/canary: "true"nginx.ingress.kubernetes.io/canary-weight: "10"   # 10%nginx.ingress.kubernetes.io/canary-weight-total: "100"
        kubectl apply -f myapp-v2-canary-weight.yaml# 100 次采样脚本
        for i in {1..100}; do curl -s myapp.timinglee.org | grep -c v2; done | awk '{v2+=$1} END{print "v2:"v2", v1:"100-v2}'
        # v2:10, v1:90   # 符合 10% 权重

        脚本测试:

        测试没有问题了

        就修改权重

        直到最后没有问题,old的版本就可以删除了

        调整权重只需修改 annotation 后 kubectl apply,即可实现 平滑全量滚动


        七、一键清理

        kubectl delete ingress --all
        kubectl delete svc --all -l app=myapp-v1,app=myapp-v2
        kubectl delete deploy --all -l app=myapp-v1,app=myapp-v2
        http://www.lryc.cn/news/622283.html

        相关文章:

      1. C#文件复制异常深度剖析:解决“未能找到文件“之谜
      2. 大白话解析 Solidity 中的防重放参数
      3. 大白话解析 Solidity 中的数据位置关键字 memory
      4. [激光原理与应用-284]:理论 - 波动光学 - 无线电波,无线通信的频谱
      5. 人工智能与社会治理:从工具到生态的范式重构
      6. 数据民主化×智能进阶化:AI+BI不可逆的决策革命已至
      7. Python 高级语法与用法详解 —— 提升编程效率与代码质量
      8. JUC LongAdder并发计数器设计
      9. Manus AI与多语言手写识别
      10. 项目生命周期
      11. stream流debug
      12. 源码分析mcp定义tools映射到LLM输入的过程
      13. L4 级别自动驾驶 软件架构设计
      14. Midjourney绘画创作入门操作
      15. 二十四、Mybatis-基础操作-删除(预编译SQL)
      16. 什么是可信空间的全域节点、区域节点、业务节点?
      17. 基于STM32与边缘计算的工业设备异常振动预警系统设计
      18. Pytest 插件使用指南:让你的测试更高效
      19. 深度解读 Browser-Use:让 AI 驱动浏览器自动化成为可能
      20. 如何回答研究过MQ的源码吗
      21. week1-[顺序结构]大海
      22. Centos7 服务管理
      23. SpatialVLM和SpatialRGPT论文解读
      24. ChatGPT-5(GPT-5)全面解析:一场从通用智能迈向专属智能的进化风暴,正在重塑人类与机器的协作边界
      25. C#---StopWatch类
      26. 【JAVA高级】实现word转pdf 实现,源码概述。深坑总结
      27. 用matlab实现的svdd算法
      28. 数据库(3)
      29. Java学习day_12之面向对象进阶(抽象类接口内部类)
      30. 【Golang】:流程控制语句