1. 从“能用”到“好用”为什么2022年的K8s最佳实践依然值得深挖最近在整理团队的技术资产翻到了去年做的一次关于K8s和Rancher 2.x的内部培训材料。当时正值容器化浪潮从“尝鲜”转向“深耕”的阶段很多团队已经完成了从零到一的搭建但随之而来的是一系列“幸福的烦恼”集群规模上来了管理却越来越乱应用部署是快了可线上故障排查却慢了YAML文件堆成了山没人敢轻易改动。我们那套课程核心就是解决这些问题——不是教你如何安装一个K8s集群这太基础了而是聚焦于如何让一个已经运行起来的K8s生产环境变得更稳定、更高效、更易于运维。时间到了现在虽然K8s的版本已经迭代了很多但你会发现当时梳理的那些核心原则和“坑点”不仅没有过时反而因为大家踩的坑更多显得愈发重要。比如如何设计命名空间和资源配额来避免“一颗老鼠屎坏了一锅粥”如何利用Rancher的多集群管理功能实现真正的“云原生”运维视图以及如何将CI/CD流水线与K8s的声明式API深度结合而不是简单地在容器里跑个Jenkins。这些都不是版本更新就能自动解决的它们关乎架构理念和运维习惯。所以我想把这些沉淀下来的东西重新梳理一遍结合这两年看到的新问题和新工具形成一份更聚焦于“最佳实践”的分享。这份内容适合已经对K8s和Docker有基本了解正在或计划将K8s用于生产环境的开发者、运维和架构师。我们会跳过“kubectl get pods”这种入门命令直接深入到集群规划、应用部署、安全加固和日常运维的“深水区”。2. 生产级集群规划与资源治理超越简单的kubeadm init很多教程止步于用kubeadm或RKERancher Kubernetes Engine把集群跑起来但这离生产就绪还差得远。一个健康的集群首先源于良好的顶层设计。2.1 节点规划与隔离策略计算、存储与网络的考量在物理机或云主机层面我们强烈建议将控制平面节点Master与工作节点Worker彻底分离。对于中小规模集群比如20个节点以内三个控制平面节点组成高可用HA模式是性价比最高的选择。这里有个细节控制平面节点的配置特别是内存和磁盘IOPS往往被低估。Etcd是集群的大脑对磁盘延迟极其敏感。如果你把Etcd和数据盘放在同一块机械硬盘或普通云盘上集群响应慢、甚至莫名故障的几率会大增。最佳实践是为Etcd单独配备高性能的SSD盘并确保网络延迟在毫秒级。工作节点的规划则要结合业务特点。我们通常采用“混合部署污点容忍”策略。例如通用计算节点池无特殊标签运行大多数无状态应用。高IO节点池打上标签node-type: high-io并加上污点diskssd:NoSchedule。只有明确声明了对应容忍Toleration和节点选择器NodeSelector的Pod比如MySQL、Redis才能调度上去。GPU节点池打上标签accelerator: nvidia-tesla-v100用于AI训练或图形处理任务。在Rancher 2.x中创建集群时你可以直接在“节点模板”里预设这些标签和污点后续通过“节点池”功能批量管理这比手动到每台机器上打标签要规范高效得多。2.2 命名空间Namespace设计逻辑隔离与资源配额命名空间不是简单的文件夹它是资源配额、网络策略和权限控制的天然边界。一个常见的反模式是把所有应用都扔在default命名空间里。我们的实践是按团队/项目划分如namespace: team-anamespace: project-eagle。这是最直观的划分方式。按环境划分dev,staging,prod。特别注意不建议在命名空间名中直接使用prod生产。因为任何有list namespace权限的人都能一眼看到哪些是生产环境增加了安全风险。可以用一些内部项目代号如namespace: blue生产namespace: green预发布再通过RBAC控制访问权限。按应用类型划分infra用于运行Prometheus、EFK等基础设施组件middleware用于MySQL、Kafka等中间件。每个命名空间都必须配套资源配额ResourceQuota。这是防止某个应用失控、耗尽集群资源的关键阀门。一个基础的Quota配置如下apiVersion: v1 kind: ResourceQuota metadata: name: compute-resources namespace: project-eagle spec: hard: requests.cpu: 20 # 最多申请20个CPU核 requests.memory: 40Gi # 最多申请40Gi内存 limits.cpu: 40 # 所有Pod的CPU限制总和不能超过40核 limits.memory: 80Gi pods: 100 # 该命名空间最多100个Pod services.loadbalancers: 5 # 最多5个负载均衡器云厂商收费通过Rancher UI你可以非常直观地为每个命名空间创建和调整Quota并且能实时看到资源的使用率这对于成本控制和容量规划至关重要。2.3 网络策略NetworkPolicy入门默认拒绝一切K8s集群内Pod之间默认是网络全通的。这意味着一旦某个Pod被攻破攻击者可以扫描并攻击集群内任何其他服务。生产环境必须实施“零信任”网络模型。第一步也是最重要的一步是在每个命名空间下创建一个默认拒绝所有入站和出站流量的策略apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: default-deny-all namespace: your-namespace spec: podSelector: {} # 选择所有Pod policyTypes: - Ingress - Egress创建这个策略后该命名空间内所有Pod将失去网络连接。然后你再像砌墙一样通过白名单方式逐个添加允许访问的规则。例如允许前端Pod访问后端APIapiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: allow-frontend-to-backend namespace: your-namespace spec: podSelector: matchLabels: app: backend-api policyTypes: - Ingress ingress: - from: - podSelector: matchLabels: app: frontend-web ports: - protocol: TCP port: 8080这个过程虽然繁琐但能极大提升集群内部的安全性。Rancher的UI提供了可视化的网络策略编辑器降低了配置复杂度但理解其背后的原理仍然必要。3. 应用部署与配置管理的进阶之道部署一个Deployment只是开始如何让它健壮、可观测、易于回滚才是体现功力的地方。3.1 工作负载Workload配置的黄金法则1. 永远定义资源请求requests和限制limits这是稳定性的基石。不设置requests调度器就不知道你的Pod需要多少资源可能导致节点超卖不设置limits单个Pod可能吃光节点资源。一个经验值是limits通常是requests的1.5到2倍给应用留出一定的突发缓冲但又不至于失控。resources: requests: memory: 256Mi cpu: 250m limits: memory: 512Mi cpu: 500m2. 配置就绪和存活探针Readiness/Liveness Probe这是实现“自愈”能力的关键。很多应用启动慢或者运行中会卡死。存活探针Liveness检测应用是否“活着”。失败则重启Pod。适用于死锁场景。就绪探针Readiness检测应用是否“准备好”接收流量。失败则将其从Service的负载均衡端点中移除。适用于启动依赖如连接数据库或临时过载场景。 一个常见的坑是探针配置不当导致频繁重启。例如一个Java应用启动可能需要60秒但你把initialDelaySeconds设成了5秒结果应用还没启动完就被探针杀死了。务必根据应用实际启动时间设置合理的延迟。3. 使用多副本Replicas和Pod反亲和性PodAntiAffinity对于关键应用至少运行2个副本。并且通过podAntiAffinity确保这些副本被调度到不同的物理节点上避免单点故障。affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: app operator: In values: - my-critical-app topologyKey: kubernetes.io/hostname # 确保Pod不在同一主机3.2 ConfigMap与Secret告别环境变量硬编码把配置写在Deployment的YAML里是初级做法。最佳实践是使用ConfigMap和Secret来管理配置。ConfigMap存储非敏感的配置如配置文件内容、环境变量。Secret存储敏感数据如密码、令牌、密钥。虽然Secret默认以Base64编码存储并非加密但K8s会对其提供更多保护例如在内存中不以明文存储。更高级的用法是以卷Volume的形式挂载ConfigMap这样应用配置文件可以动态更新。在Rancher中你可以在UI里轻松创建、编辑这些配置对象并绑定到工作负载比手写YAML更直观且减少了出错几率。关于那个常见错误k8s configmap执行脚本 permission denied这通常发生在你将一个Shell脚本作为ConfigMap挂载到容器中并尝试执行它时。ConfigMap挂载的文件默认权限是644即-rw-r--r--而执行脚本需要x执行权限。解决方法有两种在容器启动的初始化容器initContainer里用chmod x命令修改脚本权限。更优雅的做法是不要通过ConfigMap挂载可执行脚本而是将脚本内容作为配置参数在容器的主进程如通过sh -c中动态生成并执行。3.3 有状态应用StatefulSet部署实战以PostgreSQL为例部署无状态应用Deployment和部署有状态应用StatefulSet是两回事。后者涉及稳定的网络标识、有序的部署/扩缩容和持久化存储。以在K8s中部署高可用的PostgreSQL使用Crunchy Data的PostgreSQL Operator是一种更佳选择但这里演示原生方式为例你需要关注Headless Service为每个Pod提供唯一的、稳定的DNS名称格式为pod-name.svc-name.namespace.svc.cluster.local。持久化存储卷PVC使用volumeClaimTemplates每个Pod会自动绑定一个独立的PVC即使Pod被重新调度数据也会跟随。初始化容器Init Containers用于在主容器启动前进行数据目录初始化、权限设置等操作。在Rancher的“应用商店”中其实已经有很多经过验证的有状态应用模板如Redis、PostgreSQL集群它们已经帮你处理了这些复杂逻辑我强烈建议优先使用这些成熟方案而不是自己从头造轮子尤其是在生产环境。4. 运维、监控与故障排查体系化建设系统上线后运维才刚刚开始。如何快速发现问题、定位根因、恢复服务是更大的挑战。4.1 构建中心化日志与监控体系日志所有容器的标准输出stdout/stderr都应该被采集。使用Fluentd或Filebeat作为日志收集代理DaemonSet部署将日志发送到Elasticsearch或Loki并通过Grafana进行可视化查询。关键点是给日志加上丰富的标签如namespace,pod_name,app方便过滤。监控分为四个层次基础设施监控节点CPU、内存、磁盘、网络。通过Node Exporter采集。K8s组件监控API Server、Scheduler、Controller Manager、Etcd等的状态和性能。这些组件的Metrics端口通常已暴露。应用业务监控通过应用自身暴露的Prometheus格式的Metrics。中间件监控数据库、消息队列等组件的状态。你提到的“prometheus监控k8s集群状态的详细操作注意prometheus在k8集群外”这是一个非常典型的场景。核心步骤如下在集群外部署Prometheus Server避免监控系统本身故障影响集群。在K8s集群内部署Prometheus的抓取代理通常是prometheus-node-exporterDaemonSet抓节点指标和kube-state-metricsDeployment抓K8s对象状态。它们将Metrics暴露在HTTP端口上。让集群外的Prometheus能够发现并抓取这些目标这是关键。不能直接用Pod IP因为IP会变。你需要创建对应的Service然后有两种主流方式通过Service的NodePort为每个Metrics Service创建NodePort集群外的Prometheus通过NodeIP:NodePort来抓取。简单但不安全且需要管理一堆端口。通过API Server代理或Ingress更安全的方式。让Prometheus配置抓取目标为K8s API Server的地址并指定路径为/api/v1/namespaces/namespace/services/service-name:port/proxy。这需要Prometheus具有访问API Server的权限配置RBAC和Bearer Token。或者通过一个对外的Ingress统一暴露这些Metrics端点并配置安全认证。配置Prometheus的scrape_configs使用kubernetes_sd_configs自动发现集群内的Service或Pod并利用relabel_configs进行过滤和重写标签最终生成正确的抓取地址。4.2 通用故障排查思路从现象到根因当收到报警“k8s虚拟机cpu占用率太高”或“服务不可用”时一个清晰的排查路径能节省大量时间。第一步确定问题边界是个别Pod的问题还是整个Service或Deployment的问题是单个节点的问题还是整个集群的问题问题发生的时间点是否和最近的部署、配置变更有关第二步逐层排查查看事件Eventskubectl describe pod pod-name -n namespace。Events信息是黄金线索会告诉你Pod为什么卡在Pending资源不足、节点选择器不匹配为什么CrashLoopBackOff启动失败为什么被驱逐节点资源压力。查看Pod状态和日志kubectl logs pod-name -n namespace --previous查看前一个容器的日志对于崩溃的Pod尤其有用。检查资源使用kubectl top pod/node查看实时的CPU/内存使用情况。结合监控图表看是持续高位还是瞬间尖峰。检查网络连通性进入Pod内部kubectl exec -it用curl或nslookup测试到其他服务或外部的网络连接。检查NetworkPolicy是否配置过严。检查存储如果是有状态应用检查PVC/PV的状态是否为Bound挂载是否成功。第三步深入组件如果怀疑是K8s系统组件问题kubectl get cs检查组件健康状态。查看对应组件的日志。例如调度器问题看kube-scheduler日志网络问题看CNI插件如Calico的calico-node日志。在Rancher中这些排查工作得到了极大简化。其“集群管理”视图直接集成了Pod日志查看、事件流、容器Shell终端以及资源监控图表你可以在一个统一的界面里完成上述大部分操作无需在多个命令行窗口间切换。4.3 关于“若依RuoYi”等系统部署的特别提醒你提到了“ruoyi-cloud k8s 完整部署教程”和“若依k8s部署”。对于这类复杂的、多服务的Java微服务系统直接将其所有组件打包成镜像扔进K8s会遇到很多挑战服务发现系统可能依赖Eureka或Nacos。在K8s内可以继续使用它们但更云原生的做法是逐步迁移到K8s Service Ingress的组合或者使用Service Mesh如Istio。配置管理若依有自己的配置中心。需要处理好其与K8s ConfigMap/Secret的边界。通常应用启动的引导配置如连接配置中心的地址用ConfigMap业务动态配置走原有的配置中心。数据库迁移生产环境数据库强烈不建议部署在K8s内除非你使用经过严格测试的Operator并具备专业的运维能力。建议将MySQL、Redis等中间件部署在集群外的高可用托管服务上或独立的物理机/虚拟机中。文件存储这类系统常有文件上传功能。需要配置持久化存储卷如NFS、Ceph RBD或云存储并注意多个Pod实例间的文件共享与同步问题。部署这类系统的关键是先画出一张清晰的架构图标明每个服务、中间件、配置源和存储的部署位置集群内/外以及它们之间的依赖关系和网络访问路径。然后分批次、分服务地进行容器化和部署而不是搞“大爆炸”式迁移。5. 安全、备份与持续交付闭环安全不是功能而是基础备份不是选项而是必须。5.1 最小权限原则与RBAC实践Rancher 2.x自带了强大的、基于项目的RBAC角色基于访问控制体系。你应该为每个用户或组创建独立的账号而不是共享管理员账号。利用“项目Project”进行逻辑分组在Rancher中项目是命名空间的集合。你可以将dev、staging命名空间放入一个“开发项目”将生产环境的命名空间放入“生产项目”。分配“项目成员”角色例如给开发人员“项目只读”或“项目成员”角色他们只能在自己所属的项目内操作资源无法看到或影响其他项目如生产环境。定期审计利用Rancher的审计日志或集成外部SIEM系统监控所有关键操作如删除Pod、修改Service。对于需要kubectl命令行访问的情况可以通过Rancher生成并下载针对特定集群、具有特定权限的Kubeconfig文件实现精细化的权限控制。5.2 不可或缺的备份与灾难恢复即使用了高可用架构没有备份也是裸奔。你需要备份集群资源定义使用velero原名Heptio Ark这类工具定期备份整个命名空间或特定标签的资源YAML文件。持久化数据这是最关键的。Velero可以通过插件备份PVC数据到对象存储如S3。务必定期进行恢复演练确保备份是有效的。Etcd数据这是K8s集群状态的核心。虽然Velero也能备份但更底层的做法是定期对Etcd的数据目录进行快照etcdctl snapshot save。在通过RKE部署的集群中Rancher提供了便捷的Etcd备份与恢复功能建议配置定时自动备份到远程位置。5.3 打造GitOps风格的持续交付流水线最终的理想状态是实现GitOps将应用的所有声明式配置K8s YAML、Helm Charts存储在Git仓库中。Git仓库中的main分支状态就是你期望的生产环境状态。具体流程可以是开发人员提交应用代码到Git。CI流水线如Jenkins、GitLab CI触发构建Docker镜像并推送到镜像仓库同时生成或更新对应的K8s部署清单如kustomize overlay、helm chart values.yaml提交到另一个“配置仓库”。GitOps工具如Argo CD、Flux CD持续监视“配置仓库”。一旦发现配置变更它自动将变更同步到目标K8s集群使集群状态与Git仓库声明的一致。Rancher 2.x可以与Argo CD等工具很好地集成你可以在Rancher UI中直接看到来自Argo CD的部署状态和同步历史实现可视化的持续交付管理。这比单纯的在Jenkins里执行kubectl apply要可靠和清晰得多因为所有变更都有版本记录且可以一键回滚到Git中的任何一个提交。走到这一步你的K8s之旅才算真正从“技术实验”迈入了“生产实践”的成熟阶段。整个过程充满了细节和权衡没有一劳永逸的银弹但遵循这些从大量实践中总结出的最佳实践至少能让你避开80%的常见深坑构建出一个既强大又易于驾驭的容器化平台。