sbtでAkkaHttpサーバのDockerイメージとRPMを作成する

`sbt-native-packager`を使って、AkkaHttpサーバのDockerイメージとRPMを作成する方法について記載しています。

[sbt-native-packager]
https://www.scala-sbt.org/sbt-native-packager/

* 動作確認環境: Mac Mojave 10.14.1

以降、手順について記載しています。

## `project/plugin.sbt`の設定

パッケージ化に`sbt-native-packager`を使うので、`project/plugin.sbt`に以下追記しました。

```project/plugin.sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.6")
```

## `build.sbt`の設定

肝心の`build.sbt`の設定は以下の通り設定しました。
内容については、コメントを参照してください。

```build.sbt
libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-http" % "10.1.7",
  "com.typesafe.akka" %% "akka-stream" % "2.5.21" // or whatever the latest version is
)

lazy val root = (project in file(".")).
  enablePlugins(JavaAppPackaging, DockerPlugin, JavaServerAppPackaging, RpmPlugin)
  .settings(
    organization := "com.example",
    scalaVersion := "2.12.7",
    name := "AkkaHttpQuick"
  )
  .settings(
    // Dockerのimageにパッケージング化するための設定
    version in Docker := "1.0", // imageのバージョン
    packageName in Docker := "akka-http-server", // imageの名前
    dockerBaseImage := "java:openjdk-8-jdk", // image生成元のimage
    dockerRepository := Some("chocomint-kusoyaro"), // レジストリ名
    dockerExposedPorts := Seq(8080) // 公開するport
  )
  .settings(
    // RPMにパッケージング化するための設定
    packageName in Rpm := "akka-http-server", // rpmの名前
    version in Rpm := "0.1", // rpmのバージョン
    rpmRelease := "1", // rpmのリリースバージョン
    packageSummary := "Akka Http Server", // rpmのサマリ
    packageDescription := "Simple Akka Http Quickstart", // rpmのディスクリプテョン  
    rpmVendor := "chocomint-kusoyaro", // rpmのベンダ
    rpmUrl := Some("http://chocomint-kusoyaro.com"),
    rpmLicense := Some("MIT")
  )
```

## エンドポイントのWebサーバをコーディングする

プロジェクトのエンドポイントとなるWebサーバをコーディングします。
今回の場合、Docker、Rpmで起動するので、エンドポイントはプロジェクトに一つでなければならないです。
考えてみれば当たり前なのですが、私はハマったので注意してください。

ソースは以下のAkkaHttpのIntroductionから適当に持ってきました。
IPアドレスのところだけ変更しています。

[AkkaHttp Introduction]
https://doc.akka.io/docs/akka-http/current/introduction.html

```WebServer.scala
package com.example

import akka.actor.ActorSystem
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import scala.io.StdIn

object WebServer {
  def main(args: Array[String]) {

    implicit val system = ActorSystem("my-system")
    implicit val materializer = ActorMaterializer()
    // needed for the future flatMap/onComplete in the end
    implicit val executionContext = system.dispatcher

    val route =
      path("hello") {
        get {
          complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
        }
      }

    // ここだけ「127.0.0.1」から「0.0.0.0」に変更した。
    val bindingFuture = Http().bindAndHandle(route, "0.0.0.0", 8080)

    println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
    StdIn.readLine() // let it run until user presses return
    bindingFuture
      .flatMap(_.unbind()) // trigger unbinding from the port
      .onComplete(_ => system.terminate()) // and shutdown when done
  }
}
```

参考までにプロジェクトの構成は、以下の図のようになっていると思います。

<img width="302" alt="スクリーンショット 2019-03-03 18.58.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/203061/f8d261bb-65b3-50d8-f472-67901a5b967c.png">

ここまでで、設定は完了したので、以降はDockerのイメージの作成、Rpmの作成について記載します。

## sbtでDockerイメージ化〜疎通確認

Dockerのイメージ化について記載しています。

### イメージを作成

以下のsbtコマンドでDockerイメージを作成します。

```
$ sbt docker:publishLocal
...
[success] Total time: 11 s, completed 2019/03/03 19:06:33
```

イメージが作成できていることを確認します。

```
$ docker images

REPOSITORY                                      TAG                 IMAGE ID            CREATED             SIZE
chocomint-kusoyaro/akka-http-server             1.0                 69e7c5f9c55c        47 seconds ago      665MB
```

### コンテナの起動

ポートを8080番で公開しているので、以下のように起動します。
Scalaソースコードを見ればわかるのですが、`-d`でバッググラウンドプロセスとしての起動は今回出来ないようになっています。

```
$ docker run -it -p 8080:8080 chocomint-kusoyaro/akka-http-server:1.0

Server online at http://localhost:8080/
Press RETURN to stop...
```

最後に疎通確認します。
以下のように表示されれば成功です。

```
$ curl -XGET localhost:8080/hello

<h1>Say hello to akka-http</h1>
```

### sbtでそのままレジストリに登録したい場合

`sbt docker:publish`コマンドで可能です。

```
$ docker login
$ sbt docker:publish
```

以下のコマンドと同じです。

```
$ docker login
$ docker push chocomint-kusoyaro/akka-http-server:1.0
```

## sbtでRpmの作成〜疎通確認

Rpm化する場合は、以下の手順を参考にしてください。

### rpmのインストール

Macで`rpmbuild`コマンドがないと、rpm化出来なかったので、`rpm`をHomebrewでインストールしました。

```
$ brew install rpm
```

### Rpmの作成

sbtコマンドでRpmを作成します。

```
$ sbt rpm:packageBin
...
[success] Total time: 8 s, completed 2019/03/03 19:17:19
```

成功すると、`target/rpm/RPMS/noarch/`配下に`akka-http-server-0.1-1.noarch.rpm`が作成されているはずです。

```
$ ls target/rpm/RPMS/noarch
akka-http-server-0.1-1.noarch.rpm
```

### Rpmのインストール

どこか適当なサーバに上記で作成した`akka-http-server-0.1-1.noarch.rpm`を持っていってインストールします。インストールにはJava8が必要なので、インストールされていない場合はインストールしておいてください。

```
$ sudo rpm -ivh akka-http-server-0.1-1.noarch.rpm

Preparing...                          ################################# [100%]
Creating system group: akkahttpquick
Creating system user: akkahttpquick in akkahttpquick with akkahttpquick user-daemon and shell /bin/false
Updating / installing...
   1:akka-http-server-0.1-1           ################################# [100%]
```

次に起動します。
`build.sbt`で`name := "AkkaHttpQuick"`と設定した箇所がコマンドに反映されるらしく、以下のコマンドで起動します。

```
$ akkahttpquick
```

最後に疎通確認しておきます。

```
$ curl http://llhost:8080/hello
<h1>Say hello to akka-http</h1>
```