PostgreSQL kini hidup di container bernama db dengan tabel produk yang terisi, dan ritualnya ternyata ringkas: sunting environment, pasang volume, baca log, lalu sapa lewat klien. Pertanyaan alami berikutnya adalah seberapa jauh ritual itu bisa dipindahkan ke mesin database lain — dan kandidat paling layak untuk ujian pertama adalah yang paling banyak dipakai di dunia: MySQL. Image resminya menempuh filosofi yang sama dengan postgres, tetapi keluarga variabel MYSQL_* punya aturan main sendiri: satu variabel wajib, tiga lainnya hanya bekerja sebagai inisialisasi, dan servernya menyala lewat tarian dua babak yang tercatat jelas di log. Chapter ini menjalankan MySQL 8.4 berdampingan dengan PostgreSQL kita, tanpa satu paket host pun terpasang.
Menjalankan MySQL dengan Empat Variabel #
Perintah berikut menyalakan MySQL 8.4 sebagai container db-mysql, dengan identitas yang kita kenali dari proyek katalog:
$ docker run -d --name db-mysql \
-e MYSQL_ROOT_PASSWORD=rahasia123 \
-e MYSQL_DATABASE=katalog \
-e MYSQL_USER=katalog \
-e MYSQL_PASSWORD=rahasia123 \
-v db-data-mysql:/var/lib/mysql \
mysql:8.4Output yang Dihasilkan:
bcf53b80b3c48dd510df3831043c21df1910a208d005e8dc5cd60f0bb72d7cc3Image mysql:8.4 sudah ada di lokal sejak Chapter Permission dan Ownership Volume, jadi daemon langsung melahirkan container tanpa menarik apa pun. Mari kita telaah enam bagian perintah ini:
-d --name db-mysql— detached dengan namadb-mysql; nama yang berbeda daridbmembuat kedua database hidup berdampingan di mesin yang sama tanpa saling menabrak.-e MYSQL_ROOT_PASSWORD=rahasia123— satu-satunya variabel yang benar-benar wajib: entrypoint MySQL menolak bekerja tanpa kata sandi root. Dua jalan pintas sah ada —MYSQL_ALLOW_EMPTY_PASSWORDuntuk latihan santai danMYSQL_RANDOM_ROOT_PASSWORDyang menghasilkan sandi acak di log — tetapi keduanya jarang tepat untuk kebiasaan baik.-e MYSQL_DATABASE=katalog— database awal yang dibuat saat inisialisasi; padanan langsungPOSTGRES_DB.-e MYSQL_USER=katalogdanMYSQL_PASSWORD— user terbatas yang diciptakan sekaligus, lengkap dengan sandinya; user ini otomatis diberi hak penuh atasMYSQL_DATABASE, sehingga aplikasi tidak perlu menyentuh root sama sekali.-v db-data-mysql:/var/lib/mysql— volume bernama sendiri pada direktori data resmi MySQL; perhatikan bahwa path-nya memang/var/lib/mysql, bukan path postgres — dan volumenya juga kita beri nama berbeda supaya tidak tertukar.mysql:8.4— seri LTS 8.4, tag yang dipin eksplisit alih-alihlatest.
Ketiga variabel opsional itu bekerja hanya sebagai inisialisasi: sama seperti postgres, entrypoint MySQL memprosesnya saat direktori data masih kosong dan mengabaikannya begitu volume berisi. Aturan wajib-opsionalnya dirangkum pada tabel berikut:
| Variabel | Status | Peran |
|---|---|---|
MYSQL_ROOT_PASSWORD | wajib — salah satu dari tiga opsi sandi root | kata sandi user root saat inisialisasi |
MYSQL_DATABASE | opsional | database awal yang dibuat saat inisialisasi |
MYSQL_USER | opsional | user aplikasi yang dibuat bersama database awal |
MYSQL_PASSWORD | opsional | kata sandi untuk MYSQL_USER |
Log Startup yang Dua Kali Berkata Siap #
MySQL membutuhkan waktu lebih lama untuk penyalaan pertama — belasan detik untuk menyiapkan berkas data — dan lognya menceritakan tarian dua babak yang menarik:
$ docker logs db-mysqlOutput yang Dihasilkan:
2026-10-13 20:52:02+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server started.
2026-10-13 20:52:02+00:00 [Note] [Entrypoint]: Initializing database files
2026-10-13 20:52:08.617293Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.4.11) initializing of database in progress...
...
2026-10-13 20:52:24+00:00 [Note] [Entrypoint]: Database files initialized
2026-10-13 20:52:24+00:00 [Note] [Entrypoint]: Starting temporary server
2026-10-13 20:52:27+00:00 [Note] [Entrypoint]: Temporary server started.
2026-10-13 20:52:27.812315Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.11' socket: '/var/run/mysqld/mysqld.sock' port: 0
2026-10-13 20:52:27+00:00 [Note] [Entrypoint]: Creating database katalog
2026-10-13 20:52:27+00:00 [Note] [Entrypoint]: Creating user katalog@'%'
2026-10-13 20:52:27+00:00 [Note] [Entrypoint]: Giving user katalog@'%' access to schema katalog
...
2026-10-13 20:52:31+00:00 [Note] [Entrypoint]: Temporary server stopped.
2026-10-13 20:52:31+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up.
...
2026-10-13T20:52:33.104522Z 0 [System] [MY-015015] [Server] MySQL Server Initialization - start.
2026-10-13T20:52:34.512844Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.4.11' socket: '/var/run/mysqld/mysqld.sock' port: 3306 mysqlx socket: '/var/run/mysqld/mysqlx.sock' mysqlx port: 33060.Baris-baris menengah dipotong agar ringkas. Mari kita telaah alurnya:
Initializing database files— babak pertama: entrypoint menyiapkan direktori data dari nol di dalam volume; inilah pekerjaan yang membuat penyalaan pertama terasa lambat.Starting temporary server— server sementara dinyalakan bukan untuk melayani klien, melainkan agar entrypoint bisa terhubung sebagai klien dan menjalankan tugas administratif — tiga barisCreating database katalog,Creating user katalog@'%', hingga pemberian hak aksesnya adalah hasil kerja server sementara ini membaca variabelMYSQL_*kita.Temporary server stoppeddanReady for start up— babak pertama selesai; berkas-berkas sudah siap dan server sungguhan dinyalakan sebagai proses utama container.ready for connectionsmuncul dua kali — yang pertama, denganport: 0, adalah milik server sementara yang hanya mendengarkan socket lokal; yang terakhir milik server sungguhan: MySQL siap menerima koneksi, versinya 8.4.11 persis seperti seri yang dijanjikan tagmysql:8.4, socket lokalnya hidup di/var/run/mysqld/mysqld.sock, dan port TCP-nya 3306 — tetap di dalam container, karena kita sekali lagi tidak memakai-p.mysqlxport 33060 — MySQL 8 juga membuka protokol X DevAPI tambahan; cukup diketahui ada, kita tidak memakainya di course ini.
Rentang antara kedua kalimat siap itulah jendela rapuhnya: container sudah tampak hidup di docker ps, padahal server sungguhan belum membuka port. Aplikasi Budi dulu gagal terhubung tepat di jendela seperti ini pada stack Compose — dan itulah alasan Chapter depends_on dan Startup Order membutuhkan healthcheck sebelum menyalakan service yang bergantung.
Sesi mysql sebagai User katalog #
Klien resmi image ini adalah mysql, dan pola docker exec -it membawanya ke terminal kita:
$ docker exec -it db-mysql mysql -ukatalog -prahasia123Output yang Dihasilkan:
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 12
Server version: 8.4.11 MySQL Community Server - GPL
Copyright (c) 2000, 2026, Oracle and/or its affiliates.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| katalog |
+--------------------+
2 rows in set (0.00 sec)
mysql> USE katalog;
Database changed
mysql> SHOW TABLES;
Empty set (0.00 sec)
mysql> CREATE TABLE produk (nama VARCHAR(100), harga INT);
Query OK, 0 rows affected (0.03 sec)
mysql> INSERT INTO produk VALUES ('Kopi Torabika', 15000);
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM produk;
+---------------+-------+
| nama | harga |
+---------------+-------+
| Kopi Torabika | 15000 |
+---------------+-------+
1 row in set (0.00 sec)
mysql> exit
ByeBaris peringatan di awal dan banner selamat datang adalah ucapan pembuka klien mysql; seluruh sesi berjalan dalam satu blok karena docker exec -it menautkan terminal kita langsung ke proses klien. Mari kita telaah isinya:
-ukatalog -prahasia123— klien menerima user dan sandi lewat argumen rapat tanpa spasi; peringatan yang menyertainya jujur — sandi pada baris perintah terbaca siapa pun yang bisa membaca daftar proses. Untuk latihan ini dapat ditoleransi, dan pola--env-filedari Chapter Environment Variable menjadi jalan yang lebih tertata begitu daftar variabel memanjang.SHOW DATABASEShanya menampilkan dua — kita masuk sebagai user katalog, dan MySQL menyembunyikan database yang bukan hak kita; information_schema dan katalog saja yang terlihat. Bandingkan dengan sesi root yang akan menampilkanmysql,performance_schema, dansys— tabel-tabel internal milik server.USE katalog— padanan\cdi psql; perintah ini memilih database kerja tanpa membuka koneksi baru, dijawab singkatDatabase changed.SHOW TABLESmengembalikanEmpty set— database katalog baru saja lahir dan masih kosong;Empty setadalah bahasa MySQL untuk “nol baris”.CREATE TABLEdanINSERT— skema yang sama dengan versi postgres-nya, hanya idiom tipenya menyesuaikan:VARCHAR(100)danINT; jawabanQuery OKdiikuti jumlah baris yang terpengaruh.exit— menutup sesi klien; container dan servernya tetap hidup, persis perilakudocker execyang kita kenal.
Catatan Autentikasi dan Karakter Set #
Dua perilaku bawaan MySQL 8 pantas dicatat sebelum kita lanjut. Pertama soal autentikasi: sejak seri 8, user baru lahir dengan plugin caching_sha2_password, dan di 8.4 plugin lama mysql_native_password sudah tidak aktif — klien lawas yang hanya mengerti plugin lama akan menolak terhubung sampai klien itu dimutakhirkan. Kebanyakan driver modern sudah lama kompatibel, jadi catatan ini biasanya baru terasa saat menyambungkan tool peninggalan.
Kedua soal karakter: seluruh database baru di MySQL 8 otomatis memakai utf8mb4 — UTF-8 sungguhan yang menampung hingga empat byte per karakter, sehingga emoji dan aksara Nusantara tersimpan tanpa kecelakaan. Nama Kopi Torabika kita memang polos, tetapi kolom yang sama akan sama tenang menampung nama produk seaneh apa pun yang dirancang tim pemasaran.
mysqladmin ping sebagai Healthcheck #
MySQL punya padanan pg_isready yang lebih ringkas dari sesi penuh — alat administrasi mysqladmin dengan subperintah ping:
$ docker exec db-mysql mysqladmin ping -ukatalog -prahasia123Output yang Dihasilkan:
mysql: [Warning] Using a password on the command line interface can be insecure.
mysqld is aliveSatu kalimat mysqld is alive adalah seluruh laporannya: server menjawab ketukan dan siap bekerja. Exit code 0 menjadikannya bahan healthcheck yang jujur untuk skrip maupun untuk kunci healthcheck di compose.yaml — peran yang sudah kita praktikkan untuk postgres pada Chapter Healthcheck dan Service Readiness dan akan berulang setiap kali stack butuh tahu bahwa databasenya benar-benar bangun.
Peta Menuju MariaDB #
Sebelum melangkah, mari kita pasang peta kecil untuk chapter berikutnya. MariaDB adalah fork MySQL yang kompatibel protokol, dan image resminya menerima variabel MYSQL_* yang barusan kita pakai — tetapi ia lebih suka dipanggil dengan namanya sendiri:
| Di MySQL | Padanan di MariaDB | Peran |
|---|---|---|
MYSQL_ROOT_PASSWORD | MARIADB_ROOT_PASSWORD | kata sandi root saat inisialisasi |
MYSQL_DATABASE | MARIADB_DATABASE | database awal |
MYSQL_USER | MARIADB_USER | user aplikasi |
MYSQL_PASSWORD | MARIADB_PASSWORD | kata sandi user aplikasi |
Mengapa ada dua nama untuk hal yang sama, kapankah MariaDB menjadi pilihan yang lebih tepat daripada MySQL, dan di mana letak jebakan yang membuat keduanya tidak bisa saling menukar volume — itulah agendanya Chapter Menjalankan MariaDB.
MySQL 8.4 kini berdiri di samping PostgreSQL: dua server, dua volume, satu mesin, nol paket host. Namun cerita keluarga MySQL belum selesai — pada 2010-an sebagian komunitasnya berpisah dan membangun rumah sendiri bernama MariaDB, dan rumah itu kini menjadi default database di banyak distribusi Linux. Chapter Menjalankan MariaDB membawa kita ke sana: image yang hampir identik, variabel yang hampir sama, dan satu-dua kejutan yang hanya muncul ketika dua dunia ini dipaksa berbagi barang.
Referensi #
- MySQL — Docker Hub — daftar variabel MYSQL_*, opsi sandi root, dan catatan autentikasi bawaan.
- docker container run — CLI Reference
— flag
-e,-v, dan-duntuk menjalankan image database. - docker container exec — CLI Reference — menjalankan klien mysql di dalam container yang hidup.
- docker container logs — CLI Reference — membaca riwayat startup dua babak MySQL.