侧边栏壁纸
博主头像
J&S Blog

顺着一路星光,去往有你的嘉处

  • 累计撰写 14 篇文章
  • 累计创建 5 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

基于K8s的Halo博客系统自动化部署与高可用架构实践

Administrator
2026-06-04 / 0 评论 / 0 点赞 / 20 阅读 / 0 字

docker

日志轮转

​
im /etc/docker/daemon.json 
​
{
                "registry-mirrors": ["https://docker.1ms.run"],
                "insecure-registries":["192.168.135.152"],
                "log-driver":
                        "json-file",
                        "log-opts":{
                                "max-size": "10m",
                                "max-file": "3"
                        }
}

镜像制作

工具镜像

FROM 192.168.135.152/talo/rocky:9.6
WORKDIR /talo
RUN dnf install -y vim wget java-21-openjdk-devel git tar gzip unzip findutils which libatomic.so.1 libatomic xz &&\
    wget https://nodejs.org/dist/v26.2.0/node-v26.2.0-linux-x64.tar.xz &&\
    tar -xf node-v26.2.0-linux-x64.tar.xz -C /usr/local/ &&\
    rm -f node-v26.2.0-linux-x64.tar.xz  &&\
    dnf clean all && rm -rf /var/cache/dnf /var/cache/yum /tmp/* /var/tmp/*
ENV PATH=$PATH:/usr/local/node-v26.2.0-linux-x64/bin
RUN npm install -g pnpm  && npm cache clean --force
CMD ["/bin/bash"]

应用镜像

FROM eclipse-temurin:21-jre AS builder
WORKDIR /application
ARG JAR_FILE=/application/build/libs/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted
​
FROM eclipse-temurin:21-jre
LABEL maintainer="johnniang <johnniang@foxmail.com>"
WORKDIR /application
COPY --from=builder /application/extracted/dependencies/ ./
COPY --from=builder /application/extracted/spring-boot-loader/ ./
COPY --from=builder /application/extracted/snapshot-dependencies/ ./
COPY --from=builder /application/extracted/application/ ./
​
ENV JVM_OPTS="" \
    HALO_WORK_DIR="/root/.halo2" \
    SPRING_CONFIG_LOCATION="optional:classpath:/;optional:file:/root/.halo2/" \
    TZ=Asia/Shanghai
​
RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime \
    && echo $TZ > /etc/timezone
RUN java -XX:ArchiveClassesAtExit=application.jsa -Dspring.context.exit=onRefresh -jar application.jar --halo.work-dir=/tmp/halo2 \
    && rm -rf /tmp/halo2
EXPOSE 8090
ENTRYPOINT ["sh", "-c", "exec java ${JVM_OPTS} -XX:SharedArchiveFile=application.jsa -jar application.jar \"$@\"", "--"]

部署Harbor私有镜像仓库

Docker的镜像可以借助扫描器来完成镜像的漏洞扫描

#拉取harbor的安装包
[root@www ~]# mkdir /harbor
[root@www ~]# cd /harbor/
[root@www harbor]# wget https://github.com/goharbor/harbor/releases/download/v2.14.0/harbor-offline-installer-v2.14.0.tgz
​
#证书生成
看上面部署docker官方私有镜像仓库的示例
​
#修改文件配置
[root@www harbor]# vim harbor.yml
​
hostname: 192.168.135.152
​
harbor_admin_password: Harbor12345  #设置admin的密码
​
https:
  port: 443
  certificate: /pem/certs/www.zgs.com.pem #证书
  private_key: /pem/certs/www.zgs.com-key.pem #私钥
​
#运行shell脚本
[root@www harbor]# ./install.sh #不安装漏洞扫描工具
[root@www harbor]# ./install.sh --with-trivy #安装漏洞扫描工具
和harbor集成后,可以实现上传镜像到仓库,自动扫描;对于高风险的镜像自动阻止分发和用户拉取
​
#######################
#这个漏洞扫描的工具也可以单独安装进行使用
[root@www mnt]# wget https://github.com/aquasecurity/trivy/releases/download/v0.67.2/trivy_0.67.2_Linux-64bit.rpm
​
[root@www mnt]# rpm -ivh trivy_0.67.2_Linux-64bit.rpm 
​
#扫描镜像
[root@www mnt]# trivy image httpd:latest
[root@www harbor]# vim harbor.yml   #默认不需要修改
trivy:
    
  enabled: true                     # 是否启用 Trivy 扫描功能,必须开启才能扫描
   
  ignore_unfixed: false             #是否忽略未修复的漏洞,false会显示所有漏洞
    
  skip_update: false                #是否跳过漏洞数据库的自动更新,false表示启动时更新
​
  skip_java_db_update: false        #是否跳过java依赖数据库的更新,false表示更新
​
  offline_scan: false               #是否离线扫描(不联网拉取漏洞库),false表示联网更新
​
  security_check: vuln              #安全检查类型,vuln表示漏洞扫描;config表示配置检查;all表示全部
​
  insecure: false                   #是否允许访问不受信任(自签名)https仓库,使用自签证书时设为true
​
  timeout: 5m0s                     #超时时间
​
  github_token: xxx                 #GitHub令牌,提高漏洞库拉取速率,留空则使用匿名访问

注意

自建的镜像仓库因为https的证书是自生成的,因此需要忽略docker的仓库风险提示,也就是启用不受信任的仓库,要配置docker的daemon.json,并重启docker服务

[root@www conf.d]# vim /etc/docker/daemon.json 
​
{
        "registry-mirrors": [
                "https://docker.1ms.run"
        ],
        "insecure-registries": [
                "192.168.135.152"
        ]
}

暴露Docker API端口

vim /usr/lib/systemd/system/docker.service
    ExecStart=/usr/bin/dockerd -H fd:// -H tcp://0.0.0.0:2375  --containerd=/run/containerd/containerd.sock
#添加-H tcp://0.0.0.0:2375,2375是非认证的URL,不需要证书验证的,配置简便。如果需要高安全性则需要使用到TLS证书验证,开放2376端口
​
#开启TLS证书验证的方式
​
#生成CA私钥
openssl genrsa -out ca-key.pem 4096
​
#生成CA自签名证书(有效期10年)
openssl req -new -x509 -days 3650 -sha256 -key ca-key.pem -out ca.pem -subj "/CN=docker-ca"
​
#生成服务端私钥
openssl genrsa -out server-key.pem 4096
​
#生成服务端CSR,并直接写入SAN(把示例IP/域名替换成真实值)
openssl req -new -key server-key.pem -out server.csr -subj "/CN=docker-server" -addext "subjectAltName=IP:192.168.135.30" #-addext subjectAltName=... 证书必须包含你连接时用的地址,否则会报x509不匹配
​
#用CA签发服务端证书,标记用途为serverAuth,并保留CSR里的SAN扩展
printf "subjectAltName=IP:192.168.135.30\nextendedKeyUsage=serverAuth\n" | openssl x509 -req -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 3650 -sha256 -extfile /dev/stdin
​
#生成客户端私钥
openssl genrsa -out key.pem 4096
​
#生成客户端CSR
openssl req -new -key key.pem -out client.csr -subj "/CN=docker-client"
​
#用CA签发客户端证书,标记用途为clientAuth
printf "extendedKeyUsage=clientAuth\n" | openssl x509 -req -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -days 3650 -sha256 #extendedKeyUsage=clientAuth声明该证书用于TLS客户端认证(dockerd -tlsverify会校验
​
#修改docker套接字文件
vim /usr/lib/systemd/system/docker.service 
    ExecStart=/usr/bin/dockerd \   
    -H unix:///var/run/docker.sock \   
    --containerd=/run/containerd/containerd.sock \   
    -H tcp://0.0.0.0:2376 \   
    --tlsverify \ 
    --tlscacert=/certs/ca.pem \            
    --tlscert=/certs/server-cert.pem \            
    --tlskey=/certs/server-key.pem
    
#客户端连接方式
    #客户端家目录下创建.docker隐藏文件
    mkdir /root/.docker
​
    #拷贝证书、密钥..
    scp key.pem cert.pem ca.pem 192.168.135.20:/root/.docker/
    注意:权限设置为400或者600
​
    #使用tls校验进行连接
    docker --tls -H tcp://192.168.135.30:2376 ps
​
    #通过定义全局变量实现
    export DOCKER_HOST=tcp://192.168.135.30:2376
    export DOCKER_TLS_VERIFY=1  #启用TLS认证
    export DOCKER_CERT_PATH=/opt    #指定认证证书存放的目录
    注意:.docker是默认的存放目录,不需要指定

kubernetes

配置私有仓库镜像拉取

#创建目录
mkdir /etc/containerd/certs.d/192.168.135.152
​
#添加配置
vim hosts.toml 
server = "https://192.168.135.152"
​
[host."https://192.168.135.152"]
  capabilities = ["pull","resolve","push"]
  skip_verify = true
  
#重启containerd
systemctl restart containerd.service

部署NFS动态供给持久化存储

#NFS服务端配置
	#安装nfs-utils
		yum install -y nfs-utils
	#添加共享目录
		vim /etc/exports
		/data *(rw,sync,no_root_squash)
	#启动nfs-utils
		systemctl enable nfs-server.service --now
		systemctl restart rpcbind.service
	#检查
		showmount -e
        Export list for node01.example.com:
        /data *


#需要安装nfs-utils,否则无法创建出PV(在每个节点都安装nfs-utils)
vim auto.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: nfs-client-provisioner
      # replace with namespace where provisioner is deployed
      namespace: default
    ---
    kind: ClusterRole
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: nfs-client-provisioner-runner
    rules:
      - apiGroups: [""]
        resources: ["endpoints"]  
        verbs: ["get", "list", "watch", "create", "update", "patch"]
      - apiGroups: [""]
        resources: ["nodes"]
        verbs: ["get", "list", "watch"]
      - apiGroups: [""]
        resources: ["persistentvolumes"]
        verbs: ["get", "list", "watch", "create", "delete"]
      - apiGroups: [""]
        resources: ["persistentvolumeclaims"]
        verbs: ["get", "list", "watch", "update"]
      - apiGroups: ["storage.k8s.io"]
        resources: ["storageclasses"]
        verbs: ["get", "list", "watch"]
      - apiGroups: [""]
        resources: ["events"]
        verbs: ["create", "update", "patch"]
    ---
    kind: ClusterRoleBinding
    apiVersion: rbac.authorization.k8s.io/v1
    metadata:
      name: run-nfs-client-provisioner
    subjects:
      - kind: ServiceAccount
        name: nfs-client-provisioner
        # replace with namespace where provisioner is deployed
        namespace: default
    roleRef:
      kind: ClusterRole
      name: nfs-client-provisioner-runner
      apiGroup: rbac.authorization.k8s.io
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: nfs-client-provisioner
      labels:
        app: nfs-client-provisioner
      # replace with namespace where provisioner is deployed
      namespace: default
    spec:
      replicas: 1
      strategy:
        type: Recreate
      selector:
        matchLabels:
          app: nfs-client-provisioner
      template:
        metadata:
          labels:
            app: nfs-client-provisioner
        spec:
          serviceAccountName: nfs-client-provisioner
          containers:
            - name: nfs-client-provisioner
              image: registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2
              volumeMounts:
                - name: nfs-client-root
                  mountPath: /persistentvolumes
              env:
                - name: PROVISIONER_NAME
                  value: k8s-sigs.io/nfs-subdir-external-provisioner
                - name: NFS_SERVER
                  value: 192.168.135.154
                - name: NFS_PATH
                  value: /data
          volumes:
            - name: nfs-client-root
              nfs:
                server: 192.168.135.154
                path: /data
            
            
#创建SC            
vim sc.yaml 
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: nfs-client
    provisioner: k8s-sigs.io/nfs-subdir-external-provisioner # 动态供给:由上面的控制器处理
    parameters:
      pathPattern: "${.PVC.namespace}/${.PVC.name}" # 每个 PVC 一个独立子目录
      onDelete: delete # PVC 删除时删除子目录
    reclaimPolicy: Delete # 由 SC 决定回收策略
    volumeBindingMode: Immediate
    mountOptions:
    - vers=4.1

#创建PVC测试
vim pvc.yaml 
    kind: PersistentVolumeClaim
    apiVersion: v1
    metadata:
      name: nfs-pvc
    spec:
      storageClassName: nfs-client
      accessModes:
        - ReadWriteMany
      resources:
        requests:
          storage: 200Mi

部署Jenkins

#拉取部署的yaml文件
git clone https://github.com/scriptcamp/kubernetes-jenkins

#修改volume.yaml持久化存储的yaml文件

#修改deployment调度节点

#执行yaml文件
kubectl apply -f /root/kubernetes-jenkins	#执行kubernetes-jenkins目录下所有yaml文件

#查询密码
kubectl logs -n devops-tools  jenkins-6846f7864d-fpdbb

部署ingress nginx

#拉取ingress-nginx
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.14.0/deploy/static/provider/baremetal/deploy.yaml

#执行
kubectl apply -f deploy.yaml

#设置nginx ingress为默认的ingress控制器
    #查询集群中所有的ingressclass
    kubectl get ingressclasses.networking.k8s.io

    kubectl edit ingressclasses.networking.k8s.io nginx
    ingressclass.kubernetes.io/is-default-class: true	#添加这行注解
    作用:在配置ingress的规则时,不指定ingress的控制器,则采用默认的ingress

jenkins

添加cnb代码仓库凭据

image-20260602113426340.png
image-20260602113522861.png
image-20260602113532704.png
image-20260602113603820.png

pipeline

pipeline {
  agent {
    kubernetes {
      cloud 'kubernetes'
      namespace 'halo-app'
      yaml '''
apiVersion: v1
kind: Pod
spec:
  containers:
  - image: 192.168.135.152/talo/rocky9.6:tools
    name: tools
    command:
    - /bin/bash
    - -c
    - sleep 99999
  - image: 192.168.135.152/talo/docker-cli:latest
    name: dockercli
    command:
    - /bin/bash
    - -c
    - sleep 99999
    volumeMounts:
    - name: kubectl
      mountPath: /usr/bin/kubectl
      readOnly: true
  volumes:
  - name: kubectl
    hostPath:
      path: /usr/bin/kubectl
      type: File
'''
    }
  }

  options {
    timestamps()
    disableConcurrentBuilds()
  }

  environment {
    HARBOR_ADDR = '192.168.135.152'
    HARBOR_USER = 'admin'
    HARBOR_PASS = 'Harbor12345'

    IMAGE_REPO = '192.168.135.152/app/talo-app'
    DOCKER_HOST = 'tcp://192.168.135.152:2375'

    KUBECONFIG = './admin.conf'
    APP_NAMESPACE = 'halo'
  }

  stages {
    stage('拉取代码') {
      steps {
        container('tools') {
          git branch: 'main',
              credentialsId: 'e058d114-1b3c-4665-8a4c-827b8cd0a5f2',
              url: 'https://cnb.cool/gslinux/talo.git'
        }
      }
    }

    stage('编译项目') {
      steps {
        container('tools') {
          sh '''
              #因为原地址需要科学上网,所以拉取到本地并存放到apache中,加快编译的速度
              sed -i 's#^distributionUrl=.*#distributionUrl=http\\://192.168.135.151/distributions/gradle-9.5.0-bin.zip#' gradle/wrapper/gradle-wrapper.properties
              ./gradlew clean build -x test -x spotlessCheck || \
              ./gradlew clean build -x test -x spotlessCheck
            '''
        }
      }
    }

    stage('构建并推送镜像') {
      steps {
        container('dockercli') {
          sh '''
              cat > Dockerfile <<'EOF'
FROM 192.168.135.152/talo/eclipse-temurin:21-jre AS builder
WORKDIR /application
ARG JAR_FILE=/application/build/libs/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=tools -jar application.jar extract --layers --destination extracted

FROM 192.168.135.152/talo/eclipse-temurin:21-jre
LABEL maintainer="johnniang <johnniang@foxmail.com>"

WORKDIR /application

COPY --from=builder /application/extracted/dependencies/ ./
COPY --from=builder /application/extracted/spring-boot-loader/ ./
COPY --from=builder /application/extracted/snapshot-dependencies/ ./
COPY --from=builder /application/extracted/application/ ./

ENV JVM_OPTS="" \
    HALO_WORK_DIR="/root/.halo2" \
    SPRING_CONFIG_LOCATION="optional:classpath:/;optional:file:/root/.halo2/" \
    TZ=Asia/Shanghai

RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime \
    && echo $TZ > /etc/timezone

RUN java -XX:ArchiveClassesAtExit=application.jsa \
    -Dspring.context.exit=onRefresh \
    -jar application.jar \
    --halo.work-dir=/tmp/halo2 \
    && rm -rf /tmp/halo2
EXPOSE 8090
ENTRYPOINT ["sh", "-c", "exec java ${JVM_OPTS} -XX:SharedArchiveFile=application.jsa -jar application.jar \\"$@\\"", "--"]
EOF

              docker login -u "${HARBOR_USER}" -p "${HARBOR_PASS}" "${HARBOR_ADDR}"
              docker build -t "${IMAGE_REPO}:v${BUILD_NUMBER}" .
              docker push "${IMAGE_REPO}:v${BUILD_NUMBER}"
            '''
        }
      }
    }

    stage('部署到 Kubernetes') {
      steps {
        container('dockercli') {
          sh '''
            cat > admin.conf <<EOF
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCVENDQWUyZ0F3SUJBZ0lJRUU2OFdiMXZWd2d3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TlRFeE1qTXhNVFF6TkRaYUZ3MHpOVEV4TWpFeE1UUTRORFphTUJVeApFekFSQmdOVkJBTVRDbXQxWW1WeWJtVjBaWE13Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUURaMzF6Qi8xcnp3S21UalBrOFlBS0FYd1pwMStUS2R6cGhUVW5UUjZuWjhYbWRQcjBjVjNUaERyR0YKNFcrYSs1MnpNS0lNbDJoalNFRmNkRndSOFE0NDR1YTE0YU5GTmJxRDFhUU91Rm1hWWM3U1hYZ0d5YXhuRGJOTQozY1lOZlpKSy94TXI0TEllN1JTT2d3TkRjSEkxMWJVNFhoeDFJekpUUmNUWU11a1VoaGFyTVd0TnRUSFdKNmc4CitlbHNmcDFiN1dFUW96bjcwbCtkVytWM2JaMkpDWmNKMm85MDhycVhLR1FBTjdpWXZCdnZUM1JUVlhOejdHbFcKUDdlbmxFNUQwT1Y5Nmp1clZobzNHKzNNNmZTMUZ0Kzh6LzVwZlZKQ0Q3VEpqd29pMmVvdkN0NHp2Szc4b1gydwprcTdpa3YxelZ1ZGpYUkZJNFZRb3dLS29oV1M3QWdNQkFBR2pXVEJYTUE0R0ExVWREd0VCL3dRRUF3SUNwREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSQUdwTFJoNVpIemQvMVV5QVBjcGVkbndET2xUQVYKQmdOVkhSRUVEakFNZ2dwcmRXSmxjbTVsZEdWek1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQXFEbXM1M29zRgpxS21Cbmc5WUlCYklPbEtzTG40MkpzZC9aVHl2YlI4bmJTczRtWUdpQ0I1cnhRNEUwcWtCdU80YjBFSHdFemtuCmVZd2NxSXhjZk9DQmRkeW9OK0J0NVVUejlPWjdLNnpkWUpFcERMckFITlg0bVIwMWFscldjblJ6R21XTDlPdUgKeWNpU0l5TWRsb1BCVkRNSDQ1QTFKWS8xa3grc0V0cFNrK21TMFpkNS9VTVVaaXI1ZU1WMWxXWlppYWJyZDhtVApuR0hvbUhMZnQvWi9jbHA2WGNDVWRrM05PZm90WEFPbkpSakROQWpxNWxQemMrNDM1TlB0ajZYRVRYbVhxM2FnCng3bWhJZ3VtKysyQ2pVWlRWckRPTFZBVTU4TE1Sb0pXczl1Y3drR00vcHYwK2VuMW1WWkZsclVYaEp6bWtQTEsKSU1vRE0wOUI4OTl5Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    server: https://192.168.135.150:16443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURLVENDQWhHZ0F3SUJBZ0lJT1BsNENMY0hiSFV3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TlRFeE1qTXhNVFF6TkRaYUZ3MHlOakV4TWpNeE1UUTRORFphTUR3eApIekFkQmdOVkJBb1RGbXQxWW1WaFpHMDZZMngxYzNSbGNpMWhaRzFwYm5NeEdUQVhCZ05WQkFNVEVHdDFZbVZ5CmJtVjBaWE10WVdSdGFXNHdnZ0VpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCRHdBd2dnRUtBb0lCQVFDbTdIbU4KcEF2eEw4VkJMbklsRmY5LzM1RjJvaHFacGg4a0F4WFAweStFRHFJMTBSUUFKbGZMd2ozOEY0aHpJUzdhbGNjQwpwUmlRWlcxVS95UkdCQUlFcEF2aXh6cWMxRjNmU0t4a1JTcjJzdkxoOHlXN2xrWU9uc3NvOVM3NThDRXRqdnBzCmhoUXF5WSs3V0JTOU5jNHdDTFNxbEM3d3RpYzRqajBMTkdINkZ2TFZhMWZ5aFlRNGdVVitlc21uNThnZEpZNWYKUkFiSm5KN1VwOTdGeGhwOHlmUVZuMVp6b0dMS1M3S3FnclpEdXlhbWdEQjJmVHMxTlNPN3VBZVFIb3l2NElJbwpwbXRCVmtLb2ZUaitQVlAzVXVxMllsZ1ZuQmFLSy9ZQ3VYREFxTWlLK3NEU1FNOHAxTjRYbUQ4ZVcra0lsSFVFCnZjN05kYlArTzRLTzZHQ1pBZ01CQUFHalZqQlVNQTRHQTFVZER3RUIvd1FFQXdJRm9EQVRCZ05WSFNVRUREQUsKQmdnckJnRUZCUWNEQWpBTUJnTlZIUk1CQWY4RUFqQUFNQjhHQTFVZEl3UVlNQmFBRkVBYWt0R0hsa2ZOMy9WVApJQTl5bDUyZkFNNlZNQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUJBUUFydW1leWFCTDQ5SmJuN1pudjE2TkJOdFAwCjBObk1yR09zWnJyVlpQU3NZWjNMelUrTG4rL1ZtTXd1OE1XVUI0dnIvWXQwUlRRaHdQdGV0Zmk2c0szMTNCeXUKbWdtUCthQWxrR0VqSzA4aThDOXlPc3I4bmZrN0pMTm9CUEdUSC93MDl1cEpyTmVyTTlQVTc0SUlYek0zQklFRQpGSk5UOU9PU01sQnd0Qk04K0grSXhIVERuWFUzd2FlMWZlWmJQb1BPZXNIbFhJSTgzK1BMZWZyVGx2dksyS2FEClkzV2x1Z1MwWVVFYjlUZEN3RFRxS1dKWUxIbC9DQS9EWlZMZ0E5THFZaGU4a3FINTl5NTFnRlAyemE0SEs4cnQKNmFaTDNyY0xNaEVkdlc0Mkd5QzhYV3J5Yk5LREJlTWJXc3JBcDNzbmJnaEtIVGV2aWRMQlhMQVVyTE9iCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
    client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFBS0NBUUVBcHV4NWphUUw4Uy9GUVM1eUpSWC9mOStSZHFJYW1hWWZKQU1WejlNdmhBNmlOZEVVCkFDWlh5OEk5L0JlSWN5RXUycFhIQXFVWWtHVnRWUDhrUmdRQ0JLUUw0c2M2bk5SZDMwaXNaRVVxOXJMeTRmTWwKdTVaR0RwN0xLUFV1K2ZBaExZNzZiSVlVS3NtUHUxZ1V2VFhPTUFpMHFwUXU4TFluT0k0OUN6UmgraGJ5MVd0WAo4b1dFT0lGRmZuckpwK2ZJSFNXT1gwUUd5WnllMUtmZXhjWWFmTW4wRlo5V2M2Qml5a3V5cW9LMlE3c21wb0F3CmRuMDdOVFVqdTdnSGtCNk1yK0NDS0taclFWWkNxSDA0L2oxVDkxTHF0bUpZRlp3V2lpdjJBcmx3d0tqSWl2ckEKMGtEUEtkVGVGNWcvSGx2cENKUjFCTDNPelhXei9qdUNqdWhnbVFJREFRQUJBb0lCQUFNOFRZcmZwWCtjL205LwpWL3BuWW13aHlFUmdGZ1hwRmhkdzhUT2dhS2tLK09mMEpUVnZpTlRMNzJtbDNIWThMQmYraytkSzQwbFdrMHowCnpuT0dVUWV5cWxiN1VrNkhRSjZRWG1ScDF0QmJYYUUxbGxpbTFyVjMxeXVmRWw4Sk51REJoeEhOYzRkS0lEMXcKWi9rc1JDZmkzZnI1emxoMHloa3E0S2Rlbk9ZOUV2VDllcE80N1JERGd3b3VnanVIdTJkT3YvNlBGRGtaNStkVgo2YnBRN1kyT2FtN2owUHdCLzk3d3R6TlN1QkNHT3Fnb01CRitjOW45ZjMxSHdjYmdYOGhIWXVVRnAvc3dicFY3Ci9USnVGdlhxOGdsT0hwTExBRzEwZXgvS3NOVDI1dUJHQ2xZWjVvRW5EbEVDd2t5WWFzbmNQM2VDRVpHcDFDQXoKTFRZYmx4RUNnWUVBeloxcGFadlp5ZitPWUtuSkRGcGdrSkhzSnpmd0p3LzJIZ29KU0U2U2tlY0lNRnRTOGZUQQphSVNienVhdHVmNG5wMC9uNE1NaUJYU1VTa3lXMXVaSStNYXYzeFdFRGY5V3YzWTBRQTVidjVGZ2V3SkpJMERrCko3R3o5eUhkZFJDQ1A4ODZWMWNrVGxUTkFnYWFNbVBYSEZsdGgxRlNIeXBoNWtBa2hTUTlLTFVDZ1lFQXo5UGwKemg4bW9hSFRTMlpySDVJTmd0Wit6TXJ5MmZxbUdWeXhVUWRiM1VMTnFBN0F3MkYwdWFtNXF6SlpjUW5Nb0Q0cwp5WjVMQk1LMUd6MzUwSXRPajBkVmV3WWdOc3hjUWg4b3l3bzVCMVRXS2xSWUd3M0VoR3JLeFlZbEFvckp6SXN6Cm9IZkZxTnR2QXZKWWlhcjkxanZ3L1RNMlJHb0lvTXdpcVpmcXV0VUNnWUVBa0g0SlFPMEQvTDc1YUJhSXNZU2wKalpMdU9KVkh6N3VZd24xZERwSWcwQ1ZpRE5Gd2xaWGd6Tkk3eUFjMW1KbnhkZE5pYVFIWDI2dVVOaTVpWmZ5SQoxVUtTL3h0WXBKbUdkWi8wa2Z0RklIZGlMSzlyaERtcTIvWTJPUHAwYlRxL3ZXVzhZdnBiQ2l6dnNIZkJUcDM0CjRmUTZVemlqbEVFdGNTZ3NRZUE0bG0wQ2dZRUFqalEwZTB4dlY0dHVFNXFaMC9sUXkyVFBVSHZSbmZ0V3FlWDEKNGpiQjBlMEM0V3B1MVlHYXArdU5ncW1wNzZHLzVTbUY1ZE10QzYzYTFEQWMvbWhEc2VBaWlsSlB1bzhzMVlXUwptUk4rb0JpWmprODJGaFRla2FpczVHajhhL2ZoU3RjNDFTVC9GNkpHSEdNTTErNHJUK3FsNCtxbHlEd3hlUzBVCnpxeUhUWjBDZ1lFQWgrdWF3WmFoWDRVbll4QWFZNERTYUxKL3hjWDRpWEV3S2JSNVorQ1BpQ0NGaDlNL3dPbDcKbUFrWU16Q1ZTbWxJQi9zMUhlOVRUVGtmMWt4ZzZPVE9DdjNyak13bUtFL1RDa2RENnovdkE1K0xYemNlMHp6WQo5Q0R1Z04rSnBzVFFrdUl0NDdaazFnbVpsUnN5c3VqU2VQNUI5RURVeXJGU1VxdHFQRUtHSWJVPQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=
EOF

            cat > namespace.yaml <<EOF
apiVersion: v1
kind: Namespace
metadata:
  name: ${APP_NAMESPACE}
EOF

            kubectl --kubeconfig "${KUBECONFIG}" apply -f namespace.yaml

            cat > all-halo.yaml <<EOF
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: halo-pvc
  namespace: ${APP_NAMESPACE}
spec:
  storageClassName: nfs-client
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 5Gi

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: halo
  name: halo
  namespace: ${APP_NAMESPACE}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: halo
  template:
    metadata:
      labels:
        app: halo
    spec:
      imagePullSecrets:
      - name: harbor-secret
      containers:
      - image: ${IMAGE_REPO}:v${BUILD_NUMBER}
        imagePullPolicy: IfNotPresent
        name: halo
        ports:
        - name: http
          containerPort: 8090
        args:
        - --spring.r2dbc.url=r2dbc:pool:mysql://192.168.135.155:3306/halo
        - --spring.r2dbc.username=root
        - --spring.r2dbc.password=zgs888
        - --spring.sql.init.platform=mysql
        - --halo.external-url=http://www.zgs.com/
        env:
        - name: JVM_OPTS
          value: "-Xmx256m -Xms256m"
        resources:
          requests:
            cpu: 200m
            memory: 512Mi
          limits:
            cpu: 1000m
            memory: 1Gi
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8090
          initialDelaySeconds: 30
          periodSeconds: 30
          timeoutSeconds: 5
          failureThreshold: 5
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8090
          initialDelaySeconds: 60
          periodSeconds: 30
          timeoutSeconds: 5
          failureThreshold: 5
        volumeMounts:
        - name: halo
          mountPath: /root/.halo2
      volumes:
      - name: halo
        persistentVolumeClaim:
          claimName: halo-pvc

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: halo-svc
  name: halo-svc
  namespace: ${APP_NAMESPACE}
spec:
  ports:
  - name: http
    port: 8090
    protocol: TCP
    targetPort: 8090
  selector:
    app: halo
  type: ClusterIP

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: halo-ingress
  namespace: ${APP_NAMESPACE}
spec:
  ingressClassName: nginx
  rules:
  - host: www.zgs.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: halo-svc
            port:
              number: 8090

---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: halo-hpa
  namespace: ${APP_NAMESPACE}
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: halo
  minReplicas: 1
  maxReplicas: 1
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 0
      selectPolicy: Max
      policies:
      - type: Pods
        value: 1
        periodSeconds: 30
      - type: Percent
        value: 100
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      selectPolicy: Min
      policies:
      - type: Pods
        value: 1
        periodSeconds: 120
      - type: Percent
        value: 25
        periodSeconds: 120
EOF

            kubectl --kubeconfig "${KUBECONFIG}" apply -f all-halo.yaml
            kubectl --kubeconfig "${KUBECONFIG}" -n "${APP_NAMESPACE}" rollout status deployment/halo --timeout=300s
          '''
        }
      }
    }

    stage('查看部署结果') {
      steps {
        container('dockercli') {
          sh '''
            kubectl --kubeconfig "${KUBECONFIG}" -n "${APP_NAMESPACE}" get pod -o wide
            kubectl --kubeconfig "${KUBECONFIG}" -n "${APP_NAMESPACE}" get svc
            kubectl --kubeconfig "${KUBECONFIG}" -n "${APP_NAMESPACE}" get ingress
            kubectl --kubeconfig "${KUBECONFIG}" -n "${APP_NAMESPACE}" get hpa
          '''
        }
      }
    }
  }

  post {
    success {
      echo '流水线执行成功,Halo 已部署到 Kubernetes。'
    }
    failure {
      echo '流水线执行失败,请检查 Jenkins 控制台日志。'
    }
  }
}

#在jenkins中创建流水线,然后将pipeline贴进去即可

prometheus

这种安装方式内置了grafana的可视化仪表盘,部署完成通过已有的可视化仪表盘,通过命名空间即可对Pod进行监控,如果需要监控应用本身的指标,则需要应用暴露/metric指标采集

安装helm

kubectl get nodes
kubectl get ns
helm version
#如果没有则安装,去官网下载安装

添加Prometheus的helm仓库

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

查看 Chart

helm search repo prometheus-community/kube-prometheus-stack

官方社区 Chart 仓库在 prometheus-community/helm-charts下维护

创建命名空间

kubectl create namespace monitoring

准备 values.yaml

vim values.yaml 
grafana:
  enabled: true
  adminUser: admin
  adminPassword: "Admin@123456"
  service:
    type: NodePort
    nodePort: 30300
  persistence:
    enabled: true
    type: pvc
    storageClassName: "nfs-client"
    accessModes:
      - ReadWriteOnce
    size: 5Gi

prometheus:
  service:
    type: NodePort
    nodePort: 30090
  prometheusSpec:
    retention: 15d
    serviceMonitorSelectorNilUsesHelmValues: false
    podMonitorSelectorNilUsesHelmValues: false
    ruleSelectorNilUsesHelmValues: false
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: "nfs-client"
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 20Gi

alertmanager:
  enabled: true
  service:
    type: NodePort
    nodePort: 30903
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: "nfs-client"
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 5Gi

注意:这里的 storageClassName: "nfs-client" 要改成你自己集群里的 StorageClass。

如何创建NFS动态供给的StorageClass可以参考#################

查看你的 StorageClass

kubectl get storageclass

如果你没有 StorageClass,可以先把 storageSpec和 persistence相关配置删掉,先用非持久化方式跑起来。

安装 Prometheus

helm upgrade --install prometheus prometheus-community/kube-prometheus-stack \
  -n monitoring \
  -f values.yaml
#等待Pod启动
kubectl get pods -n monitoring -w
#看到如下状态则正常
prometheus-grafana-xxx                         Running
prometheus-kube-prometheus-operator-xxx        Running
prometheus-kube-state-metrics-xxx              Running
prometheus-prometheus-node-exporter-xxx        Running
prometheus-kube-prometheus-prometheus-0        Running
alertmanager-prometheus-kube-prometheus-alertmanager-0 Running
#查看SVC
kubectl get svc -n monitoring
#查看SVC暴露的端口,通过浏览器去访问Prometheus和Grafana

注意事项

部署之后,prometheus对于一些指标抓取不到,因为kubernetes中的组件默认监听127.0.0.1,需要手动修改配置使其监听指定地址或者0.0.0.0,prometheus才能抓取到指标

!!!!!!!!!!每台master都需要改,改完之后不需要做任何操作,因为静态Pod会自动重新负载!!!!!!!!!!

kube-controller-manager

vi /etc/kubernetes/manifests/kube-controller-manager.yaml

    - --bind-address=127.0.0.1
    #修改为下面的
    - --bind-address=0.0.0.0

kube-etcd

vi /etc/kubernetes/manifests/etcd.yaml
	- --listen-metrics-urls=http://127.0.0.1:2381
	#修改为
	- --listen-metrics-urls=http://0.0.0.0:2381

kube-scheduler

vi /etc/kubernetes/manifests/kube-scheduler.yaml
	- --bind-address=127.0.0.1
	#修改为
	- --bind-address=0.0.0.0

kube-proxy

kubectl -n kube-system edit cm kube-proxy
	metricsBindAddress: 127.0.0.1:10249
	#修改为
	metricsBindAddress: 0.0.0.0:10249
#这个需要手动重载
kubectl -n kube-system rollout restart ds kube-proxy

修改完成之后查看端口是否监听在指定IP地址

ss -antupl | grep -E '(2381|10257|10249|10259)'

配置告警通知

告警规则配置

#以钉钉作为告警媒介为例
	#在钉钉创建账号,然后创建一个群聊,在群聊中添加自定义机器人,创建好之后复制token和secert(安全设置选择标签)
	
#创建告警规则文件
vim halo-alert-rules.yaml
    apiVersion: monitoring.coreos.com/v1
    kind: PrometheusRule
    metadata:
      name: halo-alert-rules
      namespace: monitoring
      labels:
        release: prometheus			#注意这里的标签,使用helm list -n monitoring查询CHART下方的就是标签
    spec:
      groups:
      - name: halo.pod.resource.rules
        rules:
        - alert: HaloPodCpuUsageHigh
          expr: |
            (
              sum by (namespace, pod) (
                rate(container_cpu_usage_seconds_total{
                  namespace="halo",
                  container!="",
                  image!=""
                }[5m])
              )
              /
              sum by (namespace, pod) (
                kube_pod_container_resource_limits{
                  namespace="halo",
                  resource="cpu",
                  unit="core"
                }
              )
            ) * 100 > 80
            and
            sum by (namespace, pod) (
              kube_pod_container_resource_limits{
                namespace="halo",
                resource="cpu",
                unit="core"
              }
            ) > 0
          for: 3m
          labels:
            severity: warning
            namespace: halo
          annotations:
            summary: "Halo Pod CPU 使用率过高"
            description: "Pod {{ $labels.namespace }}/{{ $labels.pod }} CPU 使用率超过 limit 的 80%,当前值:{{ $value }}%"

        - alert: HaloPodMemoryUsageHigh
          expr: |
            (
              sum by (namespace, pod) (
                container_memory_working_set_bytes{
                  namespace="halo",
                  container!="",
                  image!=""
                }
              )
              /
              sum by (namespace, pod) (
                kube_pod_container_resource_limits{
                  namespace="halo",
                  resource="memory",
                  unit="byte"
                }
              )
            ) * 100 > 80
            and
            sum by (namespace, pod) (
              kube_pod_container_resource_limits{
                namespace="halo",
                resource="memory",
                unit="byte"
              }
            ) > 0
          for: 3m
          labels:
            severity: warning
            namespace: halo
          annotations:
            summary: "Halo Pod 内存使用率过高"
            description: "Pod {{ $labels.namespace }}/{{ $labels.pod }} 内存使用率超过 limit 的 80%,当前值:{{ $value }}%"

      - name: k8s.node.rules
        rules:
        - alert: K8sNodeNotReady
          expr: |
            kube_node_status_condition{
              condition="Ready",
              status="true"
            } == 0
          for: 3m
          labels:
            severity: critical
          annotations:
            summary: "K8s 节点 NotReady"
            description: "节点 {{ $labels.node }} 已经超过 3 分钟处于 NotReady 状态"

        - alert: NodeExporterDown
          expr: |
            up{job="node-exporter"} == 0
          for: 3m
          labels:
            severity: critical
          annotations:
            summary: "Node Exporter 掉线"
            description: "节点监控目标 {{ $labels.instance }} 已经超过 3 分钟无法被 Prometheus 抓取"

#应用规则文件
kubectl apply -f halo-alert-rules.yaml

#检查是否生效
kubectl get prometheusrule -n monitoring

#runing后去prometheus的web页面选择rule选项查看规则是否UP,如果是DOWN状态可以等几分钟

告警媒介配置

#配置
vim prometheus-webhook-dingtalk.yaml
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: prometheus-webhook-dingtalk
      namespace: monitoring
    data:
      config.yml: |
        timeout: 5s
        targets:
          webhook1:
            url: https://oapi.dingtalk.com/robot/send?access_token=你的钉钉机器人token	#修改为自己的
            secret: SEC你的钉钉加签secret		#修改为自己的
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: prometheus-webhook-dingtalk
      namespace: monitoring
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: prometheus-webhook-dingtalk
      template:
        metadata:
          labels:
            app: prometheus-webhook-dingtalk
        spec:
          containers:
          - name: prometheus-webhook-dingtalk
            image: timonwong/prometheus-webhook-dingtalk:v2.1.0
            imagePullPolicy: IfNotPresent
            args:
            - --config.file=/etc/prometheus-webhook-dingtalk/config.yml
            - --web.listen-address=:8060
            ports:
            - containerPort: 8060
              name: http
            volumeMounts:
            - name: config
              mountPath: /etc/prometheus-webhook-dingtalk
          volumes:
          - name: config
            configMap:
              name: prometheus-webhook-dingtalk
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: prometheus-webhook-dingtalk
      namespace: monitoring
    spec:
      selector:
        app: prometheus-webhook-dingtalk
      ports:
      - name: http
        port: 8060
        targetPort: 8060
        
#应用
kubectl apply -f prometheus-webhook-dingtalk.yaml

kubectl get pod -n monitoring | grep dingtalk
kubectl get svc -n monitoring | grep dingtalk

#测试钉钉webhook服务是否正常
kubectl run curl-test -n monitoring --rm -it --image=curlimages/curl -- sh

#进入容器后,执行以下测试命令(收到测试消息说明服务没问题)
curl -XPOST http://prometheus-webhook-dingtalk.monitoring.svc:8060/dingtalk/webhook1/send \
-H 'Content-Type: application/json' \
-d '{
  "version": "4",
  "groupKey": "test",
  "status": "firing",
  "receiver": "dingtalk",
  "groupLabels": {"alertname": "测试告警"},
  "commonLabels": {"alertname": "测试告警", "severity": "warning"},
  "commonAnnotations": {"summary": "测试钉钉告警"},
  "alerts": [
    {
      "status": "firing",
      "labels": {"alertname": "测试告警", "severity": "warning"},
      "annotations": {"summary": "测试钉钉告警", "description": "这是一条 Alertmanager 测试消息"}
    }
  ]
}'

#修改helm的alertmanager配置

#导出当前helm的配置
helm get values prometheus -n monitoring > values.yaml

#编辑导出的配置(加入或者修改下面的配置),如果不知道怎么加入或修改,直接把导出的和下面的配置贴给AI
vim values.yaml
    alertmanager:
      config:
        global:
          resolve_timeout: 5m

        route:
          group_by: ['alertname', 'namespace', 'pod', 'node']
          group_wait: 30s
          group_interval: 5m
          repeat_interval: 1h
          receiver: 'dingtalk-default'

          routes:
          - receiver: 'null'
            matchers:
            - alertname = "Watchdog"

          - receiver: 'dingtalk-critical'
            matchers:
            - severity = "critical"
            repeat_interval: 10m

          - receiver: 'dingtalk-warning'
            matchers:
            - severity = "warning"
            repeat_interval: 30m

        receivers:
        - name: 'null'

        - name: 'dingtalk-default'
          webhook_configs:
          - url: 'http://prometheus-webhook-dingtalk.monitoring.svc:8060/dingtalk/webhook1/send'
            send_resolved: true

        - name: 'dingtalk-critical'
          webhook_configs:
          - url: 'http://prometheus-webhook-dingtalk.monitoring.svc:8060/dingtalk/webhook1/send'
            send_resolved: true

        - name: 'dingtalk-warning'
          webhook_configs:
          - url: 'http://prometheus-webhook-dingtalk.monitoring.svc:8060/dingtalk/webhook1/send'
            send_resolved: true

        inhibit_rules:
        - source_matchers:
          - severity = "critical"
          target_matchers:
          - severity =~ "warning|info"
          equal:
          - namespace
          - alertname

        - source_matchers:
          - severity = "warning"
          target_matchers:
          - severity = "info"
          equal:
          - namespace
          - alertname
          
#更新helm
helm upgrade prometheus prometheus-community/kube-prometheus-stack \
  -n monitoring \
  -f values.yaml

nginx+keepalived

nginx

vim /etc/nginx/nginx.conf
  
  upstream ingress_nginx {
        server 192.168.135.151:31051 max_fails=3 fail_timeout=10s;
        server 192.168.135.152:31051 max_fails=3 fail_timeout=10s;
        server 192.168.135.153:31051 max_fails=3 fail_timeout=10s;
        server 192.168.135.154:31051 max_fails=3 fail_timeout=10s;
        server 192.168.135.155:31051 max_fails=3 fail_timeout=10s;
    }

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  www.zgs.com;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
            proxy_pass http://ingress_nginx;
        
            proxy_http_version 1.1;
            proxy_set_header Host www.zgs.com;
        
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
            proxy_set_header X-Forwarded-Proto $scheme;
        
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

keepalived

vim /etc/keepalived/keepalived.conf 
    global_defs {
        router_id master01
        script_user root
        enable_script_security
    }

    vrrp_script chk_nginx {
        script "/etc/keepalived/check_nginx.sh"
        interval 2
        weight -30
        fall 3
        rise 2
    }

    vrrp_instance VI_1 {
        state MASTER  				#备节点改成BACKUP
        interface ens160
        virtual_router_id 51
        priority 120				#备节点优先级设置为100
        advert_int 1

        authentication {
            auth_type PASS
            auth_pass zgs123
        }

        virtual_ipaddress {
            192.168.135.100/24
        }

        track_script {
            chk_nginx
        }
    }

#检测脚本
vim /etc/keepalived/check_nginx.sh 
    #!/bin/bash
    pidof nginx > /dev/null 2>&1
    if [ $? -eq 0 ]; then
        exit 0
    else
        exit 1
    fi

MySQL

部署MySQL

搭建MGR集群

主节点

#修改配置文件
 vim /etc/my.cnf

     server_id=1      #id号要唯一
     gtid_mode=ON
     enforce_gtid_consistency=ON
     master_info_repository=TABLE
     relay_log_info_repository=TABLE
     binlog_checksum=NONE
     log_slave_updates=ON
     log_bin=binlog
     binlog_format=ROW
     transaction_write_set_extraction=XXHASH64
     loose-group_replication_group_name='ce9be252-2b71-11e6-b8f4-00212844f856'
     loose-group_replication_start_on_boot=off
     loose-group_replication_local_address='192.168.135.153:33061'
     loose-group_replication_group_seeds='192.168.135.153:33061,192.168.135.154:33061,192.168.135.155:33061'
     loose-group_replication_bootstrap_group=off
     
#创建同步用户,刷新权限
set sql_log_bin=0;

grant replication slave on *.* to repl@'192.168.135.%' identified by '123456';

flush privileges;

set sql_log_bin=1;

#使用CHANGE MASTER TO语句将server配置为在下次需要从其他成员恢复其状态时,使用group_replication_recovery复制通道的给定凭据。

	#构建group replication集群
    change master to master_user='slave',master_password='Yutian_2026'  for channel 'group_replication_recovery';

#安装group replication插件
set sql_log_bin=0;

grant replication slave on *.* to repl@'192.168.1.%' ident

#行初始引导操作,启动服务器192.168.135.153上mysql的group replication
set global group_replication_bootstrap_group=on;

#启动mgr集群
start group_replication;

set global group_replication_bootstrap_group=OFF;

#查看MGR状态
select * from performance_schema.replication_group_members;

从节点

#修改配置文件
vim /etc/my.cnf

     server_id=1      #id号要唯一
     gtid_mode=ON
     enforce_gtid_consistency=ON
     master_info_repository=TABLE
     relay_log_info_repository=TABLE
     binlog_checksum=NONE
     log_slave_updates=ON
     log_bin=binlog
     binlog_format=ROW
     transaction_write_set_extraction=XXHASH64
     loose-group_replication_group_name='ce9be252-2b71-11e6-b8f4-00212844f856'
     loose-group_replication_start_on_boot=off
     loose-group_replication_local_address='192.168.135.154:33061' #把这里改成所在节点的IP
     loose-group_replication_group_seeds='192.168.135.153:33061,192.168.135.154:33061,192.168.135.155:33061'
     loose-group_replication_bootstrap_group=off
     
#创建同步用户,刷新权限
set sql_log_bin=0;

grant replication slave on *.* to repl@'172.17.10.%' identified by '123456';

flush privileges;

set sql_log_bin=1;

#构建group replication集群
change master to master_user='repl',master_password='Yutian_2025'  for channel 'group_replication_recovery';

#安装group replication插件
install plugin group_replication soname 'group_replication.so';

#把实例添加到之前的复制组
set global group_replication_allow_local_disjoint_gtids_join=ON;
 
start group_replication;

#查看复制组状态
select * from performance_schema.replication_group_members;

#单主模式下,查看主节点
select b.member_host the_master,a.variable_value master_uuid
     from performance_schema.global_status a
     join performance_schema.replication_group_members b
     on a.variable_value = b.member_id
     where variable_name='group_replication_primary_member';


0
博主关闭了所有页面的评论