Package

Package #

Dalam ekosistem Linux, instalasi dan pemeliharaan perangkat lunak (package management) merupakan salah satu pilar utama dari administrasi sistem. Berbagai distribusi Linux memiliki package manager bawaan yang berbeda, seperti APT pada keluarga Debian/Ubuntu dan YUM/DNF pada keluarga Red Hat (RHEL/CentOS/Fedora). Mengelola instalasi ini secara manual di lingkungan multi-server akan menciptakan celah ketidakkonsistenan versi, repositori yang tidak aman, dan kesulitan pelacakan. Ansible mempermudah proses ini dengan menyediakan modul-modul native untuk masing-masing package manager serta modul abstraksi generik. Dengan memahami cara kerja modul-modul ini secara mendalam, kita dapat merancang proses deployment perangkat lunak yang konsisten, berkinerja tinggi, dan idempoten di seluruh infrastruktur kita.


Filosofi Manajemen Package: Abstraksi Generik vs Native Modul #

Ketika kita merancang playbook Ansible yang akan berjalan di berbagai lingkungan, kita sering dihadapkan pada pilihan: apakah kita harus menggunakan modul generik yang secara otomatis mendeteksi sistem operasi, atau menggunakan modul native yang khusus untuk distribusi tertentu?

Ansible menyediakan modul ansible.builtin.package sebagai lapisan abstraksi generik. Modul ini mendeteksi package manager yang berjalan di managed node menggunakan fakta sistem (ansible_facts.pkg_mgr) dan memetakan perintah ke modul native yang sesuai, seperti apt, dnf, atau yum.

flowchart TD
    A["Mulai Task Package"] --> B{"Tipe Modul?"}
    B -- "Generik: package" --> C["Ansible Deteksi ansible_facts.pkg_mgr"]
    C --> D{"Package Manager Terdeteksi?"}
    D -- "apt" --> E["Eksekusi Module apt (Debian/Ubuntu)"]
    D -- "dnf" --> F["Eksekusi Module dnf (RHEL 8+)"]
    D -- "yum" --> G["Eksekusi Module yum (CentOS 7)"]
    B -- "Native: apt/dnf/yum" --> H["Panggil Modul Native Secara Langsung"]
    H --> I["Gunakan Fitur Spesifik (contoh: purge, autoremove, dnf modules)"]
    E --> J["Selesai: Package Terinstal"]
    F --> J
    G --> J

Menggunakan modul generik package sangat membantu jika kita hanya perlu memastikan utilitas dasar seperti curl, git, atau tmux terinstal di semua server tanpa memedulikan sistem operasinya. Namun, modul generik memiliki keterbatasan besar: ia hanya mendukung opsi-opsi dasar yang umum bagi semua package manager (seperti name dan state).

Jika kita membutuhkan fitur-fitur lanjutan—seperti membersihkan file konfigurasi lama di Debian (purge pada modul apt), atau mengelola modul aplikasi di RHEL (module_hotfixes atau modular stream pada modul dnf)—kita wajib menggunakan modul native. Selain itu, nama paket antar distribusi sering kali berbeda. Sebagai contoh, server web Apache dinamai apache2 di Ubuntu/Debian, namun dinamai httpd di RHEL/CentOS. Oleh karena itu, pendekatan terbaik untuk infrastruktur multi-OS adalah memisahkan daftar nama paket per sistem operasi ke dalam variabel terpisah dan memanggil modul native menggunakan kondisi when.

Berikut adalah perbandingan fitur utama antara modul native apt dan dnf:

Fitur Modul apt (Debian/Ubuntu) Modul dnf (RHEL/CentOS/Fedora)
Pembersihan Konfigurasi Didukung via opsi purge: true Tidak didukung secara langsung
Pembersihan Dependency Didukung via opsi autoremove: true Didukung via opsi autoremove: true
Update Cache Paket Didukung via opsi update_cache: true Didukung via opsi update_cache: true
** modular Package Stream** Tidak didukung (menggunakan repositori PPA) Didukung secara native via sintaks @module
Instalasi Grup Menggunakan nama task khusus Didukung secara native via @Group Name

Mengelola Package di Debian dan Ubuntu dengan Modul apt #

Modul ansible.builtin.apt digunakan khusus untuk mengelola paket pada sistem berbasis Debian dan Ubuntu. Proses instalasi menggunakan modul ini memerlukan perhatian pada efisiensi eksekusi dan pengelolaan cache repositori.

Salah satu parameter yang paling sering digunakan adalah update_cache. Parameter ini setara dengan menjalankan perintah apt-get update di terminal. Jika kita menyetel update_cache: true pada setiap task instalasi paket, waktu eksekusi playbook kita akan menjadi sangat lambat karena Ansible harus menghubungi server repositori berkali-kali. Untuk mengoptimalkannya, kita harus menggunakan parameter cache_valid_time. Parameter ini memberi tahu Ansible untuk hanya memperbarui cache jika pembaruan terakhir sudah melewati batas waktu tertentu (dalam hitungan detik).

Mari kita pelajari contoh instalasi paket yang efisien dan idempoten:

# ANTI-PATTERN: Menggunakan loop untuk menginstal banyak package, sangat lambat karena memicu apt berkali-kali
- name: Install utility packages (loop)
  apt:
    name: "{{ item }}"
    state: present
  loop:
    - curl
    - git
    - tmux

# BENAR: Memberikan daftar package dalam bentuk list langsung ke parameter name, dieksekusi sekali jalan
- name: Install utility packages secara efisien
  apt:
    name:
      - curl
      - git
      - tmux
    state: present
    update_cache: true
    cache_valid_time: 3600  # Hanya jalankan 'apt update' jika cache berumur > 1 jam

Dengan memberikan list ke parameter name, Ansible akan mengabstraksikan instruksi tersebut menjadi satu panggilan perintah apt di latar belakang (misalnya apt-get install curl git tmux). Ini jauh lebih cepat dibandingkan memanggil perintah apt sebanyak tiga kali dalam perulangan loop.

Jika kita perlu menghapus paket secara tuntas, termasuk file konfigurasi global yang terkait dengannya, kita harus menyetel parameter state: absent bersama dengan purge: true dan autoremove: true:

- name: Hapus Apache2 secara bersih dari sistem
  apt:
    name: apache2
    state: absent
    purge: true         # Menghapus file konfigurasi global di /etc/apache2/
    autoremove: true    # Menghapus paket dependensi yang sudah tidak digunakan

Untuk menjamin keandalan aplikasi di server produksi, kita juga disarankan untuk menentukan versi spesifik dari paket yang akan diinstal. Hal ini mencegah aplikasi kita mengalami kerusakan akibat pembaruan versi paket yang tidak kompatibel:

- name: Install Nginx versi spesifik
  apt:
    name: nginx=1.24.0-1~jammy
    state: present

Mengelola Package di RHEL, CentOS, dan Fedora dengan yum dan dnf #

Pada sistem berbasis Red Hat (RHEL, CentOS, Rocky Linux, AlmaLinux, dan Fedora), manajemen paket dilakukan menggunakan modul ansible.builtin.yum (untuk versi lama seperti CentOS 7) atau ansible.builtin.dnf (untuk versi modern RHEL 8 ke atas).

Sistem RHEL modern menggunakan konsep Application Streams (modular packages) yang memungkinkan kita memilih aliran versi perangkat lunak tertentu yang ingin kita instal dari satu repositori yang sama. Modul dnf di Ansible mendukung penuh fitur ini secara native.

Berikut adalah contoh bagaimana kita menginstal modular package dan mengelola grup paket di CentOS/RHEL:

# Menginstal Node.js dari stream versi khusus menggunakan dnf
- name: Install Node.js versi 18 dari modular stream
  dnf:
    name: "@nodejs:18/common"
    state: present

# Menginstal kelompok paket pengembangan (Development Tools)
- name: Install grup Development Tools
  dnf:
    name: "@Development Tools"
    state: present

# Menghapus paket yang tidak diinginkan
- name: Hapus podman jika terinstal
  dnf:
    name: podman
    state: absent
    autoremove: true

Sintaks @ digunakan oleh DNF untuk mengidentifikasi bahwa nama yang kita masukkan adalah sebuah modul aplikasi atau grup paket, bukan paket tunggal biasa. Ini memberikan fleksibilitas tinggi bagi kita untuk mendeploy stack teknologi yang membutuhkan runtime versi tertentu secara konsisten.


Pengelolaan Repositori dan Kunci Keamanan #

Sebelum kita dapat menginstal paket yang tidak tersedia di repositori bawaan sistem operasi (seperti Docker, PostgreSQL, atau Node.js), kita harus menambahkan kunci repositori (GPG Key) dan alamat repositori itu sendiri ke dalam sistem managed node kita.

Ansible menyediakan modul deklaratif untuk tujuan ini. Kita tidak boleh menulis repositori secara manual ke /etc/apt/sources.list menggunakan modul teks karena hal tersebut tidak idempoten dan rentan terhadap duplikasi entri.

Berikut adalah cara mendeploy repositori pihak ketiga pada Debian/Ubuntu secara aman:

- name: Setup repositori Docker di Ubuntu
  block:
    # Mengunduh dan mendaftarkan GPG key repositori
    - name: Tambahkan GPG key resmi Docker
      apt_key:
        url: https://download.docker.com/linux/ubuntu/gpg
        state: present

    # Menambahkan repositori ke daftar sources.list.d
    - name: Tambahkan repositori Docker CE
      apt_repository:
        repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
        state: present
        filename: docker-ce  # Disimpan di /etc/apt/sources.list.d/docker-ce.list
        update_cache: true   # Jalankan apt update setelah repositori ditambahkan

    # Menginstal Docker setelah repositori siap
    - name: Install Docker Engine
      apt:
        name:
          - docker-ce
          - docker-ce-cli
          - containerd.io
        state: present

Sementara itu, pada sistem berbasis RHEL, pengelolaan repositori kustom dilakukan menggunakan modul ansible.builtin.yum_repository. Modul ini secara otomatis membuat file konfigurasi .repo di direktori /etc/yum.repos.d/:

- name: Tambahkan repositori PostgreSQL kustom di RHEL
  yum_repository:
    name: pgdg15
    description: PostgreSQL 15 RPM-based repository
    baseurl: https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/
    gpgcheck: true
    gpgkey: https://download.postgresql.org/pub/repos/yum/RPM-GPG-KEY-PGDG-15
    state: present

Dengan mengelola repositori sebagai resource terpisah menggunakan modul khusus ini, Ansible dapat memverifikasi integritas kunci GPG dan alamat repositori pada setiap run, serta memperbaruinya jika ada perubahan URL tanpa merusak konfigurasi repositori lainnya.


Analisis Mendalam State: present vs latest vs absent #

Pemilihan nilai untuk parameter state adalah keputusan desain yang sangat penting dalam configuration management. Ansible mendukung beberapa status untuk manajemen paket, dengan perilaku sebagai berikut:

  • present (atau installed): Status ini memastikan bahwa paket tersebut terinstal di server. Jika paket belum ada, Ansible akan menginstalnya. Jika paket sudah ada (terlepas dari versinya), Ansible tidak akan melakukan tindakan apa pun. Ini adalah status yang paling aman untuk operasional produksi karena menjaga stabilitas versi aplikasi kita.
  • latest: Status ini memastikan bahwa paket terinstal dengan versi paling baru yang tersedia di repositori. Jika paket sudah terinstal tetapi ada versi yang lebih baru di repositori, Ansible akan melakukan upgrade. Menggunakan state: latest di lingkungan produksi adalah anti-pattern karena pembaruan otomatis yang tidak terkontrol dapat merusak kompatibilitas aplikasi kita secara tiba-tiba tanpa pengujian.
  • absent (atau removed): Status ini memastikan paket dihapus dari sistem. Jika paket tidak ada, Ansible tidak akan melakukan apa pun (idempoten).

Berikut perbandingan skenario eksekusi untuk masing-masing status:

Kondisi Awal di Server Target State: present Target State: latest Target State: absent
Paket tidak ada Menginstal paket (status: changed) Menginstal versi terbaru (status: changed) Tidak melakukan apa pun (status: ok)
Paket ada (versi lama) Tidak melakukan apa pun (status: ok) Melakukan upgrade paket (status: changed) Menghapus paket (status: changed)
Paket ada (versi terbaru) Tidak melakukan apa pun (status: ok) Tidak melakukan apa pun (status: ok) Menghapus paket (status: changed)

Mari kita lihat contoh implementasi yang memperlihatkan bahaya penggunaan latest dan cara mengatasinya:

# ANTI-PATTERN: Menggunakan latest untuk instalasi paket di server produksi
- name: Install PostgreSQL versi terbaru
  apt:
    name: postgresql
    state: latest  # BISA MEMBREAK DATA JIKA VERSI MAYOR TIBA-TIBA NAIK!

# BENAR: Menggunakan present dengan versi spesifik untuk mengunci stabilitas sistem kita
- name: Pastikan PostgreSQL 15 terinstal dengan stabil
  apt:
    name: postgresql-15
    state: present

Modular Package Management: Python pip, Node npm, dan OS Packages #

Selain paket sistem operasi, sebagai developer kita sering kali harus mengelola paket yang spesifik untuk bahasa pemrograman tertentu, seperti paket Python menggunakan pip atau modul Node.js menggunakan npm.

Salah satu kesalahan umum yang harus kita hindari adalah menginstal paket bahasa pemrograman secara global ke sistem operasi menggunakan hak akses root (misalnya sudo pip install package). Praktik ini dapat merusak dependensi internal python milik sistem operasi dan menyebabkan kegagalan fungsi pada perkakas sistem bawaan Linux.

Solusinya adalah mengisolasi dependensi aplikasi ke dalam lingkungan virtual (virtual environment) untuk Python, atau direktori lokal untuk Node.js. Ansible menyediakan modul khusus seperti ansible.builtin.pip untuk menangani hal ini secara terisolasi.

- name: Kelola dependensi aplikasi Python dalam virtualenv
  block:
    # Menginstal system packages yang dibutuhkan untuk build python packages
    - name: Install requirements sistem
      apt:
        name:
          - python3-pip
          - python3-venv
          - build-essential
          - libpq-dev
        state: present

    # Membuat virtualenv dan menginstal library python di dalamnya secara aman
    - name: Install library Python ke dalam virtualenv aplikasi
      pip:
        name:
          - flask
          - psycopg2-binary
          - gunicorn
        virtualenv: /opt/myapp/venv
        virtualenv_python: python3
        state: present

    # Atau menginstal secara bulk menggunakan file requirements.txt
    - name: Install dependensi dari requirements.txt
      pip:
        requirements: /opt/myapp/requirements.txt
        virtualenv: /opt/myapp/venv
        state: present

Dengan mengisolasi pustaka eksternal ke dalam /opt/myapp/venv, file-file sistem operasi kita tetap bersih dan kita bebas mengelola versi modul Python tanpa takut merusak komponen sistem Linux lainnya.


Studi Kasus: Deployment Node.js & Docker Multi-Distribusi #

Untuk merangkum seluruh pemahaman kita tentang manajemen paket, mari kita buat sebuah playbook yang mengonfigurasi repositori dan menginstal runtime Node.js serta Docker CE pada klaster server heterogen (gabungan Debian/Ubuntu dan RedHat/CentOS).

Playbook ini akan menggunakan deteksi fakta sistem (ansible_os_family) untuk menentukan logika instalasi dan repositori yang sesuai:

# playbooks/deploy-package-infra.yml
---
- name: Deploy Repositori dan Paket Multi-Distribusi
  hosts: all
  become: true
  
  vars:
    nodejs_version: "18"
    common_utilities:
      - curl
      - git
      - vim
      - htop

  tasks:
    # 1. Instalasi utilitas dasar menggunakan modul generik (aman untuk multi-OS)
    - name: Install utilitas dasar di semua host
      package:
        name: "{{ item }}"
        state: present
      loop: "{{ common_utilities }}"

    # 2. Blok khusus untuk keluarga Debian/Ubuntu
    - name: Konfigurasi Repositori dan Paket Debian/Ubuntu
      block:
        - name: Tambahkan GPG Key repositori Docker (Debian/Ubuntu)
          apt_key:
            url: https://download.docker.com/linux/ubuntu/gpg
            state: present

        - name: Tambahkan repositori Docker CE (Debian/Ubuntu)
          apt_repository:
            repo: "deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable"
            state: present
            filename: docker-ce
            update_cache: true

        - name: Install Docker dan Node.js di Debian/Ubuntu
          apt:
            name:
              - docker-ce
              - docker-ce-cli
              - containerd.io
              - "nodejs"
            state: present
      when: ansible_os_family == "Debian"

    # 3. Blok khusus untuk keluarga RedHat/CentOS
    - name: Konfigurasi Repositori dan Paket RedHat/CentOS
      block:
        - name: Tambahkan repositori Docker CE (RedHat/CentOS)
          yum_repository:
            name: docker-ce-stable
            description: Docker CE Stable - $basearch
            baseurl: https://download.docker.com/linux/centos/7/$basearch/stable
            gpgcheck: true
            gpgkey: https://download.docker.com/linux/centos/gpg
            state: present

        - name: Aktifkan modular stream Node.js (RedHat/CentOS)
          dnf:
            name: "@nodejs:{{ nodejs_version }}/common"
            state: present

        - name: Install Docker di RedHat/CentOS
          dnf:
            name:
              - docker-ce
              - docker-ce-cli
              - containerd.io
            state: present
      when: ansible_os_family == "RedHat"

Playbook di atas menunjukkan bagaimana kita dapat menulis satu alur otomasi tunggal yang sangat tangguh. Ansible akan mengevaluasi setiap blok tugas secara dinamis. Jika dijalankan di server Ubuntu, hanya blok Debian yang dieksekusi, dan jika dijalankan di CentOS, hanya blok RedHat yang dieksekusi, sementara instalasi utilitas dasar berjalan di kedua sistem operasi dengan mulus.


Ringkasan #

  • Modul Generik vs Native — Gunakan modul package untuk utilitas lintas sistem operasi yang sederhana, dan gunakan modul native (apt, dnf, yum) untuk fitur-fitur spesifik distribusi Linux.
  • Optimasi Cache APT — Selalu kombinasikan update_cache: true dengan cache_valid_time pada modul apt untuk menghindari eksekusi update cache yang berulang-ulang dan lambat.
  • Hindari State Latest — Gunakan state: present dikombinasikan dengan nomor versi spesifik di server produksi untuk mencegah kerusakan akibat perubahan versi paket yang tidak diuji.
  • Instalasi Paket Efisien — Kirimkan daftar paket sebagai list langsung ke parameter name alih-alih menggunakan perulangan loop untuk mengurangi overhead eksekusi.
  • Manajemen Modular Stream — Manfaatkan kemampuan modul dnf untuk mengaktifkan Application Streams (@module:version) pada sistem RHEL modern.
  • Deklaratif Repositori — Kelola penambahan repositori eksternal menggunakan apt_repository atau yum_repository untuk menghindari kerusakan konfigurasi file sources.list.
  • Isolasi Pustaka Bahasa Pemrograman — Gunakan modul pip yang dikombinasikan dengan virtualenv untuk mengisolasi dependensi python aplikasi dari pustaka sistem operasi.
  • Pembersihan Sempurna — Gunakan kombinasi state: absent dengan parameter purge: true pada sistem Debian/Ubuntu untuk menghapus sisa file konfigurasi global paket.

← Sebelumnya: User & Permission   Berikutnya: Service →

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