Sharing Data Antar Container #

Salah satu kekuatan Docker adalah kemampuan menjalankan banyak container secara terisolasi. Tapi ada situasi di mana container-container itu harus saling berbagi data — entah itu file konfigurasi bersama, asset statis, output dari satu service yang jadi input service lain, atau data bersama yang diakses dari beberapa worker.

Berbagi data antar container bukan hal yang sulit, tapi ada banyak cara melakukannya dan setiap cara punya trade-off. Pilihan yang salah bisa menyebabkan masalah performa, kebocoran data antar tenant, atau konfigurasi yang rapuh.

Artikel ini membahas semua metode yang tersedia, use case masing-masing, dan best practice yang akan membantumu memilih pendekatan yang tepat untuk setiap situasi.

Mengapa Container Perlu Berbagi Data? #

Container secara default benar-benar terisolasi satu sama lain. Filesystem, network, dan process space dipisahkan oleh Linux namespaces. Tapi ada banyak skenario nyata di mana isolasi penuh terlalu kaku.

Contoh use case:

  • Web app + sidecar logger — container aplikasi menulis log ke sebuah direktori, dan container sidecar (misal Filebeat atau Promtail) membaca dan mengirim log ke centralized logging.
  • Web app + nginx reverse proxy — nginx membaca static files (gambar, CSS, JS) dari satu direktori yang juga ditulis atau di-copy oleh web app.
  • Multi-service database cluster — beberapa node database berbagi direktori data yang sama (walau ini biasanya lebih baik pakai shared network storage).
  • Data processor + analyzer — pipeline data di mana container pertama menulis hasil, lalu container kedua membaca dan menganalisis.
  • Shared config — beberapa service membaca konfigurasi yang sama (misal nginx.conf atau file sertifikat TLS) dari satu lokasi.
flowchart LR
    subgraph App["App Container"]
        W[Web App]
    end
    subgraph Log["Logger Container"]
        F[Filebeat]
    end
    subgraph Data[("Shared Volume<br/>logs/")]
        L1[app.log]
    end
    W -->|tulis| L1
    L1 -->|baca| F
    F -->|kirim| Ext[(Elasticsearch)]

Pada diagram di atas, logs/ adalah shared volume yang bisa ditulis oleh app dan dibaca oleh logger. Keduanya terisolasi dalam hal lain, tapi berbagi filesystem untuk data tertentu.

Metode Berbagi Data #

Ada beberapa metode untuk berbagi data antar container di Docker, dan masing-masing punya karakteristik berbeda.

Volume #

Volume adalah mekanisme berbagi data yang direkomendasikan Docker untuk hampir semua kasus. Volume di-manage oleh Docker, disimpan di /var/lib/docker/volumes/, dan di-mount ke path tertentu di dalam container.

Karakteristik:

  • Isolasi dari host filesystem — tidak bergantung pada struktur direktori host.
  • Portable — bisa di-backup, di-restore, dan di-move antar host.
  • AMAN UNTUK PRODUKSI — dikelola oleh Docker, support driver untuk distributed storage.

Membuat dan berbagi volume:

# Buat shared volume
docker volume create shared-data

# Jalankan container pertama yang menulis ke volume
docker run -d --name producer -v shared-data:/data alpine sh -c "echo hello > /data/message.txt"

# Jalankan container kedua yang membaca dari volume
docker run --rm --volumes-from producer alpine cat /data/message.txt
# Output: hello

--volumes-from adalah cara lama untuk berbagi volume antar container. Cara modern adalah dengan mount volume yang sama secara eksplisit di kedua container.

docker run -d --name producer -v shared-data:/data alpine sleep 3600
docker run -d --name consumer -v shared-data:/data alpine sleep 3600

Kedua container melihat direktori /data yang sama. Perubahan dari satu container langsung terlihat di container lain.

Bind Mount #

Bind mount me-mount direktori atau file dari host langsung ke dalam container. Berbeda dengan volume, bind mount tergantung pada struktur host.

Karakteristik:

  • Tergantung path host — tidak portable antar host dengan layout direktori berbeda.
  • Performa tinggi — native filesystem access, cocok untuk development.
  • Tidak cocok untuk production multi-host — kecuali di-mount dari shared filesystem (NFS, GlusterFS).
# Mount direktori host yang sama ke dua container
docker run -d --name producer -v /opt/shared:/data alpine sleep 3600
docker run -d --name consumer -v /opt/shared:/data alpine sleep 3600

Penggunaan paling umum: development workflow di mana source code di host di-mount ke container. Code editor di host mengedit file, aplikasi di container langsung melihat perubahannya.

tmpfs Mount #

tmpfs menyimpan data di memory (RAM), bukan di disk. Data hilang saat container berhenti.

Karakteristik:

  • Sangat cepat — data di memory.
  • Ephemeral — hilang saat container restart.
  • Tidak dibagi antar container secara native — tapi bisa dipakai untuk masing-masing container dengan data berbeda.
# tmpfs untuk satu container, 100MB
docker run -d --tmpfs /tmp:size=100m alpine sleep 3600

Use case: data sementara yang perlu cepat dan tidak perlu persistent. Contoh: cache, session data, file sementara hasil processing.

Catatan penting: tmpfs tidak benar-benar “berbagi” data antar container — masing-masing container punya tmpfs sendiri. Jika kamu butuh berbagi data, volume atau bind mount adalah pilihannya.

Container sebagai Volume Source #

Docker juga memungkinkan kamu me-mount filesystem dari satu container ke container lain, tanpa harus membuat named volume.

# Container pertama sebagai "data container"
docker create --name data-container -v /data alpine true

# Container kedua me-mount dari data-container
docker run --volumes-from data-container alpine ls /data

Pola ini dulu populer, tapi sekarang kurang direkomendasikan karena named volume lebih eksplisit dan mudah di-manage. --volumes-from masih bisa dipakai tapi dianggap warisan.

Perbandingan Metode #

Memilih metode yang tepat untuk situasi yang tepat adalah skill yang penting.

Metode Performa Portabilitas Aman Production Cocok untuk
Named Volume Tinggi Sangat tinggi Ya Hampir semua kasus production
Bind Mount Sangat tinggi (native) Rendah (tergantung host path) Hati-hati Development, konfigurasi yang perlu akses langsung dari host
tmpfs Sangat tinggi (RAM) Tidak portable Ya (untuk data ephemeral) Cache, session, data sementara
volumes-from Tinggi Sedang Legacy Kode lama, migrasi bertahap
Rekomendasi umum: Gunakan named volume sebagai default. Bind mount khusus untuk development di mana kamu perlu akses langsung ke source code di host. tmpfs untuk data ephemeral yang perlu performa tinggi. Hindari --volumes-from di kode baru.

Pola Penggunaan Lanjutan #

Sidecar Pattern #

Sidecar adalah pola di mana satu container “menempel” ke container utama untuk melakukan tugas tertentu. Salah satu use case sidecar yang paling umum adalah shared logging.

# docker-compose.yml
version: "3.8"
services:
  app:
    image: myapp:latest
    volumes:
      - logs:/app/logs

  log-shipper:
    image: filebeat:8
    user: root
    volumes:
      - logs:/var/log/app:ro
      - ./filebeat.yml:/usr/share/filebeat/filebeat.yml:ro
    depends_on:
      - app

volumes:
  logs:

Di sini, app menulis log ke /app/logs, dan log-shipper me-mount direktori yang sama secara read-only (ro). Filebeat membaca log dan mengirimnya ke Elasticsearch atau sistem logging lainnya.

Kelebihan pola ini:

  • Aplikasi tidak perlu tahu tentang logging infrastructure.
  • Log shipper bisa di-restart atau di-replace tanpa mengganggu aplikasi.
  • Clean separation of concerns.

Init Container Pattern #

Init container berjalan sebelum container utama dan menyiapkan data yang dibutuhkan.

# Kubernetes pod spec
apiVersion: v1
kind: Pod
metadata:
  name: app
spec:
  initContainers:
  - name: config-loader
    image: config-loader:latest
    volumeMounts:
    - name: config
      mountPath: /config
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: config
      mountPath: /app/config
  volumes:
  - name: config
    emptyDir: {}

Init container mengisi config volume, lalu container utama me-mount volume yang sama untuk membaca konfigurasi. Ini pattern yang umum di Kubernetes, dan bisa diadopsi di Docker Compose untuk workflow yang sama.

Shared Cache #

Beberapa container benefit dari shared cache (misal build cache atau download cache).

# docker-compose.yml untuk build pipeline
services:
  builder:
    image: golang:1.22
    volumes:
      - go-cache:/root/.cache/go-build
      - go-mod:/go/pkg/mod
    command: go build ./...

  tester:
    image: golang:1.22
    volumes:
      - go-cache:/root/.cache/go-build
      - go-mod:/go/pkg/mod
    command: go test ./...

volumes:
  go-cache:
  go-mod:

Build cache go-cache dan module cache go-mod dibagi antara container builder dan tester. Ini menghemat waktu download dan kompilasi ulang.


Best Practice #

Gunakan Named Volume untuk Hampir Semua Kasus #

Named volume adalah default yang aman. Ia portable, di-manage oleh Docker, dan support driver untuk advanced use case (NFS, cloud storage, dll).

# docker-compose.yml
services:
  app:
    volumes:
      - app-data:/var/lib/app

volumes:
  app-data:

Gunakan Read-Only Mount untuk Consumer #

Jika satu container hanya perlu membaca data yang ditulis container lain, mount sebagai read-only. Ini mengurangi risiko container consumer secara tidak sengaja mengubah data.

# Producer (write)
docker run -d --name producer -v shared:/data writer-image

# Consumer (read-only)
docker run -d --name consumer -v shared:/data:ro reader-image

:ro adalah flag read-only. Mount menjadi tidak bisa ditulis, sehingga container hanya bisa membaca.

Hindari Berbagi Filesystem yang Berubah Sangat Cepat #

Jika banyak container menulis dan membaca dari volume yang sama dengan throughput tinggi, pertimbangkan shared network storage (NFS, GlusterFS, cloud storage) atau database, bukan filesystem langsung.

Filesystem bersama tidak optimal untuk workload yang butuh konsistensi tinggi dan locking. Database dengan proper transaction handling jauh lebih cocok untuk data yang sering berubah dan diakses banyak proses.

Beri Nama Volume yang Jelas #

# BURUK: nama generik
volumes:
  - data:/data
  - logs:/logs

# BAGUS: nama yang menjelaskan isinya
volumes:
  - user-uploads:/var/lib/app/uploads
  - app-logs:/var/log/app
  - nginx-cache:/var/cache/nginx

Bersihkan Volume yang Tidak Terpakai #

Volume yang tidak di-mount ke container manapun masih menggunakan disk space. Docker tidak menghapusnya otomatis.

# Lihat volume yang tidak terpakai
docker volume ls -f dangling=true

# Hapus volume yang tidak terpakai
docker volume prune
docker volume prune menghapus SEMUA volume yang tidak digunakan. Pastikan kamu yakin tidak ada data penting di volume yang akan dihapus. Untuk production, lebih baik hapus volume satu per satu berdasarkan nama.

Backup Volume Secara Rutin #

Volume berisi data persisten. Ia perlu di-backup seperti database atau disk lain. Ada beberapa cara:

  • Mount volume ke container backup yang melakukan tar dan menyimpannya ke S3 atau storage lain.
  • Gunakan tool seperti docker-backup atau restic dengan Docker volume support.
  • Untuk Kubernetes, gunakan Velero atau tool backup lain.
# Backup volume ke file .tar.gz
docker run --rm \
  -v my-volume:/source:ro \
  -v $(pwd)/backup:/backup \
  alpine tar -czf /backup/my-volume-$(date +%F).tar.gz -C /source .

Anti-Pattern yang Harus Dihindari #

Mengubah Struktur Internal Container dari Container Lain #

# ANTI-PATTERN: container A mengubah binary di container B
services:
  app:
    volumes:
      - /usr/bin/app
  updater:
    volumes:
      - /usr/bin/app
    command: cp /new-binary /usr/bin/app

Ini sangat rapuh. Container A mungkin restart dengan image yang berbeda dan binary baru hilang. Untuk update, rebuild image, bukan patch dari container lain.

Menggantungkan Diri pada --volumes-from di Kode Baru #

Pola --volumes-from adalah legacy. Untuk kode baru, gunakan named volume dan mount secara eksplisit.

Berbagi Volume yang Berisi Database State #

Database biasanya punya concurrency control sendiri. Berbagi database files antar container di filesystem yang sama tanpa orkestrasi matang akan menyebabkan data corruption.

Gunakan replication atau shared network storage dengan proper locking untuk database multi-instance. Jangan share database files antar container secara langsung.

Mengabaikan Permission #

# ANTI-PATTERN: container A jalan sebagai root, tulis file, container B jalan sebagai non-root, tidak bisa baca
docker run -d --name writer -v shared:/data alpine sh -c "echo data > /data/file"
docker run --rm -v shared:/data:ro alpine cat /data/file
# Output: cat: can't open '/data/file': Permission denied

Pastikan user ID di kedua container konsisten, atau set permission volume dengan benar saat initialization.

# SOLUSI: set permission saat membuat volume
docker run -d --name writer \
  -v shared:/data \
  alpine sh -c "chown 1000:1000 /data && echo data > /data/file"

Contoh Kasus Dunia Nyata #

Static Site Generator + Web Server #

Pola umum untuk static site generator (Hugo, Jekyll, Gatsby): satu container build, container lain serve.

# docker-compose.yml
services:
  builder:
    image: hugomods/hugo:base
    volumes:
      - ./site:/src
      - public:/src/public
    command: hugo --minify

  server:
    image: nginx:alpine
    depends_on:
      - builder
    volumes:
      - public:/usr/share/nginx/html:ro
    ports:
      - "8080:80"

volumes:
  public:

Builder menghasilkan static files ke volume public, lalu nginx me-mount volume yang sama (read-only) untuk serve. Saat build diulang, file di volume public ter-update, tapi nginx tidak perlu restart — ia membaca file dari disk setiap request.

Multi-Stage Build dengan Artifact Sharing #

# Dockerfile
FROM golang:1.22 AS builder
WORKDIR /app
COPY . .
RUN go build -o server

FROM alpine:3.19
COPY --from=builder /app/server /server
CMD ["/server"]

Multi-stage build adalah “container sharing” di level image — build stage menghasilkan artifact, lalu production stage hanya me-copy binary yang dibutuhkan. Ini mengurangi ukuran image akhir dan memastikan production image hanya berisi yang diperlukan untuk runtime.

Shared Configuration Directory #

Untuk konfigurasi bersama (file sertifikat, nginx.conf, app.yml), satu volume bisa berisi semua file dan di-mount ke beberapa service.

# Setup config volume sekali
docker volume create app-config
docker run --rm -v app-config:/config -v ./configs:/source alpine cp -r /source/. /config/

# Semua service mount config yang sama
docker run -d --name app -v app-config:/etc/app:ro myapp
docker run -d --name proxy -v app-config:/etc/nginx:ro nginx

Pola ini memastikan semua service melihat konfigurasi yang sama. Update konfigurasi = update volume = restart service yang butuh (opsional, kalau service mendukung hot-reload).

Performance Considerations #

Pemilihan metode berbagi data mempengaruhi performa. Untuk workload tertentu, perbedaan ini signifikan.

Local filesystem vs network filesystem. Volume di-host di local filesystem jauh lebih cepat daripada network filesystem (NFS, EFS, dll). Untuk database atau workload I/O-intensive, local SSD atau NVMe adalah pilihan terbaik.

Mount propagation. Docker punya beberapa mode mount propagation: rprivate (default), shared, slave, rshared, rslave. Untuk kebanyakan kasus, default sudah cukup. Untuk kasus advanced seperti saat menggunakan bind mount yang harus terlihat di sub-mount, kamu mungkin perlu shared atau rshared.

Volume driver. Docker mendukung driver untuk berbagai backend: local (default), nfs, aws (EFS), gcs (Google Cloud Storage), azurefile. Pilih driver sesuai kebutuhan performa dan availability.

# docker-compose.yml dengan NFS driver
volumes:
  shared-data:
    driver: local
    driver_opts:
      type: nfs
      o: addr=10.0.0.5,rw
      device: ":/path/to/dir"
Benchmark tip: Untuk workload yang sensitif terhadap I/O, benchmark volume sebelum commit ke production setup. Buat container yang melakukan read/write besar, ukur throughput, dan bandingkan antar driver atau storage option.

Ringkasan #

  • Container secara default terisolasi, tapi ada banyak situasi di mana mereka perlu berbagi data — sidecar logging, shared config, data pipeline, cache sharing, dll.
  • Named volume adalah default yang aman — portable, di-manage Docker, support driver untuk distributed storage. Gunakan ini untuk hampir semua kasus.
  • Bind mount cocok untuk development (source code di host) atau konfigurasi yang perlu akses langsung dari host. Hati-hati dengan portabilitas.
  • tmpfs untuk data ephemeral yang butuh performa tinggi (cache, session, file sementara). Tidak benar-benar “berbagi” antar container.
  • Sidecar pattern adalah pola paling umum untuk berbagi log: app menulis, log shipper membaca via volume yang sama.
  • Read-only mount untuk container consumer yang hanya perlu baca, untuk keamanan.
  • Backup volume secara rutin — sama seperti database atau disk lain yang berisi data persisten.
  • Hindari berbagi database files antar container tanpa orkestrasi. Gunakan replication atau shared network storage.
  • Perhatikan permission — pastikan user ID konsisten antar container yang berbagi volume.
  • Jangan pakai --volumes-from untuk kode baru. Gunakan named volume dengan mount eksplisit.

← Sebelumnya: Backup & Restore Data   Berikutnya: Network Overview →

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