Siklus hidup yang kita kuasai pada Chapter Menghentikan dan Menjalankan Container meninggalkan satu pekerjaan rumah yang belum dibereskan: setiap container yang berhenti tetap menetap di mesin, dan daftar docker ps -a kita perlahan memanjang. quirky_einstein sudah menjadi penghuni tetap sejak Chapter Menjalankan Container Pertama, dan tidak ada satu pun perintah pada chapter sebelumnya yang membuatnya pergi. Container berhenti memang tidak memakan CPU, tetapi writable layer-nya tetap memakan disk, namanya tetap memakan perhatian, dan pada mesin yang dipakai bergantian, daftar panjang semacam itu cepat atau lambat menjadi sumber kebingungan. Chapter ini melengkapi siklus hidup dengan langkah terakhirnya — penghapusan — mulai dari docker rm untuk satu container, flag --rm yang mencegah sampah lahir, sampai docker container prune yang membersihkan massal, sekaligus membahas satu pertanyaan penting: apa sebenarnya yang ikut terbuang saat sebuah container dihapus?

Menghapus Container yang Sudah Berhenti #

Perintah penghapusan adalah docker rm — bentuk modernnya docker container rm, dan keduanya setiap saat bisa saling menggantikan. Syarat utamanya tegas: container harus dalam keadaan berhenti. Kita mulai dari penghuni paling lama di mesin kita:

bash
$ docker rm quirky_einstein

Output yang Dihasilkan:

text
quirky_einstein

Mari kita telaah perintah dan output di atas:

  1. Output quirky_einstein — sama seperti stop dan start, perintah penghapusan mencetak kembali identifier yang kita sebut sebagai konfirmasi; penghapusan adalah operasi permanen, dan satu baris ini adalah tanda terima resminya.
  2. Identifier bebas dipilih — nama, ID pendek 12 karakter, maupun ID penuh 64 karakter sama-sama diterima; kebiasaan memakai nama seperti ini yang membuat --name pada docker run terasa berharga.
  3. Banyak container sekaligus — docker rm menerima beberapa argumen sekaligus, misalnya docker rm web cache, dan mencetak satu baris konfirmasi per container yang berhasil dihapus.

Bukti bahwa penghuni lama kita benar-benar pergi:

bash
$ docker ps -a

Output yang Dihasilkan:

text
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                     NAMES
f7c2a91b3e55   nginx     "/docker-entrypoint.…"   26 minutes ago   Up 5 minutes    0.0.0.0:8080->80/tcp, [::]:8080->80/tcp   web
3a1c8f2e9b7d   redis:7   "docker-entrypoint.s…"   20 minutes ago   Up 20 minutes   6379/tcp                                  cache

Tabel kini hanya menampung dua container yang sedang hidup — baris quirky_einstein lenyap bersama seluruh jejaknya. Penghapusan memang bukan pematian: stop menidurkan container agar bisa dibangunkan, sedangkan rm membuat container tidak pernah ada.

Ketika Container Masih Hidup #

Perlindungan bawaan Docker langsung terasa begitu kita mencoba menghapus container yang masih berjalan. Mari kita uji nasibnya pada web — murni sebagai percobaan, karena container ini masih kita perlukan:

bash
$ docker rm web

Output yang Dihasilkan:

text
Error response from daemon: You cannot remove a running container f7c2a91b3e55913c4a5f1c6c0a4d8e2b3f4a5b6c7d8e9f0a1b2c3d4e5f60718a. Stop the container before attempting removal or force remove

Mari kita telaah isi galat di atas:

  1. You cannot remove a running container — daemon menolak permintaan; web sedang hidup, dan menghapus container hidup tanpa peringatan adalah pintu menuju kecelakaan, sehingga Docker menutupnya secara bawaan.
  2. ID penuh 64 karakter ikut dicantumkan — daemon menyebut container dengan identitas lengkapnya agar tidak ada salah sasaran, termasuk saat kita menghapus lewat ID pendek.
  3. Dua jalan keluar ditawarkan persis di dalam pesannya — hentikan container lebih dulu lalu hapus, atau paksa penghapusan; kalimat Stop the container before attempting removal or force remove adalah peta kedua jalurnya.

Jalur pertama sudah kita kuasai: docker stop web diikuti docker rm web — sopan, bersih, dan memberi aplikasi kesempatan beres-beres. Jalur kedua adalah flag --force atau bentuk pendeknya -f. Agar web tetap utuh untuk chapter-chapter berikutnya, kita pakai container sekali pakai sebagai kelinci percobaan — image alpine:3.20 yang mungil, 7.8MB, belum pernah kita tarik ke mesin ini:

bash
$ docker run -d --name uji-rm alpine:3.20 sleep 300

Output yang Dihasilkan:

text
Unable to find image 'alpine:3.20' locally
3.20: Pulling from library/alpine
...
Status: Downloaded newer image for alpine:3.20
8bba006035ae1e292f702693fc7538c98ec49c6d6377af5cf0f324900559aef1

Baris layer dan nilai digest dipotong agar ringkas — alpine hanya berlapis sangat tipis. Container uji-rm kini hidup menjalankan sleep 300, dan kita hapus paksa dalam keadaan menyala:

bash
$ docker rm -f uji-rm

Output yang Dihasilkan:

text
uji-rm

Mari kita telaah apa yang dikerjakan flag -f:

  1. Dua langkah dalam satu perintah — rm -f menghentikan container lalu menghapusnya; secara efektif perintah ini adalah docker kill dan docker rm yang dirangkai, bukan docker stop dan docker rm.
  2. Penghentiannya memakai SIGKILL — tanpa sinyal sopan dan tanpa masa tenggang, persis seperti docker kill pada chapter sebelumnya; aplikasi di dalamnya tidak diberi kesempatan beres-beres.
  3. Tempatnya di alur kerja — flag ini nyaman untuk container latihan dan container sekali pakai yang tinggal namanya, tetapi untuk aplikasi yang sedang menulis data, jalur stop dulu lalu rm tetap merupakan urutan yang benar; risiko data setengah jadi yang kita bahas pada Chapter Menghentikan dan Menjalankan Container berlaku sama persis di sini.

Hapus Otomatis dengan Flag –rm #

Galat barusan terjadi karena ada container yang tertinggal hidup — tetapi masalah yang lebih sering menghantui pemula adalah kebalikannya: container yang tertinggal berhenti. Setiap docker run tanpa perlakuan khusus meninggalkan bekas, dan bekas itulah yang hari ini bernama quirky_einstein sebelum kita bersihkan. Docker menyediakan pencegahannya langsung pada docker run: flag --rm memerintahkan daemon menghapus container secara otomatis begitu prosesnya selesai. Kita uji dengan container perkenalan yang sudah menjadi image lokal:

bash
$ docker run --rm hello-world

Output yang Dihasilkan:

text
Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
...

Sisa pesan dipotong — isinya persis seperti yang kita baca pada Chapter Menjalankan Container Pertama. Bedanya ada di belakang layar: begitu program /hello selesai dan container berhenti, daemon langsung membersihkan container itu tanpa menunggu perintah apa pun dari kita. Sekarang kita ulangi tanpa flag tersebut:

bash
$ docker run hello-world

Output yang Dihasilkan:

text
Hello from Docker!
...

Pesan yang tercetak sama persis — dipotong lebih pendek karena tidak ada yang baru — tetapi kali ini container bekasnya menetap. Docker bahkan mengarangkannya nama otomatis, dengan kebiasaan kata-sifat-plus-nama-tokoh yang sudah kita kenal:

bash
$ docker ps -a

Output yang Dihasilkan:

text
CONTAINER ID   IMAGE         COMMAND                  CREATED              STATUS                          PORTS                                     NAMES
f7c2a91b3e55   nginx         "/docker-entrypoint.…"   30 minutes ago       Up 9 minutes                    0.0.0.0:8080->80/tcp, [::]:8080->80/tcp   web
3a1c8f2e9b7d   redis:7       "docker-entrypoint.s…"   24 minutes ago       Up 24 minutes                   6379/tcp                                  cache
93f2ea643623   hello-world   "/hello"                 About a minute ago   Exited (0) About a minute ago                                             elegant_nightingale

Mari kita telaah baris ketiga pada tabel di atas:

  1. elegant_nightingale baru saja lahir dan pensiun — ini bekas docker run hello-world yang kita jalankan tanpa --rm; status Exited (0) dicatat satu menit lalu, dan tanpa intervensi, baris ini akan menetap selama-lamanya seperti nenek moyangnya quirky_einstein.
  2. Bekas pemakaian --rm tidak tampak — percobaan pertama tidak meninggalkan baris apa pun; itulah seluruh perbedaan dua flag ini: --rm mencegah sampah lahir, docker rm membersihkannya setelah lahir.
  3. Ukuran masalahnya kecil, frekuensinya besar — satu hello-world hanya menyisakan writable layer yang nyaris kosong, tetapi kebiasaan menjalankan container percobaan berulang kali tanpa --rm akan menghasilkan tumpukan puluhan baris dalam hitungan hari.

Membersihkan Massal dengan docker container prune #

Ketika daftar container berhenti sudah membludak, membersihkannya satu per satu menjadi pekerjaan yang membosankan. Perintah docker container prune menghapus semua container yang sedang berhenti dalam satu ayunan — saat ini satu-satunya kandidat di mesin kita adalah elegant_nightingale:

bash
$ docker container prune

Output yang Dihasilkan:

text
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
93f2ea6436236d2fab8d40b5820a5b069a473db00997be00f2187b5fec591ed4

Total reclaimed space: 0B

Mari kita telaah empat bagian output di atas:

  1. Baris WARNING! This will remove all stopped containers. — daemon menyatakan dampak perintahnya terlebih dahulu; semua container berstatus Exited dan Created akan dihapus tanpa memandang asal-usul.
  2. Prompt [y/N] dan jawaban y — prune bersifat destruktif sehingga meminta konfirmasi; huruf besar pada N menandakan jawaban bawaan bila kita hanya menekan Enter. Flag --force melewati prompt ini — berguna di skrip, berbahaya di tangan yang terburu-buru.
  3. Deleted Containers: dengan ID penuh — daftar korban dicetak dengan identitas lengkapnya; satu baris untuk elegant_nightingale kita. Filter --filter until=24h membatasi korban hanya pada container yang berumur lebih dari satu hari, dan --filter label= membatasi pada yang berlabel tertentu.
  4. Total reclaimed space: 0B — ruang disk yang direbut kembali; angka 0B bukan kegagalan. Writable layer hello-world nyaris kosong karena program /hello tidak menulis apa pun, dan angka ini memang mengukur writable layer container — bukan ukuran image, yang tidak tersentuh sama sekali.

Writable Layer yang Ikut Terbuang #

Pertanyaan penutup chapter ini: ketika sebuah container dihapus, apa sebenarnya yang terbuang? Jawabannya menegaskan arsitektur yang kita kenal sejak tahap create pada Chapter Menjalankan Container Pertama — sebuah container adalah image ditambah satu writable layer tipis di puncak tumpukan layer image yang bersifat baca-saja. Seluruh tulisan yang dilakukan proses di dalam container mendarat di layer teratas tersebut. Dan docker rm membuang tepat bagian itu: writable layer beserta konfigurasi container lenyap, sementara seluruh layer image tetap utuh di penyimpanan lokal. Buktinya bisa kita lihat sekarang juga, setelah semua container latihan tadi kita bereskan:

bash
$ docker images

Output yang Dihasilkan:

text
REPOSITORY    TAG      IMAGE ID       CREATED       SIZE
nginx         latest   613f27b0341a   3 days ago    187MB
redis         7        dc30543fe5a6   2 weeks ago   138MB
alpine        3.20     2676beef0bef   2 weeks ago   7.8MB
hello-world   latest   e6590340b2f4   2 years ago   13.3kB

Mari kita telaah keempat baris di atas:

  1. Empat image tetap utuh — uji-rm yang kita rm -f dan elegant_nightingale yang kita prune lenyap sebagai container, tetapi image alpine:3.20 dan hello-world yang melahirkan mereka tetap tinggal; docker rm tidak pernah menyentuh image asalnya.
  2. Konsekuensi nyatanya — menjalankan docker run hello-world atau docker run -d alpine:3.20 sleep 300 berikutnya akan instan tanpa satu baris pull, karena cetakbirunya masih ada.
  3. Konsekuensi yang lebih penting — data yang ditulis proses ke dalam container ikut terbuang bersama writable layer; halaman yang kita edit di dalam web, berkas yang dibuat uji-rm, semuanya tidak akan mengikuti container baru dari image yang sama. Data yang memang harus bertahan melewati penghapusan membutuhkan volume atau bind mount — materi Section Penyimpanan Data — dan struktur layer yang membuat semua ini mungkin kita bedah tuntas pada Chapter Konsep Image Layer di Section berikutnya.

Ringkasan Perintah Penghapusan #

Empat alat penghapusan pada chapter ini masing-masing menjawab kebutuhan yang berbeda. Tabel berikut merangkumnya:

PerintahYang dihapusKapan dipakai
docker rm namaSatu atau beberapa container yang sudah berhentiPembersihan selektif setelah memeriksa docker ps -a
docker rm -f namaContainer apa pun, termasuk yang masih hidupContainer sekali pakai atau mesin latihan
docker run –rm …Container itu sendiri begitu proses selesaiTugas sekali jalan agar tidak meninggalkan bekas
docker container pruneSemua container berstatus Exited atau CreatedBersih-bersih massal setelah sesi latihan

Mesin kita kini berada di titik yang rapi: hanya web dan cache yang hidup, tanpa satu pun bekas menumpuk, dan empat image siap pakai di penyimpanan lokal. Tetapi ada satu kebiasaan penting yang belum kita miliki — selama ini kita hanya bisa mengamati container dari luar lewat tabel docker ps, tanpa pernah tahu apa yang sebenarnya terjadi di dalam web sepanjang hari ini: permintaan apa saja yang datang, jawaban apa yang diberikan, dan pesan apa yang tercatat ketika sebuah halaman tidak ditemukan. Jawaban untuk semua itu mengalir lewat stdout dan stderr container, dan perintah pembacanya adalah docker logs — materi Chapter Melihat Log Container.

Referensi #