漂亮,nginx Ingress 已经通了。下一步我们做 new-api 部署前准备,目标是先把 K8s 的运维基础补齐,再验证它能访问你已有的 PostgreSQL 和 Redis。
接下来分 4 步:
1. 安装 metrics-server
2. 创建 new-api namespace
3. 准备 Secret / ConfigMap
4. 用临时 Pod 测试 PostgreSQL 和 Redis 连通性
1. 安装 metrics-server
作用:
metrics-server 用来采集节点和 Pod 的 CPU、内存指标。
安装后才能用 kubectl top nodes / kubectl top pods。
安装:
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.8.1/components.yaml
家庭自建 kubeadm 集群里,metrics-server 经常因为 kubelet 证书问题启动失败。先检查:
kubectl get pods -n kube-system | grep metrics
如果 Running,直接测试:
kubectl top nodes
kubectl top pods -A
如果不正常,执行这个 patch:
kubectl patch deployment metrics-server -n kube-system --type='json' -p='[
{
"op": "add",
"path": "/spec/template/spec/containers/0/args/-",
"value": "--kubelet-insecure-tls"
}
]'
等它重启:
kubectl rollout status deployment metrics-server -n kube-system
kubectl top nodes
下面按 Helm 简单版 来装 PostgreSQL 和 Redis。这个版本适合你当前阶段:先让 new-api 在 K8s 里跑起来,后面再升级数据库 HA、备份和 Operator。
我们会放在同一个 namespace:
new-api
架构:
new-api Pod
-> postgresql.new-api.svc.cluster.local:5432
-> redis-master.new-api.svc.cluster.local:6379
0. 先确认 StorageClass
PostgreSQL 和 Redis 都需要 PVC。先看:
kubectl get storageclass
如果有默认 StorageClass,会看到类似:
local-path (default)
或者其他名字。
如果没有默认 StorageClass,先暂停,把输出发我。因为没有 StorageClass,PostgreSQL/Redis 的 PVC 会一直 Pending。
1. 创建 namespace
如果已经创建过,会提示已存在,没关系。
kubectl create namespace new-api
2. 添加 Bitnami Helm 仓库
Bitnami 官方现在推荐 OCI 方式,不一定需要 helm repo add。我们直接用 OCI:
oci://registry-1.docker.io/bitnamicharts/postgresql
oci://registry-1.docker.io/bitnamicharts/redis
Bitnami PostgreSQL 官方安装示例也是:
helm install my-release oci://registry-1.docker.io/bitnamicharts/postgresql
参考:Bitnami PostgreSQL Helm Chart。
3. 准备密码
建议你先生成几个密码:
openssl rand -base64 24
openssl rand -base64 24
openssl rand -base64 24
分别用于:
PostgreSQL postgres 管理员密码
PostgreSQL newapi 用户密码
Redis 密码
假设你准备:
POSTGRES_ADMIN_PASSWORD=替换成你的管理员密码
POSTGRES_USER_PASSWORD=替换成你的 newapi 用户密码
REDIS_PASSWORD=替换成你的 Redis 密码
后面命令里手动替换。
4. 安装 PostgreSQL
这里创建:
数据库:newapi
用户:newapi
Service:postgresql.new-api.svc.cluster.local
端口:5432
PVC:20Gi
执行:
helm upgrade --install postgresql \
oci://registry-1.docker.io/bitnamicharts/postgresql \
--namespace new-api \
--set auth.postgresPassword='替换成POSTGRES_ADMIN_PASSWORD' \
--set auth.username='newapi' \
--set auth.password='替换成POSTGRES_USER_PASSWORD' \
--set auth.database='newapi' \
--set primary.persistence.enabled=true \
--set primary.persistence.size=20Gi
说明:
auth.username/auth.password:
创建业务用户 newapi。
auth.database:
创建业务数据库 newapi。
primary.persistence.size:
PostgreSQL 数据盘大小。
检查状态:
kubectl get pods -n new-api -o wide
kubectl get pvc -n new-api
kubectl get svc -n new-api
等待 PostgreSQL Running:
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/instance=postgresql \
-n new-api \
--timeout=300s
测试连接:
kubectl run pg-test \
-n new-api \
--rm -it \
--image=postgres:16-alpine \
--restart=Never \
--env PGPASSWORD='替换成POSTGRES_USER_PASSWORD' \
-- psql -h postgresql.new-api.svc.cluster.local -p 5432 -U newapi -d newapi -c 'select 1;'
成功会看到:
?column?
----------
1
5. 安装 Redis
这里先用单实例 Redis:
架构:standalone
Service:redis-master.new-api.svc.cluster.local
端口:6379
PVC:8Gi
执行:
helm upgrade --install redis \
oci://registry-1.docker.io/bitnamicharts/redis \
--namespace new-api \
--set architecture=standalone \
--set auth.enabled=true \
--set auth.password='替换成REDIS_PASSWORD' \
--set master.persistence.enabled=true \
--set master.persistence.size=8Gi
说明:
architecture=standalone:
单实例 Redis,学习阶段够用。
auth.password:
Redis 访问密码。
master.persistence.size:
Redis 数据盘大小。
检查:
kubectl get pods -n new-api -o wide
kubectl get pvc -n new-api
kubectl get svc -n new-api
等待 Redis Running:
kubectl wait --for=condition=ready pod \
-l app.kubernetes.io/instance=redis \
-n new-api \
--timeout=300s
测试连接:
kubectl run redis-test \
-n new-api \
--rm -it \
--image=redis:7-alpine \
--restart=Never \
-- redis-cli -h redis-master.new-api.svc.cluster.local -p 6379 -a '替换成REDIS_PASSWORD' ping
成功会返回:
PONG
6. 创建 new-api 要用的 Secret
先生成两个固定密钥:
openssl rand -hex 32
openssl rand -hex 32
分别作为:
SESSION_SECRET
CRYPTO_SECRET
创建 Secret:
kubectl create secret generic new-api-secret \
-n new-api \
--from-literal=SESSION_SECRET='TV6KasGSCn8lS4h48Jl86rEoQMolzNe5' \
--from-literal=CRYPTO_SECRET='TV6KasGSCn8lS4h48Jl86rEoQMolzNe5' \
--from-literal=SQL_DSN='postgresql://newapi:TV6KasGSCn8lS4h48Jl86rEoQMolzNe5@postgresql.new-api.svc.cluster.local:5432/newapi' \
--from-literal=REDIS_CONN_STRING='redis://:TV6KasGSCn8lS4h48Jl86rEoQMolzNe5@redis-master.new-api.svc.cluster.local:6379/0' \
--dry-run=client -o yaml | kubectl apply -f -
注意:SQL_DSN 里的密码要和你安装 PostgreSQL 时的:
auth.password
一致。
7. 创建 new-api 普通配置 ConfigMap
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: new-api-config
namespace: new-api
data:
TZ: "Asia/Shanghai"
STREAMING_TIMEOUT: "300"
MAX_REQUEST_BODY_MB: "32"
ERROR_LOG_ENABLED: "true"
EOF
检查:
kubectl get cm,secret -n new-api
8. 当前阶段完成标准
执行:
kubectl get pods -n new-api -o wide
kubectl get pvc -n new-api
kubectl get svc -n new-api
你应该看到:
postgresql-0 Running
redis-master-0 Running
PVC Bound
Service postgresql
Service redis-master
连接测试:
select 1 成功
Redis PONG 成功
到这里,new-api 的数据库和缓存就准备好了。
9. 常用查看命令
PostgreSQL 日志:
kubectl logs -n new-api statefulset/postgresql
Redis 日志:
kubectl logs -n new-api statefulset/redis-master
查看 Helm release:
helm list -n new-api
查看 PostgreSQL values:
helm get values postgresql -n new-api
查看 Redis values:
helm get values redis -n new-api
下一步我们就可以写:
new-api Deployment
new-api Service
new-api Ingress
先 1 副本跑通,然后再改 2-3 副本验证多副本。
我看了你的博客,整理得挺清楚。现在下一步就是:部署 new-api 本体,也就是创建:
Deployment
Service
Ingress
不过先改一个小点:new-api 官方文档里 PostgreSQL 的 SQL_DSN 推荐格式是:
postgresql://user:password@host:5432/database
你博客里现在写的是:
host=postgresql.new-api.svc.cluster.local port=5432 ...
这个格式 PostgreSQL 驱动有时也能识别,但为了贴近 new-api 官方示例,建议改成 URL 格式。官方文档也写了 SQL_DSN、REDIS_CONN_STRING、SESSION_SECRET、CRYPTO_SECRET 这些变量;镜像用的是 calciumion/new-api:latest。参考:New API 环境变量文档、new-api GitHub README。
假设你的密码变量还在当前 shell 里:
echo $POSTGRES_USER_PASSWORD
echo $REDIS_PASSWORD
如果没有,就重新 export 一次。
一、更新 Secret
kubectl create secret generic new-api-secret \
-n new-api \
--from-literal=SESSION_SECRET='替换成你的SESSION_SECRET' \
--from-literal=CRYPTO_SECRET='替换成你的CRYPTO_SECRET' \
--from-literal=SQL_DSN='postgresql://newapi:替换成POSTGRES_USER_PASSWORD@postgresql.new-api.svc.cluster.local:5432/newapi' \
--from-literal=REDIS_CONN_STRING='redis://:替换成REDIS_PASSWORD@redis-master.new-api.svc.cluster.local:6379/0' \
--dry-run=client -o yaml | kubectl apply -f -
注意:如果密码里有 @、:、/、#、?、& 这类字符,URL 里可能需要转义。学习阶段建议密码先用字母数字下划线,少踩坑。
二、创建 new-api 的 PVC
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: new-api-data
namespace: new-api
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 5Gi
EOF
三、部署 new-api
cat <<'EOF' | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: new-api
namespace: new-api
spec:
replicas: 1
selector:
matchLabels:
app: new-api
template:
metadata:
labels:
app: new-api
spec:
containers:
- name: new-api
image: calciumion/new-api:latest
imagePullPolicy: IfNotPresent
args:
- "--log-dir"
- "/app/logs"
ports:
- containerPort: 3000
envFrom:
- configMapRef:
name: new-api-config
- secretRef:
name: new-api-secret
env:
- name: NODE_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
volumeMounts:
- name: data
mountPath: /data
- name: logs
mountPath: /app/logs
readinessProbe:
httpGet:
path: /api/status
port: 3000
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 6
livenessProbe:
httpGet:
path: /api/status
port: 3000
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
volumes:
- name: data
persistentVolumeClaim:
claimName: new-api-data
- name: logs
emptyDir: {}
EOF
四、创建 Service
cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: new-api
namespace: new-api
spec:
type: ClusterIP
selector:
app: new-api
ports:
- name: http
port: 3000
targetPort: 3000
EOF
五、创建 Ingress
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: new-api
namespace: new-api
spec:
ingressClassName: nginx
rules:
- host: k8s-ai.jihw.top
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: new-api
port:
number: 3000
EOF
六、检查状态
kubectl get pods -n new-api -o wide
kubectl get svc -n new-api
kubectl get ingress -n new-api
kubectl logs -n new-api deploy/new-api --tail=100
集群内测试:
如果你的 MetalLB 外部访问还是有问题,就先用 NodePort 访问 ingress-nginx:
curl -I -H "Host: k8s-ai.jihw.top" http://192.168.3.214:32658
浏览器访问的话,Win11 hosts 暂时写:
192.168.3.214 k8s-ai.jihw.top
然后打开:
http://k8s-ai.jihw.top:32658
等单副本访问成功、初始化后台成功之后,再考虑扩成 2 副本。现在先别急着多副本,先把 new-api 跑起来。
是的,要优化成:域名绑定一个会漂移的业务 VIP,而不是绑定某一台节点 IP。
推荐架构:
new-api.k8s.local
↓
192.168.3.231 业务 VIP,由 keepalived 漂移
↓
HAProxy
↓
ingress-nginx NodePort
192.168.3.214:32658
192.168.3.215:32658
192.168.3.216:32658
以后 Win11 hosts 只写:
192.168.3.231 new-api.k8s.local
访问:
http://new-api.k8s.local
即使 192.168.3.214 关闭,VIP 漂移到其他节点,也还能访问。
一、先确认 NodePort
在任意 master 执行:
kubectl get svc -n ingress-nginx ingress-nginx-controller
确认类似:
80:32658/TCP
443:31847/TCP
下面我按你现在的端口 32658、31847 写。
二、三个节点都配置 HAProxy
在 192.168.3.214、192.168.3.215、192.168.3.216 都执行:
sudo sysctl -w net.ipv4.ip_nonlocal_bind=1
echo 'net.ipv4.ip_nonlocal_bind=1' | sudo tee /etc/sysctl.d/99-haproxy-nonlocal-bind.conf
sudo sysctl --system

在文件末尾追加:
sudo tee -a /etc/haproxy/haproxy.cfg >/dev/null <<'EOF'
frontend ingress_http_vip
bind 192.168.3.231:80
mode tcp
option tcplog
default_backend ingress_http_nodeport
backend ingress_http_nodeport
mode tcp
balance roundrobin
option tcp-check
server k8s-master1 192.168.3.214:32467 check
server k8s-master2 192.168.3.215:32467 check
server k8s-master3 192.168.3.216:32467 check
frontend ingress_https_vip
bind 192.168.3.231:443
mode tcp
option tcplog
default_backend ingress_https_nodeport
backend ingress_https_nodeport
mode tcp
balance roundrobin
option tcp-check
server k8s-master1 192.168.3.214:32419 check
server k8s-master2 192.168.3.215:32419 check
server k8s-master3 192.168.3.216:32419 check
EOF
检查并重载:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy
三、三个节点都配置 keepalived 业务 VIP
先查看网卡名:
ip route get 192.168.3.1
你会看到类似:
dev ens33
记住这个网卡名,下面假设是 ens33,如果你的不是,就替换。
编辑:
不要删原来的 API VIP 配置,在末尾追加一个新的 vrrp_instance。
k8s-master1 / 192.168.3.214:
sudo tee -a /etc/keepalived/keepalived.conf >/dev/null <<'EOF'
vrrp_instance VI_INGRESS {
state MASTER
interface ens33
virtual_router_id 52
priority 120
advert_int 1
authentication {
auth_type PASS
auth_pass ingressvip
}
virtual_ipaddress {
192.168.3.231
}
}
EOF
k8s-master2 / 192.168.3.215:
sudo tee -a /etc/keepalived/keepalived.conf >/dev/null <<'EOF'
vrrp_instance VI_INGRESS {
state BACKUP
interface ens33
virtual_router_id 52
priority 110
advert_int 1
authentication {
auth_type PASS
auth_pass ingressvip
}
virtual_ipaddress {
192.168.3.231
}
}
EOF
k8s-master3 / 192.168.3.216:
sudo tee -a /etc/keepalived/keepalived.conf >/dev/null <<'EOF'
vrrp_instance VI_INGRESS {
state BACKUP
interface ens33
virtual_router_id 52
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass ingressvip
}
virtual_ipaddress {
192.168.3.231
}
}
EOF
然后三个节点都执行:
sudo systemctl restart keepalived
sudo systemctl status keepalived --no-pager
四、测试 VIP
在任意节点执行:
ip a | grep 192.168.3.231
正常只会有一台机器持有这个 VIP。
在 Win11 hosts 里改成:
192.168.3.231 k8s-ai.jihw.top
192.168.3.231 longhorn.k8s.local
然后访问:
http://k8s-ai.jihw.top/setup
测试端口:
Test-NetConnection 192.168.3.231 -Port 80
高可用测试:
# 关掉当前持有 192.168.3.231 的那台节点
sudo systemctl stop keepalived
然后在另一台节点看:
ip a | grep 192.168.3.231
如果 VIP 漂移成功,Win11 继续访问:
http://new-api.k8s.local
这套方案比你现在的 192.168.3.214:32658 好很多:域名绑定的是业务 VIP,后端任意一台节点挂掉,HAProxy 会自动跳过;当前持有 VIP 的节点挂掉,keepalived 会把入口漂移到另一台。
自定义域名证书
可以,下一步分两段做:先把 HTTP 域名访问跑通,再做 Let’s Encrypt HTTPS 证书。
先确认你现在的入口链路是:
k8s-ai.jihw.top -> 192.168.3.231 -> HAProxy -> ingress-nginx NodePort -> new-api
一、先改 new-api Ingress 域名
在 master 节点执行:
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: new-api
namespace: new-api
spec:
ingressClassName: nginx
rules:
- host: k8s-ai.jihw.top
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: new-api
port:
number: 3000
EOF
检查:
kubectl get ingress -n new-api
然后在 Win11 测试:
Test-NetConnection k8s-ai.jihw.top -Port 80
浏览器访问:
http://k8s-ai.jihw.top/setup
如果能打开,说明域名入口已经正常。
二、安装 cert-manager
如果你还没装 cert-manager,执行:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.20.2/cert-manager.yaml
等待启动:
kubectl get pods -n cert-manager
三个 Pod 都 Running 后再继续。
三、安装阿里云 DNS webhook
helm repo add cert-manager-alidns-webhook https://devmachine-fr.github.io/cert-manager-alidns-webhook
helm repo update
helm upgrade --install alidns-webhook \
cert-manager-alidns-webhook/alidns-webhook \
-n cert-manager \
--set groupName=jihw.top
检查:
kubectl get pods -n cert-manager
四、创建阿里云 DNS AccessKey Secret
建议在阿里云 RAM 创建专用 AccessKey,先给 AliyunDNSFullAccess,后面熟悉后再收窄权限。
kubectl create secret generic alidns-secret \
-n cert-manager \
--from-literal=access-key-id='你的AccessKeyId' \
--from-literal=access-key-secret='你的AccessKeySecret'
注意这里放在 cert-manager 命名空间。
五、创建 Let’s Encrypt Prod ClusterIssuer
这里直接使用正式证书。注意两点:
1. email 必须换成真实邮箱,不能写“你的邮箱”这种中文占位。
2. 这里安装的是 devmachine-fr/cert-manager-alidns-webhook 0.3.1,
webhook 配置字段要用 accessTokenSecretRef / secretKeySecretRef。
如果字段写成 accessKeyIdRef / accessKeySecretRef,Challenge 会报类似错误:
failed to load secret "cert-manager/": resource name may not be empty
创建正式证书 Issuer:
cat <<'EOF' | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-alidns-prod
spec:
acme:
email: 2455191080@qq.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-alidns-prod-account-key
solvers:
- dns01:
webhook:
groupName: jihw.top
solverName: alidns-solver
config:
regionId: cn-hangzhou
accessTokenSecretRef:
name: alidns-secret
key: access-key-id
secretKeySecretRef:
name: alidns-secret
key: access-key-secret
EOF
检查 Issuer:
kubectl get clusterissuer letsencrypt-alidns-prod
应该看到:
READY True
如果不是 True,先看原因:
kubectl describe clusterissuer letsencrypt-alidns-prod
kubectl -n cert-manager logs deploy/cert-manager --tail=100
六、给 Ingress 加 TLS 并申请证书
下面这个 Ingress 会自动创建名为 k8s-ai-jihw-top-tls 的 Certificate,并把证书保存到同名 Secret。
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: new-api
namespace: new-api
annotations:
cert-manager.io/cluster-issuer: letsencrypt-alidns-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- k8s-ai.jihw.top
secretName: k8s-ai-jihw-top-tls
rules:
- host: k8s-ai.jihw.top
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: new-api
port:
number: 3000
EOF
查看申请状态:
kubectl get certificate -n new-api
kubectl get certificaterequest,order,challenge -n new-api
kubectl describe certificate k8s-ai-jihw-top-tls -n new-api
正常流程大概是:
CertificateRequest Approved=True
Order pending -> valid
Challenge pending -> valid
Certificate Ready=True
成功后会看到 TLS Secret:
kubectl get secret k8s-ai-jihw-top-tls -n new-api
也可以看证书有效期:
kubectl describe certificate k8s-ai-jihw-top-tls -n new-api | grep -E "Not Before|Not After|Renewal Time|Ready"
七、测试 HTTPS
Test-NetConnection k8s-ai.jihw.top -Port 443
浏览器访问:
https://k8s-ai.jihw.top
再看状态:
kubectl get certificate -n new-api
kubectl describe certificate k8s-ai-jihw-top-tls -n new-api
八、以后申请其他域名证书
以后只要 cert-manager、alidns-webhook、alidns-secret、letsencrypt-alidns-prod 都已经存在,就不需要重复安装。只需要给新的服务写 Ingress。
假设要给 api.example.com 申请证书,命名空间是 demo,服务名是 demo-api,端口是 3000:
cat <<'EOF' | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: demo-api
namespace: demo
annotations:
cert-manager.io/cluster-issuer: letsencrypt-alidns-prod
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: api-example-com-tls
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: demo-api
port:
number: 3000
EOF
然后查对应命名空间:
kubectl get certificate -n demo
kubectl get certificaterequest,order,challenge -n demo
kubectl describe certificate api-example-com-tls -n demo
九、常见问题排查
如果 Certificate 一直是 False,先看这几个:
kubectl get clusterissuer letsencrypt-alidns-prod
kubectl describe clusterissuer letsencrypt-alidns-prod
kubectl get certificate -A
kubectl get certificaterequest,order,challenge -A
kubectl describe challenge -n new-api
kubectl -n cert-manager logs deploy/cert-manager --tail=200
kubectl -n cert-manager logs deploy/alidns-webhook --tail=200
如果看到:
Referenced issuer does not have a Ready status condition
说明 ClusterIssuer 还没 Ready,常见原因是邮箱写错,尤其不能写中文占位。
如果看到:
failed to load secret "cert-manager/": resource name may not be empty
说明 alidns webhook 字段名写错了。当前这个 webhook 要写:
accessTokenSecretRef:
name: alidns-secret
key: access-key-id
secretKeySecretRef:
name: alidns-secret
key: access-key-secret
如果改过 ClusterIssuer 后,旧的 Challenge 一直卡在删除中,可以清理旧资源再让 cert-manager 重建:
kubectl -n new-api delete challenge --all --wait=false
kubectl -n new-api delete order --all --wait=false
kubectl -n new-api delete certificaterequest --all --wait=false
如果某个旧 Challenge 有 finalizer 卡住,而且确认它没有成功写入 DNS 记录,可以移除 finalizer:
kubectl -n new-api patch challenge <challenge-name> \
--type=json \
-p='[{"op":"remove","path":"/metadata/finalizers"}]'
DNS-01 成功写入后,可以用下面命令看 TXT 记录传播:
dig +short TXT _acme-challenge.k8s-ai.jihw.top @223.5.5.5
dig +short TXT _acme-challenge.k8s-ai.jihw.top @8.8.8.8
后续申请其他域名时,优先复用 letsencrypt-alidns-prod,只需要新增或修改对应服务的 Ingress。