Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions docs/cn-robustness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# CN Network Robustness — 国内网络适配指南

HomeLab Stack 针对中国大陆网络环境的完整适配方案。

## 问题描述

在中国大陆使用 HomeLab Stack 可能遇到以下问题:

| 问题 | 原因 |
|------|------|
| Docker Hub 拉取超时 | GFW 限速/阻断 |
| ghcr.io / gcr.io 无法访问 | 被完全阻断 |
| npm install 失败 | npmmirror 同步不完整 |
| GitHub API 超时 | 连接不稳定 |
| Let's Encrypt 验证失败 | HTTP-01 挑战超时 |

## 快速配置

```bash
# 一键配置所有镜像源
./scripts/setup-cn-mirrors.sh --all

# 拉取镜像(自动使用国内镜像)
./scripts/cn-pull.sh

# 启用 CN 模式
echo 'CN_MODE=true' >> .env
```

## 详细配置

### 1. Docker 镜像加速

```bash
# 运行配置脚本
./scripts/setup-cn-mirrors.sh --docker
```

或手动配置 `/etc/docker/daemon.json`:

```json
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://mirror.ccs.tencentyun.com"
]
}
```

```bash
sudo systemctl restart docker
```

### 2. ghcr.io / gcr.io 镜像替换

`cn-pull.sh` 自动将以下镜像源替换为 DaoCloud 镜像:

| 原始源 | 替换源 |
|--------|--------|
| gcr.io | gcr.m.daocloud.io |
| ghcr.io | ghcr.m.daocloud.io |
| k8s.gcr.io | k8s-gcr.m.daocloud.io |
| quay.io | quay.m.daocloud.io |

使用方式:
```bash
# 拉取单个镜像
./scripts/cn-pull.sh pull ghcr.io/goauthentik/server:2024.8.3

# 拉取某个 stack 的所有镜像
./scripts/cn-pull.sh stack sso

# 拉取所有 stack 的镜像
./scripts/cn-pull.sh all
```

### 3. npm 镜像

```bash
npm config set registry https://registry.npmmirror.com
```

如果 npmmirror 缺少某个包:
```bash
npm install --registry=https://registry.npmjs.org
```

### 4. pip 镜像

```bash
mkdir -p ~/pip
cat > ~/pip/pip.conf << 'EOF'
[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
trusted-host = mirrors.aliyun.com
EOF
```

### 5. DNS 配置

推荐使用国内 DNS:
- 阿里 DNS: `223.5.5.5`, `223.6.6.6`
- 腾讯 DNS: `119.29.29.29`
- 114 DNS: `114.114.114.114`

在 AdGuard Home 上游 DNS 中配置:
```
https://dns.alidns.com/dns-query
https://doh.pub/dns-query
```

### 6. Let's Encrypt 替代方案

如果 HTTP-01 验证超时,使用 DNS-01 验证:

在 Traefik 配置中(`config/traefik/traefik.yml`):

```yaml
certificatesResolvers:
letsencrypt:
acme:
dnsChallenge:
provider: cloudflare
delayBeforeCheck: 30
```

### 7. 代理配置(如有)

如果使用代理,在 `.env` 中设置:

```env
HTTP_PROXY=http://proxy:port
HTTPS_PROXY=http://proxy:port
NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
```

## 每个 Stack 的 CN 注意事项

| Stack | CN 问题 | 解决方案 |
|-------|---------|---------|
| Base | 无 | Docker Hub 镜像加速 |
| SSO | ghcr.io 阻断 | `cn-pull.sh` 或使用 compose 中的 CN mirror 行 |
| AI | ghcr.io 阻断 | `cn-pull.sh` 拉取 open-webui 和 sd |
| Monitoring | gcr.io 阻断 | `cn-pull.sh` 拉取 cAdvisor |
| Productivity | lscr.io 阻断 | `cn-pull.sh` 拉取 bookstack |
| Media | lscr.io 阻断 | `cn-pull.sh` 拉取 *arr + qbittorrent |
| Database | 无 | Docker Hub 镜像加速 |
| Network | ghcr.io 阻断 | `cn-pull.sh` 拉取 wireguard, cloudflare-ddns |
| Storage | 无 | Docker Hub 镜像加速 |

## 验证

```bash
# 测试网络连接
./scripts/setup-cn-mirrors.sh

# 测试 Docker 镜像拉取
docker pull alpine:3.19 --quiet && echo "Docker Hub OK"

# 测试 ghcr.io 镜像
docker pull ghcr.m.daocloud.io/open-webui/open-webui:v0.3.35 --quiet && echo "ghcr.io mirror OK"
```
180 changes: 180 additions & 0 deletions scripts/setup-cn-mirrors.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#!/usr/bin/env bash
# =============================================================================
# HomeLab Stack — CN Network Robustness Setup
# Configures Docker mirrors, npm registries, and pip mirrors for China users
# Usage: ./scripts/setup-cn-mirrors.sh [--docker] [--npm] [--pip] [--all]
# =============================================================================
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BASE_DIR="$SCRIPT_DIR/.."

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
log_info() { echo -e "${GREEN}[cn-setup]${NC} $*"; }
log_warn() { echo -e "${YELLOW}[cn-setup]${NC} $*"; }
log_error() { echo -e "${RED}[cn-setup]${NC} $*" >&2; }

SETUP_DOCKER=false
SETUP_NPM=false
SETUP_PIP=false

while [[ $# -gt 0 ]]; do
case "$1" in
--docker) SETUP_DOCKER=true; shift ;;
--npm) SETUP_NPM=true; shift ;;
--pip) SETUP_PIP=true; shift ;;
--all) SETUP_DOCKER=true; SETUP_NPM=true; SETUP_PIP=true; shift ;;
*) log_error "Unknown option: $1"; exit 1 ;;
esac
done

# If no flags specified, do everything
if [[ "$SETUP_DOCKER" == "false" && "$SETUP_NPM" == "false" && "$SETUP_PIP" == "false" ]]; then
SETUP_DOCKER=true
SETUP_NPM=true
SETUP_PIP=true
fi

# =============================================================================
# Docker mirror configuration
# =============================================================================
setup_docker_mirror() {
echo ""
echo "=== Docker Mirror Configuration ==="

if ! command -v docker &>/dev/null; then
log_warn "Docker not found, skipping"
return
fi

# Check if Docker daemon config exists
local daemon_json="/etc/docker/daemon.json"
local mirror_url="https://docker.m.daocloud.io"

if [[ -f "$daemon_json" ]]; then
if grep -q "$mirror_url" "$daemon_json" 2>/dev/null; then
log_info "Docker mirror already configured: $mirror_url"
return
fi
fi

log_info "Configuring Docker mirror: $mirror_url"

if [[ ! -f "$daemon_json" ]]; then
echo "{\"registry-mirrors\": [\"$mirror_url\"]}" | sudo tee "$daemon_json" >/dev/null
else
# Add mirror to existing config
local tmp
tmp=$(mktemp)
python3 -c "
import json, sys
with open('$daemon_json') as f:
config = json.load(f)
mirrors = config.get('registry-mirrors', [])
if '$mirror_url' not in mirrors:
mirrors.insert(0, '$mirror_url')
config['registry-mirrors'] = mirrors
with open('$tmp', 'w') as f:
json.dump(config, f, indent=2)
" 2>/dev/null && sudo cp "$tmp" "$daemon_json" && rm -f "$tmp"
fi

sudo systemctl restart docker 2>/dev/null || true
log_info "Docker mirror configured. Restart docker to apply."
}

# =============================================================================
# npm mirror configuration
# =============================================================================
setup_npm_mirror() {
echo ""
echo "=== NPM Mirror Configuration ==="

local npmrc="$HOME/.npmrc"
local mirror="https://registry.npmmirror.com"

if [[ -f "$npmrc" ]] && grep -q "$mirror" "$npmrc" 2>/dev/null; then
log_info "npm mirror already configured: $mirror"
return
fi

log_info "Configuring npm mirror: $mirror"
npm config set registry "$mirror" 2>/dev/null || true

log_info "npm mirror configured"
}

# =============================================================================
# pip mirror configuration
# =============================================================================
setup_pip_mirror() {
echo ""
echo "=== pip Mirror Configuration ==="

local pip_conf="$HOME/pip/pip.conf"
local mirror="https://mirrors.aliyun.com/pypi/simple/"

if [[ -f "$pip_conf" ]] && grep -q "$mirror" "$pip_conf" 2>/dev/null; then
log_info "pip mirror already configured: $mirror"
return
fi

log_info "Configuring pip mirror: $mirror"
mkdir -p "$HOME/pip"
cat > "$pip_conf" << EOF
[global]
index-url = $mirror
trusted-host = mirrors.aliyun.com
EOF

log_info "pip mirror configured"
}

# =============================================================================
# Connectivity test
# =============================================================================
test_connectivity() {
echo ""
echo "=== Network Connectivity Test ==="

local sites=(
"docker.io:443:Docker Hub"
"ghcr.io:443:GitHub Container Registry"
"docker.m.daocloud.io:443:DaoCloud Mirror"
"registry.npmmirror.com:443:npm Mirror"
"mirrors.aliyun.com:443:Aliyun Mirror"
)

for site in "${sites[@]}"; do
IFS=':' read -r host port name <<< "$site"
if nc -z -w3 "$host" "$port" 2>/dev/null; then
log_info "$name ($host) reachable"
else
log_warn "$name ($host) unreachable — mirror recommended"
fi
done
}

# =============================================================================
# Run
# =============================================================================
echo "============================================"
echo " HomeLab CN Network Robustness Setup"
echo "============================================"

test_connectivity

if [[ "$SETUP_DOCKER" == "true" ]]; then setup_docker_mirror; fi
if [[ "$SETUP_NPM" == "true" ]]; then setup_npm_mirror; fi
if [[ "$SETUP_PIP" == "true" ]]; then setup_pip_mirror; fi

echo ""
echo "============================================"
echo " Setup complete"
echo ""
echo " To pull images via mirrors:"
echo " ./scripts/cn-pull.sh"
echo ""
echo " To configure .env for CN mode:"
echo " echo 'CN_MODE=true' >> .env"
echo "============================================"