# Kubernetes 部署 清单在仓库 `kubernetes/` 目录,域名与 IP 已脱敏。 ## 1. 命名空间与密钥 ```bash 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`: ```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. 工作负载 ```bash 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 ```yaml 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: ```yaml 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 ``` ```bash kubectl apply -f kubernetes/ingress.yaml kubectl -n log get ingress ``` ALB 会分配一个地址(如 `alb-xxxx..alb.aliyuncsslbintl.com`),把域名的 CNAME 指过去即可。 > 其他云把 `ingressClassName` 换成对应实现:TKE `qcloud`、CCE `nginx`/`cce`、自建 `nginx`。 ## 6. 数据迁移(从主机/旧实例) JSON DB 是单文件,迁移时先停旧实例,再写入 PVC: ```bash # 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 ./lokistack.json pvc-loader:/data/lokistack.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. 验证 ```bash 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. 升级 ```bash 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: ```bash 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", "services/proxy", "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` 更安全)。