Ubuntu: Как поддержать в актуальном состоянии компьютер без интернета?
Начну с того, что есть компьютер с Ubuntu, который не подключен к интернету. Вопрос «почему?» оставим за кадром.
И у меня, как и у любого линуксоида, есть желание иметь на нем свежее ПО.
Встает вопрос: как обновляться или устанавливать новое ПО на этот комп без больших трудозатрат?
Уточню, что есть некий второй компьютер с выходом в интернет. Но он может отличаться архитектурой (i386/amd64), версией убунту, да и не убунту это может быть — так что скопировать /var/cache/apt/ не вариант.
Вариантов у нас несколько:
- Сделать полное зеркало репозитория (например, при помощи apt-mirror), принести на каком-то носителе и обновляться с него.
- Качать deb-пакеты в интернете со всеми зависимостями вручную.
- Попробовать что-нибудь придумать поинтересней, т.е. самим реализовать то, что нам нужно.
Давайте рассмотрим за и против для этих вариантов. И в результате всё равно напишем своё!
Всё описанное в статье проверено на Ubuntu hardy/karmic/lucid/maverick, но по идее должно работать на любом дистрибутиве на основе Debian.
Полное зеркало репозитория
При этом подходе репозитории полностью выкачиваются на флешку (внешний хард) при помощи программы подобной apt-mirror.
За:
- Всё очень просто и легко настраивается
Против:
- Требуется очень много места, особенно если хочется зеркалировать не только официальный репозиторий, но и какие-нибудь ppa на launchpad’е.
- Требуется толстый канал в интернет или долго ждать.
- Скачиваются гигабайты софта, который может никогда и не потребоваться.
Качать deb-пакеты вручную
За:
- Качается только те пакеты, которые необходимы.
- Сокращается объем трафика.
- Занимает мало места на носителе — вместо дорогого внешнего харда можно использовать дешевую мелкую флешку.
Против:
- Очень трудоёмко и сложно отследить все зависимости.
Придумываем свой вариант
Хотелось бы объединить преимущества первого и второго варианта. Наша поделка должна:
- легко настраиваться,
- не требовать много места,
- иметь простой алгоритм работы,
- качать только то, что нужно.
Что нам надо:
- Возможность закачки заголовков (файлов Release, Packages и необходимой структуры каталогов).
- Прием «заявки» от клиента на необходимые пакеты, формирование очереди загрузки.
- Возможность загрузки из интернета на втором компе наиболее простым способом.
Но поскольку этим требованиям не удовлетворяет ни один проект, найденный мною в сети. Поэтому было принято решение написать что-то свое.
Что у нас есть:
- А — комп с доступом в интернет,
- Б — комп без интернета,
- флешка — носитель с набором скриптов и данными, перетаскиваемый между А и Б.
Придуман следующий алгоритм:
- На базе конфига при помощи скрипта gen_sources.pl создаётся sources.list, который прописывается на компе Б и указывает на структуру папок на флешке.
- Скрипт apt-mirror.pl выкачивает структуру папок и заголовки репозитория с сети учитывая нужные: карманы (от pocket в официальной документации, например: hardy lucid), архитектуры, языки, компоненты (например: main universe). Данные сохраняются на флешке.
- На компе Б, подключив флешку, делаем
sudo apt-get update
- На компе Б при помощи скрипта apt-get-offline.pl формируем задание загрузки (файл download.info) командами типа
./apt-get-offline.pl dist-upgrade
./apt-get-offline.pl install some-package
- Идем к компу А, вставляем флешку и запускаем
./fill_pool.pl
. Скрипт сливает с интернета файлы, которые заданы в файле download.info. - На компе Б выполняем те же команды, что и на шаге 4, только указывая уже не
apt-get-offline.pl
, аsudo apt-get
- Если всё прошло удачно, то удаляем файл download.info. В противном случае повторяем шаги 4-7.
Для очередного обновления системы повторяем шаги 2-7.
Ограничения
- Все операции лучше проводить находясь в той директории флешки, где лежат скрипты.
- Структура папок и все данные создаются в тойже папке.
- Простой, но кривенький формат файла конфигурации.
- Файл download.info надо удалять вручную. Это сделано из-за того, что не всегда закачка происходит с первого раза (обрывы связи) — приходится запускать fill_pool.pl повторно.
- Не получится обновить дистрибутив до следующей стабильной версии, т.к. программе do-release-upgrade требуется подключение к интернету.
Формат конфигурационного файла
Разберем формат на примере одной строки:
ubuntu^http://mirror.yandex.ru/ubuntu^1^lucid lucid-updates^main restricted^i386 amd64^ru de^src
Строка состоит из 7 обязательных полей, разделенных символом “^”, и одного необязательного.
Первое поле (ubuntu) — название репозитория, уникальное имя для папки, в которую будет сохраняться данные этого репозитория.
Второе поле (URL) — адрес репозитория (http(s)/ftp).
Третье поле (1) — число директорий в URL, требуется для передачи ключу –cut-dirs программы wget.
Четвертое поле (lucid lucid-updates) — карманы, один или несколько, разделяются пробелом.
Пятое поле (main restricted) — компоненты, один или несколько, разделяются пробелом.
Шестое поле (i386 amd64) — архитектуры, одна или несколько, разделяются пробелом.
Седьмое поле (ru de) — языки, одна или несколько, разделяются пробелом.
Восьмое поле (src) — необязательное, указывает на то, что надо скачать описание для deb-src.
Почитать:
1. Структура репозитория Debian
2. apt-mirror
PS. есть идеи проекта на эту тематику покрупнее, но то идеи, а это уже работает!
Исходный код скриптов на Perl:
apt-mirror.pl
#!/usr/bin/perl use Cwd; use strict; #************************ # Changelog # v.1.0 - initial release # v.1.1 - added remove of wget-logs # v.1.2 - added source support (deb-src) #************************ my $main_dir; $main_dir = getcwd; #read config open CONFIG, "<apt-repos.conf"; my ($i, @repos); $i = 0; while (){ chomp($_); next if ($_ eq ""); @{$repos[$i]} = split (/\^/, $_); $i++; } close CONFIG; foreach (@repos){ my @repo; @repo = @{$_}; my $dstdir = "$main_dir/$repo[0]"; my $repurl = $repo[1]; my $cutdirs = $repo[2]; my @dists = split (/ /,$repo[3]); my @pockets = split (/ /,$repo[4]); my @archs = split (/ /,$repo[5]); my @langs = split (/ /,$repo[6]); my $has_src = $repo[7]; my ($dist, $pocket, $arch, $lang); foreach $dist (@dists){ #load Release system "mkdir -p '$dstdir/dists/$dist'"; chdir "$dstdir" or die "\nCannot chdir to '$dstdir'"; system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/Release"; system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/Release.gpg"; foreach $pocket (@pockets){ system "mkdir -p '$dstdir/dists/$dist/$pocket'"; chdir $dstdir or die "\nCannot chdir to '$dstdir'"; # load pocket (meaning component) foreach $arch (@archs){ chdir $dstdir or die "\nCannot chdir to '$dstdir'"; system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/Contents-$arch.gz"; system "wget -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/$pocket/binary-$arch/Packages.bz2"; system "wget -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/$pocket/binary-$arch/Packages.gz"; system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/$pocket/binary-$arch/Release"; if ($has_src ne ""){ system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/$pocket/source/Release"; system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/$pocket/source/Sources.bz2"; system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/$pocket/source/Sources.gz"; } } foreach $lang (@langs){ chdir $dstdir or die "\nCannot chdir to '$dstdir'"; system "mkdir -p '$dstdir/dists/$dist/$pocket/i18n/'"; system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/$pocket/i18n/Translation-$lang"; system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/$pocket/i18n/Translation-$lang.bz2"; system "wget -b -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/dists/$dist/$pocket/i18n/Translation-$lang.gz"; } } } # remove wget-logs system "rm -rf $dstdir/wget-log*"; } print "\n****************\nAll done!\nNow run on your target machine 'apt-get update', 'apt-get --print-uris ACTION'! Then download this files and put it in pool.\n";
fill_pool.pl
#!/usr/bin/perl use Cwd; my $main_dir; $main_dir = getcwd; #read config open CONFIG, "<apt-repos.conf"; my ($i, @repos); $i = 0; while (){ chomp($_); next if ($_ eq ""); @{$repos[$i]} = split (/\^/, $_); #print "name=$repos[$i][0]\nurl=$repos[$i][1]\ncut-dirs=$repos[$i][2]\ndists=$repos[$i][3]\npockets=$repos[$i][4]\narchs=$repos[$i][5]\nlangs=$repos[$i][6]\n"; $i++; } close CONFIG; open (IN,"<download.info"); while ($line = ){ if ($line =~ m{/([\w-]*)(/pool/.*\.deb)'}){ $name = $1; $url= $2; foreach (@repos){ my @repo; @repo = @{$_}; if ($name eq $repo[0]){ my $dstdir = "$main_dir/$repo[0]"; my $repurl = $repo[1]; my $cutdirs = $repo[2]; my @dists = split (/ /,$repo[3]); my @pockets = split (/ /,$repo[4]); my @archs = split (/ /,$repo[5]); my @langs = split (/ /,$repo[6]); system "mkdir -p '$dstdir/pool'"; chdir "$dstdir" or die "\nCannot chdir to '$dstdir'"; system "wget -mcNr -nH --cut-dirs=$cutdirs --relative -l 1 $repurl/$url"; } } } } close IN;
apt-get-offline.pl
#!/usr/bin/perl $\ = " "; system "apt-get -y --print-uris @ARGV >> download.info";
gen_sources.pl
#!/usr/bin/perl use Cwd; my $main_dir; $main_dir = getcwd; #read config open CONFIG, "<apt-repos.conf"; my ($i, @repos); $i = 0; while (){ chomp($_); next if ($_ eq ""); @{$repos[$i]} = split (/\^/, $_); $i++; } close CONFIG; foreach (@repos){ my @repo; @repo = @{$_}; my $dstdir = "$main_dir/$repo[0]"; my $repurl = $repo[1]; my $cutdirs = $repo[2]; my @dists = split (/ /,$repo[3]); my @pockets = split (/ /,$repo[4]); my @archs = split (/ /,$repo[5]); my @langs = split (/ /,$repo[6]); my $has_src = $repo[7]; print "deb file://$dstdir $_ $repo[4]\n" foreach (@dists); if ($has_src ne "") { print "deb-src file://$dstdir $_ $repo[4]\n" foreach (@dists); } }
пример apt-repos.conf
ubuntu^http://mirror.yandex.ru/ubuntu^1^lucid lucid-updates lucid-proposed lucid-backports^main restricted universe multiverse^i386^ru^src
ubuntu-security^http://security.ubuntu.com/ubuntu^1^lucid-security^main restricted^i386^ru^src
kubuntu-ppa^http://ppa.launchpad.net/kubuntu-ppa/ppa/ubuntu^3^lucid^main^i386^ru^src