幫助中心

伺服器、網路和帳戶的分步教程。

Linux 伺服器

小記憶體 VPS 執行 Docker:安裝、日誌與記憶體限制、防火牆陷阱及磁碟清理

小記憶體 VPS 執行 Docker:安裝、日誌與記憶體限制、防火牆陷阱及磁碟清理

只要從 Docker 官方源安裝、限制容器日誌、為每個容器設定記憶體上限、把連接埠釋出在 127.0.0.1 並由反向代理對外(否則 Docker 會繞過 UFW 和 firewalld),再定期清理舊映象,Docker 在 1 GB 記憶體的 VPS 上也能穩定執行。本文按步驟介紹在 Debian 12/13、Ubuntu 22.04/24.04 和 Rocky Linux / AlmaLinux 上的做法。

步驟 1:從官方源安裝 Docker

Debian 和 Ubuntu 自帶的 docker.io 軟體包往往版本較舊,也沒有 Compose 外掛,請改用 Docker 官方源。在 Debian 和 Ubuntu 上以 root 身份執行:

# Debian 12/13 and Ubuntu 22.04/24.04 - remove distro packages that conflict
for p in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
  apt-get remove -y "$p" 2>/dev/null
done

apt-get update
apt-get install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
. /etc/os-release        # sets ID (debian/ubuntu) and VERSION_CODENAME
curl -fsSL "https://download.docker.com/linux/$ID/gpg" -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

cat > /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/$ID
Suites: $VERSION_CODENAME
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

部分 Rocky Linux 和 AlmaLinux 映象預裝了 Podman,會與 Docker 的軟體包衝突,請先解除安裝:

# Rocky Linux / AlmaLinux 8, 9, 10
dnf remove -y podman buildah runc 2>/dev/null
dnf install -y dnf-plugins-core
dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl enable --now docker

在 Debian 和 Ubuntu 上,服務會自動啟動。檢查一切是否正常:

systemctl is-active docker
docker version
docker compose version
docker run --rm hello-world
docker 組的成員可以控制 Docker 守護程序,這等同於擁有伺服器的 root 許可權。只用 usermod -aG docker username 新增可信使用者,並且絕不要把 Docker API(/var/run/docker.sock 或 TCP 2375 連接埠)暴露到公網。

步驟 2:限制容器日誌並開啟 live-restore

預設情況下,Docker 會儲存容器輸出的每一行日誌,且不限大小。一個日誌量大的容器幾天就能寫滿 20 GB 硬碟。請在 /etc/docker/daemon.json 中設定全侷限制:

# /etc/docker/daemon.json
{
  "log-driver": "local",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "live-restore": true
}
  • local 日誌驅動:壓縮並輪轉日誌檔案,這裡每個容器最多 3 × 10 MB,docker logs 照常可用。
  • live-restore:Docker 守護程序重啟或升級時,容器繼續執行,軟體包更新不會讓網站中斷。

如果伺服器上有工具需要直接讀取 JSON 日誌檔案,可改用 json-file,限制相同:

  "log-driver": "json-file",
  "log-opts": { "max-size": "10m", "max-file": "3" },

校驗配置檔案並重啟 Docker。這第一次重啟會讓正在執行的容器停止一次:

dockerd --validate --config-file /etc/docker/daemon.json
systemctl restart docker
docker info --format '{{.LoggingDriver}} live-restore={{.LiveRestoreEnabled}}'
日誌設定只對之後新建的容器生效。請用 docker compose up -d --force-recreate 重新建立現有容器。

步驟 3:編寫帶記憶體和 CPU 限制的 compose 檔案

不設限制時,一個記憶體洩漏的容器就可能讓整臺 VPS 瘋狂使用 swap,或讓核心殺掉 sshd 甚至資料庫。設定上限後,只有這個容器會被殺掉並重啟。在 1 GB 方案上執行一個小應用加 MariaDB,可以這樣寫:

# /opt/app/compose.yaml
services:
  app:
    image: ghcr.io/example/app:1.4.2      # pin a version, avoid :latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"             # reachable only from the VPS itself
    mem_limit: 256m
    memswap_limit: 256m                   # no extra swap for this container
    cpus: 1.0
    pids_limit: 200
    healthcheck:
      test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/health"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 30s
    depends_on:
      db:
        condition: service_healthy

  db:
    image: mariadb:11.4
    restart: unless-stopped
    command: ["--innodb-buffer-pool-size=64M"]
    environment:
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MARIADB_DATABASE: app
    volumes:
      - db_data:/var/lib/mysql
    mem_limit: 300m
    memswap_limit: 300m
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 30s
      timeout: 5s
      retries: 5
      start_period: 60s

volumes:
  db_data:
  • mem_limit / memswap_limit:記憶體硬上限。兩者設為相同值,容器就不會使用 swap。請給系統留出空間:1 GB 記憶體時,所有限制加起來控制在 600–700 MB 左右。
  • cpus:1.0 表示一個完整核心。這樣即使某個容器很忙,也不會把整臺 VPS 推過合理使用規則中的 CPU 平均值。
  • restart: unless-stopped:容器崩潰或伺服器重啟後會自動恢復,但你主動停止的容器不會被拉起。
  • healthcheck:把容器標記為 healthy 或 unhealthy,depends_on 會等待資料庫就緒。檢查命令(這裡是 wget)必須存在於映象中。注意:單純的 Docker 不會重啟 unhealthy 的容器,只會顯示狀態。

把密碼等敏感資訊放在 compose.yaml 旁邊的 .env 檔案中,然後啟動:

cd /opt/app
echo 'DB_ROOT_PASSWORD=REPLACE_WITH_A_LONG_RANDOM_PASSWORD' > .env
chmod 600 .env
docker compose up -d
docker compose ps            # STATUS should become "healthy"
docker stats --no-stream     # memory use against each limit

步驟 4:避開防火牆繞過陷阱(UFW、firewalld 與 Docker)

這是使用 Docker 時最常見的安全錯誤。用 -p 8080:8080 或 "8080:8080" 釋出連接埠時,Docker 會新增自己的 NAT 和轉發規則。這些流量根本不經過 UFW 和 firewalld 管理的 INPUT 規則,所以即使 ufw status 顯示已攔截,連接埠實際上仍對整個公網開放。

按推薦順序,解決辦法如下:

  1. 釋出在 127.0.0.1 上("127.0.0.1:8080:8080",即上面 compose 檔案的寫法)。連接埠只能從 VPS 本機訪問。
  2. 前面加一層反向代理。主機上的 Nginx 或 Caddy 監聽 80/443(照常在 UFW 或 firewalld 中放行),再轉發到 127.0.0.1:8080,HTTPS 也能集中在一處配置。
  3. 內部服務完全不要釋出連接埠。資料庫和快取只需要 Compose 網路,同一專案的容器可透過服務名互相訪問(db:3306)。

為應用配置一個最簡單的 Nginx 站點(HTTPS 可按Nginx 與 Certbot 教程用 Let's Encrypt 新增):

# /etc/nginx/sites-available/app.example.com  (Rocky/Alma: /etc/nginx/conf.d/app.conf)
server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

檢查實際對外暴露了哪些連接埠:

# On the VPS: what is published, and on which address?
docker ps --format '{{.Names}}\t{{.Ports}}'
ss -tlnp | grep docker-proxy

# From ANOTHER machine: this must fail for a port bound to 127.0.0.1
nc -vz 203.0.113.10 8080

如果某個連接埠確實必須公開,但只允許特定地址訪問,可以在 DOCKER-USER 鏈中新增規則,Docker 會先於自己的規則檢查這條鏈。--ctorigdstport 匹配的是客戶端傳送時的原始連接埠,也就是 Docker 地址轉換之前的連接埠:

# Allow new connections to published port 8080 only from 203.0.113.0/24
iptables -I DOCKER-USER -i eth0 ! -s 203.0.113.0/24 -p tcp \
  -m conntrack --ctstate NEW --ctorigdstport 8080 -j DROP
iptables -L DOCKER-USER -n -v --line-numbers
DOCKER-USER 中的規則不會自動儲存,重啟後就會丟失,寫錯規則還可能讓容器斷網。綁定到 127.0.0.1 更簡單,重啟後依然有效。主機防火牆本身的配置請參閱 UFW 與 firewalld 防火牆教程。

步驟 5:保持磁碟整潔

在 10–40 GB 的硬碟上,舊映象、已停止的容器和構建快取很快就會堆積起來。先看看 Docker 佔用了多少空間:

docker system df          # images, containers, volumes, build cache
docker system df -v       # per image / container / volume
df -h / && du -sh /var/lib/docker

然後由安全到謹慎,逐步清理:

# Safe: stopped containers, unused networks, dangling images, build cache
docker system prune

# Unused images (not used by any container) older than 7 days
docker image prune -a --filter "until=168h"

# Build cache only
docker builder prune

# CAREFUL: deletes volumes not used by any container - this is data
docker volume ls
docker volume prune
docker volume prune 和 docker system prune --volumes 會刪除當時沒有被任何容器使用的卷。如果你停止並刪除了資料庫容器,它的卷就算作未使用,資料會隨之消失。務必先執行 docker volume ls,並備份需要的內容。

每週自動清理一次,且永遠不碰資料卷:

# /etc/cron.d/docker-prune - weekly, never touches volumes
0 4 * * 0 root docker image prune -af --filter "until=168h" >/dev/null && docker builder prune -f --filter "until=168h" >/dev/null

如果硬碟已經滿了,請參閱如何查詢並釋放磁碟空間。

步驟 6:在 512 MB 和 1 GB 方案上使用 swap 或 zram

多個容器同時出現記憶體峰值時,少量 swap 可以避免小記憶體 VPS 突然因記憶體不足而殺程序。經典做法是在硬碟上建 swap 檔案(參閱如何新增 swap 空間)。在 Debian 和 Ubuntu 上,還可以加上 zram:它把壓縮後的 swap 放在記憶體裡,速度快得多,也不產生磁碟 I/O:

# Debian / Ubuntu: compressed swap in RAM
apt install -y zram-tools
cat > /etc/default/zramswap <<'EOF'
ALGO=zstd
PERCENT=50
PRIORITY=100
EOF
systemctl restart zramswap
swapon --show

# Prefer RAM, swap only under pressure
echo 'vm.swappiness=10' > /etc/sysctl.d/90-swappiness.conf
sysctl --system

Rocky Linux 和 AlmaLinux 請使用 swap 檔案。swap 只是安全網,不是額外記憶體:如果 free -h 顯示 swap 一直在被使用,說明當前方案對你的容器來說太小了。

步驟 7:安全地更新容器

固定映象版本,不要用 :latest。這樣只有在你決定時才會更新,而且隨時知道該回滾到哪個版本:

cd /opt/app
# 1. Back up first (database dump; replace "app" with your database)
docker compose exec -T db sh -c 'mariadb-dump -uroot -p"$MARIADB_ROOT_PASSWORD" --single-transaction app' > /root/app-$(date +%F).sql

# 2. Change the image tag in compose.yaml (e.g. 1.4.2 -> 1.5.0), then:
docker compose pull
docker compose up -d
docker compose ps
docker compose logs --tail=50 app

# 3. Roll back if needed: put the old tag back and run "docker compose up -d"

Docker 引擎本身隨系統軟體包一起更新。有了 live-restore,升級期間容器會繼續執行:

apt-get update && apt-get install --only-upgrade -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Rocky / AlmaLinux
dnf upgrade -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
資料庫和有狀態應用不要使用自動更新工具:新的大版本可能改變資料格式。先看更新說明、做好備份,再手動更新。大型映象請在自己的電腦或 CI 中構建後拉取,不要在 1 GB 的 VPS 上構建。

FAQ

512 MB 方案夠跑 Docker 嗎?

跑一兩個輕量容器(靜態網站、小型代理或機器人)是夠的,前提是配好 swap 並嚴格限制記憶體。資料庫加 Web 應用則建議使用 1–2 GB 方案,會寬裕很多。

容器為什麼不停地重啟?

檢視 docker compose logs app 和 docker inspect $(docker compose ps -q app) --format '{{.State.OOMKilled}}'。如果結果是 true,說明容器觸及了記憶體上限:可以調高 mem_limit、降低應用的記憶體佔用,或在服務頁面升級方案。

JUSTG 能幫忙處理容器問題嗎?

VPS 本身的問題,例如網路或控制檯訪問,請透過工單聯絡 Technical support。JUSTG 提供 7×24 小時 AI 客服(工單),需要人工處理的問題會由工程師跟進。

這篇文章有幫助嗎?
幫助中心
Linux 伺服器 Linux SSH 金鑰登入教程:設定 ed25519 金鑰並禁用密碼登入 只要伺服器有公網 IPv4,上線幾分鐘內就會遇到 SSH 暴力破解機器人,而密碼登入正是它們的首要目標。本文說明如何用較新的 ed25519 金鑰完成 Linux SSH 金鑰登入,再禁用 SSH 密碼登入,全程避免把自己鎖在門外。步驟適用於 Debian… Linux 伺服器 Linux VPS 修改 SSH 連接埠與 root 密碼教程(安全不斷線) 把 SSH 從 22 連接埠移走雖然擋不住有心的攻擊者,卻能讓日誌裡大部分自動掃描消失;而任何新伺服器的第一件事,就是換一組新的 root 密碼。本文說明如何在 Linux VPS 修改 SSH 連接埠與 root 密碼且不斷線:修改 sshd_config、在 Rocky… Linux 伺服器 Linux 防火牆設定教程:VPS 使用 UFW 與 firewalld(含 IPv6) 新安裝的 Linux 伺服器,任何服務開啟的連接埠都會直接對外響應,因此主機防火牆是最先要設定的專案之一。本文說明如何在 Debian / Ubuntu 用 UFW 設定 Linux 防火牆,以及在 CentOS、Rocky Linux、AlmaLinux 使用… Linux 伺服器 Linux VPS 新增 Swap 交換空間教程(適合 512MB / 1GB 小記憶體套餐) 在 512 MB 或 1 GB 的雲伺服器上,資料庫、PHP-FPM 加上一次軟體包更新,很容易在同一時間用光記憶體。這時核心的 OOM killer 會強制終止程序,最常被犧牲的就是 MySQL 或 MariaDB。在 Linux VPS 新增 Swap… Linux 伺服器 Linux 硬碟空間已滿怎麼辦?VPS 找出並釋放磁碟空間教程 “No space left on device”幾乎會讓伺服器上所有東西出問題:資料庫無法寫入、網站返回錯誤、軟體包更新失敗,有時甚至無法正常登入。本文一步步說明如何排除 Linux 硬碟空間已滿:先確認是資料塊還是 inode 用完,再用 du 與 ncdu…
AlipayUnionPayVISAMastercardPayPalUSDTstripe