Artifact Management

Artifact Management #

Artifact adalah output dari proses build yang siap di-deploy — Docker image, package Python, binary yang dikompilasi, atau archive tarball. Mengelola artifact dengan baik berarti: artifact bisa ditelusuri ke commit yang membuatnya, tersimpan di tempat yang persisten dan bisa diakses oleh semua environment, versinya immutable (tidak bisa diubah setelah dibuat), dan yang sudah tidak digunakan bersih dari storage. Ansible bisa mengotomasi seluruh lifecycle ini.

Prinsip Artifact yang Baik #

1. Immutable
   Setelah artifact bertag v2.1.0 dibuat, isinya tidak pernah berubah.
   Jika ada bugfix, buat artifact baru dengan tag v2.1.1.

2. Traceable
   Dari artifact, bisa ditelusuri: kode commit mana yang membuatnya,
   siapa yang menjalankan build, kapan, dan dari branch mana.

3. Verified
   Setiap artifact punya checksum (SHA256) yang bisa diverifikasi
   sebelum deployment — memastikan artifact tidak rusak atau dimanipulasi.

4. Lifecycle-Managed
   Artifact lama yang tidak diperlukan dibersihkan secara otomatis
   untuk menghemat storage.

Docker Image sebagai Artifact #

Docker registry adalah cara paling umum untuk mendistribusikan artifact berbasis container:

# playbooks/build-artifact.yml
---
- name: Build dan push Docker image artifact
  hosts: localhost
  vars:
    registry: registry.company.com
    image_name: myapp
    version: "{{ version | mandatory }}"
    git_sha: "{{ lookup('pipe', 'git rev-parse --short HEAD') }}"
    build_date: "{{ ansible_date_time.iso8601 }}"

  tasks:
    - name: Login ke registry
      community.docker.docker_login:
        registry_url: "{{ registry }}"
        username: "{{ registry_username }}"
        password: "{{ vault_registry_password }}"
      no_log: true

    - name: Build image dengan label untuk traceability
      community.docker.docker_image:
        name: "{{ registry }}/{{ image_name }}"
        tag: "{{ version }}"
        source: build
        build:
          path: "{{ playbook_dir }}/.."
          labels:
            version: "{{ version }}"
            git.sha: "{{ git_sha }}"
            build.date: "{{ build_date }}"
            build.pipeline: "{{ lookup('env', 'CI_PIPELINE_ID') | default('local') }}"
          args:
            APP_VERSION: "{{ version }}"
        state: present

    - name: Push image ke registry
      community.docker.docker_image:
        name: "{{ registry }}/{{ image_name }}"
        tag: "{{ version }}"
        push: true
        source: local

    - name: Generate checksum manifest
      command: >
        docker inspect
        --format="{{ '{{' }}index .RepoDigests 0{{ '}}' }}"
        {{ registry }}/{{ image_name }}:{{ version }}        
      register: image_digest
      changed_when: false

    - name: Simpan artifact manifest
      copy:
        content: |
          image={{ registry }}/{{ image_name }}:{{ version }}
          digest={{ image_digest.stdout }}
          version={{ version }}
          git_sha={{ git_sha }}
          build_date={{ build_date }}          
        dest: "{{ playbook_dir }}/artifact-manifest.txt"
      delegate_to: localhost

Distribusi Artifact ke Managed Node #

# playbooks/distribute-artifact.yml
---
- name: Distribusikan artifact ke managed node
  hosts: appservers
  vars:
    artifact_url: "https://artifacts.company.com/releases/{{ app_name }}/{{ version }}/{{ app_name }}-{{ version }}.tar.gz"
    artifact_checksum: "sha256:{{ artifact_sha256 }}"

  tasks:
    - name: Buat direktori artifact
      file:
        path: /opt/releases/{{ version }}
        state: directory
        owner: deployer
        mode: '0755'

    - name: Download artifact dengan verifikasi checksum
      get_url:
        url: "{{ artifact_url }}"
        dest: "/opt/releases/{{ version }}/app.tar.gz"
        checksum: "{{ artifact_checksum }}"   # Ansible otomatis verifikasi setelah download
        owner: deployer
        mode: '0644'
      register: artifact_download

    - name: Extract artifact
      unarchive:
        src: "/opt/releases/{{ version }}/app.tar.gz"
        dest: "/opt/releases/{{ version }}/"
        remote_src: true
      when: artifact_download.changed

    - name: Atomic symlink switch ke versi baru
      file:
        src: "/opt/releases/{{ version }}"
        dest: /opt/app/current
        state: link
        force: true   # Overwrite symlink yang ada

    - name: Bersihkan release lama (simpan 3 terakhir)
      shell: |
        ls -dt /opt/releases/*/ | tail -n +4 | xargs rm -rf        
      args:
        warn: false
      changed_when: false

Pola atomic symlink adalah cara yang elegan untuk deployment tanpa downtime — symlink /opt/app/current di-update atomically setelah semua file sudah di-extract.


Artifact di Object Storage (S3/MinIO) #

Untuk artifact non-container seperti binary atau package:

# Upload artifact ke S3 setelah build
- name: Upload artifact ke S3
  amazon.aws.s3_object:
    bucket: company-artifacts
    object: "releases/{{ app_name }}/{{ version }}/{{ app_name }}-{{ version }}.tar.gz"
    src: "/tmp/build/{{ app_name }}-{{ version }}.tar.gz"
    mode: put
    metadata:
      version: "{{ version }}"
      git_sha: "{{ git_sha }}"
      build_date: "{{ ansible_date_time.iso8601 }}"
    region: ap-southeast-1

# Upload checksum file
- name: Upload SHA256 checksum
  amazon.aws.s3_object:
    bucket: company-artifacts
    object: "releases/{{ app_name }}/{{ version }}/{{ app_name }}-{{ version }}.tar.gz.sha256"
    content: "{{ artifact_sha256 }}  {{ app_name }}-{{ version }}.tar.gz"
    mode: put
    region: ap-southeast-1

Artifact Lifecycle Management #

Artifact lama yang menumpuk menghabiskan storage yang mahal. Bersihkan secara berkala:

# playbooks/cleanup-artifacts.yml
---
- name: Bersihkan artifact Docker yang sudah tidak dipakai
  hosts: localhost
  vars:
    registry: registry.company.com
    image_name: myapp
    keep_versions: 10    # Simpan 10 versi terakhir

  tasks:
    - name: Ambil semua tag image dari registry
      uri:
        url: "https://{{ registry }}/v2/{{ image_name }}/tags/list"
        headers:
          Authorization: "Bearer {{ vault_registry_token }}"
        return_content: true
      register: image_tags
      no_log: true

    - name: Sortir tag dan ambil yang akan dihapus
      set_fact:
        tags_to_delete: >-
          {{ image_tags.json.tags
             | sort
             | list
             | difference(['latest'])
             | list
             | reverse
             | list
             | skip(keep_versions) }}          

    - name: Hapus tag lama dari registry
      uri:
        url: "https://{{ registry }}/v2/{{ image_name }}/manifests/{{ item }}"
        method: DELETE
        headers:
          Authorization: "Bearer {{ vault_registry_token }}"
        status_code: [202, 404]
      loop: "{{ tags_to_delete }}"
      loop_control:
        label: "Menghapus tag: {{ item }}"

Ringkasan #

  • Artifact harus immutable — setelah dibuat dengan tag tertentu, isinya tidak pernah berubah. Bugfix = artifact baru dengan tag baru.
  • Tambahkan label/metadata ke artifact saat build: versi, git SHA, tanggal build, pipeline ID — memungkinkan traceability penuh dari artifact ke kode sumber.
  • Gunakan checksum: di get_url untuk memverifikasi integritas artifact setelah download — mendeteksi download yang rusak atau artifact yang dimanipulasi.
  • Atomic symlink (/opt/app/current → /opt/releases/v2.1.0) adalah pola deployment yang elegan — switch versi terjadi secara atomic, rollback semudah mengganti symlink.
  • Simpan checksum artifact di tempat yang terpisah dari artifact itu sendiri — idealnya di object storage yang berbeda atau signed secara kriptografis.
  • Lifecycle management: bersihkan artifact lama secara otomatis — simpan hanya N versi terakhir untuk mengontrol biaya storage.

← Sebelumnya: Rollback Strategy   Berikutnya: Notification & Reporting →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact