从零搭建企业内网镜像站教程

  • 服务器系统:Ubuntu 24.04 LTS
  • 服务器资源:4core+8G+500G(这里大小自己斟酌即可,我这里没那多空间,先测试流程)
  • 镜像根目录:/mirrors (建议单独挂载磁盘使用)
  • 先做 APT / Ubuntu 系测试同步
  • 先用 IP 地址访问
  • 提供一个 像网易开源镜像站那种简洁目录首页
  • 后续可平滑扩展到 Debian / Ubuntu Ports / RPM 源

一、整体方案结构

  • 目录规划
  • 安装同步与 Web 服务软件
  • 测试同步 Ubuntu/APT 仓库
  • Nginx 发布 /mirrors
  • 做一个类似网易风格的首页
  • 定时同步、日志、权限、后续扩展

二、目录规划

shell 复制代码
# 整体目录结构
/mirrors/
├── ubuntu/                 # Ubuntu APT 仓库
├── ubuntu-ports/           # Ubuntu Ports 仓库(后续)
├── debian/                 # Debian 仓库(后续)
├── html/                   # 首页静态页面
├── scripts/                # 同步脚本
├── logs/                   # 同步日志
└── tmp/                    # 临时目录

# 创建目录
sudo mkdir -p /mirrors/{ubuntu,ubuntu-ports,debian,html,scripts,logs,tmp}
sudo chown -R root:root /mirrors
sudo chmod -R 755 /mirrors

三、安装基础软件

shell 复制代码
sudo apt update
sudo apt install -y nginx debmirror rsync apache2-utils curl vim tree jq wget lftp

说明:
nginx:Web 发布
debmirror:APT 系同步工具
rsync:同步基础
apache2-utils:后面有些辅助命令可用
curl:测试 Web 访问
vim:编辑脚本
tree:看目录结构方便

四、同步源

我这边先同步Ubuntu最小仓库合集,跑完整体流程,后面同步大差不差

先同步一个最小 Ubuntu 仓库测试集:

发行版:jammy

pocket:jammy, jammy-updates, jammy-security

组件:main

架构:amd64

不同步源码:--nosource

shell 复制代码
sudo vim /mirrors/scripts/sync-ubuntu.sh
内容如下:
#!/bin/bash
set -euo pipefail

TARGET="/mirrors/ubuntu"
LOGDIR="/mirrors/logs"
DATE="$(date +%F_%H-%M-%S)"
LOGFILE="${LOGDIR}/sync-ubuntu-${DATE}.log"

HOST="mirrors.tuna.tsinghua.edu.cn"
ROOT="/ubuntu"
SECTIONS="main"
ARCHES="amd64"
DISTS="jammy,jammy-updates,jammy-security"

mkdir -p "$TARGET" "$LOGDIR"

exec > >(tee -a "$LOGFILE") 2>&1

echo "==== $(date) start sync ubuntu ===="

/usr/bin/debmirror \
  --method=rsync \
  --host="$HOST" \
  --root="$ROOT" \
  --dist="$DISTS" \
  --section="$SECTIONS" \
  --arch="$ARCHES" \
  --nosource \
  --progress \
  --ignore-release-gpg \
  --no-check-gpg \
  "$TARGET"

echo "==== $(date) sync ubuntu done ===="

# 增加执行权限
sudo chmod +x /mirrors/scripts/sync-ubuntu.sh

# 先检查上游的rsync源 的 ubuntu是否可用
rsync rsync://mirrors.tuna.tsinghua.edu.cn/ubuntu/  
# 如有内容输出,说明可用
#如果不行,就把脚本改成 http 模式,后面写备选写法。

# 执行源同步
./sync-ubuntu.sh  # 等待同步完成

# 同步完成后,检查目录:
tree -L 2 /mirrors/ubuntu

同步Ubuntu源或者是其他源,这里只用ubuntu 做最小测试,后面要同步其他源,一些脚本都会后续整理发出来。

跳转到附录1-标准化同步脚本:

可知的是,80%的源都可以分为APT和RPM两种,剩余的20%,都是例外。

APT系的例外

直接 wget/rsync

或者用 aptly mirror

或者定制脚本

RPM系的例外

有些第三方 yum 仓库:

repo 文件写法特殊,认证特殊,历史元数据不全,modular 仓库行为复杂

这时可能要:

dnf reposync

手工 createrepo_c

或者上游直接 rsync 更省事

也就是说

APT 体系优先用 debmirror

RPM 体系优先用 reposync

但保留例外处理能力

五、Nginx发布mirrors页

现在我们要把 /mirrors 变成一个可访问的镜像站。

这里我们参考网易的镜像站简洁风格。

我们先把首页文件放到:/mirrors/html

这个目录是首页静态页面目录。

5.1 配置 Nginx 站点

shell 复制代码
sudo vim /etc/nginx/sites-available/mirror
server {
    listen 80 default_server;
    listen [::]:80 default_server;

    server_name _;

    charset utf-8;
    root /mirrors/html;
    index index.html;

    access_log /var/log/nginx/mirror_access.log;
    error_log  /var/log/nginx/mirror_error.log;

    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;

    # 首页
    location / {
        try_files $uri $uri/ /index.html;
    }

    # 静态资源
    location /static/ {
        alias /mirrors/html/static/;
        autoindex off;
    }

    # Ubuntu 镜像
    location /ubuntu/ {
        alias /mirrors/ubuntu/;
        autoindex on;
    }

    # Ubuntu Ports
    location /ubuntu-ports/ {
        alias /mirrors/ubuntu-ports/;
        autoindex on;
    }

    # Debian
    location /debian/ {
        alias /mirrors/debian/;
        autoindex on;
    }

    # 浏览整个 mirrors 根目录
    location /pub/ {
        alias /mirrors/;
        autoindex on;
    }
}

# 启用站点
sudo rm -f /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/mirror /etc/nginx/sites-enabled/mirror
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl enable nginx

5.2 做"网易"风格的站点

shell 复制代码
sudo mkdir -p /mirrors/html/static   # 风格的静态首页。
sudo vim /mirrors/html/index.html    # 首页HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <title>内网开源镜像站</title>
    <style>
        body {
            font-family: Arial, Helvetica, sans-serif;
            background: #fff;
            color: #333;
            margin: 20px;
        }
        h1 {
            color: #1e50a2;
            font-size: 28px;
            margin-bottom: 20px;
        }
        .desc {
            margin-bottom: 20px;
            color: #555;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            font-size: 14px;
        }
        th, td {
            border: 1px solid #ddd;
            padding: 8px 10px;
            text-align: left;
        }
        th {
            background: #f3f3f3;
        }
        tr:nth-child(even) {
            background: #fafafa;
        }
        a {
            color: #1a4fb3;
            text-decoration: none;
        }
        a:hover {
            text-decoration: underline;
        }
        .footer {
            margin-top: 30px;
            color: #777;
            font-size: 12px;
        }
        code {
            background: #f6f6f6;
            padding: 2px 4px;
        }
    </style>
</head>
<body>
    <h1>欢迎访问内网开源镜像站</h1>

    <div class="desc">
        当前为测试环境,暂时通过服务器 IP 访问。<br>
        镜像根目录:<code>/mirrors</code>
    </div>

    <table>
        <thead>
            <tr>
                <th>镜像名</th>
                <th>上次更新时间</th>
                <th>使用帮助</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td><a href="/ubuntu/">ubuntu/</a></td>
                <td>2026-7-9</td>
                <td><a href="/static/ubuntu-help.html">Ubuntu 使用帮助</a></td>
            </tr>
            <tr>
                <td><a href="/ubuntu-ports/">ubuntu-ports/</a></td>
                <td>2026-7-9</td>
                <td><a href="/static/ubuntu-ports-help.html">Ubuntu Ports 使用帮助</a></td>
            </tr>
            <tr>
                <td><a href="/debian/">debian/</a></td>
                <td>2026-7-9</td>
                <td><a href="/static/debian-help.html">Debian 使用帮助</a></td>
            </tr>
        </tbody>
    </table>

    <div class="footer">
        内网镜像站测试页面 · Ubuntu 24.04 LTS · Nginx
    </div>
</body>
</html>

5.3 配置【使用帮助】页面

shell 复制代码
sudo vim /mirrors/html/static/ubuntu-help.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="utf-8">
    <title>Ubuntu 使用帮助</title>
    <style>
        body { font-family: Arial, Helvetica, sans-serif; margin: 20px; color: #333; }
        h1 { color: #1e50a2; }
        pre {
            background: #f6f6f6;
            border: 1px solid #ddd;
            padding: 12px;
            overflow-x: auto;
        }
        a { color: #1a4fb3; text-decoration: none; }
    </style>
</head>
<body>
    <h1>Ubuntu 使用帮助</h1>

    <p>请将下列 IP 替换为你的镜像站服务器地址,例如:<strong>http://192.168.1.10/ubuntu</strong></p>

    <h2>Ubuntu 22.04 / 24.04 示例</h2>
    <pre>
deb http://你的服务器IP/ubuntu jammy main
deb http://你的服务器IP/ubuntu jammy-updates main
deb http://你的服务器IP/ubuntu jammy-security main
    </pre>

    <p>修改完成后执行:</p>
    <pre>
sudo apt update
    </pre>

    <p><a href="/">返回首页</a></p>
</body>
</html>

后续其余的帮助页面参考Ubuntu的这个就行,或者不行就让AI帮你写一个。

使用帮助也可以参考网易的使用帮助格式-都可以自定义,网易的使用帮助风格如下:

六、访问测试,包含客户端测试

直接浏览器 输入 IP 即可访问到镜像站首页。

如果打不开,检查防火墙。

找一台 Ubuntu 客户端测试。

shell 复制代码
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
sudo vim /etc/apt/sources.list
deb http://192.168.1.100/ubuntu jammy main
deb http://192.168.1.100/ubuntu jammy-updates main
deb http://192.168.1.100/ubuntu jammy-security main

sudo apt update
如果成功,说明:
上游同步
本地目录结构
Nginx 发布
APT 客户端访问
这一整条链路已经跑通了

七、如果 rsync 不行,改成 HTTP 模式

有些镜像站的 rsync 支持可能有限

如果你发现 debmirror 的 rsync 有问题,可以改脚本成:

shell 复制代码
#!/bin/bash
set -euo pipefail

TARGET="/mirrors/ubuntu"
LOGDIR="/mirrors/logs"
DATE="$(date +%F_%H-%M-%S)"
LOGFILE="${LOGDIR}/sync-ubuntu-test-${DATE}.log"

HOST="mirrors.tuna.tsinghua.edu.cn"
ROOT="/ubuntu"
SECTIONS="main"
ARCHES="amd64"

exec > >(tee -a "$LOGFILE") 2>&1

echo "==== $(date) start sync ubuntu test ===="

sync_one() {
    local dist="$1"
    echo "---- syncing $dist ----"
    /usr/bin/debmirror \
      --no-check-gpg \
      --ignore-release-gpg \
      --progress \
      --method=http \
      --host="$HOST" \
      --root="$ROOT" \
      --dist="$dist" \
      --section="$SECTIONS" \
      --arch="$ARCHES" \
      --nosource \
      "$TARGET"
}

sync_one "jammy"
sync_one "jammy-updates"
sync_one "jammy-security"

echo "==== $(date) sync ubuntu test done ===="

八、统一脚本规范

后面会同步更多源,所以建议从现在开始规范起来

  • 脚本目录 统一放在 /mirrors/scripts/
  • 日志目录 统一放在 /mirrors/logs/
  • 首页文件统一放在 /mirrors/html/
  • 临时文件统一放在 /mirrors/tmp/

九、定时任务

测试跑通后,你可以先加个 cron。

shell 复制代码
sudo crontab -e
例如每天凌晨 2:30 跑一次测试同步:
30 2 * * * /mirrors/scripts/sync-ubuntu.sh
.....等等

十、加一个"更新目录时间"的展示能力

当前的index.html 页面的更新时间是写死的,暂时可以写死,没问题。

可以增加一个脚本,根据目录修改时间自动生成 index.html 表格。

  • 目标:
    自动扫描 /mirrors
    自动找出镜像目录
    自动生成首页 HTML
    自动显示最后更新时间
    风格保持简单

重要: 先定义哪些目录显示在首页,脚本里可以自行修改。

例如:

ubuntu

ubuntu-ports

debian

debian-security

我这里给的是:只显示存在且非空的目录

这个脚本它其实就是做三件事:

定义哪些镜像需要展示

定义展示名/帮助链接

定义如何取最后更新时间

并输出成 HTML
也就是说,维护点只有这几个:

增加一个新仓库

删除一个仓库

修改帮助链接

修改排序

修改样式

10.1 创建更新首页的脚本

可以自动更新web页面的首页,但是不包括自动更新帮助文档,帮助文档特殊情况比较多,所以建议手动维护。

shell 复制代码
sudo vim /mirrors/scripts/update-index.sh
#!/usr/bin/env bash
# =========================
# 配置区:你以后主要维护这里
# =========================
HTML_DIR="/mirrors/html"
INDEX_FILE="${HTML_DIR}/index.html"
STATIC_PREFIX="/static"                 # 帮助页对外 URL 前缀
SITE_TITLE="内网开源镜像站"             # 站点标题
#SITE_SUBTITLE=""                       # 站点副标题
MIRROR_ROOT_LABEL="/mirrors"
#FOOTER_TEXT=""                         # 页脚

# 首页展示顺序:只展示此列表中的仓库;且目录存在时才展示
REPOS=(
  "ubuntu"
  "ubuntu-ports"
  "debian"
  "debian-security"
  "debian-backports"
  "ubuntu-old-releases"
  "docker-ce"
  "nginx"
  "postgresql"
  "nodejs"
  "mysql"
  "gitlab-runner"
  "kubernetes"
  "elastic"
)
# 仓库说明(显示在"使用帮助"的文案中)
declare -A DESC_MAP=(
  ["ubuntu"]="Ubuntu APT 镜像"
  ["ubuntu-ports"]="Ubuntu Ports 镜像"
  ["debian"]="Debian APT 镜像"
  ["debian-security"]="Debian Security 镜像"
  ["debian-backports"]="Debian Backports 镜像"
  ["ubuntu-old-releases"]="Ubuntu Old Releases 镜像"
  ["docker-ce"]="Docker CE APT 镜像"
  ["nginx"]="Nginx 官方 APT 镜像"
  ["postgresql"]="PostgreSQL APT 镜像"
  ["nodejs"]="NodeSource APT 镜像"
  ["mysql"]="MySQL APT 镜像"
  ["gitlab-runner"]="GitLab Runner APT 镜像"
  ["kubernetes"]="Kubernetes APT 镜像"
  ["elastic"]="Elastic APT 镜像"
)
# 帮助页文件名(手工维护);为空或文件不存在则显示"暂无"
declare -A HELP_MAP=(
  ["ubuntu"]="ubuntu-help.html"
  ["ubuntu-ports"]="ubuntu-ports-help.html"
  ["debian"]="debian-help.html"
  ["debian-security"]="debian-security-help.html"
  ["debian-backports"]="debian-backports-help.html"
  ["ubuntu-old-releases"]="ubuntu-old-releases-help.html"
  ["docker-ce"]="docker-help.html"
  ["nginx"]="nginx-help.html"
  ["postgresql"]="postgresql-help.html"
  ["nodejs"]="nodejs-help.html"
  ["mysql"]="mysql-help.html"
  ["gitlab-runner"]="gitlab-runner-help.html"
  ["kubernetes"]="kubernetes-help.html"
  ["elastic"]="elastic-help.html"
)

# 可选:自定义"仓库目录"与"对外访问路径"
# 默认:
#   本地目录 = /mirrors/<repo>
#   对外路径 = /<repo>/
# 若不同,可在这里覆盖。例如:
# DIR_MAP["docker-ce"]="/mirrors/docker"
# URL_MAP["docker-ce"]="/docker/"
declare -A DIR_MAP=()
declare -A URL_MAP=()

# 可选:手动隐藏某些目录(即使存在也不展示)
declare -A HIDE_MAP=(
  # ["kubernetes"]="1"
)


# =========================
# 生成逻辑:一般不用改
# =========================
mkdir -p "$HTML_DIR"
TMP_FILE="$(mktemp)"
NOW="$(date '+%F %T')"
get_server_addr() {
  if [[ "$SERVER_ADDR_MODE" == "fixed" ]]; then
    printf '%s\n' "$SERVER_ADDR_FIXED"
    return
  fi
  local ip
  ip="$(hostname -I 2>/dev/null | awk '{print $1}')"
  if [[ -n "${ip:-}" ]]; then
    printf 'http://%s\n' "$ip"
  else
    printf 'http://127.0.0.1\n'
  fi
}
SERVER_ADDR="$(get_server_addr)"
repo_dir() {
  local repo="$1"
  if [[ -n "${DIR_MAP[$repo]:-}" ]]; then
    printf '%s\n' "${DIR_MAP[$repo]}"
  else
    printf '/mirrors/%s\n' "$repo"
  fi
}
repo_url() {
  local repo="$1"
  if [[ -n "${URL_MAP[$repo]:-}" ]]; then
    printf '%s\n' "${URL_MAP[$repo]}"
  else
    printf '/%s/\n' "$repo"
  fi
}
has_any_content() {
  local dir="$1"
  [[ -d "$dir" ]] || return 1
  find "$dir" -mindepth 1 -print -quit 2>/dev/null | grep -q .
}
get_last_update() {
  local dir="$1"
  [[ -d "$dir" ]] || { printf '%s\n' "-"; return; }
  has_any_content "$dir" || { printf '%s\n' "-"; return; }
  # 取目录内最新文件时间;不看 /mirrors/logs,因此不会被日志污染
  local ts
  ts="$(
    find "$dir" -type f -printf '%TY-%Tm-%Td %TH:%TM:%TS\n' 2>/dev/null \
      | LC_ALL=C sort | tail -1 | cut -d'.' -f1
  )"
  if [[ -n "${ts:-}" ]]; then
    printf '%s\n' "$ts"
  else
    printf '%s\n' "-"
  fi
}
help_cell_html() {
  local repo="$1"
  local desc="${DESC_MAP[$repo]:-$repo}"
  local help_file="${HELP_MAP[$repo]:-}"
  if [[ -n "$help_file" && -f "${HTML_DIR}${STATIC_PREFIX}/${help_file}" ]]; then
    printf '<a href="%s/%s">%s使用帮助</a>' "$STATIC_PREFIX" "$help_file" "$desc"
  else
    printf '<span style="color:#999;">暂无</span>'
  fi
}
print_row() {
  local repo="$1"
  local dir url last help
  dir="$(repo_dir "$repo")"
  url="$(repo_url "$repo")"
  last="$(get_last_update "$dir")"
  help="$(help_cell_html "$repo")"
  cat <<EOF_ROW
        <tr>
          <td><a href="${url}">${repo}/</a></td>
          <td>${last}</td>
          <td>${help}</td>
        </tr>
EOF_ROW
}
cat >"$TMP_FILE" <<EOF
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>${SITE_TITLE}</title>
  <style>
    :root {
      --text: #000000;
      --muted: #666;
      --line: #dcdcdc;
      --head: #efefef;
      --link: #0000ff;
      --title: #1e50a2;
      --stripe: #e8e8e8;
    }
    body {
      font-family: Arial, Helvetica, sans-serif;
      background: #fff;
      color: var(--text);
      margin: 16px;
    }
    h1 {
      color: var(--title);
      font-size: 32px;
      color: #0000ff;
      margin: 10px 0 12px 0;
      margin-bottom: 8px;
      font-family: "Bitstream Vera Sans", "Lucida Sans", "Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, sans-serif;
    }
    .desc {
      margin-bottom: 0px;
      color: var(--muted);
      line-height: 1.8;
    }
    table {
      width: 100%;
      border-collapse: collapse;
      font-size: 14px;
      border: 1px solid var(--line);
    }
    th, td {
      border: none;
      padding: 4px 10px;
      text-align: left;
      vertical-align: top;
    }
    th { background: var(--head); }
    tbody tr:nth-child(even) { background: var(--stripe); }
    a {
      color: var(--link);
      text-decoration: none;
    }
    a:hover { text-decoration: underline; }
    .footer {
      margin-top: 28px;
      color: #777;
      font-size: 12px;
    }
    code {
      background: #f6f6f6;
      padding: 2px 4px;
    }
   /* 表格列宽控制 */
   th:first-child, td:first-child { width: 50%; }   /* 镜像名 */
   th:nth-child(2), td:nth-child(2) { width: 25%; } /* 更新时间 */
   th:nth-child(3), td:nth-child(3) { width: 25%; } /* 使用帮助 */
  </style>
</head>
<body>
  <h1>${SITE_TITLE}</h1>
  <div class="desc">
    ${SITE_SUBTITLE}<br>
  </div>
  <table>
    <thead>
      <tr>
        <th>镜像名</th>
        <th>上次更新时间</th>
        <th>使用帮助</th>
      </tr>
    </thead>
    <tbody>
EOF
row_count=0
for repo in "${REPOS[@]}"; do
  [[ "${HIDE_MAP[$repo]:-0}" == "1" ]] && continue
  dir="$(repo_dir "$repo")"
  [[ -d "$dir" ]] || continue
  print_row "$repo" >>"$TMP_FILE"
  row_count=$((row_count + 1))
done
if [[ "$row_count" -eq 0 ]]; then
  cat >>"$TMP_FILE" <<'EOF'
        <tr>
          <td colspan="3" style="color:#999;">暂无已发布镜像目录</td>
        </tr>
EOF
fi
cat >>"$TMP_FILE" <<EOF
    </tbody>
  </table>
  <div class="footer">${FOOTER_TEXT}</div>
</body>
</html>
EOF
mv "$TMP_FILE" "$INDEX_FILE"
chmod 644 "$INDEX_FILE"
echo "Updated ${INDEX_FILE}"

###################################
# 赋予权限
sudo chmod +x /mirrors/scripts/update-index.sh

# 执行
sudo /mirrors/scripts/update-index.sh

10.2这个脚本如何维护?

以后主要只改顶部配置区,也就是这几块

10.2.1.比如你想新增 rocky、epel,就加进去:REPO配置区

注意:

只有当对应目录存在时才会显示。

默认目录是 /mirrors/。

10.2.2.控制"帮助文案":DESC_MAP

shell 复制代码
比如:
declare -A DESC_MAP=(
  ["docker-ce"]="Docker CE APT 镜像"
  ["nginx"]="Nginx 官方 APT 镜像"
)
这会影响首页第三列链接的文字,比如:
Docker CE APT 镜像使用帮助
Nginx 官方 APT 镜像使用帮助

102.3 控制"帮助页文件":HELP_MAP

shell 复制代码
比如你手工维护了:
/mirrors/html/static/docker-help.html
/mirrors/html/static/nginx-help.html
那就这样配:
declare -A HELP_MAP=(
  ["docker-ce"]="docker-help.html"
  ["nginx"]="nginx-help.html"
)
如果文件不存在,首页会显示"暂无"。

10.2.4 目录或访问路径不是默认值时:DIR_MAP / URL_MAP

shell 复制代码
默认逻辑是:
本地目录:/mirrors/<repo>
对外路径:/<repo>/

但如果你有例外,比如:
本地目录其实是 /mirrors/docker
外部访问想仍然用 /docker-ce/
那就写:
declare -A DIR_MAP=(
  ["docker-ce"]="/mirrors/docker"
)
declare -A URL_MAP=(
  ["docker-ce"]="/docker-ce/"
)

10.2.5 想临时隐藏某个仓库:HIDE_MAP

shell 复制代码
比如你暂时不想在首页展示 kubernetes:
declare -A HIDE_MAP=(
  ["kubernetes"]="1"
)
目录还在、数据还在,只是不展示。

到此 ,流程基本跑通了...

十一、维护例子,举例

shell 复制代码
# 1.加一个新仓库时怎么改
假设你要加 grafana
1.1 新建目录
 sudo mkdir -p /mirrors/grafana
1.2 写同步脚本
/mirrors/scripts/sync-grafana.sh
1.3 写帮助页
sudo vim /mirrors/html/static/grafana-help.html
1.4 改 update-index.sh
在这三处分别加入:REPO 、DESC_MAP、HELP_MAP
"grafana"
["grafana"]="Grafana 软件仓库镜像"
["grafana"]="grafana-help.html"
1.5 执行首页更新
sudo /mirrors/scripts/update-index.sh

删除一个仓库,这个就不用说了吧,推荐暂时隐藏
declare -A HIDE_MAP=(
  ["elastic"]="1"
)
这样来说更安全。

#####################################

附录1-标准化同步脚本

后面所有脚本都采用统一规范:

set -euo pipefail

日志写到 /mirrors/logs

每个脚本都可单独执行

同步成功后自动更新首页

如果你以后想从清华或其他国内源同步 ubuntu-ports,也只要改 HOST 和 ROOT

想同步多个架构的,修改ARCHES即可,在后面追加 ARCHES="amd64,i386,arm64,armhf,ppc64el,s390x"

想同步多个dists的,在脚本的DISTS参数里后面追加即可。

总控脚本:一键同步
shell 复制代码
这个脚本非常实用,以后可以只跑它。
sudo vim /mirrors/scripts/sync-all.sh
#!/bin/bash
set -euo pipefail
echo "==== $(date) start all sync jobs ===="
/mirrors/scripts/sync-ubuntu.sh
/mirrors/scripts/sync-ubuntu-ports.sh
/mirrors/scripts/sync-debian.sh
echo "==== $(date) all test sync jobs done ===="
/mirrors/scripts/update-index.sh || true

sudo chmod +x /mirrors/scripts/sync-all.sh

如下是同步脚本的模板:

以后你新增任何同步脚本,都建议按这个格式写

shell 复制代码
#!/bin/bash
set -euo pipefail
TARGET="/mirrors/ubuntu"
LOGDIR="/mirrors/logs"
DATE="$(date +%F_%H-%M-%S)"
LOGFILE="${LOGDIR}/sync-ubuntu-full-${DATE}.log"
HOST="mirrors.tuna.tsinghua.edu.cn"
ROOT="/ubuntu"
SECTIONS="main,restricted,universe,multiverse"
ARCHES="amd64"
DISTS=("xxx" "xxx")
mkdir -p "$TARGET" "$LOGDIR"
exec > >(tee -a "$LOGFILE") 2>&1
echo "==== $(date) start sync ${NAME} ===="
###############
# 这里写同步逻辑
##############
echo "==== $(date) sync ${NAME} done ===="
/mirrors/scripts/update-index.sh || true

如下是很多个具体的同步脚本

  • Ubuntu 同步脚本
shell 复制代码
sudo vim /mirrors/scripts/sync-ubuntu.sh
#!/bin/bash
set -euo pipefail

TARGET="/mirrors/ubuntu"
LOGDIR="/mirrors/logs"
DATE="$(date +%F_%H-%M-%S)"
LOGFILE="${LOGDIR}/sync-ubuntu-full-${DATE}.log"

HOST="mirrors.tuna.tsinghua.edu.cn"
ROOT="/ubuntu"
SECTIONS="main,restricted,universe,multiverse"
ARCHES="amd64"
DISTS="jammy,jammy-updates,jammy-security,noble,noble-updates,noble-security"

mkdir -p "$TARGET" "$LOGDIR"

exec > >(tee -a "$LOGFILE") 2>&1

echo "==== $(date) start sync ubuntu full ===="

debmirror \
  --method=rsync \
  --host="$HOST" \
  --root="$ROOT" \
  --dist="$DISTS" \
  --section="$SECTIONS" \
  --arch="$ARCHES" \
  --nosource \
  --progress \
  --ignore-release-gpg \
  --no-check-gpg \
  "$TARGET"

echo "==== $(date) sync ubuntu full done ===="

/mirrors/scripts/update-index.sh || true

# 赋予权限
sudo chmod +x /mirrors/scripts/sync-ubuntu.sh
  • Ubuntu Ports 同步脚本
shell 复制代码
这个适合 ARM 等 Ports 架构
sudo vim /mirrors/scripts/sync-ubuntu-ports.sh
#!/bin/bash
set -euo pipefail

TARGET="/mirrors/ubuntu-ports"
LOGDIR="/mirrors/logs"
DATE="$(date +%F_%H-%M-%S)"
LOGFILE="${LOGDIR}/sync-ubuntu-ports-test-${DATE}.log"

HOST="ports.ubuntu.com"
ROOT="/ubuntu-ports"
SECTIONS="main"
ARCHES="arm64"
DISTS="jammy,jammy-updates,jammy-security"

mkdir -p "$TARGET" "$LOGDIR"

exec > >(tee -a "$LOGFILE") 2>&1

echo "==== $(date) start sync ubuntu-ports test ===="

debmirror \
  --method=rsync \
  --host="$HOST" \
  --root="$ROOT" \
  --dist="$DISTS" \
  --section="$SECTIONS" \
  --arch="$ARCHES" \
  --nosource \
  --progress \
  --ignore-release-gpg \
  --no-check-gpg \
  "$TARGET"

echo "==== $(date) sync ubuntu-ports test done ===="

/mirrors/scripts/update-index.sh || true

# 赋予权限
sudo chmod +x /mirrors/scripts/sync-ubuntu-ports.sh
  • Debian 同步脚本
shell 复制代码
sudo vim /mirrors/scripts/sync-debian.sh
#!/bin/bash
set -euo pipefail

TARGET="/mirrors/debian"
LOGDIR="/mirrors/logs"
DATE="$(date +%F_%H-%M-%S)"
LOGFILE="${LOGDIR}/sync-debian-test-${DATE}.log"

HOST="mirrors.tuna.tsinghua.edu.cn"
ROOT="/debian"
SECTIONS="main"
ARCHES="amd64"
DISTS="bookworm,bookworm-updates"

mkdir -p "$TARGET" "$LOGDIR"

exec > >(tee -a "$LOGFILE") 2>&1

echo "==== $(date) start sync debian test ===="

debmirror \
  --method=rsync \
  --host="$HOST" \
  --root="$ROOT" \
  --dist="$DISTS" \
  --section="$SECTIONS" \
  --arch="$ARCHES" \
  --nosource \
  --progress \
  --ignore-release-gpg \
  --no-check-gpg \
  "$TARGET"

echo "==== $(date) sync debian test done ===="

/mirrors/scripts/update-index.sh || true

# 赋予权限
sudo chmod +x /mirrors/scripts/sync-debian.sh
  • Debian Security 同步脚本
shell 复制代码
sudo vim /mirrors/scripts/sync-debian-security.sh
#!/bin/bash
set -euo pipefail
TARGET="/mirrors/debian-security"
LOGDIR="/mirrors/logs"
DATE="$(date +%F_%H-%M-%S)"
LOGFILE="${LOGDIR}/sync-debian-security-test-${DATE}.log"
HOST="security.debian.org"
ROOT="/debian-security"
SECTIONS="main"
ARCHES="amd64"
DISTS="bookworm-security"
mkdir -p "$TARGET" "$LOGDIR"
exec > >(tee -a "$LOGFILE") 2>&1
echo "==== $(date) start sync debian-security test ===="
debmirror \
  --method=rsync \
  --host="$HOST" \
  --root="$ROOT" \
  --dist="$DISTS" \
  --section="$SECTIONS" \
  --arch="$ARCHES" \
  --nosource \
  --progress \
  --ignore-release-gpg \
  --no-check-gpg \
  "$TARGET"
echo "==== $(date) sync debian-security test done ===="
/mirrors/scripts/update-index.sh || true

# 赋予权限
sudo chmod +x /mirrors/scripts/sync-debian-security-test.sh

后续会继续持续更新其他更多源的脚本...