Kubernetes调度与服务暴露:从“定时任务”到“服务发现”的完全指南

Kubernetes调度与服务暴露:从"定时任务"到"服务发现"的完全指南

引言:让集群"记住"该做什么,并让外部"找到"该访问哪里

从Linux cron到K8s CronJob:定时任务的"进化论"

想象一下你需要在每天凌晨2点备份数据库,或者在每小时第15分钟清理日志。在Linux服务器上,我们用cron来设置这些周期性的任务。在Kubernetes集群中,同样需要这样的"定时器"------这就是CronJob。

专业视角:CronJob是Kubernetes的工作负载API的一部分,用于创建和管理基于时间的、周期性的Job(即一次性任务)。它解决了容器化环境中自动化运维的定时需求,例如定期备份、报表生成、数据清理等。

从Pod直接访问到Service:服务暴露的"智慧门牌"

当你运行一组Pod提供Web服务时,这些Pod的IP地址会随着重启、扩缩容而变化。客户端如何稳定地访问它们?就像一栋大楼里有很多房间,每个房间的号码可能会变,但大楼的前台(Service)有一个固定的总机号码,你只要拨打总机,前台就会帮你转接到正确的房间。

专业视角:Service是Kubernetes中一种抽象,它定义了一组Pod的逻辑集合以及访问它们的策略。Service提供了一个固定的虚拟IP和DNS名称,作为前端,后端Pod的变化对客户端完全透明。


第一部分:CronJob------Kubernetes的"定时器"

CronJob是什么?为什么需要它?

类比 :Linux中有cron程序定时执行任务(比如每天凌晨运行备份脚本),Kubernetes的CronJob提供了完全类似的功能,只不过它调度的是Job(一次性Pod),而不是Shell命令。

核心特性:

  • 基于标准的Cron时间表语法
  • 自动创建和管理Job对象
  • 支持并发控制(允许、禁止、替换)
  • 可配置成功/失败历史保留数量

命名限制:CronJob的名称必须是一个合法的DNS子域值,且不超过52个字符。因为CronJob控制器会自动在名称后追加11个字符(用于生成Job名称),而Job名称总长度不能超过63个字符。

快速上手:创建第一个CronJob

使用kubectl命令创建

bash

复制代码
# 最简单的例子:每2分钟输出一行文字
[root@master30 ~]# kubectl create cronjob mycronjob \
    --image=busybox \
    --schedule='*/2 * * * *' \
    -- echo hello k8s job!

等待几分钟,你会看到自动生成的Pod和Job:

bash

复制代码
[root@master30 ~]# kubectl get all
NAME                           READY   STATUS      RESTARTS   AGE
pod/mycronjob-28301160-gvm2v   0/1     Completed   0          5m18s
pod/mycronjob-28301162-25jll   0/1     Completed   0          3m18s
pod/mycronjob-28301164-h7mf7   0/1     Completed   0          78s

NAME                      SCHEDULE      SUSPEND   ACTIVE   LAST SCHEDULE   AGE
cronjob.batch/mycronjob   */2 * * * *   False     0        78s             36m

NAME                           COMPLETIONS   DURATION   AGE
job.batch/mycronjob-28301160   1/1           18s        5m18s
job.batch/mycronjob-28301162   1/1           19s        3m18s
job.batch/mycronjob-28301164   1/1           19s        78s
等效的YAML配置文件

yaml

复制代码
apiVersion: batch/v1beta1   # 注意:v1版本中为batch/v1
kind: CronJob
metadata:
  name: mycronjob
spec:
  schedule: "*/2 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            command: ["echo", "hello k8s job!"]
          restartPolicy: Never

专业解释:

  • apiVersion:CronJob的API版本,在Kubernetes 1.21+为batch/v1,本文示例使用旧版batch/v1beta1。
  • spec.schedule:Cron时间表达式,定义了触发频率。
  • spec.jobTemplate:Job模板,定义了每次触发时要创建的任务规格。
  • restartPolicy: Never:Job的Pod完成运行后不重启。

CronJob核心参数详解

1. 时间表语法(标准Cron)

text

复制代码
# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6)(周日到周六)
# │ │ │ │ │
# * * * * *

常用示例:

表达式 含义
*/5 * * * * 每5分钟执行一次
0 2 * * * 每天凌晨2点执行
0 9 * * 1 每周一上午9点执行
0 0 1 * * 每月1号午夜执行
0 0 1 1 * 每年1月1日午夜执行
2. 任务模板(jobTemplate)

与标准的Job定义完全一致,包含Pod模板、并行策略、完成计数等。你可以为模板化的Job指定通用的元数据(标签、注解)。

3. 启动截止期限(startingDeadlineSeconds)

概念:如果任务由于某种原因错过了预期的调度时间,该参数定义了可以延迟启动的最大秒数。超过这个时间,本次调度将被跳过(视为失败),而未来的调度不受影响。

典型场景:你设置了一个每天凌晨2点的备份任务,允许最多延迟30分钟。如果凌晨2点时控制器因故没有创建Job,那么只要在2:30之前创建,它仍会执行;超过2:30,本次备份被跳过,等待第二天。

配置方式:

yaml

复制代码
spec:
  startingDeadlineSeconds: 200   # 最多延迟200秒

如果不设置,则没有最后期限,控制器会尽可能补执行错过的任务(但可能引发大量积压)。

4. 并发策略(concurrencyPolicy)

定义了当上一个任务尚未完成,新的调度时间又到来时的处理方式。

值 行为 适用场景
Allow(默认) 允许并发执行,新旧任务同时运行 任务可并发且资源充足
Forbid 禁止并发,新任务跳过,等待下一次调度 任务互斥(如操作同一文件)
Replace 终止当前正在运行的任务,用新任务替换 只关心最新状态的任务

注意:并发规则仅适用于同一个CronJob创建的任务。不同CronJob的任务总是允许并发。

5. 历史限制(successfulJobsHistoryLimit / failedJobsHistoryLimit)
  • successfulJobsHistoryLimit:保留的成功完成Job的数量(默认3)
  • failedJobsHistoryLimit:保留的失败Job的数量(默认1)

设置为0表示不保留对应类型的已完成Job。这些限制有助于防止大量历史Job堆积占用API资源。

综合案例

案例1:定期清理主机目录内容

需求 :每分钟清空worker31节点上的/var/data目录。

思路:

  • 使用hostPath卷将主机目录挂载到Pod中
  • 通过nodeName将Pod调度到指定节点
  • Pod执行rm -fr /var/data/*命令
  • 用CronJob每分钟触发一次

yaml

复制代码
apiVersion: batch/v1
kind: CronJob
metadata:
  name: clean
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          nodeName: worker31.whisky.cloud   # 固定到某个节点
          containers:
          - name: clean
            image: busybox
            command:
            - sh
            - -c
            - sleep 3 && rm -fr /var/data/*
            volumeMounts:
            - name: data
              mountPath: /var/data
          volumes:
          - name: data
            hostPath:
              path: /var/data
          restartPolicy: OnFailure

专业拓展 :生产环境不建议直接使用nodeName,而应使用nodeSelector或亲和性。此外,可以结合securityContext设置权限提升。

案例2:通过CronJob操作Kubernetes集群

需求 :每分钟删除namespace controller中标签为app=hello的所有Pod。

思路:

  • 使用bitnami/kubectl镜像(内含kubectl客户端)
  • 将当前集群的kubeconfig通过ConfigMap挂载到Pod中
  • Pod执行kubectl delete pods -l app=hello -n controller

yaml

复制代码
# 1. 创建ConfigMap保存kubeconfig
[root@master ~]# kubectl create cm kubeconfig --from-file=config=/root/.kube/config

# 2. 创建CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
  name: clean-pods
spec:
  schedule: "* * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: Never
          containers:
          - name: kubectl
            image: bitnami/kubectl:latest
            imagePullPolicy: IfNotPresent
            args:
            - delete
            - pods
            - -l
            - app=hello
            - -n
            - controller
            volumeMounts:
            - name: kubeconfig
              mountPath: "/.kube"
          volumes:
          - name: kubeconfig
            configMap:
              name: kubeconfig

安全提醒:生产环境中应使用RBAC和服务账户代替直接挂载管理员kubeconfig。可以创建一个具有特定权限的ServiceAccount,并授予其删除Pod的权限,然后将该ServiceAccount关联到CronJob的Pod。


第二部分:Service------稳定访问Pod的"智能代理"

为什么需要Service?

Pod的生命周期是短暂的,它们可以被创建、销毁、重新调度。每次重启或重新调度,Pod的IP地址都可能改变。如果客户端直接访问Pod IP,那么当Pod发生变化时,客户端就需要不断更新地址------这显然不可行。

类比:Pod就像餐馆后厨的厨师,Service就是前台的接待员。顾客(客户端)只和接待员说话(统一的电话号码),接待员再把请求转给某个空闲的厨师。不管厨师怎么换班,顾客永远只需拨打同一个号码。

Service的核心功能:

  1. 固定访问入口:提供稳定的虚拟IP(ClusterIP)和DNS名称。
  2. 负载均衡:将流入的请求分发到后端多个Pod。
  3. 服务发现:后端Pod的变化(扩缩容、更新)对客户端完全透明。

快速体验:从Pod直接暴露到使用Service

场景1:Pod直接使用hostPort(不推荐)

yaml

复制代码
# pod-web.yml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: web
  name: web
spec:
  containers:
  - image: httpd
    name: web
    ports:
    - containerPort: 80
      hostPort: 8080   # 将容器端口映射到节点端口

问题:

  • 客户端必须知道Pod所在节点的IP和端口(http://worker31:8080)
  • 当Pod被删除或迁移时,客户端无法自动更新
  • 无法在同一节点上运行多个相同端口的Pod(端口冲突)
场景2:Deployment + hostPort(更糟)

bash

复制代码
kubectl create deployment web --image=httpd --replicas=4
# 如果为每个Pod都设置hostPort:8080,只有2个Pod能启动(另外2个Pending)
# 因为每个节点只能有一个Pod占用8080端口
场景3:引入Service(正确做法)

bash

复制代码
# 创建Deployment
kubectl create deployment web --image=httpd --replicas=3

# 创建Service(ClusterIP类型)
kubectl expose deployment web --port=8080 --target-port=80

现在,你可以通过Service的固定IP(如10.103.19.150:8080)访问,后端Pod的变化完全不可见。

Service基本管理

创建Service的方式

方式1:kubectl expose命令

bash

复制代码
kubectl expose deployment web --port=8080 --target-port=80

方式2:kubectl create service命令

bash

复制代码
kubectl create service clusterip web --tcp=8080:80

方式3:YAML文件

yaml

复制代码
apiVersion: v1
kind: Service
metadata:
  name: web
  labels:
    app: web
spec:
  ports:
  - port: 8080        # Service监听的端口
    protocol: TCP
    targetPort: 80    # 转发到Pod的端口
  selector:
    app: web          # 选择具有此标签的Pod
  type: ClusterIP
验证Service的负载均衡

bash

复制代码
# 获取Pod名称
kubectl get pods -l app=web -o name

# 修改每个Pod的首页内容,显示Pod名称
for pod in $(kubectl get pods -l app=web -o name | cut -d/ -f2); do
  kubectl exec $pod -- sh -c "echo $pod > /usr/local/apache2/htdocs/index.html"
done

# 多次访问Service,观察流量分发
for i in {1..30}; do curl -s 10.103.19.150:8080; done | sort | uniq -c
# 输出示例:三个Pod的访问次数大致相等
Service如何匹配Pod?

Service通过标签选择器(selector) 来动态确定后端Pod集合。只要Pod的标签与Service的selector匹配(无论该Pod是由Deployment创建、手动创建,还是其他控制器创建),Service就会自动将其纳入负载均衡池。

bash

复制代码
# 手动创建一个具有相同标签的Pod
kubectl run web-extra --image=httpd --labels="app=web"

# 现在Service的后端Pod数量变为4个

Service发现:集群内如何访问Service

Kubernetes为集群内的Pod提供了三种发现Service的方式:

1. 通过ClusterIP直接访问

每个Service被分配一个虚拟IP(ClusterIP),集群内的任意Pod都可以直接访问该IP。

bash

复制代码
# 在其他Pod中
curl http://10.103.19.150:8080
2. 通过环境变量访问

当Pod启动时,kubelet会为每个活跃的Service设置一系列环境变量。格式为:

  • {SVCNAME}_SERVICE_HOST
  • {SVCNAME}_SERVICE_PORT

示例:

bash

复制代码
# 启动一个busybox Pod并查看环境变量
kubectl run test --rm -it --image=busybox -- sh
/ # env | grep WEB
WEB_SERVICE_HOST=10.103.19.150
WEB_SERVICE_PORT=8080

限制 :环境变量只有在Pod创建之后创建的Service才不会被注入。因此,推荐使用DNS发现。

3. 通过DNS名称访问(推荐)

Kubernetes集群内置了CoreDNS,为每个Service创建DNS记录。记录格式为:
<service-name>.<namespace>.svc.cluster.local

同一命名空间内可简写为<service-name>。

bash

复制代码
# 在任意Pod中
wget -qO- http://web.service:8080   # 跨命名空间
wget -qO- http://web:8080            # 同命名空间

示例 :部署WordPress+MySQL,WordPress使用DNS名称mysql连接数据库,而无需关心MySQL Service的实际IP。

Service类型详解

Kubernetes提供四种Service类型,以适应不同的访问场景。

类型 访问范围 典型用途
ClusterIP 仅集群内部 内部微服务通信
NodePort 集群外部通过节点IP+静态端口 开发测试、简单暴露
LoadBalancer 外部通过云负载均衡器IP 生产环境外部访问
ExternalName 映射到外部域名 访问外部服务(如数据库)
1. ClusterIP(默认)
  • 分配一个内部虚拟IP,仅在集群内可访问。
  • 是其他Service类型的基础。
2. NodePort
  • 在每个节点上开放一个固定端口(默认30000-32767)。
  • 外部客户端可以通过<任意节点IP>:<NodePort>访问Service。
  • 本质上是在ClusterIP基础上增加了节点端口映射。

yaml

复制代码
apiVersion: v1
kind: Service
metadata:
  name: web-nodeport
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    nodePort: 30080   # 可选,不指定则自动分配
  selector:
    app: web

bash

复制代码
# 访问任一节点
curl http://10.1.8.30:30080
curl http://10.1.8.31:30080

iptables规则示意:

text

复制代码
-A KUBE-NODEPORTS -p tcp --dport 30080 -j KUBE-SVC-XXXX
3. LoadBalancer
  • 在NodePort的基础上,向外部负载均衡器申请一个公网/私网IP。
  • 云提供商(AWS、GCP、Azure)或本地LB方案(如MetalLB)实现。
  • 外部客户端通过负载均衡器的IP直接访问。

使用MetalLB搭建本地LoadBalancer:

bash

复制代码
# 部署MetalLB
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.8/config/manifests/metallb-native.yaml

# 配置IP地址池
cat <<EOF | kubectl apply -f -
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 10.1.8.40-10.1.8.80
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
EOF

# 创建LoadBalancer类型的Service
kubectl expose deployment web --type=LoadBalancer --port=80 --target-port=80
kubectl get svc web
# NAME   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
# web    LoadBalancer   10.109.101.67   10.1.8.40     80:32101/TCP   5s

流量路径 :

客户端 → MetalLB虚拟IP(10.1.8.40) → 节点kube-proxy → Service负载均衡 → Pod

4. ExternalName
  • 将Service映射到外部的DNS名称,不创建任何代理。
  • 集群内部Pod可以通过Service名称访问外部服务。

yaml

复制代码
apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: database.example.com

当Pod解析external-db时,CoreDNS返回database.example.com的CNAME记录。

5. Headless Service
  • 设置clusterIP: None,不分配虚拟IP。
  • 用于需要直接访问每个Pod的场景(如StatefulSet)。
  • 核心DNS会返回Pod IP列表(A记录),而不是Service IP。

yaml

复制代码
apiVersion: v1
kind: Service
metadata:
  name: headless-svc
spec:
  clusterIP: None
  selector:
    app: myapp
  ports:
  - port: 80

Service高级特性

会话保持(Session Affinity)

问题:某些应用需要将同一个客户端的请求始终转发到同一个后端Pod(如购物车、WebSocket)。

解决方案 :设置sessionAffinity: ClientIP。

bash

复制代码
# 修改现有Service
kubectl patch svc web -p '{"spec":{"sessionAffinity":"ClientIP"}}'

# 可选:设置超时时间(默认10800秒=3小时)
kubectl patch svc web -p '{"spec":{"sessionAffinityConfig":{"clientIP":{"timeoutSeconds":3600}}}}'

原理:kube-proxy根据客户端IP的哈希值选择后端Pod,相同IP的请求始终映射到同一个Pod。

注意:会话保持与后端Pod扩缩容不兼容------如果目标Pod被删除,该客户端的会话将中断。

金丝雀发布(Canary Deployment)结合Service

利用Service的标签选择器,可以实现新旧版本按比例流量切换。

场景:将10%的流量导入新版本(canary),其余90%仍在旧版本(stable)。

实现步骤:

  1. 创建旧版本Deployment(stable)和新版本Deployment(canary),它们有共同的标签(如app: web, tier: frontend)。
  2. 创建Service,selector为app: web, tier: frontend,这样Service会把流量同时分发给两个版本。
  3. 通过调整两个Deployment的副本数来控制流量比例。如果Service使用默认的轮询负载均衡,副本数比例≈流量比例。

yaml

复制代码
# 旧版本 Deployment (stable)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-stable
spec:
  replicas: 9
  selector:
    matchLabels:
      app: web
      tier: frontend
      track: stable
  template:
    metadata:
      labels:
        app: web
        tier: frontend
        track: stable
    spec:
      containers:
      - image: nginx:1.28
        name: nginx

---
# 新版本 Deployment (canary)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-canary
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web
      tier: frontend
      track: canary
  template:
    metadata:
      labels:
        app: web
        tier: frontend
        track: canary
    spec:
      containers:
      - image: nginx:1.29
        name: nginx

---
# Service
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  selector:
    app: web
    tier: frontend
  ports:
  - port: 80

bash

复制代码
# 测试流量比例
for i in {1..100}; do curl -s http://10.98.235.36; done | sort | uniq -c
# 输出大致为:stable版本约90次,canary约10次

# 逐步扩大canary副本数,减少stable副本数
kubectl scale deployment web-stable --replicas 5
kubectl scale deployment web-canary --replicas 5
# 再次测试,流量接近1:1

最终,当canary版本验证无误后,可以删除stable Deployment,只保留canary,并修改其标签为track: stable。


知识点速查手册

1. CronJob 速查表

字段 作用 示例值
schedule Cron时间表达式 "*/5 * * * *"
jobTemplate Job模板 嵌套Job定义
startingDeadlineSeconds 错过调度的最大延迟秒数 200
concurrencyPolicy 并发策略 Allow/Forbid/Replace
successfulJobsHistoryLimit 保留的成功Job数 3
failedJobsHistoryLimit 保留的失败Job数 1

常用Cron表达式:

text

复制代码
*/1 * * * *      # 每分钟
0 */2 * * *      # 每2小时
0 0 * * 0        # 每周日0点
0 0 1 */3 *      # 每季度(每3个月的第1天)

2. Service 速查表

类型 ClusterIP 外部访问 使用场景
ClusterIP 自动分配 否 内部服务
NodePort 自动分配 是(节点IP:NodePort) 测试、简单暴露
LoadBalancer 自动分配 是(LB IP) 生产外部访问
ExternalName 无 否(DNS别名) 访问外部服务
Headless None 否(直接Pod IP) StatefulSet

Service YAML核心字段:

yaml

复制代码
spec:
  selector:           # 标签选择器,指定后端Pod
    app: web
  ports:
  - port: 8080        # Service监听的端口
    targetPort: 80    # Pod容器端口
    nodePort: 30080   # NodePort类型专用
  type: ClusterIP     # 服务类型
  sessionAffinity: ClientIP   # 会话保持
  clusterIP: None     # Headless Service

3. 服务发现方式比较

方式 优点 缺点 推荐度
环境变量 简单,无需额外组件 顺序依赖,仅注入已存在的Service 不推荐
ClusterIP 稳定,负载均衡 需要知道IP,IP会变(除非固定) 一般
DNS(CoreDNS) 名称稳定,自动更新,跨命名空间 需要DNS组件(已默认部署) 强烈推荐

4. 常见故障排查

症状 可能原因 排查命令
CronJob不创建Job schedule错误或concurrencyPolicy=Forbid且有未完成Job kubectl describe cronjob <name>
Pod一直Pending NodePort端口冲突或资源不足 kubectl describe pod <name>
Service无法访问 selector不匹配后端Pod kubectl get endpoints <svc>
外部无法访问NodePort 防火墙未开放端口或节点IP不可达 curl <nodeIP>:<nodePort>
DNS解析失败 CoreDNS Pod未运行 `kubectl get pods -n kube-system

5. 最佳实践清单

CronJob
  • 为CronJob设置startingDeadlineSeconds避免任务积压
  • 根据任务性质选择合适的并发策略
  • 设置合理的历史限制,防止API资源浪费
  • 对于重要任务,配置Pod级别的资源限制
  • 使用ServiceAccount而非管理员kubeconfig操作集群
Service
  • 使用DNS名称而非ClusterIP进行服务发现
  • 为重要Service设置标签(便于监控和选择)
  • 使用targetPort命名端口(便于修改容器端口)
  • 生产环境使用LoadBalancer或Ingress暴露服务
  • 为有状态服务配置sessionAffinity,但要注意Pod生命周期

文档版本 :v1.0
最后更新 :2026年4月
适用平台 :Kubernetes 1.20+
作者:基于课堂笔记扩展完善

重要提醒:

  1. CronJob的时区默认使用kube-controller-manager的时区(通常是UTC),如需自定义时区,可使用CRON_TZ变量(需特定版本支持)。
  2. Service的负载均衡基于kube-proxy模式(iptables/IPVS),默认是随机或轮询;大规模集群推荐使用IPVS模式。
  3. 金丝雀发布是持续交付的重要策略,结合Service可以实现更精细的流量控制(如基于Header的路由需要Ingress)。

下一步学习:

  • 学习Ingress Controller实现7层路由和TLS终止
  • 研究Helm管理CronJob和Service的模板化部署
  • 了解Service Mesh(如Istio)提供更高级的流量管理能力

本回答由 AI 生成,内容仅供参考,请仔细甄别。

相关推荐
Thneonl7 小时前
Flink Operator 的隐藏陷阱:容器名不是你以为的那个
安全·kubernetes
律宏阔7 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
Thneonl7 小时前
kubectl scale ds 是个 404:DaemonSet 没有 replicas
安全·kubernetes
律宏阔7 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴7 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千8 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei378 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei378 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei378 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
judezh8 小时前
api 容器一直 unhealthy?我把一个 Agent 运行时的健康检查逐条拆了,查出四个问题
运维·docker