Chuyển tới nội dung

Triển khai web ecommerce (woocommerce) chạy trên nền tảng k8s

Mô hình triển khai

HostnameOSIPMục đích
k8s-master-01Ubuntu 24.0410.100.1.21Node control plane
k8s-worker-02Ubuntu 24.0410.100.1.22Node worker 01
k8s-worker-03Ubuntu 24.0410.100.1.23Node worker 02
srv025-npm-rancherUbuntu 24.0410.100.1.25Server cài docker để cài npm và rancher
srv024-nfs-serverUbuntu 24.0410.100.1.24NFS Server
rancher10.100.1.55Rancher
npm-server10.100.1.54

Hệ thống đã cài đặt xong cụm cluster k8s gồm 3 node (1 node control plane và 2 node worker).

Các bước triển khai

I. Cài đăt môi trường

1. Cài đặt nfs-server

Trên server 10.100.1.24 thực hiện các bước sau:

sudo apt install nfs-server -y
sudo mkdir -p /opt/nfs_data/{data,config}
sudo chown -R nobody:nogroup /opt/nfs_data
sudo chmod -R 777 /opt/nfs_data/data
sudo echo "/opt/nfs_data/data 10.100.1.0/24(rw,sync,no_subtree_check,no_root_squash)" >>  /etc/exports 
sudo echo "/opt/nfs_data/config 10.100.1.0/24(rw,sync,no_subtree_check,no_root_squash)" >>  /etc/exports 
sudo exportfs -rav
sudo systemctl restart nfs-server
sudo systemctl restart nfs-kernel-server

Cài đặt và cấu hình NFS client trên tất cả các node của cụm k8s

sudo apt install nfs-common -y

2. Triển khai Dynamic Provisioner trên node master (k8s-master)

Add repo và update

helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm repo update

Cài vào kube-system, đặt tên storageClass nfs-storage

helm upgrade --install nfs-provisioner \
nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
-n woocommerce \
--create-namespace \
--set nfs.server=10.100.1.24 \
--set nfs.path=/opt/nfs_data/data \
--set storageClass.name=nfs-storage \
--set storageClass.defaultClass=false

Kiểm tra

kubectl -n tonytechlab-storage get pods -l app=nfs-subdir-external-provisioner
kubectl get sc

Triển khai các thành phần chính của hệ thống

1. Tạo namespace
01-namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: woocommerce

Áp dụng: kubectl apply -f 01-namespace.yaml
Tạo pvc tĩnh chứa các file config
02-config-pvc.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: config-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteMany
  nfs:
    server: 10.100.1.24
    path: /opt/nfs_data/config
  persistentVolumeReclaimPolicy: Retain
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: config-pvc
  namespace: woocommerce
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 1Gi
  volumeName: config-pv

Áp dụng: kubectl apply -f 02-pvc-config.yaml
Kiểm tra: kubectl get pv
kubectl get pvc -n woocommerce

2. Triển khai MariaDB

03-pvcmariadb.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mariadb-data-pvc
  namespace: woocommerce
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  storageClassName: nfs-storage

Áp dụng kubectl apply -f 03-pvc-mariadb.yaml

04-mariadb-confimap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: mariadb-configmap
  namespace: woocommerce
data:
  my.cnf: |
    [mysqld]
    max_connections=200
    sql_mode=STRICT_ALL_TABLES

Áp dụng: kubectl apply -f 04-mariadb-confimap.yaml

05-mariadb-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mariadb-secret
  namespace: woocommerce
type: Opaque
data:
  mysql-root-password: Um9vdFBhc3NAMjAyNQ==
  mysql-user: YmVfd29vX3VzZXI=
  mysql-password: MkJKb2NzZnNMR0FfNmpuQg==
  mysql-database: YmVfd29vX2Ri

Áp dụng: kubectl apply -f 05-mariadb-secret.yaml

06-mariadb-stateful.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mariadb
  namespace: woocommerce
spec:
  serviceName: mariadb
  replicas: 1
  selector:
    matchLabels:
      app: mariadb
  template:
    metadata:
      labels:
        app: mariadb
    spec:
      containers:
        - name: mariadb
          image: mariadb:11.3
          ports:
            - containerPort: 3306
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mariadb-secret
                  key: mysql-root-password
            - name: MYSQL_DATABASE
              valueFrom:
                secretKeyRef:
                  name: mariadb-secret
                  key: mysql-database
            - name: MYSQL_USER
              valueFrom:
                secretKeyRef:
                  name: mariadb-secret
                  key: mysql-user
            - name: MYSQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mariadb-secret
                  key: mysql-password
          volumeMounts:
            - name: mariadb-data
              mountPath: /var/lib/mysql
            - name: config-pvc
              mountPath: /docker-entrypoint-initdb.d   
            - name: mariadb-configmap
              mountPath: /etc/mysql/conf.d
      volumes:
        - name: mariadb-data
          persistentVolumeClaim:
            claimName: mariadb-data-pvc
        - name: config-pvc
          persistentVolumeClaim:
            claimName: config-pvc
        - name: mariadb-configmap
          configMap:
            name: mariadb-configmap

Áp dụng: kubectl apply -f 06-mariadb-stateful.yaml
kubectl get po, sts -n woocommerce

07-mariadb-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: mariadb
  namespace: woocommerce
spec:
  ports:
    - port: 3306
      targetPort: 3306
  selector:
    app: mariadb
  clusterIP: None  

App dụng: kubectl apply -f 07-mariadb-service.yaml
Kiểm tra: kubectl get po,sts,svc -n woocommerce

3. Triển khai Redis

08-redis-stateful.yaml

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
  namespace: woocommerce
spec:
  serviceName: redis
  replicas: 1
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:7.4
        args: [ "redis-server", "/usr/local/etc/redis/redis.conf" ]
        ports:
        - containerPort: 6379
        env:
        volumeMounts:
        - name: config-pvc
          mountPath: /usr/local/etc/redis
        - name: redis-data
          mountPath: /data
      volumes:
      - name: config-pvc
        persistentVolumeClaim:
          claimName: config-pvc
  volumeClaimTemplates:
  - metadata:
      name: redis-data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: nfs-storage
      resources:
        requests:
          storage: 1Gi

Áp dụng: kubectl apply -f 08-redis-stateful.yaml

09-redis-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: redis
  namespace: woocommerce
spec:
  selector:
    app: redis
  ports:
  - name: redis
    port: 6379
    targetPort: 6379
  clusterIP: None

Áp dụng: kubectl apply -f 09-redis-service.yaml
Kiểm tra: kubectl get po,svc,sts -n woocommerce

4. Triển khai woocommerce backend

10-wordpress-pvc.yaml

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wordpres-source-pvc
  namespace: woocommerce
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  storageClassName: nfs-storage

11-wordpres-secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: wordpres-secret
  namespace: woocommerce
type: Opaque
stringData:
  mysql-host: "mariadb.woocommerce.svc.cluster.local"
  redis-host: "redis.woocommerce.svc.cluster.local"
  wp-auth-key: "HN 6uo{dDGOKKwfjSA{@k>u^[=>^p,2shLbU,%$U%*:-&qy}Pkn^]dep%A*hfE|V"
  wp-secure-auth-key: ">gcmuCuxI-yhl]hE0uQ3Q0Ilj=cB3s%q. i.$aSBwGfu{*Oj#SqZ=iPweU,HLLU>"
  wp-logged-in-key: ")2z479gF0 hdiVnAB[Cz4GHtZU_8B>l-mGort2xCg?E!S9zYX<->iu/2tGVc}7pf"
  wp-nonce-key: "u`|r|ty#2T(-#{V#n+diN=ZHQjh fs8F,d*d1b]!Pw)bB^)+tl=l>!@chm(Qjq|d"
  wp-auth-salt: "|7$K+PhxA-z9(oK[7%V7ZC-zP`adx_6;!3- +$A:o$aF-YpAyVOJtgP*.t)yrnS@"
  wp-secure-auth-salt: "hdNNnU~m-jJ1dC+Ba19E#9]q#yeK}^LtehHOzpz(f+rtPAku!kd05ll>97N~{[$*"
  wp-logged-in-salt: "h?I|m/}.+1p ]:5v Y/7z;j5%=X;D7,{XNY/2^cGQ/dH_`**-|{j+;3^p<icc-Bd"
  wp-nonce-salt: "<[$)~_Br7W:l&CjTK_40F ?yjG/4@vZ,%l9k-)Dtl%qVW0=#-N;qIhT FjP-oP-]"

Lưu ý: Nếu dữ liệu đưa vào đã đươc mã hóa base64 thì từ khóa stringData sẽ được thay bằng data, Nếu dúng từ khóa stingData thì dữ liệu sẽ được k8s mã hóa qua thuật toán base64 trước khi đưa vào secrect

Áp dụng: kubectl apply -f 11-wordpres-secret.yaml

12-wordpress-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: woocommerce-backend
  namespace: woocommerce
spec:
  replicas: 1
  selector:
    matchLabels:
      app: woocommerce-backend
  template:
    metadata:
      labels:
        app: woocommerce-backend
    spec:
      containers:
      - name: wordpress
        image: beobeo/openlitespeed-wordpress:v2
        ports:
        - containerPort: 80
        env:
        - name: WORDPRESS_DB_HOST
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: mysql-host
        - name: WORDPRESS_DB_USER
          valueFrom:
            secretKeyRef:
              name: mariadb-secret
              key: mysql-user
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mariadb-secret
              key: mysql-password
        - name: WORDPRESS_DB_NAME
          valueFrom:
            secretKeyRef:
              name: mariadb-secret
              key: mysql-database
        - name: AUTH_KEY
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: wp-auth-key
        - name: SECURE_AUTH_KEY
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: wp-secure-auth-key
        - name: LOGGED_IN_KEY
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: wp-logged-in-key
        - name: NONCE_KEY
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: wp-nonce-key     
        - name: AUTH_SALT
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: wp-auth-salt    
        - name: SECURE_AUTH_SALT
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: wp-secure-auth-salt   
        - name: LOGGED_IN_SALT
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: wp-logged-in-salt      
        - name: NONCE_SALT
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: wp-nonce-salt
        - name: REDIS_HOST
          valueFrom:
            secretKeyRef:
              name: wordpres-secret
              key: redis-host
        - name: REDIS_PORT
          value: "6379"
        - name: WP_ENV
          value: "production"
        volumeMounts:
        - name: wp-config
          mountPath: /var/www/vhost/localhost/wp-config.php
          subPath: wp-config.php
        - name: wp-source
          mountPath: /var/www/vhost/localhost
      volumes:
      - name: wp-config
        persistentVolumeClaim:
          claimName: config-pvc
      - name: wp-source
        persistentVolumeClaim:
          claimName: wordpress-source-pvc

Áp dụng: kubectl apply -f 12-wordpress-deployment.yaml

Triển khai clusterip để gọi trong nội bố
13-wordpress-cluserip.yaml

apiVersion: v1
kind: Service
metadata:
  name: woocommerce-backend-cluster
  namespace: woocommerce
spec:
  selector:
    app: woocommerce-backend
  ports:
  - name: http
    port: 80
    targetPort: 80
  - name: https
    port: 443
    targetPort: 443
  - name: admin
    port: 7080
    targetPort: 7080
  type: ClusterIP

Áp dụng: kubectl apply -f 13-wordpress-cluserip.yaml
Triển khai Nodeport để truy cập từ ngoài vào và forward port qua NPM
14-wordpress-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: woocommerce-backend-nodeport
  namespace: woocommerce
spec:
  selector:
    app: woocommerce-backend
  ports:
  - name: http
    port: 80
    targetPort: 80
    nodePort: 30080
  - name: https
    port: 443
    targetPort: 443
    nodePort: 30443
  - name: admin
    port: 7080
    targetPort: 7080
    nodePort: 30780
  type: NodePort

Áp dụng: kubectl apply -f 14-wordpress-nodeport.yaml
Kiểm tra hệ thống: kubectl get pod,svc,deploy,sts -n woocommerce

5. Triển khai frontend nodejs
Ta đã có image beobeo/frontend-nodejs:v1 chứa frontend
15-frontend-configmap.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: frontend-config
  namespace: woocommerce
data:
  REACT_APP_WOOCOMMERCE_URL: "https://be.diendo.pro.vn"
  REACT_APP_WC_CONSUMER_KEY: "ck_d0beb27226a3831d6b7e4ac6e046a5443fc1e85a"
  REACT_APP_WC_CONSUMER_SECRET: "cs_2b97af95a2a5b110f57e7bb63f5e7cb1082d8f1f"
  REACT_APP_API_BASE_URL: "https://be.diendo.pro.vn/wp-json/wc/"

Áp dụng: kubectl apply -f 15-frontend-configmap.yaml

16-frontend-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-nodejs
  namespace: woocommerce
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend-nodejs
  template:
    metadata:
      labels:
        app: frontend-nodejs
    spec:
      containers:
      - name: frontend-nodejs
        image: beobeo/frontend-nodejs:v1
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        envFrom:
        - configMapRef:
            name: frontend-config

Áp dụng: kubectl apply -f 16-frontend-deployment.yaml

17-frontend-service-clusterip.yaml

apiVersion: v1
kind: Service
metadata:
  name: frontend-clusterip
  namespace: woocommerce
spec:
  selector:
    app: frontend-nodejs
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
  type: ClusterIP

Áp dụng: kubectl apply -f 17-frontend-service-clusterip.yaml

18-frontend-service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: frontend-nodeport
  namespace: woocommerce
spec:
  selector:
    app: frontend-nodejs
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30090
    protocol: TCP
  type: NodePort

Áp dụng: kubectl apply -f 18-frontend-service-nodeport.yaml

Kiểm tra lai: kubectl get pod, svc, sts, deploy -n woocommerce

Đến đây ta có thể truy cập vào frontend và backend qua <IP_Node>:<PORT NodePort>
Tiếp theo ta sẽ tiến hành cài đặt các thành phần khác như Nginx Proxy, Rancher, Backup, Monitoring.

3. Triển khai các thành phần khác của hệ thống

1. Triển khai Nginx Proxy Manager
Trên onpremise tôi dùng Nginx Proxy Manager để làm Proxy đứng sau firewall để public các dịch vụ ra ngoài Internet.
Triển khai nginx proxy manager qua docker trên server 10.100.1.25

1.1 Tạo lớp mạng mới thông qua card mạng

docker network create -d macvlan --subnet=10.100.1.0/24 --ip-range=10.100.1.0/28 --gateway=10.100.1.8 -o macvlan_mode=bridge -o parent=ens160 proxy_network

1.2 Triển khai nginx proxy manager qua docker compose

file docker-composer.yaml

version: '3.9'
services:
  nginx-proxy-manager:
    image: 'jc21/nginx-proxy-manager:latest'
    container_name: nginx-proxy-manager
    restart: always
    ports:
      - '80:80'         # HTTP
      - '81:81'         # Web UI
      - '443:443'       # HTTPS
    environment:
      DB_MYSQL_HOST: "10.100.1.54"
      DB_MYSQL_PORT: 3306
      DB_MYSQL_USER: "npm"
      DB_MYSQL_PASSWORD: "npm_password"
      DB_MYSQL_NAME: "npm_db_1"
    volumes:
      - /opt/data/nginx/data:/data
      - /opt/data/nginx/letsencrypt:/etc/letsencrypt
    networks:
      proxy_network:
        ipv4_address: 10.100.1.55

  npm-db-1:
    image: 'mariadb:10.5'
    container_name: npm-db-1
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: 'npm_rootpass'
      MYSQL_DATABASE: 'npm_db_1'
      MYSQL_USER: 'npm'
      MYSQL_PASSWORD: 'npm_password'
    volumes:
      - /opt/data/mysql:/var/lib/mysql
    networks:
      proxy_network:
        ipv4_address: 10.100.1.54
networks:
  proxy_network:
    external: true

docker compose up -d

1.3 Cấu hình domain và nginx proxy manager
Truy cập vào địa chỉ 10.100.1.55:81 để cấu hình npm dùng Email: [email protected] Password: changeme để đăng nhập vào để cấu hình.
Vào thay đổi email và password.

Cấu hình ssl trên NPM

Bấm vào Add SSL Certificate

Login vào Cloudflare để lấy API Token

Chọn Create Token

Chọn Edit zone DNS -> Use template

Chọn domain trong phần Zone Resourcer rồi chọn Continue to Summary

Chọn Create Token

Sau khi nhận được API Token copy vào NPM

Bấm Save để add domain

Add host để truy cập npm từ internet
Host -> Proxy Hosts

Từ đây có thể vào nginx proxy manger qua địa chỉ https://npm.diendo.pro.vn

Cấu hình tên miền để web có thể vào từ ngoài internet
Host -> Proxy Hosts

Chon SSL cho web.

Kiểm tra web từ ngoài vào: https://fe.diendo.pro.vn

2. Triển khai rancher
2.1 Triển khai rancher nằm ngoài cụm kubernetes để quản lý

thêm đoạn sau vào docker-compose.yaml

rancher:
    image: rancher/rancher:v2.12.0
    container_name: rancher
    restart: always
    privileged: true
    environment:
      CATTLE_BOOTSTRAP_PASSWORD: "BeoBeo@2025"
    volumes:
      - /opt/data/rancher/data:/var/lib/rancher
      - /opt/data/rancher/ui:/usr/share/rancher/ui
    networks:
      proxy_network:
        ipv4_address: 10.100.1.56
    ports:
      - "443:443"

sau đó triển khai: docker compose up -d
2.2 Cấu hình domain trên nginx proxy manager
Add Proxy Host

có thể truy cập vào rancher qua địa chỉ https://rancher.diendo.pro.vn

2.3 Phân quyền cho nhóm Dev, DevOps trong Rancher

Đăng nhập vào Rancher với quyền Admin

3. Triển khai hệ thống Monitoring, Alert sử dụng Prometheus – Grafana
3.1 Tạo namespace và add repo chart

kubectl create ns monitoring

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

3.2 Cấu hình values-prom.yaml

01-values-prom.yaml

prometheus:
  prometheusSpec:
    storageSpec:
      volumeClaimTemplate:
        spec:
          storageClassName: nfs-storage
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 10Gi
    additionalScrapeConfigs:
      - job_name: 'kube-state-metrics'
        honor_timestamps: true
        scrape_interval: 30s
        scrape_timeout: 10s
        metrics_path: /metrics
        scheme: http
        kubernetes_sd_configs:
        - role: endpoints
        relabel_configs:
        - source_labels: [__meta_kubernetes_service_label_app_kubernetes_io_name]
          regex: kube-state-metrics
          action: keep
  service:
    type: NodePort
    nodePort: 30900
  additionalService:
    enabled: true
    name: prometheus-clusterip
    type: ClusterIP
    port: 9090

alertmanager:
  alertmanagerSpec:
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: nfs-storage
          accessModes: ["ReadWriteOnce"]
          resources:
            requests:
              storage: 2Gi
  service:
    type: NodePort
    nodePort: 30902
  additionalService:
    enabled: true
    name: alertmanager-clusterip
    type: ClusterIP
    port: 9093

grafana:
  persistence:
    enabled: true
    storageClassName: nfs-storage
    accessModes: ["ReadWriteOnce"]
    size: 5Gi
  service:
    type: NodePort
    nodePort: 30901
  additionalService:
    enabled: true
    name: grafana-clusterip
    type: ClusterIP
    port: 80

ingress:
  enabled: false

kube-state-metrics:
  enabled: true
  prometheusScrape: true
  rbac:
    create: true
  resources:
    requests:
      cpu: 50m
      memory: 100Mi
    limits:
      cpu: 100m
      memory: 200Mi

prometheus-node-exporter:
  enabled: true

# Bổ sung ServiceMonitor cho kube-controller-manager & kube-scheduler
kubeControllerManager:
  enabled: true
  service:
    enabled: true
    port: 10257
    targetPort: 10257
    type: ClusterIP
    name: kube-controller-manager
    selector:
      component: kube-controller-manager

kubeScheduler:
  enabled: true
  service:
    enabled: true
    port: 10259
    targetPort: 10259
    type: ClusterIP
    name: kube-scheduler
    selector:
      component: kube-scheduler

3.3 Cài đặt kube-prometheus-stack

helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \
  -n monitoring --create-namespace \
  -f 01-values-prom.yaml

3.4 Bật servicemonitor-kube

02-servicemonitor-kube-components.yaml

apiVersion: v1
kind: Service
metadata:
  name: kube-controller-manager
  namespace: kube-system
  labels:
    k8s-app: kube-controller-manager
spec:
  ports:
    - name: http-metrics
      port: 10257
      targetPort: 10257
  selector:
    component: kube-controller-manager
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: kube-controller-manager
  namespace: monitoring
  labels:
    release: monitoring
spec:
  selector:
    matchLabels:
      k8s-app: kube-controller-manager
  namespaceSelector:
    matchNames:
      - kube-system
  endpoints:
    - port: http-metrics
      scheme: https
      tlsConfig:
        insecureSkipVerify: true
      interval: 30s
---
apiVersion: v1
kind: Service
metadata:
  name: kube-scheduler
  namespace: kube-system
  labels:
    k8s-app: kube-scheduler
spec:
  ports:
    - name: http-metrics
      port: 10259
      targetPort: 10259
  selector:
    component: kube-scheduler
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: kube-scheduler
  namespace: monitoring
  labels:
    release: monitoring
spec:
  selector:
    matchLabels:
      k8s-app: kube-scheduler
  namespaceSelector:
    matchNames:
      - kube-system
  endpoints:
    - port: http-metrics
      scheme: https
      tlsConfig:
        insecureSkipVerify: true
      interval: 30s

Áp dụng: kubectl apply -f 02-servicemonitor-kube-components.yaml

3.5 Dùng Nginx Proxy Manager để public các dịch vụ ra internet

Làm tương tự với các domain:
grafana.diendo.pro.vn
promtheus.diendo.pro.vn
alert.diendo.pro.vn

4. Triển khai hệ thống Horizontal Pod Autoscaler (HPA)
Triển khai hệ thống HPA, yêu cầu như sau:
1. Khi CPU trên 75%, RAM trên 85%, CCU đặt 2000 thì sẽ scale Backend
2. Khi CPU trên 70%, RAM trên 80% CCU đạt 1500 thì sẽ scale Frontend
3. Khi CPU trên 85%, RAM trên 90% thì tăng statefulset mariadb

4.1 Đảm bảo Metrics API đang hoạt động

kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes" | jq

Có dữ liệu CPU, memory thì đã OK

4.2 Tạo HPA cho Backend và Frontend bằng CPU và RAM

01-hpa-backend.yaml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: backend-hpa
  namespace: woocommerce
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: woocommerce-backend
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 75
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 85

Áp dụng: kubectl apply -f 01-hpa-backend.yaml

02-hpa-frontend.yaml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: frontend-hpa
  namespace: woocommerce
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: frontend-nodejs
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

Áp dụng: kubectl apply -f 02-hpa-frontend.yaml

03-hpa-statefulset.yaml

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: mariadb-hpa
  namespace: woocommerce
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: StatefulSet
    name: mariadb
  minReplicas: 1
  maxReplicas: 5
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 85
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 90

Áp dụng: kubectl apply -f 03-hpa-mariadb.yaml

4.3 Thềm điều kiện Concurrent User (CCU)

Backend (scale khi CCU ≥ 2000)
Frontend (scale khi CCU ≥ 1500)

Sẽ nghiên cứu triển khai sau.

4.4 Triển khai kuma và cảnh báo qua email khi vượt quá ngưỡng alert

Cài đặt kuma bằng helm

helm repo add kuma https://kumahq.github.io/charts
helm repo update

helm upgrade --install kuma kuma/kuma \
  --namespace kuma-system \
  --create-namespace \
  --set crds.enabled=true

kubectl get pods -n kuma-system
NAME                                  READY   STATUS    RESTARTS   AGE
kuma-control-plane-65d9c9cc9f-r7trj   1/1     Running   0          53s

Bật Prometheus Metrics cho Kuma

03-mesh-metrics.yaml

apiVersion: kuma.io/v1alpha1
kind: Mesh
metadata:
  name: default
spec:
  metrics:
    enabledBackend: prometheus-1
    backends:
      - name: prometheus-1
        type: prometheus
        conf:
          port: 5670

Áp dụng: kubectl apply -f 03-mesh-metrics.yaml

Cấu hình AlertManager gửi email

04-alertmanager.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: alertmanager-config
  namespace: monitoring
  labels:
    app: alertmanager
data:
  alertmanager.yaml: |
    global:
      resolve_timeout: 5m
      smtp_smarthost: 'smtp.gmail.com:587'
      smtp_from: '[email protected]'
      smtp_auth_username: '[email protected]'
      smtp_auth_password: 'C5-FB-2E-60-AC-BF'

    route:
      receiver: email-alert
      group_wait: 30s
      group_interval: 5m
      repeat_interval: 3h

    receivers:
    - name: email-alert
      email_configs:
      - to: '[email protected]'
        send_resolved: true

Áp dụng: kubectl -n monitoring apply -f 04-alertmanager.yaml

Viết Alert Rules cho Kuma

Cảnh báo khi letency > 500ms hoặc error rate > 5%

05-kuma-alert-rules.yaml

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: kuma-alert-rules
  namespace: monitoring
spec:
  groups:
  - name: kuma.rules
    rules:
    - alert: HighLatency
      expr: histogram_quantile(0.95, rate(kuma_dp_requests_latency_seconds_bucket[5m])) > 0.5
      for: 2m
      labels:
        severity: warning
      annotations:
        summary: "High request latency detected"
        description: "95th percentile latency > 500ms"
    - alert: HighErrorRate
      expr: rate(kuma_dp_requests_errors_total[5m]) / rate(kuma_dp_requests_total[5m]) > 0.05
      for: 2m
      labels:
        severity: critical
      annotations:
        summary: "High request error rate detected"
        description: "Error rate > 5%"

Tạo Grafana Dashboard

kuma-grafana-dashboard.json

{
  "id": null,
  "title": "Kuma Service Mesh Metrics",
  "tags": ["kuma", "service-mesh"],
  "timezone": "browser",
  "schemaVersion": 36,
  "version": 1,
  "refresh": "30s",
  "panels": [
    {
      "type": "graph",
      "title": "Request Latency (p95)",
      "targets": [
        {
          "expr": "histogram_quantile(0.95, rate(kuma_dp_requests_latency_seconds_bucket[5m]))",
          "legendFormat": "{{service}}"
        }
      ],
      "yaxes": [
        { "format": "s", "label": "Latency (s)" },
        { "format": "short" }
      ]
    },
    {
      "type": "graph",
      "title": "Request Throughput (RPS)",
      "targets": [
        {
          "expr": "rate(kuma_dp_requests_total[1m])",
          "legendFormat": "{{service}}"
        }
      ],
      "yaxes": [
        { "format": "reqps", "label": "Requests/s" },
        { "format": "short" }
      ]
    },
    {
      "type": "graph",
      "title": "Error Rate (%)",
      "targets": [
        {
          "expr": "rate(kuma_dp_requests_errors_total[5m]) / rate(kuma_dp_requests_total[5m]) * 100",
          "legendFormat": "{{service}}"
        }
      ],
      "yaxes": [
        { "format": "percent", "label": "Error Rate" },
        { "format": "short" }
      ]
    }
  ]
}

Vào grafana -> Dashboard -> New -> Import

Đã có Dashboard để theo dõi

5. Backup và Restore trên công cụ Velero

5.1 Cài đặt MiniO

Triển khai trên nfs-server
Cài đặt docker để sử dụng

sudo apt update
sudo apt install apt-transport-https ca-certificates curl software-properties-common -y
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list> /dev/null

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose -y

#add user vào nhóm docker
sudo usermod -aG docker iadmin

Do server nfs phân vùng để /opt là lớn nhất nên cần chuyển vị trí lưu trữ của docker sang /opt

sda                     8:0    0  200G  0 disk 
├─sda1                  8:1    0    1M  0 part 
├─sda2                  8:2    0    2G  0 part /boot
└─sda3                  8:3    0   98G  0 part 
  ├─vg01-lv--01--root 252:0    0   40G  0 lvm  /
  ├─vg01-lv--01--opt  252:1    0   50G  0 lvm  /opt
  └─vg01-lv--03--swap 252:2    0    8G  0 lvm  [SWAP]

edit file /etc/docker/daemon.json

{
  "data-root":"/opt/docker",
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "bip": "172.17.0.1/24", 
  "storage-driver": "overlay2"
}

Tạo thư mục /opt/docker và phân quyền

sudo mkdir /opt/docker
sudo chown -R iadmin:docker /opt/docker/
sudo chmod -R 775 /opt/docker
sudo systemctl restart docker.service docker.socket 

Tạo lớp mạng để thực hiện kết nối với hệ thống

docker network create -d macvlan --subnet=10.100.1.0/24 --ip-range=10.100.1.0/28 --gateway=10.100.1.8 -o macvlan_mode=bridge -o parent=ens160 backup_network

Tạo thư mục chứa dữ liệu backup

sudo mkdir /opt/storage

Tạo file docker-compose.yaml để triển khai MiniO

version: '3'
services:
  minio:
    image: minio/minio
    container_name: minio
    ports:
      - "9000:9000"
      - "9001:9001"
    volumes:
      - /opt/storage:/data
    environment:
      MINIO_ROOT_USER: beobeo
      MINIO_ROOT_PASSWORD: BeoBeo123@123a
    command: server --console-address ":9001" /data
    networks:
      storage_network:
	ipv4_address: 10.100.1.58
networks:
  storage_network:
    external: true

Triển khai: docker compose up -d

Dung NPM để public minio ra internet

Login vào với username và password như trong file docker-compose.yml.
Tạo bucket để chưa backup.

5.2 Tạo xác thực MiniO

Tại máy khác (máy cải rancher) ta tiến hành cài mc-client

Bước 1: Cài đặt MiniO Client

wget https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo cp mc /usr/local/bin/mc

Bước 2: Cấu hình Alias kết nối đến MiniO Server

# mc alias set <TÊN_ALIAS>  <ACCESS_KEY_GỐC> <SECRET_KEY_GỐC>
mc alias set beobeo-minio http://10.100.1.58:9000 beobeo BeoBeo123@123a

Bước 3: Tạo Service Account (Access Key & Secret Key mới)

# mc admin user svcacct add <TÊN_ALIAS> <TÊN_USER_SỞ_HỮU>
mc admin user svcacct add beobeo-minio beobeo
Kết quả:
Access Key: KSOWE6IW6G7M0ORM7OG9
Secret Key: PH7+kCuOObxWoyVaT5k+t+egcrSVeQIF1z2jc0Su
Expiration: no-expiry

5.3 Cài đặt Velero client thực hiện trên node master

Ta truy cập trực tiếp vào Releases · vmware-tanzu/velero để lấy bản mới nhất

# wget https://github.com/vmware-tanzu/velero/releases/download/v1.17.0-rc.1/velero-v1.17.0-rc.1-linux-amd64.tar.gz
# tar -xvf velero-*
# sudo cp velero-v1.17.0-rc.1-linux-amd64/velero /usr/local/bin

5.4 Cấu hình các biến MiniO Thực hiện trên node master

cat << EOF > ./credentials-velero
[default]
aws_access_key_id=KSOWE6IW6G7M0ORM7OG9
aws_secret_access_key=PH7+kCuOObxWoyVaT5k+t+egcrSVeQIF1z2jc0Su
EOF

5.5 Cài đặt MiniO làm backend lưu trữ các bạn sao lưu của kubernetes

Thực hiện trên node master

velero install \
	--provider aws \
	--plugins velero/velero-plugin-for-aws:v1.10.0 \
	--bucket beobeo-k8s-backup \
	--namespace velero \
	--secret-file ./credentials-velero \
	--use-volume-snapshots=false \
	--backup-location-config region=minio,s3ForcePathStyle="true",s3Url=http://10.100.1.58:9000

Đến đây ta có thể backup và restore tài nguyên trên k8s

Backup tài nguyên:

# Backup 1 namespace cụ thể
velero backup create backup-woocommerce-20250919 --include-namespaces woocommerce
# Kiểm tra trạng thái của backup
velero backup describe backup-woocommerce-20250919
#Backup cả cụm k8s
velero backup create full-cluster-except-system-20250919 --exclude-namespaces kube-system,velero
# Kiểm tra trạng thái của backup
velero backup describe full-cluster-except-system-20250919

Restore

# Restore toàn bộ từ môt bản backup
# velero restore create --from-backup <TÊN_BẢN_BACKUP>
velero restore create --from-backup full-cluster-except-system-20250919
velero restore describe full-cluster-except-system-20250919
# Restore một namespace cụ thể
# velero restore create <TÊN_RESTORE> --from-backup <TÊN_BẢN_BACKUP> --include-namespaces <TÊN_NAMESPACE>
velero restore create restore-woocommerce-only --from-backup full-cluster-except-system-20250919 --include-namespaces woocommerce
# Restore từ một namespace này sang một namespace khác
# velero restore create <TÊN_RESTORE> \
    --from-backup <TÊN_BACKUP> \
    --namespace-mappings <NAMESPACE_CŨ>:<NAMESPACE_MỚI>
velero restore create restore-prod-to-staging \
      --from-backup backup-production-app \
      --namespace-mappings production:staging
# Kiểm tra trạng thái
# Xem danh sách
velero restore get

# Xem chi tiết một lần phục hồi
velero restore describe <TÊN_RESTORE>

Lập lịch Backup tự động

# Backup vào lúc 2 giờ sáng mỗi ngày và tự động xóa các bản backup cũ hơn 30 ngày (720h)
velero schedule create daily-full-backup \
    --schedule="0 2 * * *" \
    --ttl 720h0m0s
# Backup namespace production vào 3 giờ sáng Chủ Nhật hàng tuần.
velero schedule create weekly-production-backup \
    --schedule="0 3 * * 0" \
    --include-namespaces production \
    --ttl 720h0m0s
# Kiểm tra lịch đã tạo
velero schedule get
Liên hệ