昨天透過 GitHub Actions 成功將我的 SpringBoot 模組容器化並部署到了 GCE,容器 run 是 run 了起來,但內部的 SpringBoot 啟動時出現 "Communications link failure" 錯誤,因為它無法連接到資料庫,所以今天主要目標是解決 SpringBoot 應用在 GCE 上的資料庫連接問題:
Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
Caused by: java.net.ConnectException: Connection refused
我決定在同一台 GCE 實例上部署 MySQL 容器來解決這個問題,避免額外的雲端資料庫成本,因為我看網路上好像沒有免費額度的 MySQL 雲服務,所以最終選擇在同一台 GCE 上運行 MySQL 容器,只求先快速解決問題,因為我想趕快看到我的專案順利運作起來,順便學習一下容器網路配置。
在 GCE Console 用 SSH 連進 VM 並啟動 MySQL 容器:
sudo docker run -d \
--name mysql-db \
-e MYSQL_ROOT_PASSWORD=Password123 \
-e MYSQL_DATABASE=XXX_BASE \
-p 3306:3306 \
-v mysql-data:/var/lib/mysql \
--restart unless-stopped \
mysql:8.0
即使 MySQL 容器正常運行,SpringBoot 應用仍然無法連接。網路測試:
docker exec playground ping mysql-db
# 結果:ping: bad address 'mysql-db'
docker exec 意思是連進容器裡面執行終端機指令的意思
兩個容器運行在不同的 Docker 網路中:
Docker 的預設行為是為每個容器創建獨立的網路命名空間,即使在同一主機上的容器也無法直接通過容器名稱互相訪問。
# 創建自定義網路
sudo docker network create playground-network
# 將容器連接到同一網路
sudo docker network connect playground-network mysql-db
sudo docker network connect playground-network playground
在啟動時指定網路
docker run --network playground-network ...
sudo docker run -d --name $SERVICE --restart unless-stopped -p 8080:8080 $IMAGE_URL
這個指令沒有指定 --network
參數的話,每次重部署容器會連接到預設網路。
將資料庫連接字串從:
url: jdbc:mysql://localhost:3306/CORE_BASE
改為:
url: jdbc:mysql://mysql-db:3306/CORE_BASE
即使網路連接成功,MySQL 8.0 還是認證錯誤:
Public Key Retrieval is not allowed
在連接字串中加入:
url: jdbc:mysql://mysql-db:3306/CORE_BASE?useSSL=false&serverTimezone=Asia/Taipei&allowPublicKeyRetrieval=true
在部署腳本考慮網路配置,避免每次部署後失去網路連接:
sudo docker run -d --name $SERVICE --network playground-network --restart unless-stopped -p 8080:8080 $IMAGE_URL
docker exec container ping target
docker logs -f container
和 docker logs --tail N container
docker network ls
和 docker network inspect network-name
docker ps -a
和 docker inspect container