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.
Mục lục
- 1 Giới thiệu
- 2 Chuẩn bị để cài đặt WordPress bằng Docker Compose
- 3 Bước 1: Thiết lập cấu hình Web Server
- 4 Bước 2: Định nghĩa biến môi trường (Environment Variables)
- 5 Bước 3: Định nghĩa service với Docker Compose
- 6 Bước 4: Yêu cầu chứng chỉ và chứng nhận SSL
- 7 Bước 5: Cập nhật cấu hình Service và Web Server
- 8 Bước 6: Hoàn tất cài đặt bằng giao diện web
- 9 Bước 7: Gia hạn chứng chỉ
- 10 Lời kết
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
sudovà 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_domaintrỏ tới địa chỉ IP của server. - Một A record với tên miền
www.your_domaintrỏ tới địa chỉ IP của server.
- Một A record với tên miền
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 wordpressCác bạn mở thư mục vừa tạo.
$ cd wordpressTiếp theo, chúng ta tạo thư mục chứa file cấu hình.
$ mkdir nginx-confSau 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.confChú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ổng80để 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ổng443. Ở 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 thayyour_domainbằ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 fileindex.phptrướcindex.htmlđể Nginx ưu tiên gọiindex.phpkhi 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/htmlcò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_filesgiú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 fileindex.phpcủ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 đếnwordpresscontainer. Do Nginx không có trình xử lí PHP, chúng ta sử dụng trìnhphp-fpmđược imagephp:fpmtích hợp sẵn. Vì phụ thuộc imagephp: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 chowordpress, đồ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.ico,location = /robots.txt: Không lưu lịch sử các request yêu cầu file/favicon.icovà/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 .envChú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 initCác bạn tạo và mở file .gitignore.
$ nano .gitignoreThê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 .dockerignoreThêm .env vào file.
.envCá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.ymlThê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-networkCấu hình của service db bao gồm:
image: Quy định image được dùng để tạo container. Do imagemysql:latestluôn thay đổi, để tránh xung đột, ở đây Mailinhwp.vn sẽ sử dụng cố định phiên bản imagemysql: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ủarestartlà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.envtrong 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ánwordpresscho biếnMYSQL_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 filedocker-compose.ymlvì 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êndbdata(named volume) vào thư mục/var/lib/mysqltrê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 trongmysql: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ạngapp-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-networkTrong 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 servicewordpresskhởi động sau servicedb.image: Tạo container bằng image WordPress5.1.1-fpm-alpine– một imagealpinecó nguồn gốc từ dự án Alpine Linux.5.1.1-fpm-alpinecó nhiệm vụ giảm kích thước image tổng thể, đồng thời cung cấp trìnhphp-fpmcầ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ừ.envcho 2 biếnWORDPRESS_DB_USERvàWORDPRESS_DB_PASSWORDcủa image WordPress.WORDPRESS_DB_HOSTsẽ truy cập server MySQL trên cổng mặc định3306của servicedb. Tên cơ sở dữ liệuWORDPRESS_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_DATABASE:wordpress).volumes: Mount volume có tênwordpressvà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 containerwordpressvào mạngapp-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-networkCá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ổng80, tạo điều kiện để cấu hình trong filenginx.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ụcroottrong 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 trongwebserver.
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_domainVớ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: bridgevolumes là khóa top-level dùng để định nghĩa các volume certbot-etc, dbdata 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 db, wordpress 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 db, wordpress và webserver chạy nền.
$ docker-compose up -dCá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 ... doneCác bạn kiểm tra trạng thái của service bằng lệnh docker-compose ps.
$ docker-compose psKhi hoàn tất khởi động, các service db, wordpress 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/tcpTrườ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_nameNế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/liveCá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_domainSau 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.ymlDướ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 certbotOutput 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 0Sau 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 webserverTrướ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.confDò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.confBạn tạo lại một phiên bản khác của file.
$ nano nginx-conf/nginx.confCá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-Options, X-Content-Type-Options, Referrer Policy, Content-Security-Policy và X-XSS-Protection. Những header này có thể giúp các bạn đạt A 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.ymlTrong 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-networkCá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 psNế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/tcpGiờ đâ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ị.

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.

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.

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

Đế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.shBạ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 -afTrong đ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 containercertbotvà ghi đè tùy chọncommandđã định nghĩa trong service. Thay chocertonly, subcommandrenewđượ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ệuSIGHUPđến containerwebserverđể 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.shChúng ta tiếp tục mở file root crontab để cấu hình cho script chạy định kỳ.
$ sudo crontab -eNế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>&1Hiệ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.logCá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>&1Cá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 -afJob 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é.

![[WooCommerce] Hiển thị ngày dự kiến giao hàng trên trang Sản phẩm 5 [WooCommerce] Hiển thị ngày dự kiến giao hàng trên trang Sản phẩm](https://mailinhwp.vn/wp-content/uploads/2025/02/woo-commerce-eshop.png)
![[Flatsome] Tạo popup form liên hệ báo giá bằng lightbox trong theme flatsome 6 [Flatsome] Tạo popup form liên hệ báo giá bằng lightbox trong theme flatsome](https://mailinhwp.vn/wp-content/uploads/2025/02/flatsome.png)


