WordPress là một hệ quản trị nội dung (CMS) miễn phí và mã nguồn mở, được xây dựng trên MySQL và xử lý bằng PHP. Nhờ khả năng mở rộng qua hệ thống plugin và template, WordPress cho phép quản trị viên dễ dàng quản lý nội dung thông qua giao diện web trực quan. Đây chính là lý do WordPress trở thành nền tảng phổ biến để xây dựng nhiều loại trang web, từ blog cá nhân, trang doanh nghiệp đến thương mại điện tử.
Thông thường, để chạy WordPress, bạn cần thiết lập một stack LAMP (Linux, Apache, MySQL, PHP) hoặc LEMP (Linux, Nginx, MySQL, PHP), nhưng việc này có thể mất nhiều thời gian. Nhờ có Docker và Docker Compose, quá trình triển khai WordPress trở nên nhanh chóng và đơn giản hơn. Thay vì cài đặt từng thành phần thủ công, bạn có thể sử dụng các image tiêu chuẩn, giúp đồng nhất thư viện, tệp cấu hình và biến môi trường. Sau đó, các thành phần này sẽ chạy trong container – các tiến trình độc lập nhưng có thể tương tác trên cùng một hệ điều hành. Với Docker Compose, bạn có thể dễ dàng quản lý nhiều container cùng lúc, chẳng hạn như WordPress và cơ sở dữ liệu.
Sau đây DataOnline sẽ hướng dẫn bạn triển khai WordPress trên Docker với Nginx làm máy chủ web và MySQL làm cơ sở dữ liệu. Bên cạnh đó, bạn sẽ bảo mật website bằng chứng chỉ TLS/SSL miễn phí từ Let’s Encrypt, đảm bảo dữ liệu được mã hóa an toàn. Cuối cùng, bạn sẽ thiết lập cron job để tự động gia hạn chứng chỉ, giúp website luôn bảo mật và hoạt động ổn định.
Yêu cầu
Nếu bạn đang sử dụng Ubuntu phiên bản 16.04 hoặc thấp hơn, chúng tôi khuyến nghị bạn nâng cấp lên phiên bản mới hơn vì Ubuntu hiện không còn hỗ trợ các phiên bản này. Bộ sưu tập các hướng dẫn này sẽ giúp bạn nâng cấp phiên bản Ubuntu.
Để làm theo hướng dẫn này, bạn cần có:
- Máy chủ chạy Ubuntu: Kèm theo tài khoản non-root có quyền
sudo
và firewall đang hoạt động. - Docker: Đã được cài đặt trên máy chủ của bạn, theo các bước 1 và 2 của bài viết cách cài đặt và sử dụng Docker trên Ubuntu cho phiên bản 22.04 / 20.04 / 18.04.
- Docker Compose: Đã được cài đặt trên máy chủ của bạn, theo bước 1 của bài viết Cách cài đặt Docker Compose trên Ubuntu cho phiên bản 22.04 / 20.04 / 18.04.
- Tên miền đã đăng ký: Hướng dẫn này sử dụng your_domain làm ví dụ. Bạn có thể lấy miễn phí từ Freenom hoặc sử dụng nhà đăng ký tên miền khác.
- Cài đặt các bản ghi DNS sau cho máy chủ của bạn:
- Một bản ghi A với your_domain trỏ về địa chỉ IP công cộng của máy chủ.
- Một bản ghi A với www.your_domain trỏ về địa chỉ IP công cộng của máy chủ.
Khi đã thiết lập xong tất cả, bạn đã sẵn sàng bắt đầu bước đầu tiên.
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 hướng các yêu cầu xác minh của Let’s Encrypt đến Certbot để gia hạn chứng chỉ tự động.
Tạo thư mục dự án cho cài đặt WordPress. Trong ví dụ này, thư mục được đặt tên là wordpress
. (Bạn có thể đặt tên khác nếu muốn.)
mkdir wordpress
Di chuyển vào thư mục dự án:
cd wordpress
Tạo thư mục cho file cấu hình Nginx:
mkdir nginx-conf
Mở file cấu hình Nginx với nano hoặc trình soạn thảo ưa thích:
nano nginx-conf/nginx.conf
Thêm vào file nội dung khối cấu hình sau (đừng quên thay thế your_domain
bằng tên miền của bạn):
~/wordpress/nginx-conf/nginx.conf 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; } }
Giải thích các phần chính trong khối cấu hình:
Directives:
listen
: Chỉ thị này báo cho Nginx biết cần lắng nghe trên cổng 80
, cho phép bạn sử dụng plugin webroot của Certbot cho các yêu cầu chứng chỉ. Lưu ý rằng bạn chưa bao gồm cổng 443
– bạn sẽ cập nhật cấu hình để tích hợp SSL sau khi lấy được chứng chỉ thành công.
server_name
: Chỉ thị này xác định tên máy chủ của bạn và khối server sẽ được sử dụng để xử lý các yêu cầu gửi đến server. Hãy chắc chắn thay thế your_domain
trong dòng này bằng tên miền của riêng bạn.
index
: Chỉ thị này định nghĩa các tập tin sẽ được sử dụng làm file chỉ mục khi xử lý các yêu cầu gửi đến server. Bạn đã thay đổi thứ tự ưu tiên mặc định ở đây, đặt index.php
lên trước index.html để Nginx ưu tiên sử dụng các tập tin có tên index.php
khi có thể.
root
: Chỉ thị này đặt tên cho thư mục gốc chứa các yêu cầu gửi đến server của bạn. Thư mục này, /var/www/html
, được tạo ra như một điểm mount trong quá trình build theo các chỉ dẫn trong Dockerfile của WordPress. Các chỉ dẫn trong Dockerfile cũng đảm bảo rằng các tập tin từ bản phát hành của WordPress được mount vào volume này.
Location Blocks
location ~ /.well-known/acme-challenge
: Khối location này sẽ xử lý các yêu cầu đến thư mục .well-known, nơi Certbot sẽ đặt một file tạm thời để xác minh rằng DNS của tên miền của bạn đã trỏ về server. Với cấu hình này, bạn sẽ có thể sử dụng plugin webroot của Certbot để lấy chứng chỉ cho tên miền của mình.
location
/: Trong khối location này, chỉ thị try_files
được sử dụng để kiểm tra sự tồn tại của các tập tin phù hợp với từng yêu cầu URI. Thay vì trả về trạng thái 404 Not Found theo mặc định, yêu cầu sẽ được chuyển tiếp sang file index.php
của WordPress kèm theo các tham số yêu cầu.
location ~ .php$
: Khối location này sẽ xử lý việc xử lý các file PHP và chuyển tiếp các yêu cầu này đến container wordpress của bạn. Vì image WordPress của bạn được xây dựng dựa trên image php:fpm, nên bạn cũng sẽ bao gồm các tùy chọn cấu hình đặc thù cho giao thức FastCGI trong khối này. Nginx yêu cầu một bộ xử lý PHP độc lập cho các yêu cầu PHP; trong trường hợp này, các yêu cầu sẽ được xử lý bởi bộ xử lý php-fpm
đi kèm với image php:fpm
. Ngoài ra, khối location này còn bao gồm các chỉ thị, biến và tùy chọn riêng cho FastCGI để chuyển tiếp các yêu cầu đến ứng dụng WordPress đang chạy trong container wordpress, thiết lập file chỉ mục ưu tiên cho URI được phân tích và xử lý các yêu cầu URI.
location ~ /.ht
: Khối này sẽ xử lý các file .htaccess
vì Nginx không phục vụ chúng. Chỉ thị deny_all
đảm bảo rằng các file .htaccess
sẽ không bao giờ được phục vụ cho người dùng.
location = /favicon.ico, location = /robots.tx
t: Các khối này đảm bảo rằng các yêu cầu đến /favicon.ico
và /robots.txt
sẽ không được ghi log.
location ~ .(css|gif|ico|jpeg|jpg|js|png)$:* Khối này tắt việc ghi log cho các yêu cầu đến các tài nguyên tĩnh và đảm bảo rằng các tài nguyên này có thể được cache cao, vì chúng thường có chi phí phục vụ lớn.
Để biết thêm thông tin về việc chuyển tiếp FastCGI, hãy đọc bài “Hiểu biết và triển khai FastCGI Proxying trong Nginx“. Đối với thông tin về các khối server và location, hãy tham khảo “Hiểu về thuật toán lựa chọn Server Và Location Block trong Nginx“.
Lưu và đóng file khi bạn đã hoàn tất chỉnh sửa. Nếu bạn sử dụng nano, hãy nhấn CTRL+X
, sau đó nhấn Y
và ENTER
để lưu các thay đổi.
Với cấu hình Nginx đã được thiết lập, bạn có thể chuyển sang tạo các biến môi trường để truyền cho các container ứng dụng và cơ sở dữ liệu khi chạy.
Bước 2 – Định nghĩa biến môi trường
Các container cơ sở dữ liệu và ứng dụng WordPress của bạn sẽ cần truy cập một số biến môi trường khi chạy để dữ liệu của ứng dụng được lưu trữ và có thể truy cập được. Các biến này bao gồm cả thông tin nhạy cảm và không nhạy cảm: những giá trị nhạy cảm cho mật khẩu root của MySQL cũng như tên người dùng và mật khẩu của cơ sở dữ liệu ứng dụng, và những thông tin không nhạy cảm cho tên cơ sở dữ liệu và host.
Thay vì đặt tất cả các giá trị này trực tiếp trong file Docker Compose – file chính chứa thông tin về cách các container của bạn sẽ chạy – hãy đặt các giá trị nhạy cảm vào một file .env và hạn chế việc phân phối nó. Điều này sẽ ngăn các giá trị này bị sao chép vào các repository của dự án và bị lộ ra công khai.
Trong thư mục dự án chính của bạn, ~/wordpress
, hãy mở một file có tên .env
:
Mở file .env trong thư mục dự án của bạn:
nano .env
Các giá trị nhạy cảm mà bạn thiết lập trong file này bao gồm mật khẩu cho tài khoản root của MySQL, cùng với tên người dùng và mật khẩu mà WordPress sẽ sử dụng để truy cập cơ sở dữ liệu.
Thêm các tên biến và giá trị sau vào file. Hãy nhớ thay thế các giá trị mẫu bằng giá trị thực của bạn cho từng biến:
~/wordpress/.env MYSQL_ROOT_PASSWORD=your_root_password MYSQL_USER=your_wordpress_database_user MYSQL_PASSWORD=your_wordpress_database_password
Bao gồm trong đó là mật khẩu cho tài khoản quản trị root, cũng như tên người dùng và mật khẩu ưa thích của bạn cho cơ sở dữ liệu ứng dụng.
Lưu và đóng file khi bạn đã hoàn tất chỉnh sửa.
Vì file .env của bạn chứa thông tin nhạy cảm, bạn cần đảm bảo rằng nó được thêm vào các file .gitignore
và .dockerignore
của dự án. Điều này sẽ thông báo cho Git và Docker biết những file nào không nên sao chép vào repository Git và image Docker.
Nếu bạn dự định sử dụng Git để quản lý phiên bản, hãy khởi tạo thư mục làm việc hiện tại của bạn thành một repository bằng lệnh:
Tạo và mở file .gitignore:
git init
Thêm dòng sau:
nano .gitignore
Lưu và đóng file.
~/wordpress/.gitignore .env
Thêm dòng sau:
nano .dockerignore
Lưu và đóng file.
~/wordpress/.dockerignore .env
Phía dưới, bạn có thể tùy chọn thêm các file và thư mục liên quan đến quá trình phát triển của ứng dụng của bạn:
~/wordpress/.dockerignore .env .git docker-compose.yml .dockerignore
Lưu và đóng file khi bạn đã hoàn tất chỉnh sửa.
Với các thông tin nhạy cảm đã được thiết lập, giờ bạn có thể chuyển sang định nghĩa các dịch vụ của mình trong file docker-compose.yml.
Bước 3 – Định nghĩa các dịch vụ với Docker Compose
File docker-compose.yml
của bạn sẽ chứa các định nghĩa dịch vụ cho setup của bạn. Một dịch vụ trong Docker Compose chính là một container đang chạy, và các định nghĩa dịch vụ chỉ định thông tin về cách mỗi container sẽ hoạt động.
Nhờ Compose, bạn có thể định nghĩa các dịch vụ khác nhau để chạy ứng dụng đa container, vì Compose cho phép liên kết các dịch vụ này với nhau qua các mạng và volume chung. Điều này sẽ rất hữu ích cho setup hiện tại của bạn vì bạn sẽ tạo ra các container riêng biệt cho cơ sở dữ liệu, ứng dụng WordPress và máy chủ web. Bạn cũng sẽ tạo một container chạy Certbot để lấy chứng chỉ cho máy chủ web của bạn.
Để bắt đầu, hãy tạo và mở file docker-compose.yml
:
nano docker-compose.yml
Thêm vào file nội dung định nghĩa phiên bản Compose và dịch vụ db (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
Định nghĩa dịch vụ db
chứa các tùy chọn sau:
- image: Chỉ thị này báo cho Compose biết image nào sẽ được kéo về để tạo container. Bạn đang cố định sử dụng image
mysql:8.0
để tránh xung đột trong tương lai khi imagemysql:latest
tiếp tục được cập nhật. (Để biết thêm thông tin về việc cố định phiên bản và tránh xung đột phụ thuộc, hãy xem tài liệu Docker về best practices cho Dockerfile.) - container_name: Xác định tên cho container.
- restart: Xác định chính sách khởi động lại container. Mặc định là không khởi động lại, nhưng bạn đã cấu hình để container khởi động lại trừ khi bị dừng thủ công.
- env_file: Tùy chọn này thông báo cho Compose rằng bạn muốn thêm các biến môi trường từ một file có tên
.env
, nằm trong build context (trong trường hợp này, build context là thư mục hiện tại của bạn). - environment: Tùy chọn này cho phép bạn thêm các biến môi trường bổ sung, ngoài những biến đã được định nghĩa trong file
.env
. Bạn sẽ đặt biếnMYSQL_DATABASE
bằngwordpress
để cung cấp tên cho cơ sở dữ liệu ứng dụng. Vì đây là thông tin không nhạy cảm, bạn có thể thêm trực tiếp vào file docker-compose.yml. - volumes: Tại đây, bạn gắn một volume có tên
dbdata
đến thư mục/var/lib/mysql
trong container. Đây là thư mục dữ liệu tiêu chuẩn của MySQL trên hầu hết các bản phân phối. - command: Tùy chọn này chỉ định một lệnh để ghi đè lệnh CMD mặc định của image. Trong trường hợp này, bạn sẽ thêm một tham số vào lệnh
mysqld
mặc định của Docker image, lệnh này khởi động MySQL server trong container. Tham số--default-authentication-plugin=mysql_native_password
sẽ thiết lập biến hệ thống--default-authentication-plugin
thànhmysql_native_password
, chỉ định cơ chế xác thực nào sẽ được áp dụng cho các yêu cầu xác thực mới đến server. Vì PHP và do đó image WordPress của bạn sẽ không hỗ trợ cơ chế xác thực mới của MySQL, bạn cần thực hiện điều chỉnh này để xác thực người dùng cơ sở dữ liệu ứng dụng. - networks: Tùy chọn này xác định rằng dịch vụ ứng dụng của bạn sẽ tham gia vào mạng
app-network
, mạng mà bạn sẽ định nghĩa ở cuối file.Tiếp theo, bên dưới định nghĩa dịch vụ db, hãy thêm định nghĩa cho dịch vụ ứng dụng WordPress của bạn:
... 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 định nghĩa dịch vụ này, bạn đặt tên cho container và định nghĩa chính sách khởi động lại, giống như với dịch vụ db. Bạn cũng thêm một số tùy chọn riêng cho container này:
-
depends_on: Tùy chọn này đảm bảo các container khởi động theo thứ tự phụ thuộc, với container wordpress khởi động sau container db. Ứng dụng WordPress của bạn phụ thuộc vào sự tồn tại của cơ sở dữ liệu và người dùng ứng dụng, vì vậy việc thể hiện thứ tự phụ thuộc này sẽ giúp ứng dụng khởi động đúng cách.
-
image: Trong cấu hình này, bạn sử dụng image WordPress phiên bản 5.1.1-fpm-alpine. Như đã đề cập ở Bước 1, việc sử dụng image này đảm bảo rằng ứng dụng của bạn sẽ có bộ xử lý php-fpm mà Nginx yêu cầu để xử lý các yêu cầu PHP. Đây cũng là một image
alpine
, được phát triển từ dự án Alpine Linux, giúp giảm kích thước tổng thể của image. Để biết thêm về lợi ích và hạn chế của việc sử dụng imagealpine
và liệu nó có phù hợp với ứng dụng của bạn hay không, hãy xem phần thảo luận chi tiết trong mụcImage Variants
của trang Docker Hub WordPress. -
env_file: Một lần nữa, bạn chỉ định rằng bạn muốn lấy các giá trị biến môi trường từ file
.env
, vì đây là nơi bạn đã định nghĩa tên người dùng và mật khẩu của cơ sở dữ liệu ứng dụng. -
environment: Tại đây, bạn sử dụng các giá trị đã định nghĩa trong file
.env
nhưng gán cho các tên biến mà image WordPress yêu cầu:WORDPRESS_DB_USER
vàWORDPRESS_DB_PASSWORD
. Bạn cũng định nghĩaWORDPRESS_DB_HOST
, là máy chủ MySQL chạy trong container db có thể truy cập qua cổng mặc định 3306 của MySQL. BiếnWORDPRESS_DB_NAME
sẽ có giá trị giống như giá trị bạn đã chỉ định cho biếnMYSQL_DATABASE
trong định nghĩa dịch vụ MySQL:wordpress
. -
volumes: Bạn đang gắn một volume có tên
wordpress
vào điểm mount/var/www/html
được tạo bởi image WordPress. Việc sử dụng volume có tên theo cách này sẽ cho phép bạn chia sẻ code ứng dụng với các container khác.
-
- networks: Bạn cũng thêm container wordpress vào mạng
app-network
.
Thêm định nghĩa dịch vụ cho máy chủ web 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
Trong định nghĩa này, bạn đặt tên cho container và làm cho nó phụ thuộc vào container wordpress trong thứ tự khởi động. Bạn cũng đang sử dụng image alpine – cụ thể là image Nginx phiên bản 1.15.12-alpine.
Định nghĩa dịch vụ này cũng bao gồm các tùy chọn sau:
-
ports: Tùy chọn này mở cổng 80 để kích hoạt các cấu hình bạn đã định nghĩa trong file nginx.conf ở Bước 1.
-
volumes: Tại đây, bạn định nghĩa sự kết hợp giữa các volume có tên và bind mounts:
- wordpress:/var/www/html: Sẽ gắn code ứng dụng WordPress của bạn vào thư mục
/var/www/html
, thư mục mà bạn đã đặt làmroot
trong khối server của Nginx. - ./nginx-conf:/etc/nginx/conf.d: Sẽ bind mount thư mục cấu hình Nginx trên host đến thư mục tương ứng trong container, đảm bảo rằng mọi thay đổi bạn thực hiện trên host đều được phản ánh trong container.
- certbot-etc:/etc/letsencrypt: Sẽ gắn các chứng chỉ và key của Let’s Encrypt cho tên miền của bạn vào thư mục thích hợp trong container.
Bạn cũng đã thêm container này vào mạng app-network
.
Cuối cùng, bên dưới định nghĩa dịch vụ webserver
, hãy thêm định nghĩa dịch vụ cuối cùng cho container certbot
. Hãy nhớ thay thế địa chỉ email và tên miền được liệt kê trong cấu hình này bằng thông tin của riêng bạn.
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
Định nghĩa này báo cho Compose kéo image certbot/certbot từ Docker Hub. Nó cũng sử dụng các volume có tên để chia sẻ tài nguyên với container Nginx, bao gồm chứng chỉ và key cho tên miền trong volume certbot-etc
cũng như code ứng dụng trong volume wordpress
.
Một lần nữa, bạn đã sử dụng depends_on
để chỉ định rằng container certbot sẽ được khởi động sau khi dịch vụ webserver đang chạy.
Bạn cũng đã bao gồm tùy chọn command để chỉ định subcommand chạy với lệnh mặc định của certbot. Subcommand certonly
sẽ lấy chứng chỉ với các tùy chọn sau:
- –webroot: Chỉ thị này báo cho Certbot sử dụng plugin webroot để đặt các tập tin vào thư mục gốc phục vụ cho việc xác thực. Plugin này dựa vào phương thức xác thực HTTP-01, sử dụng một yêu cầu HTTP để chứng minh rằng Certbot có thể truy cập tài nguyên từ một server phản hồi theo tên miền đã chỉ định.
- –webroot-path: Xác định đường dẫn của thư mục gốc.
- –email: Địa chỉ email ưa thích của bạn dùng cho đăng ký và khôi phục.
- –agree-tos: Xác nhận rằng bạn đồng ý với Thỏa thuận Người đăng ký của ACME.
- –no-eff-email: Chỉ thị này báo cho Certbot rằng bạn không muốn chia sẻ email của mình với Electronic Frontier Foundation (EFF). Bạn có thể bỏ qua tùy chọn này nếu muốn.
- –staging: Chỉ thị này báo cho Certbot rằng bạn muốn sử dụng môi trường staging của Let’s Encrypt để lấy chứng chỉ thử nghiệm. Việc sử dụng tùy chọn này cho phép bạn kiểm tra các cấu hình và tránh các giới hạn yêu cầu đối với tên miền. Để biết thêm thông tin về các giới hạn này, vui lòng tham khảo tài liệu về rate limits của Let’s Encrypt.
- -d: Tùy chọn này cho phép bạn chỉ định các tên miền muốn áp dụng cho yêu cầu của bạn. Trong trường hợp này, bạn đã bao gồm
your_domain
vàwww.your_domain
. Hãy chắc chắn thay thế chúng bằng tên miền của riêng bạn.
Cuối cùng, bên dưới định nghĩa dịch vụ webserver, hãy thêm định nghĩa dịch vụ cuối cùng cho container certbot. Hãy nhớ thay thế địa chỉ email và tên miền được liệt kê trong cấu hình này bằng thông tin của riêng bạn.
... volumes: certbot-etc: wordpress: dbdata: networks: app-network: driver: bridge
Khóa volumes cấp trên định nghĩa các volume certbot-etc, wordpress, và dbdata. Khi Docker tạo các volume, nội dung của volume được lưu trữ trong một thư mục trên hệ thống file của host, /var/lib/docker/volumes/, được Docker quản lý. Nội dung của mỗi volume sau đó sẽ được mount từ thư mục này vào bất kỳ container nào sử dụng volume đó. Nhờ vậy, có thể chia sẻ code và dữ liệu giữa các container.
Mạng cầu nối do người dùng định nghĩa app-network cho phép các container giao tiếp với nhau vì chúng cùng chạy trên một Docker daemon host. Điều này giúp tối ưu hóa lưu lượng và giao tiếp trong ứng dụng, khi mở tất cả các cổng giữa các container trên cùng một mạng cầu nối mà không cần mở cổng ra bên ngoài. Do đó, các container db, wordpress, và webserver của bạn có thể giao tiếp với nhau, và bạn chỉ cần mở cổng 80 cho truy cập giao diện người dùng.
Dưới đây là file docker-compose.yml đầy đủ:
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
Lưu và đóng file khi bạn đã hoàn tất chỉnh sửa.
Với các định nghĩa dịch vụ đã được thiết lập, bạn đã sẵn sàng khởi động các container và kiểm tra các yêu cầu chứng chỉ của bạn.
Bước 4 – Lấy chứng chỉ SSL và thông tin đăng nhập
Khởi động các container với lệnh docker-compose up:
Chạy lệnh docker-compose up
để tạo và chạy các container theo thứ tự đã được định nghĩa. Khi thêm tùy chọn -d
, lệnh này sẽ chạy các container db
, wordpress
, và webserver
ở chế độ nền:
docker-compose up -d
Khi quá trình hoàn tất, bạn sẽ thấy thông báo xác nhận rằng các dịch vụ đã được tạo:
Output Creating db ... done Creating wordpress ... done Creating webserver ... done Creating certbot ... done
Sử dụng lệnh sau để kiểm tra trạng thái của các container:
docker-compose ps
Nếu quá trình hoàn tất thành công, các dịch vụ db
, wordpress
, và webserver
sẽ có trạng thái Up
, trong khi container certbot
sẽ thoát với mã trạng thái 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
Nếu bất kỳ dịch vụ nào có trạng thái khác Up
, hoặc container certbot
có mã thoát khác 0
, bạn cần kiểm tra nhật ký dịch vụ bằng lệnh sau:
docker-compose logs service_name
Xác nhận rằng chứng chỉ SSL đã được gắn vào container webserver
bằng lệnh:
docker-compose exec webserver ls -la /etc/letsencrypt/live
Nếu yêu cầu chứng chỉ thành công, bạn sẽ nhận được kết quả sau:
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
Loại bỏ --staging
và yêu cầu chứng chỉ SSL thực
Sau khi xác nhận rằng yêu cầu chứng chỉ sẽ thành công, bạn cần chỉnh sửa cấu hình certbot
để loại bỏ cờ --staging
. Mở tệp docker-compose.yml
:
nano docker-compose.yml
Tìm phần định nghĩa dịch vụ certbot
và thay thế --staging
bằng --force-renewal
để yêu cầu chứng chỉ mới:
Sau đó, chạy lệnh sau để tạo lại container certbot, sử dụng –no-deps để tránh khởi động lại container webserver:
docker-compose up --force-recreate --no-deps certbot
Nếu quá trình cấp chứng chỉ thành công, bạn sẽ thấy thông báo sau:
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
Cấu hình Nginx để sử dụng SSL
Sau khi chứng chỉ đã được cấp, bạn có thể tiến hành chỉnh sửa cấu hình Nginx để kích hoạt SSL trên website của mình.
Bước 5 – Chỉnh sửa cấu hình máy chủ Web và định nghĩa dịch vụ
Để kích hoạt SSL trong cấu hình Nginx, bạn cần thêm phần chuyển hướng HTTP sang HTTPS, chỉ định vị trí chứng chỉ và key, cũng như thêm các tham số bảo mật và header. Vì bạn sẽ tái tạo lại dịch vụ webserver để bao gồm các thay đổi này, hãy tạm dừng container webserver:
docker-compose stop webserver
Tải các tham số bảo mật Nginx được khuyến nghị từ Certbot bằng lệnh curl
:
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
Lệnh này sẽ lưu các tham số vào file options-ssl-nginx.conf
trong thư mục nginx-conf.
Xóa file cấu hình Nginx cũ:
rm nginx-conf/nginx.conf
Tạo và mở file cấu hình Nginx mới:
nano nginx-conf/nginx.conf
Thêm đoạn mã sau vào file (đừng quên thay thế your_domain bằng tên miền của bạn):
~/wordpress/nginx-conf/nginx.conf 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; } }
Khối server HTTP xác định webroot cho các yêu cầu gia hạn chứng chỉ của Certbot đến thư mục .well-known/acme-challenge
. Nó cũng bao gồm một chỉ thị rewrite để chuyển hướng tất cả các yêu cầu HTTP đến thư mục gốc sang HTTPS.
Khối server HTTPS kích hoạt SSL
và HTTP/2
. Để tìm hiểu thêm về cách HTTP/2 cải tiến so với các giao thức HTTP trước đây và lợi ích của nó đối với hiệu suất website, hãy đọc bài giới thiệu Cách thiết lập Nginx hỗ trợ HTTP/2 trên Ubuntu 18.04.
Khối này cũng bao gồm đường dẫn đến chứng chỉ SSL và key, cùng với các tham số bảo mật được Certbot đề xuất mà bạn đã lưu trong nginx-conf/options-ssl-nginx.conf.
Ngoài ra, cấu hình này còn bao gồm một số header bảo mật, giúp bạn đạt xếp hạng A trên các công cụ kiểm tra bảo mật như SSL Labs và Security Headers. Các header này bao gồm: X-Frame-Options
, X-Content-Type-Options
, Referrer Policy
, Content-Security-Policy
, X-XSS-Protection
Header HTT
STS)
P
Strict Transport Security
(Hiện đang được ghi chú (commented out). Bạn chỉ nên kích hoạt nó nếu hiểu rõ tác động và đã đánh giá chức năng preload của nó
.
Chỉ thị root
và index
cũng nằm trong khối này, cùng với các khối location
dành riêng cho WordPress đã được thảo luận ở Bước 1.
Sau khi hoàn tất chỉnh sửa, lưu và đóng file.
Trước khi khởi tạo lại dịch vụ webserver
, bạn cần thêm ánh xạ cổng 443
vào định nghĩa dịch vụ webserver.
Mở file docker-compose.yml
:
nano docker-compose.yml
Trong định nghĩa dịch vụ webserver
, hãy thêm ánh xạ cổng sau vào phần ports:
... 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
Dưới đây là file docker-compose.yml
hoàn chỉnh 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
Sau khi chỉnh sửa xong, lưu và đóng file. Tiếp theo, bạn có thể khởi động lại dịch vụ webserver để áp dụng cấu hình SSL mới.
Tạo lại dịch vụ webserver:
docker-compose up -d --force-recreate --no-deps webserver
Kiểm tra trạng thái các dịch vụ:
docker-compose ps
Output đầu ra nên hiển thị rằng các dịch vụ db, wordpress, và webserver đang chạy đúng cách:
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
Khi các container của bạn đã chạy ổn định, bạn có thể hoàn tất quá trình cài đặt WordPress thông qua giao diện web.
Bước 6 – Hoàn tất cài đặt qua giao diện web
Với các container đã chạy, hãy mở trình duyệt và truy cập tên miền của server (thay your_domain bằng tên miền của bạn):
https://your_domain
Chọn ngôn ngữ bạn muốn sử dụng:
Sau khi nhấn Tiếp tục, bạn sẽ được chuyển đến trang thiết lập chính, nơi bạn cần đặt tên cho trang web của mình và tạo một tên người dùng.
Nên chọn một tên người dùng dễ nhớ (tránh sử dụng “admin”) và một mật khẩu mạnh. Bạn có thể sử dụng mật khẩu do WordPress tạo tự động hoặc nhập mật khẩu của riêng bạn.
Cuối cùng, hãy nhập địa chỉ email của bạn và quyết định xem có muốn chặn công cụ tìm kiếm lập chỉ mục trang web hay không (tùy chọn này hữu ích nếu bạn chưa muốn trang web hiển thị trên Google và các công cụ tìm kiếm khác).
Nhấp vào Cài đặt WordPress ở cuối trang sẽ đưa bạn đến màn hình đăng nhập:
Sau khi đăng nhập, bạn sẽ có quyền truy cập vào bảng điều khiển quản trị WordPress.
Sau khi hoàn tất cài đặt WordPress, bạn có thể thực hiện các bước để đảm bảo rằng chứng chỉ SSL sẽ được gia hạn tự động
Bước 7 – Gia hạn chứng chỉ
Chứng chỉ Let’s Encrypt có thời hạn 90 ngày. Để đảm bảo chúng không bị hết hạn, bạn có thể thiết lập một quy trình tự động gia hạn bằng cách sử dụng cron job. Trong hướng dẫn này, bạn sẽ tạo một cron job để chạy định kỳ một script, giúp gia hạn chứng chỉ và tải lại cấu hình Nginx.
Trước tiên, mở một file script có tên ssl_renew.sh bằng lệnh sau:
nano ssl_renew.sh
Thêm đoạn mã sau vào script để gia hạn chứng chỉ và tải lại cấu hình máy chủ web. Hãy nhớ thay thế tên người dùng mẫu bằng tên người dùng không phải 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
Script này trước tiên gán đường dẫn đến docker-compose
vào một biến có tên COMPOSE
, đồng thời chỉ định tùy chọn --no-ansi
để chạy các lệnh docker-compose
mà không có ký tự điều khiển ANSI. Sau đó, nó cũng làm tương tự với docker
bằng cách gán đường dẫn vào biến DOCKER.
Cuối cùng, script sẽ chuyển đến thư mục dự án ~/wordpress
và chạy các lệnh docker-compose
sau:
docker-compose run
: Lệnh này sẽ khởi động containercertbot
và ghi đè lệnh được xác định trong dịch vụcertbot
. Thay vì sử dụng subcommandcertonly
, script sử dụngrenew
, giúp gia hạn chứng chỉ sắp hết hạn. Ngoài ra, tùy chọn--dry-run
được sử dụng để kiểm tra script trước khi chạy thực tế.docker-compose kill
: Lệnh này gửitín hiệu
SIGHUP
đến containerwebserver
để tải lại cấu hình Nginx.docker system prune
: Lệnh này dọn dẹp tất cả các container và image không sử dụng để tiết kiệm dung lượng ổ đĩa.
Sau khi chỉnh sửa xong, lưu và đóng file.
Tiếp theo, cấp quyền thực thi cho script bằng lệnh sau:
chmod +x ssl_renew.sh
Tiếp theo, mở file crontab của root để thiết lập lịch chạy script gia hạn chứng chỉ theo khoảng thời gian định sẵn:
sudo crontab -e
Nếu đây là lần đầu tiên bạn chỉnh sửa file này, hệ thống sẽ yêu cầu bạn chọn một trình soạn thảo:
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]: ...
Ở cuối file, thêm dòng sau:
crontab ... */5 * * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
Dòng lệnh này sẽ đặt khoảng thời gian chạy script mỗi 5 phút, giúp bạn kiểm tra xem yêu cầu gia hạn chứng chỉ có hoạt động đúng không. Một file log
cron.log
sẽ được tạo để ghi lại các thông tin liên quan từ quá trình thực thi.
Sau 5 phút, kiểm tra log bằng lệnh sau để xác nhận xem yêu cầu gia hạn có thành công không:
tail -f /var/log/cron.log
Output sau xác nhận rằng quá trình gia hạn chứng chỉ đã thành công:
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.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Nhấn CTRL+C
trong terminal để thoát khỏi chế độ xem log.
Bạn có thể chỉnh sửa file crontab
để đặt lịch chạy script hàng ngày. Ví dụ, để chạy script mỗi ngày vào lúc 12:00 trưa, hãy thay dòng cuối của file bằng:
crontab ... 0 12 * * * /home/sammy/wordpress/ssl_renew.sh >> /var/log/cron.log 2>&1
Bạn cũng cần xóa tùy chọn --dry-run
khỏi script ssl_renew.sh
để quá trình gia hạn chứng chỉ diễn ra thực sự.
#!/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
Cron job
của bạn sẽ đảm bảo rằng chứng chỉ Let’s Encrypt không bị hết hạn bằng cách tự động gia hạn khi đủ điều kiện.
Bạn cũng có thể thiết lập log rotation bằng công cụ Logrotate trên Ubuntu 22.04 / 20.04 để xoay vòng và nén file log, giúp tránh việc log chiếm quá nhiều dung lượng ổ đĩa.
Kết luận
Trong hướng dẫn này, bạn đã triển khai WordPress bằng Docker Compose, sử dụng Nginx làm máy chủ web. Trong quá trình này, bạn cũng đã cấu hình chứng chỉ TLS/SSL cho tên miền của trang WordPress bằng Let’s Encrypt, giúp bảo mật kết nối. Ngoài ra, để đảm bảo chứng chỉ luôn hợp lệ, bạn đã thiết lập cron job để tự động gia hạn khi cần thiết, giúp website duy trì tính bảo mật và hoạt động ổn định.
Để cải thiện hiệu suất và độ tin cậy của trang web, bạn có thể tham khảo thêm bài viết Cách Cài Đặt WordPress với MySQL trên Kubernetes Sử Dụng Helm.