【已在线完成,零数据丢失】
K8s ns=log 资源全部改名:
Deployment opsx-loki → lokistack
Service opsx-loki → lokistack
Service opsx-loki-nodeport → lokistack-nodeport
PVC opsxloki-data → lokistack-data
Secret opsxloki-secrets → lokistack-secrets
Label app: opsx-loki → app: lokistack
Ingress log.astx.club 后端 → lokistack-nodeport:8080
镜像 lghost/opsxloki → lghost/lokistack
docker-compose 卷/容器名同步更名
【保留不变(改了会出事)】
namespace: log
数据文件名 opsxloki.json(代码硬编码)
容器内路径 /app/data
【迁移过程】
1. 备份 PVC 数据(32KB) + Secret 原值 + 旧 yaml → /root/lokistack-migration-backup-20261026-171141
2. scale 旧 Deployment → 0,等 Pod 消失
3. 复制 Secret(JWT sha256 校验一致)
4. 新建 PVC lokistack-data(Bound 30Gi cbs)
5. 临时 Pod 恢复数据(chown 1000:1000,opsxloki.json 保留 0600)
6. apply 新 Deployment → 1/1 Running
7. 修复 Ingress 后端(TKE qcloud 要求 NodePort)→ CLB 恢复 200
8. 删除旧 Deployment / 失效 Service
【验证】
lokistack 1/1,0 重启,镜像 lghost/lokistack:latest(statically linked)
/api/v1/health → {"status":"ok","version":"2.0.0"}
/app/data/opsxloki.json 610B 完整(admin 用户 + 配置)
http://log.astx.club/ → HTTP 200
镜像加固:admin123=0、/app/backend/=0、initial-admin-password.txt 存在
【回滚兜底(建议 7 天后删)】
pvc/opsxloki-data、secret/opsxloki-secrets
【文档】
README 新增「从 opsx-loki 升级」章节:
K8s 七步迁移脚本 + docker-compose 卷迁移命令
6.8 KiB
Kubernetes 部署
清单在仓库 kubernetes/ 目录,域名与 IP 已脱敏。
1. 命名空间与密钥
kubectl create namespace log # 已存在可跳过
kubectl -n log create secret generic lokistack-secrets \
--from-literal=JWT_SECRET="$(openssl rand -hex 32)" \
--from-literal=ENCRYPTION_KEY="$(openssl rand -hex 32)"
ENCRYPTION_KEY必须固定保存:DB 里的 kubeconfig、对象存储 AK/SK、SSH 私钥都用它加密,丢失后无法解密。 从旧实例迁移时,必须使用原实例相同的JWT_SECRET/ENCRYPTION_KEY。
2. 数据卷
kubernetes/pvc.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: lokistack-data
namespace: log
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 30Gi
不写 storageClassName,使用集群默认 StorageClass。没有默认 SC 时按云填写:
| 云 | StorageClass |
|---|---|
| 阿里云 ACK | alicloud-disk-essd |
| AWS EKS | gp3 |
| 腾讯云 TKE | cbs |
| 华为云 CCE | csi-disk |
3. 工作负载
kubectl apply -f kubernetes/namespace.yaml \
-f kubernetes/pvc.yaml \
-f kubernetes/deployment.yaml \
-f kubernetes/service.yaml
关键约束(deployment.yaml 已内置):
replicas: 1:JSON DB 单写者,多副本会互相覆盖。strategy: Recreate:滚动更新会同时挂同一个 RWO PVC。DATA_DIR=/app/data,挂 PVC;runAsUser/fsGroup: 1000。- 镜像
lghost/lokistack:latest,imagePullPolicy: Always。 - 探针:
/api/v1/health。
4. Service
apiVersion: v1
kind: Service
metadata:
name: lokistack
namespace: log
spec:
type: ClusterIP
selector:
app: lokistack
ports:
- name: http
port: 8080
targetPort: http
需要主机 nginx 反代时,额外创建 NodePort(kubernetes/service-nodeport.yaml,30080)。
5. Ingress(ALB)
log.xxx.io 指向 Loki/平台入口,使用阿里云 ALB IngressClass:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: loki.xxx.io
namespace: log
labels:
app: lokistack
spec:
ingressClassName: alb
rules:
- host: log.xxx.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: lokistack
port:
number: 8080
kubectl apply -f kubernetes/ingress.yaml
kubectl -n log get ingress
ALB 会分配一个地址(如 alb-xxxx.<region>.alb.aliyuncsslbintl.com),把域名的 CNAME 指过去即可。
其他云把
ingressClassName换成对应实现:TKEqcloud、CCEnginx/cce、自建nginx。
6. 数据迁移(从主机/旧实例)
JSON DB 是单文件,迁移时先停旧实例,再写入 PVC:
# 1) 缩容到 0,避免应用关机时覆盖
kubectl -n log scale deploy/lokistack --replicas=0
# 2) 临时 Pod 挂 PVC 拷数据(与业务 Pod 同节点,RWO 盘才能挂上)
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Pod
metadata: {name: pvc-loader, namespace: log}
spec:
restartPolicy: Never
securityContext: {runAsUser: 1000, runAsGroup: 1000, fsGroup: 1000}
containers:
- name: loader
image: busybox:1.36
command: ["sleep", "1800"]
volumeMounts: [{name: data, mountPath: /data}]
volumes:
- name: data
persistentVolumeClaim: {claimName: lokistack-data}
EOF
kubectl -n log cp ./opsxloki.json pvc-loader:/data/opsxloki.json
kubectl -n log cp ./known_hosts pvc-loader:/data/known_hosts
kubectl -n log delete pod pvc-loader
kubectl -n log scale deploy/lokistack --replicas=1
不要在业务 Pod 运行时
kubectl cp覆盖 DB:应用退出时会用内存数据回写,覆盖你拷进去的文件。
7. 验证
kubectl -n log get pods,pvc,svc,ingress
kubectl -n log logs deploy/lokistack --tail=20
kubectl -n log exec deploy/lokistack -- wget -qO- http://127.0.0.1:8080/api/v1/health
8. 升级
kubectl -n log set image deploy/lokistack lokistack=lghost/lokistack:v0.2
kubectl -n log rollout status deploy/lokistack
Recreate 会先停旧 Pod 再起新 Pod,期间平台短暂不可用(单写者约束的代价)。
9. Token 接入(长期有效,仅 log 命名空间)
平台支持 ServiceAccount Token 接入。推荐使用长期 Token + 命名空间级 Role:
Token 不过期,但权限只覆盖 log 命名空间,泄露影响面同样仅限于 log,不会波及其他命名空间。
在集群里执行一次(管理员权限),然后取 Token:
kubectl apply -f - <<'EOF'
apiVersion: v1
kind: Namespace
metadata:
name: log
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: lokistack
namespace: log
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: lokistack
namespace: log
rules:
- apiGroups: [""]
resources: ["pods", "pods/log", "services", "configmaps", "secrets", "persistentvolumeclaims", "events", "serviceaccounts"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["apps"]
resources: ["statefulsets", "daemonsets", "deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: lokistack
namespace: log
subjects:
- kind: ServiceAccount
name: lokistack
namespace: log
roleRef:
kind: Role
name: lokistack
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Secret
metadata:
name: lokistack-token
namespace: log
annotations:
kubernetes.io/service-account.name: lokistack
type: kubernetes.io/service-account-token
EOF
kubectl -n log get secret lokistack-token -o jsonpath='{.data.token}' | base64 -d
- 这个 Token 不会自动过期,但只绑定
log命名空间的 Role:能操作该命名空间的 Pod、日志、工作负载、Service、ConfigMap、Secret、PVC,无权访问其他命名空间和集群级资源。 - 平台展示节点数需要
nodes只读权限;命名空间级 Token 没有该权限时,平台会跳过节点信息,不影响集群接入与部署。 - 平台收到 Token 后会转成 kubeconfig,并用
ENCRYPTION_KEY加密存 DB(ENC:前缀),接口不返回明文。 - 轮换:删除 Secret 后重建,旧 Token 立即失效。
- 如果集群策略要求短期凭证,可改用
kubectl -n log create token lokistack --duration=24h定期轮换。 - 上面的 Namespace 由管理员创建,平台 Token 本身没有集群级权限;只有确实需要平台自建命名空间时,才额外授予
namespaces: create/get的 ClusterRole(create 无法按名称限制,预建log更安全)。