25
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ScalaAdvent Calendar 2016

Day 1

Akka-http on Google App Engine flexible environment

Last updated at Posted at 2016-11-30

ScalaよりもGAE色が強くなってしまった、Scala advent calendarなのに...^^;

はじめに:Google App Engine

元祖PaaSのGAE、忘れられた存在、、、かと思いきや、最近またよく耳にするようになってきました。
東京リージョンでの利用開始ニュースや主にGolangでの採用事例紹介などなど。

翻ってScalaエンジニア的にGAE/Javaってどうだろう?と考えると、バージョンがJava7だったりjettyベースだったりしてちょっと魅力に欠けるかなと個人的には思ってしまいます。

ただし、ドキュメントをよくよく読んでみると、GAEには2種類あり、これまでのStandard Environmentに加えベータではあるもののflexible environmentというのがあり(Standardはつい最近東京リージョンでも利用可能になりましたがベータはUSかヨーロッパのみとなります)こちらはJava8とあります。

さらにドキュメントのAbout Java Runtime Environmentsのところを見ると2種類あることがわかります。

Standard同様jetty版があるのはいいとして、 単なるJava8の実行環境だけというのもあります!

あら、もしかしてakka-http動くんじゃないの?と思ってやってみた、というのがこの記事となります。

稼働させるための条件

上記のリンクを読むと、GAE flex環境でアプリケーションを稼働させるには以下の3つあればいける模様。

  • app.yaml
  • 8080ポートでlistenするmainプログラムを含んだjar(各種ライブラリを含んだfat-jar)
  • Dockerfile
    • なおGAE flexible environment自体のDockerfileは こちらの模様 ではなくこちらに移動
      • 2016/12/1現時点のopen-jdkのバージョンは1.8.111です。

準備

事前に以下2点を行っておく必要があります。

  • GAE上でプロジェクトを作っておく
  • Cloud SDKを開発PCにインストールしておく
    • GAEにデプロイするのに使用する gcloud コマンドを含む

※あと今回のscalaコードではslack認証をしているので、サンプルコードを動かすにはSlack上でアプリケーションを登録し、client_idおよびclient_secretを取得しておく必要があります。

ソースコード

まずはじめにディレクトリ構成は以下。

.
├── Dockerfile
├── app.yaml
├── build.sbt
└── src
    └── main
        ├── resources
        └── scala
            └── my
                └── Main.scala

app.yaml

とりあえず必要最低限だけ。

app.yaml
runtime: custom
env: flex

Dockerfile

自分で作るfat-jarの名前はmygae.jarとします。

Dockerfile
FROM gcr.io/google_appengine/openjdk8
VOLUME /tmp
ADD mygae.jar app.jar
CMD [ "java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

build.sbt

scalaは最新の2.12.0を、akka-httpも最新の10.0.0を。
なおfat-jarを作るためにsbt-assemblyプラグインも別途入れております。

build.sbt
scalaVersion := "2.12.0"

libraryDependencies += "com.typesafe.akka" %% "akka-http" % "10.0.0"

assemblyJarName in assembly := "mygae.jar"
mainClass in assembly := Some("my.Main")

Main.scala

Slack認証する部分をサンプルコードとしてみました。
slauth というエンドポイント部分はakka-httpのサーバーとしてのプログラム例として、またそこからSlackのoauth APIを呼び出す部分はakka-httpのクライアント例として見ることが出来ます。
本来は返ってきたjson文字列をcase classにunmarshalするようにすべきですがそこはサボって単に画面にjson文字列をベタッと表示するだけのシンプルなものにしています。

Main.scala
package my

import akka.actor.ActorSystem
import akka.http.scaladsl.model.{HttpRequest, HttpResponse, StatusCodes}
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.{Http, HttpExt}
import akka.stream.ActorMaterializer
import akka.util.ByteString

import scala.concurrent.Await
import scala.concurrent.duration._
import scala.util.{Failure, Success}

object Main extends App {

  implicit val actorSystem = ActorSystem()
  implicit val ec = actorSystem.dispatcher
  implicit val actorMaterializer = ActorMaterializer()

  val http: HttpExt = Http(actorSystem)

  object Controller {

    val route: Route =
      path("slauth") {
        get {
          parameters('code) { code =>
            onComplete(
              http.singleRequest(HttpRequest(uri = s"https://slack.com/api/oauth.access?client_id=クライアントID&client_secret=クライアントシークレット&code=$code")).flatMap {
                case HttpResponse(StatusCodes.OK, headers, entity, _) =>
                  entity.dataBytes.runFold(ByteString(""))(_ ++ _).map(_.utf8String)
                case resp @ HttpResponse(httpCode, _, _, _) =>
                  sys.error(s"fail: $httpCode")
              }
            ) {
              case Success(result) => complete(result)
              case Failure(_) => complete(StatusCodes.InternalServerError)
            }
          }
        }
      }
  }

  val bindingFuture = Http().bindAndHandle(Controller.route, "0.0.0.0", 8080)

  sys.addShutdownHook {
    val awaitable = for {
      binding <- bindingFuture
      _ <- binding.unbind()
      _ <- actorSystem.terminate()
    } yield ()

    Await.ready(awaitable, 10 seconds)
  }
}

なお、 bindAndHandle メソッドの第二引数部分は "0.0.0.0" とします。ここは localhost とか 10.0.0.1 とかだと動かないので注意が必要です。

デプロイ

  • コンソール上で sbt assembly 実行
  • 出来たmygae.jarをtargetディレクトリから app.yamlDockerfileが配置してあるプロジェクト直下のディレクトリに移動
  • コンソール上で gcloud app deploy

初回は少し時間がかかりますが、2回目以降は3-4分くらいでデプロイ出来ます。

実行

ブラウザから https://slack.com/oauth/authorize?client_id=....&scope=...&redirect_uri=... のURLを入力してSlackの認証画面を表示し「Authorize」ボタンを押下すれば、ブラウザ上にjson文字列が表示されるはずです!

終わりに

scalaを稼働させる新たな基盤候補が見つかってよかったです。

追記

ログ出力は以下のAPI使ってやるのかな、と思っていたのですが
http://qiita.com/mtoyoshi/items/d11bcfa6f7ed51e5a813
こちらの方式のほうが簡単ですね。
http://qiita.com/chidakiyo/items/1452ab1ea83279a8e485

25
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
25
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?