k8s保姆级教程:K8s高可用集群教程-05-应用上线实战(最终篇)

2026-04-11 14:101阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:

这是本系列教程的最后一篇了,感谢各位佬友的关注

k8s保姆级教程:K8s高可用集群教程-01-使用 Kubespray 快速搭建集群(有手就行) 开发调优
第一部分:引言与架构规划 1.1 项目背景与目标 Kubernetes (K8s) 是什么就不多赘述了,最近在迁移部分服务到一个新的k8s集群中,借此机会写个部署教程 本教程将采用 Kubespray —— 一个基于 Ansible 的开源项目,来自动化部署一套生产级别的 K8s 集群。 本次部署的核心目标: 高可用性:构建 3 Master 节点的控制平面,确保单一节点故障不影响集群管理…
k8s保姆级教程:K8s高可用集群教程-02-Load Balancer和Ingress Controller 开发调优
关注的小伙伴久等了,因为工作的变动,关于k8s的教程一直没有更新,最近稍微有点时间,这就继续更新了,新刷到的小伙伴可以先看上一篇:k8s保姆级教程:K8s高可用集群教程-01-使用 Kubespray 快速搭建集群(有手就行) 废话不多说,下面开始正文 第四部分:流量入口与可视化 (Load Balancer, Traefik & Dashboard) 在我们拥有了一个健康的高可用集群后,首要…
k8s保姆级教程:K8s高可用集群教程-03-持久化存储 (Persistent Storage) 开发调优
本篇主要讲述在k8s集群中如何使用持久化存储,熟悉docker的佬友肯定对这个词不陌生,容器中的数据想要不丢失,那就需要把文件、目录挂载出来,在k8s集群中也一样,不过相对于docker,k8s可选的挂载方式和类型有很多种可供选择,我这里分享两种比较常用常见的方式,如果有跟着做的佬友,请务必看完整篇文章后再跟着操作。 感兴趣的佬友可以先看一下前两篇文章 下面开始正文 第五部分:打通数据任…
k8s保姆级教程:K8s高可用集群教程-04-自动化 HTTPS 开发调优
本章介绍如何使用 Cert-Manager 签发泛域名证书,给ingress controller使用 前情提要: 第六部分:自动化 HTTPS (Cert-Manager 泛域名证书) 在拥有了计算、网络和存储资源后,集群已经具备了完整的生产能力。本章会讲述如何利用 Cert-Manager 为集群自动签发一张受浏览器信任的 Let’s Encrypt 内网泛域名证书。 以下域名均使…

第七部分:应用上线实战 ——部署new-api 和 open-webui

我们已经拥有了一个包含高可用计算(Master/Worker)、分布式存储(Longhorn/NFS)、强大的网络和流量网关(Cilium + Traefik)以及自动化 HTTPS 证书的生产级 Kubernetes 集群。

现在,是时候让这个集群跑起来了。本章我将演示 K8s 中部署业务应用最常用的两种方式:手写原生 YAML 与使用 Helm Chart 本地包。

7.1 必修课:先搞懂 K8s 的服务暴露方式 (Service Types)

在正式动手写 YAML 之前,我们必须先理解 Kubernetes 的网络通信哲学。

为什么需要 Service? 在 K8s 中,Pod 是短暂的(Ephemeral)。当你更新应用或节点发生故障时,旧的 Pod 会被销毁,新的 Pod 会被创建,它们的 IP 地址随时都在变化。如果你直接通过 Pod 的 IP 去访问服务,就像记住了一个随时会换手机号的联系人,迟早会失联。

Service (服务) 就是 K8s 引入的“稳定通讯录”。它为一组提供相同功能的 Pod 分配一个固定的 IP,并自带负载均衡功能。

根据业务是对内提供还是对外暴露,K8s 提供了以下几种主流的 Service 类型:

1. ClusterIP (集群内部的专线)

  • 概念:这是 K8s 默认的 Service 类型。它会分配一个仅在集群内部可以访问的虚拟 IP。
  • 比喻:就像公司内部的“分机号”。只有坐在公司办公室里的人(集群内的其他 Pod)才能拨通,外面的客户(公网用户)打不进来。
  • 适用场景:微服务架构中,不需要直接暴露给外网的内部组件。比如你的 Web 后端服务要去连接的 MySQL 数据库Redis 缓存

2. NodePort (简单粗暴的城墙挖洞)

  • 概念:在 ClusterIP 的基础上,K8s 会在集群的每一个 Worker 节点上打开一个相同的静态端口(默认范围:30000-32767)。外部流量只要访问 <任何节点IP>:<NodePort>,就会被路由到背后的 Pod。
  • 比喻:就像在公司的围墙上挖了一个特定的洞。不管是客户 A 还是客户 B,只要找到围墙上的这个洞口,就能把信件塞进对应的部门。
  • 适用场景:临时测试、开发环境,或者在没有外部负载均衡器的情况下,用于小规模暴露少量服务。不推荐直接用于大规模生产环境,因为端口太长且难记,且存在单点故障风险(如果用户绑死访问某一个节点的 IP,该节点挂了就断网了)。

3. LoadBalancer (云厂商的豪华前台)

  • 概念:在 NodePort 的基础上,向底层基础设施(如云厂商的 AWS ELB、阿里云 SLB,或者我们内网自建的 HAProxy)申请一个外部的负载均衡 IP。
  • 比喻:公司花钱在园区大门请了一个专业的“接待前台”(外网 VIP)。前台负责记住围墙上所有的洞(NodePort),客户只需要拨打前台的统一对外电话,前台会自动把流量均匀地分发给各个节点。
  • 适用场景:生产环境对外暴露服务的标准做法(四层负载)。我们在上一章部署 Traefik 时,就是通过 LoadBalancer 为其分配了 172.16.5.194 这个外部入口 IP。

4. 终极形态:Ingress (七层智能路由器)

  • 概念:严格来说,Ingress 不是一种 Service,而是一个独立于 Service 之外的路由规则集合。如果 LoadBalancer 是处理 IP 和端口(四层),那 Ingress 就是处理 HTTP/HTTPS 的域名和路径(七层)。
  • 工作机制:通常我们只用 LoadBalancer 暴露一个 Ingress Controller(比如 Traefik),然后由这个控制器根据域名(如 api.k8s.example.com)把流量智能地分发到集群内成百上千个 ClusterIP 服务上。
  • 适用场景:暴露 Web 服务、配置 HTTPS 证书、基于域名的虚拟主机拆分。

** 总结对比表:**

暴露方式 流量入口 适用场景 优缺点对比
ClusterIP 集群内虚拟 IP 内部组件通信 (如 DB, Redis) 优点:安全,缺点:不可外网访问
NodePort 所有节点的物理 IP + 3xxxx 端口 测试环境、小型临时服务 优点:配置极其简单,缺点: 端口难记,不美观,需占用节点端口
LoadBalancer 独立于节点的外部 VIP 作为网关(Ingress)的唯一入口 优点:生产标准,高可用,缺点:强依赖云厂商或外部组件支持
Ingress 根据 HTTP 域名和路径分发 Web 应用对外暴露 优点:节省公网 IP,支持 HTTPS,缺点:需要前置网关组件

7.2 方式一:K8s 原生派 —— 手写 YAML 部署完整业务

适用场景:自定义开发的内部微服务、架构简单的无状态/有状态应用、需要对底层资源有 100% 掌控力的场景。

我们将以部署 new-api为例,演示资源对象的联动, 注意需要自行修改的内容,改成自己的。

1. 申请持久化存储 (PVC) pvc.yaml 首先,向我们的存储大本营要一块空间。

apiVersion: v1 kind: PersistentVolumeClaim metadata: name: new-api-data-pvc namespace: ai spec: accessModes: - ReadWriteMany storageClassName: longhorn resources: requests: storage: 2Gi # 根据需要调整大小 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: new-api-logs-pvc namespace: ai spec: accessModes: - ReadWriteMany storageClassName: longhorn resources: requests: storage: 2Gi # 根据需要调整大小

2. 声明业务负载 (Deployment) Deployment.yaml定义应用的版本、副本数,并将刚才申请的 PVC 挂载进容器。

# new-api需要使用redis和mysql数据库,这里我使用更为稳定的外部mysql数据库,redis数据无关紧要,直接起一个Deployment即可 --- apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: ai spec: replicas: 1 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:latest ports: - containerPort: 6379 resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" --- # 涉及到密码之类的配置,可以通过Secret资源存储,在pod中挂载 apiVersion: v1 kind: Secret metadata: name: new-api-secrets namespace: ai type: Opaque stringData: # 自行修改 session-secret: "xxxxxxx" --- apiVersion: apps/v1 kind: Deployment metadata: name: new-api namespace: ai spec: # 指定副本数量,因为我只提供给少数人使用,所以只需要一个Deployment即可 replicas: 1 selector: matchLabels: app: new-api template: metadata: labels: app: new-api spec: containers: - name: new-api image: calciumion/new-api:v0.11.5 args: ["--log-dir", "/app/logs"] ports: - containerPort: 3000 env: # redis链接,同namespace下只需要指定service名称加端口号即可,这是k8s集群内部的dns在起作用 - name: REDIS_CONN_STRING value: "redis://redis" - name: SQL_DSN value: "root:xxxxxxxx@tcp(172.16.5.191:3306)/new-api" - name: SESSION_SECRET valueFrom: # 引用上面创建的secret secretKeyRef: name: new-api-secrets key: session-secret - name: TZ value: "Asia/Shanghai" volumeMounts: - name: data mountPath: /data - name: logs mountPath: /app/logs livenessProbe: httpGet: path: /api/status port: 3000 initialDelaySeconds: 30 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 volumes: - name: data # 将文件挂载到pvc中 persistentVolumeClaim: claimName: new-api-data-pvc - name: logs persistentVolumeClaim: claimName: new-api-logs-pvc

3. 暴露内部网络 (Service) 为漂移不定的 Pod 分配一个固定的集群内访问地址。

--- # redis-service apiVersion: v1 kind: Service metadata: name: redis namespace: ai spec: selector: app: redis ports: - port: 6379 targetPort: 6379 type: ClusterIP --- # new-api-service apiVersion: v1 kind: Service metadata: name: new-api namespace: ai spec: selector: # deployment的名称 app: new-api ports: - port: 3000 targetPort: 3000 type: ClusterIP # 这里统一使用的 ClusterIP 作为服务暴露方式,因为我们外部还有一层IngressRoute

4. 配置外部访问与安全加密 (IngressRoute) 利用我们在第六章配置的泛域名证书和 Traefik 网关,让应用见光。

--- apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: new-api-route-tls namespace: ai spec: entryPoints: - websecure routes: - match: Host(`new-api.k8s.example.com`) # 域名 kind: Rule services: - name: new-api port: 3000 tls: secretName: wildcard-k8s-example-com-tls # secretName引用

操作步骤:

  1. 创建namespace,如果之前创建过忽略这一步,kubectl create namespace ai

  2. 将上述所有yaml内容报错到同一个目录中,执行kubectl apply -f .

  3. 访问 https://new-api.k8s.example.com

7.3 方式二:云原生包管理 —— 使用本地 Helm Chart 部署

适用场景:部署开源组件、包含数十个 YAML 文件的复杂系统、内网离线/断网环境。

如果你每次部署 服务 都要手写一堆 StatefulSet、ConfigMap 和 Service,那绝对是重复造轮子。Helm 是 K8s 的包管理器,而下载 Chart 包到本地进行部署,是企业内网环境(尤其是需要定制化修改或离线部署时)的最标准做法。

1. 寻找并下载 Chart 包 以部署一个open-webui为例。

# 查看官网:https://docs.openwebui.com/getting-started/quick-start/ 部署教程 # 添加官方仓库 helm repo add open-webui https://open-webui.github.io/helm-charts helm repo update # 将 Chart 包下载到本地解压 (而不是直接 install) helm pull open-webui/open-webui --untar cd open-webui

2. 核心:定制化 values.yaml 打开解压后的目录,你会看到一个庞大的 values.yaml。在这里修改配置,而不是去改晦涩的模板文件。由于 values.yaml文件太长,我这里只说一下需要修改的内容,找到对应的部分按需修改即可。

主目录values.yaml
  • ollama:按需打开,我这里不需要,enabled: false,如果开启了ollama,记得看下 charts/ollama 目录下的values.yaml配置,按需修改

  • pipelinesenabled: true

  • tikaenabled: true

  • websocketenabled: true

  • clusterDomain:重点,这里要改成部署时在kubespray中设置的hasakey.local,即clusterDomain: hasakey.local

    k8s保姆级教程:K8s高可用集群教程-01-使用 Kubespray 快速搭建集群(有手就行)

    cluster_name: hasakey.local

  • persistence:存储,enabled: truesize: 50Gi 这个大小自己设置,storageClass: "longhorn" 这里指定使用哪个sc,我这里是longhorn

  • service:服务暴露方式,type: NodePortnodePort: "30001" ,这里使用 NodePort 先留下一个伏笔,讲述内网服务对外暴漏的一种方式

  • databaseUrl:数据库连接地址。注意:这个配置默认是注释掉的,需要自行取消注释后修改,我这里依旧使用外部pg数据库,databaseUrl: "postgresql://openwebui:openwebui@172.16.5.191:5432/openwebui"

其他配置自己按需修改

charts/pipelines 目录下的 values.yaml 配置
  • clusterDomain:同上面的一致,clusterDomain: hasakey.local

3. 执行本地部署

# 使用本地目录进行安装,并指定我们修改过的 values.yaml helm upgrade --install --values=./values.yaml open-webui . -n ai

4. 暴漏服务

有两种方式可以选择,1、针对内网域名访问:ingressroute,2、针对公网访问

  1. 使用ingressroute,创建 ingressroute.yaml

    --- apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: owu-route-tls namespace: ai spec: entryPoints: - websecure routes: - match: Host(`owu.k8s.example.com`) kind: Rule services: - name: open-webui port: 80 tls: secretName: wildcard-k8s-example-com-tls

    浏览器访问:https://owu.k8s.example.com

  2. 使用nodeport,直接通过端口对外暴露服务,可以通过此方式对外网暴露服务

    由于我们外部有一层LB节点,所以要同步修改外部的LB配置,在HAProxy节点上修改,vim /etc/haproxy/haproxy.cfg

    # 文件的中添加以下内容,放在之前配置的 traefik 配置下面即可 frontend owu-30001 bind *:30001 mode tcp option tcplog default_backend owu-30001-backend backend owu-30001-backend mode tcp balance roundrobin option tcp-check server k8s-master-01 172.16.5.195:30001 check server k8s-master-02 172.16.5.196:30001 check server k8s-master-03 172.16.5.197:30001 check

    保存文件后执行 systemctl restart haproxy

    浏览器访问:http://172.16.5.194:30001/,此时如果想把服务映射到公网,只需要把这个端口映射到公网ip上即可,不论是通过frp还是端口映射

7.5 如何优雅地排错与观测

应用部署上去了,如果无法访问怎么办?这里教大家几个程序员日常排错的三板斧:

1. 基础三连 (Pod 层面)

  • kubectl get pods (看 STATUS 是否为 Running,如果有重启看 RESTARTS)
  • kubectl describe pod <pod-name> (看最下面的 Events,是不是拉不到镜像?是不是挂载不到 Longhorn?)
  • kubectl logs -f <pod-name> (看应用自身打印的业务报错)

2. 流量追踪 (网关层面)

  • 检查 Traefik Dashboard 或日志,看请求是否成功到达了 Traefik。
  • 检查 Service 的 Endpoints:kubectl get endpoints <svc-name>,如果为空,说明 Service 标签没对齐 Pod。

3. 网络显微镜 (Cilium Hubble 彩蛋)

在第二章 Kubespray 部署时,我们特意开启了 Cilium Hubble。

  • 现在你可以通过 hubble observe 命令行,或者访问 Hubble UI,直观地看到刚才部署的 Web 应用或 Redis 是如何与其他组件进行 TCP/HTTP 通信的,甚至能看到网络丢包在哪一层!

7.6 结语

趁着离职交接的这段间隙,我将这段时间的实践经验匆匆整理成了这份教程。因为时间紧凑,加之本人的文字表达能力实在有限,文中难免会有疏漏或不够严谨的地方,还请各位佬友们多多包涵,如果发现问题,请一定在评论区指出来,大家共同交流探讨。

回首从业的这 6 年,我的角色慢慢从一个纯粹的 Java 开发,阴差阳错地向“兼职运维”延伸,这也是小公司的现状,一人身兼多职。这份教程,其实也是我利用工作时间,对公司历史遗留的服务器和网络架构进行全面梳理、重构的真实记录。

从专注于编写业务逻辑代码,到开始折腾服务器运维,整个过程完全是“摸着石头过河,边学边做”。正如评论区的佬友所说,现在都在推行Gateway API,确实是这样的,技术的发展太快了,Kubernetes 的生态更是深似海,尤其是在AI大力发展的今天,学习的成本无限降低,更多需要我们动手去做,去执行。我把这些曾经踩过的坑、一步步敲出来的命令写下来,其实初衷非常简单:如果这个粗糙的教程,能够帮助到哪怕一个正对着屏幕排错排到焦头烂额的佬友,帮你避开几个我踩过的坑,节省几个小时的查阅时间,那这篇文章就实现了它最大的价值。

感谢大家的阅读!祝大家在技术的道路上保持热情,头发浓密,能够快速解决发现的bug!

网友解答:
--【壹】--:

感谢大佬 。


--【贰】--:

感谢大佬


--【叁】--:

感谢大佬


--【肆】--:

感谢佬友分享,学习了


--【伍】--:

很有用,感谢佬

问题描述:

这是本系列教程的最后一篇了,感谢各位佬友的关注

k8s保姆级教程:K8s高可用集群教程-01-使用 Kubespray 快速搭建集群(有手就行) 开发调优
第一部分:引言与架构规划 1.1 项目背景与目标 Kubernetes (K8s) 是什么就不多赘述了,最近在迁移部分服务到一个新的k8s集群中,借此机会写个部署教程 本教程将采用 Kubespray —— 一个基于 Ansible 的开源项目,来自动化部署一套生产级别的 K8s 集群。 本次部署的核心目标: 高可用性:构建 3 Master 节点的控制平面,确保单一节点故障不影响集群管理…
k8s保姆级教程:K8s高可用集群教程-02-Load Balancer和Ingress Controller 开发调优
关注的小伙伴久等了,因为工作的变动,关于k8s的教程一直没有更新,最近稍微有点时间,这就继续更新了,新刷到的小伙伴可以先看上一篇:k8s保姆级教程:K8s高可用集群教程-01-使用 Kubespray 快速搭建集群(有手就行) 废话不多说,下面开始正文 第四部分:流量入口与可视化 (Load Balancer, Traefik & Dashboard) 在我们拥有了一个健康的高可用集群后,首要…
k8s保姆级教程:K8s高可用集群教程-03-持久化存储 (Persistent Storage) 开发调优
本篇主要讲述在k8s集群中如何使用持久化存储,熟悉docker的佬友肯定对这个词不陌生,容器中的数据想要不丢失,那就需要把文件、目录挂载出来,在k8s集群中也一样,不过相对于docker,k8s可选的挂载方式和类型有很多种可供选择,我这里分享两种比较常用常见的方式,如果有跟着做的佬友,请务必看完整篇文章后再跟着操作。 感兴趣的佬友可以先看一下前两篇文章 下面开始正文 第五部分:打通数据任…
k8s保姆级教程:K8s高可用集群教程-04-自动化 HTTPS 开发调优
本章介绍如何使用 Cert-Manager 签发泛域名证书,给ingress controller使用 前情提要: 第六部分:自动化 HTTPS (Cert-Manager 泛域名证书) 在拥有了计算、网络和存储资源后,集群已经具备了完整的生产能力。本章会讲述如何利用 Cert-Manager 为集群自动签发一张受浏览器信任的 Let’s Encrypt 内网泛域名证书。 以下域名均使…

第七部分:应用上线实战 ——部署new-api 和 open-webui

我们已经拥有了一个包含高可用计算(Master/Worker)、分布式存储(Longhorn/NFS)、强大的网络和流量网关(Cilium + Traefik)以及自动化 HTTPS 证书的生产级 Kubernetes 集群。

现在,是时候让这个集群跑起来了。本章我将演示 K8s 中部署业务应用最常用的两种方式:手写原生 YAML 与使用 Helm Chart 本地包。

7.1 必修课:先搞懂 K8s 的服务暴露方式 (Service Types)

在正式动手写 YAML 之前,我们必须先理解 Kubernetes 的网络通信哲学。

为什么需要 Service? 在 K8s 中,Pod 是短暂的(Ephemeral)。当你更新应用或节点发生故障时,旧的 Pod 会被销毁,新的 Pod 会被创建,它们的 IP 地址随时都在变化。如果你直接通过 Pod 的 IP 去访问服务,就像记住了一个随时会换手机号的联系人,迟早会失联。

Service (服务) 就是 K8s 引入的“稳定通讯录”。它为一组提供相同功能的 Pod 分配一个固定的 IP,并自带负载均衡功能。

根据业务是对内提供还是对外暴露,K8s 提供了以下几种主流的 Service 类型:

1. ClusterIP (集群内部的专线)

  • 概念:这是 K8s 默认的 Service 类型。它会分配一个仅在集群内部可以访问的虚拟 IP。
  • 比喻:就像公司内部的“分机号”。只有坐在公司办公室里的人(集群内的其他 Pod)才能拨通,外面的客户(公网用户)打不进来。
  • 适用场景:微服务架构中,不需要直接暴露给外网的内部组件。比如你的 Web 后端服务要去连接的 MySQL 数据库Redis 缓存

2. NodePort (简单粗暴的城墙挖洞)

  • 概念:在 ClusterIP 的基础上,K8s 会在集群的每一个 Worker 节点上打开一个相同的静态端口(默认范围:30000-32767)。外部流量只要访问 <任何节点IP>:<NodePort>,就会被路由到背后的 Pod。
  • 比喻:就像在公司的围墙上挖了一个特定的洞。不管是客户 A 还是客户 B,只要找到围墙上的这个洞口,就能把信件塞进对应的部门。
  • 适用场景:临时测试、开发环境,或者在没有外部负载均衡器的情况下,用于小规模暴露少量服务。不推荐直接用于大规模生产环境,因为端口太长且难记,且存在单点故障风险(如果用户绑死访问某一个节点的 IP,该节点挂了就断网了)。

3. LoadBalancer (云厂商的豪华前台)

  • 概念:在 NodePort 的基础上,向底层基础设施(如云厂商的 AWS ELB、阿里云 SLB,或者我们内网自建的 HAProxy)申请一个外部的负载均衡 IP。
  • 比喻:公司花钱在园区大门请了一个专业的“接待前台”(外网 VIP)。前台负责记住围墙上所有的洞(NodePort),客户只需要拨打前台的统一对外电话,前台会自动把流量均匀地分发给各个节点。
  • 适用场景:生产环境对外暴露服务的标准做法(四层负载)。我们在上一章部署 Traefik 时,就是通过 LoadBalancer 为其分配了 172.16.5.194 这个外部入口 IP。

4. 终极形态:Ingress (七层智能路由器)

  • 概念:严格来说,Ingress 不是一种 Service,而是一个独立于 Service 之外的路由规则集合。如果 LoadBalancer 是处理 IP 和端口(四层),那 Ingress 就是处理 HTTP/HTTPS 的域名和路径(七层)。
  • 工作机制:通常我们只用 LoadBalancer 暴露一个 Ingress Controller(比如 Traefik),然后由这个控制器根据域名(如 api.k8s.example.com)把流量智能地分发到集群内成百上千个 ClusterIP 服务上。
  • 适用场景:暴露 Web 服务、配置 HTTPS 证书、基于域名的虚拟主机拆分。

** 总结对比表:**

暴露方式 流量入口 适用场景 优缺点对比
ClusterIP 集群内虚拟 IP 内部组件通信 (如 DB, Redis) 优点:安全,缺点:不可外网访问
NodePort 所有节点的物理 IP + 3xxxx 端口 测试环境、小型临时服务 优点:配置极其简单,缺点: 端口难记,不美观,需占用节点端口
LoadBalancer 独立于节点的外部 VIP 作为网关(Ingress)的唯一入口 优点:生产标准,高可用,缺点:强依赖云厂商或外部组件支持
Ingress 根据 HTTP 域名和路径分发 Web 应用对外暴露 优点:节省公网 IP,支持 HTTPS,缺点:需要前置网关组件

7.2 方式一:K8s 原生派 —— 手写 YAML 部署完整业务

适用场景:自定义开发的内部微服务、架构简单的无状态/有状态应用、需要对底层资源有 100% 掌控力的场景。

我们将以部署 new-api为例,演示资源对象的联动, 注意需要自行修改的内容,改成自己的。

1. 申请持久化存储 (PVC) pvc.yaml 首先,向我们的存储大本营要一块空间。

apiVersion: v1 kind: PersistentVolumeClaim metadata: name: new-api-data-pvc namespace: ai spec: accessModes: - ReadWriteMany storageClassName: longhorn resources: requests: storage: 2Gi # 根据需要调整大小 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: new-api-logs-pvc namespace: ai spec: accessModes: - ReadWriteMany storageClassName: longhorn resources: requests: storage: 2Gi # 根据需要调整大小

2. 声明业务负载 (Deployment) Deployment.yaml定义应用的版本、副本数,并将刚才申请的 PVC 挂载进容器。

# new-api需要使用redis和mysql数据库,这里我使用更为稳定的外部mysql数据库,redis数据无关紧要,直接起一个Deployment即可 --- apiVersion: apps/v1 kind: Deployment metadata: name: redis namespace: ai spec: replicas: 1 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: containers: - name: redis image: redis:latest ports: - containerPort: 6379 resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" --- # 涉及到密码之类的配置,可以通过Secret资源存储,在pod中挂载 apiVersion: v1 kind: Secret metadata: name: new-api-secrets namespace: ai type: Opaque stringData: # 自行修改 session-secret: "xxxxxxx" --- apiVersion: apps/v1 kind: Deployment metadata: name: new-api namespace: ai spec: # 指定副本数量,因为我只提供给少数人使用,所以只需要一个Deployment即可 replicas: 1 selector: matchLabels: app: new-api template: metadata: labels: app: new-api spec: containers: - name: new-api image: calciumion/new-api:v0.11.5 args: ["--log-dir", "/app/logs"] ports: - containerPort: 3000 env: # redis链接,同namespace下只需要指定service名称加端口号即可,这是k8s集群内部的dns在起作用 - name: REDIS_CONN_STRING value: "redis://redis" - name: SQL_DSN value: "root:xxxxxxxx@tcp(172.16.5.191:3306)/new-api" - name: SESSION_SECRET valueFrom: # 引用上面创建的secret secretKeyRef: name: new-api-secrets key: session-secret - name: TZ value: "Asia/Shanghai" volumeMounts: - name: data mountPath: /data - name: logs mountPath: /app/logs livenessProbe: httpGet: path: /api/status port: 3000 initialDelaySeconds: 30 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 volumes: - name: data # 将文件挂载到pvc中 persistentVolumeClaim: claimName: new-api-data-pvc - name: logs persistentVolumeClaim: claimName: new-api-logs-pvc

3. 暴露内部网络 (Service) 为漂移不定的 Pod 分配一个固定的集群内访问地址。

--- # redis-service apiVersion: v1 kind: Service metadata: name: redis namespace: ai spec: selector: app: redis ports: - port: 6379 targetPort: 6379 type: ClusterIP --- # new-api-service apiVersion: v1 kind: Service metadata: name: new-api namespace: ai spec: selector: # deployment的名称 app: new-api ports: - port: 3000 targetPort: 3000 type: ClusterIP # 这里统一使用的 ClusterIP 作为服务暴露方式,因为我们外部还有一层IngressRoute

4. 配置外部访问与安全加密 (IngressRoute) 利用我们在第六章配置的泛域名证书和 Traefik 网关,让应用见光。

--- apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: new-api-route-tls namespace: ai spec: entryPoints: - websecure routes: - match: Host(`new-api.k8s.example.com`) # 域名 kind: Rule services: - name: new-api port: 3000 tls: secretName: wildcard-k8s-example-com-tls # secretName引用

操作步骤:

  1. 创建namespace,如果之前创建过忽略这一步,kubectl create namespace ai

  2. 将上述所有yaml内容报错到同一个目录中,执行kubectl apply -f .

  3. 访问 https://new-api.k8s.example.com

7.3 方式二:云原生包管理 —— 使用本地 Helm Chart 部署

适用场景:部署开源组件、包含数十个 YAML 文件的复杂系统、内网离线/断网环境。

如果你每次部署 服务 都要手写一堆 StatefulSet、ConfigMap 和 Service,那绝对是重复造轮子。Helm 是 K8s 的包管理器,而下载 Chart 包到本地进行部署,是企业内网环境(尤其是需要定制化修改或离线部署时)的最标准做法。

1. 寻找并下载 Chart 包 以部署一个open-webui为例。

# 查看官网:https://docs.openwebui.com/getting-started/quick-start/ 部署教程 # 添加官方仓库 helm repo add open-webui https://open-webui.github.io/helm-charts helm repo update # 将 Chart 包下载到本地解压 (而不是直接 install) helm pull open-webui/open-webui --untar cd open-webui

2. 核心:定制化 values.yaml 打开解压后的目录,你会看到一个庞大的 values.yaml。在这里修改配置,而不是去改晦涩的模板文件。由于 values.yaml文件太长,我这里只说一下需要修改的内容,找到对应的部分按需修改即可。

主目录values.yaml
  • ollama:按需打开,我这里不需要,enabled: false,如果开启了ollama,记得看下 charts/ollama 目录下的values.yaml配置,按需修改

  • pipelinesenabled: true

  • tikaenabled: true

  • websocketenabled: true

  • clusterDomain:重点,这里要改成部署时在kubespray中设置的hasakey.local,即clusterDomain: hasakey.local

    k8s保姆级教程:K8s高可用集群教程-01-使用 Kubespray 快速搭建集群(有手就行)

    cluster_name: hasakey.local

  • persistence:存储,enabled: truesize: 50Gi 这个大小自己设置,storageClass: "longhorn" 这里指定使用哪个sc,我这里是longhorn

  • service:服务暴露方式,type: NodePortnodePort: "30001" ,这里使用 NodePort 先留下一个伏笔,讲述内网服务对外暴漏的一种方式

  • databaseUrl:数据库连接地址。注意:这个配置默认是注释掉的,需要自行取消注释后修改,我这里依旧使用外部pg数据库,databaseUrl: "postgresql://openwebui:openwebui@172.16.5.191:5432/openwebui"

其他配置自己按需修改

charts/pipelines 目录下的 values.yaml 配置
  • clusterDomain:同上面的一致,clusterDomain: hasakey.local

3. 执行本地部署

# 使用本地目录进行安装,并指定我们修改过的 values.yaml helm upgrade --install --values=./values.yaml open-webui . -n ai

4. 暴漏服务

有两种方式可以选择,1、针对内网域名访问:ingressroute,2、针对公网访问

  1. 使用ingressroute,创建 ingressroute.yaml

    --- apiVersion: traefik.io/v1alpha1 kind: IngressRoute metadata: name: owu-route-tls namespace: ai spec: entryPoints: - websecure routes: - match: Host(`owu.k8s.example.com`) kind: Rule services: - name: open-webui port: 80 tls: secretName: wildcard-k8s-example-com-tls

    浏览器访问:https://owu.k8s.example.com

  2. 使用nodeport,直接通过端口对外暴露服务,可以通过此方式对外网暴露服务

    由于我们外部有一层LB节点,所以要同步修改外部的LB配置,在HAProxy节点上修改,vim /etc/haproxy/haproxy.cfg

    # 文件的中添加以下内容,放在之前配置的 traefik 配置下面即可 frontend owu-30001 bind *:30001 mode tcp option tcplog default_backend owu-30001-backend backend owu-30001-backend mode tcp balance roundrobin option tcp-check server k8s-master-01 172.16.5.195:30001 check server k8s-master-02 172.16.5.196:30001 check server k8s-master-03 172.16.5.197:30001 check

    保存文件后执行 systemctl restart haproxy

    浏览器访问:http://172.16.5.194:30001/,此时如果想把服务映射到公网,只需要把这个端口映射到公网ip上即可,不论是通过frp还是端口映射

7.5 如何优雅地排错与观测

应用部署上去了,如果无法访问怎么办?这里教大家几个程序员日常排错的三板斧:

1. 基础三连 (Pod 层面)

  • kubectl get pods (看 STATUS 是否为 Running,如果有重启看 RESTARTS)
  • kubectl describe pod <pod-name> (看最下面的 Events,是不是拉不到镜像?是不是挂载不到 Longhorn?)
  • kubectl logs -f <pod-name> (看应用自身打印的业务报错)

2. 流量追踪 (网关层面)

  • 检查 Traefik Dashboard 或日志,看请求是否成功到达了 Traefik。
  • 检查 Service 的 Endpoints:kubectl get endpoints <svc-name>,如果为空,说明 Service 标签没对齐 Pod。

3. 网络显微镜 (Cilium Hubble 彩蛋)

在第二章 Kubespray 部署时,我们特意开启了 Cilium Hubble。

  • 现在你可以通过 hubble observe 命令行,或者访问 Hubble UI,直观地看到刚才部署的 Web 应用或 Redis 是如何与其他组件进行 TCP/HTTP 通信的,甚至能看到网络丢包在哪一层!

7.6 结语

趁着离职交接的这段间隙,我将这段时间的实践经验匆匆整理成了这份教程。因为时间紧凑,加之本人的文字表达能力实在有限,文中难免会有疏漏或不够严谨的地方,还请各位佬友们多多包涵,如果发现问题,请一定在评论区指出来,大家共同交流探讨。

回首从业的这 6 年,我的角色慢慢从一个纯粹的 Java 开发,阴差阳错地向“兼职运维”延伸,这也是小公司的现状,一人身兼多职。这份教程,其实也是我利用工作时间,对公司历史遗留的服务器和网络架构进行全面梳理、重构的真实记录。

从专注于编写业务逻辑代码,到开始折腾服务器运维,整个过程完全是“摸着石头过河,边学边做”。正如评论区的佬友所说,现在都在推行Gateway API,确实是这样的,技术的发展太快了,Kubernetes 的生态更是深似海,尤其是在AI大力发展的今天,学习的成本无限降低,更多需要我们动手去做,去执行。我把这些曾经踩过的坑、一步步敲出来的命令写下来,其实初衷非常简单:如果这个粗糙的教程,能够帮助到哪怕一个正对着屏幕排错排到焦头烂额的佬友,帮你避开几个我踩过的坑,节省几个小时的查阅时间,那这篇文章就实现了它最大的价值。

感谢大家的阅读!祝大家在技术的道路上保持热情,头发浓密,能够快速解决发现的bug!

网友解答:
--【壹】--:

感谢大佬 。


--【贰】--:

感谢大佬


--【叁】--:

感谢大佬


--【肆】--:

感谢佬友分享,学习了


--【伍】--:

很有用,感谢佬