Docker merekomendasikan untuk menjalankan hanya satu proses per container, yang secara umum berarti bahwa setiap container harus menjalankan satu perangkat lunak. Mari kita bedah program apa saja yang mendasari LEMP:
Linux adalah sistem operasi yang dijalankan Docker, sehingga kita hanya memiliki Nginx, MySQL, dan PHP. Untuk mempermudah, kita juga akan menambahkan phpMyAdmin ke dalamnya. Oleh karena itu, kita sekarang membutuhkan container berikut:
Nah disini kita akan menyiapkan container dan membuat container tersebut dapat berinteraksi satu sama lain?
Docker Desktop dilengkapi dengan alat bernama Docker Compose yang memungkinkan Anda mendefinisikan dan menjalankan aplikasi Docker multi-container(jika sistem Anda berjalan di Linux, Anda perlu menginstalnya secara terpisah).
Container diatur di dalam file konfigurasi YAML dan Docker Compose akan menangani pembuatan image dan memulai container, serta beberapa hal lainnya seperti menghubungkan container secara otomatis ke jaringan internal.
File konfigurasi YAML adalah awal kita: buka editor teks favorit Anda dan tambahkan file docker-compose.yml baru ke direktori pilihan Anda di mesin lokal (komputer Anda), dengan konten berikut:
# Services
services:
# Nginx Service
nginx:
image: nginx:1.27
ports:
- 80:80
Penjelasan :
Buka terminal jalan kan docker compose :
docker compose up -d
Penjelasan :
Buka web browser anda, lalu kikan http:localhost
Untuk melihat container apa saja yang sedang berjalan, ketikan perintah berikut :
docker compose ps
Dan untuk menghentikan container ketikan perintah berikut :
docker compose stop
Pada titik ini, Anda mungkin bertanya-tanya apa perbedaan antara service, image, dan container. Service hanyalah salah satu komponen aplikasi Anda, seperti yang tercantum di docker-compose.yml. Setiap service mengacu pada image, dan container berisi beberapa service yang terkait.
Untuk perintah lengkap docker compose dapat dilihat disini.
Pada bagian ini, Nginx akan menyajikan file index.php sederhana melalui PHP-FPM, yang merupakan manajer proses yang paling banyak digunakan untuk PHP.
Ubah konten docker-compose.yml dengan yang berikut ini:
# Services
services:
# Nginx Service
nginx:
image: nginx:1.27
ports:
- 80:80
volumes:
- ./src:/var/www/php
- ./nginx/conf.d:/etc/nginx/conf.d
depends_on:
- php
# PHP Service
php:
image: php:8.2-fpm
working_dir: /var/www/php
volumes:
- ./src:/var/www/php
Beberapa hal terjadi di sini: mari kita lupakan layanan Nginx sejenak. Kita fokus pada service PHP yang baru saja ditambahkan.
Kita mulai dari key image php:8.2-fpm, sesuai dengan tag 8.2-fpm image dari hub resmi PHP, kita akan menggunakan php versi 8.2 dari PHP-FPM.
Mari lewati working_dir untuk saat ini, dan lihat key volume pada kedua service. /var/www/php merupakan link/tautan dari folder src pada direktori lokal. Service nginx dan php berbagi volume pada direktori yang sama.
Buat direktori src (pada level yang sama dengan docker-compose.yml) dan tambahkan file index.php berikut ke dalamnya:
Selanjutnya terkait konfigurasi nginx pada service nginx yang akan mengarah ke kode aplikasi kita:
- ./nginx/conf.d:/etc/nginx/conf.d
/etc/nginx/conf.d merupakan link dari nginx/conf.d. Nginx secara otomatis membaca file yang diakhiri dengan .conf yang terletak di direktori /etc/nginx/conf.d.
Buat folder nginx/conf.d dan tambahkan file default.conf berikut ke dalamnya:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /var/www/php;
index index.php;
location ~* \.php$ {
fastcgi_pass php:9000;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
}
}
Kita mengarahkan root ke /var/www/php, yang merupakan direktori tempat kita memasang kode aplikasi kita di container Nginx dan PHP, dan kita menyetel indeks ke index.php.
Perhatikan juga baris berikut :
fastcgi_pass php:9000;
Ini memberitahu Nginx untuk meneruskan permintaan file PHP ke port kontainer PHP 9000, yang merupakan port default dari PHP-FPM. Secara internal, Docker Compose akan secara otomatis menyelesaikan kata kunci php ke alamat IP pribadi apa pun yang ditetapkan ke container PHP.
Ini merupakan salah satu fitur dari Docker Compose dimana saat start-up, ia akan secara otomatis menyiapkan jaringan internal di mana setiap container dapat ditemukan melalui nama layanannya.
Terakhir, mari kita lihat bagian konfigurasi terakhir dari layanan Nginx:
depends_on:
- php
Terkadang, urutan Docker Compose memulai container itu penting. Karena kita ingin Nginx meneruskan permintaan PHP ke port kontainer PHP 9000, kesalahan berikut mungkin terjadi jika Nginx sudah siap sebelum PHP:
[emerg] 1#1: host not found in upstream "php" in /etc/nginx/conf.d/php.conf:7
nginx_1 | nginx: [emerg] host not found in upstream "php" in /etc/nginx/conf.d/php.conf:7
nginx_1 exited with code 1
Hal ini menyebabkan proses Nginx terhenti, dan karena container Nginx hanya akan berjalan selama proses Nginx aktif, container tersebut juga akan berhenti. Konfigurasi depend_on memastikan container PHP akan dimulai sebelum Nginx, sehingga menyelamatkan kita dari situasi di atas.
Direktori dan struktur file Anda sekarang akan terlihat seperti ini:
lemp/
├── nginx/
│ └── conf.d/
│ └── php.conf
├── src/
│ └── index.php
└── docker-compose.yml
Sekarang kita siap untuk menjalankan test kedua yaitu menjalankan php di dalam webserver nginx. Kembali ke terminal Anda dan jalankan perintah yang sama lagi (kali ini, image PHP akan diunduh):
docker compose up -d
Sekarang buka browser, lalu akses kembali “http:localhost” :
Fungsi working_diretory pada service php. Jika kita mejalankan docker compose ps, kita akan melihat dua container yg sedang berjalan. Mari kita periksa container PHP:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
lemp-nginx-1 nginx:1.27 "/docker-entrypoint.…" nginx 12 minutes ago Up 5 seconds 0.0.0.0:80->80/tcp
lemp-php-1 php:8.2-fpm "docker-php-entrypoi…" php 12 minutes ago Up 5 seconds 9000/tcp
docker compose exec php bash
Dengan menjalankan perintah di atas, kita meminta Docker Compose untuk mengeksekusi Bash pada container PHP. Anda akan mendapatkan prompt baru yang menunjukkan bahwa Anda saat ini berada di direktori /var/www/php. Itulah fungsi working_direktory. Jalankan perintah ls
sederhana untuk melihat daftar isi direktori: Anda akan melihat index.php, yang merupakan link dari folder src lokal yang ditautkan ke folder /var/www/php pada container.
Ketikan exit
untuk keluar dari terminal bash container.
Sebelum kita melanjutkan ke bagian berikutnya, ada satu trik terakhir. Kembali ke terminal Anda dan jalankan perintah berikut:
docker compose logs -f
Tunggu hingga beberapa log ditampilkan, buka webbrowser anda kembali dan arahkan ke http://localhost
lalu klik tombol refresh beberapa kali maka terminal anda akan menunjukan log berikut :
Komponen kunci terakhir dari LEMP adalah MySQL. Mari perbarui docker-compose.yml lagi:
# Services
services:
# Nginx Service
nginx:
image: nginx:1.27
ports:
- 80:80
volumes:
- ./src:/var/www/php
- ./nginx/conf.d:/etc/nginx/conf.d
depends_on:
- php
# PHP Service
php:
build: ./.docker/php
working_dir: /var/www/php
volumes:
- ./src:/var/www/php
depends_on:
mysql:
condition: service_healthy
# MySQL Service
mysql:
image: mysql/mysql-server:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_HOST: "%"
MYSQL_DATABASE: demo
volumes:
- ./.docker/mysql/my.cnf:/etc/mysql/my.cnf
- mysqldata:/var/lib/mysql
healthcheck:
test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD
interval: 5s
retries: 10
# Volumes
volumes:
mysqldata:
Service Nginx masih sama, namun kita akan melakukan sedikit pembaruan pada service PHP. Kita sudah familiar dengan depend_on: kali ini, kita akan memberitahukan bahwa service MySQL harus dimulai sebelum PHP. Perbedaan lainnya adalah hadirnya opsi kondisi, tapi sebelum saya menjelaskan semuanya, mari kita lihat bagian build baru dari layanan PHP yang sepertinya menggantikan versi image nya. Daripada menggunakan image PHP resmi apa adanya, kami memberi tahu Docker Compose untuk menggunakan Dockerfile dari .docker/php untuk membuat image baru.
Docker memiliki panduan untuk membuat image: setiap image memilikinya, bahkan image resmi (contoh misalnya Nginx). kita memberi tahu Docker Compose untuk menggunakan Dockerfile dari .docker/php untuk membuat image baru.
Buat folder .docker/php dan tambahkan file bernama Dockerfile ke dalamnya, dengan konten berikut:
FROM php:8.2-fpm
RUN docker-php-ext-install pdo_mysql
PHP memerlukan ekstensi pdo_mysql untuk membaca dari database MySQL. Meskipun tidak disertai dengan image resminya, Docker Hub memberikan beberapa instruksi untuk menginstal ekstensi PHP dengan mudah.
Di bagian atas Dockerfile, kita akan memanggil image dengan versinya dan dilanjutkan dengan menginstal pdo_mysql dengan perintah RUN. Dan saat berikutnya kita memulai container, Docker Compose akan mengambil perubahan dan membuat image baru berdasarkan Dockerfile yang diberikan.
Masih banyak lagi yang bisa dilakukan dengan Dockerfile, contoh di atas sangat dasar, beberapa kasus penggunaan tingkat lanjut akan dibahas di artikel berikutnya.
Untuk saat ini, mari perbarui index.php untuk memanfaatkan ekstensi baru:
Hello there
query("SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'demo'");
$tables = $query->fetchAll(PDO::FETCH_COLUMN);
if (empty($tables)) {
echo 'There are no tables in database demo
.
';
} else {
echo 'Database demo
contains the following tables:
';
echo ' ';
foreach ($tables as $table) {
echo "- {$table}
";
}
echo '
';
}
?>
Kode di atas merupakan kode PHP untuk menghubungkan ke database yang belum ada.
Sekarang mari kita lihat lebih dekat layanan MySQL di docker-compose.yml:
# MySQL Service
mysql:
image: mysql/mysql-server:8.0
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_HOST: "%"
MYSQL_DATABASE: demo
volumes:
- ./.docker/mysql/my.cnf:/etc/mysql/my.cnf
- mysqldata:/var/lib/mysql
healthcheck:
test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD
interval: 5s
retries: 10
Bagian image menunjuk ke image MySQL Server untuk versi 8.0, dan diikuti oleh enviroment yang belum kita buat sebelumnya. Ini berisi tiga kunci:
yang merupakan variabel enviroment yang akan ditetapkan pada container saat pembuatan. Dimungkinkan untuk mengatur kata sandi root, mengotorisasi koneksi dari alamat IP mana pun, dan membuat database default masing-masing.
Dengan kata lain, database demo akan otomatis dibuat untuk kita saat container dimulai.
Setelah variable enviroment adalah volume yang sebelumnya sudah pernah dibuat. Volume pertama adalah file konfigurasi yang akan kita gunakan untuk mengatur set karakter ke utf8mb4_unicode_ci secara default, yang cukup standar saat ini.
Buat folder mysql dan tambahkan file my.cnf berikut ke dalamnya:
[mysqld]
collation-server = utf8mb4_unicode_ci
character-set-server = utf8mb4
default-authentication-plugin = mysql_native_password
Jika container sudah berjalan, destroy container beserta volumenya dengan docker composer down -v
dan jalankan docker composer up -d
lagi.
Volume kedua terlihat sedikit berbeda dari apa yang telah kita lihat sejauh ini: alih-alih menunjuk ke folder lokal, volume ini mengacu pada volume bernama yang ditentukan di bagian volume baru yang berada pada level yang sama dengan layanan:
# Volumes
volumes:
mysqldata:
Kita memerlukan jenis volume seperti itu karena tanpanya, setiap kali container service mysql di destroy, database juga ikut di destroy. Untuk membuatnya persisten(tetap), kita memberi tahu container MySQL untuk menggunakan volume mysqldata untuk menyimpan data secara lokal, lokal menjadi driver default (sama seperti jaringan, volume dilengkapi dengan berbagai driver dan opsi yang dapat Anda pelajari di sini). Hasilnya, direktori lokal dipasang ke container, perbedaannya adalah alih-alih kita yang menentukan lokasinya, kita membiarkan Docker Compose yang menentukan lokasinya.
Bagian terakhir yang merupakan hal baru yaitu: healthcheck
.Hal ini memungkinkan kita untuk menentukan pada kondisi mana sebuah container siap, dan bukan baru dimulai. Dalam hal ini, memulai container MySQL saja tidak cukup – kami juga ingin membuat database sebelum container PHP mencoba mengaksesnya. Dengan kata lain, tanpa pemeriksaan kesehatan ini, container PHP mungkin mencoba mengakses database meskipun database tersebut belum ada, sehingga menyebabkan kesalahan koneksi.
depends_on:
mysql:
condition: service_healthy
Secara default, depend_on hanya akan menunggu container yang direferensikan dimulai, kecuali kami menentukan sebaliknya. Namun pemeriksaan kesehatan ini mungkin tidak berhasil pada percobaan pertama; itu sebabnya kami mengaturnya untuk mencoba lagi setiap 5 detik hingga 10 kali, masing-masing menggunakan tombol interval dan coba lagi.
Healtcheck
sendiri menggunakan mysqladmin, sebuah program administrasi server MySQL, untuk melakukan ping ke server hingga mendapat respons. Ia melakukannya dengan menggunakan pengguna root dan nilai yang ditetapkan dalam variabel enviroment salah satunya MYSQL_ROOT_PASSWORD
sebagai kata sandi.
Kembali ke terminal Anda dan jalankan docker composer up -d
lagi. Setelah selesai mengunduh image MySQL dan semua container sudah aktif dan berjalan, buka browsr dan refresh localhost. Anda akan melihat ini:
Sekarang kita memiliki Nginx yang menjalankan PHP yang dapat terhubung ke database MySQL, artinya LEMP sudah terpenuhi. Langkah selanjutnya adalah memperbaiki pengaturan kita, dimulai dengan melihat bagaimana kita dapat berinteraksi dengan database dengan cara yang lebih mudah.
Ketika berurusan dengan database MySQL, phpMyAdmin tetap menjadi pilihan populer; mudahnya, mereka menyediakan image Docker yang cukup mudah untuk diatur.
Namun jika anda terbiasa dengan beberapa alat lain seperti Sekuel Ace, Navicat atau MySQL Workbench, Anda cukup memperbarui konfigurasi MySQL di docker-compose.yml dan menambahkan bagian port yang memetakan port 3306 mesin lokal Anda ke kontainer:
...
# MySQL Service
mysql:
ports:
- 3306:3306
...
...
Buka docker-compose.yml untuk terakhir kalinya dan tambahkan konfigurasi layanan berikut setelah MySQL:
# PhpMyAdmin Service
phpmyadmin:
image: phpmyadmin/phpmyadmin:5
ports:
- 8080:80
environment:
PMA_HOST: mysql
depends_on:
mysql:
condition: service_healthy
Kita mulai dari image versi 5 dan kita memetakan port mesin lokal 8080 ke port kontainer 80. Kita menunjukkan bahwa kontainer MySQL harus dimulai dan siap terlebih dahulu dengan depend_on, dan mengatur host yang harus dihubungkan oleh phpMyAdmin menggunakan lingkungan PMA_HOST variabel (ingat bahwa Docker Compose akan secara otomatis menyelesaikan mysql ke alamat IP pribadi yang ditetapkan ke container).
Simpan perubahan dan jalankan docker composer up -d
lagi. Image akan diunduh, lalu setelah semuanya beres, kunjungi localhost:8080:
Jika anda ingin menambahkan Service Postgre SQL berikut baris inisialisasinya pada docker-compose.yml
...
# PHP Service
php:
...
depends_on:
...
postgres:
condition: service_healthy
...
#PosgreSQL Service
postgres:
image: postgres
restart: always
environment:
#POSTGRES_DB: postgres
#POSTGRES_USER: postgres
POSTGRES_PASSWORD: root
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready"]
interval: 1s
timeout: 5s
retries: 10
...
# Volumes
volumes:
...
pgdata:
...
Dan tambahkan baris berikut pada php/Dockerfile
...
RUN apt-get update \
...
&& apt-get install -y libpq-dev \
&& docker-php-ext-install pgsql pdo_pgsql pdo\
...
Buat file baru src/mssql-test.php
Perhatikan bagian `host=posgres` , hal tersebut dikarenakan container tidak dapat membaca localhost melainkan nama service dalam container itu sendiri. Untuk melihat nama service yang sedang berjalan kerikan perintah docker compose ps
.
Hasilnya sbb :
Jika anda ingin menambahkan Service Sql Server berikut baris inisialisasinya pada docker-compose.yml
...
# MSSQL Services
mssql:
# SQL Server image
image: mcr.microsoft.com/mssql/server:2019-latest
ports:
# Map host port 1433 to container port 1433
- "1433:1433"
environment:
# Accept the End-User License Agreement
- ACCEPT_EULA=Y
# Set the sa user password for the SQL Server
- SA_PASSWORD=YourStrong!Password
# persist SQL Server data
volumes:
- mssql-srv:/var/opt/mssql
...
# Volumes
volumes:
...
mssql-srv:
Dan tambahkan baris berikut pada php/Dockerfile
RUN apt-get update \
...
# mssql headers
&& apt-get install -y gnupg2
ENV ACCEPT_EULA=Y
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
RUN curl https://packages.microsoft.com/config/debian/11/prod.list > /etc/apt/sources.list.d/mssql-release.list
# Update package lists and install dependencies
RUN apt-get update && apt-get install -y \
unixodbc-dev \
unixodbc \
msodbcsql17
# Install pdo_sqlsrv extension
RUN pecl install sqlsrv pdo_sqlsrv
RUN docker-php-ext-enable sqlsrv pdo_sqlsrv
Buat file baru src/mssql-test.php
setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Execute a query to get the SQL Server version
$q = $conn->query('SELECT @@VERSION');
// Display the SQL Server version
echo 'MSSQL VERSION: ' . $q->fetchColumn() . '
';
} catch (Exception $e) {
// Error message and terminate the script
die(print_r($e->getMessage()));
}
// Display the PHP version
echo 'PHP VERSION: ' . phpversion() . '
';
Hasilnya sbb :