Metadata dan sejarah image pada chapter sebelumnya menutup dengan satu pertanyaan praktis yang belum terjawab: bagaimana membawa sebuah image ke mesin lain ketika registry sama sekali tidak bisa dihubungi. Server produksi di jaringan terisolasi, ruang uji laboratorium tanpa akses internet, sampai kebijakan kantor yang hanya mengizinkan pemindahan berkas lewat USB — semua skenario itu tidak bisa dilayani oleh docker pull. Docker menyediakan jalur kedua untuk kebutuhan semacam itu: sepasang perintah docker save dan docker load yang mengemas image menjadi berkas arsip biasa, seanggun tag di registry tetapi sepraktis berkas yang bisa digenggam. Chapter ini mempraktikkan pasangan tersebut pada image yang sudah kita kenal, membedah isi arsipnya sampai ke blob-nya, lalu menegaskan batasnya dengan sepasang perintah yang sering tertukar: docker export dan docker import.

Menyimpan Image ke Berkas Arsip #

Objek latihan kita adalah nginx:1.29-alpine — image 47.9MB yang sempat kita hapus pada Chapter Menghapus Image demi melihat wajah baris Untagged dan Deleted. Kita tarik kembali varian tersebut terlebih dahulu, persis seperti pull pertama kita:

bash
$ docker pull nginx:1.29-alpine

Output yang Dihasilkan:

text
1.29-alpine: Pulling from library/nginx
d4c8b2f7a9e1: Pull complete
3f6b1c9e2a7d: Pull complete
...
Digest: sha256:6d9f2c8a4e7b1d5f3c9a2e8b7d4f1c6a9e3b5d7f2c8a4e1b6d9f3c7a5e2b8d1f
Status: Downloaded newer image for nginx:1.29-alpine
docker.io/library/nginx:1.29-alpine

Baris-baris layer di tengah dipotong — lima layer unduhannya sama persis dengan pull pertama. Sekarang kita kemas image ini menjadi satu berkas:

bash
$ docker save -o nginx.tar nginx:1.29-alpine

Perintah ini tidak mencetak apa pun saat berhasil — keheningan yang sudah kita kenal sebagai tanda sukses. Bukti keberhasilannya hidup di berkas yang baru lahir di folder kerja:

bash
$ ls -lh nginx.tar

Output yang Dihasilkan:

text
-rw-r--r-- 1 budi budi 48M Oct 14 09:41 nginx.tar

Mari kita telaah dua perintah di atas:

  1. -o nginx.tar — flag --output menentukan nama berkas tujuan; tanpa flag ini, arsip dialirkan ke standard output, perilaku yang justru kita manfaatkan pada bagian kompresi nanti.
  2. nginx:1.29-alpine sebagai argumen — referensi image yang akan disimpan; satu perintah docker save boleh menyebut banyak referensi sekaligus, dan image tetap utuh di penyimpanan lokal — save menyalin, bukan memindahkan.
  3. 48M pada kolom ukuran — angka yang sepadan dengan SIZE 47.9MB pada tabel docker images; arsip memuat seluruh layer image, dan di mesin dengan containerd image store bawaan Engine 29, isi arsip mengikuti tata letak OCI yang sama dengan yang dipakai registry.

Memuat Kembali tanpa Registry #

Berkas nginx.tar kini bisa dibawa ke mana pun — USB, server berkas, atau SSH. Sebelum membuktikan daya gunanya, kita ciptakan dulu keadaan yang merebutkannya: hapus image dari penyimpanan lokal, lengkap dengan seluruh layernya:

bash
$ docker rmi nginx:1.29-alpine

Output yang Dihasilkan:

text
Untagged: nginx:1.29-alpine
Untagged: nginx@sha256:6d9f2c8a4e7b1d5f3c9a2e8b7d4f1c6a9e3b5d7f2c8a4e1b6d9f3c7a5e2b8d1f
Deleted: sha256:5b1e8c3a9f7d...
...

Mesin kita kembali tak mengenal image itu. Saatnya memuat dari arsip — tanpa registry, tanpa jaringan, tanpa autentikasi:

bash
$ docker load -i nginx.tar

Output yang Dihasilkan:

text
Loaded image: nginx:1.29-alpine

Mari kita telaah baris tunggal tersebut beserta konsekuensinya:

  1. -i nginx.tar — flag --input membaca arsip dari berkas; bentuk panjang docker load < nginx.tar juga sah dan berguna di dalam skrip.
  2. Loaded image: nginx:1.29-alpine — tag asal dilaporkan utuh; satu baris seperti ini muncul untuk setiap image yang dimuat, sehingga arsip multi-image mudah dibaca hasilnya.
  3. IMAGE ID yang sama — docker images kembali menampilkan 5b1e8c3a9f7d lama; arsip membawa konfigurasi image yang sama sehingga identitasnya tidak berubah, dan mesin yang sudah menyimpan layer bersama tidak akan menyimpan salinan kedua.

Perintah yang sama persis bekerja di mesin lain. Siti cukup menerima berkas nginx.tar — lewat USB di skenario kantor kita — lalu menjalankan docker load -i nginx.tar; baris Loaded image: nginx:1.29-alpine yang sama akan muncul di terminalnya, dan image siap dijalankan tanpa Docker Hub pernah dilibatkan.

Menyimpan Banyak Image Sekaligus #

Satu arsip boleh memuat banyak image — cukup sebutkan seluruh referensinya setelah flag output:

bash
$ docker save -o bundle.tar nginx:1.29-alpine alpine:3.20
bash
$ ls -lh bundle.tar

Output yang Dihasilkan:

text
-rw-r--r-- 1 budi budi 48M Oct 14 09:52 bundle.tar
bash
$ docker load -i bundle.tar

Output yang Dihasilkan:

text
Loaded image: alpine:3.20
Loaded image: nginx:1.29-alpine

Mari kita telaah perilaku arsip campuran ini:

  1. Ukuran bundle tetap 48M — padahal dua image ditampung. Layer dasar alpine yang dipakai bersama oleh nginx:1.29-alpine dan alpine:3.20 hanya ditulis sekali di dalam arsip — aturan berbagi layer dari Chapter Konsep Image Layer terbawa sampai ke berkas arsip.
  2. Dua baris Loaded image: — setiap tag dilaporkan terpisah; urutan barisnya mengikuti susunan internal arsip, bukan urutan argumen kita.
  3. Pasangan save-load berbasis referensi, bukan container — yang disimpan adalah image utuh beserta sejarah buildnya; container yang pernah berjalan dari image itu tidak ikut, dan writable layer-nya memang bukan milik image.

Kompresi dan Pemipaan #

Karena docker save tanpa -o mengalirkan arsip ke standard output, berkas hasilnya bisa langsung dipipakan ke alat kompresi:

bash
$ docker save nginx:1.29-alpine | gzip > nginx.tar.gz
bash
$ ls -lh nginx.tar.gz

Output yang Dihasilkan:

text
-rw-r--r-- 1 budi budi 47M Oct 14 09:55 nginx.tar.gz

Hasil 47M dari 48M memang tampak kecil — dan itu jujur. Blob layer di dalam arsip OCI memang sudah tersimpan terkompresi, sehingga kompresi kedua di luar hanya meraih sisa metafora kecil. Nilai pola pipa ini bukan pada angka kompresinya, melainkan pada komposisinya: arsip tidak pernah mendarat sebagai berkas perantara di disk. Bentuk paling bergunanya bahkan melompat langsung ke mesin tujuan:

bash
$ docker save nginx:1.29-alpine | gzip | ssh [email protected] docker load

Satu baris tersebut mengemas, mengompresi, mengirim lewat SSH, dan memuat di mesin Siti — tanpa satu pun berkas sementara di kedua sisi. Di sisi penerima, docker load membaca arsip ter-gzip langsung tanpa perlu di-ekstrak lebih dulu.

Membedah Isi Arsip #

Berkas .tar bisa dibuka seperti arsip tar biasa, dan isinya menjelaskan bagaimana image “dibungkus”:

bash
$ tar -tf nginx.tar

Output yang Dihasilkan:

text
oci-layout
index.json
manifest.json
blobs/
blobs/sha256/
blobs/sha256/6d9f2c8a4e7b1d5f3c9a2e8b7d4f1c6a9e3b5d7f2c8a4e1b6d9f3c7a5e2b8d1f
blobs/sha256/5b1e8c3a9f7d628601351dcc926f01844ebcf5190d9a18a16365f8395d00f6d1
blobs/sha256/508a98663e79dcd9a2195f49df9da5f3219621d495c48ab3da39ef5ce8cbd001
...

Tiga baris blob terakhir dipotong — lima layer image mengisi sisa daftar. Salah satu berkas kecil di dalamnya adalah peta bagi docker load:

bash
$ tar -xOf nginx.tar manifest.json

Output yang Dihasilkan:

json
[
  {
    "Config": "blobs/sha256/5b1e8c3a9f7d628601351dcc926f01844ebcf5190d9a18a16365f8395d00f6d1",
    "RepoTags": [
      "nginx:1.29-alpine"
    ],
    "Layers": [
      "blobs/sha256/508a98663e79dcd9a2195f49df9da5f3219621d495c48ab3da39ef5ce8cbd001",
      "blobs/sha256/cc47fb74efcb6767bc48270f7a0a8e07500f5bdb85d6d056f5432976e65f5c7c",
      "blobs/sha256/8a7966a2cbe2915c3abf199f6054bf8145658a9c18ff8325a3954f360d3f147f",
      "blobs/sha256/a7786f83bcace3f8c1577294ed38cb640904e82e7ec12145b56df6376627c15b",
      "blobs/sha256/7e5434d8121c9d0ba4486c5373f016f2d2bc31bd93e40811efeb94410a3eb839"
    ]
  }
]

Tabel berikut memetakan peran tiap penghuni arsip:

Isi arsipPeranKeterangan
manifest.jsonpeta bagi docker loadmencantumkan RepoTags, blob konfigurasi, dan daftar blob layer
index.json dan oci-layoutpenanda tata letak OCImenyatakan arsip mengikuti standar tata letak image yang sama dengan registry
blobs/sha256/ konfigurasimetadata imageisi field .Config yang kita bedah pada Chapter Image Inspect dan History — ENV, CMD, ENTRYPOINT
blobs/sha256/ manifest imagedaftar isi imageblob dengan digest 6d9f2c8a... — digest yang sama dengan output pull kita
blobs/sha256/ layerisi filesystem per layerlima blob terkompresi; nama berkasnya adalah digest isi — content-addressable

Dua hal layak digarisbawahi dari tabel itu. Pertama, digest yang tertera pada blob manifest adalah karakter demi karakter digest yang pernah tercetak pada docker pull — arsip ini benar-benar membawa identitas mesin yang sama dengan registry. Kedua, docker load bekerja murah karena strukturnya: ia cukup membaca peta, memverifikasi digest tiap blob, dan menyalinnya ke penyimpanan lokal — tidak ada proses build apa pun di dalamnya.

save versus push #

Dua jalur distribusi yang kita kuasai sekarang patut duduk berdampingan dalam satu tabel:

Aspekdocker save dan docker loaddocker push dan docker pull
Butuh registrytidak — berkas arsip adalah satu-satunya perantaraya — registry menyimpan dan menyebarkan image
Butuh jaringantidak, setelah arsip berada di tanganya, ke registry dan dari registry
Identitas yang dibawatag dan IMAGE IDtag dan digest — kepastian mesin dari Chapter Image Digest
Skala penyebaransatu arsip untuk satu tujuanbanyak mesin menarik bersamaan
Riwayat dan versidikelola manual — nama berkas adalah satu-satunya catatandikelola registry — tag, digest, dan halaman riwayatnya
Cocok untukmesin terisolasi, transfer fisik, audit berkas tunggaltim, publikasi, dan segala hal yang berulang

Aturan praktisnya sederhana: registry tetap jalur utama, dan save–load adalah jalur luar biasa untuk keadaan luar biasa. Detail operasional push dan pull kelak kita praktikkan tuntas pada Section Registry dan Distribusi.

docker export dan import Bukan save dan load #

Docker punya sepasang perintah lain yang tampak serupa namun bekerja pada objek yang berbeda: docker export mengambil container, bukan image, dan menghasilkan filesystem yang pipih tanpa sejarah. Kita peragakan pada container web yang masih menyala:

bash
$ docker export -o web-flat.tar web
bash
$ ls -lh web-flat.tar

Output yang Dihasilkan:

text
-rw-r--r-- 1 budi budi 187M Oct 14 10:03 web-flat.tar

Angka 187M sepadan dengan image nginx varian Debian, ditambah writable layer container yang ikut terbawa. Sekarang kita coba membangkitkan image dari arsip pipih ini:

bash
$ docker import web-flat.tar web-flat:1.0

Output yang Dihasilkan:

text
sha256:53b39d621bdb94d08db529a2fdfba8e4d38b9ef48ea2ac01375dce19d641b7fb

Image lahir dengan ID baru. Namun begitu dijalankan, keanehannya muncul seketika:

bash
$ docker run --rm web-flat:1.0

Output yang Dihasilkan:

text
docker: Error response from daemon: No command specified.

Perbaikannya tersedia lewat flag --change yang menyuntikkan metadata saat import dilakukan:

bash
$ docker import --change 'CMD ["nginx", "-g", "daemon off;"]' web-flat.tar web-flat:1.1

Output yang Dihasilkan:

text
sha256:81afc607e8790660b72aaebb89471f3d40f65ee4e642dd08e5e7d1ee2c2ea9af

Perintah docker run dari image ini kini berhasil — tetapi setiap metadata yang hilang harus ditulis ulang satu per satu, persis pekerjaan yang sudah diselesaikan oleh penulis image asalnya. Tabel berikut menegaskan pembagian tugas keduanya:

Aspekdocker save dan docker loaddocker export dan docker import
Objek sumberimagecontainer yang sedang berjalan atau berhenti
Isi arsiplayer-layer image plus konfigurasi dan sejarah buildfilesystem pipih hasil gabungan layer dan writable layer
Metadata ENV, CMD, ENTRYPOINTikut utuhhilang — disuntik ulang lewat --change
Struktur layerbertahan — layer bersama tetap dibagikanlenyap — menjadi satu gumpalan berkas
Ukuran arsipsepadan SIZE imagesepadan filesystem + writable layer
Cocok untukdistribusi image antar mesinmemindahkan kondisi berkas sebuah container

Kedua arsip percobaan kita bereskan sebelum chapter ditutup — web-flat:1.0 dan web-flat:1.1 tidak akan dipakai lagi:

bash
$ docker rmi web-flat:1.0 web-flat:1.1
$ rm web-flat.tar

Output yang Dihasilkan:

text
Untagged: web-flat:1.0
Untagged: web-flat:1.1
...

Arsip nginx.tar, nginx.tar.gz, dan bundle.tar sengaja kita biarkan di folder kerja sebagai bahan cerita — dan di sinilah satu urusan rumah tangga baru menunggu: arsip-arsip itu, image yang dimuat ulangnya, build cache dari sesi-sesi build, dan sisa eksperimen menumpuk bersama di disk. Berapa besar sesungguhnya wilayah yang dikuasai Docker di mesin kita, dan bagaimana membereskannya dengan aman, adalah pekerjaan chapter penutup section ini — docker system df dan keluarga perintah prune.

Referensi #