Tutorial Lengkap Membuat Docker Image Aplikasi PHP

1. Persiapan dan Prerequisites

Instalasi Docker

Untuk Ubuntu/Debian:

# Update sistem
sudo apt update

# Install dependencies
sudo apt install apt-transport-https ca-certificates curl gnupg lsb-release

# Add Docker GPG key
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# Add Docker repository
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install Docker
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io

# Add user to docker group
sudo usermod -aG docker $USER

Untuk Windows/Mac:

  • Download Docker Desktop dari https://www.docker.com/products/docker-desktop
  • Install dan restart komputer

Verifikasi Instalasi

docker --version
docker run hello-world

2. Struktur Proyek PHP

Buat struktur folder proyek:

my-php-app/
├── src/
│   ├── index.php
│   ├── config/
│   │   └── database.php
│   └── pages/
│       └── about.php
├── Dockerfile
├── docker-compose.yml
├── nginx.conf
└── .dockerignore

File Aplikasi PHP

src/index.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My PHP Docker App</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 50px; }
        .container { max-width: 800px; margin: 0 auto; }
        .info-box { background: #f4f4f4; padding: 20px; border-radius: 5px; }
    </style>
</head>
<body>
    <div class="container">
        <h1>Selamat Datang di Aplikasi PHP Docker!</h1>
        
        <div class="info-box">
            <h2>Informasi Server</h2>
            <p><strong>PHP Version:</strong> <?php echo phpversion(); ?></p>
            <p><strong>Server:</strong> <?php echo $_SERVER['SERVER_SOFTWARE']; ?></p>
            <p><strong>Document Root:</strong> <?php echo $_SERVER['DOCUMENT_ROOT']; ?></p>
            <p><strong>Server Time:</strong> <?php echo date('Y-m-d H:i:s'); ?></p>
        </div>

        <h2>PHP Extensions</h2>
        <div class="info-box">
            <?php
            $extensions = get_loaded_extensions();
            sort($extensions);
            foreach ($extensions as $ext) {
                echo "<span style='display: inline-block; margin: 2px 5px; padding: 2px 8px; background: #007cba; color: white; border-radius: 3px; font-size: 12px;'>$ext</span>";
            }
            ?>
        </div>

        <h2>Database Connection Test</h2>
        <div class="info-box">
            <?php
            try {
                $host = getenv('DB_HOST') ?: 'localhost';
                $dbname = getenv('DB_NAME') ?: 'testdb';
                $username = getenv('DB_USER') ?: 'root';
                $password = getenv('DB_PASS') ?: '';
                
                $pdo = new PDO("mysql:host=$host;dbname=$dbname", $username, $password);
                echo "<p style='color: green;'>✅ Database connection successful!</p>";
                echo "<p>Host: $host | Database: $dbname</p>";
            } catch (PDOException $e) {
                echo "<p style='color: orange;'>⚠️ Database connection failed: " . $e->getMessage() . "</p>";
                echo "<p>This is normal if database is not configured.</p>";
            }
            ?>
        </div>

        <p><a href="pages/about.php">About Page</a></p>
    </div>
</body>
</html>

src/pages/about.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>About - My PHP Docker App</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 50px; }
        .container { max-width: 800px; margin: 0 auto; }
    </style>
</head>
<body>
    <div class="container">
        <h1>About This Application</h1>
        <p>This is a sample PHP application running in Docker container.</p>
        <p>Built with PHP <?php echo phpversion(); ?> and Nginx.</p>
        <p><a href="../index.php">Back to Home</a></p>
    </div>
</body>
</html>

src/config/database.php:

<?php
class Database {
    private $host;
    private $dbname;
    private $username;
    private $password;
    private $pdo;

    public function __construct() {
        $this->host = getenv('DB_HOST') ?: 'localhost';
        $this->dbname = getenv('DB_NAME') ?: 'testdb';
        $this->username = getenv('DB_USER') ?: 'root';
        $this->password = getenv('DB_PASS') ?: '';
    }

    public function connect() {
        try {
            $this->pdo = new PDO("mysql:host={$this->host};dbname={$this->dbname}", 
                               $this->username, $this->password);
            $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            return $this->pdo;
        } catch (PDOException $e) {
            throw new Exception("Connection failed: " . $e->getMessage());
        }
    }
}
?>

3. Dockerfile

Dockerfile:

# Menggunakan PHP 8.2 dengan Apache sebagai base image
FROM php:8.2-apache

# Set working directory
WORKDIR /var/www/html

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    libzip-dev \
    zip \
    unzip \
    nginx \
    supervisor \
    && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install \
    pdo_mysql \
    mbstring \
    exif \
    pcntl \
    bcmath \
    gd \
    zip

# Enable Apache modules
RUN a2enmod rewrite ssl headers

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Copy custom Apache configuration
COPY apache-config.conf /etc/apache2/sites-available/000-default.conf

# Copy application files
COPY src/ /var/www/html/

# Set proper permissions
RUN chown -R www-data:www-data /var/www/html \
    && chmod -R 755 /var/www/html

# Expose port 80
EXPOSE 80

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD curl -f http://localhost/ || exit 1

# Start Apache
CMD ["apache2-foreground"]

4. Konfigurasi Apache

apache-config.conf:

<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    DocumentRoot /var/www/html
    
    <Directory /var/www/html>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # Enable PHP
    <FilesMatch \.php$>
        SetHandler application/x-httpd-php
    </FilesMatch>

    # Logging
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    # Security headers
    Header always set X-Content-Type-Options nosniff
    Header always set X-Frame-Options DENY
    Header always set X-XSS-Protection "1; mode=block"
</VirtualHost>

5. Docker Compose (Opsional)

docker-compose.yml:

version: '3.8'

services:
  php-app:
    build: .
    container_name: my-php-app
    ports:
      - "8080:80"
    volumes:
      - ./src:/var/www/html
    environment:
      - DB_HOST=mysql
      - DB_NAME=myapp
      - DB_USER=root
      - DB_PASS=rootpassword
    depends_on:
      - mysql
    networks:
      - app-network

  mysql:
    image: mysql:8.0
    container_name: mysql-db
    environment:
      MYSQL_ROOT_PASSWORD: rootpassword
      MYSQL_DATABASE: myapp
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppassword
    ports:
      - "3306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
      - app-network

  phpmyadmin:
    image: phpmyadmin:latest
    container_name: phpmyadmin
    environment:
      PMA_HOST: mysql
      PMA_PORT: 3306
      PMA_USER: root
      PMA_PASSWORD: rootpassword
    ports:
      - "8081:80"
    depends_on:
      - mysql
    networks:
      - app-network

volumes:
  mysql_data:

networks:
  app-network:
    driver: bridge

6. File Pendukung

.dockerignore:

.git
.gitignore
README.md
Dockerfile
docker-compose.yml
node_modules
*.log
.env.local
.env.*.local

init.sql:

CREATE DATABASE IF NOT EXISTS myapp;
USE myapp;

CREATE TABLE IF NOT EXISTS users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users (name, email) VALUES 
('John Doe', 'john@example.com'),
('Jane Smith', 'jane@example.com');

7. Build dan Menjalankan Docker Image

Method 1: Menggunakan Docker Commands

Build Image:

# Masuk ke direktori proyek
cd my-php-app

# Build Docker image
docker build -t my-php-app:latest .

# Lihat images yang tersedia
docker images

Menjalankan Container:

# Run container sederhana
docker run -d -p 8080:80 --name php-container my-php-app:latest

# Run dengan environment variables
docker run -d -p 8080:80 \
  -e DB_HOST=localhost \
  -e DB_NAME=myapp \
  -e DB_USER=root \
  -e DB_PASS=password \
  --name php-container \
  my-php-app:latest

# Run dengan volume mapping untuk development
docker run -d -p 8080:80 \
  -v $(pwd)/src:/var/www/html \
  --name php-container \
  my-php-app:latest

Method 2: Menggunakan Docker Compose

# Build dan jalankan semua services
docker-compose up -d --build

# Lihat status containers
docker-compose ps

# Lihat logs
docker-compose logs -f php-app

# Stop semua services
docker-compose down

# Stop dan hapus volumes
docker-compose down -v

8. Management dan Monitoring

Perintah Docker Berguna

# Lihat containers yang berjalan
docker ps

# Lihat semua containers
docker ps -a

# Masuk ke container
docker exec -it php-container bash

# Lihat logs container
docker logs php-container

# Stop container
docker stop php-container

# Start container
docker start php-container

# Remove container
docker rm php-container

# Remove image
docker rmi my-php-app:latest

Monitoring Resource Usage

# Lihat resource usage
docker stats php-container

# Inspect container
docker inspect php-container

# Lihat port mapping
docker port php-container

9. Testing dan Debugging

Test Aplikasi

Setelah container berjalan, buka browser dan akses:

  • Aplikasi utama: http://localhost:8080
  • PhpMyAdmin: http://localhost:8081 (jika menggunakan docker-compose)

Debugging

# Check container logs
docker logs -f php-container

# Masuk ke container untuk debugging
docker exec -it php-container bash

# Check Apache error logs di dalam container
tail -f /var/log/apache2/error.log

# Check Apache access logs
tail -f /var/log/apache2/access.log

# Test PHP configuration
docker exec php-container php -v
docker exec php-container php -m

10. Production Considerations

Optimalisasi Image

Multi-stage Dockerfile untuk production:

# Build stage
FROM php:8.2-apache as builder
WORKDIR /var/www/html
COPY src/ .
RUN composer install --no-dev --optimize-autoloader

# Production stage
FROM php:8.2-apache
WORKDIR /var/www/html

# Install hanya dependencies yang diperlukan
RUN apt-get update && apt-get install -y \
    libpng-dev \
    libzip-dev \
    && docker-php-ext-install pdo_mysql gd zip \
    && rm -rf /var/lib/apt/lists/*

# Copy dari build stage
COPY --from=builder /var/www/html /var/www/html
COPY apache-config.conf /etc/apache2/sites-available/000-default.conf

RUN a2enmod rewrite \
    && chown -R www-data:www-data /var/www/html

EXPOSE 80
CMD ["apache2-foreground"]

Security Best Practices

# Jangan run sebagai root
RUN groupadd -r phpuser && useradd -r -g phpuser phpuser
USER phpuser

# Set proper file permissions
RUN chmod -R 644 /var/www/html \
    && find /var/www/html -type d -exec chmod 755 {} \;

# Remove unnecessary packages
RUN apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false

Environment Variables untuk Production

# Production environment
docker run -d -p 80:80 \
  -e ENVIRONMENT=production \
  -e DB_HOST=prod-db-server \
  -e DB_NAME=prod_database \
  -e DB_USER=prod_user \
  -e DB_PASS=secure_password \
  --restart=unless-stopped \
  --name php-prod \
  my-php-app:latest

11. Troubleshooting

Masalah Umum dan Solusi

1. Port sudah digunakan:

# Cek port yang digunakan
netstat -tulpn | grep :8080

# Gunakan port lain
docker run -d -p 8090:80 --name php-container my-php-app:latest

2. Permission denied:

# Fix ownership
sudo chown -R $USER:$USER ./src
chmod -R 755 ./src

3. Container tidak bisa diakses:

# Check firewall
sudo ufw allow 8080

# Check container status
docker ps
docker logs php-container

4. Database connection error:

# Check network connection
docker network ls
docker network inspect bridge

# Test database connectivity
docker exec php-container ping mysql

Kesimpulan

Tutorial ini memberikan panduan lengkap untuk membuat, build, dan menjalankan aplikasi PHP dalam Docker container. Dengan mengikuti langkah-langkah di atas, Anda dapat:

  1. Membuat Docker image untuk aplikasi PHP
  2. Mengkonfigurasi Apache dan PHP extensions
  3. Mengelola container dengan Docker dan Docker Compose
  4. Melakukan debugging dan monitoring
  5. Mengoptimalkan untuk production

Docker memberikan fleksibilitas untuk mengembangkan dan deploy aplikasi PHP dengan konsistensi lingkungan yang terjamin.

إرسال تعليق