前言:配置管理与外部访问的重要性

在运营容器化应用程序时,最重要的课题之一是配置的分离。将应用程序代码与配置分离后,可以在开发、预发布、生产环境中重用同一镜像。Kubernetes 为此提供了 ConfigMapSecret 这两种资源。

此外,从集群外部访问内部服务需要适当的路由机制。Ingress 作为 L7 负载均衡器,将 HTTP/HTTPS 流量路由到集群内部的服务。本篇将详细介绍这三个核心资源。

1. 配置管理的重要性

1.1 为什么要分离配置?

正如 12-Factor App 方法论所强调的,配置应该与代码分离:

  • 环境部署:可以将同一镜像部署到各种环境(开发、测试、生产)
  • 安全增强:敏感信息无需包含在代码仓库中
  • 灵活变更:更改配置时无需重新构建镜像即可应用
  • 团队协作:开发人员和运维人员可以独立管理各自的领域

1.2 Kubernetes 的配置管理方式

Kubernetes 通过两种资源来管理配置:

资源 用途 存储方式
ConfigMap 一般配置数据 明文(Plain text)
Secret 敏感信息 Base64 编码

2. ConfigMap 的创建和使用

2.1 什么是 ConfigMap?

ConfigMap 是用于存储键值对配置数据的 Kubernetes 资源。可以存储应用程序的环境变量、配置文件、命令行参数等。

2.2 ConfigMap 创建方法

方法1:使用字面值创建

# 单个键值对
kubectl create configmap app-config --from-literal=APP_ENV=production

# 多个键值对
kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --from-literal=LOG_LEVEL=info \
  --from-literal=MAX_CONNECTIONS=100

方法2:从文件创建

# 单个文件
kubectl create configmap nginx-config --from-file=nginx.conf

# 整个目录
kubectl create configmap app-configs --from-file=./configs/

方法3:使用 YAML 清单创建

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  # 简单键值对
  APP_ENV: "production"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"

  # 多行配置文件
  application.properties: |
    server.port=8080
    spring.datasource.url=jdbc:mysql://mysql:3306/mydb
    spring.jpa.hibernate.ddl-auto=update
    logging.level.root=INFO

  # JSON 格式配置
  config.json: |
    {
      "apiEndpoint": "https://api.example.com",
      "timeout": 30,
      "retryCount": 3
    }
kubectl apply -f configmap.yaml

2.3 ConfigMap 使用:环境变量

将单个键作为环境变量使用

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    env:
    # 仅注入特定键作为环境变量
    - name: APPLICATION_ENV
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: APP_ENV
    - name: LOG_LEVEL
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: LOG_LEVEL

将所有键作为环境变量使用

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    envFrom:
    - configMapRef:
        name: app-config
    # 可以添加前缀
    - configMapRef:
        name: app-config
      prefix: CONFIG_

2.4 ConfigMap 使用:卷挂载

如果需要配置文件,可以将其作为卷挂载:

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    volumeMounts:
    - name: config-volume
      mountPath: /etc/nginx/conf.d
      readOnly: true
  volumes:
  - name: config-volume
    configMap:
      name: nginx-config
      # 仅挂载特定键时
      items:
      - key: nginx.conf
        path: default.conf

使用 subPath 挂载特定文件

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: config-volume
      mountPath: /app/config/application.properties
      subPath: application.properties
  volumes:
  - name: config-volume
    configMap:
      name: app-config

3. Secret 的创建和使用

3.1 什么是 Secret?

Secret 是用于存储密码、OAuth 令牌、SSH 密钥等敏感信息的资源。与 ConfigMap 类似,但数据以 Base64 编码方式存储。

3.2 Secret 类型

类型 说明 使用示例
Opaque 任意用户自定义数据 密码、API 密钥
kubernetes.io/tls TLS 证书 HTTPS 证书
kubernetes.io/dockerconfigjson Docker 仓库认证 私有仓库访问
kubernetes.io/basic-auth 基本认证信息 用户名/密码
kubernetes.io/ssh-auth SSH 认证 SSH 私钥

3.3 Opaque Secret 创建

方法1:通过命令行创建

# 使用字面值创建
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=s3cr3tP@ssw0rd

# 从文件创建
kubectl create secret generic ssh-key \
  --from-file=ssh-privatekey=/path/to/id_rsa

方法2:使用 YAML 清单创建

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  # Base64 编码的值
  # echo -n 'admin' | base64 -> YWRtaW4=
  username: YWRtaW4=
  # echo -n 's3cr3tP@ssw0rd' | base64 -> czNjcjN0UEBzc3cwcmQ=
  password: czNjcjN0UEBzc3cwcmQ=
---
# 使用 stringData(自动 Base64 编码)
apiVersion: v1
kind: Secret
metadata:
  name: api-credentials
type: Opaque
stringData:
  api-key: my-super-secret-api-key
  api-secret: another-secret-value

3.4 TLS Secret 创建

# 创建 TLS 证书 Secret
kubectl create secret tls tls-secret \
  --cert=path/to/tls.crt \
  --key=path/to/tls.key
# tls-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: tls-secret
type: kubernetes.io/tls
data:
  tls.crt: |
    LS0tLS1CRUdJTi... (Base64 编码的证书)
  tls.key: |
    LS0tLS1CRUdJTi... (Base64 编码的密钥)

3.5 使用 Secret

作为环境变量注入

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password

作为卷挂载

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: secret-volume
    secret:
      secretName: db-credentials
      # 文件权限设置
      defaultMode: 0400

3.6 Secret 安全注意事项

Secret 默认只进行 Base64 编码,并未加密。需要考虑以下安全措施:

  • etcd 加密:对存储在 etcd 中的 Secret 进行加密
    # EncryptionConfiguration
    apiVersion: apiserver.config.k8s.io/v1
    kind: EncryptionConfiguration
    resources:
      - resources:
        - secrets
        providers:
        - aescbc:
            keys:
            - name: key1
              secret: base64-encoded-32-byte-key
        - identity: {}
  • RBAC 限制:最小化对 Secret 的访问权限
  • 使用外部 Secret 管理工具:HashiCorp Vault、AWS Secrets Manager、Azure Key Vault 等
  • Secret 轮换:定期更改 Secret 值
  • 审计日志:启用 Secret 访问的审计日志

4. 什么是 Ingress?

4.1 Ingress 概念

Ingress 是管理从集群外部到内部服务的 HTTP/HTTPS 流量的 API 对象。作为 L7(应用层)负载均衡器,提供以下功能:

  • 基于主机的路由:根据域名路由到不同的服务
  • 基于路径的路由:根据 URL 路径路由到不同的服务
  • TLS/SSL 终止:处理 HTTPS 流量
  • 负载均衡:将流量分发到多个 Pod

4.2 Ingress vs Service(NodePort/LoadBalancer)

特性 NodePort LoadBalancer Ingress
层级 L4 L4 L7
协议 TCP/UDP TCP/UDP HTTP/HTTPS
URL 路由 不可能 不可能 可能
TLS 终止 不可能 不可能 可能
成本 免费 产生云费用 单个 LB 服务多个服务

5. Ingress Controller

5.1 什么是 Ingress Controller?

Ingress 资源只是声明性的路由规则,实际运行需要 Ingress Controller。Ingress Controller 监视 Ingress 资源并据此配置负载均衡器。

5.2 主要 Ingress Controller

Nginx Ingress Controller

# Nginx Ingress Controller 安装(Helm)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install nginx-ingress ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace

# 或使用 manifest 安装
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml

Traefik Ingress Controller

# Traefik 安装(Helm)
helm repo add traefik https://helm.traefik.io/traefik
helm repo update
helm install traefik traefik/traefik \
  --namespace traefik \
  --create-namespace

其他 Ingress Controller

  • HAProxy Ingress:高性能负载均衡
  • Contour:基于 Envoy
  • AWS ALB Ingress Controller:集成 AWS Application Load Balancer
  • GKE Ingress Controller:集成 Google Cloud Load Balancer

6. Ingress YAML 编写

6.1 基本 Ingress 结构

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  annotations:
    # Ingress Controller 特定设置
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx  # 指定使用的 Ingress Controller
  rules:
  - host: example.com      # 基于主机的路由
    http:
      paths:
      - path: /            # 基于路径的路由
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80

6.2 pathType 选项

  • Exact:仅匹配完全一致的路径
  • Prefix:匹配以指定路径开头的所有请求
  • ImplementationSpecific:根据 Ingress Controller 行为

7. 基于主机/路径的路由

7.1 基于主机的路由

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: host-based-ingress
spec:
  ingressClassName: nginx
  rules:
  # api.example.com -> api-service
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

  # web.example.com -> web-service
  - host: web.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

  # admin.example.com -> admin-service
  - host: admin.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: admin-service
            port:
              number: 3000

7.2 基于路径的路由

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: path-based-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - host: example.com
    http:
      paths:
      # example.com/api/* -> api-service
      - path: /api(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: api-service
            port:
              number: 8080

      # example.com/web/* -> web-service
      - path: /web(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: web-service
            port:
              number: 80

      # example.com/* (默认) -> frontend-service
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80

7.3 复合路由(主机 + 路径)

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: combined-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /products
        pathType: Prefix
        backend:
          service:
            name: product-service
            port:
              number: 8080
      - path: /orders
        pathType: Prefix
        backend:
          service:
            name: order-service
            port:
              number: 8080
      - path: /users
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 8080

8. TLS 配置

8.1 TLS Secret 创建

# 创建自签名证书(测试用)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key \
  -out tls.crt \
  -subj "/CN=example.com"

# 创建 TLS Secret
kubectl create secret tls example-tls \
  --cert=tls.crt \
  --key=tls.key

8.2 在 Ingress 中应用 TLS

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    - www.example.com
    secretName: example-tls
  - hosts:
    - api.example.com
    secretName: api-tls
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80
  - host: api.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

8.3 使用 cert-manager 自动管理证书

# 安装 cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# Let's Encrypt ClusterIssuer 配置
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx
---
# 自动证书签发 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: auto-tls-ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    secretName: example-tls-auto  # cert-manager 自动生成
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-service
            port:
              number: 80

9. 实战示例:微服务路由

# 完整的微服务 Ingress 配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: microservices-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - myapp.example.com
    secretName: myapp-tls
  rules:
  - host: myapp.example.com
    http:
      paths:
      # API Gateway
      - path: /api/v1/users
        pathType: Prefix
        backend:
          service:
            name: user-service
            port:
              number: 8080
      - path: /api/v1/products
        pathType: Prefix
        backend:
          service:
            name: product-service
            port:
              number: 8080
      - path: /api/v1/orders
        pathType: Prefix
        backend:
          service:
            name: order-service
            port:
              number: 8080
      # WebSocket
      - path: /ws
        pathType: Prefix
        backend:
          service:
            name: websocket-service
            port:
              number: 8081
      # 前端(默认)
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend-service
            port:
              number: 80

10. 结论

本篇我们详细了解了 Kubernetes 的核心配置管理资源 ConfigMap 和 Secret,以及管理外部流量的 Ingress:

  • ConfigMap:管理一般配置数据,可以作为环境变量或卷注入到 Pod
  • Secret:安全存储敏感信息,需要额外的安全措施
  • Ingress:作为 L7 负载均衡器,提供基于主机/路径的路由和 TLS 终止
  • Ingress Controller:实际实现 Ingress 资源的组件

在下一篇第10篇中,我们将介绍 Kubernetes 包管理器 HelmCI/CD 流水线 的构建。将涵盖使用 Helm Chart 进行应用程序部署和基于 GitOps 的自动化部署策略。