Ubuntu Weekly Recipe

第489回ARM向けバックポートリポジトリをaptlyで作る

第485回で紹介したaptlyを使えば、簡単に自分専用のリポジトリを構築できます。さらに第487回の方法で任意のアーキテクチャーのパッケージを作ることができます。今回は応用編ということで、ARM用のバイナリパッケージを提供するバックポートリポジトリを作ってみましょう。

リポジトリの作成

第487回でバイナリパッケージの作成方法を学びました。次はそれを公開するリポジトリをaptlyを用いて作成しましょう。aptlyのインストール方法や基本的な使い方は第485回を参照してください。

aptlyで作ったリポジトリのフロントエンド部分(HTTPサーバー部分)はNginxを使うことにします。第485回ではaptlyが作ったユーザーローカルのディレクトリ~/.aptly/public/をリポジトリとして公開していましたが、NginxなどのHTTPサーバーから外部に公開するのであれば/srv以下のディレクトリに置くのが無難でしょう。よってaptlyを実行するユーザーのホームディレクトリに~/.aptly.confを次のような内容で作成します。もしくは/etc/aptly.confとしてシステムグローバルな設定にしてもかまいません。

{
  "architectures": [ "all", "source", "armhf" ],
  "FileSystemPublishEndpoints": {
    "pomera": {
      "rootDir": "/srv/aptly"
    }
  }
}

「pomera」として公開するリポジトリを/srv/aptly以下に作るよう設定しています。当然のことながらaptlyコマンドを実行するユーザーは該当ディレクトリへの書き込み権限が必要になります。architecturesにはアーキテクチャー非依存とソースパッケージ、それにarmhfを記述していますが、限定しないのであれば空でもかまいません。この設定は個々のaptlyコマンドの-architectureオプションで上書き可能です。

またローカルリポジトリも作りましょう。

$ aptly repo create -distribution=xenial-backports -component=main xenial-armhf

-distributionは公開時のディストリビューション名であり、Ubuntuのリポジトリ/etc/apt/sources.listでいうところの「xenial」「zesty」です。-componentはUbuntuでいうところのmain・universeのようにパッケージの種別ごとに別のディレクトリにしたいときに使用します。最後の「xenial-armhf」がaptlyにおけるリポジトリの名前になります。

パッケージのリポジトリへの取り込み

aptlyのローカルリポジトリにパッケージを取り込みます。第487回でも説明したように、パッケージは「ソースパッケージ」「バイナリパッケージ」の二種類にわかれます。またそれぞれの種類のパッケージは複数のファイルから構成されます。aptlyはこれらのパッケージを取り込むことでリポジトリを構築するのです。

個々のパッケージを構成するファイル群は.changesファイル」としてリストアップされます。.changesファイルにはファイル名だけでなくそのハッシュ値も記録されているために、そのファイルにGPGによる署名を加えるだけでパッケージを構成するファイル群の正当性を担保できるわけです。第487回では話を簡単にするために特に署名のことは考慮せずdebファイルを直接取り込みました。しかしなら単一のソースパッケージから複数のバイナリパッケージを生成する場合は.changesファイルを用いて一度に取り込めたほうが便利ですし、よりまっとうな方法と言えます。今回はその方法を紹介しましょう。

まずは.changesファイルに署名を行う人のGPG公開鍵をaptlyユーザーの鍵束に取り込んでおきます。

(パッケージに署名を行うマシン上で、公開鍵をエクスポートする)
$ gpg --export --armor --output shibata.asc shibata@example.com
$ file shibata.asc
shibata.asc: PGP public key block Public-Key (old)

上記の手順でエクスポートした公開鍵ファイルshibata.ascをaptlyが動くマシン上に何らかの安全な経路を使ってコピーしておきます[1]⁠。もちろん鍵サーバーを経由する方法でもかまいません。

aptlyが動いているマシン上で、この公開鍵を取り込みます。aptly自身はaptlyコマンドを実行したユーザーの~/.gnupg/trustedkeys.gpgを鍵束として使用しますので、--keyringオプションで指定する必要があります。

$ gpg --no-default-keyring --keyring trustedkeys.gpg --import shibata.asc
gpg: keyring `/home/aptly/.gnupg/trustedkeys.gpg' created
gpg: key DF3CCF34: public key "Mitsuya Shibata <shibata@example.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1  (RSA: 1)
gpg: public key of ultimately trusted key 73C2FAC3 not found
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u

最後に取り込んだGPG公開鍵で署名された.changesファイルをリポジトリに取り込んでみましょう。第487回のやり方に沿ってパッケージをビルドしていれば、ソースパッケージについてはGPG公開鍵による署名が行われているはずです。このソースパッケージをaptlyを実行するマシン上にコピーし、次のコマンドを実行してください。

$ aptly repo include -repo=xenial-armhf tmux_2.5-3build1~ubuntu16.04.1_source.changes
gpgv: Signature made Sat Sep  2 12:18:40 2017 JST using RSA key ID DF3CCF34
gpgv: Good signature from "Mitsuya Shibata <shibata@example.com>"
Loading repository xenial-armhf for changes file tmux_2.5-3build1~ubuntu16.04.1_source.changes...
[+] tmux_2.5-3build1~ubuntu16.04.1_source added

-repoオプションで取り込むリポジトリ名を指定します。省略した場合は、.changesファイルのDistributionフィールドの名前が使われます。また.changesファイルの代わりにディレクトリを指定した場合は、そのディレクトリ内部の.changesファイルを順番にチェックしていきます。

-uploaders-fileオプションを使うと、ユーザー単位でリポジトリへのアップロード権限を細かく設定するJSONファイルを指定できます。詳細は公式ドキュメントの「UPLOADERS.JSON」の項目を参照してください。このオプションを指定しない場合は、すべてのユーザーが自由にパッケージを取り込める状態となります。ただしその場合もオプションで無効化しない限り、.changesファイルの署名のチェックは行います。ちなみにaptly repo createのときにも指定できます。

同じ名前・アーキテクチャー・バージョンのファイルを同じリポジトリ上に取り込みたい場合は、明示的に-force-replaceオプションを付けなくてはなりません。

リポジトリにパッケージを取り込むことに成功したら、オリジナルのパッケージファイルは削除されます。もし残したい場合は-no-remove-filesオプションを付けてください。

aptly repo showコマンドで、そのローカルリポジトリ上にあるパッケージのリストを得られます。

$ aptly repo show --with-packages=true xenial-armhf
Name: xenial-armhf
Comment:
Default Distribution: xenial-backports
Default Component: main
Number of packages: 1
Packages:
  tmux_2.5-3build1~ubuntu16.04.1_source

バイナリパッケージへの署名

一般的なDebianパッケージの開発フローにおいて、パッケージのメンテナーがバイナリパッケージに署名を行うことはありません。バイナリパッケージへの署名はパッケージをビルドするサーバーが自動的に行うためです。しかしながらaptlyで作ったローカルリポジトリーに自分でビルドしたバイナリパッケージを取り込む場合は、自分で署名を行う必要が出てきます。

もっとも手っ取り早い方法はdevscriptsパッケージに所属するdebsignコマンドを使うことです。

$ debsign tmux_2.5-3build1~ubuntu16.04.1_armhf.changes

これだけで.changesファイルが署名済みのファイルに置き換わります。オプションなどの詳しい使い方についてはdebsignのマニュアルページを参照してください。

あとはソースパッケージと同じように.changesファイルを指定すれば、バイナリパッケージ一式がローカルリポジトリに取り込まれます。

$ aptly repo include -repo=xenial-armhf tmux_2.5-3build1~ubuntu16.04.1_armhf.changes
gpgv: Signature made Mon Sep 18 18:53:32 2017 JST using RSA key ID DF3CCF34
gpgv: Good signature from "Mitsuya Shibata <shibata@example.com>"
Loading repository xenial-armhf for changes file tmux_2.5-3build1~ubuntu16.04.1_armhf.changes...
[+] tmux-dbgsym_2.5-3build1~ubuntu16.04.1_armhf added
[+] tmux_2.5-3build1~ubuntu16.04.1_armhf added

aptly repo showコマンドを使うと、バイナリパッケージも追加されていることがわかります。

$ aptly repo show --with-packages=true xenial-armhf
Name: xenial-armhf
Comment:
Default Distribution: xenial-backports
Default Component: main
Number of packages: 3
Packages:
  tmux_2.5-3build1~ubuntu16.04.1_armhf
  tmux_2.5-3build1~ubuntu16.04.1_source
  tmux-dbgsym_2.5-3build1~ubuntu16.04.1_armhf

パッケージリポジトリを公開状態にする

ローカルリポジトリのデータを元にパッケージリポジトリを作成しましょう。第485回でも説明したように公開するパッケージリポジトリはリポジトリの流儀に沿ってHTTPでアクセス可能なディレクトリツリーです。

まずあらかじめ~/.aptly.confで指定した公開リポジトリ用のディレクトリを作っておきます。

$ sudo mkdir -p /srv/aptly
$ sudo chown aptly: /srv/aptly

さらにローカルリポジトリのスナップショットを作成します。そうすることでローカルリポジトリの状態と公開リポジトリの状態を独立して管理できます。

$ aptly snapshot create xenial-backports-20170918 from repo xenial-armhf

Snapshot xenial-backports-20170918 successfully created.
You can run 'aptly publish snapshot xenial-backports-20170918' to publish snapshot as Debian repository.

最後に作ったスナップショットを公開します。第485回と異なるのは公開するストレージ(エンドポイント)を明示的に指定することです。~/.aptly.confではFileSystemPublishEndpointsとしてpomeraを作成しましたので、エンドポイント名はfilesystem:pomeraになります。

$ aptly publish snapshot xenial-backports-20170918 filesystem:pomera:
Loading packages...
Generating metadata files and linking package files...
Finalizing metadata files...
Signing file 'Release' with gpg, please enter your passphrase when prompted:
Clearsigning file 'Release' with gpg, please enter your passphrase when prompted:

Snapshot xenial-backports-20170918 has been successfully published.
Please setup your webserver to serve directory '/srv/aptly' with autoindexing.
Now you can add following line to apt sources:
  deb http://your-server/ xenial-backports main
  deb-src http://your-server/ xenial-backports main
Don't forget to add your GPG key to apt with apt-key.

You can also use `aptly serve` to publish your repositories over HTTP quickly.

エンドポイントとしてローカルストレージ以外にAmazon S3やOpenStack Swiftといったクラウドストレージも指定可能です。また最後のコロンの後ろにパス名を指定することで、サブディレクトリの下にリポジトリを作ることもできます。

作られたディレクトリの中身は次のような構造になっています。

(メタデータ類)
$ ls /srv/aptly/dists/xenial-backports/
InRelease  Release  Release.gpg  main

(パッケージの実体)
$ ls /srv/aptly/pool/main/t/tmux/
tmux-dbgsym_2.5-3build1~ubuntu16.04.1_armhf.ddeb  tmux_2.5-3build1~ubuntu16.04.1.dsc        tmux_2.5.orig.tar.gz
tmux_2.5-3build1~ubuntu16.04.1.debian.tar.xz      tmux_2.5-3build1~ubuntu16.04.1_armhf.deb

たとえばjp.archive.ubuntu.comの同じディレクトリ見てみると、似たような構造になっていることがわかりますね。ちなみに個々のパッケージファイルは~/.aptly/以下のファイルに対するハードリンクになっているため、ローカルファイルシステムを用いてパッケージを公開するだけであればストレージの使用量は増えません。

Nginxを用いて外部からアクセスできるようにする

ここまでの状態だとローカルファイルシステム上にパッケージディレクトリが配置されているだけです。リポジトリとして運用するためには、これを外部からHTTPでアクセスできるようにしなくてはなりません。といっても難しい話ではなく、他の静的なコンテンツと同様にたとえばHTTPサーバーを使って見えるようにするだけです。

たとえばNginxを使う場合、次のような内容の設定ファイルを/etc/nginx/sites-avialable/aptlyとして用意し、/etc/nginx/sites-enabled/からシンボリックリンクをはることになるでしょう。server_name部分をリポジトリのドメイン名にしておきます。またautoindex onにしておくことで、リポジトリのディレクトリの中身の構造が見えるので何かあったときにユーザーにとっては便利です。ちなみにReleaseファイルなどからリポジトリに属するパッケージファイルのパスを辿れるため、リポジトリ上のパッケージのパスが見えてはいけない場合は別途何らかの対策が必要になります。もちろん単にアクセスするユーザーを制限したい場合は、Basic認証などの手法を使うというのもひとつの手です。

server {
       listen 80;
       listen [::]:80;

       server_name aptly.example.com;

       root /srv/aptly;

       location / {
               autoindex on;
       }
}

設定したらsudo service nginx configtestsudo systemctl restart nginxなどで設定のチェックとサーバーの再起動を行いましょう。ブラウザーでアクセスできることを確認したら、サーバー側の設定は完了です。

クライアント側ではリポジトリの公開鍵とURLを登録します。リポジトリの公開鍵は、aptlyを実行するユーザーがあらかじめエクスポートしておき、何らかの信頼できる経路でクライアント側がそれを取得できるようにしておきます。

$ gpg --export --armor --output aptly.asc aptly@example.com

一般的には上記ファイルをHTTPS経由で取得できる場所に置いておきます。もしくは鍵サーバー上に公開鍵を登録しておき、その鍵IDのみHTTPS上に記載するというのもひとつの手です。PPAなどはUbuntuの鍵サーバー上に公開鍵を登録し、HTTPS経由で鍵IDを取得する流れになっています。

あとはこのリポジトリ鍵を登録し、sources.listを設定します。ここでaptlyが動いているマシンのアドレスをaptly.example.comだとしています。

$ sudo apt-key add aptly.asc
$ echo "deb http://aptly.example.com/ xenial-backports main" | sudo tee /etc/apt/sources.list.d/aptly.list
$ sudo apt update
$ sudo apt policy tmux

先程登録したtmuxのバックポートパッケージのバージョンが表示されたら成功です。

パッケージのアップロードを簡単にする

パッケージをビルドするマシン(sbuildなどを実行するマシン)と公開するマシン(aptlyを実行するマシン)が異なる場合、.changesファイルをはじめとしたビルドした成果物を公開するマシンにコピーする必要があります。このときdputコマンドを使うと手間が省けて便利です。dputはDebianパッケージのメンテナーが新しいパッケージをアップロードキューに送ったり、UbuntuでもPPAでビルドキューに送る場合に使われています。

dput自体は必要なファイルを指定したサーバーに送るだけの単なるPythonスクリプトであり、/etc/dput.cfもしくは~/.dput.cfで設定すれば、簡単に任意のアップロードサーバーを追加できます。

たとえばaptly.example.comの/srv/aptly/queue/ディレクトリに、aptlyユーザーでscpを使ってポート2222を経由してアップロードするなら次のような設定になります。

[aptly]
login                   = aptly
fqdn                    = aptly.example.com
method                  = scp
ssh_config_options      =
  Port 2222
incoming                = /srv/aptly/queue
allow_dcut              = 1

dputコマンドに「対象マシン名」.changesファイルを指定すればアップロードできます。

$ dput aptly tmux_2.5-3build1~ubuntu16.04.1_source.changes
Checking signature on .changes
(中略)
Good signature on /home/shibata/backport/sign/tmux_2.5-3build1~ubuntu16.04.1.dsc.
Package includes an .orig.tar.gz file although the debian revision suggests
that it might not be required. Multiple uploads of the .orig.tar.gz may be
rejected by the upload queue management software.
Uploading to aptly (via scp to aptly.example.com):
tmux_2.5-3build1~ubuntu16.04.1.dsc                                                                           100% 2060     3.3MB/s   00:00
tmux_2.5.orig.tar.gz                                                                                         100%  465KB  62.9MB/s   00:00
tmux_2.5-3build1~ubuntu16.04.1.debian.tar.xz                                                                 100%   12KB  17.7MB/s   00:00
tmux_2.5-3build1~ubuntu16.04.1_source.changes                                                                100% 7162    12.4MB/s   00:00
Successfully uploaded packages.

署名済みかどうかもチェックしてくれるので便利です。ちなみにローカルで一度アップロードするとchangesファイル名.対象マシン名.uploadというファイルが作られて、多重アップロードを抑止します。もし同じファイルを強制的に再アップロードしたい場合は--forceオプションを付けてください。

aptly側でsystemd.timerやcronなどを利用して定期的に/srv/aptly/queueをチェックするようにしておけば、ローカル側からのアップロード処理だけで新しいリポジトリの公開までを自動化できます。もちろんaptlyのREST APIなどを用いてCIを間に挟むことも可能です。

おすすめ記事

記事・ニュース一覧