Thứ sáu, 24/05/2019 | 00:00 GMT+7

Cách cài đặt WordPress với Docker Compose

WordPress là một Hệ thống Quản lý Nội dung (CMS) open-souce và miễn phí được xây dựng trên sở dữ liệu MySQL với xử lý PHP . Nhờ kiến trúc plugin có thể mở rộng và hệ thống tạo mẫu, và thực tế là hầu hết việc quản trị của nó có thể được thực hiện thông qua giao diện web, WordPress là một lựa chọn phổ biến khi tạo các loại trang web khác nhau, từ blog đến các trang sản phẩm đến các trang Thương mại điện tử.

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

Trong hướng dẫn này, bạn sẽ xây dựng một cài đặt WordPress nhiều containers . Vùng chứa của bạn sẽ bao gồm database MySQL, web server Nginx và chính WordPress. Bạn cũng sẽ bảo mật cài đặt của bạn bằng cách lấy certificate TLS / SSL với Let's Encrypt cho domain bạn muốn liên kết với trang web của bạn . Cuối cùng, bạn sẽ cài đặt một cron việc cron để gia hạn certificate của bạn để domain của bạn vẫn an toàn.

Yêu cầu

Để làm theo hướng dẫn này, bạn cần :

  • Server chạy Ubuntu 18.04, cùng với user không phải root có quyền sudo và firewall đang hoạt động. Để được hướng dẫn về cách cài đặt những điều này, vui lòng xem hướng dẫn Cài đặt Server Ban đầu này.
  • Docker được cài đặt trên server của bạn, làm theo các Bước 1 và 2 của Cách cài đặt và sử dụng Docker trên Ubuntu 18.04 .
  • Docker Compose được cài đặt trên server của bạn, làm theo Bước 1 của Cách cài đặt Docker Compose trên Ubuntu 18.04 .
  • Tên domain đã đăng ký. Hướng dẫn này sẽ sử dụng example.com xuyên suốt. Bạn có thể nhận một miễn phí tại Freenom hoặc sử dụng công ty đăng ký domain mà bạn chọn.
  • Cả hai bản ghi DNS sau được cài đặt cho server của bạn. Bạn có thể theo dõi phần giới thiệu này về DigitalOcean DNS để biết chi tiết về cách thêm chúng vào account DigitalOcean, nếu đó là những gì bạn đang sử dụng:

    • Bản ghi A với example.com trỏ đến địa chỉ IP công cộng của server của bạn.
    • Một bản ghi A với www. example.com trỏ đến địa chỉ IP công cộng của server của bạn.

Bước 1 - Xác cấu hình web server

Trước khi chạy bất kỳ containers nào, bước đầu tiên của ta sẽ là xác cấu hình cho web server Nginx của ta . Tệp cấu hình của ta sẽ bao gồm một số khối vị trí dành riêng cho WordPress, cùng với khối vị trí để hướng các yêu cầu xác minh Let's Encrypt đến ứng dụng client Certbot để gia hạn certificate tự động.

Đầu tiên, hãy tạo một folder dự án cho cài đặt WordPress của bạn có tên là wordpress và chuyển đến nó:

  • mkdir wordpress && cd wordpress

Tiếp theo, tạo một folder cho file cấu hình:

  • mkdir nginx-conf

Mở file bằng nano hoặc editor bạn quen dùng :

  • nano nginx-conf/nginx.conf

Trong file này, ta sẽ thêm một khối server với các lệnh cho tên server và root tài liệu của ta cũng như các khối vị trí để định hướng yêu cầu của ứng dụng client Certbot đối với certificate , PHP processor và các yêu cầu nội dung tĩnh.

Dán đoạn mã sau vào file . Đảm bảo thay thế example.com bằng domain của bạn :

~ / wordpress / nginx-conf / nginx.conf
server {         listen 80;         listen [::]:80;          server_name example.com www.example.com;          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;         } } 

Khối server của ta bao gồm các thông tin sau:

Chỉ thị:

  • listen : Đây nói với Nginx để lắng nghe trên cổng 80 , mà sẽ cho phép ta sử dụng Certbot của Plugin webroot cho các yêu cầu giấy chứng nhận của ta . Lưu ý ta chưa bao gồm cổng 443 - ta sẽ cập nhật cấu hình của bạn để bao gồm SSL khi ta đã lấy được certificate thành công.
  • server_name : Điều này xác định tên server của bạn và khối server sẽ được sử dụng cho các yêu cầu tới server của bạn. Đảm bảo thay thế example.com trong dòng này bằng domain của bạn .
  • index : index thị index xác định các file sẽ được sử dụng làm index khi xử lý các yêu cầu đến server của bạn. Ta đã sửa đổi thứ tự ưu tiên mặc định ở đây, di chuyển index.php trước index.html để Nginx ưu tiên các file được gọi là index.php khi có thể.
  • root : Chỉ thị root của ta đặt tên cho folder root cho các yêu cầu tới server của ta . Thư mục này, /var/www/html , được tạo như một điểm mount tại thời điểm xây dựng theo hướng dẫn trong WordPress Dockerfile của ta . Các hướng dẫn Dockerfile này cũng đảm bảo các file từ bản phát hành WordPress được gắn vào ổ đĩa này.

Khối vị trí:

  • location ~ /.well-known/acme-challenge : Khối vị trí này sẽ xử lý các yêu cầu .well-known folder .well .well-known , nơi Certbot sẽ đặt một file tạm thời để xác thực rằng DNS cho domain của ta phân giải tới server của ta . Với cấu hình này, ta sẽ có thể sử dụng plugin webroot của Certbot để lấy certificate cho domain của ta .
  • location / : Trong khối vị trí này, ta sẽ sử dụng lệnh try_files để kiểm tra các file phù hợp với các yêu cầu URI riêng lẻ. Tuy nhiên, thay vì trả về trạng thái 404 Not Found làm mặc định, ta sẽ chuyển quyền kiểm soát đến index.php của WordPress với các đối số yêu cầu.
  • location ~ \.php$ : Khối vị trí này sẽ xử lý quá trình PHP processor và ủy quyền các yêu cầu này tới containers wordpress của ta . Vì Docker image WordPress của ta sẽ dựa trên hình ảnh php:fpm , ta cũng sẽ bao gồm các tùy chọn cấu hình dành riêng cho giao thức FastCGI trong khối này. Nginx yêu cầu một PHP processor độc lập cho các yêu cầu PHP: trong trường hợp của ta , các yêu cầu này sẽ được xử lý bởi bộ xử lý php-fpm bao gồm trong hình ảnh php:fpm . Ngoài ra, khối vị trí này bao gồm các chỉ thị, biến và tùy chọn dành riêng cho FastCGI sẽ ủy quyền các yêu cầu đến ứng dụng WordPress đang chạy trong containers wordpress của ta , đặt index ưu tiên cho URI yêu cầu được phân tích cú pháp và phân tích cú pháp các yêu cầu URI.
  • location ~ /\.ht : Khối này sẽ xử lý các .htaccess vì Nginx sẽ không phân phát chúng. Chỉ thị deny_all đảm bảo các .htaccess sẽ không bao giờ được cung cấp cho user .
  • location = /favicon.ico , location = /robots.txt : Các khối này đảm bảo các yêu cầu đến /favicon.ico/robots.txt sẽ không được ghi lại.
  • location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ : Khối này tắt ghi log cho các yêu cầu nội dung tĩnh và đảm bảo những nội dung này có khả năng lưu vào bộ nhớ cache cao, vì chúng thường đắt tiền để phân phát.

Để biết thêm thông tin về FastCGI proxy, hãy xem Hiểu và triển khai FastCGI Proxying trong Nginx . Để biết thông tin về khối server và vị trí, hãy xem Tìm hiểu về server Nginx và các thuật toán lựa chọn khối vị trí .

Lưu file khi bạn hoàn tất chỉnh sửa.Nếu bạn sử dụng nano , hãy làm như vậy bằng cách nhấn CTRL+X , Y , sau đó ENTER .

Với cấu hình Nginx của bạn tại chỗ, bạn có thể chuyển sang tạo các biến môi trường để chuyển đến ứng dụng và containers database của bạn trong thời gian chạy.

Bước 2 - Xác định các biến môi trường

Database và containers ứng dụng WordPress của bạn cần quyền truy cập vào các biến môi trường nhất định trong thời gian chạy để dữ liệu ứng dụng của bạn tồn tại và có thể truy cập vào ứng dụng của bạn. Các biến này bao gồm cả thông tin nhạy cảm và không nhạy cảm: giá trị nhạy cảm cho password gốc MySQL và password user và database ứng dụng của bạn và thông tin không nhạy cảm cho tên và server database ứng dụng của bạn.

Thay vì đặt tất cả các giá trị này trong file Docker Compose của ta - file chính chứa thông tin về cách các containers của ta sẽ chạy - ta có thể đặt các giá trị nhạy cảm trong file .env và hạn chế lưu thông của nó. Điều này sẽ ngăn các giá trị này sao chép vào kho dự án của ta và bị lộ công khai.

Trong folder dự án chính của bạn, ~/ wordpress , hãy mở một file có tên .env :

  • nano .env

Các giá trị bí mật mà ta sẽ đặt trong file này bao gồm password cho user gốc MySQL của ta , tên user và password mà WordPress sẽ sử dụng để truy cập database .

Thêm các tên và giá trị biến sau vào file . Hãy nhớ cung cấp các giá trị của bạn tại đây cho mỗi biến:

~ / wordpress / .env
MYSQL_ROOT_PASSWORD=your_root_password MYSQL_USER=your_wordpress_database_user MYSQL_PASSWORD=your_wordpress_database_password 

Ta đã bao gồm password cho account quản trị gốc , cũng như tên user và password ưa thích cho database ứng dụng của ta .

Lưu file khi bạn hoàn tất chỉnh sửa.

Bởi vì file .env của bạn chứa thông tin nhạy cảm, bạn cần đảm bảo nó có trong các file .gitignore.dockerignore của dự án .gitignore .dockerignore file này sẽ cho Git và Docker biết những file nào không được sao chép vào repository Git và Docker image của bạn.

Nếu bạn định làm việc với Git để kiểm soát version , hãy khởi tạo folder làm việc hiện tại của bạn dưới dạng repository với git init :

  • git init

Sau đó, mở file .gitignore :

  • nano .gitignore

Thêm .env vào file :

~ / wordpress / .gitignore
.env 

Lưu file khi bạn hoàn tất chỉnh sửa.

Tương tự như vậy, bạn nên thận trọng khi thêm .env vào file .dockerignore , để nó không kết thúc trên containers của bạn khi bạn đang sử dụng folder này làm ngữ cảnh xây dựng của bạn .

Mở tập tin:

  • nano .dockerignore

Thêm .env vào file :

~ / wordpress / .dockerignore
.env 

Dưới đây, bạn có thể tùy chọn thêm các file và folder được liên kết với sự phát triển ứng dụng của bạn :

~ / wordpress / .dockerignore
.env .git docker-compose.yml .dockerignore 

Lưu file khi bạn hoàn tất.

Với thông tin nhạy cảm của bạn đã có sẵn, giờ đây bạn có thể chuyển sang xác định các dịch vụ của bạn trong file docker-compose.yml .

Bước 3 - Xác định Dịch vụ với Docker Compose

Tệp docker-compose.yml của bạn sẽ chứa các định nghĩa dịch vụ cho cài đặt của bạn. Dịch vụ trong Soạn là một containers đang chạy và các định nghĩa dịch vụ chỉ định thông tin về cách mỗi containers sẽ chạy.

Sử dụng tính năng Soạn thư, bạn có thể xác định các dịch vụ khác nhau để chạy các ứng dụng nhiều containers , vì tính năng Soạn thư cho phép bạn liên kết các dịch vụ này với nhau với các mạng và dung lượng chia sẻ. Điều này sẽ hữu ích cho cài đặt hiện tại của ta vì ta sẽ tạo các containers khác nhau cho database , ứng dụng WordPress và web server của ta . Ta cũng sẽ tạo một containers để chạy ứng dụng client Certbot nhằm lấy certificate cho web server của ta .

Để bắt đầu, hãy mở file docker-compose.yml :

  • nano docker-compose.yml

Thêm mã sau để xác định version file Soạn và dịch vụ database db :

~ / wordpress / docker-compo.yml
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 : Điều này cho biết Soạn hình ảnh nào cần kéo để tạo containers . Ta đang ghim hình ảnh mysql: 8.0 ở đây để tránh xung đột trong tương lai vì hình ảnh mysql:latest tiếp tục được cập nhật. Để biết thêm thông tin về ghim version và tránh xung đột phụ thuộc, hãy xem tài liệu Docker về các phương pháp hay nhất của Dockerfile .
  • container_name : Điều này chỉ định tên cho containers .
  • restart : Điều này xác định policy khởi động lại containers . Mặc định là no , nhưng ta đã đặt containers khởi động lại trừ khi nó bị dừng theo cách thủ công.
  • env_file : Tùy chọn này cho Soạn biết rằng ta muốn thêm các biến môi trường từ một file có tên .env , nằm trong ngữ cảnh xây dựng của ta . Trong trường hợp này, bối cảnh xây dựng là folder hiện tại của ta .
  • 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 số được xác định trong file .env của bạn. Ta sẽ đặt biến MYSQL_DATABASE bằng với wordpress để cung cấp tên cho database ứng dụng của ta . Vì đây là thông tin không nhạy cảm nên ta có thể đưa trực tiếp thông tin đó vào file docker-compose.yml .
  • volumes : Ở đây, ta đang gắn một volume có tên là dbdata vào folder /var/lib/mysql trên containers . Đây là folder dữ liệu tiêu chuẩn cho 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 để overrides lệnh CMD mặc định cho hình ảnh. Trong trường hợp của ta , ta sẽ thêm một tùy chọn vào lệnh mysqld chuẩn của Docker image , lệnh này khởi động server MySQL trên containers . Tùy chọn này, --default-authentication-plugin=mysql_native_password , đặt biến hệ thống mysql_native_password --default-authentication-plugin thành mysql_native_password , chỉ định cơ chế xác thực nào sẽ chi phối các yêu cầu xác thực mới tới server . Vì PHP và do đó hình ảnh WordPress của ta sẽ không hỗ trợ mặc định xác thực mới hơn của MySQL , ta phải thực hiện điều chỉnh này để xác thực user database ứng dụng của ta .
  • networks : Điều này chỉ định rằng dịch vụ ứng dụng của ta sẽ tham gia app-network mà ta sẽ xác định ở cuối file .

Tiếp theo, bên dưới định nghĩa dịch vụ db của bạn, hãy thêm định nghĩa cho dịch vụ ứng dụng wordpress của bạn:

~ / wordpress / docker-compo.yml
...   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, ta đang đặt tên containers của bạn và xác định policy khởi động lại, như ta đã làm với dịch vụ db . Ta cũng đang thêm một số tùy chọn cụ thể cho containers này:

  • depends_on : Tùy chọn này đảm bảo các containers của ta sẽ bắt đầu theo thứ tự phụ thuộc, với containers wordpress bắt đầu sau containers db . Ứng dụng WordPress của ta dựa trên sự tồn tại của database ứng dụng và user của ta , vì vậy việc thể hiện thứ tự phụ thuộc này sẽ cho phép ứng dụng của ta khởi động đúng cách.
  • image : Đối với cài đặt này, ta đang sử dụng hình ảnh WordPress 5.1.1 -fpm-alpine . Như đã thảo luận trong Bước 1 , việc sử dụng hình ảnh này đảm bảo ứng dụng của ta sẽ có bộ xử lý php-fpm mà Nginx yêu cầu để xử lý quá trình PHP processor . Đây cũng là một hình ảnh alpine , có nguồn root từ dự án Alpine Linux , sẽ giúp giảm kích thước hình ảnh tổng thể của ta . Để biết thêm thông tin về lợi ích và hạn chế của việc sử dụng hình ảnh alpine và liệu điều này có phù hợp với ứng dụng của bạn hay không, hãy xem toàn bộ cuộc thảo luận trong phần Biến thể hình ảnh của trang Docker image Hub WordPress .
  • env_file : , ta chỉ định rằng ta muốn lấy các giá trị từ file .env , vì đây là nơi ta xác định user và password database ứng dụng của bạn .
  • environment : Ở đây, ta đang sử dụng các giá trị mà ta đã xác định trong file .env của .env , nhưng ta đang gán chúng cho các tên biến mà hình ảnh WordPress mong đợi: WORDPRESS_DB_USERWORDPRESS_DB_PASSWORD . Ta cũng đang định nghĩa WORDPRESS_DB_HOST , sẽ là server MySQL chạy trên containers db có thể truy cập trên cổng mặc định của MySQL, 3306 . WORDPRESS_DB_NAME của ta sẽ có cùng giá trị mà ta đã chỉ định trong định nghĩa dịch vụ MySQL cho MYSQL_DATABASE : wordpress của ta .
  • volumes : Ta đang gắn một volume có tên là wordpress vào mountpoint /var/www/html được tạo bởi hình ảnh WordPress . Sử dụng một ổ đĩa được đặt tên theo cách này sẽ cho phép ta chia sẻ mã ứng dụng của bạn với các containers khác.
  • networks : Ta cũng đang thêm containers wordpress vào app-network mạng.

Tiếp theo, bên dưới định nghĩa dịch vụ ứng dụng wordpress , hãy thêm định nghĩa sau cho dịch vụ webserver Nginx của bạn:

~ / wordpress / docker-compo.yml
...   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 

, ta đặt tên cho containers của bạn và làm cho nó phụ thuộc vào containers wordpress theo thứ tự bắt đầu. Ta cũng đang sử dụng một alpine hình ảnh - những 1.15.12 -alpine Nginx hình ảnh .

Định nghĩa dịch vụ này cũng bao gồm các tùy chọn sau:

  • ports : Điều này cho thấy cổng 80 để kích hoạt các tùy chọn cấu hình mà ta đã xác định trong file nginx.conf của ta ở Bước 1 .
  • volumes : Ở đây, ta đang xác định sự kết hợp của các volume được đặt tên và các mount liên kết :
    • wordpress:/var/www/html : Thao tác này sẽ gắn mã ứng dụng WordPress của ta vào folder /var/www/html , folder mà ta đặt làm folder root trong khối server Nginx của ta .
    • ./nginx-conf:/etc/nginx/conf.d : Thao tác này sẽ mount folder cấu hình Nginx trên server lưu trữ với folder liên quan trên containers , đảm bảo bất kỳ thay đổi nào ta thực hiện đối với file trên server lưu trữ sẽ được phản ánh trong thùng đựng hàng.
    • certbot-etc:/etc/letsencrypt : Điều này sẽ gắn các certificate và khóa Let's Encrypt có liên quan cho domain của ta vào folder thích hợp trên containers .

Và , ta đã thêm containers này vào app-network mạng.

Cuối cùng, bên dưới định nghĩa webserver của bạn, hãy thêm định nghĩa dịch vụ cuối cùng của bạn cho dịch vụ certbot . Đảm bảo thay thế địa chỉ email và domain được liệt kê ở đây bằng thông tin của bạn :

~ / wordpress / docker-compo.yml
  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@example.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com 

Định nghĩa này yêu cầu Soạn thảo kéo hình ảnh certbot/certbot từ Docker Hub. Nó cũng sử dụng dung lượng được đặt tên để chia sẻ tài nguyên với containers Nginx, bao gồm certificate domain và khóa trong certbot-etc và mã ứng dụng trong wordpress .

, ta đã sử dụng depends_on để chỉ định rằng containers certbot sẽ được khởi động khi dịch vụ webserver đang chạy.

Ta cũng đã bao gồm một tùy chọn command định một lệnh con để chạy với lệnh certbot mặc định của containers . Lệnh con certonly sẽ nhận được certificate với các tùy chọn sau:

  • --webroot : Điều này yêu cầu Certbot sử dụng plugin webroot để đặt các file trong folder webroot để xác thực. Plugin này phụ thuộc vào phương thức xác thực HTTP-01 , phương thức này sử dụng yêu cầu HTTP để chứng minh rằng Certbot có thể truy cập tài nguyên từ server phản hồi với một domain nhất định.
  • --webroot-path : Điều này chỉ định đường dẫn của folder webroot.
  • --email : Email bạn muốn để đăng ký và khôi phục.
  • --agree-tos : Điều này chỉ rõ rằng bạn đồng ý với Thỏa thuận người đăng ký của ACME .
  • --no-eff-email : Điều này cho Certbot biết rằng bạn không muốn chia sẻ email của bạn với Electronic Frontier Foundation (EFF). Hãy bỏ qua điều này nếu bạn muốn.
  • --staging : Điều này cho Certbot biết rằng bạn muốn sử dụng môi trường staging của Let's Encrypt để lấy certificate thử nghiệm. Sử dụng tùy chọn này cho phép bạn kiểm tra các tùy chọn cấu hình của bạn và tránh các giới hạn yêu cầu domain có thể xảy ra. Để biết thêm thông tin về các giới hạn này, vui lòng xem tài liệu giới hạn tỷ lệ của Let's Encrypt.
  • -d : Điều này cho phép bạn chỉ định domain mà bạn muốn áp dụng cho yêu cầu của bạn . Trong trường hợp này, ta đã bao gồm example.comwww. example.com . Đảm bảo thay thế các domain này bằng domain của bạn .

Bên dưới định nghĩa dịch vụ certbot , hãy thêm định nghĩa mạng và dung lượng của bạn:

~ / wordpress / docker-compo.yml
... volumes:   certbot-etc:   wordpress:   dbdata:  networks:   app-network:     driver: bridge   

Khóa volumes cấp cao nhất của ta xác định các dung lượng certbot-etc , wordpressdbdata . Khi Docker tạo tập, nội dung của tập được lưu trữ trong một folder trên hệ thống file server , /var/lib/docker/volumes/ , được quản lý bởi Docker. Nội dung của mỗi tập sau đó được mount từ folder này vào bất kỳ containers nào sử dụng tập. Bằng cách này, có thể chia sẻ mã và dữ liệu giữa các containers .

Mạng app-network mạng cầu nối do user xác định cho phép giao tiếp giữa các containers của ta vì chúng nằm trên cùng một server Docker daemon. Điều này hợp lý hóa lưu lượng truy cập và giao tiếp trong ứng dụng, vì nó 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 để lộ bất kỳ cổng nào ra thế giới bên ngoài. Do đó, các containers db , wordpresswebserver của ta có thể giao tiếp với nhau và ta chỉ cần để lộ cổng 80 để truy cập front-end vào ứng dụng.

Tệp docker-compose.yml đã hoàn thành sẽ giống như sau:

~ / wordpress / docker-compo.yml
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@example.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com  volumes:   certbot-etc:   wordpress:   dbdata:  networks:   app-network:     driver: bridge   

Lưu file khi bạn hoàn tất chỉnh sửa.

Với các định nghĩa dịch vụ của bạn, bạn đã sẵn sàng khởi động các containers và kiểm tra các certificate request của bạn .

Bước 4 - Lấy certificate SSL và thông tin đăng nhập

Ta có thể bắt đầu các containers của bạn bằng lệnh docker-compose up , lệnh này sẽ tạo và chạy các containers của ta theo thứ tự mà ta đã chỉ định. Nếu yêu cầu domain của ta thành công, ta sẽ thấy trạng thái thoát chính xác trong kết quả của ta và các certificate phù hợp được gắn trong folder /etc/letsencrypt/live trên containers webserver .

Tạo các containers với docker-compose up và cờ -d , sẽ chạy các containers db , wordpresswebserver trong nền:

  • docker-compose up -d

Bạn sẽ thấy kết quả xác nhận các dịch vụ của bạn đã được tạo:

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

Sử dụng docker-compose ps , kiểm tra trạng thái dịch vụ của bạn:

  • docker-compose ps

Nếu mọi thứ thành công, các dịch vụ db , wordpresswebserver bạn sẽ được Up và containers certbot sẽ thoát với thông báo 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ạn thấy bất cứ điều gì khác hơn Up trong State cột cho db , wordpress , hoặc webserver dịch vụ, hoặc một trạng thái thoát khác hơn 0 cho certbot container, hãy chắc chắn để kiểm tra các bản ghi dịch vụ với các docker-compose logs lệnh:

  • docker-compose logs service_name

Bây giờ, bạn có thể kiểm tra xem các certificate của bạn đã được gắn vào containers webserver bằng docker-compose exec :

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

Nếu certificate request của bạn thành công, bạn sẽ thấy kết quả như 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 example.com

Đến đây bạn biết rằng yêu cầu của bạn sẽ thành công, bạn có thể chỉnh sửa định nghĩa dịch vụ certbot để xóa cờ --staging .

Mở docker-compose.yml :

  • nano docker-compose.yml

Tìm phần của file có định nghĩa dịch vụ certbot và thay thế cờ --staging trong tùy chọn command bằng cờ --force-renewal , cờ này sẽ cho Certbot biết rằng bạn muốn certificate request mới với các domain giống như một certificate hiện có. Định nghĩa dịch vụ certbot bây giờ sẽ giống như sau:

~ / wordpress / docker-compo.yml
...   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@example.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com ... 

Đến đây bạn có thể chạy certbot docker-compose up để tạo lại containers certbot . Ta cũng sẽ bao gồm tùy chọn --no-deps để cho Soạn biết rằng nó có thể bỏ qua việc khởi động dịch vụ webserver , vì nó đã chạy:

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

Bạn sẽ thấy kết quả cho biết rằng certificate request của bạn đã 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 example.com certbot | http-01 challenge for www.example.com 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/example.com/fullchain.pem certbot | Your key file has been saved at: certbot | /etc/letsencrypt/live/example.com/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

Với các certificate của bạn, bạn có thể chuyển sang sửa đổi cấu hình Nginx của bạn để bao gồm SSL.

Bước 5 - Sửa đổi cấu hình web server và định nghĩa dịch vụ

Bật SSL trong cấu hình Nginx của ta sẽ liên quan đến việc thêm chuyển hướng HTTP đến HTTPS, chỉ định certificate SSL và các vị trí chính của ta , đồng thời thêm các thông số bảo mật và tiêu đề.

Vì bạn sẽ tạo lại dịch vụ webserver để bao gồm những bổ sung này, bạn có thể dừng nó ngay bây giờ:

  • docker-compose stop webserver

Trước khi ta sửa đổi chính file cấu hình, trước tiên hãy lấy các thông số bảo mật Nginx được đề xuất từ Certbot bằng cách sử dụng 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ố này trong một file có tên là options-ssl-nginx.conf , nằm trong folder nginx-conf .

Tiếp theo, xóa file cấu hình Nginx bạn đã tạo trước đó:

  • rm nginx-conf/nginx.conf

Mở một version khác của file :

  • nano nginx-conf/nginx.conf

Thêm mã sau vào file để chuyển hướng HTTP sang HTTPS và thêm thông tin đăng nhập SSL, giao thức và tiêu đề bảo mật. Hãy nhớ thay thế example.com bằng domain của bạn :

~ / wordpress / nginx-conf / nginx.conf
server {         listen 80;         listen [::]:80;          server_name example.com www.example.com;          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 example.com www.example.com;          index index.php index.html index.htm;          root /var/www/html;          server_tokens off;          ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;         ssl_certificate_key /etc/letsencrypt/live/example.com/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 chỉ định webroot cho các yêu cầu gia hạn Certbot tới folder .well .well-known/acme-challenge . Nó cũng bao gồm một chỉ thị viết lại hướng các yêu cầu HTTP đến folder root tới HTTPS.

Khối server HTTPS cho phép sslhttp2 . Để đọc thêm về cách HTTP / 2 lặp lại trên các giao thức HTTP và những lợi ích mà nó có thể có đối với hiệu suất trang web, vui lòng xem phần giới thiệu về Cách Cài đặt Nginx với Hỗ trợ HTTP / 2 trên Ubuntu 18.04 .

Khối này cũng bao gồm certificate SSL và các vị trí chính của ta , cùng với các thông số bảo mật Certbot được đề xuất mà ta đã lưu vào nginx-conf/options-ssl-nginx.conf .

Ngoài ra, ta đã bao gồm một số tiêu đề bảo mật sẽ cho phép ta nhận được xếp hạng A trên những thứ như Phòng thí nghiệm SSL và các trang web kiểm tra server Tiêu đề bảo mật . Các tiêu đề này bao gồm X-Frame-Options , X-Content-Type-Options , Referrer Policy , Content-Security-PolicyX-XSS-Protection . Tiêu đề Strict Transport Security HTTP (HSTS) được comment - chỉ bật tính năng này nếu bạn hiểu ý nghĩa và đã đánh giá chức năng “tải trước” của nó .

Các index thị rootindex của ta cũng nằm trong khối này, cũng như phần còn lại của các khối vị trí dành riêng cho WordPress được thảo luận trong Bước 1 .

Khi bạn đã chỉnh sửa xong, hãy lưu file .

Trước khi 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 của bạn .

Mở file docker-compose.yml của bạn:

  • nano docker-compose.yml

Trong định nghĩa dịch vụ webserver , hãy thêm ánh xạ cổng sau:

~ / wordpress / docker-compo.yml
...   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 

Tệp docker-compose.yml sẽ trông như thế này khi hoàn tất:

~ / wordpress / docker-compo.yml
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@example.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com  volumes:   certbot-etc:   wordpress:   dbdata:  networks:   app-network:     driver: bridge   

Lưu file khi bạn hoàn tất chỉnh sửa.

Tạo lại dịch vụ webserver :

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

Kiểm tra các dịch vụ của bạn bằng docker-compose ps :

  • docker-compose ps

Bạn sẽ thấy kết quả cho biết rằng các dịch vụ db , wordpresswebserver đang chạ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

Với các containers của bạn đang chạy, bây giờ bạn có thể hoàn tất cài đặt WordPress của bạn thông qua giao diện web.

Bước 6 - Hoàn tất cài đặt thông qua giao diện web

Với các containers của ta đang chạy, ta có thể hoàn tất cài đặt thông qua giao diện web WordPress.

Trong trình duyệt web , chuyển đến domain server của bạn. Hãy nhớ thay thế example.com tại đây bằng domain của bạn :

https://example.com 

Chọn ngôn ngữ bạn muốn sử dụng:

Bộ chọn ngôn ngữ WordPress

Sau khi nhấp vào Tiếp tục , bạn sẽ đến trang cài đặt chính, nơi bạn cần chọn tên cho trang web của bạn và tên user . Bạn nên chọn một tên user dễ nhớ ở đây (thay vì "admin" ) và một password mạnh. Bạn có thể sử dụng password mà WordPress tạo tự động hoặc tạo password của bạn .

Cuối cùng, bạn cần nhập địa chỉ email của bạn và quyết định xem bạn có muốn ngăn cản các công cụ tìm kiếm lập index trang web hay không:

Trang  cài đặt  chính của WordPress

Nhấp vào Cài đặt WordPress ở cuối trang sẽ đưa bạn đến dấu nhắc đăng nhập:

Màn hình đăng nhập WordPress

Sau khi đăng nhập, bạn sẽ có quyền truy cập vào console quản trị WordPress:

Trang tổng quan quản trị chính của WordPress

Với việc cài đặt WordPress của bạn hoàn tất, bây giờ bạn có thể thực hiện các bước đảm bảo rằng certificate SSL của bạn sẽ tự động gia hạn.

Bước 7 - Gia hạn certificate

Chứng chỉ Let's Encrypt có giá trị trong 90 ngày, vì vậy bạn cần cài đặt quy trình gia hạn tự động đảm bảo rằng chúng không mất hiệu lực. Một cách để làm điều này là tạo một công việc với tiện ích lập lịch cron . Trong trường hợp này, ta sẽ tạo một cron việc cron để chạy định kỳ một tập lệnh sẽ gia hạn certificate của ta và reload cấu hình Nginx của ta .

Đầu tiên, hãy mở một tập lệnh có tên ssl_renew.sh :

  • nano ssl_renew.sh

Thêm mã sau vào tập lệnh để gia hạn certificate và reload cấu hình web server của bạn. Hãy nhớ thay thế tên user mẫu ở đây bằng tên user không phải root của bạn :

~ / wordpress / 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 --dry-run && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af 

Đầu tiên, tập lệnh này sẽ gán mã binary docker-compose cho một biến có tên là COMPOSE và chỉ định tùy chọn --no-ansi , tùy chọn này sẽ chạy các lệnh docker-compose mà không có ký tự điều khiển ANSI . Sau đó, nó thực hiện tương tự với mã binary docker . Cuối cùng, nó thay đổi thành folder dự án ~/wordpress và chạy các lệnh docker-compose sau:

  • certbot docker-compose run : Thao tác này sẽ khởi động certbot chứa certbot và overrides command được cung cấp trong định nghĩa dịch vụ certbot của ta . Thay vì sử dụng lệnh certonly , ta đang sử dụng lệnh con renew ở đây, lệnh này sẽ gia hạn các certificate sắp hết hạn. Ta đã bao gồm tùy chọn --dry-run ở đây để kiểm tra tập lệnh của ta .
  • docker-compose kill : Thao tác này sẽ gửi SIGHUP hiệu SIGHUP tới containers webserver để reload cấu hình Nginx. Để biết thêm thông tin về cách sử dụng quy trình này để reload cấu hình Nginx của bạn, vui lòng xem bài đăng trên blog Docker này về việc triển khai hình ảnh Nginx chính thức với Docker .

Sau đó, nó chạy sơ lược docker system prune để loại bỏ tất cả các containers và hình ảnh không sử dụng.

Đóng file khi bạn hoàn tất chỉnh sửa. Làm cho nó có thể thực thi:

  • chmod +x ssl_renew.sh

Tiếp theo, mở file crontab gốc của bạn để chạy tập lệnh gia hạn tại một khoảng thời gian cụ thể:

  • sudo crontab -e

Nếu đây là lần đầu tiên bạn chỉnh sửa file này, bạn cần chọn một editor :

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 

Điều này sẽ đặt khoảng thời gian công việc thành năm phút một lần, vì vậy bạn có thể kiểm tra xem yêu cầu gia hạn của bạn có hoạt động như dự định hay không. Ta cũng đã tạo một file log , cron.log , để ghi lại kết quả có liên quan từ công việc.

Sau năm phút, hãy kiểm tra cron.log để xem liệu yêu cầu gia hạn đã thành công hay chưa:

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

Bạn sẽ thấy kết quả xác nhận gia hạn 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/example.com/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Đến đây bạn có thể sửa đổi file crontab để đặt khoảng thời gian hàng ngày. Ví dụ: để chạy tập lệnh hàng ngày vào buổi trưa, bạn sẽ sửa đổi dòng cuối cùng của file để trông giống như sau:

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 tập lệnh ssl_renew.sh của bạn :

~ / wordpress / 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 

Công việc cron của bạn sẽ đảm bảo các certificate Let's Encrypt của bạn không mất hiệu lực bằng cách gia hạn chúng khi chúng đủ điều kiện. Bạn cũng có thể cài đặt xoay log với tiện ích Logrotate để xoay và nén các file log của bạn .

Kết luận

Trong hướng dẫn này, bạn đã sử dụng Docker Compose để tạo cài đặt WordPress với web server Nginx. Là một phần của quy trình làm việc này, bạn đã nhận được certificate TLS / SSL cho domain bạn muốn liên kết với trang web WordPress của bạn . Ngoài ra, bạn đã tạo một cron việc cron để gia hạn các certificate này khi cần thiết.

Là các bước bổ sung để cải thiện hiệu suất và dự phòng của trang web, bạn có thể tham khảo các bài viết sau về phân phối và backup nội dung WordPress:

Nếu bạn quan tâm đến việc khám phá quy trình làm việc được tích hợp với Kubernetes, bạn cũng có thể xem:


Tags:

Các tin liên quan

Cách di chuyển Docker compose workflow sang Kubernetes
2019-04-03
Cách tối ưu hóa image Docker cho sản xuất
2019-03-25
Giữ lại một ứng dụng Node.js để phát triển với Docker Compose
2019-03-05
Cách cài đặt và sử dụng Docker Compose trên CentOS 7
2019-01-23
Cách sử dụng Traefik làm reverse-proxy cho container Docker trên Debian 9
2019-01-08
Cách thiết lập registry Docker riêng trên Ubuntu 18.04
2019-01-07
Cách thiết lập triển khai nhiều node với Rancher 2.1, Kubernetes và Docker Machine trên Ubuntu 18.04
2019-01-03
Cách tạo ứng dụng Node.js với Docker
2018-11-29
Cách quản lý triển khai nhiều node với Máy Rancher và Docker trên Ubuntu 16.04
2018-10-30
Cách cài đặt và sử dụng Docker trên Ubuntu 16.04
2018-10-19