标签归档:k8s

Kubernetes kubectl –raw 使用指南

什么是 kubectl –raw?

kubectl --raw 是一个强大的底层工具,允许你直接访问 Kubernetes API Server 的 REST API,绕过 kubectl 的客户端逻辑、准入控制器(Admission Controllers)和 Webhook。

为什么需要 –raw?

标准 kubectl 的请求流程

kubectl 命令
    ↓
客户端验证和处理
    ↓
Admission Controllers
    ↓
Mutating Webhooks (修改请求)
    ↓
Validating Webhooks (验证请求)
    ↓
API Server 存储到 etcd

kubectl –raw 的请求流程

kubectl --raw
    ↓
直接 HTTP 请求到 API Server
    ↓
绕过大部分中间件
    ↓
直接操作 etcd

适用场景

  1. 绕过 Webhook 干扰 – 当 Mutating/Validating Webhook 阻止正常操作时
  2. 调试 API Server – 排查 kubectl 客户端与 API Server 的交互问题
  3. 访问特殊端点 – 访问 metrics、healthz 等非资源端点
  4. 绕过客户端限制 – kubectl 版本不支持某些新特性时
  5. 性能测试 – 直接测试 API Server 响应时间
  6. 修复僵尸资源 – 清理被控制器锁定的资源状态

基本语法

# 基本格式
kubectl get --raw <API-PATH>

# 或在某些版本中
kubectl --raw <API-PATH>

常用操作示例

1. GET 请求 – 查询资源

查看集群级别资源

# 获取所有节点
kubectl get --raw /api/v1/nodes | jq .

# 获取特定节点
kubectl get --raw /api/v1/nodes/node-name | jq .

# 获取节点状态
kubectl get --raw /api/v1/nodes/node-name/status | jq .

# 获取所有命名空间
kubectl get --raw /api/v1/namespaces | jq .

查看命名空间级别资源

# 获取 default 命名空间的所有 Pod
kubectl get --raw /api/v1/namespaces/default/pods | jq .

# 获取特定 Pod
kubectl get --raw /api/v1/namespaces/default/pods/pod-name | jq .

# 获取 Deployment
kubectl get --raw /apis/apps/v1/namespaces/default/deployments/deploy-name | jq .

# 获取 Service
kubectl get --raw /api/v1/namespaces/default/services/svc-name | jq .

查看子资源

# Pod 日志
kubectl get --raw /api/v1/namespaces/default/pods/pod-name/log

# Pod 状态
kubectl get --raw /api/v1/namespaces/default/pods/pod-name/status | jq .

# Service 的 Endpoint
kubectl get --raw /api/v1/namespaces/default/endpoints/service-name | jq .

2. PUT 请求 – 完整更新资源

# 更新节点(先获取,修改,再替换)
kubectl get --raw /api/v1/nodes/node-name > node.json

# 编辑 node.json 文件
vim node.json

# 替换(注意:不同版本语法可能不同)
kubectl replace --raw /api/v1/nodes/node-name -f node.json

# 或使用 kubectl proxy 方式
kubectl proxy --port=8001 &
curl -X PUT \
  -H "Content-Type: application/json" \
  -d @node.json \
  http://localhost:8001/api/v1/nodes/node-name

实战案例:清除节点僵尸条件

# 获取节点当前状态
kubectl get --raw /api/v1/nodes/node-name > /tmp/node.json

# 使用 jq 删除特定条件
jq 'del(.status.conditions[] | select(.type == "EtcdIsVoter"))' \
  /tmp/node.json > /tmp/node-fixed.json

# 更新节点状态
kubectl replace --raw /api/v1/nodes/node-name/status -f /tmp/node-fixed.json

3. POST 请求 – 创建资源

# 创建 Pod
cat > pod.json <<EOF
{
  "apiVersion": "v1",
  "kind": "Pod",
  "metadata": {
    "name": "test-pod",
    "namespace": "default"
  },
  "spec": {
    "containers": [{
      "name": "nginx",
      "image": "nginx:latest"
    }]
  }
}
EOF

kubectl create --raw /api/v1/namespaces/default/pods -f pod.json

4. DELETE 请求 – 删除资源

# 删除 Pod
kubectl delete --raw /api/v1/namespaces/default/pods/pod-name

# 使用 kubectl proxy 方式
kubectl proxy --port=8001 &
curl -X DELETE http://localhost:8001/api/v1/namespaces/default/pods/pod-name

5. PATCH 请求 – 部分更新

# JSON Patch (精确的操作指令)
kubectl patch --raw /api/v1/nodes/node-name \
  --type='json' \
  -p='[
    {"op": "add", "path": "/metadata/labels/new-label", "value": "new-value"},
    {"op": "remove", "path": "/status/conditions/0"}
  ]'

# Strategic Merge Patch (合并式更新)
kubectl patch --raw /api/v1/nodes/node-name \
  --type='merge' \
  -p '{
    "metadata": {
      "labels": {
        "environment": "production"
      }
    }
  }'

# Merge Patch (简单合并)
kubectl patch --raw /api/v1/nodes/node-name \
  --type='merge' \
  -p '{"spec":{"unschedulable":true}}'

API 路径规则

核心 API 组 (Core API Group)

# 格式
/api/v1/<resource-type>                        # 集群级别
/api/v1/namespaces/
<namespace>/<resource-type> # 命名空间级别

# 示例
/api/v1/nodes
/api/v1/nodes/node-name
/api/v1/nodes/node-name/status
/api/v1/namespaces/default/pods
/api/v1/namespaces/default/pods/pod-name
/api/v1/namespaces/default/services

命名 API 组 (Named API Groups)

# 格式
/apis/
<group>/<version>/<resource-type>
/apis/
<group>/<version>/namespaces/<ns>/<resource-type>

# 常用 API 组示例
/apis/apps/v1/deployments                           # Deployment
/apis/apps/v1/namespaces/default/deployments
/apis/batch/v1/cronjobs                             # CronJob
/apis/networking.k8s.io/v1/ingresses                # Ingress
/apis/rbac.authorization.k8s.io/v1/clusterroles     # ClusterRole
/apis/storage.k8s.io/v1/storageclasses              # StorageClass

子资源 (Subresources)

# 状态子资源
/api/v1/nodes/
<name>/status
/apis/apps/v1/namespaces/
<ns>/deployments/<name>/status

# 日志
/api/v1/namespaces/
<ns>/pods/<name>/log
/api/v1/namespaces/
<ns>/pods/<name>/log?container=container-name

# 执行命令
/api/v1/namespaces/
<ns>/pods/<name>/exec

# 端口转发
/api/v1/namespaces/
<ns>/pods/<name>/portforward

# 代理
/api/v1/nodes/
<name>/proxy
/api/v1/namespaces/
<ns>/pods/<name>/proxy
/api/v1/namespaces/
<ns>/services/<name>/proxy

特殊端点

查看 API 资源

# 列出所有 API 版本
kubectl get --raw /apis | jq '.groups[].name'

# 查看特定 API 组
kubectl get --raw /apis/apps/v1 | jq .

# 列出所有可用资源
kubectl get --raw /api/v1 | jq '.resources[].name'

# OpenAPI 规范
kubectl get --raw /openapi/v2 | jq . > openapi.json

集群信息

# 版本信息
kubectl get --raw /version | jq .

# 健康检查
kubectl get --raw /healthz
kubectl get --raw /livez
kubectl get --raw /readyz

# API Server 标志
kubectl get --raw /debug/flags/v

# Metrics
kubectl get --raw /metrics

认证和授权

# 检查当前用户权限
kubectl get --raw /apis/authorization.k8s.io/v1/selfsubjectaccessreviews \
  -X POST \
  -d '{
    "apiVersion": "authorization.k8s.io/v1",
    "kind": "SelfSubjectAccessReview",
    "spec": {
      "resourceAttributes": {
        "namespace": "default",
        "verb": "get",
        "resource": "pods"
      }
    }
  }'

使用 kubectl proxy 的方式

kubectl --raw 不可用或语法复杂时,可以使用 proxy 方式:

# 启动代理
kubectl proxy --port=8001 &

# 使用 curl 访问
curl http://localhost:8001/api/v1/nodes | jq .

# GET 请求
curl http://localhost:8001/api/v1/namespaces/default/pods

# POST 请求
curl -X POST \
  -H "Content-Type: application/json" \
  -d @pod.json \
  http://localhost:8001/api/v1/namespaces/default/pods

# PUT 请求
curl -X PUT \
  -H "Content-Type: application/json" \
  -d @node.json \
  http://localhost:8001/api/v1/nodes/node-name/status

# DELETE 请求
curl -X DELETE \
  http://localhost:8001/api/v1/namespaces/default/pods/pod-name

# 停止代理
pkill -f "kubectl proxy"

实战案例

案例 1: 绕过 Webhook 修改节点标签

# 问题:Mutating Webhook 拦截标签修改
# 解决:直接通过 API 修改

# 1. 获取节点
kubectl get --raw /api/v1/nodes/node-name > node.json

# 2. 使用 jq 添加标签
jq '.metadata.labels["custom-label"] = "custom-value"' node.json > node-updated.json

# 3. 替换节点
kubectl replace --raw /api/v1/nodes/node-name -f node-updated.json

案例 2: 清理僵尸 Finalizer

# 问题:资源因 finalizer 无法删除
# 解决:直接清空 finalizers

# 1. 获取资源
kubectl get --raw /api/v1/namespaces/stuck-namespace > ns.json

# 2. 清空 finalizers
jq '.spec.finalizers = []' ns.json > ns-clean.json

# 3. 更新
kubectl replace --raw /api/v1/namespaces/stuck-namespace/finalize -f ns-clean.json

案例 3: 批量查询资源状态

#!/bin/bash
# 批量检查节点状态

for node in $(kubectl get nodes -o name | cut -d/ -f2); do
  echo "=== Node: $node ==="
  kubectl get --raw /api/v1/nodes/$node/status | \
    jq -r '.status.conditions[] | select(.type=="Ready") | 
    "Status: \(.status), Reason: \(.reason)"'
done

案例 4: 性能测试

#!/bin/bash
# 测试 API Server 响应时间

echo "Testing API Server performance..."
for i in {1..10}; do
  time kubectl get --raw /api/v1/nodes > /dev/null 2>&1
done

案例 5: 导出所有资源

#!/bin/bash
# 导出命名空间的所有资源

NAMESPACE="default"
OUTPUT_DIR="./k8s-backup"
mkdir -p $OUTPUT_DIR

# 导出 Pods
kubectl get --raw /api/v1/namespaces/$NAMESPACE/pods | \
  jq . > $OUTPUT_DIR/pods.json

# 导出 Services
kubectl get --raw /api/v1/namespaces/$NAMESPACE/services | \
  jq . > $OUTPUT_DIR/services.json

# 导出 Deployments
kubectl get --raw /apis/apps/v1/namespaces/$NAMESPACE/deployments | \
  jq . > $OUTPUT_DIR/deployments.json

echo "Backup completed in $OUTPUT_DIR"

注意事项

1. 权限要求

# 需要相应的 RBAC 权限
# 检查权限
kubectl auth can-i get nodes
kubectl auth can-i update nodes

2. resourceVersion 冲突

# 更新时可能遇到冲突
# Error: the object has been modified; please apply your changes to the latest version

# 解决:重新获取最新版本
kubectl get --raw /api/v1/nodes/node-name > node-latest.json
# 重新修改并更新

3. 数据格式验证

# 使用 jq 验证 JSON 格式
cat resource.json | jq . > /dev/null

# 如果有错误会提示

4. 备份重要资源

# 在修改前务必备份
kubectl get --raw /api/v1/nodes/node-name > node-backup-$(date +%Y%m%d).json

5. 只读操作优先

# 先用 GET 查看,确认无误后再 PUT/PATCH
kubectl get --raw /api/v1/nodes/node-name | jq .

版本兼容性

Kubernetes 1.18+

kubectl get --raw /api/v1/nodes
kubectl create --raw /api/v1/namespaces/default/pods -f pod.json
kubectl replace --raw /api/v1/nodes/node-name -f node.json
kubectl patch --raw /api/v1/nodes/node-name --type=merge -p '{...}'
kubectl delete --raw /api/v1/namespaces/default/pods/pod-name

早期版本或不支持时

# 使用 kubectl proxy
kubectl proxy --port=8001 &
curl http://localhost:8001/api/v1/nodes

调试技巧

1. 查看完整请求

# 增加日志级别
kubectl get --raw /api/v1/nodes -v=8

2. 使用 jq 过滤输出

# 只查看节点名称
kubectl get --raw /api/v1/nodes | jq '.items[].metadata.name'

# 查看 Pod 状态
kubectl get --raw /api/v1/namespaces/default/pods | \
  jq '.items[] | {name: .metadata.name, status: .status.phase}'

3. 格式化时间戳

# 转换时间格式
kubectl get --raw /api/v1/nodes/node-name | \
  jq '.metadata.creationTimestamp | fromdate | strftime("%Y-%m-%d %H:%M:%S")'

总结

kubectl --raw 是 Kubernetes 的”瑞士军刀”,提供了:

直接访问 API – 绕过客户端限制
调试工具 – 排查 kubectl 和 API Server 问题
应急修复 – 处理 Webhook 和控制器导致的问题
性能测试 – 直接测试 API Server
学习工具 – 理解 Kubernetes API 结构

⚠️ 使用场景: 作为最后的调试和修复手段
⚠️ 不推荐: 日常操作应使用标准 kubectl 命令
⚠️ 需谨慎: 直接操作可能破坏资源状态

参考资源

k3s k8s 快速部署轻量节点监控方案 beszel

在逛 Reddit 时看到 这篇帖子 发现 beszel 这个熟悉又陌生的名字。看了一下官网发现还支持 kubernetes 的部署,直接使用 daemonset 就可以在所有节点自动部署 agent ,虽然还需要手动在 hub 添加,但已经很方便,用了一下不错。

首页截图

节点详情页

作为轻量级的 k3s/k8s 集群监控方案确实不错,比 kube-prometheus-stack 这样的庞然大物轻便太多,解决轻量的监控和告警需求。

下面直接贴出 hubagentmanifests

hub

---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: beszel-zgus1-pvc
  namespace: beszel
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: local-zgus1
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: app
  namespace: beszel
  labels:
    app: beszel
spec:
  replicas: 1
  selector:
    matchLabels:
      app: beszel
  template:
    metadata:
      annotations: {}
      labels:
        app: beszel
    spec:
      #nodeSelector:
      #  kubernetes.io/hostname: zgocloud-us1
      containers:
        - name: app
          image: henrygd/beszel:0.11.1
          ports:
            - containerPort: 8090
              name: web
          env:
            - name: TZ
              value: "Asia/Shanghai"
          volumeMounts:
            - name: beszel-data
              mountPath: /beszel_data
      volumes:
        - name: beszel-data
          persistentVolumeClaim:
            claimName: beszel-zgus1-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: beszel
  namespace: beszel
spec:
  selector:
    app: beszel
  ports:
    - name: web
      port: 8090
      targetPort: 8090
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: beszel-ingress
  namespace: beszel
  annotations:
    cert-manager.io/cluster-issuer: "cf-cluster-issuer"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
      - <YOUR DOMAIN>
    secretName: <YOUR DOMAIN TLS SECRET NAME>            
  rules:
  - host: <YOUR DOMAIN>
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: beszel
            port:
              name: web

agent

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: agent
  namespace: beszel
spec:
  selector:
    matchLabels:
      app: agent
  template:
    metadata:
      labels:
        app: agent
    spec:
      hostNetwork: true
      containers:
        - env:
            - name: LISTEN
              value: '45876'
            - name: KEY
              value: 'YOUR-KEY-HERE'
          image: henrygd/beszel-agent:latest
          imagePullPolicy: Always
          name: beszel-agent
          ports:
            - containerPort: 45876
              hostPort: 45876
      restartPolicy: Always
      tolerations:
        - effect: NoSchedule
          key: node-role.kubernetes.io/master
          operator: Exists
        - effect: NoSchedule
          key: node-role.kubernetes.io/control-plane
          operator: Exists
  updateStrategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 100%
    type: RollingUpdate

注意,agent 使用了hostNetwork 网络,实现对宿主机网络的监控并监听 45876 端口,需放通端口后在可以在 hub 加入。如果不使用这个模式,会收集不到网络数据,看到的带宽情况一直是 .

References

k3s-k8s 实现 DevOps 方案横向对比

目前在用 Keel,感觉良好。

主流方案对比

以下是几种可以在 K3s 中实现轻量级 DevOps 解决方案对比:

方案 资源占用 易用性 Web UI 集成能力 配置复杂度 特点
ArgoCD 中等 ★★★★☆ 优秀 原生支持 Git/镜像更新 中等 GitOps 专注,声明式部署
FluxCD ★★★☆☆ 基础(最新版改进) 原生支持 Git/镜像更新 中等 GitOps 专注,自动化程度高
Drone ★★★★☆ 优秀 需配置触发器 轻量级,无需 CRD
Jenkins X ★★☆☆☆ 良好 丰富 功能全面但较重
Tekton 中等 ★★★☆☆ 需安装Dashboard 高度可定制 中高 云原生管道
Keel 极低 ★★★★★ 简单 专注镜像更新 极低 超轻量,专注自动部署

方案详细分析

1. ArgoCD

优势:

  • 优秀的 Web UI,直观展示应用状态
  • GitOps 原生支持,可监控仓库变化
  • 支持镜像更新自动化(通过 Image Updater 插件)
  • 良好的 K8s 集成度,使用 CRD 扩展

劣势:

  • 资源占用相对较高
  • 初期配置有一定学习曲线

资源需求: 至少 1-2 核 CPU,2GB 内存

2. FluxCD

优势:

  • 极轻量级设计,资源消耗小
  • 完全自动化的 GitOps 流程
  • 内置镜像更新自动化
  • 无需持续手动干预

劣势:

  • UI 相对简单(Flux v2 已有改进)
  • 学习曲线略陡

资源需求: 约 0.5 核 CPU,512MB 内存

3. Drone

优势:

  • 极轻量级 CI/CD 系统
  • 简单直观的 Web UI
  • 配置简单(YAML 文件)
  • 与 GitHub 集成良好

劣势:

  • 需要额外配置触发器实现自动化部署
  • 功能不如大型 CI/CD 平台丰富

资源需求: 约 0.5 核 CPU,512MB 内存

4. Keel

优势:

  • 超轻量级,专注于一件事:自动化部署更新后的镜像
  • 配置极其简单(注解或简单 CRD)
  • 支持多种触发方式:Webhook、轮询或 Pub/Sub
  • 几乎零配置即可工作

劣势:

  • 功能单一,仅专注于部署更新
  • UI 非常基础
  • 社区相对小众

资源需求: 约 0.2 核 CPU,256MB 内存

推荐方案

基于需求(轻量级、简单配置、Web UI、自动化部署):

主推方案: Keel + GitHub Actions

  1. GitHub Actions 负责 CI 部分(代码提交触发构建并推送到 Docker Hub)
  2. Keel 负责 CD 部分(检测到新镜像自动更新部署)

优势:

  • 最轻量级组合,资源占用最小
  • 配置极其简单,只需在部署中添加几个注解
  • GitHub Actions 原生集成 GitHub
  • 完全满足您的自动化流程需求

配置示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: your-app
  annotations:
    keel.sh/policy: force  # 强制更新策略
    keel.sh/trigger: poll  # 轮询 Docker Registry
    keel.sh/pollSchedule: "@every 2m"  # 每2分钟检查
spec:
  template:
    spec:
      containers:
        - name: your-container
          image: your-dockerhub/your-image:latest

替代方案: ArgoCD

如果您需要更强大的 UI 和更完整的 GitOps 工作流,推荐使用 ArgoCD,虽然资源消耗稍高,但提供了更全面的功能和更好的可视化体验。

结论

Keel + GitHub Actions 是最轻量且直接的解决方案,几乎零配置即可工作。如果需要更全面的 GitOps 体验和更好的可视化,可以考虑 ArgoCD,它虽然资源消耗稍高,但提供了更完善的功能。

k8s 配置访问私有镜像仓库

harbor 私有仓库、aliyun acr 等同理。

创建凭据

以创建 docker-registry-creds 为例,按需调整名称

kubectl create secret docker-registry docker-registry-creds --docker-server="<私有仓库域名>"
--docker-email=test@test.com 
--docker-username='******' 
--docker-password='******'

# 参数解释
# --docker-server 是私有docker仓库全限定域名(FQDN)
# --docker-username 是机器人账户的username,需要用单引号引起来。
# --docker-password 是机器人账户生成的token,需要用单引号引起来。
# --docker-email 是docker邮箱(非必须)。
# 这样就成功地将集群中的docker凭据设置为名为docker-registry-creds的secret。

使用凭据

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: <私有仓库域名>/kubernetes/nginx:latest 
    ports:
    - containerPort: 80
  imagePullSecrets:
    - name: docker-registry-creds

References

k8s csi-driver-nfs的一个坑

TL;DR

发现 k8s csi 组的社区项目 csi-driver-nfs v4.10v4.11 至少这两个版本存在删除 pv 时会连带将整个根删除的问题。

声明 StorageClass 时虽然支持 subDir ,类似这样:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-aliyun-gz
provisioner: nfs.csi.k8s.io
parameters:
  share: "/csi"
  server: "28364f4a1fa-eok75.cn-guangzhou.nas.aliyuncs.com"
  #server: "172.26.12.20"
  #subDir: "${pvc.metadata.namespace}/${pvc.metadata.name}"
reclaimPolicy: Delete
#volumeBindingMode: WaitForFirstConsumer
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
#  - nolock,tcp,noresvport
  - vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport

但如果类似这样使用 subDir 声明路径,同命名空间下的其他 pvc 删除,会导致整个 subDir 根目录都被删除。目前官方 pr 已经修复,但实测还是有问题,有空再研究一下代码,不知道是不是刻意为之。

回溯 issuer 历史发现是有人提了 bug 发现目录下出现很多空目录,认为需要删除,修复者修复这一问题时错误的将整个根删除。为了规避这一问题,暂时回退到更早的 4.9 版本 csi

helm upgrade --install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system --version v4.  
9.0 -f values.yaml

升级版本要谨慎,新装版本要充分测试,特别是这种涉及数据安全的!

最后发现 sig 组还有一个 nfs-subdir-external-provisioner 可以看一下。

References

k3s 容器 mirror 配置方法

TL; DR

root@tencent-sh1:~# cat /etc/rancher/k3s/registries.yaml 
mirrors:
  "docker.io":
    endpoint:
      - "https://harbor.xxx.me"
    rewrite:
      "^(.*)": "mirror-dockerhub/$1"
  "registry.k8s.io":
    endpoint:
      - "https://harbor.xxx.me"
    rewrite:
      "^(.*)": "mirror-registry-k8s-io/$1"
  "ghcr.io":
    endpoint:
      - "https://harbor.xxx.me"
    rewrite:
      "^(.*)": "mirror-registry-ghcr-io/$1"
  "quay.io":
    endpoint:
      - "https://harbor.xxx.me"
    rewrite:
      "^(.*)": "mirror-registry-quay-io/$1"

以上是我的配置,在 harbor 中镜像以上镜像源,之后这样 配置即可。

如果没有路径,比如使用 registry 镜像,忽略 rewrite 部分即可。

References

wordpress 使用 k8s 部署并使用 nginx ingress 代理无限 302 到 ssl 问题解决

发现容器化之后,wp 网站打开一直尝试 302 到 https 的页面,即使我当前已经是 https 了,经过排查是由于代理提供了 ssl 但 wordpress 不知道,默认会再重定向一次,出现无限 302 。

TL; DR

解决方法很简单,只需在 wp 配置文件 /wp-config.php 中增加这几行即可解决:

define( 'FORCE_SSL_ADMIN', true );
// in some setups HTTP_X_FORWARDED_PROTO might contain 
// a comma-separated list e.g. http,https
// so check for https existence
if( strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false )
    $_SERVER['HTTPS'] = 'on';

方法来源于官网.

References