Table of Contents

Join CloudFly's Telegram channel to receive more offers and never miss any promotions from CloudFly

Hướng dẫn cài đặt WordPress với Docker Compose và Nginx, cùng chứng chỉ TLS/SSL

Posted on: July 17, 2025

description image

Giới thiệu

Chạy WordPress thường yêu cầu cài đặt ngăn xếp LAMP (Linux, Apache, MySQL và PHP) hoặc LEMP (Linux, Nginx, MySQL và PHP), điều này có thể mất nhiều thời gian. Tuy nhiên, bằng cách sử dụng các công cụ như Docker và Docker Compose, bạn có thể đơn giản hóa quá trình thiết lập ngăn xếp ưa thích và cài đặt WordPress. Thay vì cài từng thành phần riêng lẻ, bạn có thể sử dụng các image tiêu chuẩn hóa các thư viện, file cấu hình và biến môi trường. Sau đó, chạy các image này trong các container, là các tiến trình tách biệt chạy trên cùng hệ điều hành chia sẻ. Ngoài ra, bằng Compose, bạn có thể phối hợp nhiều container — ví dụ một ứng dụng và cơ sở dữ liệu — để giao tiếp với nhau.

Trong hướng dẫn này, bạn sẽ xây dựng một cài đặt WordPress đa container. Các container của bạn sẽ bao gồm cơ sở dữ liệu MySQL, máy chủ web Nginx và WordPress. Bạn cũng sẽ bảo mật cài đặt bằng cách lấy chứng chỉ TLS/SSL với Let’s Encrypt cho tên miền bạn muốn liên kết với website. Cuối cùng, bạn sẽ thiết lập một công việc cron để gia hạn chứng chỉ tự động nhằm giữ cho tên miền luôn bảo mật.

Bước 1 — Định nghĩa cấu hình máy chủ web

Trước khi chạy bất kỳ container nào, bước đầu tiên là định nghĩa cấu hình cho máy chủ web Nginx của bạn. File cấu hình sẽ bao gồm một số khối location đặc thù cho WordPress, cùng với khối location để chuyển yêu cầu xác thực Let’s Encrypt tới client Certbot nhằm tự động gia hạn chứng chỉ.

Đầu tiên, tạo thư mục dự án cho WordPress, ví dụ gọi là wordpress. Bạn có thể đặt tên khác nếu muốn:

mkdir wordpress
cd wordpress
mkdir nginx-conf
nano nginx-conf/nginx.conf

Trong file này, thêm một server block với các chỉ thị cho tên máy chủ và thư mục gốc, cùng các khối location nhằm xử lý yêu cầu chứng thực của Certbot, PHP và tài nguyên tĩnh.

Nội dung file (thay thế your_domain bằng tên miền của bạn):

server {
        listen 80;
        listen [::]:80;

        server_name your_domain www.your_domain;

        index index.php index.html index.htm;

        root /var/www/html;

        location ~ /.well-known/acme-challenge {
                allow all;
                root /var/www/html;
        }

        location / {
                try_files $uri $uri/ /index.php$is_args$args;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass wordpress:9000;
                fastcgi_index index.php;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
        }

        location ~ /\.ht {
                deny all;
        }

        location = /favicon.ico {
                log_not_found off; access_log off;
        }
        location = /robots.txt {
                log_not_found off; access_log off; allow all;
        }
        location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
                expires max;
                log_not_found off;
        }
}

Server block này bao gồm:

  • listen 80: Nginx lắng nghe cổng 80 cho plugin webroot của Certbot xử lý yêu cầu chứng thực (port 443 chưa được thêm vì bạn sẽ cập nhật sau khi có chứng chỉ SSL).
  • server_name: tên miền và bản ghi www.
  • index: ưu tiên file index.php.
  • root: thư mục gốc /var/www/html được mount bởi WordPress Dockerfile.

Các khối location xử lý:

  • .well-known/acme-challenge: thư mục xác thực Let’s Encrypt.
  • /: xử lý các URI bằng cách check file hoặc chuyển tiếp đến WordPress index.php.
  • .php: xử lý PHP và proxy qua container WordPress (dùng php-fpm với FastCGI).
  • .htaccess: chặn truy cập file .ht.
  • favicon.ico, robots.txt: không ghi log.
  • Tống các tài nguyên như css, gif, jpg, js với thời gian cache dài và tắt log.

Sau khi chỉnh sửa lưu file.


Bước 2 — Định nghĩa biến môi trường

Container cơ sở dữ liệu và WordPress cần truy cập các biến môi trường chứa dữ liệu nhạy cảm (mật khẩu root MySQL, tài khoản DB WordPress) cũng như thông tin không nhạy cảm (tên DB, host).

Bạn nên đặt các biến nhạy cảm trong file .env và giới hạn quyền truy cập để tránh bị lộ khi push repo.

Tạo file .env trong thư mục dự án ~/wordpress và thêm nội dung:

nano .env
MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_USER=your_wordpress_database_user
MYSQL_PASSWORD=your_wordpress_database_password

Thay your_root_password, your_wordpress_database_useryour_wordpress_database_password bằng thông tin thực tế.

Đảm bảo thêm .env vào .gitignore.dockerignore để tránh bị commit hoặc copy vào Docker image:

nano .gitignore

Thêm dòng:

Tương tự cho .dockerignore:

nano .dockerignore

Thêm:

.git
docker-compose.yml
.dockerignore

Bước 3 — Định nghĩa các service với Docker Compose

Tạo file docker-compose.yml trong thư mục dự án để định nghĩa các service (container).

nano docker-compose.yml

Thêm cấu hình cho service db:

version: '3'
services:
  db:
    image: mysql:8.0
    container_name: db
    restart: unless-stopped
    env_file: .env
    environment:
      - MYSQL_DATABASE=wordpress
    volumes:
      - dbdata:/var/lib/mysql
    command: '--default-authentication-plugin=mysql_native_password'
    networks:
      - app-network

Giải thích:

  • mysql:8.0: version cố định để tránh xung đột.
  • env_file: lấy biến từ .env.
  • environment: định nghĩa tên database.
  • volumes: mount volume để lưu dữ liệu MySQL.
  • command: dùng plugin xác thực legacy hỗ trợ php-fpm.
  • networks: tham gia mạng nội bộ app-network.

Thêm service wordpress:

  wordpress:
    depends_on:
      - db
    image: wordpress:5.1.1-fpm-alpine
    container_name: wordpress
    restart: unless-stopped
    env_file: .env
    environment:
      - WORDPRESS_DB_HOST=db:3306
      - WORDPRESS_DB_USER=$MYSQL_USER
      - WORDPRESS_DB_PASSWORD=$MYSQL_PASSWORD
      - WORDPRESS_DB_NAME=wordpress
    volumes:
      - wordpress:/var/www/html
    networks:
      - app-network

Thêm service webserver sử dụng Nginx:

  webserver:
    depends_on:
      - wordpress
    image: nginx:1.15.12-alpine
    container_name: webserver
    restart: unless-stopped
    ports:
      - "80:80"
    volumes:
      - wordpress:/var/www/html
      - ./nginx-conf:/etc/nginx/conf.d
      - certbot-etc:/etc/letsencrypt
    networks:
      - app-network

Cuối cùng service certbot để lấy chứng chỉ:

  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - wordpress:/var/www/html
    command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --staging -d your_domain -d www.your_domain

volumes:
  certbot-etc:
  wordpress:
  dbdata:

networks:
  app-network:
    driver: bridge

Thay sammy@your_domain, your_domain bằng thông tin thực.

Bước 4 — Lấy chứng chỉ SSL và đăng nhập

Khởi chạy container với:

docker-compose up -d

Kiểm tra trạng thái:

docker-compose ps

Kiểm tra thư mục /etc/letsencrypt/live trong container webserver:

docker-compose exec webserver ls -la /etc/letsencrypt/live

Nếu chứng chỉ thành công, hãy chỉnh sửa file docker-compose.yml để xóa flag --staging và thay bằng --force-renewal trong service certbot:

command: certonly --webroot --webroot-path=/var/www/html --email sammy@your_domain --agree-tos --no-eff-email --force-renewal -d your_domain -d www.your_domain

Sau đó chạy lại certbot:

docker-compose up --force-recreate --no-deps certbot

Chứng chỉ sẽ được gia hạn trên server.


Bước 5 — Sửa đổi cấu hình máy chủ web và định nghĩa service

Để kích hoạt SSL trên Nginx, bạn sẽ cấu hình chuyển hướng HTTP sang HTTPS, thêm chứng chỉ SSL, các tham số bảo mật và header.

Dừng webserver:

docker-compose stop webserver

Tải file tham số bảo mật SSL từ Certbot:

curl -sSLo nginx-conf/options-ssl-nginx.conf https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf

Xóa file cấu hình nginx hiện tại:

rm nginx-conf/nginx.conf

Tạo lại file cấu hình với nội dung sau (thay thế your_domain):

server {
        listen 80;
        listen [::]:80;

        server_name your_domain www.your_domain;

        location ~ /.well-known/acme-challenge {
                allow all;
                root /var/www/html;
        }

        location / {
                rewrite ^ https://$host$request_uri? permanent;
        }
}

server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name your_domain www.your_domain;

        index index.php index.html index.htm;

        root /var/www/html;

        server_tokens off;

        ssl_certificate /etc/letsencrypt/live/your_domain/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/your_domain/privkey.pem;

        include /etc/nginx/conf.d/options-ssl-nginx.conf;

        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Referrer-Policy "no-referrer-when-downgrade" always;
        add_header Content-Security-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always;
        # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

        location / {
                try_files $uri $uri/ /index.php$is_args$args;
        }

        location ~ \.php$ {
                try_files $uri =404;
                fastcgi_split_path_info ^(.+\.php)(/.+)$;
                fastcgi_pass wordpress:9000;
                fastcgi_index index.php;
                include fastcgi_params;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
                fastcgi_param PATH_INFO $fastcgi_path_info;
        }

        location ~ /\.ht {
                deny all;
        }

        location = /favicon.ico {
                log_not_found off; access_log off;
        }
        location = /robots.txt {
                log_not_found off; access_log off; allow all;
        }
        location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
                expires max;
                log_not_found off;
        }
}

Thêm port 443 vào service webserver trong docker-compose.yml:

    ports:
      - "80:80"
      - "443:443"

Khởi động lại webserver:

docker-compose up -d --force-recreate --no-deps webserver

Kiểm tra dịch vụ đang chạy:

docker-compose ps

Bước 6 — Hoàn thành cài đặt qua giao diện web

Truy cập trang web bằng trình duyệt với URL:

https://your_domain

Chọn ngôn ngữ sử dụng, tiếp tục chọn tên trang, tên đăng nhập và mật khẩu, nhập email và thiết lập có cho phép các công cụ tìm kiếm index trang hay không.

Bấm “Install WordPress” và tiến hành đăng nhập.

Bạn sẽ thấy được trang dashboard quản trị WordPress.


Bước 7 — Gia hạn chứng chỉ

Chứng chỉ Let’s Encrypt có hiệu lực 90 ngày, bạn cần thiết lập gia hạn tự động bằng công việc cron.

Tạo script ssl_renew.sh:

nano ssl_renew.sh

Nội dung:

#!/bin/bash

COMPOSE="/usr/local/bin/docker-compose --no-ansi"
DOCKER="/usr/bin/docker"

cd /home/sammy/wordpress/
$COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af

Thay sammy bằng username không phải root của bạn.

Cấp quyền thực thi:

chmod +x ssl_renew.sh

Chỉnh sửa crontab cho root:

sudo crontab -e

Thêm dòng chạy script sau mỗi 5 phút (để kiểm tra):

*/5 * * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1

Kiểm tra log:

tail -f /var/log/cron.log

Nếu thành công, xem thông báo gia hạn thành công.

Bạn có thể chỉnh lại thời gian chạy theo ngày bằng cách sửa crontab:

0 12 * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1

Và nhớ bỏ --dry-run trong script để gia hạn thực:

$COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver
$DOCKER system prune -af

Việc này đảm bảo chứng chỉ của bạn luôn được gia hạn trước khi hết hạn.

Kết luận

Hướng dẫn này đã chỉ bạn cách sử dụng Docker Compose để tạo cài đặt WordPress với Nginx làm webserver, đồng thời lấy và gia hạn chứng chỉ TLS/SSL cho tên miền.

Bạn cũng đã thiết lập script cùng cron job gia hạn tự động chứng chỉ để duy trì bảo mật.

Ngoài ra, bạn có thể tham khảo thêm hướng dẫn tăng tốc phân phối tài nguyên WordPress, sao lưu và lưu trữ tài nguyên trên DigitalOcean Spaces hoặc thử workflow container với Kubernetes và Helm.

Join CloudFly's Telegram channel to receive more offers and never miss any promotions from CloudFly
Share

0 replies