bloghost),透過啟動腳本自動安裝 Apache 與 PHP 環境,並開啟 HTTP (80) 防火牆。allUsers:R)。blog-db),建立專用使用者,並設定授權網路允許 VM 的 External IP 存取。index.php,整合 GCS 圖片與 Cloud SQL 資料庫連線,驗證網頁正常顯示與連線成功。開啟 Cloud Shell 後,預先定義本次實驗分配到的區域與地理位置:
# 根據實驗手冊提示設定對應的 Region 與 Zone
export REGION=us-east4
export ZONE=us-east4-b
export LOCATION=US
透過 startup-script 在開機時自動安裝 Apache2 與 PHP MySQL 模組,並加上 http-server 網路標籤:
# 1. 建立 VM Instance
gcloud compute instances create bloghost \
--project=$DEVSHELL_PROJECT_ID \
--zone=$ZONE \
--machine-type=e2-standard-2 \
--image-family=debian-12 \
--image-project=debian-cloud \
--tags=http-server \
--metadata=startup-script='#!/bin/bash
apt-get install apache2 php php-mysql -y
service apache2 restart'
# 2. 建立防火牆規則允許 HTTP (Port 80)
gcloud compute firewall-rules create default-allow-http \
--project=$DEVSHELL_PROJECT_ID \
--direction=INGRESS \
--priority=1000 \
--network=default \
--action=ALLOW \
--rules=tcp:80 \
--target-tags=http-server
# 3. 取得並暫存 VM 的外部公用 IP
VM_EXT_IP=$(gcloud compute instances describe bloghost \
--zone=$ZONE \
--format='get(networkInterfaces[0].accessConfigs[0].natIP)')
echo "VM External IP: $VM_EXT_IP"
Bucket 名稱需全球唯一,本實驗使用預設環境變數 $DEVSHELL_PROJECT_ID 作為名稱:
# 1. 建立 Storage Bucket
gcloud storage buckets create -l $LOCATION gs://$DEVSHELL_PROJECT_ID
# 2. 下載範例圖片至 Cloud Shell 本地
gcloud storage cp gs://cloud-training/gcpfci/my-excellent-blog.png my-excellent-blog.png
# 3. 上傳圖片至建立的 Bucket
gcloud storage cp my-excellent-blog.png gs://$DEVSHELL_PROJECT_ID/my-excellent-blog.png
# 4. 修改物件 ACL,將該圖片設為公開讀取
gsutil acl ch -u allUsers:R gs://$DEVSHELL_PROJECT_ID/my-excellent-blog.png
建立 MySQL 8.0 實例,設定專用帳號,並將 VM 的對外 IP 加入白名單:
# 1. 建立 Cloud SQL Instance(注意:僅指定 --zone,勿同時指定 --region)
gcloud sql instances create blog-db \
--project=$DEVSHELL_PROJECT_ID \
--database-version=MYSQL_8_0 \
--zone=$ZONE \
--tier=db-custom-2-7680 \
--root-password="Passw0rd1!"
# 2. 建立資料庫使用者 blogdbuser
gcloud sql users create blogdbuser \
--instance=blog-db \
--password="Passw0rd1!"
# 3. 授權 VM 的外部 IP (以 /32 CIDR 格式) 連線至 Cloud SQL
gcloud sql instances patch blog-db \
--authorized-networks="${VM_EXT_IP}/32"
# 4. 取得並暫存 Cloud SQL 的公用 IP
SQL_IP=$(gcloud sql instances describe blog-db \
--format='get(ipAddresses[0].ipAddress)')
echo "Cloud SQL IP: $SQL_IP"
在 Cloud Shell 本地透過 Here-Doc 生成網頁程式碼,透過 sed 替換動態 IP 與專案 ID,再使用 scp 與 ssh 佈署到 VM:
# 1. 在本地建立包含 GCS 圖片與 PDO 資料庫連線的 index.php 範本
cat <<'EOF' > index.php
<html>
<head><title>Welcome to my excellent blog</title></head>
<body>
<img src="https://storage.googleapis.com/PROJECT_ID/my-excellent-blog.png">
<h1>Welcome to my excellent blog</h1>
<?php
$dbserver = "SQL_IP";
$dbuser = "blogdbuser";
$dbpassword = "Passw0rd1!";
try {
$conn = new PDO("mysql:host=$dbserver;dbname=mysql", $dbuser, $dbpassword);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Connected successfully";
} catch(PDOException $e) {
echo "Database connection failed:: " . $e->getMessage();
}
?>
</body></html>
EOF
# 2. 替換專案 ID 與 Cloud SQL IP 變數
sed -i "s/PROJECT_ID/$DEVSHELL_PROJECT_ID/g" index.php
sed -i "s/SQL_IP/$SQL_IP/g" index.php
# 3. 將檔案傳送至 bloghost VM 並放置到 /var/www/html/,最後重啟 Apache
gcloud compute scp index.php bloghost:/tmp/index.php --zone=$ZONE
gcloud compute ssh bloghost --zone=$ZONE --command="sudo mv /tmp/index.php /var/www/html/index.php && sudo systemctl restart apache2"
直接在 Cloud Shell 發送 HTTP 請求驗證整合狀態:
curl http://$VM_EXT_IP/index.php
預期輸出結果:
<html>
<head><title>Welcome to my excellent blog</title></head>
<body>
<img src="https://storage.googleapis.com/<YOUR_PROJECT_ID>/my-excellent-blog.png">
<h1>Welcome to my excellent blog</h1>
Connected successfully</body></html>
--region 與 --zone,CLI 會報錯:argument --region: At most one of --region | --gce-zone | --secondary-zone --zone can be specified.。--zone=$ZONE,系統會自動匹配對應 Region。! 的密碼(如 "Passw0rd1!")會觸發歷史事件解析,出現 -bash: !: event not found,導致密碼被清空(PHP 出現 using password: NO 錯誤)。cat <<'EOF')阻斷終端解析,或將檔案先寫在本地再用 scp 傳送至 VM。/32(例如 136.107.121.62/32),否則 API 會回報無效格式。