Add the Loki query UI, access policies, ingress insights, alert and registry settings, and their backend APIs. Include the related Kubernetes deployment and collector updates required by the integrated application. Validated with go test ./..., npm run build, and npm run lint.
244 lines
5.3 KiB
Go
244 lines
5.3 KiB
Go
// LokiDeployer — k8s-yaml 模式 manifest 生成(StatefulSet/PVC/ConfigMap/Service)。
|
||
package services
|
||
|
||
import (
|
||
"crypto/sha256"
|
||
"encoding/base64"
|
||
"fmt"
|
||
)
|
||
|
||
func (d *LokiDeployer) generateManifest(ns string) string {
|
||
version := d.effectiveVersion()
|
||
image := ResolveImage("docker.io/grafana/loki:" + version)
|
||
replicas := d.deployment.Replicas
|
||
if replicas < 1 {
|
||
replicas = 1
|
||
}
|
||
storageSize := d.deployment.StorageSize
|
||
if storageSize == "" {
|
||
storageSize = "10Gi"
|
||
}
|
||
|
||
cpu := d.deployment.ResourcesCPU
|
||
mem := d.deployment.ResourcesMem
|
||
if cpu == "" {
|
||
cpu = "1000m"
|
||
}
|
||
if mem == "" {
|
||
mem = "1Gi"
|
||
}
|
||
|
||
configData := d.generateLokiConfig()
|
||
secretManifest := ""
|
||
configExpandArg := ""
|
||
storageEnvBlock := ""
|
||
if sc := d.deployment.StorageConfig; sc != nil {
|
||
switch sc.StorageType {
|
||
case "s3", "oss", "minio", "azure":
|
||
secretManifest = fmt.Sprintf(`---
|
||
apiVersion: v1
|
||
kind: Secret
|
||
metadata:
|
||
name: loki-storage-credentials
|
||
namespace: %s
|
||
type: Opaque
|
||
data:
|
||
accessKey: %s
|
||
secretKey: %s`, ns,
|
||
base64.StdEncoding.EncodeToString([]byte(sc.AccessKey)),
|
||
base64.StdEncoding.EncodeToString([]byte(sc.SecretKey)))
|
||
configExpandArg = "\n - -config.expand-env=true"
|
||
storageEnvBlock = `
|
||
env:
|
||
- name: LOKI_STORAGE_ACCESS_KEY
|
||
valueFrom:
|
||
secretKeyRef:
|
||
name: loki-storage-credentials
|
||
key: accessKey
|
||
- name: LOKI_STORAGE_SECRET_KEY
|
||
valueFrom:
|
||
secretKeyRef:
|
||
name: loki-storage-credentials
|
||
key: secretKey`
|
||
}
|
||
}
|
||
// ConfigMap/Secret 更新本身不会重建 Pod。把内容摘要写入 Pod 模板以触发滚动更新。
|
||
configHash := sha256.Sum256([]byte(configData + "\n" + secretManifest))
|
||
|
||
manifest := fmt.Sprintf(`apiVersion: v1
|
||
kind: Namespace
|
||
metadata:
|
||
name: %s
|
||
---
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: loki-config
|
||
namespace: %s
|
||
data:
|
||
loki.yaml: |
|
||
%s
|
||
%s
|
||
---
|
||
apiVersion: v1
|
||
kind: Service
|
||
metadata:
|
||
name: loki
|
||
namespace: %s
|
||
labels:
|
||
app: loki
|
||
spec:
|
||
type: ClusterIP
|
||
ports:
|
||
- name: http
|
||
port: 3100
|
||
targetPort: 3100
|
||
protocol: TCP
|
||
- name: grpc
|
||
port: 9095
|
||
targetPort: 9095
|
||
protocol: TCP
|
||
selector:
|
||
app: loki
|
||
---
|
||
apiVersion: v1
|
||
kind: PersistentVolumeClaim
|
||
metadata:
|
||
name: loki-data
|
||
namespace: %s
|
||
spec:
|
||
accessModes:
|
||
- ReadWriteOnce
|
||
resources:
|
||
requests:
|
||
storage: %s
|
||
---
|
||
apiVersion: apps/v1
|
||
kind: StatefulSet
|
||
metadata:
|
||
name: loki
|
||
namespace: %s
|
||
labels:
|
||
app: loki
|
||
spec:
|
||
serviceName: loki
|
||
replicas: %d
|
||
selector:
|
||
matchLabels:
|
||
app: loki
|
||
template:
|
||
metadata:
|
||
labels:
|
||
app: loki
|
||
annotations:
|
||
checksum/loki-config: "%x"
|
||
spec:
|
||
securityContext:
|
||
runAsUser: 0
|
||
runAsGroup: 0
|
||
fsGroup: 0
|
||
containers:
|
||
- name: loki
|
||
image: grafana/loki:%s
|
||
imagePullPolicy: IfNotPresent
|
||
args:
|
||
- -config.file=/etc/loki/loki.yaml%s%s
|
||
ports:
|
||
- name: http
|
||
containerPort: 3100
|
||
protocol: TCP
|
||
- name: grpc
|
||
containerPort: 9095
|
||
protocol: TCP
|
||
resources:
|
||
limits:
|
||
cpu: "%s"
|
||
memory: "%s"
|
||
requests:
|
||
cpu: "200m"
|
||
memory: "256Mi"
|
||
securityContext:
|
||
allowPrivilegeEscalation: false
|
||
readOnlyRootFilesystem: false
|
||
capabilities:
|
||
drop:
|
||
- ALL
|
||
livenessProbe:
|
||
httpGet:
|
||
path: /ready
|
||
port: http
|
||
initialDelaySeconds: 30
|
||
periodSeconds: 10
|
||
timeoutSeconds: 5
|
||
failureThreshold: 6
|
||
readinessProbe:
|
||
httpGet:
|
||
path: /ready
|
||
port: http
|
||
initialDelaySeconds: 10
|
||
periodSeconds: 5
|
||
timeoutSeconds: 3
|
||
failureThreshold: 3
|
||
startupProbe:
|
||
httpGet:
|
||
path: /ready
|
||
port: http
|
||
initialDelaySeconds: 10
|
||
periodSeconds: 10
|
||
timeoutSeconds: 5
|
||
failureThreshold: 30
|
||
volumeMounts:
|
||
- name: config
|
||
mountPath: /etc/loki
|
||
readOnly: true
|
||
- name: data
|
||
mountPath: /var/lib/loki
|
||
- name: wal
|
||
mountPath: /var/lib/loki/wal
|
||
volumes:
|
||
- name: config
|
||
configMap:
|
||
name: loki-config
|
||
- name: data
|
||
persistentVolumeClaim:
|
||
claimName: loki-data
|
||
- name: wal
|
||
emptyDir: {}
|
||
`, ns, ns, indentYAML(configData, 4), secretManifest, ns, ns, storageSize, ns, replicas, configHash, image, configExpandArg, storageEnvBlock, cpu, mem)
|
||
|
||
// 多副本:追加 Headless Service(clusterIP: None + selector app:loki),
|
||
// 使 memberlist 的 join_members (loki-headless.<ns>.svc.cluster.local) 能解析到所有 Pod IP。
|
||
if replicas >= 2 {
|
||
manifest += fmt.Sprintf(`
|
||
---
|
||
apiVersion: v1
|
||
kind: Service
|
||
metadata:
|
||
name: loki-headless
|
||
namespace: %s
|
||
labels:
|
||
app: loki
|
||
spec:
|
||
clusterIP: None
|
||
ports:
|
||
- name: http
|
||
port: 3100
|
||
targetPort: 3100
|
||
protocol: TCP
|
||
- name: grpc
|
||
port: 9095
|
||
targetPort: 9095
|
||
protocol: TCP
|
||
- name: memberlist
|
||
port: 7946
|
||
targetPort: 7946
|
||
protocol: TCP
|
||
selector:
|
||
app: loki
|
||
`, ns)
|
||
}
|
||
return manifest
|
||
}
|
||
|
||
// ==================== Loki 本地配置生成 (text/template) ====================
|