Skip to content

Instantly share code, notes, and snippets.

@hunandy14
Last active August 14, 2025 13:54
Show Gist options
  • Save hunandy14/7dafceef67cb47db38620a3c2479792d to your computer and use it in GitHub Desktop.
Save hunandy14/7dafceef67cb47db38620a3c2479792d to your computer and use it in GitHub Desktop.
快速部署 nginx 容器網站
#!/usr/bin/env bash
# =======================================
# 🐳 run_nginx_site 函式
# =======================================
# 用途:快速部署 nginx 容器網站
#
# 🔧 支援參數(具名與位置皆可):
# --domain <名稱> ✅ 必填(位置參數1),預設為 mysite.com
# --port <數字> ➕ 可選(位置參數2),預設為 8080
# --site-dir <路徑> ➕ 可選,預設為 /srv/www/<domain>
# --container-name <名稱> ➕ 可選,預設為 www.<domain>
# --no-restart ➕ 可選,不加上 --restart=always
#
# 💡 使用範例:
# run_nginx_site
# run_nginx_site mysite.com
# run_nginx_site mysite.com 8081
# run_nginx_site --domain mysite.com --no-restart
# run_nginx_site --domain mysite.com --port 8081 --site-dir /srv/www/mysite.com/html --container-name www.mysite.com
# =======================================
run_nginx_site() {
local DOMAIN=""
local PORT=""
local SITE_DIR=""
local CONTAINER_NAME=""
local NO_RESTART=false
# 🔍 處理具名參數
while [[ $# -gt 0 ]]; do
case "$1" in
--domain)
DOMAIN="$2"
shift 2
;;
--port)
PORT="$2"
shift 2
;;
--site-dir)
SITE_DIR="$2"
shift 2
;;
--container-name)
CONTAINER_NAME="$2"
shift 2
;;
--no-restart)
NO_RESTART=true
shift
;;
*)
# 如果不是具名參數,當作位置參數處理
if [[ -z "$DOMAIN" ]]; then
DOMAIN="$1"
elif [[ -z "$PORT" ]]; then
PORT="$1"
else
echo "❌ 未知參數:$1"
return 1
fi
shift
;;
esac
done
# 🔍 檢查 Docker 是否已安裝
if ! command -v docker >/dev/null 2>&1; then
echo "❌ Docker 未安裝"
echo "💡 請執行以下命令安裝 Docker:"
echo " curl -fsSL get.docker.com | sudo sh && sudo usermod -aG docker $USER && newgrp docker"
echo "📝 說明:"
echo " - 安裝 Docker"
echo " - 將用戶加入 docker 群組"
echo " - 立即載入群組權限(無需重新登錄)"
return 1
fi
# 檢查 Docker 服務是否運行
if ! docker info >/dev/null 2>&1; then
echo "❌ Docker 服務未運行"
echo "💡 請執行以下命令啟動 Docker:"
echo " sudo systemctl enable --now docker"
return 1
fi
# 設定預設值
DOMAIN="${DOMAIN:-mysite.com}"
PORT="${PORT:-8080}"
SITE_DIR="${SITE_DIR:-/srv/www/$DOMAIN/html}"
CONTAINER_NAME="${CONTAINER_NAME:-www.$DOMAIN}"
# 🧹 安全移除舊容器(僅限 www. 開頭且端口一致)
if [[ "$CONTAINER_NAME" =~ ^www\. ]]; then
if docker ps -a --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
# 檢查容器是否使用相同端口
local EXISTING_PORT=$(docker port "$CONTAINER_NAME" 80/tcp 2>/dev/null | cut -d: -f2)
if [[ "$EXISTING_PORT" == "$PORT" ]] || [[ -z "$EXISTING_PORT" ]]; then
echo "🧹 停止並移除舊容器:$CONTAINER_NAME"
docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1
else
echo "❌ 容器名稱 $CONTAINER_NAME 已被佔用(端口 $EXISTING_PORT),請使用不同的 domain 名稱"
return 1
fi
fi
fi
# 🔍 檢查端口是否被佔用
if netstat -tuln 2>/dev/null | grep -q ":$PORT "; then
# 嘗試找出佔用端口的 Docker 容器
local OCCUPYING_CONTAINER=$(docker ps --format "{{.Names}}" --filter "publish=$PORT" 2>/dev/null | head -1)
if [[ -n "$OCCUPYING_CONTAINER" ]]; then
echo "📦 容器 $OCCUPYING_CONTAINER 已佔用端口 $PORT 請使用其他端口"
else
# 嘗試找出佔用端口的進程
local OCCUPYING_PROCESS=$(ss -tlnp 2>/dev/null | grep ":$PORT " | head -1)
if [[ -n "$OCCUPYING_PROCESS" ]]; then
# 提取進程名稱
local PROCESS_NAME=$(echo "$OCCUPYING_PROCESS" | grep -o 'users:(([^)]*))' | sed 's/users:((\([^,]*\).*/\1/' | tr -d '"')
if [[ -n "$PROCESS_NAME" ]]; then
echo "🔍 進程 $PROCESS_NAME 已佔用端口 $PORT 請使用其他端口"
else
echo "🔍 端口 $PORT 已被其他進程佔用,請使用其他端口"
fi
else
echo "🔍 端口 $PORT 已被佔用,請使用其他端口"
fi
fi
return 1
fi
# 📁 建立網站資料夾與 index.html(僅當資料夾不存在或為空)
local NEED_CREATE_INDEX=false
local SITE_EXISTS=false
if [[ -d "$SITE_DIR" ]]; then
if find "$SITE_DIR" -mindepth 1 | read; then
SITE_EXISTS=true
else
NEED_CREATE_INDEX=true
fi
else
sudo mkdir -p "$SITE_DIR"
NEED_CREATE_INDEX=true
fi
if [[ "$NEED_CREATE_INDEX" == true ]]; then
echo "<!DOCTYPE html>
<html>
<head>
<meta charset=\"UTF-8\">
<title>$DOMAIN</title>
</head>
<body>
<h1>歡迎來到 $DOMAIN 的網站!</h1>
<p>這是來自 $SITE_DIR 的內容。</p>
</body>
</html>" | sudo tee "$SITE_DIR/index.html" > /dev/null
fi
# 設定網站根目錄(現在預設就是 html 目錄)
local WEB_ROOT="$SITE_DIR"
# 顯示網站根目錄信息
if [[ "$SITE_EXISTS" == true ]]; then
echo "📂 網站根目錄: $WEB_ROOT (目錄已存在沿用原本內容)"
else
echo "📂 網站根目錄: $WEB_ROOT"
fi
# 🚀 啟動容器
echo "🚀 啟動 Nginx 容器:$CONTAINER_NAME"
local CONTAINER_ID=""
if [[ "$NO_RESTART" == true ]]; then
CONTAINER_ID=$(docker run -d \
--name "$CONTAINER_NAME" \
-v "$WEB_ROOT":/usr/share/nginx/html:ro \
-p "$PORT":80 \
nginx:alpine 2>/dev/null)
else
CONTAINER_ID=$(docker run -d \
--name "$CONTAINER_NAME" \
-v "$WEB_ROOT":/usr/share/nginx/html:ro \
-p "$PORT":80 \
--restart=always \
nginx:alpine 2>/dev/null)
fi
# ✅ 驗證容器是否成功啟動
if [[ -z "$CONTAINER_ID" ]]; then
echo "❌ 容器啟動失敗"
return 1
fi
# 等待容器啟動
sleep 2
# 檢查容器狀態
if ! docker ps --format '{{.Names}}' | grep -q "^$CONTAINER_NAME$"; then
echo "❌ 容器啟動失敗,請檢查日誌:"
docker logs "$CONTAINER_NAME" 2>/dev/null || echo "無法獲取容器日誌"
return 1
fi
echo "✅ 網站 $DOMAIN 已啟動:http://localhost:$PORT"
}
# 快速啟動腳本
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
run_nginx_site "$@"
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment