Hướng dẫn cài đặt WordPress bằng Docker Compose

Admin
24/06/23
0

Hiện nay, nhu cầu tạo website của các cá nhân hay tổ chức ngày một mở rộng. Trong đó, WordPress là một trong những nền tảng thiết kế web được ưa chuộng nhất. Để tìm hiểu cách cài đặt WordPress bằng Docker Compose, các bạn hãy cùng Mailinhwp.vn tham khảo bài viết dưới đây.

Giới thiệu

WordPress là một hệ quản trị nội dung (CMS) mã nguồn mở miễn phí, viết bằng PHP và sử dụng cơ sở dữ liệu MySQL. Nhờ hệ thống template và cấu trúc plugin dễ mở rộng, hầu hết tác vụ quản lí của WordPress có thể thực hiện bằng giao diện web. Vì lí do trên, WordPress trở nên nổi tiếng trong lĩnh vực thiết kế các loại website như blog, trang sản phẩm hay các trang thương mại điện tử.

Để sử dụng WordPress, các bạn cần cấu hình LAMP stack (Linux, Apache, MySQL và PHP) hoặc LEMP stack (Linux, Nginx, MySQL và PHP). Do thực hiện thủ công khá tốn thời gian, Mailinhwp.vn sẽ hướng dẫn các bạn sử dụng Docker và Docker Compose để hỗ trợ cài đặt. Quy trình bao gồm việc thiết lập các thư viện, file cấu hình và biến môi trường bằng Docker image. Những image này sau đó được thực thi độc lập trong các container. Cuối cùng, chúng ta sẽ sử dụng Compose để cấu hình cho các container có khả năng giao tiếp (cơ sở dữ liệu và WordPress).

Qua bài viết, các bạn sẽ biết cách cài đặt WordPress bằng multi-container (nhiều container): cơ sở dữ liệu MySQL, web server Nginx cũng như bản thân WordPress. Chúng ta sẽ yêu cầu Let’s Encrypt cấp các chứng chỉ TLS/SSL để bảo mật cho tên miền website. Ngoài ra, Mailinhwp.vn sẽ hướng dẫn các bạn cấu hình cho chứng chỉ có thể tự động gia hạn, đảm bảo trạng thái bảo mật của website luôn được duy trì.

Chuẩn bị để cài đặt WordPress bằng Docker Compose

Trước khi cài đặt, các bạn cần đáp ứng:

  • Đăng nhập server Ubuntu 20.04 bằng tài khoản non-root với đặc quyền sudo và kích hoạt tường lửa.
  • Cài đặt Docker.
  • Cài đặt Docker Compose.
  • Đăng ký một tên miền. Trong bài hướng dẫn này, Mailinhwp.vn sẽ ví dụ tên miền là your_domain.
  • Cấu hình hai DNS record:
    • Một A record với tên miền your_domain trỏ tới địa chỉ IP của server.
    • Một A record với tên miền www.your_domain trỏ tới địa chỉ IP của server.

Sau khi chuẩn bị xong, chúng ta tiến hành cài đặt.

Bước 1: Thiết lập cấu hình Web Server

Để bắt đầu, các bạn cần cấu hình cho Nginx bằng cách thêm một số location block đặc trưng của WordPress vào file cấu hình. Một location block khác cũng được thêm để hỗ trợ tự động gia hạn chứng chỉ cho website. Block này có nhiệm vụ điều hướng các request xác minh của Let’s Encrypt đến Certbot.

Đầu tiên, chúng ta tạo một thư mục dự án để cài đặt WordPress. Các bạn có thể tùy ý đặt tên thư mục. Ở đây, thư mục sẽ được đặt tên là wordpress.

$ mkdir wordpress

Các bạn mở thư mục vừa tạo.

$ cd wordpress

Tiếp theo, chúng ta tạo thư mục chứa file cấu hình.

$ mkdir nginx-conf

Sau khi tạo xong, các bạn sử dụng nano hoặc trình editor khác tùy thích để mở file.

$ nano nginx-conf/nginx.conf

Chúng ta thêm một server block vào file để thiết lập server_name cũng như thư mục mã nguồn root. Trong đó, một số location block được bổ sung để điều hướng các request của Certbot yêu cầu chứng chỉ, tài nguyên tĩnh và mã nguồn PHP.

Các bạn dán đoạn code dưới đây vào file, đừng quên thay your_domain bằng tên miền của đã chuẩn bị.

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;
        }
}

Các thông tin trong server block bao gồm:

Các chỉ thị (directive):

  • listen: Mở cổng 80 để plugin webroot của Certbot có thể xử lí các request yêu cầu chứng chỉ. Tạm thời, các bạn chưa bao gồm mở cổng 443. Ở những bước tiếp theo, sau khi tên miền đã được Certbot xác thực, các bạn sẽ bổ sung SSL và cập nhật lại cấu hình cho Nginx.
  • server_name: Chọn server name và server block cho các request. Đừng quên thay your_domain bằng tên miền của các bạn.
  • index: Liệt kê các trang chỉ mục của request. Các bạn có thể thay đổi thứ tự trả về của file tại đây (đặt file index.php trước index.html để Nginx ưu tiên gọi index.php khi request không yêu cầu file cụ thể).
  • root: Chọn thư mục chứa mã nguồn website. Ngoài ra, theo mặc định trong WordPress Dockerfile, /var/www/html còn là một mount point (thư mục chứa các file được mount) khi build image WordPress.

Location block:

  • location ~ /.well-known/acme-challenge: Cấu hình cách xử lí các request đến thư mục .well-known. Certbot sẽ tạo một file tạm trong thư mục này nhằm đối chiếu DNS tên miền với server của bạn, từ đó sử dụng webroot plugin để cấp chứng chỉ cho tên miền.
  • location /: Chứa chỉ thị try_files giúp xác định các tài nguyên được yêu cầu bởi URI request. Trường hợp không tìm thấy, request và các đối số sẽ được điều hướng đến file index.php của WordPress, thay vì trả về trạng thái mặc định 404 Not Found.
  • location ~ \.php$: Xử lí và ủy quyền các PHP request đến wordpress container. Do Nginx không có trình xử lí PHP, chúng ta sử dụng trình php-fpm được image php:fpm tích hợp sẵn. Vì phụ thuộc image php:fpm, các bạn cần cấu hình bổ sung một số tùy chọn của giao thức FastCGI. Các tùy chọn trên có nhiệm vụ ủy quyền những request PHP cho wordpress, đồng thời hỗ trợ phân tích cú pháp và trả về tài nguyên được các request URI yêu cầu.
  • location ~/\.th: Chứa chỉ thị deny_all, đảm bảo Nginx không phục vụ cho người dùng các file .htaccess.
  • location = /favicon.icolocation = /robots.txt: Không lưu lịch sử các request yêu cầu file /favicon.ico và /robots.txt.
  • location ~* \.(css|gif|ico|jpeg|jpg|js|png)$: Không lưu lịch sử các request tài nguyên tĩnh. Vì khá tốn kém để phục vụ, ta cần đảm bảo những tài nguyên này dễ lưu vào bộ nhớ đệm cache.

Các bạn đóng file và lưu lại các tùy chọn vừa thiết lập.

Như vậy, Mailinhwp.vn đã hướng dẫn xong cho các bạn cách để cấu hình Nginx web server. Ở bước tiếp theo sẽ tạo các biến môi trường mà container WordPress và MySQL sử dụng trong quá trình thực thi.

Bước 2: Định nghĩa biến môi trường (Environment Variables)

Khi thực thi, các container WordPress và MySQL sử dụng biến môi trường nhằm đảm bảo khả năng truy cập ứng dụng và tính nhất quán dữ liệu. Những biến môi trường này bao gồm mật khẩu root của MySQL, người dùng và mật khẩu truy cập (thông tin nhạy cảm) cũng như tên và host (thông tin không nhạy cảm) của cơ sở dữ liệu.

Thay vì thiết lập toàn bộ trong file Docker Compose (file quy định hành động của container), chúng ta sẽ lưu và giới hạn truy cập cho các thông tin nhạy cảm vào file .env. Bằng cách này, các bạn có thể ngăn thông tin bị sao chép sang các repository khác và tránh nguy cơ bị lộ dữ liệu.

Các bạn đến thư mục dự án và mở file .env.

$ nano .env

Chúng ta sẽ lưu vào .env mật khẩu root của MySQL cũng như người dùng và mật khẩu mà WordPress sử dụng để truy cập cơ sở dữ liệu.

MYSQL_ROOT_PASSWORD=your_root_password
MYSQL_USER=your_wordpress_database_user
MYSQL_PASSWORD=your_wordpress_database_password

Đóng và lưu file để cập nhật các thay đổi.

Vì lưu thông tin nhạy cảm, các bạn cần thêm .env vào các file .gitignore và .dockerignore. Bằng cách này, .env sẽ không được Git và Docker sao chép đến các Git repository cũng như các Docker image.

Nếu quản lý version control bằng Git, các bạn mở thư mục dự án và gọi lệnh git init để tạo một repository.

$ git init

Các bạn tạo và mở file .gitignore.

$ nano .gitignore

Thêm .env vào file vừa tạo.

.env

Đóng và lưu file để cập nhật thay đổi.

Để đề phòng, các bạn nên thêm .env vào .dockerignore, tránh trường hợp .env được sao chép vào các container khi thư mục dự án được sử dụng làm build context.

Mở .dockerignore.

$ nano .dockerignore

Thêm .env vào file.

.env

Các bạn có thể tiếp tục thêm vào những dòng tiếp theo một số file và thư mục tùy chọn trong quá trình cài đặt.

.env
.git
docker-compose.yml
.dockerignore

Đóng và lưu file để lưu lại các thay đổi. Cấu hình các thông tin nhạy cảm xong, bạn tiến hành định nghĩa các service trong file docker-compose.yml.

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

docker-compose.yml là file định nghĩa các service cho quá trình cài đặt. Mỗi service sẽ tương ứng và quy định cách hoạt động của một container.

Compose cho phép các service kết nối qua mạng chia sẻ (shared network) và những không gian lưu trữ liên tục (volume). Vì vậy, các bạn có thể định nghĩa nhiều service nếu muốn chạy một ứng dụng multi-container (nhiều container). Trong bài viết này, để hỗ trợ cài đặt WordPress, Mailinhwp.vn sẽ tạo một số container bao gồm: Cơ sở dữ liệu MySQL, ứng dụng WordPress, web server Nginx và một container Certbot có nhiệm vụ xác thực tên miền.

Đầu tiên, các bạn tạo và mở file docker-compose.yml.

$ nano docker-compose.yml

Thêm đoạn code sau để khai báo phiên bản Compose và định nghĩa service db cho cơ sở dữ liệu.

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

Cấu hình của service db bao gồm:

  • image: Quy định image được dùng để tạo container. Do image mysql:latest luôn thay đổi, để tránh xung đột, ở đây Mailinhwp.vn sẽ sử dụng cố định phiên bản image mysql:8.0.
  • container_name: Đặt tên cho container.
  • restart: Chọn cách container tái khởi động. Thông thường, giá trị mặc định của restart là no. Tuy nhiên, ở đây sẽ cho phép container tự khởi động lại trừ khi service được dừng bởi người quản trị.
  • env_file: Cho phép bổ sung biến môi trường từ file .env trong build context (thư mục hiện hành).
  • environment: Bổ sung biến môi trường không được định nghĩa trong .env. Các bạn gán wordpress cho biến MYSQL_DATABASE để đặt tên cơ sở dữ liệu. Bạn có thể trực tiếp thêm giá trị này vào file docker-compose.yml vì tên cơ sở dữ liệu không phải là thông tin nhạy cảm.
  • volumes: Mount (sao chép) volume có tên dbdata (named volume) vào thư mục /var/lib/mysql trên container. Đây là thư mục dữ liệu chuẩn của MySQL ở hầu hết các phiên bản.
  • command: Ghi đè các lệnh CMD (CMD instruction) mặc định của image. Ở đây, các bạn sẽ thêm một lệnh mới vào tập lệnh tiêu chuẩn trong mysql:8.0 để khởi động server MySQL trên container. Vì image WordPress và PHP không hỗ trợ các phương thức xác thực gần đây của MySQL, chúng ta cần bổ sung tùy chọn --default-authentication-plugin=mysql_native_password để quy định cơ chế xác minh người dùng (mysql_native_password) cho cơ sở dữ liệu.
  • networks: Thêm service vào mạng app-network được định nghĩa cuối file.

Tiếp theo sẽ tiến hành cấu hình 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

Trong service này, ngoài đặt tên và thiết lập quy luật tái khởi động như db, các bạn cần bổ sung một số tùy chọn đặc trưng dưới đây của wordpress, bao gồm:

  • depends_on: Đảm bảo các container khởi động theo đúng thứ tự phụ thuộc. Vì WordPress sử dụng cơ sở dữ liệu và thông tin người dùng, để ứng dụng hoạt động ổn định, các bạn cần cấu hình cho service wordpress khởi động sau service db.
  • image: Tạo container bằng image WordPress 5.1.1-fpm-alpine – một image alpine có nguồn gốc từ dự án Alpine Linux. 5.1.1-fpm-alpine có nhiệm vụ giảm kích thước image tổng thể, đồng thời cung cấp trình php-fpm cần thiết để hỗ trợ Nginx xử lí các request PHP (đề cập ở trên).
  • env_file: Tương tự db, chúng ta tiếp tục sử dụng file .env để lấy người dùng và mật khẩu truy cập cơ sở dữ liệu.
  • environment: Gán người dùng và mật khẩu từ .env cho 2 biến WORDPRESS_DB_USER và WORDPRESS_DB_PASSWORD của image WordPress. WORDPRESS_DB_HOST sẽ truy cập server MySQL trên cổng mặc định 3306 của service db. Tên cơ sở dữ liệu WORDPRESS_DB_NAME được gán bằng tên mà các bạn đặt trong thân service của MySQL trước đó (MYSQL_DATABASEwordpress).
  • volumes: Mount volume có tên wordpress vào thư mục /var/www/html được tạo bởi image WordPress. Bằng cách này, mã nguồn ứng dụng của các bạn có thể được chia sẻ giữa các container.
  • networks: thêm container wordpress vào mạng app-network.

Tiếp theo sẽ tiến hành định nghĩa service Nginx webserver.

...
  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

Các bạn đặt tên và cấu hình cho service webserver khởi động sau wordpress, đồng thời tạo container bằng image Nginx 1.15.12-alpine.

Các tùy chọn bổ sung của webserver bao gồm:

  • ports: Mở cổng 80, tạo điều kiện để cấu hình trong file nginx.conf ở bước 1 hoạt động.
  • volumes: Định nghĩa nhóm các volume có tên và các bind mount (sao chép liên kết):
    • wordpress:/var/www/html: mount mã nguồn WordPress vào thư mục /var/www/html – thư mục root trong server block Nginx.
    • ./nginx-conf:/etc/nginx/conf.d: Bind mount thư mục cấu hình Nginx từ host vào thư mục tương ứng trong container, đảm bảo tất cả thay đổi trên host đều được cập nhật ở container.
    • certbot-etc:/etc/letsencrypt: Mount các khóa và chứng nhận tên miền Let’s Encrypt liên quan vào thư mục thích hợp trong webserver.

Bạn cũng thêm container này vào mạng app-network.

Sau service webserver, các bạn tiến hành định nghĩa cho service certbot. Đừng quên thay đổi tên miền và địa chỉ email sử dụng.

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

Với service này, bạn sẽ nhờ Compose kéo image certbot/certbot từ Docker Hub để tạo container. Các bạn sử dụng những volume có tên để chia sẻ tài nguyên với container Nginx, bao gồm các khóa, chứng nhận tên miền trong thư mục certbot-etc cũng như mã nguồn ứng dụng trong wordpress container.

Các bạn tiếp tục cấu hình depends_on để đảm bảo certbot khởi động sau service webserver.

Cuối cùng, bạn thêm một tùy chọn command để bổ sung các subcommand (lệnh con) vào các lệnh mặc định của certbot. Các lệnh con certonly này có nhiệm vụ cấp một chứng chỉ với các tùy chọn:

  • --webroot: Chọn webroot làm thư mục chứa các file xác thực của webroot plugin. Plugin sẽ gọi một HTTP request để chứng minh Certbot có thể lấy tài nguyên định sẵn khi server phản hồi lại tên miền được cung cấp (phương pháp xác thực HTTP-01).
  • --webroot-path: Cho biết đường dẫn đến thư mục webroot.
  • --email: Email tùy chọn cho việc đăng ký và phục hồi.
  • --agree-tos: Chấp thuận các điều khoản trong ACME’s Subcriber Agreement (Thỏa thuận Đăng ký Người dùng ACME).
  • --no-eff-email: Ngăn Certbot chia sẻ email cho EFF (Electronic Frontier Foundation). Tùy chọn này có thể bỏ qua.
  • --staging: Sử dụng môi trường staging của Let’s Encrypt để thử nghiệm yêu cầu chứng chỉ. Tùy chọn này giúp các bạn kiểm tra lại thiết lập cấu hình, đồng thời tránh khả năng bị giới hạn request của tên miền.
  • -d: Liệt kê danh sách tên miền sẽ phản hồi các request. Đừng quên sử dụng đúng tên miền các bạn đã chuẩn bị.

Sau khi certbot được cấu hình, bạn tiếp tục định nghĩa các volume và mạng.

...
volumes:
  certbot-etc:
  wordpress:
  dbdata:

networks:
  app-network:
    driver: bridge

volumes là khóa top-level dùng để định nghĩa các volume certbot-etcdbdata và wordpress. Khi được tạo, các volume sẽ được lưu trong thư mục /var/lib/docker/volumes/ – quản lý bởi Docker trên filesystem (hệ thống file) của host. Khi có nhu cầu sử dụng, nội dung của các volume sẽ được mount đến các container để chia sẻ dữ liệu và mã nguồn.

Định nghĩa bridge network (cầu nối mạng) app-network cho phép các container trên cùng một Docker daemon host có thể giao tiếp. Nhờ mở toàn bộ cổng nội bộ nhưng không để lộ các cổng nào ra ngoài, bridge network có khả năng điều phối lưu lượng mạng và hỗ trợ giao tiếp giữa các container, giúp dbwordpress và webserver có thể giao tiếp với nhau trên cùng một host. Để front-end truy cập ứng dụng, các bạn chỉ cần mở cổng 80.

Chúng ta xem lại toàn bộ file docker-compose.yml vừa thiết lập.

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

  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

  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

  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

Đóng file và lưu lại các cấu hình. Như vậy là đã hoàn tất việc định nghĩa các service. Giờ đây, các bạn đã sẵn sàng cho việc khởi động các container và gọi thử các request yêu cầu chứng chỉ cho tên miền.

Bước 4: Yêu cầu chứng chỉ và chứng nhận SSL

Các bạn sử dụng lệnh docker-compose up để khởi động các container theo thứ tự, đồng thời bổ sung cờ -d để cho phép dbwordpress và webserver chạy nền.

$ docker-compose up -d

Các service được tạo sẽ hiển thị trong Output dưới đây.

Output
Creating db ... done
Creating wordpress ... done
Creating webserver ... done
Creating certbot   ... done

Các bạn kiểm tra trạng thái của service bằng lệnh docker-compose ps.

$ docker-compose ps

Khi hoàn tất khởi động, các service dbwordpress và webserver sẽ có trạng thái Up, riêng certbot sẽ trả về thông điệp Exit 0.

Output
  Name                 Command               State           Ports       
-------------------------------------------------------------------------
certbot     certbot certonly --webroot ...   Exit 0                      
db          docker-entrypoint.sh --def ...   Up       3306/tcp, 33060/tcp
webserver   nginx -g daemon off;             Up       0.0.0.0:80->80/tcp 
wordpress   docker-entrypoint.sh php-fpm     Up       9000/tcp

Trường hợp cột State không hiển thị như trên, các bạn có thể kiểm tra lại service logs bằng lệnh docker-compose logs.

$ docker-compose logs service_name

Nếu mọi việc suôn sẻ, các bạn gọi lệnh docker-compose exec để kiểm tra các chứng chỉ được mount đến container webserver.

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

Các chứng chỉ được cấp thành công sẽ được hiển thị trong Output.

Output
total 16
drwx------    3 root     root          4096 May 10 15:45 .
drwxr-xr-x    9 root     root          4096 May 10 15:45 ..
-rw-r--r--    1 root     root           740 May 10 15:45 README
drwxr-xr-x    2 root     root          4096 May 10 15:45 your_domain

Sau khi đảm bảo các request xác thực đã hoạt động, các bạn có thể cập nhật lại định nghĩa service certbot và bỏ đi cờ --staging.

Bạn mở lại file docker-compose.yml.

$ nano docker-compose.yml

Dưới tùy chọn command của service, các bạn thay cờ --staging bằng cờ --force-renewal. Thiết lập này cho phép chúng ta yêu cầu chứng chỉ mới cho một tên miền đã xác thực. Các bạn có thể kiểm tra lại cấu hình certbot sau khi thay đổi.

...
  certbot:
    depends_on:
      - webserver
    image: certbot/certbot
    container_name: certbot
    volumes:
      - certbot-etc:/etc/letsencrypt
      - certbot-var:/var/lib/letsencrypt
      - wordpress:/var/www/html
    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
...

Các bạn gọi lệnh docker-compose up để khởi tạo lại container certbot. Ngoài ra, vì webserver đã hoạt động, chúng ta sẽ bổ sung cờ --no-deps để ngăn Certbot khởi động service này.

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

Output dưới đây cho biết các bạn đã yêu cầu chứng chỉ thành công.

Output
Recreating certbot ... done
Attaching to certbot
certbot      | Saving debug log to /var/log/letsencrypt/letsencrypt.log
certbot      | Plugins selected: Authenticator webroot, Installer None
certbot      | Renewing an existing certificate
certbot      | Performing the following challenges:
certbot      | http-01 challenge for your_domain
certbot      | http-01 challenge for www.your_domain
certbot      | Using the webroot path /var/www/html for all unmatched domains.
certbot      | Waiting for verification...
certbot      | Cleaning up challenges
certbot      | IMPORTANT NOTES:
certbot      |  - Congratulations! Your certificate and chain have been saved at:
certbot      |    /etc/letsencrypt/live/your_domain/fullchain.pem
certbot      |    Your key file has been saved at:
certbot      |    /etc/letsencrypt/live/your_domain/privkey.pem
certbot      |    Your cert will expire on 2019-08-08. To obtain a new or tweaked
certbot      |    version of this certificate in the future, simply run certbot
certbot      |    again. To non-interactively renew *all* of your certificates, run
certbot      |    "certbot renew"
certbot      |  - Your account credentials have been saved in your Certbot
certbot      |    configuration directory at /etc/letsencrypt. You should make a
certbot      |    secure backup of this folder now. This configuration directory will
certbot      |    also contain certificates and private keys obtained by Certbot so
certbot      |    making regular backups of this folder is ideal.
certbot      |  - If you like Certbot, please consider supporting our work by:
certbot      | 
certbot      |    Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
certbot      |    Donating to EFF:                    https://eff.org/donate-le
certbot      | 
certbot exited with code 0

Sau khi được cấp chứng chỉ, các bạn sẽ tiến hành bổ sung SSL và cập nhật lại cấu hình cho Nginx.

Bước 5: Cập nhật cấu hình Service và Web Server

Để cấu hình SSL cho Nginx, các bạn cần bổ sung một số thiết lập, bao gồm: Điều hướng HTTP đến HTTPS, chỉ định thư mục chứa các khóa và chứng chỉ SSL, thêm một vài tham số và header (tiêu đề) cho request.

Vì webserver sẽ được khởi tạo lại trong chốc lát, chúng ta tạm thời dừng service bằng lệnh sau.

$ docker-compose stop webserver

Trước khi cập nhật cấu hình, các bạn sử dụng curl để lấy danh sách tham số bảo mật khuyên dùng cho Nginx.

$ 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

Dòng lệnh trên sẽ lưu các tham số cung cấp bởi Certbot vào file options-ssl-nginx.conf trong thư mục nginx-conf.

Tiếp theo, các bạn xóa file cấu hình Nginx ban đầu.

$ rm nginx-conf/nginx.conf

Bạn tạo lại một phiên bản khác của file.

$ nano nginx-conf/nginx.conf

Các bạn thêm đoạn code sau để điều hướng HTTP đến HTTPS, đồng thời bổ sung các header bảo mật, các giao thức cũng như các chứng chỉ SSL. Đừng quên thay your_domain bằng tên miền đã chuẩn bị.

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;
        # enable strict transport security only if you understand the implications

        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 HTTP sẽ quy định thư mục .well-known/acme-challenge làm webroot cho các renewal request (yêu cầu gia hạn) từ Certbot, đồng thời hỗ trợ điều hướng HTTP đến thư mục root của HTTPS bằng cách bổ sung một chỉ thị rewrite.

Server block HTTPS cho phép lắng nghe ssl và http2 trên cổng 443. Block cũng bao gồm đường dẫn đến các khóa, chứng chỉ SSL và file nginx-conf/options-ssl-nginx.conf – nơi lưu các tham số bảo mật được khuyên dùng của Certbot.

Ngoài ra, block còn bổ sung một số header bảo mật như: X-Frame-OptionsX-Content-Type-OptionsReferrer PolicyContent-Security-Policy và X-XSS-Protection. Những header này có thể giúp các bạn đạt rating trên các trang thử nghiệm server như SSL Labs hoặc Security Headers. Mặc định, header HTTP Strict Transport Security (HSTS) sẽ được comment. Các bạn chỉ nên sử dụng header này một khi đã xem xét và nắm rõ được chức năng “preload”.

Block HTTPS cũng bao gồm các chỉ thị root và index. Những location block còn lại chúng ta đã được tìm hiểu trong bước 1 của bài.

Các bạn đóng và lưu file để cập nhật lại cấu hình.

Trước khi tái khởi động container, chúng ta cần bổ sung mapping (ánh xạ) cho cổng 443 vào service webserver.

Các bạn mở file docker-compose.yml.

$ nano docker-compose.yml

Trong service webserver, các bạn thêm mapping của cổng 443.

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

Các bạn kiểm tra lại file docker-compose.yml sau khi chỉnh sửa.

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

  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

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

  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 --force-renewal -d your_domain -d www.your_domain

volumes:
  certbot-etc:
  wordpress:
  dbdata:

networks:
  app-network:
    driver: bridge

Đóng và lưu file để cập nhật cấu hình.

Bạn tiến hành khởi tạo lại service webserver.

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

Một lần nữa, các bạn gọi lệnh docker-compose ps để kiểm tra các service.

$ docker-compose ps

Nếu mọi việc suôn sẻ, các bạn sẽ nhận được output dưới đây.

Output
  Name                 Command                          State                     Ports                  
----------------------------------------------------------------------------------------------
certbot     certbot certonly --webroot ...         Exit 0                                           
db          docker-entrypoint.sh --def ...            Up       3306/tcp, 33060/tcp                     
webserver   nginx -g daemon off;                  Up       0.0.0.0:443->443/tcp, 0.0.0.0:80->80/tcp
wordpress   docker-entrypoint.sh php-fpm     Up       9000/tcp

Giờ đây, khi các container đã hoạt động ổn định, bạn sẽ sử dụng giao diện web để hoàn tất thiết lập cho WordPress.

Bước 6: Hoàn tất cài đặt bằng giao diện web

Các bạn truy cập vào tên miền của server. Đừng quên thay đổi your_domain bằng tên miền đã chuẩn bị.

https://your_domain

Chúng ta chọn ngôn ngữ hiển thị.

Hướng dẫn cài đặt WordPress bằng Docker Compose
Trang tùy chọn ngôn ngữ WordPress

Click chọn Continue, các bạn sẽ được chuyển đến trang thiết lập chính của WordPress. Bạn tiến hành đặt tên cho website và chọn tên đăng nhập. Thay vì “admin”, các bạn nên chọn một tên đăng nhập dễ nhớ khác đi kèm một mật khẩu mạnh. Nếu không muốn dùng mật khẩu riêng, các bạn có thể sử dụng mật khẩu tự tạo của WordPress.

Cuối cùng, các bạn cung cấp địa chỉ email và cho biết bạn có muốn search engine đánh chỉ mục (index) cho website của bạn hay không.

Hướng dẫn cài đặt WordPress bằng Docker Compose
Trang thiết lập chính của WordPress

Click chọn Install WordPress ở cuối trang, một biểu mẫu sẽ được hiển thị để chúng ta đăng nhập.

Hướng dẫn cài đặt WordPress bằng Docker Compose
Biểu mẫu đăng nhập

Sau khi đăng nhập, các bạn sẽ được truy cập đến trang dashboard quản lý của WordPress.

Hướng dẫn cài đặt WordPress bằng Docker Compose
Trang WordPress Dashboard

Đến đây, các bạn đã thành công trong việc cài đặt và thiết lập một ứng dụng WordPress cho riêng mình. Tiếp theo, Mailinhwp.vn sẽ hướng dẫn các bạn cấu hình một số tùy chọn để tự động gia hạn các chứng chỉ SSL.

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

Các chứng chỉ của Let’s Encrypt có thời hạn là 90 ngày. Để website luôn được bảo mật, các bạn nên cấu hình cho các chứng chỉ được làm mới một cách tự động. Một trong những cách thực hiện là sử dụng job của trình lập lich cron. Trong phần tiếp theo, chúng ta sẽ học cách tạo một cron job để chạy script hỗ trợ các bạn gia hạn chứng chỉ và cập nhật cấu hình Nginx theo định kỳ.

Các bạn mở file script ssl_renew.sh.

$ nano ssl_renew.sh

Bạn thêm đoạn code sau vào file để script có khả năng làm mới chứng chỉ và cập nhật cấu hình Nginx. Các bạn thay username bằng username non-root của bạn.

#!/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

Trong đoạn mã trên, bạn để script gán binary docker-compose cho biến COMPOSE với cờ --no-ansi nhằm chạy các lệnh docker-compose mà không cần sử dụng ANSI control characters (ký tự điều khiển ANSI). Điều tương tự được thực hiện với binary của docker. Tiếp theo, bạn sẽ cho script mở thư mục dự án ~/wordpress và chạy các lệnh docker-compose:

  • docker-compose run: Chạy một container certbot và ghi đè tùy chọn command đã định nghĩa trong service. Thay cho certonly, subcommand renew được sử dụng để gia hạn các chứng chỉ hết hiệu lực. Ngoài ra, bạn cũng bổ sung khả năng kiểm tra script với tùy chọn --dry-run.
  • docker-compose kill: Gửi tín hiệu SIGHUP đến container webserver để cập nhật lại cấu hình service.

Cuối cùng, script sẽ gọi lệnh docker system prune để xóa toàn bộ các container và image không được sử dụng.

Sau khi đóng và lưu lại các thay đổi của file, các bạn cấp quyền thực thi cho script bằng lệnh.

$ chmod +x ssl_renew.sh

Chúng ta tiếp tục mở file root crontab để cấu hình cho script chạy định kỳ.

$ sudo crontab -e

Nếu đây là lần đầu chỉnh sửa, các bạn sẽ được yêu cầu chọn một trình biên dịch để sử dụng.

Output
no crontab for root - using an empty one

Select an editor.  To change later, run 'select-editor'.
  1. /bin/nano        <---- easiest
  2. /usr/bin/vim.basic
  3. /usr/bin/vim.tiny
  4. /bin/ed

Choose 1-4 [1]:
...

Bạn thêm dòng dưới đây vào cuối file root.

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

Hiện tại, để kiểm tra request gia hạn chứng chỉ có hoạt động hay không, bạn sẽ cấu hình cho job chạy script mỗi 5 phút. File cron.log được tạo để ghi lại các output liên quan của job.

Sau 5 phút, các bạn mở file cron.log và kiểm tra request gia hạn của mình.

$ tail -f /var/log/cron.log

Các bạn đã thành công nếu Output dưới đây được hiển thị.

Output
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates below have not been saved.)

Congratulations, all renewals succeeded. The following certs have been renewed:
  /etc/letsencrypt/live/your_domain/fullchain.pem (success)
** DRY RUN: simulating 'certbot renew' close to cert expiry
**          (The test certificates above have not been saved.)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Chúng ta sử dụng tổ hợp phím Ctrl+C để thoát.

Đến đây, các bạn có thể cấu hình lại file crontab để job chạy script hằng ngày. Giả sử các bạn cần chạy script vào mỗi buổi trưa, sửa lại dòng cuối cùng trong file như sau.

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

Các bạn nhớ xóa tùy chọn --dry-run trong script ssl_renew.sh.

#!/bin/bash

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

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

Job cron sẽ gia hạn các chứng chỉ Let’s Encrypt khi vẫn còn hiệu lực để đảm bảo các chứng chỉ không bị quá hạn. Các bạn cũng có thể thiết lập Logrotate để luân phiên và nén các file log trong trường hợp cần thiết.

Ngoài ra, bạn cũng có thể tham khảo thêm cách sử dụng Docker để cài đặt và triển khai Gitea trên Ubuntu một cách đơn giản và hiệu quả, một công cụ mã nguồn mở quản lý mã nguồn, trên Ubuntu bằng Docker, giúp tăng tính linh hoạt và độc lập cho dự án phát triển phần mềm của bạn.

Lời kết

Cảm ơn các bạn đã theo dõi bài viết. Hy vọng qua bài hướng dẫn này, các bạn đã biết cách cài đặt WordPress bằng Docker Compose cho một web server Nginx, cũng như cách để bảo mật tên miền và gia hạn chứng chỉ trước khi hết hiệu lực. Nếu có bất kỳ thắc mắc hay phản hồi nào, các bạn hãy bình luận bên dưới nhé.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *