All Products
Search
Document Center

Enterprise Distributed Application Service:Gunakan toolkit-maven-plugin untuk melakukan rilis canary di kluster Kubernetes

Last Updated:Mar 11, 2026

toolkit-maven-plugin mengotomatiskan rilis canary untuk aplikasi Spring Cloud, Dubbo, dan High-speed Service Framework (HSF) yang diterapkan di Enterprise Distributed Application Service (EDAS). Alih-alih memperbarui semua instans sekaligus, rilis canary menerapkan perubahan terlebih dahulu ke sejumlah kecil instans, lalu secara bertahap mempromosikan pembaruan tersebut ke instans yang tersisa dalam batch.

Cara kerja rilis canary

Rilis canary dengan toolkit-maven-plugin mengikuti proses dua tahap:

  1. Tahap canary: Terapkan versi baru ke sejumlah instans tertentu (gray). Instans-instans ini menerima traffic langsung, sehingga Anda dapat memvalidasi versi baru sebelum peluncuran penuh.

  2. Tahap peluncuran bertahap: Terapkan versi baru ke instans yang tersisa dalam batch terkendali (batch), dengan interval yang dapat dikonfigurasi (batchWaitTime) antar setiap batch. Batch dapat berjalan secara otomatis atau memerlukan persetujuan manual.

Jenis strategi pembaruan EDAS untuk proses ini adalah GrayBatchUpdate.

Prasyarat

Sebelum memulai, pastikan Anda telah memiliki:

  • Aplikasi Spring Cloud, Dubbo, atau HSF yang diterapkan di kluster Kubernetes EDAS

  • ID AccessKey dan Rahasia AccessKey untuk Pengguna Resource Access Management (RAM) (disarankan daripada menggunakan kredensial akun root)

  • ID aplikasi (appId), atau ID ruang mikroservis (namespaceId) dan nama aplikasi (appName) dari aplikasi target

Persiapkan rilis canary

Untuk menyiapkan rilis canary, tambahkan dependensi plug-in, buat file konfigurasi, dan jalankan perintah deployment.

Langkah 1: Tambahkan dependensi plug-in

Tambahkan dependensi berikut ke pom.xml Anda:

<build>
    <plugins>
        <plugin>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>toolkit-maven-plugin</artifactId>
            <version>1.1.9</version>
        </plugin>
    </plugins>
</build>
Catatan

Selalu gunakan versi toolkit-maven-plugin terbaru.

Langkah 2: Buat file konfigurasi

Buat tiga file YAML di direktori root proyek yang telah dikemas. Jika Anda menerapkan submodule Maven, buat file konfigurasi paket di direktori submodule tersebut.

Konfigurasi akun (toolkit_profile.yaml)

regionId:        # Wilayah tempat aplikasi diterapkan, misalnya cn-hangzhou
accessKeyId:     # ID AccessKey Pengguna RAM
accessKeySecret: # Rahasia AccessKey Pengguna RAM
jarPath:         # (Opsional) Path ke paket penyebaran. Melewati packaging Maven jika ditentukan.

Konfigurasi paket (toolkit_package.yaml)

apiVersion: V1
kind: AppPackage
spec:
  packageType:   # War, FatJar, Image, atau url
  packageUrl:    # (Opsional) URL paket penyebaran remote. Digunakan saat packageType adalah url.
  imageUrl:      # (Opsional) URL gambar. Digunakan saat packageType adalah Image.

Konfigurasi deployment (toolkit_deploy.yaml)

apiVersion: V1
kind: AppDeployment
spec:
  type: kubernetes
  target:
    appId:        # ID aplikasi. Jika ditentukan, namespaceId dan appName tidak diperlukan.
    namespaceId:  # ID ruang mikroservis. Diperlukan jika appId tidak ditentukan.
    appName:      # Nama aplikasi. Diperlukan jika appId tidak ditentukan.
  updateStrategy:
    type: GrayBatchUpdate
    grayUpdate:
      gray: 2              # Jumlah instans yang diperbarui pada tahap canary
    batchUpdate:
      batch: 3             # Jumlah batch untuk instans yang tersisa
      releaseType: auto     # auto atau manual
      batchWaitTime: 5      # Menit menunggu antar batch

Tabel berikut menjelaskan parameter rilis canary:

ParameterDeskripsiContoh
appIdID aplikasi EDAS6bbc57a2-a2a0-4edb-****-************
namespaceIdID ruang mikroserviscn-hangzhou:my-namespace
grayJumlah instans canary2
batchJumlah batch peluncuran3
releaseTypeauto (otomatis) atau manual (memerlukan persetujuan)auto
batchWaitTimeMenit antar batch5

Langkah 3: Jalankan perintah deployment

Buka direktori yang berisi pom.xml (atau pom.xml submodule) dan jalankan:

mvn clean package toolkit:deploy \
  -Dtoolkit_profile=toolkit_profile.yaml \
  -Dtoolkit_package=toolkit_package.yaml \
  -Dtoolkit_deploy=toolkit_deploy.yaml

Jika deployment berhasil, output akan menampilkan BUILD SUCCESS.

Parameter perintah:

ParameterDeskripsi
toolkit:deployMenerapkan aplikasi setelah packaging Maven selesai.
-Dtoolkit_profilePath ke file konfigurasi akun.
-Dtoolkit_packagePath ke file konfigurasi paket.
-Dtoolkit_deployPath ke file konfigurasi deployment.
-Ddeploy_version(Opsional) Versi deployment. Menggantikan versi di toolkit_deploy.yaml. Memerlukan toolkit-maven-plugin 1.0.6 atau lebih baru.
Catatan

Untuk melewati parameter -Dtoolkit_profile, -Dtoolkit_package, dan -Dtoolkit_deploy, letakkan file konfigurasi di direktori yang sama dengan pom.xml dan tambahkan titik di awal nama file (.toolkit_profile.yaml, .toolkit_package.yaml, .toolkit_deploy.yaml). Plug-in akan mendeteksinya secara otomatis.

Skenario deployment

Deploy dengan paket WAR atau FatJar lokal

Buat paket WAR atau FatJar secara lokal dan deploy ke aplikasi EDAS yang sudah ada.

toolkit_package.yaml:

apiVersion: V1
kind: AppPackage
spec:
  packageType: War    # Atau FatJar

toolkit_deploy.yaml:

apiVersion: V1
kind: AppDeployment
spec:
  type: kubernetes
  target:
    appId:        # ID aplikasi
    namespaceId:  # (Opsional) ID namespace, wajib jika appId tidak ditentukan
    appName:      # (Opsional) Nama aplikasi, wajib jika appId tidak ditentukan

Deploy dengan URL gambar yang sudah ada

Deploy aplikasi menggunakan gambar kontainer yang sudah ada di registri.

toolkit_package.yaml:

apiVersion: V1
kind: AppPackage
spec:
  packageType: Image
  imageUrl: registry.cn-beijing.aliyuncs.com/test/gateway:latest

toolkit_deploy.yaml:

apiVersion: V1
kind: AppDeployment
spec:
  type: kubernetes
  target:
    appId:        # ID aplikasi
    namespaceId:  # (Opsional) ID namespace, wajib jika appId tidak ditentukan
    appName:      # (Opsional) Nama aplikasi, wajib jika appId tidak ditentukan

Buat dan dorong gambar Docker lokal

Buat gambar Docker secara lokal, dorong ke repository gambar kontainer Alibaba Cloud, lalu deploy.

toolkit_package.yaml:

apiVersion: V1
kind: AppPackage
spec:
  packageType: Image
  build:
    docker:
      dockerfile: Dockerfile
      imageRepoAddress:   # Alamat repository gambar
      imageTag:           # Tag gambar
      imageRepoUser:      # Username repository
      imageRepoPassword:  # Password repository

toolkit_deploy.yaml:

apiVersion: V1
kind: AppDeployment
spec:
  type: kubernetes
  target:
    appId:        # ID aplikasi
    namespaceId:  # (Opsional) ID namespace, wajib jika appId tidak ditentukan
    appName:      # (Opsional) Nama aplikasi, wajib jika appId tidak ditentukan

Referensi konfigurasi

Parameter paket

apiVersion: V1
kind: AppPackage
spec:
  packageType:         # War, FatJar, Image, atau url
  imageUrl:            # URL gambar. Wajib saat deploy dengan gambar.
  packageUrl:          # URL paket. Digunakan saat packageType adalah url.
  build:
    docker:
      dockerfile:        # Path Dockerfile. Wajib untuk pembuatan gambar lokal.
      imageRepoAddress:  # Alamat repository gambar Alibaba Cloud
      imageTag:          # Tag gambar
      imageRepoUser:     # Username repository
      imageRepoPassword: # Password repository
    oss:
      bucket:            # Nama bucket Object Storage Service (OSS)
      key:               # Path objek OSS
      accessKeyId:       # ID AccessKey untuk akses OSS
      accessKeySecret:   # Rahasia AccessKey untuk akses OSS

Parameter deployment

Klik untuk memperluas parameter yang didukung oleh file konfigurasi deployment.

File deployment mendukung parameter berikut. Semua parameter bersifat opsional kecuali dinyatakan lain.

apiVersion: V1
kind: AppDeployment
spec:
  type: kubernetes
  target:
    appName:       # Nama aplikasi
    namespaceId:   # ID ruang mikroservis
    appId:         # ID aplikasi. Jika ditentukan, namespaceId dan appName tidak diperlukan.
    version:       # Versi deployment. Format default: hari-jam-menit-detik.
    jdk:           # Versi JDK (Open JDK 7 atau Open JDK 8). Tidak berlaku untuk deployment berbasis gambar.
    webContainer:  # Versi Tomcat (apache-tomcat-7.0.91). Tidak berlaku untuk deployment berbasis gambar.
    batchWaitTime: # Interval antar batch (menit)
    command:       # Perintah startup kontainer. Menggantikan perintah default gambar.
    commandArgs:   # Argumen perintah startup
    - 1d
  envs:            # Variabel lingkungan kontainer
    - name: ENV_NAME
      value: 'value'

Pemeriksaan kesehatan

Konfigurasikan pemeriksaan kelangsungan hidup (liveness) dan kesiapan (readiness) untuk memantau kesehatan kontainer dan aplikasi. Tentukan salah satu dari exec, tcpSocket, atau httpGet untuk setiap pemeriksaan.

Kontainer yang gagal dalam pemeriksaan kelangsungan hidup akan dimulai ulang. Kontainer yang gagal dalam pemeriksaan kesiapan beberapa kali akan dihentikan lalu dimulai ulang, dan tidak menerima traffic dari instans Server Load Balancer (SLB).

  liveness:
    exec:
      command:
        - cat
        - /tmp/healthy
    tcpSocket:
      host: "192.168.1.109"  # (Opsional) Default ke alamat IP pod
      port: "8080"
    httpGet:
      path: "/health"
      port: "8080"
      host: "192.168.1.109"  # (Opsional) Default ke alamat IP pod
      scheme: "HTTP"       # HTTP atau HTTPS
      httpHeaders:
        - name: "Custom-Header"
          value: "value"
    initialDelaySeconds: 5   # Detik sebelum pemeriksaan pertama
    timeoutSeconds: 11       # Detik sebelum pemeriksaan timeout
    periodSeconds: 5         # Detik antar pemeriksaan
    successThreshold: 1      # Harus 1 untuk pemeriksaan kelangsungan hidup
    failureThreshold: 3      # Kegagalan berturut-turut sebelum tindakan

  readiness:
    exec:
      command:
        - cat
        - /tmp/healthy
    tcpSocket:
      host: "192.168.1.109"  # (Opsional) Default ke alamat IP pod
      port: "8080"
    httpGet:
      path: "/health"
      port: "8080"
      host: "192.168.1.109"  # (Opsional) Default ke alamat IP pod
      scheme: "HTTP"
      httpHeaders:
        - name: "Custom-Header"
          value: "value"
    initialDelaySeconds: 5
    timeoutSeconds: 11
    periodSeconds: 5
    successThreshold: 2
    failureThreshold: 3

Panggilan balik siklus hidup

  preStop:          # Dijalankan sebelum kontainer dihentikan
    exec:
      command:
        - /bin/bash
        - -c
        - "sleep 30"
    httpGet:
      host: "192.168.1.109"  # (Opsional) Default ke alamat IP pod
      path: "/shutdown"
      port: "8080"
      scheme: "HTTP"         # HTTP atau HTTPS
      httpHeaders:
        - name: "Custom-Header"
          value: "value"

  postStart:        # Dijalankan setelah kontainer dimulai
    exec:
      command:
        - /bin/bash
        - -c
        - "echo started"
    httpGet:
      host: "192.168.1.109"  # (Opsional) Default ke alamat IP pod
      path: "/initialize"
      port: "8080"
      scheme: "HTTP"         # HTTP atau HTTPS
      httpHeaders:
        - name: "Custom-Header"
          value: "value"

Pemasangan konfigurasi

Pasang resource ConfigMap atau Secret ke dalam kontainer.

  configMountDescs:
    - type: "ConfigMap"           # ConfigMap atau Secret
      name: "my-config"
      mountPath: "/home/admin"    # Direktori pemasangan
      mountItems:                 # Pasang sebagai file individual
        - key: "config-key"
          path: "config-file"
      useSubPath: true            # true: pertahankan file yang ada; false: timpa

Parameter startup Java

  javaStartUpConfig:
    initialHeapSize:             # -Xms (ukuran heap awal, MB)
      original: 1000
      startup: "-Xms1000m"
    maxHeapSize:                 # -Xmx (ukuran heap maksimum, MB)
      original: 1000
      startup: "-Xmx1000m"
    newSize:                     # -XX:NewSize (generasi muda awal, MB)
      original: 200
      startup: "-XX:NewSize=200m"
    maxNewSize:                  # -XX:MaxNewSize (generasi muda maksimum, MB)
      original: 200
      startup: "-XX:MaxNewSize=200m"
    survivorRatio:               # -XX:SurvivorRatio (rasio Eden ke Survivor)
      original: 2
      startup: "-XX:SurvivorRatio=2"
    newRatio:                    # -XX:NewRatio (rasio generasi tua ke muda)
      original: 8
      startup: "-XX:NewRatio=8"
    permSize:                    # -XX:PermSize (generasi permanen, MB)
      original: 512
      startup: "-XX:PermSize=512m"
    maxPermSize:                 # -XX:MaxPermSize (generasi permanen maksimum, MB)
      original: 512
      startup: "-XX:MaxPermSize=200m"
    maxDirectMemorySize:         # -XX:MaxDirectMemorySize (MB)
      original: 100
      startup: "-XX:MaxDirectMemorySize=100m"
    threadStackSize:             # -XX:ThreadStackSize
      original: 500
      startup: "-XX:ThreadStackSize=500"
    hsfserverPort:               # Port server HSF
      original: 12200
      startup: "-Dhsf.server.port=12200"
    hsfserverMinPoolSize:        # Ukuran kolam thread minimum HSF
      original: 50
      startup: "-Dhsf.server.min.poolsize=50"
    hsfserverMaxPoolSize:        # Ukuran kolam thread maksimum HSF
      original: 720
      startup: "-Dhsf.server.max.poolsize=720"
    youngGarbageCollector:       # Kebijakan GC generasi muda
      original: "UseSerialGC"   # UseSerialGC, UseG1GC, UseParNewGC, atau UseParallelGC
      startup: "-XX:+UseSerialGC"
    oldGarbageCollector:         # Kebijakan GC generasi tua
      original: "UseConcMarkSweepGC"   # UseConcMarkSweepGC, UseSerialGC, UseG1GC, UseParNewGC, UseParallelOldGC, atau UseParallelGC
      startup: "-XX:+UseConcMarkSweepGC"
    concGCThreads:               # Thread GC konkuren
      original: 5
      startup: "-XX:ConcGCThreads=5"
    parallelGCThreads:           # Thread GC paralel
      original: 5
      startup: "-XX:ParallelGCThreads=5"
    g1HeapRegionSize:            # Ukuran region G1 (MB)
      original: 50
      startup: "-XX:G1HeapRegionSize=50m"
    gclogFilePath:               # Direktori log GC
      original: "/tmp/"
      startup: "-Xloggc:/tmp/"
    useGCLogFileRotation:        # Aktifkan rotasi log GC
      original: true
      startup: "-XX:+UseGCLogFileRotation"
    numberOfGCLogFiles:          # Jumlah file log GC
      original: 5
      startup: "-XX:NumberOfGCLogFiles=5"
    gclogFileSize:               # Ukuran file log GC (MB)
      original: 100
      startup: "-XX:GCLogFileSize=100m"
    heapDumpOnOutOfMemoryError:  # Aktifkan dump heap saat kehabisan memori (OOM)
      original: true
      startup: "-XX:+HeapDumpOnOutOfMemoryError"
    heapDumpPath:                # Path file dump OOM
      original: "/tmp/dumpfile"
      startup: "-XX:HeapDumpPath=/tmp/dumpfile"
    customParams:                # Parameter JVM kustom
      original: "-Dtest=true"
      startup: "-Dtest=true"

Aturan penjadwalan

  deployAcrossZones: "true"    # Sebarkan pod di berbagai zona ketersediaan (disarankan)
  deployAcrossNodes: "true"    # Sebarkan pod di berbagai node (disarankan)

  customTolerations:           # Toleransi penjadwalan Kubernetes
    - key: my-key
      operator: Exists         # Exists atau Equal
      effect: NoSchedule       # NoSchedule atau NoExecute
    - key: another-key
      operator: Equal
      value: "my-value"
      effect: "NoExecute"
      tolerationSeconds: 300

  customAffinity:
    nodeAffinity:              # Jadwalkan pod berdasarkan label node
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: "topology.kubernetes.io/zone"
                operator: "In"
                values:
                  - "cn-hangzhou-a"
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 1
          preference:
            matchExpressions:
              - key: "topology.kubernetes.io/zone"
                operator: "In"
                values:
                  - "cn-hangzhou-b"
    podAffinity:               # Tempatkan pod berdekatan dengan pod yang cocok
      requiredDuringSchedulingIgnoredDuringExecution:
        - namespaces:
            - "default"
          topologyKey: "topology.kubernetes.io/zone"
          labelSelector:
            matchExpressions:
              - key: "app"
                operator: "In"
                values:
                  - "my-app"
    podAntiAffinity:           # Sebarkan pod menjauh dari pod yang cocok
      requiredDuringSchedulingIgnoredDuringExecution:
        - namespaces:
            - "default"
          topologyKey: "kubernetes.io/hostname"
          labelSelector:
            matchExpressions:
              - key: "app"
                operator: "In"
                values:
                  - "my-app"
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 2
          podAffinityTerm:
            namespaces:
              - "default"
            topologyKey: "kubernetes.io/hostname"
            labelSelector:
              matchExpressions:
                - key: "app"
                  operator: "In"
                  values:
                    - "my-app"