Files
Linux/docker-userland-proxy-off.sh
T

139 lines
5.5 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
#
# docker-userland-proxy-off.sh
#
# 关闭 Docker userland-proxy,消除"每个发布端口一个 docker-proxy 进程"的内存开销。
# 实测收益:14 个发布端口时约省 70-90MB 常驻内存。
# 现代 Docker(>=23,dockerd 统一监听发布端口)关闭后本机 127.0.0.1 访问仍可用,
# 外部访问走 iptables DNAT 不受影响。
#
# 适用系统: Debian/Ubuntu(systemd)/ Alpine(OpenRC)
# 要求: root 权限
#
# 使用方法:
# URL="https://git.1-h.cc/Scripts/Linux/raw/branch/2026/docker-userland-proxy-off.sh"; curl -fsSL "$URL" | bash
#
# 幂等: 已关闭时直接报告"已生效"并退出,不会重复重启 docker。
set -euo pipefail
# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
# root 检查
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}错误:需要 root 权限${NC}" >&2
exit 1
fi
# docker 检查
if ! command -v docker >/dev/null 2>&1; then
echo -e "${RED}错误:未检测到 docker${NC}" >&2
exit 1
fi
# --- 1. 检测服务管理器,决定重启命令 ---
RESTART_CMD=""
if [ -f /etc/alpine-release ] && command -v rc-service >/dev/null 2>&1; then
RESTART_CMD="rc-service docker restart"
echo -e "${GREEN}[系统] Alpine / OpenRC${NC}"
elif command -v systemctl >/dev/null 2>&1; then
RESTART_CMD="systemctl restart docker"
echo -e "${GREEN}[系统] systemd${NC}"
else
echo -e "${RED}错误:无法识别的服务管理器(仅支持 Alpine/OpenRC 与 systemd)${NC}" >&2
exit 1
fi
# --- 2. 幂等检查:当前是否已生效 ---
# 注意: docker info 的 EnableUserlandProxy 字段只在 true 时显示,false 时字段消失,
# 因此以 daemon.json 配置为准,docker info 仅作辅助。
CFG_VAL="unset"
if [ -f /etc/docker/daemon.json ]; then
if command -v jq >/dev/null 2>&1; then
CFG_VAL=$(jq -r '."userland-proxy" // "unset"' /etc/docker/daemon.json 2>/dev/null || echo unset)
elif command -v python3 >/dev/null 2>&1; then
CFG_VAL=$(python3 -c 'import json;v=json.load(open("/etc/docker/daemon.json")).get("userland-proxy", "unset");print("false" if v is False else ("true" if v is True else v))' 2>/dev/null || echo unset)
elif grep -q '"userland-proxy"[[:space:]]*:[[:space:]]*false' /etc/docker/daemon.json 2>/dev/null; then
CFG_VAL="false"
fi
fi
if [ "$CFG_VAL" = "false" ]; then
# 注意: [d]ocker-proxy 正则避免 pgrep 匹配到脚本自身;|| true 规避 pipefail 下 pgrep 无匹配(退出1)触发 set -e
COUNT=$(pgrep -f '[d]ocker-proxy' 2>/dev/null | wc -l || true)
echo -e "${GREEN}[已生效] daemon.json 已配置 userland-proxy: false,当前 docker-proxy 进程数: ${COUNT},无需操作${NC}"
exit 0
fi
echo -e "${YELLOW}[状态] 当前 daemon.json 未配置 userland-proxy: false(值: ${CFG_VAL})${NC}"
# --- 3. 备份现有 daemon.json ---
mkdir -p /etc/docker
if [ -f /etc/docker/daemon.json ]; then
BAK="/etc/docker/daemon.json.bak-$(date +%Y%m%d%H%M%S)"
cp /etc/docker/daemon.json "$BAK"
echo -e "${GREEN}[备份] $BAK${NC}"
fi
# --- 4. 合并写入 userland-proxy: false(保留原有其他配置)---
if [ -f /etc/docker/daemon.json ]; then
if command -v jq >/dev/null 2>&1; then
TMP="/etc/docker/daemon.json.tmp.$$"
jq '."userland-proxy" = false' /etc/docker/daemon.json > "$TMP"
mv "$TMP" /etc/docker/daemon.json
elif command -v python3 >/dev/null 2>&1; then
python3 - <<'PYEOF'
import json
p = "/etc/docker/daemon.json"
with open(p) as f:
cfg = json.load(f)
cfg["userland-proxy"] = False
with open(p, "w") as f:
json.dump(cfg, f, indent=2)
PYEOF
else
echo -e "${RED}错误:需要 jq 或 python3 来合并 daemon.json,请先安装其一${NC}" >&2
exit 1
fi
else
printf '{\n "userland-proxy": false\n}\n' > /etc/docker/daemon.json
fi
echo -e "${GREEN}[配置] /etc/docker/daemon.json -> userland-proxy: false${NC}"
cat /etc/docker/daemon.json
# --- 5. 重启 docker(容器会短暂中断几秒)---
# 注: systemctl restart 在个别环境下会因时序返回非零,实际已生效;
# 因此不在此处退出,改由下面的验证步骤给出真实结果。
echo -e "${YELLOW}[重启] $RESTART_CMD${NC}"
if ! $RESTART_CMD; then
echo -e "${YELLOW}[提示] 重启命令返回非零,继续验证实际状态...${NC}"
fi
sleep 6
# 等待 docker 就绪(最多 30s)
for _ in $(seq 1 15); do
if docker info >/dev/null 2>&1; then break; fi
sleep 2
done
# --- 6. 验证 ---
PROXY_COUNT=$(pgrep -f '[d]ocker-proxy' 2>/dev/null | wc -l || true)
if [ "$PROXY_COUNT" = "0" ]; then
echo -e "${GREEN}[验证] docker-proxy 进程数: 0${NC}"
else
echo -e "${RED}[警告] docker-proxy 进程数: ${PROXY_COUNT}(期望 0),userland-proxy 可能未生效,请检查${NC}"
fi
CONF=$(docker info 2>/dev/null | awk -F': ' '/EnableUserlandProxy/{gsub(/ /,"",$2); print $2}' | head -1)
echo -e "${GREEN}[验证] daemon.json 配置: userland-proxy: false(docker info 字段: ${CONF:-不显示=false 时正常})${NC}"
echo "--- 内存 ---"
free -m | head -2
echo "--- 容器状态 ---"
docker ps --format '{{.Names}}\t{{.Status}}' 2>/dev/null || true
echo ""
echo -e "${YELLOW}验证建议:${NC}"
echo " 1. 外部访问发布端口(如 curl http://<公网IP>:<端口>)"
echo " 2. 本机 loopback(如 curl http://127.0.0.1:<端口>)"
echo " 3. 容器互访(反代容器访问业务容器内网地址)"
echo -e "${YELLOW}回滚:${NC} rm /etc/docker/daemon.json && $RESTART_CMD"