k8s保姆级教程:K8s高可用集群教程-05-应用上线实战(最终篇)
- 内容介绍
- 文章标签
- 相关推荐
这是本系列教程的最后一篇了,感谢各位佬友的关注
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引用
操作步骤:
-
创建namespace,如果之前创建过忽略这一步,
kubectl create namespace ai -
将上述所有yaml内容报错到同一个目录中,执行
kubectl apply -f . -
访问
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配置,按需修改 -
pipelines:
enabled: true -
tika:
enabled: true -
websocket:
enabled: true -
clusterDomain:重点,这里要改成部署时在kubespray中设置的
k8s保姆级教程:K8s高可用集群教程-01-使用 Kubespray 快速搭建集群(有手就行)hasakey.local,即clusterDomain: hasakey.localcluster_name: hasakey.local -
persistence:存储,
enabled: true,size: 50Gi这个大小自己设置,storageClass: "longhorn"这里指定使用哪个sc,我这里是longhorn -
service:服务暴露方式,
type: NodePort,nodePort: "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、针对公网访问
-
使用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 -
使用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引用
操作步骤:
-
创建namespace,如果之前创建过忽略这一步,
kubectl create namespace ai -
将上述所有yaml内容报错到同一个目录中,执行
kubectl apply -f . -
访问
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配置,按需修改 -
pipelines:
enabled: true -
tika:
enabled: true -
websocket:
enabled: true -
clusterDomain:重点,这里要改成部署时在kubespray中设置的
k8s保姆级教程:K8s高可用集群教程-01-使用 Kubespray 快速搭建集群(有手就行)hasakey.local,即clusterDomain: hasakey.localcluster_name: hasakey.local -
persistence:存储,
enabled: true,size: 50Gi这个大小自己设置,storageClass: "longhorn"这里指定使用哪个sc,我这里是longhorn -
service:服务暴露方式,
type: NodePort,nodePort: "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、针对公网访问
-
使用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 -
使用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!
网友解答:--【壹】--:
感谢大佬 。
--【贰】--:
感谢大佬
--【叁】--:
感谢大佬
--【肆】--:
感谢佬友分享,学习了
--【伍】--:
很有用,感谢佬

