Belajar Jenkins - Integrasi Docker di Dalam Jenkins Pipeline
Episode 8 of 21

Belajar Jenkins - Integrasi Docker di Dalam Jenkins Pipeline

Container adalah kunci reproducibility: build harus menghasilkan hasil yang sama di mesin mana pun. Episode ini membahas menjalankan pipeline di dalam container agent Docker, membangun dan mendorong image dari Jenkins, integrasi dengan Docker registry, serta perbandingan keamanan Docker-in-Docker versus host socket.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Pendahuluan

Di episode 7 sebelumnya pipeline kita sudah cepat dengan eksekusi paralel dan cerdas dengan eksekusi kondisional. Tapi ada masalah klasik yang belum terselesaikan: reproducibility. Build yang berhasil di mesin Andi belum tentu berhasil di mesin Budi — versi Node berbeda, library sistem hilang, konfigurasi lokal bertabrakan. Di dunia nyata ini menyebabkan fenomena yang paling dibenci developer: "kok jalan di mesin saya?".

Container adalah jawabannya. Analogikan seperti laboratorium kimia: setiap eksperimen dijalankan di ruangan steril yang diatur ulang dari nol setiap kali, dengan peralatan dan bahan yang persis sama. Tidak ada sisa percobaan kemarin yang mencemari hasil hari ini. Di episode ini kita menggabungkan kekuatan itu dengan Jenkins: menjalankan pipeline di dalam container Docker, membangun dan mendorong image dari dalam pipeline, dan terhubung ke registry secara aman — sehingga pipeline menghasilkan artefak container yang siap deploy ke mana pun.

Pembahasan Utama

Menjalankan Pipeline di Dalam Container Agent

Cara paling sederhana mendapatkan environment steril adalah meminta agent mengeksekusi seluruh stage di dalam sebuah container. Jenkins Docker plugin akan menarik image, menjalankan kontainer dengan workspace yang ter-mount ke dalamnya, mengeksekusi steps di dalamnya, lalu menghapusnya setelah selesai:

JenkinsSeluruh pipeline di dalam container
pipeline {
    agent {
        docker {
            image 'node:20-alpine'
            args '-v /tmp/npm-cache:/root/.npm'
        }
    }
 
    stages {
        stage('Install & Test') {
            steps {
                sh 'node --version'
                sh 'npm ci'
                sh 'npm test'
            }
        }
    }
}

Image node:20-alpine menyediakan Node.js versi 20 yang sudah terkunci — setiap build berjalan dengan toolchain yang identik, apa pun yang terpasang di mesin host. Argumen args melewatkan opsi ekstra ke docker run, di sini volume untuk cache npm agar instalasi dependensi tidak selalu diunduh dari awal.

Container tidak harus menaungi seluruh pipeline. Agent Docker juga bisa diterapkan per stage, berguna saat stage yang berbeda butuh toolchain berbeda:

JenkinsAgent Docker per stage
stage('Build Frontend') {
    agent {
        docker {
            image 'node:20-alpine'
        }
    }
    steps {
        sh 'npm run build'
    }
}
 
stage('Build Backend') {
    agent {
        docker {
            image 'maven:3.9-eclipse-temurin-17'
        }
    }
    steps {
        sh 'mvn -q package'
    }
}

Note

Agent Docker bergantung pada plugin Docker dan daemon Docker di node agent. Bedakan dua hal yang mudah tertukar: agent { docker } menjalankan steps pipeline di dalam container (toolchain), sedangkan membangun image membutuhkan Docker CLI yang terhubung ke daemon — topik berikutnya. Keduanya kerap dipakai bersamaan.

Membangun Image Docker dari Jenkins

Setelah aplikasi siap, tujuan akhirnya adalah image container. Membangun image berarti memanggil docker build, dan itu membutuhkan Docker CLI di dalam container agent plus akses ke daemon Docker. Di sinilah muncul pilihan arsitektur penting dengan konsekuensi keamanan besar.

Docker-in-Docker (DinD). Pendekatan ini menjalankan daemon Docker yang terpisah di dalam container. Container dalam dibungkus container luar. Terisolasi penuh, tetapi lebih berat dan overhead-nya nyata.

Host socket mount. Pendekatan ini me-mount socket daemon host ke dalam container agent dengan volume /var/run/docker.sock. Container agent lalu bisa memerintah daemon host secara langsung — cepat, tanpa daemon ekstra, dan menjadi cara paling umum di pipeline CI. Konsekuensinya: container agent mendapatkan kendali penuh atas daemon host, yang artinya setara akses root di host tersebut.

JenkinsAgent container dengan host socket
pipeline {
    agent {
        docker {
            image 'docker:24'
            args '-v /var/run/docker.sock:/var/run/docker.sock'
        }
    }
 
    stages {
        stage('Build Image') {
            steps {
                sh 'docker build -t company/api-service .'
            }
        }
    }
}

Warning

Mem-mount /var/run/docker.sock memberi akses setara root ke host. Jika container agent bisa diakses pihak tidak tepercaya — misalnya pipeline dari fork atau PR eksternal — itu menjadi jalur RCE langsung ke server. Aturan keamanan Jenkins dari episode 3 tetap berlaku: jangan pernah mengeksekusi build dari kode tidak tepercaya pada agent yang memiliki akses ke daemon host. Di lingkungan yang sangat ketat, preferensi diberikan ke DinD atau daemon Docker terpisah yang di-scope khusus.

Integrasi dengan Docker Registry

Image yang sudah dibangun harus sampai ke registry agar bisa di-deploy. Untuk autentikasi yang aman, Jenkins menyediakan helper docker.withRegistry yang mengombinasikan URL registry dengan credentials dari credentials store — tanpa pernah menuliskan password di Jenkinsfile:

JenkinsBuild dan push image dengan autentikasi
stage('Build & Push Image') {
    steps {
        script {
            docker.withRegistry('https://index.docker.io/v1/', 'docker-hub-credentials') {
                def image = docker.build("company/api-service:${env.GIT_COMMIT}")
                image.push()
                image.push('latest')
            }
        }
    }
}

Cara membacanya: docker.withRegistry membuka sesi terautentikasi ke registry, lalu blok di dalamnya bisa membangun dan mendorong image. Credentials docker-hub-credentials diambil dari credentials store — cukup diisi sekali di Manage Jenkins. Image diberi tag unik berdasarkan commit, lalu didorong dengan dua tag: satu tag immutable per commit (bisa di-rollback presisi), dan satu tag latest untuk kemudahan deploy.

Contoh Multi-Stage Dockerfile

Image production yang baik memisahkan tahap build dari tahap runtime agar image akhir sekecil mungkin. Dockerfile multi-stage berikut membangun aplikasi frontend di stage builder, lalu hanya menyalin hasil akhirnya ke image nginx yang ramping:

Dockerfile multi-stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
 
FROM nginx:1.27-alpine
COPY --from=builder /app/dist /usr/share/nginx/html
EXPOSE 80

Stage pertama mengunduh dan mengunci dependensi lalu membangun aplikasi. Stage kedua tidak membawa Node.js, source code, atau node_modules — hanya hasil dist yang disalin ke image nginx. Hasilnya image yang berukuran puluhan MB bukan ratusan MB, dengan permukaan serangan yang jauh lebih kecil.

Contoh Pipeline Lengkap

Menggabungkan semuanya: build berjalan di container agent yang steril, image multi-stage dibangun, dan didorong ke registry dengan autentikasi tersimpan aman:

JenkinsPipeline Docker end-to-end
pipeline {
    agent {
        docker {
            image 'docker:24'
            args '-v /var/run/docker.sock:/var/run/docker.sock'
        }
    }
 
    stages {
        stage('Test') {
            steps {
                sh 'npm test'
            }
        }
 
        stage('Build & Push Image') {
            steps {
                script {
                    docker.withRegistry('https://index.docker.io/v1/', 'docker-hub-credentials') {
                        def image = docker.build("company/api-service:${env.GIT_COMMIT}")
                        image.push()
                        image.push('latest')
                    }
                }
            }
        }
    }
 
    post {
        cleanup {
            cleanWs()
        }
    }
}

Tip

Perhatikan bahwa pipeline lengkap ini memakai host socket, yang cepat dan sederhana. Untuk environment yang lebih serius, pertimbangkan menyiapkan agent Docker yang sudah mengantongi daemon pribadi, atau menunggu episode 13 yang membahas Jenkins Configuration as Code untuk mengelola konfigurasi agent ini secara repeatable dan bisa diaudit.

Penutup

Pada episode 8 ini kita membedah peran Docker dalam pipeline Jenkins dari tiga arah: menjalankan steps di dalam container agent dengan agent { docker { image ... } } untuk toolchain yang steril dan reproducible, membangun image dari dalam pipeline menggunakan Docker CLI dengan dua arsitektur daemon — Docker-in-Docker yang terisolasi atau host socket mount yang cepat namun berisiko — serta mendorong image ke registry secara aman dengan docker.withRegistry dan credentials store. Kita juga melihat Dockerfile multi-stage yang memisahkan builder dari runtime untuk image yang ramping.

Inti yang harus kalian bawa:

  • agent { docker { image 'node:20-alpine' } } menjalankan seluruh pipeline di environment yang terkunci dan seragam.
  • Agent Docker per stage memungkinkan satu pipeline memakai toolchain berbeda tiap tahap.
  • Mem-mount /var/run/docker.sock setara memberi akses root ke host; hindari pada build yang menerima kode tidak tepercaya.
  • docker.withRegistry(url, credentialsId) membuat autentikasi push image tersimpan aman tanpa password di Jenkinsfile.
  • Dockerfile multi-stage memangkas ukuran image dan memperkecil permukaan serangan.

Pipeline kalian sekarang menghasilkan image container yang siap deploy. Di episode 9 berikutnya kita menghadapi masalah skala yang berbeda: reusability — ketika Jenkinsfile yang sama harus dipakai puluhan repository, copy-paste menjadi mimpi buruk. Kita akan membahas Jenkins Shared Libraries untuk memusatkan logika pipeline dalam satu repo yang dikelola tim. Sampai jumpa di episode 9!

Belajar Jenkins - Integrasi Docker di Dalam Jenkins Pipeline | Belajar Jenkins