go-carbonのdebパッケージをsbuildとPPAでビルドした

はじめに

lomik/go-carbon: Golang implementation of Graphite/Carbon server with classic architecture: Agent -> Cache -> Persister のdebパッケージをsbuildとPPAでビルドしたときのメモです。

成果物は以下に有ります。

debianでのgoのパッケージング方針

に書いてあります。

Goのバイナリ実行ファイルを作る場合に依存ライブラリを別パッケージで作る方針になっていますが、現時点では個人的にはこれは賛同できないです。複数のバイナリパッケージがあるときに同じライブラリに依存するけど必要なバージョンが異なるケースがあり得るからです。

現状だとベンダリングとスタティックリンクで個々のバイナリパッケージ単独で完結するほうがお手軽なので、自作のdebパッケージではそうすることにしました。都合の良いことに go-carbon は dep を使ってベンダリングしており、依存ライブラリのソースは全て vendor ディレクトリに含まれています。

dh-golangとdh-make-golangをインストール

dh-golang は goのパッケージを作るための dh (debhelper) のアドオンです。 dh-make-golang はgoのパッケージのソースからdebパッケージを作成するのを自動化するツールです。

sudo apt install dh-golang dh-make-golang

dh-make-golangでdebパッケージのソースのベースを作成

dh-make-golang というツールを使えばdebianのgoのパッケージ方針に従ってパッケージを作成してくれるようなのですが、上記のように依存ライブラリの方針が違うので、debパッケージのソースを生成するところだけ使って、そこから先は手で編集することにしました。

mkdir -p ~/go-carbon-deb-work
cd !$

dh-make-golang ビルド対象のgoパッケージ名 でdebパッケージのソースが作成されます。

$ dh-make-golang github.com/lomik/go-carbon
2018/06/15 11:33:49 Downloading "github.com/lomik/go-carbon/..."
2018/06/15 11:33:52 Deleting upstream vendor/ directory, installing remaining dependencies
2018/06/15 11:35:57 Determining upstream version number
2018/06/15 11:35:57 Package version is "0.12.0+git20180608.b1e6baf"
2018/06/15 11:35:57 Determining package type
2018/06/15 11:35:57 Assuming you are packaging a program (because "github.com/lomik/go-carbon" defines a main package), use -type to override
2018/06/15 11:35:57 Determining dependencies
2018/06/15 11:36:02 Build-Dependency "github.com/go-graphite/carbonzipper" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:02 Build-Dependency "github.com/lomik/stop" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:02 Build-Dependency "github.com/dgryski/go-expirecache" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:02 Build-Dependency "github.com/lomik/og-rek" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:02 Build-Dependency "github.com/dgryski/go-trigram" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:02 Build-Dependency "github.com/dgryski/httputil" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:02 Build-Dependency "github.com/go-graphite/go-whisper" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:02 Build-Dependency "github.com/go-graphite/protocol" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:02 Build-Dependency "github.com/lomik/zapwriter" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:02 Build-Dependency "github.com/lomik/graphite-pickle" is not yet available in Debian, or has not yet been converted to use XS-Go-Import-Path in debian/control
2018/06/15 11:36:06
2018/06/15 11:36:06 Packaging successfully created in /home/hnakamur/go-carbon-deb-work/go-carbon
2018/06/15 11:36:06
2018/06/15 11:36:06 Resolve all TODOs in itp-go-carbon.txt, then email it out:
2018/06/15 11:36:06     sendmail -t < itp-go-carbon.txt
2018/06/15 11:36:06
2018/06/15 11:36:06 Resolve all the TODOs in debian/, find them using:
2018/06/15 11:36:06     grep -r TODO debian
2018/06/15 11:36:06
2018/06/15 11:36:06 To build the package, commit the packaging and use gbp buildpackage:
2018/06/15 11:36:06     git add debian && git commit -a -m 'Initial packaging'
2018/06/15 11:36:06     gbp buildpackage --git-pbuilder
2018/06/15 11:36:06
2018/06/15 11:36:06 To create the packaging git repository on alioth, use:
2018/06/15 11:36:06     ssh git.debian.org "/git/pkg-go/setup-repository go-carbon 'Packaging for go-carbon'"
2018/06/15 11:36:06
2018/06/15 11:36:06 Once you are happy with your packaging, push it to alioth using:
2018/06/15 11:36:06     git remote set-url origin git+ssh://git.debian.org/git/pkg-go/packages/go-carbon.git
2018/06/15 11:36:06     gbp push

~/go-carbon-deb-work/go-carbon ディレクトリが新たに作られてdebパッケージのソースがそこに生成されています。 上記の出力にあるとおり、go-carbonに含まれるvendorディレクトリは削除されて、依存ライブラリのソースが別途取得されています。

というわけで debian/* ファイルだけ頂くことにします。

gbp import-origでgo-carbonのソースをインポート

まず今回ビルドする go-carbon の v0.12.0 のソースを取得します。

mkdir -p ~/go-carbon-deb-work
cd !$
curl -LO https://github.com/lomik/go-carbon/archive/v0.12.0.tar.gz

debパッケージ用の作業ディレクトリを作成してそちらに移動しgitレポジトリを作成します。

mkdir -p ~/.ghq/github.com/hnakamur/go-carbon-deb
cd !$
git init

gbp import-orig でgo-carbonのソースをインポートします。以下のようにソースパッケージ名を聞かれるので go-carbon と入力します。

$ gbp import-orig --pristine-tar -u 0.12.0 ~/go-carbon-deb-work/v0.12.0.tar.gz
What will be the source package name? [] go-carbon

dh-make-golangで生成したdebianディレクトリのファイルをコピー

上記で dh-make-golangで生成した debian/* ファイルをコピーして、一旦コミットします。

rsync -a ~/go-carbon-deb-work/go-carbon/debian .
git add .
git commit -m "Add debian/* files generated by dh-make-golang"

debianディレクトリのファイルを編集

以下は主なところだけ説明します。

debian/controlを編集

Build-Dependsから依存ライブラリを外します。 また、Maintainerなど他の項目も適宜変更しました。

diff --git a/debian/control b/debian/control
index 486eb87..b5ee819 100644
--- a/debian/control
+++ b/debian/control
@@ -1,397 +1,19 @@
 Source: go-carbon
 Section: devel
 Priority: optional
-Maintainer: Debian Go Packaging Team <pkg-go-maintainers@lists.alioth.debian.org>
-Uploaders: Hiroaki Nakamura <hnakamur@gmail.com>
+Maintainer: Hiroaki Nakamura <hnakamur@gmail.com>
 Build-Depends: debhelper (>= 10),
		dh-golang,
-               golang-any,
-               golang-github-klauspost-compress-dev,
-               golang-github-nytimes-gziphandler-dev,
-               golang-github-sevlyar-go-daemon-dev,
-               golang-github-shopify-sarama-dev,
-               golang-github-stretchr-testify-dev,
-               golang-go.uber-zap-dev,
-               golang-gogoprotobuf-dev,
-               golang-golang-x-net-dev,
-               golang-goleveldb-dev,
-               golang-google-api-dev,
-               golang-google-cloud-dev,
-               golang-google-grpc-dev,
-               golang-toml-dev
+               golang-any
…(略) …

debian/go-carbon.dirsを作成

インストール時に作成するディレクトリを指定します。

/etc/go-carbon
/var/lib/graphite/whisper
/var/log/go-carbon

debian/go-carbon.postinstを作成

インストール後に実行するスクリプトを作成します。

nginx.orgのdebパッケージに含まれる debian/nginx.postinst を参考にしました。そちらではsystemdではないinit.dにも対応していましたが、私は不要なのでsystemd限定にしています。

#!/bin/sh

set -e

if [ "$1" != "configure" ]; then
    exit 0
fi

# Set permisions on default data directory on installation
if [ -z "$2" ]; then
    chown carbon:carbon /var/lib/graphite/whisper
fi

if [ -f /var/run/go-carbon.pid ] && kill -0 $(cat /var/run/nginx.pid) >/dev/null; then
    echo "######################################"
    echo "# Please restart go-carbon manually. #"
    echo "######################################"
else
    invoke-rc.d go-carbon start || true
fi

#DEBHELPER#

exit 0

invoke-rc.d コマンドは今回初めて知ったのですが man invoke-rc.d のDESCRIPTIONに以下のように書かれているので、debパッケージのスクリプトでサービス起動するときは systemctl ではなくこちらを使うのが良いようです。

All access to the init scripts by Debian packages' maintainer scripts should be done through invoke-rc.d.

また、go-carbonは graceful restart に対応していないので、プロセス起動中にパッケージアップデートする場合はメッセージを表示するだけで再起動はしないようにしました。別途再起動を行う想定です。

debian/go-carbon.postrmを作成

アンインストール後に実行するスクリプトを作成します。

nginxの debian/nginx.postrm ではサービスを止めるようなコマンドも含まれていたのですが、試してみると自分で書かなくても止めてくれたので省きました。

#!/bin/sh

set -e

case "$1" in
    purge)
	rm -rf /var/lib/graphite/whisper /var/log/go-carbon
	;;
    remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
	;;
    *)
	echo "postrm called with unknown argument \`$1'" >&2
	exit 1
esac

#DEBHELPER#

exit 0

apt remove go-carbon でアンインストールした後 apt purge go-carbon を実行すると、whisperファイルとログファイルを消すようにしました。

nginxの debian/nginx.postrm では設定ファイルのディレクトリ /etc/nginx/ も消すように書かれていましたが、試してみるとgo-carbonの設定ファイルのディレクトリ /etc/go-carbon は明示的に消すように書かなくても自動で消されたので上記のスクリプトでは省いています。

go-carbonのソースに含まれていたsystemd service定義ファイルを改良

PIDFile の項目がなかったので追加しました。 これを書いておくと systemctl stop go-carbon でサービスを止めたり、起動中に apt remove go-carbon でアンインストールしたときにPIDファイルを自動で消してくれました。

diff --git a/debian/go-carbon.service b/debian/go-carbon.service
index 0d933dd..a421bb7 100644
--- a/debian/go-carbon.service
+++ b/debian/go-carbon.service
@@ -6,6 +6,7 @@ After=network.target
 [Service]
 Type=forking
 ExecStart=/usr/bin/go-carbon -config /etc/go-carbon/go-carbon.conf -pidfile /var/run/go-carbon.pid -daemon
+PIDFile=/var/run/go-carbon.pid
 ExecReload=/bin/kill -HUP $MAINPID
 KillSignal=USR2
 Restart=on-failure
@@ -15,4 +16,4 @@ LimitNOFILE=55555
 LimitMEMLOCK=infinity

 [Install]
-WantedBy=multi-user.target
\ No newline at end of file
+WantedBy=multi-user.target

ビルド手順

他にも debian/changelog などを編集、コミット、タグ打ちをして準備ができた状態で、以下のコマンドでビルドしました。

ソースパッケージのビルド

gbp buildpackage --git-export-dir=.. -p/home/hnakamur/bin/gpg-passphrase -S -sa -d

以前は --git-export-dir../build-area としていましたが、この後のsbuildでupstreamのソースを ../go-carbon_0.12.0.orig.tar.gz というパスで参照しようとするので .. に変えました。

バイナリパッケージのビルド

TERM=unknown DEB_BUILD_OPTIONS=parallel=2 V=1 sbuild --sbuild-mode=buildd \
    --extra-repository="deb http://ppa.launchpad.net/hnakamur/golang-1.10/ubuntu bionic main" \
    --extra-repository-key /etc/apt/trusted.gpg.d/hnakamur_ubuntu_golang-1_10.gpg

何度も試行錯誤しているとPPAからダウンロードする時間が気になってくるので、以下のコマンドでchrootのホストのfreightを使うようにしました。

TERM=unknown DEB_BUILD_OPTIONS=parallel=2 V=1 sbuild --sbuild-mode=buildd \
	--extra-repository="deb http://127.0.0.1/freight bionic main" \
	--extra-repository-key /var/cache/freight/pubkey.gpg

/etc/nginx/conf.d/default.conf には以下のように設定しています。

server {
    listen       80;

…(略) …

    location /freight {
	alias  /var/cache/freight;
	index  index.html index.htm;
    }

…(略) …

2018-06-19 追記。 man sbuild によると sbuild は引数に .dsc ファイルのパスを渡すこともできます。当初は省略して使っていたのですが、こうすると親ディレクトリの *.dsc*_source.changes が署名なしのファイルで上書きされ dput でPPAにアップロードしようとするとエラーになってしまうことがわかりました。

sbuild でも署名する方法もあるようなのですがchroot環境でgpgを使えるようにセットアップが必要そうで面倒そうです。sbuildの引数に .dsc ファイルのパスを渡せば、指定したファイルを読み込んでビルドし上書きはしないことがわかったので、渡すようにしました。

例えば以下のように実行します。

TERM=unknown DEB_BUILD_OPTIONS=parallel=2 V=1 sbuild --sbuild-mode=buildd \
	--extra-repository="deb http://127.0.0.1/freight bionic main" \
	--extra-repository-key /var/cache/freight/pubkey.gpg \
            ../go-carbon_0.12.0-1ppa6~ubuntu18.04.1.dsc

.dscファイルパスを書くときにbashのコマンドライン補完が効かなかったので、事前に ls とかで補完して tmux でコピーしてから、上記のコマンドを書いてペーストするようにしました。

引数に .dsc ファイルを指定した場合は、ビルドの成果物は親ディレクトリではなくカレントディレクトリに作られました。

$ ls -1 go-carbon_*
go-carbon_0.12.0-1ppa6~ubuntu18.04.1_amd64.buildinfo
go-carbon_0.12.0-1ppa6~ubuntu18.04.1_amd64.changes
go-carbon_0.12.0-1ppa6~ubuntu18.04.1_amd64.deb