分类 K3S 下的文章

在 Kubernetes 集群中,为服务配置 HTTPS 是生产环境的标配。本文将介绍如何使用 Cert-Manager 结合 Traefik Ingress,通过 Let's Encrypt 自动申请并续签免费的 SSL/TLS 证书。

部署 Cert-Manager

1、首先,我们需要在集群中安装 Cert-Manager。这里我们创建一个独立的命名空间来管理它。

kubectl create namespace cert-manager

2、部署 cert-manager

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml

3、ClusterIssuer 是 Cert-Manager 的核心资源,用于定义证书颁发机构

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: gua3j7@126.com   #*用于接收证书过期通知等*
    privateKeySecretRef:    #指定存储 ACME 客户端私钥的 Kubernetes Secret 的名称
      name: letsencrypt-prod
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:               #定义用于验证证书颁发请求的解析器。
      - http01:
          ingress:
            class: traefik

4、创建ingress 自动申请证书

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: work-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: traefik
    cert-manager.io/cluster-issuer: letsencrypt-prod  # letsencrypt-prod为ClusterIssuer名称
spec:
  ingressClassName: traefik # 指定 Ingress Class 为 traefik
  tls:
    - secretName: miiv-tls *# 证书将被存储在此 Kubernetes Secret 中*
      hosts:
        - miiv.top # *指定需要证书的域名*
  rules:
    - host: miiv.top  # 转发规则名称
      http:
        paths:
          - path: /
            pathType: ImplementationSpecific
            backend:
              service:
                name: miiv-lnp # 服务名
                port:
                  number: 80 # 服务的端口号 service port,非pod port

如访问Miiv.top出现not found,查看traefik日志发现

intSlice: failed to list *v1.EndpointSlice: endpointslices.discovery.k8s.io is forbidden: User \"system:serviceaccount:kube-system:traefik\" cannot list resource \"endpointslices\" in API group \"discovery.k8s.io\" at the cluster scope" logger="UnhandledError"
W0811 14:29:30.798885       1 reflector.go:561] k8s.io/client-go@v0.31.1/tools/cache/reflector.go:243: failed to list *v1.EndpointSlice: endpointslices.discovery.k8s.io is forbidden: User "system:serviceaccount:kube-system:traefik" cannot list resource "endpointslices" in API group "discovery.k8s.io" at the cluster scope


**原因:缺少对endpointslices、nodes访问规则**
kubectl edit clusterrole traefik-kube-system
ruls末尾增加
- apiGroups:
  - discovery.k8s.io
  resources:
  - endpointslices
  - nodes
  verbs:
  - list
  - watch

构建 Prometheus + Grafana 监控栈离线 Helm 包

适用场景:无法直接访问互联网的 Kubernetes 集群,通过 Helm 离线部署 kube-prometheus-stack(Prometheus Operator、Alertmanager、Grafana 等)以及 Loki 日志系统。

第一步:安装 Helm 客户端

在用于制作离线包的中转机器(Linux)上安装 Helm。

  1. 安装 Helm 3
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
  1. 验证安装
helm version
# 预期输出:version.BuildInfo{Version:"v3.x.x", ...}

第二步:配置 Helm 仓库

添加 Prometheus 社区仓库与 Grafana 官方仓库。

  1. 添加仓库
# Prometheus 社区仓库
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

# Grafana 官方仓库
helm repo add grafana https://grafana.github.io/helm-charts
  1. 更新索引
helm repo update

第三步:下载并补齐 Chart 依赖(核心)

建议在专门目录下操作。

  1. 创建工作目录
mkdir -p helm-offline && cd helm-offline
  1. 下载 kube-prometheus-stack 并更新依赖
# 拉取并解压主包
helm pull prometheus-community/kube-prometheus-stack --untar

# 进入目录下载 dependencies 中声明的子 Chart(如 node-exporter、kube-state-metrics 等)
cd kube-prometheus-stack
helm dependency update
cd ..
  1. 下载 loki 并更新依赖
helm pull grafana/loki --untar

cd loki
helm dependency update
cd ..
  1. 下载 promtail
helm pull grafana/promtail --untar

第四步:打包离线文件

完成后,helm-offline 目录下应包含以下三个目录:

  • kube-prometheus-stack/
  • loki/
  • promtail/

打包命令(示例):

cd ..
zip -r helm-charts-all.zip \
  helm-offline/kube-prometheus-stack \
  helm-offline/loki \
  helm-offline/promtail
# values-low-resource.yaml
# 同级别目录创建 kube-prometheus-stack  loki  promtail
# 针对 2C4G 环境的极限优化配置(已修复重复定义 Bug + 镜像加速)

# 1. 开启 Alertmanager
alertmanager:
  enabled: true
  # --- 以下为内存优化配置 ---
  alertmanagerSpec:
    # 减少保留时间,降低内存占用
    retention: 4h
    # 限制 Alertmanager 自身的资源使用
    resources:
      requests:
        memory: 50Mi
        cpu: 10m
      limits:
        memory: 150Mi  # 限制在 150MB,防止吃光内存
        cpu: 100m
    nodeSelector:
      kubernetes.io/hostname: k3s-node-txy
  # 禁用持久化存储(低配环境建议使用内存存储,除非你需要长期保留告警历史)
  persistentVolume:
    enabled: false

# 2. Prometheus 资源限制与调度
prometheus:
  service:
    type: NodePort
  prometheusSpec:
    retention: 6h
    resources:
      requests:
        memory: 100Mi
        cpu: 50m
      limits:
        memory: 600Mi
        cpu: 500m
    nodeSelector:
      kubernetes.io/hostname: k3s-node-txy

# 3. Grafana 资源限制与调度
grafana:
  service:
    type: NodePort
  adminPassword: "admin"
  resources:
    requests:
      memory: 50Mi
      cpu: 10m
    limits:
      memory: 200Mi
      cpu: 100m
  nodeSelector:
    kubernetes.io/hostname: k3s-node-txy

# 4. 基础组件(合并了 enabled 和 image 配置)
kubeStateMetrics:
  enabled: true
  image:
    registry: registry.cn-hangzhou.aliyuncs.com
    repository: google_containers/kube-state-metrics
    tag: v2.18.0

prometheus-node-exporter:
  enabled: true

目标设备执行

# Helm安装
helm install prometheus ./kube-prometheus-stack     --namespace monitoring     --timeout 15m     --disable-openapi-validation     --no-hooks     -f values-low-resource.yaml

任务运行失败

执行安装时可能报错:

Error: UPGRADE FAILED: pre-upgrade hooks failed: 1 error occurred:
* job prometheus-kube-prometheus-admission-create failed: BackoffLimitExceeded

  • ubernetes Job 的 backoffLimit 字段定义了 单个 Job 允许失败并重试的最大次数(默认值为 6)7。
  • 当 Job 中的 Pod 连续失败次数超过 backoffLimit 时,Job 会被标记为 Failed 状态,错误信息即为 BackoffLimitExceeded7。

这时候需要详细分析下原因

#  查看pod创建情况
kubectl get pods -n monitoring
NAME                                                     READY   STATUS    RESTARTS        AGE
prometheus-kube-prometheus-admission-create-8w4rc        0/1     Error     1 (35s ago)  

**# 查看Pod详细信息**
kubectl logs prometheus-kube-prometheus-admission-create-8w4rc
error: error from server (NotFound): pods "prometheus-kube-prometheus-admission-create-8w4rc" not found in namespace "default"
[root@k3s-master ~]# kubectl logs prometheus-kube-prometheus-admission-create-8w4rc  -n monitoring
W0729 15:43:19.423771       1 client_config.go:683] Neither --kubeconfig nor --master was specified.  Using the inClusterConfig.  This might not work.
Error: failed to get secret: error getting secret: Get "https://10.43.0.1:443/api/v1/namespaces/monitoring/secrets/prometheus-kube-prometheus-admission": dial tcp 10.43.0.1:443: i/o timeout
Usage:
  kube-webhook-certgen create [flags]

Flags:
      --ca-name string       Name of ca file in the secret (default "ca")
      --cert-name string     Name of cert file in the secret (default "cert")
  -h, --help                 help for create
      --host string          Comma-separated hostnames and IPs to generate a certificate for
      --key-name string      Name of key file in the secret (default "key")
      --namespace string     Namespace of the secret where certificate information will be written
      --secret-name string   Name of the secret where certificate information will be written
      --secret-type string   Type of the secret where certificate information will be written (default "Opaque")

Global Flags:
      --kubeconfig string   Path to kubeconfig file: e.g. ~/.kube/kind-config-kind
      --log-format string   Log format: text|json (default "json")
      --log-level string    Log level: error|warn|info|debug (default "info")

{"time":"2026-07-29T15:43:49.450671764Z","level":"ERROR","source":{"function":"github.com/jkroepke/kube-webhook-certgen/cmd.Execute","file":"github.com/jkroepke/kube-webhook-certgen@v1.8.2/cmd/root.go","line":47},"msg":"failed to get secret: error getting secret: **Get \"****https://10.43.0.1:443/api/v1/namespaces/monitoring/secrets/prometheus-kube-prometheus-admission\":**** dial tcp 10.43.0.1:443: i/o timeout"}**

# 测试发现本Master节点无法和CoreDNS、API Server正常通信

以上报错是因为使用了自己创建的证书,导致CoreDNS、API Server异常

不要生成证书,否则容器会异常

恢复方式
# 1. 备份整个 tls 目录
sudo mv /var/lib/rancher/k3s/server/tls /var/lib/rancher/k3s/server/tls.bak

# 2. (可选但推荐) 删除动态生成的证书清单,确保所有证书都被重新创建
sudo rm -f /var/lib/rancher/k3s/server/cred/cert.json

# 3. 重新启动服务生成默认证书

镜像拉取失败(ImagePullBackOff)

现象:

prometheus-kube-state-metrics-...   0/1   ImagePullBackOff   ...

原因:

Failed to pull image "registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.18.0" ... i/o timeout

解决思路:通过国内镜像源提前拉取并打 Tag,使运行时仍按原镜像名找到本地镜像。

registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.18.0 - 镜像下载 | registry.k8s.io

ctr images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.18.0

ctr images tag \
  swr.cn-north-4.myhuaweicloud.com/ddn-k8s/registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.18.0 \
  registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.18.0
  
ctr images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/jkroepke/kube-webhook-certgen:1.8.2

ctr images tag  swr.cn-north-4.myhuaweicloud.com/ddn-k8s/ghcr.io/jkroepke/kube-webhook-certgen:1.8.2  ghcr.io/jkroepke/kube-webhook-certgen:1.8.2

一、故障概述

在 Typecho 博客系统执行文章保存操作(POST /action/contents-post-edit)时,页面返回 HTTP 504 Gateway Timeout 错误。对存储、资源、数据库及 PHP 环境进行分层排查后,确认底层运行正常,问题定位为反向代理超时配置。

二、排查过程与实证结果

1、存储系统验证

排查命令:在 Pod 内执行

time touch /var/www/html/nfs_test && echo "test">/var/www/html/nfs_test && rm /var/www/html/nfs_test

实测结果

real    0m0.125s
user    0m0.000s
sys     0m0.001s

结论:NFS 文件读写操作耗时仅 0.125 秒,无 I/O 延迟或挂载异常,可排除存储瓶颈。

2、资源使用率检查

排查命令kubectl top pod miiv-lnp-557f547c45-d4745

实测结果

NAME                        CPU(cores)   MEMORY(bytes)
miiv-lnp-557f547c45-d4745   13m          25Mi

结论:Pod CPU 使用率 13m、内存 25Mi,资源配额充足,无资源争抢或耗尽情况。

3、数据库状态分析

排查命令SHOW FULL PROCESSLIST;

实测结果

| Id     | User    | Host            | db      | Command | Time | State    | Info                  |
| 404341 | root    | localhost       | NULL    | Query   |    0 | starting | SHOW FULL PROCESSLIST |
| 404377 | typecho | 10.42.2.3:45330 | typecho | Sleep   |   61 |          | NULL                  |
| 404378 | typecho | 10.42.2.3:45340 | typecho | Sleep   |   61 |          | NULL                  |

结论:数据库连接均为 Sleep 状态,无长时间运行查询,无连接阻塞或死锁,负载正常。

4、PHP 执行能力测试

测试脚本:创建 PHP 文件模拟耗时操作,访问

<?php
echo "Start: " . date('Y-m-d H:i:s') . "\n";
sleep(2); // 模拟耗时操作
echo "End: " . date('Y-m-d H:i:s') . "\n";
?>

实测结果

Start: 2026-05-02 12:56:03
End: 2026-05-02 12:56:05

结论:PHP-FPM 可正常处理 2 秒耗时请求,脚本执行不受限制,应用层基础处理能力无异常。

5、故障日志定位

日志内容

10.42.1.2 - - [02/May/2026:12:44:32 +0000] "POST /action/contents-post-edit?_=94be44fc416e098a75bd09ba9d85aec0 HTTP/1.1" 504 569

关键信息:文章保存请求触发 504 超时,与 PHP 正常处理能力测试结果不一致。

结论:问题并非应用层逻辑错误,而是网关层超时配置导致。

6、调整反向代理超时配置

修改 Nginx 或 Ingress Controller 配置,增加proxy_read_timeoutproxy_send_timeout值(建议设置为 120 秒以上)

示例配置(Nginx):

location~\.php$ {
    proxy_read_timeout 120s;
    proxy_send_timeout 120s;
}

重新发布文章发现正常(耗时还是有问题)

三、根因分析

综合实测数据,系统底层环境(存储、资源、数据库、PHP)均无异常。HTTP 504 错误本质为反向代理等待后端响应超过预设超时阈值。PHP 可正常处理 2 秒耗时请求,说明基础执行能力正常;但文章保存操作可能包含额外处理逻辑(如插件触发、缓存更新等),导致总耗时超过网关默认超时时间(通常为 60 秒)。

四、解决方案

关闭Typecho中的AISummary插件正常

typecho 动态 IP 环境适配与故障排查指南

场景一:解决动态 IP 导致的后台死链问题

Typecho 是一个使用 PHP 编写的博客系统。在部署环境(如容器、动态 IP 服务器)中,如果域名或 IP 地址不固定,系统生成的后台按钮链接往往会指向旧的 IP 地址或错误的端口,导致访问异常。

为了解决这个问题,我们需要开启相对地址模式。虽然开启后永久链接(伪静态)可能无法正常使用,但这能确保后台按钮在 IP 变动时依然可用。

开启相对地址的两种方法

方法一:修改数据库(不推荐)
直接修改数据库 typecho_options 表,将 siteUrl 字段的值改为相对路径(如 /)。

方法二:修改核心代码(推荐)
通过修改核心文件来绕过后台对“绝对 URL”的强制校验。

操作步骤:

1.定位文件:var/Widget/Options/General.php

2.查找代码(约 87 行):

->addRule('url', _t('请填写一个合法的URL地址'))

3.修改代码:

->addRule('xssCheck', _t('请填写一个合法的URL地址'))

4.保存文件,刷新后台即可生效。

修改效果Typecho 后台不再强制校验站点地址协议和域名,后台所有链接自动适配当前访问地址,彻底解决动态 IP / 端口导致的死链问题。

场景二:后台 “Server Error” 排查与修复

访问后台模块(如附件管理)出现 Server Error (500) 且无具体报错时,按以下步骤修复。

1. 开启调试模式

编辑网站根目录 config.inc.php,在 <?php 下方直接粘贴:

// ----------调试使用-------
define('__TYPECHO_DEBUG__', true);
error_reporting(E_ALL);
ini_set('display_errors', 'On');
// 强制输出错误到浏览器,防止被 Nginx 拦截
ini_set('html_errors', 'Off');
// -------------------------

2. 分析报错信息

典型错误
TypeError: Argument 1 passed to Typecho\Common::mimeIconType() must be of the type string, null given

原因:数据库中部分附件 mime 字段为 NULL,但函数强制要求字符串,导致崩溃。

3. 修复方案

编辑文件admin/manage-medias.php

修改前(报错)

<?php $mime = \Typecho\Common::mimeIconType($attachments->attachment->mime); ?>

修改后(修复)

<?php $mime = \Typecho\Common::mimeIconType($attachments->attachment->mime ?? ''); ?>

修复后完整代码段

<?php while ($attachments->next()): ?>

<?php $mime = \Typecho\Common::mimeIconType($attachments->attachment->mime ?? ''); ?>

4. 结果与注意

保存后刷新后台,Server Error 消失,附件管理正常显示。

️ 注意:调试完成后,务必注释 / 删除 config.inc.php 里的调试代码,避免生产环境泄露敏感信息。