维护与开发手册
约 7626 字大约 25 分钟
2025-04-26
本文是面向基于 MirrorZ 项目镜像站的维护与开发手册,帮助新站长快速上手,了解镜像站的基本操作和维护方法。
需求评估
搭建一个镜像站,最基本的问题有如下几个:
是否需要新建镜像
新建镜像一般要瞄准特定的用户群体,典型的例子是校园网、云计算租户网络、软件研发机构内网等。如果没有一定规模的用量,维护一套镜像所带来的人力、管理和资金成本很可能会高于收益,此时建议采用公网镜像并自建缓存(如 HTTP 代理)来提高带宽使用效率和用户体验。常见的代理软件有 Squid、Apache TrafficServer 等,针对 Debian/Ubuntu 也有如 apt-cacher-ng 的成套方案。需要注意的是,如果缓存没有正确配置,可能会导致出现严重的使用问题。
计划镜像的内容清单
如果确实需要新建镜像,那么紧接着就是要针对计划镜像的内容做一份清单,这份清单应该按照需求的强烈程度排序。镜像不是越多越好,当规模增加到一定程度后,可能会带来意想不到的维护复杂度,因而导致相应的资源和精力投入并不总是随规模线性增减。
查看 CQMU Mirror Wiki,根据调查,越靠前说明越有需求。
适宜的硬件资源
对镜像的最直观投入是存储和网络,因为镜像站属于重IO业务(disk & network),通常建议使用实体物理机来承载,以免在虚拟化平台上对其他业务造成影响。
存储容量可以参考 TUNA 镜像站同步状态页给出的各个镜像的大小; 网络接入建议将服务器放在尽量靠近流量主干的位置,或根据具体网络情况和使用需求评估适宜的位置; 为镜像服务器配置适宜的内存,建议采用 ECC 内存,具体容量根据投入大小决定;一个不精确的估算建议:为每TB镜像数据配置至少1GB内存,并额外配置若干内存用于数据同步等任务。
sudo dmidecode --type memory | grep -i "Error Correction"
## if Error Correction Type: Multi-bit ECC, it's ECC; Vice versa.
数据同步管理
镜像站需要通过从外部进行数据同步来保持更新,因此如何管理和调度这些任务也成为了一个问题:
当镜像数量较少时,可使用 cron 或 systemd timers 的简单方式管理,易于理解和维护 镜像数量较多时,需要使用更复杂的调度器,如 tunasync 有几种明确 不建议 的做法:
- 将许多同步任务定时到同时启动,或安排在非常相近的时刻;这会导致上游和本地服务器均出现 io 瓶颈,显著降低同步效率和成功率; 两次同步间隔过短,绝大多数镜像建议每天同步 1-2 次,最多不超过 4 次;主要的上游源都不会更新过于频繁,更短的间隔不能明显提升时效性; 使用循环脚本,依次不停地尝试同步每个镜像;除上述效率原因外,这样做会浪费上游同步服务器资源,可能导致IP地址被封禁。 以下做法应务必避免
- 定时启动同步任务,并且同步命令没有超时的时间限制。
- 在这种情况下,如果出现网络问题,则会导致已经执行的同步命令还未终止时,再次执行同步命令,长此以往将会积累较多的卡住的同步进程。一旦网络恢复,这些卡住的同步进程则会出现巨量的网络流量,阻塞网络。代替地,应该在同步任务结束一定时间间隔后启动下一次同步任务,而不是定时启动;或者限制同步命令的执行时间,确保在出现故障的情况下,同步命令可以在合理的时间内终止。
参考:
使用较少的镜像源时,使用 systemd timers 进行管理
时间调度设计
[Unit]
Description=Daily mirror sync with randomized delay
[Timer]
OnCalendar=*-*-* 03:00:00 # 每天凌晨3点触发
RandomizedDelaySec=1h # 随机延迟0-1小时(避免同时启动)
Persistent=true # 补执行错过任务
[Install]
WantedBy=timers.target
任务执行控制
#!/bin/bash
TIMEOUT=3600 # 1小时超时
for repo in $(list-your-repos); do
timeout $TIMEOUT rsync -az --delete rsync://upstream/$repo /mirror/$repo
[ $? -eq 124 ] && echo "Timeout: $repo" >> /var/log/mirrorsync.err
done
监控建议
[Service]
Type=oneshot
ExecStart=/usr/local/bin/sync-mirrors.sh
LogLevelMax=warning # 抑制成功日志
Web 服务和用户界面
一个简单的 Web 服务器软件(例如 TUNA 使用的 Nginx),将所有有关文件设置好路径提供下载,就可以实现最基本的镜像站功能。镜像站绝大多数业务均为静态文件服务,因此类似的高性能静态文件 Web 服务器软件均可按需选择,不建议使用不常见的或过于侧重动态业务的 Web 服务器软件,如 SimpleHTTPServer、Tomcat 等。
如果希望用户界面更加友好,可以在主页前端、目录索引等功能上进行界面改进,可以参考如下几个项目:
- TUNA 镜像的前端页面:https://github.com/tuna/mirror-web
- USTCLUG 的镜像前端页面:https://git.lug.ustc.edu.cn/mirrors/mirrors-index
- 目录索引美化的 ngx-fancyindex
此外,还有镜像的使用帮助,可以参考:
- TUNA 的使用帮助:https://github.com/tuna/mirror-web/tree/master/help/_posts
- USTCLUG 的使用帮助:https://github.com/ustclug/mirrorhelp
MirrorZ 项目
清华大学开源的MirrorZ项目为国内镜像提供了一种开源、高效的镜像同步方案,包含前端项目和后端同步脚本,极大便利了各高校和组织的镜像服务。
测速脚本
MirrorZ 项目提供了测速脚本,帮助用户选择合适的镜像源。测速脚本会自动检测当前网络环境,并根据用户的需求选择合适的镜像源。
运行方式
linux安装curl后,执行shell脚本
curl https://mirrors.cernet.edu.cn/oh-my-mirrorz.py | python3
源文件
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim: expandtab ts=4 sts=4 sw=4
import subprocess
import urllib.request
import json
import os
import argparse
VERSION = '20250426'
CURL_VERSION = ''
UA_URL = 'https://mirrorz.org'
big = {
'centos': '/7/isos/x86_64/CentOS-7-x86_64-Everything-2009.iso',
'centos-vault': '/6.0/isos/x86_64/CentOS-6.0-x86_64-LiveDVD.iso',
'opensuse': '/distribution/leap/15.5/iso/openSUSE-Leap-15.5-DVD-x86_64-Media.iso',
'ubuntu-releases': '/22.04/ubuntu-22.04.3-desktop-amd64.iso',
'debian-cd': '/current/amd64/iso-bd/debian-edu-12.1.0-amd64-BD-1.iso',
'kali-images': '/kali-2023.2/kali-linux-2023.2-live-amd64.iso',
'CTAN': '/systems/texlive/Images/texlive.iso',
'blackarch': '/iso/blackarch-linux-full-2023.04.01-x86_64.iso',
'archlinux': '/iso/latest/archlinux-x86_64.iso',
'ubuntu': '/indices/md5sums.gz',
'debian': '/ls-lR.gz',
}
# filled by CI
mirrors = ["https://mirrorz.org/static/json/legacy/tuna.json","https://mirrorz.org/static/json/legacy/bfsu.json","https://mirrorz.org/static/json/legacy/bjtu.json","https://mirrorz.org/static/json/legacy/njupt.json","https://mirrorz.org/static/json/legacy/cqu.json","https://mirrorz.org/static/json/legacy/hit.json","https://mirrorz.org/static/json/legacy/nju.json","https://mirrorz.org/static/json/legacy/neusoft.json","https://mirrorz.org/static/json/legacy/pku.json","https://mirrorz.org/static/json/legacy/ynuosa.json","https://mirrorz.org/static/json/legacy/xjtu.json","https://mirrorz.org/static/json/legacy/xtom.json","https://mirrorz.org/static/json/legacy/xtom-hk.json","https://mirrorz.org/static/json/legacy/xtom-de.json","https://mirrorz.org/static/json/legacy/xtom-nl.json","https://mirrorz.org/static/json/legacy/xtom-ee.json","https://mirrorz.org/static/json/legacy/xtom-jp.json","https://mirrorz.org/static/json/legacy/xtom-au.json","https://mirrorz.org/static/json/legacy/xtom-sg.json","https://mirrorz.org/static/json/legacy/wsyu.json","https://mirrorz.org/static/json/legacy/bupt.json","https://mirrorz.org/static/json/legacy/njtech.json","https://mirrorz.org/static/json/legacy/geekpie.json","https://mirrorz.org/static/json/legacy/qlut.json","https://mirrorz.org/static/json/legacy/nyist.json","https://mirrorz.org/static/json/legacy/jcut.json","https://mirrorz.org/static/json/legacy/nwafu.json","https://mirrors.ustc.edu.cn/static/json/mirrorz.json","https://mirror.sjtu.edu.cn/mirrorz/siyuan.json","https://mirrors.sjtug.sjtu.edu.cn/mirrorz/zhiyuan.json","https://mirrors.sustech.edu.cn/mirrorz/mirrorz.json","https://mirror.iscas.ac.cn/.mirrorz/mirrorz.json","https://linux.xidian.edu.cn/mirrors/status.json","https://mirrors.zju.edu.cn/api/mirrorz.json","https://mirrors.sdu.edu.cn/mirrorz.json","https://mirrors.scau.edu.cn/mirrorz.d.json","https://uestclug.org/mirrors-status/mirrorz.d.json","https://mirror.lzu.edu.cn/.mirrorz.json","https://mirrors.jlu.edu.cn/_api/mirrorz.json","https://mirrors.cqupt.edu.cn/api/mirrorz.json"]
map = {}
res = {}
def check_curl():
global CURL_VERSION
try:
res = subprocess.run(['curl', '--version'], stdout=subprocess.PIPE)
out = res.stdout.decode('utf-8')
CURL_VERSION = out.split()[1]
print(out)
return 0
except:
print("No curl found!")
return -1
def site_info(url):
user_agent = 'oh-my-mirrorz/%s (+https://github.com/mirrorz-org/oh-my-mirrorz) %s %s' % (VERSION, UA_URL, "urllib/" + urllib.request.__version__)
headers = {
'User-Agent': user_agent
}
request = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(request, timeout=10) as response:
return json.loads(response.read().decode('utf-8'))
def speed_test(url, args):
opt = '-qs'
if args.ipv4:
opt += '4'
elif args.ipv6:
opt += '6'
res = subprocess.run(['curl', opt, '-o', os.devnull, '-w', '%{http_code} %{speed_download}',
'-m'+str(args.time), '-A', 'oh-my-mirrorz/%s (+https://github.com/mirrorz-org/oh-my-mirrorz) %s curl/%s' % (VERSION, UA_URL, CURL_VERSION), url], stdout=subprocess.PIPE)
code, speed = res.stdout.decode('utf-8').split()
return int(code), float(speed)
def human_readable_speed(speed):
scale = ['B/s', 'KiB/s', 'MiB/s', 'GiB/s', 'TiB/s']
i = 0
while (speed > 1024.0):
i += 1
speed /= 1024.0
return f'{speed:.2f} {scale[i]}'
def main():
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument("-4", "--ipv4", help="IPv4 only when speed testing", action="store_true")
group.add_argument("-6", "--ipv6", help="IPv6 only when speed testing", action="store_true")
parser.add_argument("-t", "--time", type=int, default=5, choices=[3, 5, 10, 30, 60], help="Duration of a speed test for one mirror (default: %(default)d)")
args = parser.parse_args()
if check_curl() != 0:
exit(-1)
for url in mirrors:
try:
map[url] = site_info(url)
print('Loaded', map[url]['site']['abbr'], ':', map[url]['site']['url'])
except:
print('! Failed to load', url)
pass
print() # one empty line to separate metadata and speedtest
for _, v in map.items():
uri_list = []
if 'big' in v['site']:
uri_list.append(v['site']['big'])
for r, u in big.items():
for m in v['mirrors']:
if m['cname'] == r:
uri_list.append(m['url'] + u)
if len(uri_list) == 0:
print('! No big file found for', v['site']['abbr'], v['site']['url'])
continue
for uri in uri_list:
res[v['site']['abbr']] = 0
print('Speed testing', v['site']['abbr'], uri if uri.startswith("http") else v['site']['url'] + uri, '... ', end='', flush=True)
code, speed = speed_test(v['site']['url'] + uri, args)
if code != 200:
print('HTTP Code', code, 'Speed', human_readable_speed(speed))
else:
print(human_readable_speed(speed))
res[v['site']['abbr']] = speed
break
print() # one empty line to separate speedtest and result
print('RANK', 'ABBR', 'SPEED', sep='\t\t')
for i, (k, v) in enumerate(sorted(res.items(), key = lambda x: x[1], reverse=True)):
print(f'{i:02d}:', k, human_readable_speed(v), sep='\t\t')
if __name__ == '__main__':
main()
镜像源上游
镜像源上游是指镜像源的原始数据来源,通常是一个或多个官方源。镜像源会定期从这些上游源同步数据,以确保提供最新的软件包和更新。
在测速后,CQMU Mirror选择使用 NYISY
USTC
CQU
TUNA
BFSU
QLUT
作为上游镜像。为了避免误差,还使用了不同运营商但同在校园网下的工作室服务器。
构建前的准备工作
前置知识
- 计算机网络基础
- Linux中级知识
- VMware等虚拟化技术
- Python基础、SQL基础、前端基础
- Redis缓存及其集群控制
- 信息安全管理
- 硬盘分区及引导设置
测试环境
操作系统:CentOS Stream9-20250414
安装与配置SSH链接
在实际的操作系统控制中,大多数情况是在内网环境下进行操作,SSH连接是最常用的远程连接方式。
此处待完善
整合进 Linux/SSH配置
配置CentOS Stream软件包镜像
提示
这里是 CERNET教育网联合镜像配置,阿里云/腾讯云/华为云等类似,请自行参考相关文档。
CentOS Stream 9 默认启用了包管理工具 dnf,其是 yum 包管理工具的替代品。dnf 与 yum 大部分的命令都是通用的,dnf 也使用 /etc/yum.repos.d/
进行镜像配置。
CentOS Stream 9 中源被整合入两个文件 centos.repo
和 centos-addons.repo
,由于文件中不包含 baseurl 字段,需要手动插入,通过文本替换修改源的方法较为复杂,也可以选择直接复制最后的替换结果覆盖源文件。
将这段代码保存为一个文件,例如
update_mirror.pl
。
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
my $mirrors = 'https://mirrors.cernet.edu.cn/centos-stream';
if (@ARGV < 1) {
die "Usage: $0 <filename1> <filename2> ...\n";
}
while (my $filename = shift @ARGV) {
my $backup_filename = $filename . '.bak';
rename $filename, $backup_filename;
open my $input, "<", $backup_filename;
open my $output, ">", $filename;
while (<$input>) {
s/^metalink/# metalink/;
if (m/^name/) {
my (undef, $repo, $arch) = split /-/;
$repo =~ s/^\s+|\s+$//g;
($arch = defined $arch ? lc($arch) : '') =~ s/^\s+|\s+$//g;
if ($repo =~ /^Extras/) {
$_ .= "baseurl=${mirrors}/SIGs/\$releasever-stream/extras" . ($arch eq 'source' ? "/${arch}/" : "/\$basearch/") . "extras-common\n";
} else {
$_ .= "baseurl=${mirrors}/\$releasever-stream/$repo" . ($arch eq 'source' ? "/" : "/\$basearch/") . ($arch ne '' ? "${arch}/tree/" : "os") . "\n";
}
}
print $output $_;
}
}
然后,在命令行中使用以下命令来执行它:
sudo perl /tmp/update_mirror.pl /etc/yum.repos.d/centos*.repo
安装 autodie
Perl 模块
这个错误表明你的系统缺少 Perl 模块 autodie,导致 /tmp/update_mirror.pl 脚本无法运行。以下是解决方法:
Can't locate autodie.pm in @INC (you may need to install the autodie module) (@INC contains: /usr/local/lib64/perl5/5.32 /usr/local/share/perl5/5.32 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5) at /tmp/update_mirror.pl line 5. BEGIN failed--compilation aborted at /tmp/update_mirror.pl line 5.
sudo dnf install perl-autodie
重建缓存
sudo dnf clean all && sudo dnf makecache
重要
注意,如果需要启用其中一些 repo,需要将其中的 enabled=0 改为 enabled=1。
注:截至 2023-05-16,并未在官方源与镜像源中发现 nfv-source,建议不要开启 nfv-source。
验证
看到是baseurl是 https://mirrors.cernet.edu.cn/centos-stream/ 即可
cat /etc/yum.repos.d/centos.repo
cat /etc/yum.repos.d/centos-addons.repo
镜像站架构
网络架构
镜像站点是异构的。单个镜像难以提供所有镜像,因此会出现差异。对于终端用户来说,这不是一个好的体验,因为他们需要搜索可用的镜像。为了简化操作,MirrorZ 旨在包含所有镜像,因此需要一个统一的界面。
也就是说,如果你想你的镜像站点在 MirrorZ 上显示,你需要提供一个 JSON 文件,包含你镜像站点的基本信息和镜像列表。你可以使用
mirrorz.json
作为模板,修改其中的内容来适应你的镜像站点。
数据结构由 mirrorz.json
定义:
{
"version": 1.7,
"site": {
"url": "https://example.org",
"logo": "https://example.org/img/logo.svg",
"logo_darkmode": "https://example.org/img/logo-white.svg",
"abbr": "EXAMPLE",
"name": "样例镜像站",
"homepage": "https://blog.example.org",
"issue": "https://github.com/example/issues",
"request": "https://github.com/example/mirror-request",
"email": "admin@example.com",
"group": "QQ: 10086 and/or Telegram @something",
"disk": "may be any string showing usage of disk, e.g. usage",
"note": "may be any string; like speed limit or connection limit",
"big": "/speedtest/1000mb.bin",
"disable": false
},
"info": [
{
"distro": "Debian",
"category": "os",
"urls": [
{
"name": "10.7.0 (amd64, CD installer with xfce)",
"url": "/debian-cd/current/amd64/iso-cd/debian-10.7.0-amd64-xfce-CD-1.iso"
}
]
}
],
"mirrors": [
{
"cname": "AOSP",
"desc": "Android 操作系统源代码",
"url": "/AOSP",
"status": "S",
"help": "/help/AOSP/",
"upstream": "https://android.googlesource.com/mirror/manifest",
"size": "596G",
"disable": false
},
{
"cname": "AUR",
"desc": "Arch Linux 用户软件库",
"url": "https://aur.tuna.tsinghua.edu.cn",
"status": "S1612195849X1612196849N",
"help": "/help/AUR/",
"upstream": "https://aur.archlinux.org/"
}
]
}
镜像站后端们
得益于LUGers的大力贡献,现在已涌现出许许多多新的后端解决方案,以下是一些比较流行的镜像站后端同步解决方案:
TUNA Sync
使用 worker 和 manager 管理节点
快速入门:
本例中:
- 只镜像elvish项目
- 禁用了https
- 禁用了cgroup支持
- 快速上手
获取最新的TUNA Sync二进制包源文件
curl -O https://github.com/tuna/tunasync/releases/download/v0.9.3/tunasync-v0.9.3-linux-amd64-bin.tar.gz
服务器下不了就自己下用SSH传,版本号可能不一样,自行更改
解压
tar -zxvf tunasync-v0.9.3-linux-amd64-bin.tar.gz
将文件移动到标准二进制目录
sudo mv ~/tunasync ~/tunasynctl /usr/local/bin/
验证
which tunasync tunasynctl # 应返回 /usr/local/bin/tunasync 等路径
配置
mkdir ~/tunasync_demo
mkdir /tmp/tunasync
编辑~/tunasync_demo/worker.conf
[global]
name = "test_worker"
log_dir = "/tmp/tunasync/log/tunasync/{{.Name}}"
mirror_dir = "/tmp/tunasync"
concurrent = 10
interval = 120
[manager]
api_base = "http://localhost:12345"
token = ""
ca_cert = ""
[cgroup]
enable = false
base_path = "/sys/fs/cgroup"
group = "tunasync"
[server]
hostname = "localhost"
listen_addr = "127.0.0.1"
listen_port = 6000
ssl_cert = ""
ssl_key = ""
[[mirrors]]
name = "elvish"
provider = "rsync"
upstream = "rsync://rsync.elv.sh/elvish/"
use_ipv6 = false
编辑 ~/tunasync_demo/manager.conf
debug = false
[server]
addr = "127.0.0.1"
port = 12345
ssl_cert = ""
ssl_key = ""
[files]
db_type = "bolt"
db_file = "/tmp/tunasync/manager.db"
ca_cert = ""
除了 bolt 以外,还支持 badger、leveldb 和 redis 的数据库后端。对于 badger 和 leveldb,只需要修改 db_type。如果使用 redis 作为数据库后端,把 db_type 改为 redis,并把下面的 db_file 设为 redis 服务器的地址: redis://user:password@host:port/db_number
运行
tunasync manager --config ~/tunasync_demo/manager.conf
tunasync worker --config ~/tunasync_demo/worker.conf
本例中,镜像的数据在 /tmp/tunasync/
查看同步状态
tunasynctl list -p 12345 --all
tunasynctl 也支持配置文件。配置文件可以放在
/etc/tunasync/ctl.conf
或者~/.config/tunasync/ctl.conf
两个位置,后者可以覆盖前者的配置值。
配置文件内容为:
manager_addr = "127.0.0.1"
manager_port = 12345
ca_cert = ""
安全设置
worker
和 manager
之间用 http(s)
通信,如果你 worker 和 manager 都是在本机,那么没必要使用 https。此时 manager 就不指定 ssl_key 和 ssl_cert,留空;worker 的 ca_cert 留空,api_base 以 http:// 开头。
如果需要加密的通信,manager 需要指定 ssl_key 和 ssl_cert,worker 要指定 ca_cert,并且 api_base 应该是 https://
开头。
work.conf 示例配置
# /home/scripts in this example points to https://github.com/tuna/tunasync-scripts/
[global]
name = "mirror_worker"
log_dir = "/srv/tunasync/log/tunasync/{{.Name}}"
mirror_dir = "/srv/tunasync"
concurrent = 10
interval = 120
# ensure the exec user be add into `docker` group
[docker]
# in `command provider` can use docker_image and docker_volumes
enable = true
[manager]
api_base = "http://localhost:12345"
token = "some_token"
ca_cert = ""
[cgroup]
enable = false
base_path = "/sys/fs/cgroup"
group = "tunasync"
[server]
hostname = "localhost"
listen_addr = "127.0.0.1"
listen_port = 6000
ssl_cert = ""
ssl_key = ""
[[mirrors]]
name = "adobe-fonts"
interval = 1440
provider = "command"
upstream = "https://github.com/adobe-fonts"
command = "/home/scripts/adobe-fonts.sh"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "AdoptOpenJDK"
interval = 5760
provider = "command"
command = "/home/scripts/adoptopenjdk.py"
upstream = "https://adoptopenjdk.jfrog.io/adoptopenjdk"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "alpine"
provider = "rsync"
upstream = "rsync://rsync.alpinelinux.org/alpine/"
memory_limit = "256M"
[[mirrors]]
name = "anaconda"
provider = "command"
upstream = "https://repo.continuum.io/"
command = "/home/scripts/anaconda.py --delete"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
interval = 720
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "apache"
provider = "rsync"
upstream = "rsync://rsync.apache.org/apache-dist/"
use_ipv4 = true
rsync_options = [ "--delete-excluded" ]
memory_limit = "256M"
[[mirrors]]
name = "armbian"
provider = "two-stage-rsync"
stage1_profile = "debian"
upstream = "rsync://rsync.armbian.com/apt/"
memory_limit = "256M"
[[mirrors]]
name = "armbian-releases"
provider = "rsync"
stage1_profile = "debian"
upstream = "rsync://rsync.armbian.com/dl/"
memory_limit = "256M"
[[mirrors]]
name = "bananian"
provider = "command"
upstream = "https://dl.bananian.org/"
command = "/home/scripts/lftp.sh"
interval = 1440
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "bioconductor"
provider = "rsync"
upstream = "master.bioconductor.org:./"
rsync_options = [ "--rsh=ssh -i /root/id_rsa -o PasswordAuthentication=no -l sync" ]
exclude_file = "/etc/excludes/bioconductor.txt"
memory_limit = "256M"
[[mirrors]]
name = "blender"
provider = "rsync"
upstream = "rsync://mirrors.dotsrc.org/blender/"
rsync_options = [ "--delete-excluded" ]
exclude_file = "/etc/excludes/blender.txt"
interval = 1440
memory_limit = "256M"
[[mirrors]]
name = "chakra"
provider = "rsync"
upstream = "rsync://rsync.chakralinux.org/packages/"
memory_limit = "256M"
[[mirrors]]
name = "chakra-releases"
provider = "rsync"
upstream = "rsync://rsync.chakralinux.org/releases/"
memory_limit = "256M"
[[mirrors]]
name = "chef"
interval = 1440
provider = "command"
upstream = "https://packages.chef.io/repos"
command = "/home/scripts/chef.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "clickhouse"
interval = 2880
provider = "rsync"
upstream = "rsync://repo.yandex.ru/yandexrepo/clickhouse/"
exclude_file = "/etc/excludes/clickhouse.txt"
memory_limit = "256M"
[[mirrors]]
name = "clojars"
provider = "command"
upstream = "s3://clojars-repo-production/"
command = "/home/scripts/s3.sh"
docker_image = "tunathu/ftpsync:latest"
[mirrors.env]
TUNASYNC_S3_ENDPOINT = "https://s3.dualstack.us-east-2.amazonaws.com"
#TUNASYNC_S3_ENDPOINT = "https://s3.us-east-2.amazonaws.com"
TUNASYNC_AWS_OPTIONS = "--delete --exclude index.html"
[[mirrors]]
name = "CPAN"
provider = "rsync"
upstream = "rsync://cpan-rsync.perl.org/CPAN/"
memory_limit = "256M"
[[mirrors]]
name = "CRAN"
provider = "rsync"
upstream = "rsync://cran.r-project.org/CRAN/"
rsync_options = [ "--delete-excluded" ]
memory_limit = "256M"
[[mirrors]]
name = "CTAN"
provider = "rsync"
upstream = "rsync://mirrors.rit.edu/CTAN/"
memory_limit = "256M"
[[mirrors]]
name = "dart-pub"
provider = "command"
upstream = "https://pub.dev/api"
command = "/home/scripts/pub.sh"
interval = 30
docker_image = "tunathu/pub-mirror:latest"
[mirrors.env]
MIRROR_BASE_URL = "https://mirrors.tuna.tsinghua.edu.cn/dart-pub"
[[mirrors]]
name = "debian"
provider = "command"
upstream = "rsync://mirrors.tuna.tsinghua.edu.cn/debian/"
command = "/home/scripts/debian.sh sync:archive:debian"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
docker_image = "tunathu/ftpsync"
docker_volumes = [
"/etc/misc/ftpsync-debian.conf:/ftpsync/etc/ftpsync-debian.conf:ro",
"/log/ftpsync:/home/log/tunasync/ftpsync",
]
[mirrors.env]
FTPSYNC_LOG_DIR = "/home/log/tunasync/ftpsync"
[[mirrors]]
name = "docker-ce"
provider = "command"
upstream = "https://download.docker.com/"
command = "timeout 3h /home/scripts/docker-ce.py --workers 10 --fast-skip"
interval = 1440
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "ELK"
interval = 1440
provider = "command"
upstream = "https://packages.elastic.co"
command = "/home/scripts/ELK.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
# set environment varialbes
[mirrors.env]
WGET_OPTIONS = "-6"
[[mirrors]]
name = "elasticstack"
interval = 1440
provider = "command"
upstream = "https://artifacts.elastic.co/"
command = "/home/scripts/elastic.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "erlang-solutions"
interval = 1440
provider = "command"
upstream = "https://packages.erlang-solutions.com"
command = "/home/scripts/erlang.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "flutter"
interval = 1440
provider = "command"
upstream = "https://storage.googleapis.com/flutter_infra/"
command = "/home/scripts/flutter.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "github-release"
provider = "command"
upstream = "https://api.github.com/repos/"
command = "/home/scripts/github-release.py --workers 5"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
interval = 720
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
GITHUB_TOKEN = "xxxxx"
[[mirrors]]
name = "gitlab-ce"
interval = 1440
provider = "command"
upstream = "https://packages.gitlab.com/gitlab/gitlab-ce/"
command = "/home/scripts/gitlab-ce.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "gitlab-ee"
interval = 1440
provider = "command"
upstream = "https://packages.gitlab.com/gitlab/gitlab-ee/"
command = "/home/scripts/gitlab-ce.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "gitlab-runner"
interval = 1440
provider = "command"
upstream = "https://packages.gitlab.com/runner/gitlab-runner"
command = "/home/scripts/gitlab-runner.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "grafana"
interval = 1440
provider = "command"
upstream = "https://packages.grafana.com/oss"
command = "/home/scripts/grafana.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "hackage"
provider = "command"
command = "/home/scripts/hackage.sh"
upstream = "https://hackage.haskell.org/"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "homebrew-bottles"
provider = "command"
upstream = "https://homebrew.bintray.com"
command = "/home/scripts/linuxbrew-bottles.sh"
docker_image = "tunathu/homebrew-mirror"
# set environment varialbes
[mirrors.env]
HOMEBREW_REPO = "https://neomirrors.tuna.tsinghua.edu.cn/git/homebrew"
[[mirrors]]
name = "influxdata"
interval = 1440
provider = "command"
upstream = "https://repos.influxdata.com"
command = "/home/scripts/influxdata.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "kali"
provider = "two-stage-rsync"
stage1_profile = "debian"
upstream = "rsync://ftp.nluug.nl/kali/"
rsync_options = [ "--delete-excluded" ] # delete .~tmp~ folders
memory_limit = "256M"
[[mirrors]]
name = "kali-images"
provider = "rsync"
upstream = "rsync://ftp.nluug.nl/kali-images/"
rsync_options = [ "--delete-excluded" ] # delete .~tmp~ folders
memory_limit = "256M"
[[mirrors]]
name = "KaOS"
provider = "rsync"
upstream = "rsync://kaosx.tk/kaos/"
rsync_options = [ "--delete-excluded" ]
memory_limit = "256M"
[[mirrors]]
name = "kernel"
provider = "rsync"
upstream = "rsync://rsync.kernel.org/pub/linux/kernel/"
rsync_options = [ "--delete-excluded" ]
memory_limit = "256M"
[[mirrors]]
name = "kicad"
provider = "command"
upstream = "s3://kicad-downloads/"
command = "/home/scripts/s3.sh"
docker_image = "tunathu/ftpsync:latest"
[mirrors.env]
TUNASYNC_S3_ENDPOINT = "https://s3.cern.ch"
TUNASYNC_AWS_OPTIONS = "--delete --exclude index.html"
[[mirrors]]
name = "kodi"
provider = "rsync"
upstream = "rsync://mirror.yandex.ru/mirrors/xbmc/"
rsync_options = [ "--delete-excluded" ]
memory_limit = "256M"
use_ipv6 = true
[[mirrors]]
name = "kubernetes"
interval = 2880
provider = "command"
upstream = "http://packages.cloud.google.com"
command = "/home/scripts/kubernetes.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "linuxbrew-bottles"
provider = "command"
upstream = "https://linuxbrew.bintray.com"
command = "/home/scripts/linuxbrew-bottles.sh"
docker_image = "tunathu/homebrew-mirror"
# set environment varialbes
[mirrors.env]
RUN_LINUXBREW = "true"
HOMEBREW_REPO = "https://neomirrors.tuna.tsinghua.edu.cn/git/homebrew"
[[mirrors]]
name = "linuxmint"
provider = "two-stage-rsync"
stage1_profile = "debian"
upstream = "rsync://mirrors.kernel.org/linuxmint-packages/"
rsync_options = [ "--delete-excluded" ]
memory_limit = "256M"
[[mirrors]]
name = "lxc-images"
provider = "command"
upstream = "https://us.images.linuxcontainers.org/"
command = "/home/scripts/lxc-images.sh"
docker_image = "tunathu/tunasync-scripts:latest"
interval = 720
[[mirrors]]
name = "lyx"
provider = "command"
upstream = "ftp://ftp.lyx.org/pub/lyx/"
command = "/home/scripts/lftp.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
TUNASYNC_LFTP_OPTIONS = "--only-newer"
[[mirrors]]
name = "mongodb"
interval = 1440
provider = "command"
upstream = "https://repo.mongodb.org"
command = "/home/scripts/mongodb.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "msys2"
provider = "command"
upstream = "http://repo.msys2.org/"
command = "/home/scripts/lftp.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "mysql"
interval = 30
provider = "command"
upstream = "https://repo.mysql.com"
command = "/home/scripts/mysql.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
# set environment varialbes
[mirrors.env]
USE_IPV6 = "1"
[[mirrors]]
name = "nix"
interval = 1440
provider = "command"
upstream = "s3://nix-releases/nix/"
command = "/home/scripts/nix.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
MIRROR_BASE_URL = 'https://mirrors.tuna.tsinghua.edu.cn/nix/'
[[mirrors]]
name = "nix-channels"
interval = 300
provider = "command"
upstream = "https://nixos.org/channels"
command = "timeout 20h /home/scripts/nix-channels.py"
docker_image = "tunathu/nix-channels:latest"
docker_options = [
"--cpus", "20",
]
[[mirrors]]
name = "nodesource"
provider = "command"
upstream = "https://deb.nodesource.com/"
command = "/home/scripts/nodesource.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "openresty"
provider = "command"
upstream = "https://openresty.org/package/"
command = "/home/scripts/lftp.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
TUNASYNC_LFTP_OPTIONS = "--only-newer"
[[mirrors]]
name = "packagist"
provider = "command"
upstream = "http://packagist.org/"
command = "/home/scripts/packagist.sh"
interval = 1440
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "proxmox"
interval = 1440
provider = "command"
upstream = "http://download.proxmox.com"
command = "/home/scripts/proxmox.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "pypi"
provider = "command"
upstream = "https://pypi.python.org/"
command = "/home/scripts/pypi.sh"
docker_image = "tunathu/bandersnatch:latest"
interval = 5
[[mirrors]]
name = "qt"
provider = "rsync"
upstream = "rsync://master.qt-project.org/qt-all/"
exclude_file = "/etc/excludes/qt.txt"
rsync_options = [ "--delete-excluded" ]
memory_limit = "256M"
[[mirrors]]
name = "raspberrypi"
provider = "two-stage-rsync"
stage1_profile = "debian"
upstream = "rsync://apt-repo.raspberrypi.org/archive/debian/"
memory_limit = "256M"
[[mirrors]]
name = "raspbian-images"
interval = 5760
provider = "command"
upstream = "https://downloads.raspberrypi.org/"
command = "/home/scripts/lftp.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
TUNASYNC_LFTP_OPTIONS = "-x ^icons/$ -c --only-missing -v --no-perms"
[[mirrors]]
name = "raspbian"
provider = "two-stage-rsync"
stage1_profile = "debian"
upstream = "rsync://archive.raspbian.org/archive/"
rsync_options = [ "--delete-excluded" ] # delete .~tmp~ folders
memory_limit = "256M"
[[mirrors]]
name = "redhat"
provider = "rsync"
upstream = "rsync://ftp.redhat.com/redhat/"
rsync_options = [ "--delete-excluded" ]
memory_limit = "256M"
exclude_file = "/etc/excludes/redhat.txt"
interval = 1440
[mirrors.env]
RSYNC_PROXY="127.0.0.1:8123"
[[mirrors]]
name = "remi"
interval = 1440
provider = "command"
upstream = "rsync://rpms.remirepo.net"
command = "/home/scripts/remi.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "repo-ck"
provider = "command"
upstream = "http://repo-ck.com"
command = "/home/scripts/repo-ck.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "ros"
provider = "rsync"
upstream = "rsync://mirror.umd.edu/packages.ros.org/ros/"
memory_limit = "256M"
[[mirrors]]
name = "ros2"
interval = 1440
provider = "command"
upstream = "http://packages.ros.org/ros2"
command = "/home/scripts/ros2.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "rubygems"
provider = "command"
upstream = "https://rubygems.org"
command = "/home/scripts/rubygems.sh"
docker_image = "tunathu/rubygems-mirror"
interval = 60
# set environment varialbes
[mirrors.env]
INIT = "0"
[[mirrors]]
name = "rudder"
interval = 2880
provider = "command"
upstream = "https://repository.rudder.io"
command = "/home/scripts/rudder.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "rustup"
provider = "command"
upstream = "https://rustup.rs/"
command = "/home/scripts/rustup.sh"
interval = 1440
docker_image = "tunathu/rustup-mirror:latest"
docker_volumes = [
]
docker_options = [
]
[mirrors.env]
MIRROR_BASE_URL = "https://mirrors.tuna.tsinghua.edu.cn/rustup"
[[mirrors]]
name = "saltstack"
interval = 1440 # required on http://repo.saltstack.com/#mirror
provider = "command"
upstream = "s3://s3/"
command = "/home/scripts/s3.sh"
docker_image = "tunathu/ftpsync:latest"
[mirrors.env]
TUNASYNC_S3_ENDPOINT = "https://s3.repo.saltstack.com"
TUNASYNC_AWS_OPTIONS = "--delete --exact-timestamps"
[[mirrors]]
name = "solus"
provider = "rsync"
upstream = "rsync://mirrors.rit.edu/solus/"
rsync_options = [ "--exclude", "/shannon", "--exclude", "/unstable" ]
memory_limit = "256M"
[[mirrors]]
name = "stackage"
provider = "command"
command = "/home/scripts/stackage.py"
upstream = "https://www.stackage.org/"
docker_image = "tunathu/tunasync-scripts:latest"
# set environment varialbes
[mirrors.env]
GIT_COMMITTER_NAME = "TUNA mirrors"
GIT_COMMITTER_EMAIL = "mirrors@tuna.tsinghua.edu.cn"
[[mirrors]]
name = "steamos"
interval = 1440
provider = "command"
upstream = "http://repo.steampowered.com"
command = "/home/scripts/lftp.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
TUNASYNC_LFTP_OPTIONS = "--only-newer --exclude icons/ "
[[mirrors]]
name = "termux"
interval = 1440
provider = "command"
upstream = "https://dl.bintray.com/termux/termux-packages-24/"
command = "/home/scripts/termux.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "ubuntu"
provider = "two-stage-rsync"
stage1_profile = "debian"
upstream = "rsync://archive.ubuntu.com/ubuntu/"
rsync_options = [ "--delete-excluded" ]
memory_limit = "256M"
[[mirrors]]
name = "ubuntu-ports"
provider = "two-stage-rsync"
stage1_profile = "debian"
upstream = "rsync://ports.ubuntu.com/ubuntu-ports/"
rsync_options = [ "--delete-excluded" ]
exclude_file = "/etc/excludes/ubuntu-ports-exclude.txt"
memory_limit = "256M"
[[mirrors]]
name = "virtualbox"
interval = 1440
provider = "command"
upstream = "http://download.virtualbox.org/virtualbox"
command = "/home/scripts/virtualbox.sh"
size_pattern = "size-sum: ([0-9\\.]+[KMGTP])"
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "winehq"
provider = "command"
upstream = "ftp://ftp.winehq.org/pub/"
command = "/home/scripts/lftp.sh"
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
TUNASYNC_LFTP_OPTIONS = "-x wine-builds.old/ -x /\\..+"
[[mirrors]]
name = "zabbix"
provider = "rsync"
upstream = "rsync://repo.zabbix.com/mirror/"
rsync_options = [ "--delete-excluded", "--chmod=o+r,Do+x,Fa-x" ]
memory_limit = "256M"
[[mirrors]]
name = "AOSP"
interval = 720
provider = "command"
command = "/home/tunasync-scripts/aosp.sh"
upstream = "https://android.googlesource.com/mirror/manifest"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
REPO = "/usr/local/bin/aosp-repo"
REPO_URL = "https://mirrors.tuna.tsinghua.edu.cn/git/git-repo"
USE_BITMAP_INDEX = "1"
[[mirrors]]
name = "lineageOS"
interval = 720
provider = "command"
command = "/home/tunasync-scripts/aosp.sh"
upstream = "https://github.com/LineageOS/mirror"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
REPO = "/usr/local/bin/aosp-repo"
REPO_URL = "https://mirrors.tuna.tsinghua.edu.cn/git/git-repo"
USE_BITMAP_INDEX = "1"
[[mirrors]]
name = "chromiumos"
interval = 720
provider = "command"
command = "/home/tunasync-scripts/cros.sh"
upstream = "https://chromium.googlesource.com"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
fail_on_match = "fatal: "
docker_image = "tunathu/tunasync-scripts:latest"
[mirrors.env]
USE_BITMAP_INDEX = "1"
CONCURRENT_JOBS = "20"
[[mirrors]]
name = "crates.io-index.git"
provider = "command"
command = "/home/tunasync-scripts/git.sh"
upstream = "https://github.com/rust-lang/crates.io-index.git"
docker_image = "tunathu/tunasync-scripts:latest"
size_pattern = "size-pack: ([0-9\\.]+[KMGTP])"
[[mirrors]]
name = "flutter-sdk.git"
provider = "command"
command = "/home/tunasync-scripts/git.sh"
upstream = "git://github.com/flutter/flutter.git"
docker_image = "tunathu/tunasync-scripts:latest"
size_pattern = "size-pack: ([0-9\\.]+[KMGTP])"
[[mirrors]]
name = "gcc.git"
provider = "command"
command = "/home/tunasync-scripts/git.sh"
upstream = "git://gcc.gnu.org/git/gcc.git"
docker_image = "tunathu/tunasync-scripts:latest"
size_pattern = "size-pack: ([0-9\\.]+[KMGTP])"
[[mirrors]]
name = "gentoo-portage.git"
provider = "command"
command = "/home/tunasync-scripts/git.sh"
upstream = "git://github.com/gentoo-mirror/gentoo.git"
docker_image = "tunathu/tunasync-scripts:latest"
size_pattern = "size-pack: ([0-9\\.]+[KMGTP])"
[[mirrors]]
name = "git-repo"
provider = "command"
command = "/home/tunasync-scripts/git-repo.sh"
upstream = "https://gerrit.googlesource.com/git-repo"
size_pattern = "size-pack: ([0-9\\.]+[KMGTP])"
fail_on_match = "fatal: "
docker_image = "tunathu/tunasync-scripts:latest"
[[mirrors]]
name = "homebrew"
provider = "command"
command = "/home/tunasync-scripts/homebrew.sh"
upstream = "https://github.com/Homebrew"
docker_image = "tunathu/tunasync-scripts:latest"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
[[mirrors]]
name = "CocoaPods"
provider = "command"
command = "/home/tunasync-scripts/cocoapods.sh"
upstream = "https://github.com/CocoaPods"
docker_image = "tunathu/tunasync-scripts:latest"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
[[mirrors]]
name = "pybombs"
interval = 720
provider = "command"
command = "/home/tunasync-scripts/pybombs.sh"
upstream = "https://github.com/scateu/pybombs-mirror/"
docker_image = "tunathu/tunasync-scripts:latest"
docker_volumes = ["/home/pybombs-mirror:/opt/pybombs-mirror"]
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
[mirrors.env]
PYBOMBS_MIRROR_SCRIPT_PATH = "/opt/pybombs-mirror"
MIRROR_BASE_URL = "https://mirrors.tuna.tsinghua.edu.cn/pybombs"
[[mirrors]]
name = "llvm"
provider = "command"
command = "/home/tunasync-scripts/llvm.sh"
upstream = "https://git.llvm.org/git"
docker_image = "tunathu/tunasync-scripts:latest"
size_pattern = "Total size is ([0-9\\.]+[KMGTP]?)"
# vim: ft=toml
USTCLUG YUKI
中国科学技术大学Linux用户组开发的Go语言控制后端
镜像站前端们
TUNA Mirror Web
- 安装 Ruby
sudo dnf install ruby ruby-devel
验证
ruby --version
- 安装Node.js
dnf install git
NodeJS Releases多个版本管理器
export NVM_DIR="$HOME/.nvm" && (
git clone https://github.com/nvm-sh/nvm.git "$NVM_DIR"
cd "$NVM_DIR"
git checkout `git describe --abbrev=0 --tags --match "v[0-9]*" $(git rev-list --tags --max-count=1)`
) && \. "$NVM_DIR/nvm.sh"
# Download and install fnm:
curl -o- https://fnm.vercel.app/install | bash
# Download and install Node.js:
fnm install 22
# Verify the Node.js version:
node -v # Should print "v22.15.0".
# Verify npm version:
npm -v # Should print "10.9.2".
ZJU Mirror
浙江大学的前端编写较为复杂,针对HTTP请求设置代理检测、IP检测与请求头(浏览器)检测,安全性高。由专业UI设计师和前端开发组成,设计恰当。
CQMU Mirror大部分灵感来自于此,查看前端仓库与开发方法。
SDU Mirror
由山东大学开发的镜像站前端页面,组件自行封装,简约大气。
SUSTech-CRA ccse-mirror-web
南方科技大学镜像站前端是基于Vue3编写的,相对容易上手,而且开发指南清晰易懂,不再赘述。
CQU Mirror
重庆大学使用NuxtJS自行构建开发的镜像站前端界面,小清新。
XJTUANA Mirror
西安交通大学软件镜像站
CQUPT Mirror
重庆邮电大学开发的前端也是基于Vue3编写的,对我们学校大二、大三动手能力强的同学比较友好,可以看看,启动与开发也十分简单
- 导入git仓库
git clone
- 安装pnpm
npm install -g pnpm
- 安装依赖
pnpm install
- 预览
pnpm dev
技术选型之前端
fancy-ngx
负载均衡器
框架技术与静态生成
技术选型之后端
反向代理
负载均衡
RAID磁盘阵列
RaidZ2