k8s部署canal 1.1.8

 

说明:canal部署的是单实例,pvc挂载日志目录,因日志存储路径修改太过麻烦,所以把mysql的bin log点位信息存在日志目录下

推荐使用多实例

一、单实例

1、部署configmap,注意canal.destinations要和canal.instance.id一致

vim canal-config.yaml

 

 

apiVersion: v1
kind: ConfigMap
metadata:
  name: canal-subotiz
  namespace: staging
data:
  canal.properties: |
    # ========== Canal基础配置 ==========
    canal.id = 1
    canal.port = 11111
    canal.destinations = canal-subotiz-common
    
    # ========== 持久化路径 ==========
    canal.file.data.dir = /home/admin/canal-server/logs/db
    
    # ========== Zookeeper配置(可以注释掉不使用) ==========
    # canal.zkServers = z-2.subotizstgmsk.ck8tsh.c2.kafka.ap-southeast-1.amazonaws.com:2181,z-1.subotizstgmsk.ck8tsh.c2.kafka.ap-southeast-1.amazonaws.com:2181,z-3.subotizstgmsk.ck8tsh.c2.kafka.ap-southeast-1.amazonaws.com:2181
    
    # ========== Server模式配置 ==========
    canal.serverMode = kafka
    
    # ========== Kafka生产者配置 ==========
    kafka.bootstrap.servers = b-2.subotizstgmsk.ck8tsh.c2.kafka.ap-southeast-1.amazonaws.com:9092,b-1.subotizstgmsk.ck8tsh.c2.kafka.ap-southeast-1.amazonaws.com:9092,b-3.subotizstgmsk.ck8tsh.c2.kafka.ap-southeast-1.amazonaws.com:9092
    canal.mq.servers = b-2.subotizstgmsk.ck8tsh.c2.kafka.ap-southeast-1.amazonaws.com:9092,b-1.subotizstgmsk.ck8tsh.c2.kafka.ap-southeast-1.amazonaws.com:9092,b-3.subotizstgmsk.ck8tsh.c2.kafka.ap-southeast-1.amazonaws.com:9092
    canal.mq.topic = subotiz-canal-merchantdb
    canal.mq.flatMessage = true
    canal.mq.partitionsNum = 1
    
    # Kafka生产者高级配置
    canal.mq.properties.max.request.size = 134217728
    canal.mq.maxRequestSize = 134217728
    canal.mq.bufferMemory = 134217728
    canal.mq.kafka.producer.retries = 5
    canal.mq.kafka.producer.retry.backoff.ms = 1000
    canal.mq.kafka.producer.retry.backoff.max.ms = 5000
    canal.mq.kafka.producer.metadata.fetch.timeout.ms = 120000
    canal.mq.kafka.producer.metadata.max.age.ms = 300000
    
    # ========== 自动扫描配置 ==========
    canal.auto.scan = true
    canal.auto.scan.interval = 5
  
  # instance.properties 实例配置
  instance.properties: |
    # ========== 核心新增:实例ID(必须和destinations一致) ==========
    canal.instance.id = canal-subotiz-common

    # ========== MySQL连接配置 ==========
    canal.instance.master.address = mypaydb1.cluster-cl20ky4iyqmn.ap-southeast-1.rds.amazonaws.com:3306
    canal.instance.dbUsername = canal
    canal.instance.dbPassword = vGgRTZubA2qsI2mN
    canal.instance.connectionCharset = UTF-8
    
    #========== 动态Topic配置 ==========
    canal.mq.topic = subotiz-canal-merchantdb
    canal.mq.dynamicTopic = subotiz-canal-developerdb:developerdb,subotiz-canal-merchantdb:merchantdb,subotiz-canal-paymentdb:paymentdb,subotiz-canal-tradingdb:tradingdb,subotiz-canal-pgsbilling:pgsbilling,subotiz-canal-promotiondb:promotiondb,subotiz-canal-robin:robin,subotiz-canal-email-server:email_server,subotiz-canal-notifydb:notifydb,shoplazza-canal-discount-limit:promotiondb\\.discount_used_total,subotiz-canal-tradingdb-trade:tradingdb\\.trade,subotiz-canal-tradingdb-refund-record:tradingdb\\.refund_record,subotiz-canal-tradingdb-checkout-session:tradingdb\\.checkout_session,subotiz-canal-merchantdb-customer:merchantdb\\.customer,subotiz-canal-merchantdb-merchant-setting:merchantdb\\.merchant_setting,subotiz-canal-paymentdb-pgs-i18n-message:paymentdb\\.pgs_i18n_message,subotiz-canal-paymentdb-pgs-subscription:paymentdb\\.pgs_subscription,subotiz-canal-paymentdb-pgs-invoice:paymentdb\\.pgs_invoice,subotiz-canal-tradingdb-trade-line-item:tradingdb\\.trade_line_item,subotiz-canal-paymentdb-pgs-subscription-period:paymentdb\\.pgs_subscription_period,subotiz-canal-paymentdb-pgs-payment-provider-method-attribute:paymentdb\\.pgs_payment_provider_method_attribute,subotiz-canal-paymentdb-pgs-payment-flow:paymentdb\\.pgs_payment_flow,subotiz-canal-paymentdb-pgs-prepayment:paymentdb\\.pgs_prepayment,subotiz-canal-llm-router-db-model-logs:llm_router_db\\.logs,subotiz-canal-llm-router-db-model-pricing:llm_router_db\\.model_pricing,subotiz-canal-llm-router-db-tokens:llm_router_db\\.tokens,subotiz-canal-llm-router-db-user-balances:llm_router_db\\.user_balances
    canal.mq.dynamicTopicPartitionNum = subotiz-canal-merchantdb:1
    canal.mq.partitionHash = .*\..*

    # ========== 表过滤配置 ==========
    canal.instance.filter.regex = (developerdb|merchantdb|paymentdb|tradingdb|pgsbilling|promotiondb|robin|email_server|notifydb|llm_router_db)\\..*
    canal.instance.filter.black.regex = (mysql|dts|mqbone|oauth2_production|account|act|crab|crawfish|datafountain|email_api_production|exec|fortunecat|kirin|notify_api_production|pangu_db|leopard)\\..*,tiger\\._order_transactions_new,main\\.menus,main\\.menu_items
    canal.instance.filter.query = true
    canal.instance.filter.query.ddl = true
    canal.instance.filter.query.dml = true
    canal.instance.filter.query.regex = ^(SELECT|SHOW|DESC|EXPLAIN).*$
    
    # ========== 心跳检测 ==========
    canal.instance.filter.heartbeat = true
    
    # ========== DDL解析 ==========
    canal.instance.parse.ddl = true
    
    # ========== 表元数据配置 ==========
    canal.instance.filter.table.meta = true
    canal.instance.tableMetaCache.enable = false
    
    # ========== TSDB表结构存储 ==========
    canal.instance.tsdb.enable = true
    
    # ========== 默认GTID配置 ==========
    canal.instance.gtidon = false
    
    # ========== 内存配置 ==========
    canal.instance.memory.buffer.size = 16384
    canal.instance.memory.buffer.memunit = 1024
    canal.instance.transaction.size = 1024
    
    # ========== 并行解析 ==========
    canal.instance.parser.parallel = true

 

 

kubectl apply -f  canal-config.yaml

 

2、部署statefulset,mysql bin点位信息放到logs目录下。不能挂载conf目录,不然pod首次启动时pvc此时目录为空导致canal启动失败

11111是canal的服务端口,11112是监控metrics端口

vim canal-statefulset.yaml

 

---
apiVersion: v1
kind: Service
metadata:
  name: canal-subotiz
  namespace: staging
spec:
  selector:
    app: canal-subotiz
  ports:
  - name: canal-server
    port: 11112
    targetPort: 11112
  type: ClusterIP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: canal-subotiz
  namespace: staging
spec:
  serviceName: canal-subotiz
  replicas: 1
  selector:
    matchLabels:
      app: canal-subotiz
  template:
    metadata:
      labels:
        app: canal-subotiz
    spec:
      securityContext:
        runAsUser: 0
        runAsGroup: 0
        fsGroup: 1000
      containers:
      - name: canal-server
        image: canal/canal-server:v1.1.8
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 11111
          name: canal
        env:
        - name: JAVA_OPTS
          value: "-Xms2g -Xmx4g -XX:+UseG1GC"
        command: ["/bin/sh", "-c"]
        args:
        - |
          set -e
          # 初始化全局配置文件
          [ ! -f /home/admin/canal-server/conf/canal.properties ] && cp /home/admin/canal-server/conf/canal.properties.example /home/admin/canal-server/conf/canal.properties
          
          # 定义update_config函数
          update_config() {
              local config_file=$1
              local custom_config=$2
              while IFS='=' read -r key value; do
                  if [[ -z "$key" || "$key" =~ ^# ]]; then
                      continue
                  fi
                  local clean_key=$(echo "$key" | tr -d ' \t' | xargs)
                  local clean_value=$(echo "$value" | xargs)
                  local standard_line="$clean_key = $clean_value"
                  if grep -q "^[ \t]*$clean_key[ \t]*=" "$config_file"; then
                      sed -i "s@^\([ \t]*$clean_key[ \t]*=[ \t]*\).*@\1$clean_value@" "$config_file"
                      echo "修改配置项:$clean_key = $clean_value"
                  else
                      echo "$standard_line" >> "$config_file"
                      echo "新增配置项:$clean_key = $clean_value"
                  fi
              done < "$custom_config"
          }
          
          # 处理全局配置
          echo "===== 处理canal.properties ====="
          update_config "/home/admin/canal-server/conf/canal.properties" "/tmp/config/canal.properties"
          
          # 读取DEST
          DEST=$(grep "^canal.destinations" /home/admin/canal-server/conf/canal.properties | awk -F'=' '{print $2}' | tr -d ' \t' | xargs)
          echo "===== 动态识别实例名:$DEST ====="
          
          # 处理自定义实例配置
          echo "===== 处理$DEST实例配置 ====="
          mkdir -p /home/admin/canal-server/conf/$DEST/
          cp /home/admin/canal-server/conf/example/instance.properties /home/admin/canal-server/conf/$DEST/instance.properties
          update_config "/home/admin/canal-server/conf/${DEST}/instance.properties" "/tmp/config/${DEST}.properties"
          
          # 权限配置
          chown -R admin:admin /home/admin/canal-server/conf/$DEST/
          chmod -R 755 /home/admin/canal-server/conf/
          mkdir -p /home/admin/canal-server/data/db /home/admin/canal-server/data/log
          chown -R admin:admin /home/admin/canal-server/data/db /home/admin/canal-server/data/log
          
          # 验证配置
          echo "===== 最终配置验证 ====="
          grep "canal.destinations" /home/admin/canal-server/conf/canal.properties
          grep "canal.instance.master.address" /home/admin/canal-server/conf/$DEST/instance.properties
          echo "========================"
          
          # ===================== 核心新增:判断并删除example目录 =====================
          if [ "$DEST" != "example" ]; then
              echo "===== 非example实例,删除原生example目录避免扫描 ====="
              rm -rf /home/admin/canal-server/conf/example
          else
              echo "===== 当前实例为example,保留原生example目录 ====="
          fi
          # ==========================================================================
          
          # 启动Canal
          /home/admin/app.sh
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "4Gi"
            cpu: "2000m"
        volumeMounts:
        - name: canal-config
          mountPath: /tmp/config
        - name: canal-data
          mountPath: /home/admin/canal-server/logs
      volumes:
      - name: canal-config
        configMap:
          name: canal-subotiz
  volumeClaimTemplates:
  - metadata:
      name: canal-data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "gp2-ap-southeast-1b"
      resources:
        requests:
          storage: 20Gi

 

 

kubectl apply -f  canal-statefulset.yaml

 

二、多实例

1、部署configmap,注意canal.destinations要和canal.instance.id一致

vim canal-saas-config.yaml

 

 

apiVersion: v1
kind: ConfigMap
metadata:
  name: canal-saas
  namespace: staging
data:
  # ======================
  # 公共配置(所有实例共享)
  # ======================
  canal.properties: |
    canal.id = 1
    canal.port = 11111
    canal.destinations = canal-saas,canal-ads
    canal.file.data.dir = /home/admin/canal-server/logs/db

    # ZK 公共
    canal.zkServers = mse-f01be7312-zk.mse.aliyuncs.com:2181 

    # Kafka 公共地址
    canal.serverMode = kafka
    kafka.bootstrap.servers = alikafka-post-cn-qzm4d4obv002-3-vpc.alikafka.aliyuncs.com:9092
    canal.mq.servers = alikafka-post-cn-qzm4d4obv002-3-vpc.alikafka.aliyuncs.com:9092

    # 公共 Kafka 高级参数
    canal.mq.flatMessage = true
    canal.mq.partitionsNum = 1
    canal.mq.maxRequestSize = 52428800
    canal.mq.kafka.producer.retries = 5
    canal.mq.kafka.producer.retry.backoff.ms = 1000
    canal.mq.kafka.producer.retry.backoff.max.ms = 5000
    canal.mq.kafka.producer.metadata.fetch.timeout.ms = 120000
    canal.mq.kafka.producer.metadata.max.age.ms = 300000
    canal.mq.partitionHash = .*\..*

    # 自动扫描
    canal.auto.scan = true
    canal.auto.scan.interval = 5

  # ======================
  # 实例1:canal-saas(独立Topic)
  # ======================
  canal-saas.properties: |
    canal.instance.id = canal-saas

    # ========== 实例独立 MQ 配置(核心!)==========
    canal.mq.topic = subotiz-canal-merchantdb
    canal.mq.dynamicTopic = subotiz-canal-merchantdb:product_api_dev\\..*
    canal.mq.dynamicTopicPartitionNum = subotiz-canal-merchantdb:1

    # MySQL 连接
    canal.instance.master.address = tidbdevob.aliyun-us-west-1.oceanbase.cloud:3306
    canal.instance.dbUsername = canal
    canal.instance.dbPassword = vGgRTZubA2qsI2mN
    canal.instance.connectionCharset = UTF-8

    # 过滤
    canal.instance.filter.regex = (product_api_dev)\\..*
    canal.instance.filter.black.regex = (mysql|dts|mqbone|oauth2_production|account|act|crab|crawfish|datafountain|email_api_production|exec|fortunecat|kirin|notify_api_production|pangu_db|leopard)\\..*,tiger\\._order_transactions_new,main\\.menus,main\\.menu_items
    canal.instance.filter.query = true
    canal.instance.filter.query.ddl = true
    canal.instance.filter.query.dml = true
    canal.instance.filter.query.regex = ^(SELECT|SHOW|DESC|EXPLAIN).*$

    # 基础
    canal.mq.properties.max.request.size = 134217728
    canal.mq.maxRequestSize = 134217728
    canal.mq.bufferMemory = 134217728
    canal.instance.filter.heartbeat = true
    canal.instance.parse.ddl = true
    canal.instance.filter.table.meta = true
    canal.instance.tableMetaCache.enable = false
    canal.instance.tsdb.enable = true
    canal.instance.gtidon = false
    canal.instance.memory.buffer.size = 16384
    canal.instance.memory.buffer.memunit = 1024
    canal.instance.transaction.size = 1024
    canal.instance.parser.parallel = true

  # ======================
  # 实例2:canal-ads(独立Topic)
  # ======================
  canal-ads.properties: |
    canal.instance.id = canal-ads

    # ========== 实例独立 MQ 配置(核心!)==========
    canal.mq.topic = saas-canal-ads-google-categories
    canal.mq.dynamicTopic = saas-canal-ads-google-categories:ads_api_dev\\..*
    canal.mq.dynamicTopicPartitionNum = saas-canal-ads-google-categories:1

    # MySQL 连接
    canal.instance.master.address = tidbdevob.aliyun-us-west-1.oceanbase.cloud:3306
    canal.instance.dbUsername = canal
    canal.instance.dbPassword = vGgRTZubA2qsI2mN
    canal.instance.connectionCharset = UTF-8

    # 过滤
    canal.instance.filter.regex = (ads_api_dev)\\..*
    canal.instance.filter.black.regex = (mysql|dts|mqbone)\\..*
    canal.instance.filter.query = true
    canal.instance.filter.query.ddl = true
    canal.instance.filter.query.dml = true
    canal.instance.filter.heartbeat = true
    canal.instance.parse.ddl = true
    canal.instance.tsdb.enable = true
    canal.instance.gtidon = false
    canal.instance.memory.buffer.size = 16384
    canal.instance.transaction.size = 1024
    canal.instance.parser.parallel = true

 

 

kc apply -f canal-saas-config.yaml

 

2、部署statefulset,mysql bin点位信息放到logs目录下。不能挂载conf目录,不然pod首次启动时pvc此时目录为空导致canal启动失败

11111是canal的服务端口,11112是监控metrics端口

vim canal-saas-sts.yaml

 

---
apiVersion: v1
kind: Service
metadata:
  name: canal-saas
  namespace: staging
spec:
  selector:
    app: canal-saas
  ports:
  - name: canal-server
    port: 11112
    targetPort: 11112
  type: ClusterIP
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: canal-saas
  namespace: staging
spec:
  serviceName: canal-saas
  replicas: 1
  selector:
    matchLabels:
      app: canal-saas
  template:
    metadata:
      labels:
        app: canal-saas
    spec:
      securityContext:
        runAsUser: 0
        runAsGroup: 0
        fsGroup: 1000
      containers:
      - name: canal-server
        image: canal/canal-server:v1.1.8
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 11111
          name: canal
        env:
        - name: JAVA_OPTS
          value: "-Xms2g -Xmx4g -XX:+UseG1GC"
        command: ["/bin/sh", "-c"]
        args:
        - |
          set -e
          # 初始化全局配置文件
          [ ! -f /home/admin/canal-server/conf/canal.properties ] && cp /home/admin/canal-server/conf/canal.properties.example /home/admin/canal-server/conf/canal.properties
          
          # 定义update_config函数
          update_config() {
              local config_file=$1
              local custom_config=$2
              while IFS='=' read -r key value; do
                  if [[ -z "$key" || "$key" =~ ^# ]]; then
                      continue
                  fi
                  local clean_key=$(echo "$key" | tr -d ' \t' | xargs)
                  local clean_value=$(echo "$value" | xargs)
                  local standard_line="$clean_key = $clean_value"
                  if grep -q "^[ \t]*$clean_key[ \t]*=" "$config_file"; then
                      sed -i "s@^\([ \t]*$clean_key[ \t]*=[ \t]*\).*@\1$clean_value@" "$config_file"
                      echo "修改配置项:$clean_key = $clean_value"
                  else
                      echo "$standard_line" >> "$config_file"
                      echo "新增配置项:$clean_key = $clean_value"
                  fi
              done < "$custom_config"
          }
          
          # 处理全局canal.properties配置
          echo "===== 处理canal.properties全局配置 ====="
          update_config "/home/admin/canal-server/conf/canal.properties" "/tmp/config/canal.properties"
          
          # 解析多实例列表
          DEST_LIST=$(grep "^canal.destinations" /home/admin/canal-server/conf/canal.properties | awk -F'=' '{print $2}' | tr -d ' \t')
          echo "===== 识别多实例列表:$DEST_LIST ====="
          
          # 循环初始化每个实例
          for DEST in $(echo "$DEST_LIST" | tr ',' ' '); do
              echo "===== 开始初始化实例:$DEST ====="
              mkdir -p /home/admin/canal-server/conf/${DEST}/
              # 拷贝默认实例配置模板
              cp /home/admin/canal-server/conf/example/instance.properties /home/admin/canal-server/conf/${DEST}/instance.properties
              # 加载对应实例独立配置 无多余前缀 直接${DEST}.properties
              update_config /home/admin/canal-server/conf/${DEST}/instance.properties /tmp/config/${DEST}.properties
              # 实例目录权限
              chown -R admin:admin /home/admin/canal-server/conf/${DEST}/
          done
          
          # 全局目录权限
          chmod -R 755 /home/admin/canal-server/conf/
          mkdir -p /home/admin/canal-server/data/db /home/admin/canal-server/data/log
          chown -R admin:admin /home/admin/canal-server/data/db /home/admin/canal-server/data/log
          
          # 删除原生example目录避免自动扫描干扰
          rm -rf /home/admin/canal-server/conf/example
          
          # 启动Canal服务
          /home/admin/app.sh
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "4Gi"
            cpu: "2000m"
        volumeMounts:
        - name: canal-config
          mountPath: /tmp/config
        - name: canal-data
          mountPath: /home/admin/canal-server/logs
      volumes:
      - name: canal-config
        configMap:
          name: canal-saas
  volumeClaimTemplates:
  - metadata:
      name: canal-data
    spec:
      accessModes: ["ReadWriteOnce"]
      storageClassName: "alicloud-disk-ssd"
      resources:
        requests:
          storage: 20Gi

 

 

kc apply -f  canal-saas-sts.yaml

 

posted @ 2026-01-19 09:35  苦逼yw  阅读(65)  评论(0)    收藏  举报