会社ではほとんどのアプリケーションをScalaで開発しさくらのクラウドやハウジング環境で運用しているのですが、どんな言語で開発するにせよどのような環境で運用するにせよ、当然ながらアプリケーションはdeployする必要があります。
deployに関しては色々な方法があると思うのですが、私は「ミドルウェアのインストールやアップデートと同じように自社プロダクトもパッケージとしてdeployしたい」と考えていました。
つまりnginxやMariaDBをアップデートするように yum update
や apt-get upgrade
, emerge -uND world
(「うどんアップデート」というらしいですね)すると自社プロダクトもアップデートされるということです。
そう!emerge !!
portageパッケージ管理システムに則ってアプリのebuildを作ればいいではないですか!!!
1日に何回も何十回もcommitがpushされる自社プロダクトで、もし都度バイナリパッケージを作るとなるとパッケージ作成自体のコストが高く感じてしまいますが、ebuildなら「GitHubのリポジトリから直接masterのHEADを取得後コンパイルしインストールする」という手順自体をパッケージングできます!
portageにはインストールすると同時にバイナリパッケージを作る機能もあるので、たとえScalaアプリのコンパイルに初回は1曲聴ける程度の時間がかかったとしても2回目以降はイントロを聴いている間にインストールできます!
ということでScalaアプリをebuildでdeployするようになってから数ヶ月経ち課題を残しつつも安定してきたので概要を書き留めておきます。
なお、世間にはGentoo Linuxでサービス運用している会社が少なくとも2社あるようです。
2000年以前はおそらくどこにも採用されていなかったことを考えると近年の導入会社数の伸び率は目覚ましく、ひとりのGentoo Linuxユーザーとしてもうれしい限りです。
流れ
例としてHelloPlayというPlayFrameworkを使ったScalaのサンプルアプリケーションをebuildでパッケージングしてみます。
インストール先はさくらのクラウドですが、今回プラットフォーム固有の機能は使っていないのでクラウドでもハウジングでも差異はありません。
また、バージョンとして下記2つを想定します。
- 安定版である
0.0.1
→ tag v0.0.1 がインストールされる
- 開発版である
9999
→ その時点のmasterのHEADがインストールされる
なおこのアプリケーションでは v0.0.1
に対して master
で GET /hello
というエンドポイントが追加されており、リクエストを受けるとステータス200で"Hello"というメッセージを返す機能が追加されています。
差分はこんな感じです。
ebuild作成
このアプリケーションを www-apps/playscala-example
というパッケージにします。
こんなebuildを書いてみました。
また、 application.conf
の雛形や起動スクリプトなども作成します。
ebuild自体の差分としては開発版である 9999
には ~amd64
キーワードを指定しますが、安定版である 0.0.1
にはキーワードを指定しません。
ebuild内でバージョンを判定することにより 0.0.1
ではtagを、 9999
ではmasterのHEADを取得するよう振る舞いを分けます。
if [[ ${PV} != 9999 ]]; then
# set tag as "EGIT_COMMIT"
EGIT_COMMIT="v${PV}"
SLOT=$(get_version_component_range 1-2)
fi
リポジトリ登録
repositories.xml
を作成し自身のportageリポジトリのrootに配置します。
内容はこのようなXMLファイルです。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE repositories SYSTEM "/dtd/repositories.dtd">
<repositories xmlns="" version="1.0">
<repo quality="experimental" status="unofficial">
<name><![CDATA[mazgi-experimental]]></name>
<description lang="en"><![CDATA[An overlay by @mazgi]]></description>
<homepage>http://mazgi.com/</homepage>
<owner type="person">
<email>MATSUKI.Hidenori@gmail.com</email>
<name><![CDATA[Hidenori MATSUKI]]></name>
</owner>
<source type="git">git://github.com/mazgi/portage-overlay.git</source>
</repo>
</repositories>
このrepositories.xml
を/etc/portage/repos.conf
に今回はmazgi-experimental.conf
という名前で配置します。
内容はこんな感じです。
$ cat /etc/portage/repos.conf/mazgi-experimental.conf
[mazgi-experimental]
location = /var/lib/portage/repo.data/mazgi-experimental
sync-type = git
sync-uri = git://github.com/mazgi/portage-overlay.git
auto-sync = yes
またScalaアプリケーションをsbtでビルドするようebuildを書いたので、sbtが必要になりますが、sbtのebuildパッケージも作成して同じリポジトリに入れています。
普通にsbtのebuildとして使えますのでもし必要でしたら上記の設定を行って emerge sbt-bin
してみてください。
インストール
まずは sync
して先ほど設定したリポジトリを取得します。
$ sudo emaint sync -r mazgi-experimental
>>> Syncing repository 'mazgi-experimental' into '/var/lib/portage/repo.data/mazgi-experimental'...
/usr/bin/git pull
Already up-to-date.
=== Sync completed for mazgi-experimental
Action: sync for repo: mazgi-experimental, returned code = 0
またJDK周りのUSEフラグを必要に応じて変更しておきます。
$ sudo flaggie dev-java/oracle-jdk-bin -awt -fontconfig
$ sudo flaggie dev-java/icedtea-bin -X -alsa -cups
ここまでやって emerge playscala-example
すると下記のようにJDKやsbt等必要なパッケージを含めて全てがリストアップされます。
$ sudo emerge -uav playscala-example
These are the packages that would be merged, in order:
Calculating dependencies... done!
[ebuild N ] media-libs/giflib-4.1.6-r3::gentoo USE="-X -rle -static-libs" ABI_X86="(64) -32 (-x32)" 0 KiB
[ebuild N ] app-eselect/eselect-java-0.1.0::gentoo 0 KiB
[ebuild N ] dev-util/patchelf-0.8::gentoo 0 KiB
[ebuild N ] media-libs/libpng-1.6.16:0/16::gentoo USE="-apng (-neon) -static-libs" ABI_X86="(64) -32 (-x32)" 0 KiB
[ebuild N ] media-libs/lcms-2.6-r1:2::gentoo USE="threads zlib -doc -jpeg -static-libs {-test} -tiff" ABI_X86="(64) -32 (-x32)" 0 KiB
[ebuild N ] dev-java/java-config-wrapper-0.16::gentoo 0 KiB
[ebuild N ] sys-apps/baselayout-java-0.1.0::gentoo 0 KiB
[ebuild N ] dev-lang/nasm-2.11.08::gentoo USE="-doc" 0 KiB
[ebuild N ] dev-java/java-config-2.2.0:2::gentoo PYTHON_TARGETS="python2_7 python3_4 -python3_3" 0 KiB
[ebuild N ] gnome-base/gsettings-desktop-schemas-3.14.2::gentoo USE="-introspection" 0 KiB
[ebuild N ] media-libs/libjpeg-turbo-1.3.1::gentoo USE="-java -static-libs" ABI_X86="(64) -32 (-x32)" 0 KiB
[ebuild N ] virtual/jpeg-62:62::gentoo ABI_X86="(64) -32 (-x32)" 0 KiB
[ebuild N f ] dev-java/oracle-jdk-bin-1.8.0.51:1.8::gentoo USE="-alsa -awt -cups -derby -doc -examples -fontconfig -javafx -jce -nsplugin -pax_kernel (-selinux) -source" 0 KiB
[ebuild N ~] virtual/jdk-1.8.0:1.8::gentoo 0 KiB
[ebuild N ] dev-java/icedtea-bin-7.2.5.5:7::gentoo USE="-X -alsa -cjk -cups -doc -examples -nsplugin -pulseaudio (-selinux) -source -webstart" 0 KiB
[ebuild N ] virtual/jdk-1.7.0:1.7::gentoo 0 KiB
[ebuild N ~] virtual/jre-1.8.0:1.8::gentoo 0 KiB
[ebuild N ] virtual/jre-1.7.0:1.7::gentoo 0 KiB
[ebuild N ] dev-java/sbt-bin-0.13.7:0.13::mazgi-experimental 0 KiB
[ebuild N ] www-apps/playscala-example-0.0.1:0.0::mazgi-experimental USE="autoclean symlink -doc" 0 KiB
Total: 20 packages (20 new), Size of downloads: 0 KiB
Fetch Restriction: 1 package
The following keyword changes are necessary to proceed:
(see "package.accept_keywords" in the portage(5) man page for more details)
# required by www-apps/playscala-example-0.0.1::mazgi-experimental
# required by playscala-example (argument)
=virtual/jdk-1.8.0 ~amd64
# required by www-apps/playscala-example-0.0.1::mazgi-experimental
# required by playscala-example (argument)
=virtual/jre-1.8.0 ~amd64
The following license changes are necessary to proceed:
(see "package.license" in the portage(5) man page for more details)
# required by virtual/jdk-1.8.0::gentoo
# required by virtual/jre-1.8.0::gentoo
# required by www-apps/playscala-example-0.0.1::mazgi-experimental
# required by playscala-example (argument)
>=dev-java/oracle-jdk-bin-1.8.0.51 Oracle-BCLA-JavaSE
Would you like to add these changes to your config files? [Yes/No]
JDK 1.8 のmaskを外し、Oracle JDKのライセンスに同意し、インストールを進めます。
$ sudo dispatch-conf
$ sudo emerge -uav playscala-example
These are the packages that would be merged, in order:
Calculating dependencies... done!
[ebuild N ] app-eselect/eselect-java-0.1.0::gentoo 0 KiB
[ebuild N ] dev-java/java-config-wrapper-0.16::gentoo 0 KiB
[ebuild N ] sys-apps/baselayout-java-0.1.0::gentoo 0 KiB
[ebuild N ] dev-java/java-config-2.2.0:2::gentoo PYTHON_TARGETS="python2_7 python3_4 -python3_3" 0 KiB
[ebuild N f ] dev-java/oracle-jdk-bin-1.8.0.51:1.8::gentoo USE="-alsa -awt -cups -derby -doc -examples -fontconfig -javafx -jce -nsplugin -pax_kernel (-selinux) -source" 0 KiB
[ebuild N ~] virtual/jdk-1.8.0:1.8::gentoo 0 KiB
[ebuild N ~] virtual/jre-1.8.0:1.8::gentoo 0 KiB
[ebuild N ] dev-java/sbt-bin-0.13.7:0.13::mazgi-experimental 0 KiB
[ebuild N ] www-apps/playscala-example-0.0.1:0.0::mazgi-experimental USE="autoclean symlink -doc" 0 KiB
Total: 9 packages (9 new), Size of downloads: 0 KiB
Fetch Restriction: 1 package
Would you like to merge these packages? [Yes/No]
インストールできたら application.conf
を作成し、startしてみます。
$ sudo cp /etc/playscala-example/application.conf{.example,}
$ sudo /etc/init.d/playscala-example start
* Starging playscala-example ... [ ok ]
$ /etc/init.d/playscala-example status
* status: started
$ curl -L 'localhost:9000/' -o /dev/null -w '%{http_code}\n' -s
200
無事 /
へのGETが200を返すことを確認できました!
アップデート
安定版である 0.0.1
がインストールできました。
$ curl -L 'localhost:9000/hello' -o /dev/null -w '%{http_code}\n' -s
404
しかしながら 0.0.1
は /hello
へのGETが404になってしまいます。
そこで、今度は開発版の 9999
にアップデートしてみます。
9999
は特定のtagを参照しているわけではなくemergeした時点でのmasterのHEADがインストールされます。
$ sudo emerge -uavq \=www-apps/playscala-example-9999
[ebuild NS ] www-apps/playscala-example-9999 [0.0.1] USE="autoclean -doc symlink"
The following keyword changes are necessary to proceed:
(see "package.accept_keywords" in the portage(5) man page for more details)
# required by =www-apps/playscala-example-9999 (argument)
=www-apps/playscala-example-9999 ~amd64
Would you like to add these changes to your config files? [Yes/No]
Autounmask changes successfully written.
* IMPORTANT: config file '/etc/portage/package.accept_keywords/00_default' needs updating.
* See the CONFIGURATION FILES section of the emerge
* man page to learn how to update config files.
~amd64
でマスクされていますので外します。
$ sudo dispatch-conf
マスクを外したら改めてemergeします。
$ USE="-symlink" sudo emerge -uavq \=www-apps/playscala-example-9999
[ebuild NS ] www-apps/playscala-example-9999 [0.0.1] USE="autoclean -doc -symlink"
Would you like to merge these packages? [Yes/No]
このパッケージは /var/lib/playscala-example/versions
以下にバージョン毎にsymlinkを作る構造になっているのでsymlinkを貼り替えます。
$ cd /var/lib/playscala-example/versions
/var/lib/playscala-example/versions ~
$ sudo rm current
$ sudo ln -s playscala-example.9999.957b03072271df6586c1e6db8b2b76b4be5e3f08 current
$ sudo chown play:play -h current
$ ls -l
total 4
lrwxrwxrwx 1 play play 63 Aug 31 16:19 current -> playscala-example.9999.957b03072271df6586c1e6db8b2b76b4be5e3f08/
drwxr-xr-x 1 play play 100 Aug 31 03:10 playscala-example.0.0.1.9e023916c4c911408145ff5f181a42543fbced4a/
drwxr-xr-x 1 play play 102 Aug 31 16:12 playscala-example.9999.957b03072271df6586c1e6db8b2b76b4be5e3f08/
サービスを再起動します。
$ sudo /etc/init.d/playscala-example restart
* Stopping playscala-example ... [ ok ]
* Starging playscala-example ... [ ok ]
$ sudo /etc/init.d/playscala-example status
* status: started
/hello
へのGETが通るようになったか確認してみます。
$ curl -L 'localhost:9000/' -o /dev/null -w '%{http_code}\n' -s
200
$ curl -L 'localhost:9000/hello' -w '\n%{http_code}\n'
Hello
200
無事アップデートされ /hello
へのGETが200を返すことが確認できました!
まとめのかわりに
以上、さらっと普通のScalaアプリケーションをebuildでdeployする方法を書いてみたのですが、これ、「よーし!当社もGentooにしたしちょっとebuild作成に挑戦するぞ!」っていう方にとっては暗黙知が多すぎて伝わらないですね。
自分でフラグ立ててしまったので回収しつつ、もう少し順を追って書いてみようと思います。
まずは自社サービスのebuild化とdeployについて会社のブログに書くところからかな...!