叩丁狼k8s-运维管理

叩丁狼k8s-运维管理

文章目录

一:Helm包管理器

1:什么是Helm

Helm 是 Kubernetes 生态中最主流的包管理器,被称为 K8s 的 "apt/yum",核心是用 Chart 标准化打包、部署、升级与管理复杂应用,大幅降低多资源、多环境的运维复杂度

Helm 管理名为 chart 的 Kubernetes 包的工具。

Helm 可以做以下的事情:

  • 从头开始创建新的 chart
  • 将 chart 打包成归档(tgz)文件
  • 与存储 chart 的仓库进行交互
  • 在现有的 Kubernetes 集群中安装和卸载 chart
  • 管理与 Helm 一起安装的 chart 的发布周期

对于Helm,有三个重要的概念:

  1. chart 创建Kubernetes应用程序所必需的一组信息。
  2. config 包含了可以合并到打包的chart中的配置信息,用于创建一个可发布的对象。
  3. release 是一个与特定配置相结合的chart的运行实例。

2:核心定位和价值

Helm 解决了 Kubernetes 原生部署的三大痛点:

  • 配置碎片化:将 Deployment、Service、ConfigMap 等数十个 YAML 整合成一个可复用包。
  • 环境适配难:一套模板 + 多套 Values,轻松适配开发 / 测试 / 生产。
  • 版本不可控:提供原子化升级、回滚与完整发布历史。

它是 CNCF 毕业项目,当前主流为 Helm 3(已移除服务端 Tiller,纯客户端架构)。

3:核心概念

3.1:chart

Helm 的 "安装包",是一个标准化目录结构,包含应用的所有资源定义、模板、配置与元数据

python 复制代码
# 一个典型的目录结构是:
my-chart/
├── Chart.yaml          # 元数据(名称、版本、描述等)
├── values.yaml         # 默认配置(可被覆盖)
├── charts/             # 依赖的子 Chart
├── templates/           # Go 模板文件(生成最终 YAML)
│   ├── deployment.yaml
│   ├── service.yaml
│   └── _helpers.tpl     # 可复用模板函数
└── README.md           # 使用说明
3.2:release

Chart 部署到集群后的唯一运行实例,一个 Chart 可部署为多个 Release

每个 Release 有独立版本、配置与生命周期(安装 / 升级 / 回滚 / 卸载)。

3.3:repository

存储与分发 Chart 的地方,类似 Docker Hub 或 Maven 仓库

  • 公共仓库:Artifact Hub(官方)、Bitnami、Helm Hub 等。
  • 私有仓库:ChartMuseum、Harbor、JFrog Artifactory 等(企业内部使用)。
3.4:values

values.yaml 是 Chart 的默认配置

部署时可通过 --set 或自定义 values-prod.yaml 覆盖,实现 "一次打包、多环境复用"。

命令行 --set > 自定义 Values 文件 > Chart 默认 Values。

3.5:templates

基于 Go 模板语言,结合 Values 动态渲染生成 Kubernetes YAML 清单。

支持条件判断、循环、变量、函数与模板复用,灵活适配不同场景。

4:工作流程和mysql测试

(有种docker 镜像和容器的感觉)

mysql为例测试一下:

bash 复制代码
# ----- 首先是安装和仓库的指定 ---------
# 安装 Helm(以 macOS 为例)
brew install helm

# 添加公共仓库(如 Bitnami)
helm repo list
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add aliyun https://apphub.aliyuncs.com/stable
helm repo add azure http://mirror.azure.cn/kubernetes/charts

# 更新仓库索引
helm repo update

# 搜索 Chart
helm search repo mysql
helm show readme bitnami/mysql


# ------- 安装和管理release ---------
# 安装(指定名称、版本、自定义 Values)
helm install my-mysql bitnami/mysql --version 9.10.1 -f values-prod.yaml

# 查看所有 Release
helm list

# 查看 Release 详情与历史
helm status my-mysql
helm history my-mysql

# 升级(修改配置/版本)
helm upgrade my-mysql bitnami/mysql --set auth.rootPassword=newpass

# 回滚到版本 1
helm rollback my-mysql 1

# 卸载(保留历史)
helm uninstall my-mysql --keep-history

chart的开发和调试

shell 复制代码
# 创建新 Chart
helm create my-chart

# 本地渲染模板(不实际部署)
helm template my-chart ./my-chart

# 调试安装(预览最终 YAML)
helm install --dry-run --debug my-chart ./my-chart

# 打包 Chart
helm package ./my-chart

5:原生对比和优势

简化部署与复用

  • 一键部署复杂应用(如 PostgreSQL、Redis 集群、ELK),无需手写大量 YAML。
  • Chart 可在团队 / 社区共享,避免重复造轮子Helm。

灵活配置与多环境适配

  • 一套模板,通过不同 Values 文件适配开发、测试、生产环境。
  • 支持条件渲染、资源动态调整(副本数、资源限制、镜像版本等)。

强大的版本管理

  • 原子升级:升级失败自动回滚,保证集群一致性。
  • 发布历史helm history 查看所有版本,helm rollback 一键回滚。
  • 依赖管理:自动解析并安装子 Chart,支持层级依赖与条件激活。

安全与生态

  • 无 Tiller,权限更可控,支持 Chart 签名验证。
  • 公共仓库有数千个官方 / 社区维护的 Chart,覆盖数据库、中间件、监控、CI/CD 等全场景。
方式 优点 缺点
原生yaml 简单、直接、无依赖 配置零散、多环境难管理、无版本控制、易出错
Helm Chart 标准化、可复用、多环境适配、版本 / 回滚、依赖管理 需学习 Chart 与 Go 模板

6:实际应用部署示例

假设想要为一个 Spring Boot 3(Java 17)应用编写完整的 Helm Chart,该应用依赖 MySQL 8 和 Redis 7,并且已经准备好了应用的 JAR 包但还未部署到服务器

6.1:部署思路

创建基础 Chart 骨架:使用 Helm 命令生成标准目录结构,作为开发起点。

定制核心模板文件

  • Deployment:配置 Spring Boot 应用的容器(Java 17 运行时、JAR 包挂载 / 拉取、JVM 参数)。
  • Service:暴露应用端口,供内部 / 外部访问。
  • ConfigMap:存储应用配置(如数据库、Redis 连接信息)。
  • Secret:存储敏感信息(如 MySQL 密码、Redis 密码)。

管理依赖服务:通过 Helm 依赖集成官方的 MySQL 和 Redis Chart,统一部署。

配置 Values.yaml:定义可配置参数(端口、资源限制、依赖服务配置等)。

测试与部署:本地调试模板、安装 Chart、验证应用运行。

6.2:环境准备

已安装 Helm 3(helm version 验证)。

已配置 K8s 集群访问(kubectl get nodes 验证)。

应用 JAR 包:假设文件名为 my-app-1.0.0.jar

6.3:分步实现chart

1️⃣ 创建chart骨架

shell 复制代码
# 创建 Chart
helm create my-app

# 进入 Chart 目录(后续操作均在此目录下)
cd my-app

此时可以看见helm已经帮助我们创建chart骨架了

bash 复制代码
my-app/
├── Chart.yaml          # Chart 元数据
├── values.yaml         # 核心配置文件
├── charts/             # 依赖的 MySQL/Redis Chart 目录
├── templates/          # 模板文件目录
│   ├── deployment.yaml # 应用部署模板(核心)
│   ├── service.yaml    # 服务暴露模板
│   ├── _helpers.tpl    # 辅助模板函数
│   └── NOTES.txt       # 部署后提示信息
└── .helmignore         # 忽略文件配置

2️⃣ 修改 Chart.yaml(元数据与依赖)

主要是添加应用信息和mysql/redis依赖信息:

yaml 复制代码
apiVersion: v2
name: my-app
description: A Helm chart for Spring Boot 3 (Java 17) app with MySQL 8 and Redis 7
type: application
version: 1.0.0          # Chart 版本(SemVer 2.0)
appVersion: "1.0.0"     # 应用版本

# 添加 MySQL 和 Redis 依赖(从 Bitnami 仓库拉取官方 Chart)
dependencies:
  - name: mysql
    version: 9.16.0     # 适配 MySQL 8 的版本
    repository: https://charts.bitnami.com/bitnami
    condition: mysql.enabled  # 可通过 values 控制是否部署 MySQL
  - name: redis
    version: 17.12.0    # 适配 Redis 7 的版本
    repository: https://charts.bitnami.com/bitnami
    condition: redis.enabled  # 可通过 values 控制是否部署 Redis

添加依赖后,执行以下命令拉取依赖的 Chart 到 charts/ 目录:

shell 复制代码
helm dependency update

3️⃣ 定制 Values.yaml(核心配置)

替换 values.yaml 内容,适配 Spring Boot + MySQL + Redis 配置,关键参数均可自定义

yaml 复制代码
# -------------------------- 应用基础配置 --------------------------
replicaCount: 1  # 应用副本数
image:
  # 方式1:使用 Java 17 基础镜像(后续挂载本地 JAR 包)
  repository: eclipse-temurin
  tag: 17-jre-alpine  # 轻量的 Java 17 运行时
  pullPolicy: IfNotPresent
  # 方式2:若已将 JAR 包打包到自定义镜像,替换为你的镜像地址
  # repository: your-registry/my-app
  # tag: 1.0.0

# JAR 包相关配置(方式1用)
jar:
  name: my-app-1.0.0.jar  # 你的 JAR 包名称
  mountPath: /app         # JAR 包挂载到容器的路径

# 应用端口(对应 Spring Boot 的 server.port)
service:
  type: ClusterIP  # 集群内访问,若需外部访问改为 NodePort/LoadBalancer
  port: 8080
  targetPort: 8080

# JVM 参数(根据应用调整)
jvm:
  args: "-Xms512m -Xmx1024m -Dspring.profiles.active=prod"

# 资源限制
resources:
  limits:
    cpu: 1000m
    memory: 1024Mi
  requests:
    cpu: 500m
    memory: 512Mi

# -------------------------- MySQL 配置 --------------------------
mysql:
  enabled: true  # 是否部署 MySQL
  auth:
    rootPassword: MyApp@123456  # root 密码(建议部署时通过 --set 覆盖)
    database: my_app_db         # 应用使用的数据库名
    username: my_app_user       # 应用访问数据库的用户名
    password: MyAppDB@123456    # 应用数据库密码
  primary:
    service:
      port: 3306
    resources:
      limits:
        cpu: 500m
        memory: 512Mi
      requests:
        cpu: 200m
        memory: 256Mi

# -------------------------- Redis 配置 --------------------------
redis:
  enabled: true  # 是否部署 Redis
  auth:
    enabled: true
    password: MyAppRedis@123456  # Redis 密码
  master:
    service:
      port: 6379
    resources:
      limits:
        cpu: 200m
        memory: 256Mi
      requests:
        cpu: 100m
        memory: 128Mi

# -------------------------- 应用配置(注入到 ConfigMap) --------------------------
app:
  config:
    spring:
      datasource:
        url: jdbc:mysql://my-app-mysql:3306/my_app_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
        username: my_app_user
        driver-class-name: com.mysql.cj.jdbc.Driver
      redis:
        host: my-app-redis-master
        port: 6379
        password: MyAppRedis@123456

4️⃣ 修改模板文件

修改templates/deployment.yaml(核心)

替换默认的 deployment.yaml,适配 Spring Boot 应用启动、JAR 包挂载、配置注入:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "my-app.fullname" . }}
  labels:
    {{- include "my-app.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "my-app.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "my-app.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          # Spring Boot 启动命令(挂载 JAR 包方式)
          command: ["java"]
          args:
            - {{ .Values.jvm.args }}
            - "-jar"
            - "{{ .Values.jar.mountPath }}/{{ .Values.jar.name }}"
          # 端口映射
          ports:
            - name: http
              containerPort: {{ .Values.service.targetPort }}
              protocol: TCP
          # 环境变量(从 ConfigMap/Secret 注入配置)
          env:
            # 数据库密码(从 Secret 注入)
            - name: SPRING_DATASOURCE_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: {{ include "my-app.fullname" . }}-mysql
                  key: password
            # Redis 密码(从 Secret 注入)
            - name: SPRING_REDIS_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: {{ include "my-app.fullname" . }}-redis
                  key: password
          # 挂载 ConfigMap(应用配置)
          volumeMounts:
            - name: app-config
              mountPath: /app/config
              readOnly: true
            # 挂载本地 JAR 包(方式1:需先将 JAR 包上传到服务器并创建 PV/PVC)
            - name: jar-volume
              mountPath: {{ .Values.jar.mountPath }}
          # 健康检查(Spring Boot Actuator,需应用开启)
          livenessProbe:
            httpGet:
              path: /actuator/health/liveness
              port: http
            initialDelaySeconds: 60
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /actuator/health/readiness
              port: http
            initialDelaySeconds: 30
            periodSeconds: 5
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
      # 定义卷
      volumes:
        # 应用配置卷(ConfigMap)
        - name: app-config
          configMap:
            name: {{ include "my-app.fullname" . }}-config
        # JAR 包卷(方式1:需提前创建 PVC,名称为 jar-pvc)
        - name: jar-volume
          persistentVolumeClaim:
            claimName: jar-pvc

创建templates/configmap.yaml(应用配置)

创建 templates/configmap.yaml,存储非敏感的应用配置(注入到 Spring Boot):

yaml 复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "my-app.fullname" . }}-config
  labels:
    {{- include "my-app.labels" . | nindent 4 }}
data:
  application-prod.yml: |
    server:
      port: {{ .Values.service.targetPort }}
    spring:
      datasource:
        url: {{ .Values.app.config.spring.datasource.url }}
        username: {{ .Values.app.config.spring.datasource.username }}
        driver-class-name: {{ .Values.app.config.spring.datasource.driver-class-name }}
      redis:
        host: {{ .Values.app.config.spring.redis.host }}
        port: {{ .Values.app.config.spring.redis.port }}

创建templates/secret.yaml(敏感信息)

创建 templates/secret.yaml,存储 MySQL/Redis 密码等敏感信息:

yaml 复制代码
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "my-app.fullname" . }}-mysql
  labels:
    {{- include "my-app.labels" . | nindent 4 }}
type: Opaque
data:
  password: {{ .Values.mysql.auth.password | b64enc }}  # Base64 加密

---
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "my-app.fullname" . }}-redis
  labels:
    {{- include "my-app.labels" . | nindent 4 }}
type: Opaque
data:
  password: {{ .Values.redis.auth.password | b64enc }}  # Base64 加密

修改service.yaml

保留默认的 service.yaml,仅确认端口配置:

yaml 复制代码
apiVersion: v1
kind: Service
metadata:
  name: {{ include "my-app.fullname" . }}
  labels:
    {{- include "my-app.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.targetPort }}
      protocol: TCP
      name: http
  selector:
    {{- include "my-app.selectorLabels" . | nindent 4 }}

5️⃣ jar包的处理

方式 1:本地挂载 JAR 包(适合测试 / 临时部署)

在 K8s 集群节点上创建目录,上传 JAR 包

shell 复制代码
# 节点上创建目录
mkdir -p /data/my-app/jar
# 上传 JAR 包到该目录(如用 scp)
scp my-app-1.0.0.jar root@node-ip:/data/my-app/jar/

创建 PV/PVC(持久卷 / 持久卷声明),供 Deployment 挂载:

yaml 复制代码
apiVersion: v1
kind: PersistentVolume # 指定成为PV持久卷
metadata:
  name: jar-pv
spec:
  capacity:
    storage: 1Gi  # 指定存储
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data/my-app/jar  # 节点上的目录
  storageClassName: manual
---
apiVersion: v1
kind: PersistentVolumeClaim  # 指定pvc
metadata:
  name: jar-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: manual
bash 复制代码
kubectl apply -f jar-pv.yaml

方式 2:打包 JAR 到自定义镜像(推荐生产环境)

创建dockerfile,和JAR同目录

dockerfile 复制代码
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY my-app-1.0.0.jar /app/
EXPOSE 8080
CMD ["java", "-Xms512m", "-Xmx1024m", "-jar", "my-app-1.0.0.jar"]

构建并推送镜像到私有仓库

bash 复制代码
# 构建镜像
docker build -t your-registry/my-app:1.0.0 .
# 推送镜像
docker push your-registry/my-app:1.0.0

修改 values.yaml 中的 image 配置,指向自定义镜像(注释掉方式 1 的 JAR 挂载相关配置)。

6️⃣ 测试与部署 Chart

如果是本地的debug测试

bash 复制代码
# 渲染模板,查看最终生成的 YAML(不实际部署)
helm template my-app ./my-app --debug

# 模拟安装(检查配置是否合法)
helm install --dry-run --debug my-app-release ./my-app

正式部署

bash 复制代码
# 安装 Chart(指定 release 名称为 my-app-release)
# 建议通过 --set 覆盖敏感密码,避免硬编码
helm install my-app-release ./my-app \
  --set mysql.auth.rootPassword=YourStrongRootPass123 \
  --set mysql.auth.password=YourStrongDBPass123 \
  --set redis.auth.password=YourStrongRedisPass123

# 查看部署状态
helm list
kubectl get pods  # 确认 my-app、mysql、redis 容器均 Running
kubectl get svc   # 查看服务端口

验证应用的运行

bash 复制代码
# 进入应用容器查看日志
kubectl logs -f $(kubectl get pods -l app.kubernetes.io/name=my-app -o jsonpath="{.items[0].metadata.name}")

# 测试接口(若 Service 为 NodePort)
curl http://node-ip:node-port/actuator/health

升级,回滚和卸载

bash 复制代码
# 升级(修改配置后)
helm upgrade my-app-release ./my-app --set replicaCount=2

# 回滚(如回滚到版本 1)
helm rollback my-app-release 1

# 卸载
helm uninstall my-app-release

二:K8s集群监控

Prometheus 是一套开源的监控系统、报警、时间序列的集合,最初由 SoundCloud 开发,后来随着越来越多公司的使用,于是便独立成开源项目。自此以后,许多公司和组织都采用了 Prometheus 作为监控告警工具。

1:传统部署方式

前置准备条件

  • 你的 K8s 集群已正常运行(v1.20+ 版本)
  • 已安装kubectl并配置好集群访问权限
  • 集群有至少 1 个可用节点(推荐 2 核 4G 以上资源)

1️⃣ 创建namespace, 防止和业务资源混在一起

bash 复制代码
kubectl create namespace monitoring

2️⃣ 创建 Prometheus 的 ConfigMap

ConfigMap 用于存储 Prometheus 的核心配置(如采集目标、存储策略、告警配置等),创建prometheus-config.yaml文件

yaml 复制代码
apiVersion: v1
kind: ConfigMap # 指定资源清单类型为config-map
metadata: # 元数据信息,指定config-map的名称和所处的命名空间
  name: prometheus-config
  namespace: monitoring
data:  # 具体config, 就是指定prometheus的核心配置
  prometheus.yml: |
    global:
      scrape_interval: 15s  # 全局采集间隔
      evaluation_interval: 15s  # 规则评估间隔

    # 告警规则文件(后续会挂载)
    rule_files:
      - "/etc/prometheus/rules/*.yml"

    # 采集目标配置
    scrape_configs:
      # 1. 采集Prometheus自身指标
      - job_name: 'prometheus'
        static_configs:
          - targets: ['localhost:9090']
      
      # 2. 采集K8s apiserver(自动发现)
      - job_name: 'kubernetes-apiservers'
        kubernetes_sd_configs:
          - role: endpoints
        scheme: https
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
        relabel_configs:
          - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
            action: keep
            regex: default;kubernetes;https
      
      # 3. 采集K8s节点(通过node-exporter,后续需部署)
      - job_name: 'kubernetes-nodes'
        kubernetes_sd_configs:
          - role: node
        relabel_configs:
          - action: labelmap
            regex: __meta_kubernetes_node_label_(.+)
          - target_label: __address__
            replacement: kubernetes.default.svc:443
          - source_labels: [__meta_kubernetes_node_name]
            regex: (.+)
            target_label: __metrics_path__
            replacement: /api/v1/nodes/${1}/proxy/metrics
        scheme: https
        tls_config:
          ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
      
      # 4. 采集Pod指标(自动发现)
      - job_name: 'kubernetes-pods'
        kubernetes_sd_configs:
          - role: pod
        relabel_configs:
          - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
            action: keep
            regex: true
          - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
            action: replace
            target_label: __metrics_path__
            regex: (.+)
          - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
            action: replace
            regex: ([^:]+)(?::\d+)?;(\d+)
            replacement: $1:$2
            target_label: __address__
bash 复制代码
# 指定创建
kubectl apply -f prometheus-config.yaml

3️⃣ 部署 Prometheus 服务(Deployment + Service)

创建prometheus-deployment.yaml,部署 Prometheus 并挂载 ConfigMap:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus-server
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: prometheus-server
  template:
    metadata:
      labels:
        app: prometheus-server
    spec:
      containers:
        - name: prometheus
          image: prom/prometheus:v2.45.0  # 稳定版本
          args:
            - "--config.file=/etc/prometheus/prometheus.yml"
            - "--storage.tsdb.path=/prometheus/"
            - "--web.enable-lifecycle"  # 支持热加载配置
          ports:
            - containerPort: 9090
          volumeMounts:
            # 挂载ConfigMap中的prometheus.yml
            - name: prometheus-config-volume
              mountPath: /etc/prometheus
            # 挂载规则文件目录(后续创建)
            - name: prometheus-rules-volume
              mountPath: /etc/prometheus/rules
            # 持久化存储指标数据
            - name: prometheus-storage
              mountPath: /prometheus
          resources:
            requests:
              cpu: 100m
              memory: 256Mi
            limits:
              cpu: 500m
              memory: 512Mi
      volumes:
        - name: prometheus-config-volume
          configMap:
            name: prometheus-config  # 关联前面创建的ConfigMap
        - name: prometheus-rules-volume
          emptyDir: {}  # 临时存储,生产建议用ConfigMap/持久卷
        - name: prometheus-storage
          emptyDir: {}  # 生产建议替换为PersistentVolumeClaim
---
# 创建Service暴露Prometheus(NodePort方式,方便访问)
apiVersion: v1
kind: Service
metadata:
  name: prometheus-service
  namespace: monitoring
spec:
  type: NodePort
  selector:
    app: prometheus-server
  ports:
    - port: 8080
      targetPort: 9090
      nodePort: 30090  # 集群外访问端口:节点IP:30090

执行部署

bash 复制代码
kubectl apply -f prometheus-deployment.yaml

# 验证部署,查看是否有prometheus的running
kubectl get pods -n monitoring

4️⃣ 部署 Node Exporter(采集节点指标)

Prometheus 需要 Node Exporter 采集 K8s 节点的 CPU、内存、磁盘等指标

创建node-exporter-daemonset.yaml

yaml 复制代码
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      hostNetwork: true
      hostPID: true
      containers:
        - name: node-exporter
          image: prom/node-exporter:v1.6.1
          args:
            - "--path.procfs=/host/proc"
            - "--path.sysfs=/host/sys"
            - "--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($|/)"
          ports:
            - containerPort: 9100
          volumeMounts:
            - name: proc
              mountPath: /host/proc
              readOnly: true
            - name: sys
              mountPath: /host/sys
              readOnly: true
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
            limits:
              cpu: 200m
              memory: 200Mi
      volumes:
        - name: proc
          hostPath:
            path: /proc
        - name: sys
          hostPath:
            path: /sys
---
# 暴露Node Exporter服务
apiVersion: v1
kind: Service
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  selector:
    app: node-exporter
  ports:
    - port: 9100
      targetPort: 9100
  type: ClusterIP

执行部署

bash 复制代码
kubectl apply -f node-exporter-daemonset.yaml

5️⃣ 编写 Prometheus 监控规则(告警 + 指标)

创建prometheus-rules.yaml(ConfigMap 形式挂载规则):

yaml 复制代码
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-rules
  namespace: monitoring
data:
  node-alert.yml: |
    groups:
    - name: node-monitor-rules
      rules:
      # 1. 节点CPU使用率超过80%告警
      - alert: NodeCPUUsageHigh
        expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "节点CPU使用率过高 ({{ $labels.instance }})"
          description: "节点CPU使用率超过80% (当前值: {{ $value }}%)"
      
      # 2. 节点内存使用率超过85%告警
      - alert: NodeMemoryUsageHigh
        expr: 100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100) > 85
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "节点内存使用率过高 ({{ $labels.instance }})"
          description: "节点内存使用率超过85% (当前值: {{ $value }}%)"
      
      # 3. 节点磁盘使用率超过90%告警
      - alert: NodeDiskUsageHigh
        expr: 100 - (node_filesystem_avail_bytes{mountpoint="/"} / node_filesystem_size_bytes{mountpoint="/"} * 100) > 90
        for: 2m
        labels:
          severity: critical
        annotations:
          summary: "节点磁盘使用率过高 ({{ $labels.instance }})"
          description: "节点根目录磁盘使用率超过90% (当前值: {{ $value }}%)"

执行创建,并更新 Prometheus Deployment 挂载该 ConfigMap:

yaml 复制代码
# 1. 创建规则ConfigMap
kubectl apply -f prometheus-rules.yaml

# 2. 修改Prometheus Deployment,挂载规则ConfigMap(替换之前的emptyDir)
# 编辑Deployment:
kubectl edit deployment prometheus-server -n monitoring
# 将volumes中的prometheus-rules-volume修改为:
# - name: prometheus-rules-volume
#   configMap:
#     name: prometheus-rules

# 3. 热加载Prometheus配置(无需重启Pod)
kubectl exec -n monitoring $(kubectl get pods -n monitoring -l app=prometheus-server -o jsonpath="{.items[0].metadata.name}") -- curl -X POST http://localhost:9090/-/reload

6️⃣ 部署grafana

创建grafana-deployment.yaml,并配置 Prometheus 作为数据源:

yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grafana
  namespace: monitoring
spec:
  replicas: 1
  selector:
    matchLabels:
      app: grafana
  template:
    metadata:
      labels:
        app: grafana
    spec:
      containers:
        - name: grafana
          image: grafana/grafana:9.5.2
          ports:
            - containerPort: 3000
          env:
            # 默认管理员账号密码(首次登录修改)
            - name: GF_SECURITY_ADMIN_USER
              value: "admin"
            - name: GF_SECURITY_ADMIN_PASSWORD
              value: "admin123"
          volumeMounts:
            - name: grafana-storage
              mountPath: /var/lib/grafana
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
            limits:
              cpu: 500m
              memory: 512Mi
      volumes:
        - name: grafana-storage
          emptyDir: {}  # 生产建议用PersistentVolumeClaim
---
# 暴露Grafana服务(NodePort)
apiVersion: v1
kind: Service
metadata:
  name: grafana-service
  namespace: monitoring
spec:
  type: NodePort
  selector:
    app: grafana
  ports:
    - port: 3000
      targetPort: 3000
      nodePort: 30030  # 集群外访问:节点IP:30030

执行部署

bash 复制代码
kubectl apply -f grafana-deployment.yaml

7️⃣ Grafana 配置 Prometheus 数据源 + 导入仪表盘

浏览器打开 http://<集群节点IP>:30030,使用账号admin/ 密码admin123登录(首次登录需修改密码)

添加 Prometheus 数据源:

  • 左侧菜单 → Configuration → Data sources → Add data source → 选择 Prometheus。
  • URL 填写:http://prometheus-service.monitoring.svc:8080(K8s 内部服务地址)。
  • 点击 "Save & test",提示 "Data source is working" 即配置成功。

导入 K8s 监控仪表盘:

  • 左侧菜单 → Dashboards → Import → 输入仪表盘 ID:1860(Node Exporter Full,官方推荐)。
  • 选择已配置的 Prometheus 数据源 → 点击 "Import",即可看到完整的节点监控面板。
  • 额外推荐仪表盘 ID:7249(K8s 集群监控)、3119(Prometheus 自身监控)。

2:Helm包管理器部署

前置准备

  • K8s 集群(1.20+),已配置 kubectl 访问权限。
  • Helm 3 已安装(helm version 验证)。
  • 集群已安装 metrics-server(可选,用于采集 Pod / 节点资源指标)
bash 复制代码
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
# 若 metrics-server 启动失败,添加 --kubelet-insecure-tls 参数
kubectl edit deployment metrics-server -n kube-system
# 在 args 中添加:--kubelet-insecure-tls

目录结构如下:

plaintext 复制代码
k8s-monitoring/                # 监控总目录
├── prometheus/                # Prometheus 核心配置目录
│   ├── prometheus-values.yaml # Prometheus Helm 部署的完整 values 配置(含 ConfigMap、监控规则)
│   └── scripts/               # 辅助脚本目录
│       └── port-forward.sh    # 临时访问 Prometheus 的端口转发脚本
├── grafana/                   # Grafana 配置目录
│   └── grafana-values.yaml    # Grafana Helm 部署的完整 values 配置
├── deploy.sh                  # 一键部署 Prometheus + Grafana 的脚本
└── README.md                  # 部署说明文档

k8s-monitoring/prometheus/prometheus-values.yaml

包含 Prometheus 的 ConfigMap、持久化、RBAC、监控规则

yaml 复制代码
# Prometheus Helm 部署核心配置
# 覆盖 prometheus-community/prometheus Chart 的默认值

# 1. Prometheus 主配置(ConfigMap 形式的 prometheus.yml)
prometheus:
  configMap:
    # ConfigMap 名称(自动创建)
    name: prometheus-server-config
    # prometheus.yml 核心配置(采集 K8s 集群指标)
    prometheus.yml: |
      global:
        scrape_interval: 15s
        evaluation_interval: 15s

      alerting:
        alertmanagers:
          - static_configs:
              - targets: []

      # 加载自定义监控规则
      rule_files:
        - /etc/prometheus/rules/prometheus-prometheus-rule-0.yaml

      # 采集配置(K8s 集群组件 + Prometheus 自身)
      scrape_configs:
        # 采集 Prometheus 自身
        - job_name: 'prometheus'
          static_configs:
            - targets: ['localhost:9090']

        # 采集 K8s APIServer
        - job_name: 'kubernetes-apiservers'
          kubernetes_sd_configs:
            - role: endpoints
          scheme: https
          tls_config:
            ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
          bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
          relabel_configs:
            - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
              action: keep
              regex: default;kubernetes;https

        # 采集 K8s Node(kubelet 指标)
        - job_name: 'kubernetes-nodes'
          kubernetes_sd_configs:
            - role: node
          scheme: https
          tls_config:
            ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
          bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
          relabel_configs:
            - action: labelmap
              regex: __meta_kubernetes_node_label_(.+)
            - target_label: __address__
              replacement: kubernetes.default.svc:443
            - source_labels: [__meta_kubernetes_node_name]
              regex: (.+)
              target_label: __metrics_path__
              replacement: /api/v1/nodes/${1}/proxy/metrics

        # 采集 K8s Pod(自动发现带注解的 Pod)
        - job_name: 'kubernetes-pods'
          kubernetes_sd_configs:
            - role: pod
          relabel_configs:
            - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
              action: keep
              regex: true
            - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
              action: replace
              target_label: __metrics_path__
              regex: (.+)
            - source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
              action: replace
              regex: ([^:]+)(?::\d+)?;(\d+)
              replacement: $1:$2
              target_label: __address__
            - action: labelmap
              regex: __meta_kubernetes_pod_label_(.+)
            - source_labels: [__meta_kubernetes_namespace]
              action: replace
              target_label: kubernetes_namespace
            - source_labels: [__meta_kubernetes_pod_name]
              action: replace
              target_label: kubernetes_pod_name

  # 2. 持久化配置(避免数据丢失)
  persistence:
    enabled: true
    storageClassName: "manual"  # 若集群无默认存储类,需提前创建(或改为 "" 使用默认)
    size: 10Gi

  # 3. RBAC 权限(必须开启,否则无法访问 K8s API)
  rbac:
    create: true
    pspEnabled: false

  # 4. 服务暴露(ClusterIP,供 Grafana 内部访问)
  service:
    type: ClusterIP
    port: 9090

  # 5. 资源限制(生产环境必配)
  resources:
    limits:
      cpu: 2000m
      memory: 4Gi
    requests:
      cpu: 1000m
      memory: 2Gi

# 2. 自定义监控规则(PrometheusRule CRD)
prometheusRule:
  enabled: true
  additionalLabels:
    prometheus: k8s
  # 监控规则组(节点 + Pod 告警)
  groups:
    - name: node-monitoring.rules
      rules:
        - alert: NodeHighCPUUsage
          expr: avg(rate(node_cpu_seconds_total{mode!="idle"}[5m])) by (node) > 0.8
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "节点 {{ $labels.node }} CPU 使用率过高"
            description: "节点 {{ $labels.node }} CPU 使用率超过 80% (当前值: {{ $value }})"
        - alert: NodeHighMemoryUsage
          expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 85
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "节点 {{ $labels.node }} 内存使用率过高"
            description: "节点 {{ $labels.node }} 内存使用率超过 85% (当前值: {{ $value }})"
    - name: pod-monitoring.rules
      rules:
        - alert: PodHighCPUUsage
          expr: avg(rate(container_cpu_usage_seconds_total[5m])) by (pod, namespace) > 0.8
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: "Pod {{ $labels.pod }} ({{ $labels.namespace }}) CPU 使用率过高"
            description: "Pod {{ $labels.pod }} CPU 使用率超过 80% (当前值: {{ $value }})"

# 3. 其他全局配置
server:
  # 关闭不必要的功能,减少资源占用
  statefulSet:
    enabled: true
  # 数据保留时间(默认 15 天,生产建议 30 天)
  retention: 30d

k8s-monitoring/prometheus/scripts/port-forward.sh

辅助脚本

shell 复制代码
#!/bin/bash
# 临时端口转发,访问 Prometheus UI
echo "=== 启动 Prometheus 端口转发 ==="
echo "访问地址:http://localhost:9090"
echo "按 Ctrl+C 停止转发"
kubectl port-forward svc/prometheus-server 9090:9090 -n monitoring

k8s-monitoring/grafana/grafana-values.yaml

grafana的配置

yaml 复制代码
# Grafana Helm 部署配置
# 覆盖 grafana/grafana Chart 的默认值

# 1. 持久化配置
persistence:
  enabled: true
  size: 5Gi
  storageClassName: "manual"  # 同 Prometheus 存储类

# 2. 服务暴露(NodePort 供外部访问,生产建议 Ingress)
service:
  type: NodePort
  nodePort: 30000  # 自定义 NodePort 端口(30000-32767 之间)
  port: 3000

# 3. 管理员密码(生产环境建议改为复杂密码)
adminPassword: "Admin@123456"

# 4. 资源限制
resources:
  limits:
    cpu: 500m
    memory: 1Gi
  requests:
    cpu: 200m
    memory: 512Mi

# 5. 自动导入仪表盘(可选,提前下载 JSON 放到 dashboards/ 目录)
dashboard:
  enabled: false  # 先手动导入,熟悉后再开启自动导入
  # 若开启,需配置 dashboardFiles 指向本地 JSON 文件
  # dashboardFiles:
  #   - dashboards/k8s-cluster.json

# 6. 自动配置数据源(可选,替代手动配置)
datasources:
  datasources.yaml:
    apiVersion: 1
    datasources:
      - name: Prometheus
        type: prometheus
        url: http://prometheus-server.monitoring.svc:9090
        access: proxy
        isDefault: true

k8s-monitoring/deploy.sh

一键部署脚本

shell 复制代码
#!/bin/bash
set -e  # 出错立即停止执行

# 颜色输出函数(可选,提升可读性)
red() { echo -e "\033[31m$1\033[0m"; }
green() { echo -e "\033[32m$1\033[0m"; }
yellow() { echo -e "\033[33m$1\033[0m"; }

# 步骤 1:检查前置条件
green "=== 检查前置条件 ==="
if ! command -v helm &> /dev/null; then
  red "错误:未安装 Helm 3,请先安装 Helm!"
  exit 1
fi

if ! kubectl get nodes &> /dev/null; then
  red "错误:kubectl 未配置 K8s 集群访问权限!"
  exit 1
fi

# 步骤 2:创建监控命名空间
green "=== 创建 monitoring 命名空间 ==="
kubectl create namespace monitoring || yellow "命名空间已存在,跳过"

# 步骤 3:添加 Helm 仓库
green "=== 添加 Prometheus/Grafana Helm 仓库 ==="
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts || yellow "Prometheus 仓库已添加,跳过"
helm repo add grafana https://grafana.github.io/helm-charts || yellow "Grafana 仓库已添加,跳过"
helm repo update

# 步骤 4:部署 Prometheus
green "=== 部署 Prometheus ==="
helm install prometheus prometheus-community/prometheus \
  -n monitoring \
  -f ./prometheus/prometheus-values.yaml

# 步骤 5:部署 Grafana
green "=== 部署 Grafana ==="
helm install grafana grafana/grafana \
  -n monitoring \
  -f ./grafana/grafana-values.yaml

# 步骤 6:验证部署状态
green "=== 验证部署状态 ==="
kubectl get pods -n monitoring
kubectl get svc -n monitoring

# 步骤 7:输出访问信息
green "=== 部署完成!==="
yellow "1. Prometheus 访问:执行 ./prometheus/scripts/port-forward.sh,然后访问 http://localhost:9090"
yellow "2. Grafana 访问:http://<K8s节点IP>:30000(用户名:admin,密码:Admin@123456)"
yellow "3. Grafana 数据源:已自动配置 Prometheus(http://prometheus-server.monitoring.svc:9090)"
bash 复制代码
./deploy.sh

Prometheus访问方式

  • 执行端口转发:./prometheus/scripts/port-forward.sh
  • 访问:http://localhost:9090
  • 验证指标:在 Graph 中输入 node_cpu_seconds_total,查看是否有数据

Grafana访问方式

  • 访问:http://<K8s 节点 IP>:30000

  • 用户名:admin

  • 密码:Admin@123456

  • 导入仪表盘:

  • 节点监控:1860(Node Exporter Full)

  • K8s 集群监控:7249(Kubernetes Cluster Monitoring)

三:ELK日志管理

所谓的ELK就是filebeat -> logstach -> es -> kibana

1️⃣ 先部署filebeat完成数据的采集

yaml 复制代码
# 创建 filebeat.yaml 并部署
--- 
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: filebeat-config 
  namespace: kube-logging 
  labels: 
    k8s-app: filebeat 
data: 
  filebeat.yml: |- 
    filebeat.inputs: 
    - type: container 
      enable: true
      paths: 
        - /var/log/containers/*.log #这里是filebeat采集挂载到pod中的日志目录 
      processors: 
        - add_kubernetes_metadata: #添加k8s的字段用于后续的数据清洗 
            host: ${NODE_NAME}
            matchers: 
            - logs_path: 
                logs_path: "/var/log/containers/" 
    #output.kafka:  #如果日志量较大,es中的日志有延迟,可以选择在filebeat和logstash中间加入kafka 
    #  hosts: ["kafka-log-01:9092", "kafka-log-02:9092", "kafka-log-03:9092"] 
    # topic: 'topic-test-log' 
    #  version: 2.0.0 
    output.logstash: #因为还需要部署logstash进行数据的清洗,因此filebeat是把数据推到logstash中 
       hosts: ["logstash:5044"] 
       enabled: true 
--- 
apiVersion: v1 
kind: ServiceAccount 
metadata: 
  name: filebeat 
  namespace: kube-logging 
  labels: 
    k8s-app: filebeat
--- 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole 
metadata: 
  name: filebeat 
  labels: 
    k8s-app: filebeat 
rules: 
- apiGroups: [""] # "" indicates the core API group 
  resources: 
  - namespaces 
  - pods 
  verbs: ["get", "watch", "list"] 
--- 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding 
metadata: 
  name: filebeat 
subjects: 
- kind: ServiceAccount 
  name: filebeat 
  namespace: kube-logging 
roleRef: 
  kind: ClusterRole 
  name: filebeat 
  apiGroup: rbac.authorization.k8s.io 
--- 
apiVersion: apps/v1 
kind: DaemonSet 
metadata: 
  name: filebeat 
  namespace: kube-logging 
  labels: 
    k8s-app: filebeat 
spec: 
  selector: 
    matchLabels: 
      k8s-app: filebeat 
  template: 
    metadata: 
      labels: 
        k8s-app: filebeat 
    spec: 
      serviceAccountName: filebeat 
      terminationGracePeriodSeconds: 30 
      containers: 
      - name: filebeat 
        image: docker.io/kubeimages/filebeat:7.9.3 #该镜像支持arm64和amd64两种架构 
        args: [ 
          "-c", "/etc/filebeat.yml", 
          "-e","-httpprof","0.0.0.0:6060" 
        ] 
        #ports: 
        #  - containerPort: 6060 
        #    hostPort: 6068 
        env: 
        - name: NODE_NAME 
          valueFrom: 
            fieldRef: 
              fieldPath: spec.nodeName 
        - name: ELASTICSEARCH_HOST 
          value: elasticsearch-logging 
        - name: ELASTICSEARCH_PORT 
          value: "9200" 
        securityContext: 
          runAsUser: 0 
          # If using Red Hat OpenShift uncomment this: 
          #privileged: true 
        resources: 
          limits: 
            memory: 1000Mi 
            cpu: 1000m 
          requests: 
            memory: 100Mi 
            cpu: 100m 
        volumeMounts: 
        - name: config #挂载的是filebeat的配置文件 
          mountPath: /etc/filebeat.yml 
          readOnly: true 
          subPath: filebeat.yml 
        - name: data #持久化filebeat数据到宿主机上 
          mountPath: /usr/share/filebeat/data 
        - name: varlibdockercontainers #这里主要是把宿主机上的源日志目录挂载到filebeat容器中,如果没有修改docker或者containerd的runtime进行了标准的日志落盘路径,可以把mountPath改为/var/lib 
          mountPath: /var/lib
          readOnly: true 
        - name: varlog #这里主要是把宿主机上/var/log/pods和/var/log/containers的软链接挂载到filebeat容器中 
          mountPath: /var/log/ 
          readOnly: true 
        - name: timezone 
          mountPath: /etc/localtime 
      volumes: 
      - name: config 
        configMap: 
          defaultMode: 0600 
          name: filebeat-config 
      - name: varlibdockercontainers 
        hostPath: #如果没有修改docker或者containerd的runtime进行了标准的日志落盘路径,可以把path改为/var/lib 
          path: /var/lib
      - name: varlog 
        hostPath: 
          path: /var/log/ 
      # data folder stores a registry of read status for all files, so we don't send everything again on a Filebeat pod restart 
      - name: inputs 
        configMap: 
          defaultMode: 0600 
          name: filebeat-inputs 
      - name: data 
        hostPath: 
          path: /data/filebeat-data 
          type: DirectoryOrCreate 
      - name: timezone 
        hostPath: 
          path: /etc/localtime 
      tolerations: #加入容忍能够调度到每一个节点 
      - effect: NoExecute 
        key: dedicated 
        operator: Equal 
        value: gpu 
      - effect: NoSchedule 
        operator: Exists

2️⃣ 再部署logstach完成数据的过滤和收集

yaml 复制代码
# 创建 logstash.yaml 并部署服务
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: logstash 
  namespace: kube-logging 
spec: 
  ports: 
  - port: 5044 
    targetPort: beats 
  selector: 
    type: logstash 
  clusterIP: None 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: logstash 
  namespace: kube-logging 
spec: 
  selector: 
    matchLabels: 
      type: logstash 
  template: 
    metadata: 
      labels: 
        type: logstash 
        srv: srv-logstash 
    spec: 
      containers: 
      - image: docker.io/kubeimages/logstash:7.9.3 #该镜像支持arm64和amd64两种架构 
        name: logstash 
        ports: 
        - containerPort: 5044 
          name: beats 
        command: 
        - logstash 
        - '-f' 
        - '/etc/logstash_c/logstash.conf' 
        env: 
        - name: "XPACK_MONITORING_ELASTICSEARCH_HOSTS" 
          value: "http://elasticsearch-logging:9200" 
        volumeMounts: 
        - name: config-volume 
          mountPath: /etc/logstash_c/ 
        - name: config-yml-volume 
          mountPath: /usr/share/logstash/config/ 
        - name: timezone 
          mountPath: /etc/localtime 
        resources: #logstash一定要加上资源限制,避免对其他业务造成资源抢占影响 
          limits: 
            cpu: 1000m 
            memory: 2048Mi 
          requests: 
            cpu: 512m 
            memory: 512Mi 
      volumes: 
      - name: config-volume 
        configMap: 
          name: logstash-conf 
          items: 
          - key: logstash.conf 
            path: logstash.conf 
      - name: timezone 
        hostPath: 
          path: /etc/localtime 
      - name: config-yml-volume 
        configMap: 
          name: logstash-yml 
          items: 
          - key: logstash.yml 
            path: logstash.yml 
 
--- 
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: logstash-conf 
  namespace: kube-logging 
  labels: 
    type: logstash 
data: 
  logstash.conf: |- 
    input {
      beats { 
        port => 5044 
      } 
    } 
    filter {
      # 处理 ingress 日志 
      if [kubernetes][container][name] == "nginx-ingress-controller" {
        json {
          source => "message" 
          target => "ingress_log" 
        }
        if [ingress_log][requesttime] { 
          mutate { 
            convert => ["[ingress_log][requesttime]", "float"] 
          }
        }
        if [ingress_log][upstremtime] { 
          mutate { 
            convert => ["[ingress_log][upstremtime]", "float"] 
          }
        } 
        if [ingress_log][status] { 
          mutate { 
            convert => ["[ingress_log][status]", "float"] 
          }
        }
        if  [ingress_log][httphost] and [ingress_log][uri] {
          mutate { 
            add_field => {"[ingress_log][entry]" => "%{[ingress_log][httphost]}%{[ingress_log][uri]}"} 
          } 
          mutate { 
            split => ["[ingress_log][entry]","/"] 
          } 
          if [ingress_log][entry][1] { 
            mutate { 
              add_field => {"[ingress_log][entrypoint]" => "%{[ingress_log][entry][0]}/%{[ingress_log][entry][1]}"} 
              remove_field => "[ingress_log][entry]" 
            }
          } else { 
            mutate { 
              add_field => {"[ingress_log][entrypoint]" => "%{[ingress_log][entry][0]}/"} 
              remove_field => "[ingress_log][entry]" 
            }
          }
        }
      }
      # 处理以srv进行开头的业务服务日志 
      if [kubernetes][container][name] =~ /^srv*/ { 
        json { 
          source => "message" 
          target => "tmp" 
        } 
        if [kubernetes][namespace] == "kube-logging" { 
          drop{} 
        } 
        if [tmp][level] { 
          mutate{ 
            add_field => {"[applog][level]" => "%{[tmp][level]}"} 
          } 
          if [applog][level] == "debug"{ 
            drop{} 
          } 
        } 
        if [tmp][msg] { 
          mutate { 
            add_field => {"[applog][msg]" => "%{[tmp][msg]}"} 
          } 
        } 
        if [tmp][func] { 
          mutate { 
            add_field => {"[applog][func]" => "%{[tmp][func]}"} 
          } 
        } 
        if [tmp][cost]{ 
          if "ms" in [tmp][cost] { 
            mutate { 
              split => ["[tmp][cost]","m"] 
              add_field => {"[applog][cost]" => "%{[tmp][cost][0]}"} 
              convert => ["[applog][cost]", "float"] 
            } 
          } else { 
            mutate { 
              add_field => {"[applog][cost]" => "%{[tmp][cost]}"} 
            }
          }
        }
        if [tmp][method] { 
          mutate { 
            add_field => {"[applog][method]" => "%{[tmp][method]}"} 
          }
        }
        if [tmp][request_url] { 
          mutate { 
            add_field => {"[applog][request_url]" => "%{[tmp][request_url]}"} 
          } 
        }
        if [tmp][meta._id] { 
          mutate { 
            add_field => {"[applog][traceId]" => "%{[tmp][meta._id]}"} 
          } 
        } 
        if [tmp][project] { 
          mutate { 
            add_field => {"[applog][project]" => "%{[tmp][project]}"} 
          }
        }
        if [tmp][time] { 
          mutate { 
            add_field => {"[applog][time]" => "%{[tmp][time]}"} 
          }
        }
        if [tmp][status] { 
          mutate { 
            add_field => {"[applog][status]" => "%{[tmp][status]}"} 
            convert => ["[applog][status]", "float"] 
          }
        }
      }
      mutate { 
        rename => ["kubernetes", "k8s"] 
        remove_field => "beat" 
        remove_field => "tmp" 
        remove_field => "[k8s][labels][app]" 
      }
    }
    output { 
      elasticsearch { 
        hosts => ["http://elasticsearch-logging:9200"] 
        codec => json 
        index => "logstash-%{+YYYY.MM.dd}" #索引名称以logstash+日志进行每日新建 
      } 
    } 
---
 
apiVersion: v1 
kind: ConfigMap 
metadata: 
  name: logstash-yml 
  namespace: kube-logging 
  labels: 
    type: logstash 
data: 
  logstash.yml: |- 
    http.host: "0.0.0.0" 
    xpack.monitoring.elasticsearch.hosts: http://elasticsearch-logging:9200

3️⃣ 先部署elasticsearch

yaml 复制代码
# 需要提前给 es 落盘节点打上标签 
kubectl label node <node name> es=data 

# 创建 es.yaml 
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: elasticsearch-logging 
  namespace: kube-logging 
  labels: 
    k8s-app: elasticsearch-logging 
    kubernetes.io/cluster-service: "true" 
    addonmanager.kubernetes.io/mode: Reconcile 
    kubernetes.io/name: "Elasticsearch" 
spec: 
  ports: 
  - port: 9200 
    protocol: TCP 
    targetPort: db 
  selector: 
    k8s-app: elasticsearch-logging 
--- 
# RBAC authn and authz 
apiVersion: v1 
kind: ServiceAccount 
metadata: 
  name: elasticsearch-logging 
  namespace: kube-logging 
  labels: 
    k8s-app: elasticsearch-logging 
    kubernetes.io/cluster-service: "true" 
    addonmanager.kubernetes.io/mode: Reconcile 
--- 
kind: ClusterRole 
apiVersion: rbac.authorization.k8s.io/v1 
metadata: 
  name: elasticsearch-logging 
  labels: 
    k8s-app: elasticsearch-logging 
    kubernetes.io/cluster-service: "true" 
    addonmanager.kubernetes.io/mode: Reconcile 
rules: 
- apiGroups: 
  - "" 
  resources: 
  - "services" 
  - "namespaces" 
  - "endpoints" 
  verbs: 
  - "get" 
--- 
kind: ClusterRoleBinding 
apiVersion: rbac.authorization.k8s.io/v1 
metadata: 
  namespace: kube-logging 
  name: elasticsearch-logging 
  labels: 
    k8s-app: elasticsearch-logging 
    kubernetes.io/cluster-service: "true" 
    addonmanager.kubernetes.io/mode: Reconcile 
subjects: 
- kind: ServiceAccount 
  name: elasticsearch-logging 
  namespace: kube-logging 
  apiGroup: "" 
roleRef: 
  kind: ClusterRole 
  name: elasticsearch-logging 
  apiGroup: "" 
--- 
# Elasticsearch deployment itself 
apiVersion: apps/v1 
kind: StatefulSet #使用statefulset创建Pod 
metadata: 
  name: elasticsearch-logging #pod名称,使用statefulSet创建的Pod是有序号有顺序的 
  namespace: kube-logging  #命名空间 
  labels: 
    k8s-app: elasticsearch-logging 
    kubernetes.io/cluster-service: "true" 
    addonmanager.kubernetes.io/mode: Reconcile 
    srv: srv-elasticsearch 
spec: 
  serviceName: elasticsearch-logging #与svc相关联,这可以确保使用以下DNS地址访问Statefulset中的每个pod (es-cluster-[0,1,2].elasticsearch.elk.svc.cluster.local) 
  replicas: 1 #副本数量,单节点 
  selector: 
    matchLabels: 
      k8s-app: elasticsearch-logging #和pod template配置的labels相匹配 
  template: 
    metadata: 
      labels: 
        k8s-app: elasticsearch-logging 
        kubernetes.io/cluster-service: "true" 
    spec: 
      serviceAccountName: elasticsearch-logging 
      containers: 
      - image: docker.io/library/elasticsearch:7.9.3 
        name: elasticsearch-logging 
        resources: 
          # need more cpu upon initialization, therefore burstable class 
          limits: 
            cpu: 1000m 
            memory: 2Gi 
          requests: 
            cpu: 100m 
            memory: 500Mi 
        ports: 
        - containerPort: 9200 
          name: db 
          protocol: TCP 
        - containerPort: 9300 
          name: transport 
          protocol: TCP 
        volumeMounts: 
        - name: elasticsearch-logging 
          mountPath: /usr/share/elasticsearch/data/   #挂载点 
        env: 
        - name: "NAMESPACE" 
          valueFrom: 
            fieldRef: 
              fieldPath: metadata.namespace 
        - name: "discovery.type"  #定义单节点类型 
          value: "single-node" 
        - name: ES_JAVA_OPTS #设置Java的内存参数,可以适当进行加大调整 
          value: "-Xms512m -Xmx2g"  
      volumes: 
      - name: elasticsearch-logging 
        hostPath: 
          path: /data/es/ 
      nodeSelector: #如果需要匹配落盘节点可以添加 nodeSelect 
        es: data 
      tolerations: 
      - effect: NoSchedule 
        operator: Exists 
      # Elasticsearch requires vm.max_map_count to be at least 262144. 
      # If your OS already sets up this number to a higher value, feel free 
      # to remove this init container. 
      initContainers: #容器初始化前的操作 
      - name: elasticsearch-logging-init 
        image: alpine:3.6 
        command: ["/sbin/sysctl", "-w", "vm.max_map_count=262144"] #添加mmap计数限制,太低可能造成内存不足的错误 
        securityContext:  #仅应用到指定的容器上,并且不会影响Volume 
          privileged: true #运行特权容器 
      - name: increase-fd-ulimit 
        image: busybox 
        imagePullPolicy: IfNotPresent 
        command: ["sh", "-c", "ulimit -n 65536"] #修改文件描述符最大数量 
        securityContext: 
          privileged: true 
      - name: elasticsearch-volume-init #es数据落盘初始化,加上777权限 
        image: alpine:3.6 
        command: 
          - chmod 
          - -R 
          - "777" 
          - /usr/share/elasticsearch/data/ 
        volumeMounts: 
        - name: elasticsearch-logging 
          mountPath: /usr/share/elasticsearch/data/

# 创建命名空间
kubectl create ns kube-logging

# 创建服务
kubectl create -f es.yaml

# 查看 pod 启用情况
kubectl get pod -n kube-logging

4️⃣ 再部署kibana完成可视化

yaml 复制代码
# 此处有配置 kibana 访问域名,如果没有域名则需要在本机配置 hosts
192.168.113.121 kibana.wolfcode.cn

# 创建 kibana.yaml 并创建服务
---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: kube-logging
  name: kibana-config
  labels:
    k8s-app: kibana
data:
  kibana.yml: |-
    server.name: kibana
    server.host: "0"
    i18n.locale: zh-CN                      #设置默认语言为中文
    elasticsearch:
      hosts: ${ELASTICSEARCH_HOSTS}         #es集群连接地址,由于我这都都是k8s部署且在一个ns下,可以直接使用service name连接
--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: kibana 
  namespace: kube-logging 
  labels: 
    k8s-app: kibana 
    kubernetes.io/cluster-service: "true" 
    addonmanager.kubernetes.io/mode: Reconcile 
    kubernetes.io/name: "Kibana" 
    srv: srv-kibana 
spec: 
  type: NodePort
  ports: 
  - port: 5601 
    protocol: TCP 
    targetPort: ui 
  selector: 
    k8s-app: kibana 
--- 
apiVersion: apps/v1 
kind: Deployment 
metadata: 
  name: kibana 
  namespace: kube-logging 
  labels: 
    k8s-app: kibana 
    kubernetes.io/cluster-service: "true" 
    addonmanager.kubernetes.io/mode: Reconcile 
    srv: srv-kibana 
spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      k8s-app: kibana 
  template: 
    metadata: 
      labels: 
        k8s-app: kibana 
    spec: 
      containers: 
      - name: kibana 
        image: docker.io/kubeimages/kibana:7.9.3 #该镜像支持arm64和amd64两种架构 
        resources: 
          # need more cpu upon initialization, therefore burstable class 
          limits: 
            cpu: 1000m 
          requests: 
            cpu: 100m 
        env: 
          - name: ELASTICSEARCH_HOSTS 
            value: http://elasticsearch-logging:9200 
        ports: 
        - containerPort: 5601 
          name: ui 
          protocol: TCP 
        volumeMounts:
        - name: config
          mountPath: /usr/share/kibana/config/kibana.yml
          readOnly: true
          subPath: kibana.yml
      volumes:
      - name: config
        configMap:
          name: kibana-config
--- 
apiVersion: networking.k8s.io/v1
kind: Ingress 
metadata: 
  name: kibana 
  namespace: kube-logging 
spec: 
  ingressClassName: nginx
  rules: 
  - host: kibana.wolfcode.cn
    http: 
      paths: 
      - path: / 
        pathType: Prefix
        backend: 
          service:
            name: kibana 
            port:
              number: 5601

进入 Kibana 界面,打开菜单中的 Stack Management 可以看到采集到的日志

避免日志越来越大,占用磁盘过多,进入 索引生命周期策略 界面点击 创建策略 按钮

设置策略名称为 logstash-history-ilm-policy 关闭 热阶段

开启删除阶段,设置保留天数为 7 天,最后保存配置

四:K8s可视化

kubesphere是K8s 之上的分布式操作系统 / 容器管理平台,开源、多租户、可插拔、开箱即用

  • 可视化:Web 界面替代复杂 kubectl,新手快速上手。
  • 全栈能力:集群管理、DevOps、服务网格、监控 / 日志 / 告警、应用商店、多租户、边缘计算KubeSphere。
  • 灵活部署:支持 All-in-One、多节点、已有 K8s 集成、离线安装。
  • 可扩展:v4.x 采用扩展组件架构,按需开启 DevOps、服务网格、可观测等

安装要求

硬件要求(最低)

  • All-in-One(单节点):4 核 CPU、8GB 内存、100GB 存储、CentOS 7.9+/Ubuntu 20.04+
  • 多节点:Master ≥ 4 核 8GB,Worker ≥ 2 核 4GB,节点间网络互通

软件依赖

  • 关闭防火墙 / SELinux、时间同步
  • 安装 socatconntrackipsetebtables
  • 可选:Docker/containerd(KubeKey 可自动安装)

1:本地存储动态的PVC

shell 复制代码
# 在所有节点安装 iSCSI 协议客户端(OpenEBS 需要该协议提供存储支持)
yum install iscsi-initiator-utils -y
# 设置开机启动
systemctl enable --now iscsid
# 启动服务
systemctl start iscsid
# 查看服务状态
systemctl status iscsid

# 安装 OpenEBS 
kubectl apply -f https://openebs.github.io/charts/openebs-operator.yaml

# 查看状态(下载镜像可能需要一些时间)
kubectl get all -n openebs

# 在主节点创建本地 storage class
kubectl apply -f default-storage-class.yaml

2:安装部署

bash 复制代码
# 安装资源
kubectl apply -f https://github.com/kubesphere/ks-installer/releases/download/v3.3.1/kubesphere-installer.yaml
kubectl apply -f https://github.com/kubesphere/ks-installer/releases/download/v3.3.1/cluster-configuration.yaml

# 检查安装日志
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l 'app in (ks-install, ks-installer)' -o jsonpath='{.items[0].metadata.name}') -f

# 查看端口
kubectl get svc/ks-console -n kubesphere-system

# 默认端口是 30880,如果是云服务商,或开启了防火墙,记得要开放该端口

# 登录控制台访问,账号密码:admin/P@88w0rd
相关推荐
江畔何人初2 小时前
kubernetes中configmap与secret的区别
linux·运维·云原生·容器·kubernetes
henry1010102 小时前
Ansible自动化运维全攻略(AI生成)
linux·运维·python·ansible·devops
vortex52 小时前
APT软件包管理从入门到精通
linux·运维·服务器·kali
古译汉书2 小时前
RTOS:ISR与互斥量的关系
运维·服务器·stm32·嵌入式硬件
feng68_3 小时前
Keepalived基础实现
运维·服务器·keepalived
暴力求解3 小时前
Linux---基础IO详解
linux·运维·服务器
lichao8904274 小时前
MacOS 上部署 OpenClaw 的全界面操作方案
macos·docker·容器
猫头虎4 小时前
[精选] 2025最新MySQL和PostgreSQL区别、迁移、安全、适用场景全解析
运维·数据库·mysql·安全·postgresql·云原生·容器
西门吹雪分身4 小时前
K8S之Ingress
java·容器·kubernetes·k8s