Last active
August 14, 2025 13:54
-
-
Save hunandy14/7dafceef67cb47db38620a3c2479792d to your computer and use it in GitHub Desktop.
快速部署 nginx 容器網站
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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