DevOps 入门系列:从 Pod 到 Ingress(K8s 核心概念)

你已经会用 Docker 把应用打包成镜像,也会用 GitLab CI 自动构建和推送镜像。
现在你想把这些镜像放到 Kubernetes 里运行起来。
本文用一个真实的 Flask 应用作为例子,带你一步步理解 K8s 最核心的几个概念。
不执行命令,只讲概念和 YAML 的关键部分。


你遇到了什么问题?

假设你写了一个 Flask 应用,镜像已经推到了阿里云 ACR 私有仓库

你现在想在 K8s 集群里运行它,希望:

  • 一直运行 —— 万一容器挂了,能自动重启。

  • 跑 3 个副本 —— 分担流量,一台机器坏了还有别的。

  • 升级版本时不中断服务 —— 从 v1 换到 v2,用户无感知。

  • 密码不能写死在镜像里 —— 数据库密码要安全地传进去。

  • 配置能随时改 —— 比如日志级别、数据库地址,不想重新打包镜像。

  • 能通过域名从外面访问 —— 用户输入 flask.mycompany.com 就能打开页面。

这些问题,K8s 里有对应的资源逐个解决。

现在,我们从最基础的开始:怎么让一个容器在 K8s 里跑起来?

1. Pod:K8s 里最小的 “工人”

你遇到的第一个问题

Docker 里你直接用 docker run 启动容器。
但在 K8s 里,你不能直接 “跑一个容器”。K8s 的最小调度单位叫 Pod。

Pod 是什么?

定义: Pod 是 Kubernetes 中可以创建和管理的最小部署单元。
一个 Pod 里可以放一个或多个容器。最常见的用法是一个 Pod 只放一个容器(就是你的应用容器)。

Pod 的关键特点

  1. 短暂:Pod 可能随时被删除或重建(比如机器坏了、手动删除)。重建后 IP 会变。

  2. 最小单位:K8s 不直接操作容器,而是操作 Pod。你告诉 K8s “帮我起一个 Pod”,K8s 才会在里面创建容器。

Pod 的标签:给工人贴上“工牌”

Pod 有一个非常重要的功能:打标签(Label)。

定义: 标签就是贴在 Pod 上的一组 key: value(比如 app: flask、env: prod、tier: backend)。

为什么需要标签?
K8s 集群里可能有成百上千个 Pod。Deployment 怎么知道哪些 Pod 是自己管的?Service 怎么知道把流量转发给哪些 Pod?靠的就是标签。Deployment 和 Service 会说:“我只关心带有 app: flask 这个标签的 Pod”。

在 YAML 里怎么写:

yaml
metadata:
  labels:
    app: flask          # 标签 key 是 app,value 是 flask
    env: prod

重点:

  • 标签的 key 和 value 可以随便起(比如 app: my-app、tier: frontend),只要统一就好。

  • Deployment 和 Service 里的 selector(选择器)会写相同的标签,表示“我就找这个标签的 Pod”。

  • 一个 Pod 可以打多个标签,用换行分开。


在实际生产过程中,你几乎不会单独创建 Pod。手动创建的 Pod 挂了就真的挂了,没人帮你重启。那谁来帮你管 Pod 的生命周期?答案是 Deployment

2. Deployment:帮你管 Pod 的 “店长”

你遇到的第二个问题

Pod 能跑容器,但 Pod 太 “脆” 了 —— 如果它挂了(比如程序崩溃、机器宕机),没人帮它重启。
你想要的是:一直有 3 个 Pod 在运行,如果少了就自动补上。
另外,以后升级镜像版本(比如 v1 → v2),你希望不停机,一个接一个地换,用户无感知。这就需要Deployment来实现

Deployment 是什么?

定义: Deployment 是 Kubernetes 中用来管理 Pod 的控制器。你可以声明 “我要 3 个 Pod 永远运行”,Deployment 就会维持这个数量,并支持滚动更新和回滚。

关键点

  • Pod管理:你告诉 Deployment“我要 3 个 Pod,用这个镜像”,它负责创建并监控这些 Pod。

  • 自愈:Pod 意外消失,Deployment 自动重建(全新 Pod,IP 会变)。

  • 滚动更新:改镜像版本后,Deployment 逐步替换旧 Pod,不停机。

  • 回滚:新版有问题,可以一键回到旧版本。

YAML 核心字段

apiVersion: apps/v1
kind: Deployment                  # 声明这是Deployment的配置
metadata:
  name: flask-deployment          # 这个 Deployment 的名字
spec:
  replicas: 3                     # 我要 3 个 Pod
  selector:
    matchLabels:
      app: flask                  # 这个 Deployment 只管带 app=flask 标签的 Pod
  template:                       # Pod 的“模板”
    metadata:
      labels:
        app: flask                # Pod 的标签(必须和上面 selector 一致)
    spec:
      containers:
      - name: flask
        image: registry.cn-hangzhou.aliyuncs.com/my-lab/flask-app:v1
        ports:
        - containerPort: 5000

使用重点

  1. 你只需要关心 replicas(副本数)、image(镜像地址)、containerPort(容器监听端口)。

  2. 标签(app: flask)很重要:Deployment 用它来找到自己管的 Pod,后面 Service 也会用它来找到 Pod。

  3. 你几乎不会手动创建 Pod,永远是通过 Deployment 来创建。

3. Service:给 Pod 一个 “永不变化的门牌号”

你遇到的第三个问题

Deployment 帮你把 Pod 管得好好的,Pod 挂了会自动重建。但有一个新问题:Pod 每次重建,IP 都会变
比如你的 Flask 后端 Pod 从 10.244.1.5 变成了 10.244.2.8,前端怎么知道新的 IP 是什么?你不可能每次都去改前端的配置。

你需要一个固定的访问入口,不管后端 Pod 怎么变,这个入口永远不变。

Service 是什么?

定义: Service 为一组 Pod 提供一个固定的访问入口,并自动把请求转发给当前在线的 Pod。

核心

  • 固定 IP:Service 创建后会分配一个固定的 ClusterIP(如 10.96.100.1),不会变。

  • 靠标签找人:Service 不关心 Pod 的 IP,它通过标签(比如 app: flask)自动找到当前所有带有这个标签的 Pod。

  • 负载均衡:如果有多个匹配的 Pod,Service 会把请求轮流转发给它们(默认轮询)。

Service 类型

  • ClusterIP(默认):只在集群内部可访问。

  • NodePort:在集群每个节点上开放一个端口(如 30080),外部可以通过 节点IP:30080 访问。

  • LoadBalancer:云厂商自动分配一个公网 IP(生产环境常用)。

YAML 核心字段

apiVersion: v1
kind: Service
metadata:
  name: flask-service
spec:
  type: ClusterIP          # 默认值,不写也行
  selector:
    app: flask             # 只转发给带这个标签的 Pod(和 Deployment 里的标签一致)
  ports:
    - port: 80             # Service 自己的端口(别人访问这个端口)
      targetPort: 5000     # 转发到 Pod 的 5000 端口

重点

  1. selector.app 必须和 Pod 上的标签(Deployment 里 template.metadata.labels)完全一致,否则 Service 找不到 Pod。

  2. 先有 Deployment 再建 Service(Pod 先跑起来,Service 才能找到它们)。

  3. 如果访问 Service 没反应,用 kubectl get endpoints 检查 Endpoints 是不是空 —— 空的说明标签没匹配上。


现在 Pod 有固定入口了。但还有两个问题:数据库密码写在哪里?普通配置经常改怎么办?

4. Secret:藏密码的 “保险柜”

你遇到的第四个问题

你的 Flask 应用需要连接 MySQL 数据库。数据库密码是 my-secret-pw
你不想把这个密码写死在 Dockerfile 里(那样任何人都能看到镜像里的密码),也不想写死在代码里(改密码要重新打包镜像)。你想把密码单独存起来,Pod 启动时自己来取。于是你想到了Secret。

Secret 是什么?

定义: Secret 是 Kubernetes 中用来存储敏感信息(密码、Token、SSH 密钥)的对象。

核心

  • 存敏感信息:密码、API 密钥、证书等。

  • 数据是 Base64 编码:不是加密,只是避免肉眼直接看到。生产环境要配合 etcd 加密或外部密钥管理。

  • Pod 怎么用:可以通过环境变量或挂载文件的方式读取 Secret 里的值。

两种常用类型

  • Opaque(通用,存任意键值对)

  • [kubernetes.io/dockerconfigjson](kubernetes.io/dockerconfigjson)(存镜像仓库的账号密码,给 Deployment 拉私有镜像用)

YAML 核心字段

apiVersion: v1
kind: Secret
metadata:
  name: mysql-secret
type: Opaque
data:
  password: bXktc2VjcmV0LXB3    # "my-secret-pw" 的 base64 编码

在 Deployment 中引用

env:
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: mysql-secret
      key: password

生产环境须知

  1. 创建 Secret 通常用命令 kubectl create secret generic,不用手写 base64,一般也不会手动创建到YAML文件里。

  2. 永远不要把密码写在 YAML 文件里提交到 Git(除非你用了外部加密工具如 sops)。

  3. Secret 只能被同一个命名空间里的 Pod 引用。


密码解决了。但还有一些不是密码、但经常改的配置(比如日志级别、数据库地址),用 Secret 太麻烦,有没有更简单的?

5. ConfigMap:随手记的 “便利贴”

你遇到的第五个问题

你的 Flask 应用需要知道:

  • 日志级别是 info 还是 debug

  • 数据库主机地址是 mysql-service(这个地址在 K8s 内部是固定的)

这些信息不是秘密,但经常要改(比如开发环境用 debug,生产环境用 info)。你不想每次改日志级别都重新打包镜像。

ConfigMap 是什么?

定义: ConfigMap 是 Kubernetes 中用来存储非敏感配置信息的对象,如环境变量、命令行参数、配置文件。

核心

  • 存普通配置:不敏感的信息(端口、日志级别、地址)。

  • Pod 怎么用:同 Secret—— 环境变量或挂载文件。

  • 和 Secret 的区别:Secret 存密码等敏感信息,ConfigMap 存非敏感信息。

  • 更新问题:修改 ConfigMap 后,已运行的 Pod 不会自动刷新环境变量。需要重启 Pod(比如删除 Pod 让 Deployment 重建)。

YAML 核心字段

apiVersion: v1
kind: ConfigMap
metadata:
  name: flask-config
data:
  LOG_LEVEL: info
  DB_HOST: mysql-service

在 Deployment 中引用

env:
- name: LOG_LEVEL
  valueFrom:
    configMapKeyRef:
      name: flask-config
      key: LOG_LEVEL
- name: DB_HOST
  valueFrom:
    configMapKeyRef:
      name: flask-config
      key: DB_HOST

生产环境须知

  1. 开发、测试、生产环境可以用同一个镜像,只换 ConfigMap(和 Secret)—— 好比连锁店,配方一样,每家店的营业时间、菜单价目表各自调整。

  2. ConfigMap 可以挂载成文件,如果你的应用需要读取配置文件(如 application.properties),把整个文件内容塞进 ConfigMap 就行。

  3. 如果配置项不多,用环境变量最简单。


现在 Pod 能跑、配置能抽离,就剩最后一步:让用户通过域名访问。

6. Ingress:商场门口的 “总导览牌”

你遇到的第六个问题

你的 Flask 应用已经在集群内部跑起来了,Service 也给了固定 IP(ClusterIP)。但 ClusterIP 只能在集群内部访问,用户怎么从外面访问呢?

你可以把 Service 的类型改成 NodePort 或 LoadBalancer,但它们都有缺点:

  • NodePort:端口号很大(如 http://node-ip:31234),不好记,也不安全。

  • LoadBalancer:每个 Service 都要买一个云负载均衡器,很贵。

如果以后你有多个应用(比如 api.myapp.comweb.myapp.com),难道每个都配一个 LoadBalancer?

你需要一个统一入口,根据域名把请求转发到不同的 Service。

Ingress 是什么?

定义: Ingress 是 Kubernetes 中管理外部访问集群内服务的 API 对象,通常提供 HTTP/HTTPS 路由。

核心

  • Ingress 只定义规则:写清楚 “域名 flask.mycompany.com → 转发到 flask-service”。

  • Ingress Controller 才是干活的:集群里必须有一个 Ingress Controller(如 Nginx Ingress、Traefik)监听这些规则,并实际转发流量。k3s(低配版k8s) 自带 Traefik,minikube 需要手动启用。

  • 支持 HTTPS:可以配置 TLS 证书(存在 Secret 里),自动处理 HTTPS。

  • 节省成本:一个 Ingress + 一个 LoadBalancer 就能服务几十个 Service。

YAML 核心字段

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: flask-ingress
spec:
  rules:
  - host: flask.mycompany.com        # 用户访问这个域名
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: flask-service      # 转发给这个 Service
            port:
              number: 80

生产环境须知

  1. Ingress 本身不干活,要确认集群里有 Ingress Controller(kubectl get pods -n kube-system | grep ingress)。

  2. 本地测试可以用 [nip.io](nip.io) 魔法域名,比如 flask.127.0.0.1.nip.io 自动解析到 [127.0.0.1](127.0.0.1),不用配 DNS。

  3. 如果你只有一两个服务,直接用 type: LoadBalancerkubectl port-forward 可能更简单。Ingress 适合中大型项目。

7. 完整串联:从用户请求到容器处理

用户访问 https://flask.mycompany.com
    │
    ▼
DNS 解析到云厂商的 LoadBalancer 公网 IP(Ingress Controller 暴露的)
    │
    ▼
Ingress Controller(Nginx/Traefik)接收请求
    │
    ├── 匹配 Ingress 规则:host = flask.mycompany.com
    │
    ▼
转发给 flask-service(ClusterIP,地址 10.96.100.1:80)
    │
    ▼
flask-service 通过标签 `app=flask` 找到所有 Pod(比如 3 个)
    │ 轮询选中其中一个 Pod IP(如 10.244.1.5:5000)
    ▼
Pod 里的 Flask 容器处理请求
    ├── 从环境变量读取配置(来自 ConfigMap 和 Secret)
    └── 返回响应

资源清单(按顺序准备)

资源 必须吗 作用
Deployment 必须 管理 Pod 副本数、更新
Service 必须 给 Pod 固定入口
Secret 按需 存密码、镜像仓库认证
ConfigMap 按需 存普通配置
Ingress 按需 域名路由 + HTTPS

学习重点

刚开始练习,只需 Deployment + Service(ClusterIP),先在集群内部用 kubectl port-forward 测试。熟练后再加 Secret、ConfigMap、Ingress。

8. 总结

  • Pod:最小的工人,里面跑容器(通常是 1 个)。

  • Deployment:店长,负责维持 Pod 数量、滚动更新、自愈。

  • Service:前台总机,给动态变化的 Pod 一个固定 IP,并负载均衡。

  • Secret:保险柜,存密码。

  • ConfigMap:便利贴,存普通配置。

  • Ingress:总导览牌,根据域名路由到不同 Service。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐