簡介
WordPress 是目前最受歡迎的內容管理系統 (CMS) 之一。根據統計,它驅動了全球超過 39% 的網站。由於其透過外掛程式實現的擴充性以及靈活的模板系統,它成為了一個熱門的選擇。它讓您能在幾秒鐘內改變網站外觀。此外,其管理可以透過網頁介面進行,不需要太多的技術知識。
此外,WordPress 是免費且開源的,並建立在 MySQL 資料庫並使用 PHP 進行處理。您可以 在 LAMP 架構(Linux、Apache、MySQL 和 PHP)上部署 WordPress 或 LEMP 架構(Linux、Nginx、MySQL 和 PHP)。然而,每次想要部署時都要重新設定這些架構會非常耗時。
幸運的是,現代軟體交付方法,例如 雲端運算, Docker、以及 Docker Compose 讓整體的開發者體驗變得更加順暢。這些工具透過避免每次部署應用程式時安裝和設定個別元件的額外開銷,簡化了建立任何架構的過程。相反地,您只需編寫設定檔,用於拉取並建立映像檔,並在 Docker 容器中執行它們,讓您只需一個指令就能部署應用程式。
容器是輕量級、虛擬化、可移植且由軟體定義的標準化環境,允許軟體與實體主機上執行的其他軟體隔離執行。Docker Compose 允許您管理多個容器並確保它們能夠互相通訊。例如,應用程式原始碼和資料庫必須進行通訊。
在本教學中,我們將 建立一個多容器化的 WordPress 應用程式。一個完整的 WordPress 應用程式需要三個容器:MySQL 資料庫、Nginx 伺服器和 WordPress 原始碼。 由於安全性是現代網站的首要任務,我們將從 Let’s Encrypt 取得 SSL 憑證以保護您的安裝。接著,我們將設定一個 cron 工作排程,定期檢查並更新憑證,以持續維護您網站的安全。
先決條件
- 由於這是一個動手實作的教學,您應該安裝 Ubuntu 20.04 作為初始作業環境。您還需要有一個 具有 sudo 權限的非 root 使用者。這裡有一個 逐步教學,協助您設定 Ubuntu 伺服器.
- 。您還需要安裝 Docker。您可以參考這篇關於 如何在 Ubuntu 18.04 上安裝和操作 Docker.
- 的教學。安裝 Docker Compose。您可以按照 教學 《如何在 Ubuntu 20.04 上安裝與設定 Docker Compose》.
- 的步驟 1 進行。需要一個已註冊的網域名稱才能從 Let’s Encrypt 取得 TLS/SSL 憑證。在本教學中,我們將使用
example.com. - 設定 DNS 記錄以將流量導向您的 VPS。您需要兩筆 DNS 記錄:
- 一筆 A 記錄,將
example.com指向您伺服器的公用 IP 位址。 - 一筆 A 記錄,將
www.example.com指向您伺服器的公用 IP 位址。
- 一筆 A 記錄,將
步驟 1:定義網頁伺服器的設定
網頁伺服器存放您的網站檔案,並讓使用者存取您的網頁應用程式。因此,在第一步中定義網頁伺服器的設定是最適當的。我們將定義一個 Nginx 伺服器設定 檔案,其中將包含 WordPress 特定的 location 區塊。我們還將包含 location 區塊,以將 Let’s Encrypt 驗證請求導向 Certbot 用戶端,以便自動更新憑證。
首先,為專案建立一個目錄。您可以選擇您喜歡的目錄名稱。在本教學中,我們將使用 wordpress_docker。輸入以下指令以建立目錄並進入該目錄:
|
1 |
mkdir wordpress_docker && cd wordpress_docker |
接下來,使用以下指令建立一個目錄來存放 Nginx 設定檔:
|
1 |
mkdir nginx-conf |
使用 nano 透過以下指令開啟檔案:
|
1 |
nano nginx-conf/nginx.conf |
在此檔案中,我們將為 Nginx 伺服器區塊(server block)設定定義基本指令。這些包括伺服器名稱、文件根目錄以及用於將 Certbot 外掛程式的憑證請求、靜態檔案和 PHP 處理進行導向的 location 區塊。您可以閱讀我們的教學課程:如何使用 Let’s Encrypt 保護 Nginx 以了解更多資訊。將以下程式碼新增至檔案中,並將 example.com 替換為您註冊的網域名稱:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
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 app: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; } } |
讓我們來定義您新增的區段:
-
Directives:
listen:它告訴 Nginx 監聽連接埠80。這允許使用 Certbot 的 webroot 外掛程式來建立憑證請求。一旦我們取得 SSL 憑證,我們將更新此設定以使用連接埠443.server_name:這定義了 網域 名稱,此設定應處理該名稱。導向至此處定義之網域名稱的流量將會被引導至這個特定的伺服器區塊,進而導向至文件root.root:它定義了對上述網域名稱請求的根目錄。它通常是存放我們實際網站檔案的目錄。我們已將該目錄設定為/var/www/html。它將被建立為一個 Docker 掛載點 在容器建置期間。我們將在以下檔案中定義此程序的指令:WordPress Dockerfile.index:這定義了在處理請求時,將用作索引或網頁伺服器進入點的檔案。我們已將 index.php 移至 index.html 之前,以便 Nginx 優先處理index.php.
-
Location 區塊:
location ~ /.well-known/acme-challenge:處理對 well-known 目錄的請求,Certbot 會在該目錄中新增一個暫存檔案,以驗證指定的 DNS 是否指向我們申請 SSL 憑證的特定伺服器。這就是為什麼您應該在此步驟中加入有效的網域,而不是我們在本教學中使用的example.com。location /:接收 URI 請求並將控制權交給 WordPressindex.php以請求參數進行處理。location ~ \.php$:處理 PHP 處理並將請求傳遞給 WordPress 容器(我們將在後續步驟中為此定義設定檔)。我們在此處定義了專門針對 FastCGI 協定的設定,因為 WordPress Docker 映像檔將基於 php:fpm 映像檔。Nginx 使用獨立的 PHP 處理器來處理 PHP 特定的請求。我們將使用隨附於php-fpm處理器,該處理器隨附於php:fpmDocker 映像檔。location ~ /\.ht:處理.htaccess檔案(Nginx 不使用這些檔案)。deny all指令可確保這些檔案永遠不會提供給網站訪客。location = /favicon.ico, location = /robots.txt:如定義所示,這可以防止記錄對/favicon.ico和/robots.txt檔案的請求。location ~* \.(css|gif|ico|jpeg|jpg|js|png)$:關閉靜態檔案請求的記錄,並確保它們被快取以減輕伺服器的負載。
您現在可以儲存並關閉檔案,方法是按下 CTRL+X, Y,然後按下 ENTER。這樣就完成了第一步。
步驟 2:定義環境變數
環境變數對於促進 WordPress 應用程式與資料庫之間的通訊是必要的。它們還能確保應用程式資料得以持久化。環境變數包括敏感資訊(例如資料庫憑證)和非敏感資訊(例如資料庫名稱和主機)。
出於安全考量,不將敏感資訊新增至專案儲存庫中始終是一個好主意。因此,我們不會在 Docker Compose 檔案中設定敏感值,而是會在 .env 檔案中定義 MySQL 憑證,該檔案不會被提交到專案儲存庫,從而避免公開曝光的風險。在專案 根目錄 ~/wordpress_docker 中,開啟 .env 檔案:
|
1 |
nano .env |
|
1 2 3 |
MYSQL_ROOT_PASSWORD=your_strong_root_password MYSQL_USER=your_wordpress_database_user MYSQL_PASSWORD=strong_wordpress_database_password |
接下來您必須做的是將 .env 檔案新增至 .gitignore 和 .dockerignore 檔案中,以確保它分別不會被新增到您的儲存庫或 Docker 映像檔中。
這在本教學中不是必需的,但如果您想使用 Git 進行版本控制,請輸入以下指令將目前目錄初始化為 git 儲存庫:
|
1 |
git init |
開啟 .gitignore (使用 nano):
|
1 |
nano .gitignore |
新增以下行:
|
1 |
.env |
儲存並關閉檔案。接下來,開啟 .dockerignore (使用 nano):
|
1 |
nano .dockerignore |
新增以下行:
|
1 |
.env |
在此同時,您也可以選擇性地新增與應用程式開發相關的其他檔案和目錄:
|
1 2 3 |
.env .git docker-compose.yml |
完成後儲存並關閉檔案。此步驟到此結束。讓我們繼續進行 Docker Compose 定義。
步驟 3:使用 Docker Compose 設定服務
Docker Compose 使用一個 docker-compose.yml 檔案來建置映像檔。此檔案包含用於完整設定應用程式的服務定義。服務定義基本上是容器如何運行的說明。服務是實際運行的容器。
Docker Compose 使得透過將各種服務與共享網路和磁碟卷連結在一起,為多容器應用程式定義不同的服務成為可能。您將在實際操作中看到這一點,因為我們將為我們的應用程式定義三個容器:網頁伺服器、WordPress 安裝和資料庫。我們將添加第四個容器來運行用於憑證更新的 Certbot 用戶端。
輸入以下指令以建立 docker-compose.yml 檔案:
|
1 |
nano docker-compose.yml |
在 docker-compose.yml 檔案中的第一行是 version definition行。我們將我們的設定為 3。然後,您可以開始定義您的服務。在檔案中加入以下程式碼片段以定義 db 服務:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
version: '3' services: #MySQL 服務 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 |
讓我們討論一下我們在 db 服務定義中的內容:
image: 決定容器將基於哪個映像檔。指定特定版本(mysql:8.0)總是比使用最新標籤(mysql:latest)更好,因為未來版本的 MySQL 映像檔 如果我們碰巧重新建置此映像檔,可能會與我們的應用程式發生衝突。您可以找到更多關於 官方 Dockerfile 文件上的 Dockerfile 最佳實踐的資訊.container_name: 我們在此處指定容器名稱。restart: 此指令決定容器的重啟行為。預設值為no,但我們已將其設定為一律重啟,除非手動停止。env_file: 此指令用於指定包含應用程式所使用環境變數的檔案位置(.env)。environment: 用於指定額外的環境變數。在本教學中,我們指定了MYSQL_DATABASE變數來保存我們應用程式的資料庫名稱。資料庫名稱可以包含在docker-compose.yml.volumes: 用於指定掛載位置。在我們的範例中,我們掛載了一個具名的 磁碟卷(稱為 dbdata)到容器的/var/lib/mysql目錄,這通常是 MySQL 的標準資料目錄。command: 此指令指定一個將覆蓋映像檔預設 CMD 指令 的指令。我們在 Docker 映像檔的標準mysqld指令中添加了一個選項,該指令用於在容器內啟動 MySQL 伺服器。我們添加的選項是--default-authentication-plugin=mysql_native_password,這會將 MySQL 的預設驗證外掛程式更新為使用密碼驗證(mysql_native_password)。這對於您的 PHP(WordPress 應用程式運作)是必要的,因為它們使用使用者名稱和密碼來存取資料庫。在較新的 MySQL 版本中,預設驗證外掛程式已變更。然而,大多數應用程式使用密碼驗證。因此,您必須變更此設定才能使應用程式正常運作。networks: 此指令用於指定db服務應該加入app-network,我們將在教學進行過程中定義它。
接下來,讓我們為 WordPress 應用程式定義服務設定。我們將服務和 container_name 命名為 app。在 db 服務定義下方新增以下程式碼片段,並注意正確的縮排:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#WordPress 應用程式程式碼服務 app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app 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: - app:/var/www/html networks: - app-network |
就像我們對 db 服務所做的一樣,我們已經命名了容器並定義了重啟策略。我們新增的其他一些選項定義如下:
depends_on:此指令可確保容器依依賴關係的順序啟動。在我們的案例中,app容器依賴於db容器。因此,它將在db容器啟動後啟動。必須按此順序進行,因為 WordPress 應用程式需要 MySQL 資料庫可用才能運作。image:如程式碼片段所示,我們將使用 WordPress 版本 5.1.1 fpm alpine 映像檔。我們之前已經解釋過 Nginx 進行 PHP 處理所需的php-fpm處理器。此映像檔會處理該部分。基於 Alpine Linux 專案 的 alpine 映像檔有助於保持較小的映像檔大小。如果您需要有關映像檔變體的更多資訊,可以點擊此連結以取得 Docker Hub WordPress 映像檔.env_file:指定包含資料庫憑證的.env檔案的位置。environment:此指令定義了額外的環境變數。在我們的案例中,我們定義了 WordPress 所需的變數,並將我們.env檔案中的變數值分配給它們。這些是WORDPRESS_DB_USER,WORDPRESS_DB_PASSWORD、以及WORDPRESS_DB_HOST,它指向在db容器上執行的 MySQL 伺服器,可透過 MySQL 的預設連接埠3306存取。最後,您會看到WORDPRESS_DB_NAME,我們已將其設定為 wordpress。在 db 容器的 MySQL 服務定義中也指定了相同的值:MYSQL_DATABASE=wordpress.volumes:此指令將名為 app 的資料卷掛載到/var/www/html掛載點,該掛載點是由 WordPress 映像檔 建立的。命名資料卷允許與其他容器共享應用程式程式碼。networks:最後,我們將 app 容器新增到app-network,以確保它與網路上的其他容器進行通訊。
以上就是 WordPress 映像檔的 app 服務容器的全部內容。現在讓我們為 Nginx 映像檔定義 webserver 服務。首先,在您的 docker-compose.yml 檔案中,於 app 服務定義下方新增以下程式碼片段:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#Webserver Nginx 服務 webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network |
我們已經解釋過 depends_on 選項。在此 webserver 服務中,容器將在 app 容器啟動後啟動。網頁伺服器容器是基於 alpine Nginx 映像檔。它具有與先前服務定義類似的重啟策略。webserver 服務定義中的其他選項包括:
ports:綁定主機與容器之間的連接埠。在 步驟 1 中,我們定義了連接埠80於nginx.conf檔案中。此連接埠被對應到容器上的連接埠80。volumes:在此選項下,我們結合了 綁定掛載 與具名磁碟區:app:/var/www/html:此磁碟區定義將 WordPress 應用程式掛載到/var/www/html目錄,該目錄是我們稍早在 Nginx 伺服器區塊中設定為根目錄的。./nginx-conf:/etc/nginx/conf.d:此定義將主機上的 Nginx 設定目錄綁定掛載到我們為容器定義的 Nginx 設定目錄。因此,主機上的任何變更都會自動反映在容器中。certbot-etc:/etc/letsencrypt:此定義將網域的 Let’s Encrypt 憑證和金鑰掛載到容器上的相應目錄。
networks:與之前的服務定義一樣,networks指令將 webserver 服務新增至app-networks.
既然我們已經完成了 webserver 的定義,接下來讓我們為 Certbot 服務新增指令。這將處理從 Let’s Encrypt 獲取您的 TLS/SSL 憑證。如果您想了解更多關於保護 Nginx 伺服器的資訊,這篇關於如何使用 Let’s Encrypt 保護 Nginx 的教學是一個很好的資源。
接下來,在 webserver 服務下方新增以下程式碼片段。請記得設定正確的網域名稱和電子郵件地址:
|
1 2 3 4 5 6 7 8 9 10 |
#certbot 服務 certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com |
由於 certbot 映像檔只會在 webserver 啟動後才會啟動,這是因為有 depends_on 指令。Docker Compose 將會按照定義拉取 來自 Docker Hub 的 Certbot 映像檔。
在 volumes 定義下,Certbot 容器將會共享 certbot-etc 中的網域憑證和金鑰給 Nginx webserver 容器,並將應用程式程式碼共享給 app 容器。
在 command 定義下,我們指定了一個子指令來執行容器預設的 Certbot certonly 指令,並附帶如下所示的額外選項:
-
--webroot:指定使用 webroot 外掛程式,該外掛程式會將檔案放置在 webroot 資料夾中以進行驗證。--webroot-path:指定 webroot 目錄的路徑。--agree-tos:指定您同意 ACME 的服務條款.--no-eff-email:指定您不想與 EFF 分享您的電子郵件。如果您想分享,可以省略此項。--staging:告訴 Certbot 您希望先從 Let’s Encrypt 的預備(staging)環境獲取測試憑證,以便在獲取實際憑證之前測試您的設定。Let’s Encrypt 設有網域請求速率限制。因此,先測試您的設定將有助於避免您的網域受到限制。-d:此選項接受憑證請求的網域名稱。在本教學中,我們已包含example.com和www.example.com。請指定您實際註冊的網域。
我們的 docker-compose.yml 檔案已接近完成。然而,您還必須在 Certbot 服務下方新增網路和磁碟區定義:
|
1 2 3 4 5 6 7 8 9 10 |
#Volumes volumes: certbot-etc: app: dbdata: #Networks networks: app-network: driver: bridge |
這個 volumes 鍵定義了要與此 compose 檔案中定義的所有服務(容器)共享的磁碟區:certbot-etc, app、以及 dbdata。Docker 建立的磁碟區內容儲存在主機檔案系統上由 Docker 管理的目錄中:/var/lib/docker/volumes/。然後,每個資料卷的內容會被掛載到任何使用該資料卷的容器。這使得在容器之間共享資料和程式碼成為可能。
該 networks 鍵定義了允許容器之間進行通訊的橋接網路。位於同一個橋接網路上的容器(例如 webserver 和 db)可以安全地透過連接埠進行通訊,而不會將流量暴露給外部網路。我們只公開連接埠 80 以允許存取前端網站頁面。
完整的 docker-compose.yml 檔案將如下所示:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
version: '3' services: #MySQL 服務 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 應用程式碼服務 app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app 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: - app:/var/www/html networks: - app-network #Webserver Nginx 服務 webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network #certbot 服務 certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --staging -d example.com -d www.example.com #資料卷 volumes: certbot-etc: app: dbdata: #網路 networks: app-network: driver: bridge |
您可以儲存並關閉檔案。在下一步中,我們將開始並測試容器和憑證請求。
步驟 4:執行容器並取得 SSL 憑證
Docker Compose 的最大優勢在於,一旦您在 docker-compose.yml 檔案,您只需一個指令即可啟動所有容器:docker-compose up。該指令會執行指定的每個說明。如果網域請求成功,您應該能在終端機中看到正確的結束狀態。輸入以下指令以建立容器。該 -d 旗標用於在背景執行容器:
|
1 |
docker-compose up -d |
如果您看到如下圖螢幕截圖所示的輸出,則表示服務已成功建立:
若要確認服務的狀態,請執行 docker-compose ps 指令:
|
1 |
docker-compose ps |
如果一切順利,該指令的輸出如下所示。 app, db 以及 webserver 容器的狀態應該為 up,而 certbot 容器應該具有 Exit0 狀態:
如果您看到除了 Up 以外的內容在狀態欄中,針對 app, db 或 webserver,,或是 Exit 狀態不是 0 ,針對 certbot 容器,那麼就代表出錯了。您可以使用以下指令檢查每個容器的記錄:docker-compose logs,並指定 service_name:
|
1 |
docker-compose logs service_name |
例如,您可以輸入以下指令來檢查 certbot 容器的記錄:
|
1 |
docker-compose logs certbot |
若要檢查憑證是否已掛載到 webserver 容器,請使用 docker-compose exec 指令:
|
1 |
docker-compose exec webserver ls -la /etc/letsencrypt/live |
如果您使用了實際註冊的網域名稱,而不是 example.com 且憑證請求成功,您應該會看到類似以下的輸出:
確認憑證請求成功後,您可以編輯 docker-compose.yml 檔案並移除 --staging 旗標。使用 nano:
|
1 |
nano docker-compose.yml |
向下捲動到 Certbot 服務定義區段,在 command 選項中將 --staging 旗標替換為 --force-renewal 旗標。這會告訴 Certbot 您正在為相同網域的憑證請求憑證更新。您的 Certbot 服務定義現在應該如下所示:
|
1 2 3 4 5 6 7 8 9 10 |
#certbot service certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com |
編輯完成後儲存檔案。
輸入以下指令以重新建立 certbot 容器。其中包含的 --no-deps 旗標會告訴 Compose 跳過重新啟動網頁伺服器服務,因為它已經在執行中:
|
1 |
docker-compose up --force-recreate --no-deps Certbot |
該指令會輸出以下螢幕截圖,顯示憑證請求已成功:
此步驟到此結束。在下一步中,您將修改 Nginx 設定檔以包含 SSL 憑證。
步驟 5:在 Nginx 設定和服務定義中啟用 SSL
為了讓 Nginx 透過安全的 SSL 提供流量服務,您首先需要修改 Nginx 設定檔以新增 HTTP 重新導向至 HTTPS。接著,您需要指定憑證和金鑰的位置,最後新增安全性參數和標頭。
在修改設定檔之前,您應該使用 推薦的 Nginx 安全性參數,從 Certbot 的 GitHub 儲存庫中透過 curl 執行以下指令來取得:
|
1 |
curl -sSLo nginx-conf/options-ssl-nginx.conf |
該命令會執行並將其提取的參數儲存到名為 options-ssl-nginx.conf 的檔案中,該檔案位於 nginx-conf 目錄內。刪除 Nginx 設定檔,以便我們可以使用以下命令建立一個新檔案:
|
1 2 |
rm nginx-conf/nginx.conf nano nginx-conf/nginx.conf |
在目前空白的 nginx.conf 檔案中,新增以下程式碼,其中包含從 HTTP 重新導向至 HTTPS、SSL 憑證協定和安全性標頭。正如您之前所做的那樣,請將 example.com 網域替換為您自己註冊的網域:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
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-安全性-Policy "default-src * data: 'unsafe-eval' 'unsafe-inline'" always; # add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # 僅在您了解其影響時才啟用 strict transport security location / { try_files $uri $uri/ /index.php$is_args$args; } location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app: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; } } |
在第一個處理使用連接埠 80 的未安全請求的 server 區塊中,我們指定了 Certbot 更新請求的 webroot。我們還包含了一個 重定向指令,它會將 HTTP 請求重定向至 HTTPS.
第二個 server 區塊處理安全 HTTPS 流量,該流量來自連接埠 443。如您所見,我們還啟用了 SSL 和 HTTP2。HTTP/2 提高了伺服器的效能。您可以從 官方 Nginx 關於 HTTP/2 的文件.
中閱讀更多相關資訊。在此區塊中,我們還指定了 Nginx 包含 SSL 憑證和金鑰位置,以及推薦的 Certbot 安全參數,這些參數是 curl 儲存到 nginx-conf/options-ssl-nginx.conf 目錄。
額外的安全標頭有助於提高您的網站在安全測試網站上的評級,例如 Security Headers 和 SSL Labs。您可以點擊這些標頭的連結以了解更多資訊:X-Frame-Options, Referrer Policy, X-Content-Type-Options, X-XSS-Protection, Content-Security-Policy。我們已經註解掉了 HTTP Strict Transport Security (HSTS) 標頭。您可以自由閱讀有關其 預載功能 的說明,並決定是否要啟用它。
其餘的指令,例如 root, index, 以及 WordPress 特定的 location 區塊,仍與 步驟 1 中討論的一樣。編輯完成後,您現在可以儲存並關閉檔案。
現在我們已經啟用了 HTTPS 流量,該流量使用連接埠 443,,我們還必須在網頁伺服器的服務定義中啟用該連接埠。輸入以下命令以開啟 docker-compose.yml 檔案,使用 nano:
|
1 |
nano docker-compose.yml |
在 web server 區段的 ports 選項下,新增連接埠 443 的對應,如下面醒目提示所示:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network |
完整的 docker-compose.yml 檔案現在應該如下所示:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
version: '3' services: #MySQL 服務 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 應用程式程式碼服務 app: depends_on: - db image: wordpress:5.1.1-fpm-alpine container_name: app 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: - app:/var/www/html networks: - app-network #Webserver Nginx 服務 webserver: depends_on: - app image: nginx:1.15.12-alpine container_name: webserver restart: unless-stopped ports: - "80:80" - "443:443" volumes: - app:/var/www/html - ./nginx-conf:/etc/nginx/conf.d - certbot-etc:/etc/letsencrypt networks: - app-network #certbot 服務 certbot: depends_on: - webserver image: certbot/certbot container_name: certbot volumes: - certbot-etc:/etc/letsencrypt - app:/var/www/html command: certonly --webroot --webroot-path=/var/www/html --email hackins@cloudsigma.com --agree-tos --no-eff-email --force-renewal -d example.com -d www.example.com #磁碟卷 volumes: certbot-etc: app: dbdata: #網路 networks: app-network: driver: bridge |
確認一切無誤後,儲存並關閉檔案。之後,執行以下命令以重新建立 webserver 服務:
|
1 |
docker-compose up -d --force-recreate --no-deps webserver |
|
1 |
docker-compose ps |
現在所有容器都已運行,您可以從網頁介面繼續進行 WordPress 設定。
步驟 6:從網頁介面完成您的 WordPress 設定
導訪至您伺服器的網域名稱以繼續安裝。您應該會看到 WordPress 設定首頁。它會歡迎您並在繼續之前選擇您的語言:
選擇您的語言並點擊 繼續 以進入下一頁:
在此頁面上,填寫您的網站標題,選擇一個好記的使用者名稱和強密碼。出於安全原因,建議不要使用 Admin 作為您的使用者名稱。輸入您的電子郵件並點擊 安裝 WordPress 按鈕以開始安裝 WordPress。
安裝完成後,您將被引導至登入畫面,您需要在該畫面中提供您設定的使用者名稱和密碼。輸入有效的憑證後,您應該就能看到您的 WordPress 控制台:
您現在已成功安裝 WordPress!接下來,您需要採取步驟以確保 SSL 憑證會自動續期。
步驟 7:設定自動 SSL 憑證續期
Let’s Encrypt TLS/SSL 憑證的有效期僅為 90 天。您需要建立自動續期設定以確保它們不會過期。您可以透過建立指令碼並使用 cron 工作 公用程式進行排程來實現此目的。在此步驟中,我們將向您展示如何建立一個用於續期憑證的指令碼。然後,我們將使用 cron 工作公用程式對其進行排程,以便定期執行它並在憑證即將到期時進行續期。
在 wordpress_docker 專案目錄中,開啟一個名為 ssl_renewer.sh 的指令碼,使用 nano:
|
1 |
nano ssl_renewer.sh |
將以下程式碼新增至指令碼中以處理自動續期和 Nginx 設定重新載入。請記得將醒目提示的使用者名稱替換為您的非 root 使用者名稱:
|
1 2 3 4 5 6 7 8 |
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose –ansi never" DOCKER="/usr/bin/docker" cd /home/hackins/wordpress_docker/ $COMPOSE run certbot renew --dry-run && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af |
在此指令碼中,我們將 docker-compose 二進位檔案分配給名為 COMPOSE 的變數。我們還包含了 –ansi never 選項,該選項指示指令碼在執行 docker-compose 指令時不使用 ANSI 控制 字元。我們進一步將 Docker 二進位檔案分配給名為 DOCKER.
的變數。接著,指令碼會進入我們的專案目錄 wordpress_docker 並執行以下指令:
docker-compose run:它會啟動 certbot 容器並覆寫我們在 certbot 服務定義中提供的指令。它不會執行 certonly 子指令,而是執行 renew 子指令,如果 Let’s Encrypt 的 SSL/TLS 憑證即將到期,該子指令將對其進行續期。docker-compose kill:向 SIGHUP 訊號傳送至webserver容器以重新載入 Nginx 設定。您可能想查看 Docker 關於 如何使用官方 Nginx Docker 映像檔的教學課程.docker system prune:此指令會移除所有未使用的容器和映像檔。
編輯完成後儲存並關閉檔案。然後,執行以下指令使其成為可執行檔案:
|
1 |
chmod +x ssl_renewer.sh |
將其設為可執行檔案後,開啟您的 root crontab 檔案,以便在我們指定的間隔時間定期執行指令碼:
|
1 |
sudo crontab -e |
The crontab 如果是您第一次使用,會要求您選擇偏好的編輯器:
選擇您偏好的編輯器並按下 Enter 以開啟檔案。在檔案底部新增以下行:
|
1 |
*/5 * * * * /home/hackins/wordpress_docker/ssl_renewer.sh >> /var/log/cron_docker.log 2>&1 |
這會將間隔設定為五分鐘,以便我們測試續期指令碼是否正常運作。我們還指定了一個記錄檔來儲存該工作的輸出:cron_docker.log.
請等待五分鐘並檢查 cron.log,以查看腳本是否成功執行續期請求:
|
1 |
tail -f /var/log/cron_docker.log |
如果請求成功,您應該會看到類似下方螢幕截圖的內容:
既然我們已經測試並確認其正常運作,您可以修改 crontab 檔案以指定每日續訂。例如,您可能希望指定腳本在每天下午 6 點執行。為此,請修改 crontab 的最後一行,使其看起來像這樣:
|
1 |
0 18 * * * /home/hackins/wordpress_docker/ssl_renewer.sh >> /var/log/cron_docker.log 2>&1 |
此外,您需要將 –dry-run 旗標從 ssl_renewer.sh 腳本中移除,以確保在執行時進行實際的續訂。它應該看起來像這樣:
|
1 2 3 4 5 6 7 8 |
#!/bin/bash COMPOSE="/usr/local/bin/docker-compose --ansi never" DOCKER="/usr/bin/docker" cd /home/hackins/wordpress_docker/ $COMPOSE run certbot renew && $COMPOSE kill -s SIGHUP webserver $DOCKER system prune -af |
接下來,儲存並關閉檔案。完成後,cron 工作將在 90 天到期前自動續訂,以保持您的憑證有效。
結論
如果您已經閱讀到本教學的此處,您可以認為自己距離成為 DevOps 工程師又近了一步。您成功建立了一個 Nginx 設定腳本、建立了一個 docker-compose.yml 檔案,並定義了使用 Docker 和 Docker Compose 執行 WordPress 應用程式所需的數個服務。您從 Let’s Encrypt 取得了 SSL/TLS 憑證,以確保您的網頁伺服器安全。最後,您建立了一個 cron 工作以確保憑證不會過期。做得好!
如果您想更深入了解 DevOps,請查看更多關於容器的資源,來自 我們的部落格:
- 認識 Kubernetes
- 如何在 Ubuntu 20.04 上使用 Docker 部署 Node.js (Express.js) 應用程式
- 在具有 Ubuntu 18.04 的 Kubernetes 叢集上部署 PHP 應用程式。
- 使用 Docker Compose 部署 Laravel、Nginx 和 MySQL
祝您運算愉快!










留言
目前尚無留言。成為第一個留言的人吧。