GCE + GCR + Docker(JIB)の覚え書き


概要

GCEでDockerが簡単に使えることを知ったのでやった。
KotlinでのWebアプリケーション作ってみた。

前提

「GCPのGCRにJIBを使ったDocker Imageをdeployして、GCEを簡単に作成する」が
「パルスのファルシのルシがパージでコクーン」という言葉を思い出したりするので、頭の整理をする

言葉 何に使うか
GCP Google Cloud Platform Googleのクラウドサービス全体
GCE Google Compute Engine Docker Imageを動かすサーバ
GCR Google Container Repository Docker Imageを保存するレポジトリ
JIB https://github.com/GoogleContainerTools/jib Javaのアプリケーションを簡単にDockerImageにできる

読んで得する人

  • Java(or Kotlin)でのアプリケーションのDocker化ってどうやるの?って人
  • GCPでDocker使いたいんだけどっていう人(not GKEなので注意、GCRにdeployする部分は参考になる?)

環境

名前 version
Gradle 5.4.1
JIB 1.2.0
GCloud 240.0.0
Kotlin 1.3.31
Spring Boot 2.2.0.M3

やること

  • 前準備
  • JIBの設定する
  • ローカルでの確認する
  • GCRにdeployする
  • GCEで動かす

前準備

GCRにdeployするので、gcloudコマンドが使えるようにしておく
https://cloud.google.com/sdk/downloads?hl=ja
このあたりを参考にインストールをする。そして最後のinit時に利用するGCPに対するログインを行う。

JIBの設定

アプリケーションはSpringBootで適宜作っているとしてbuild.gradleに以下を追加する

build.gradle
plugins {
    // jibのpluginの追加
    id 'com.google.cloud.tools.jib' version '1.2.0'
}

// わからないことがあれば
// https://github.com/GoogleContainerTools/jib/blob/master/docs/faq.md
// https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin
// などを見てもらうと良いかも
jib {
    to {
        image = 'gcr.io/${ログインしたGCPのプロジェクトID}/${アプリケーションの名前}' // DockerImageのpush先
        // 以前プロジェクト名と書いてましたが、、、間違い(https://cloud.google.com/container-registry/docs/overview?hl=ja の通り)
        auth {
            username = 'oauth2accesstoken' // gcrにコマンドでdeployする際に使える名前
            password = 'gcloud auth print-access-token'.execute().text.trim() // gcloudが端末に入っていること前提でのpassを作成するコマンド。おそらくGCRを使う場合はこのauthで良いと思われ
        }
    }
    from {
        image = 'openjdk:alpine' // pullしてくるImage。ここは自分の好きなものを使う
    }
    container {
        mainClass = 'jp.john.test.MainKt' // 起動のentiry point。SpringBoot + Kotlinなら main関数 が記載されているクラス
        jvmFlags = [
                '-Dspring.profiles.active=product'
        ] // SpringBootに渡したい値があればこちらを使う
        format = 'OCI' // buildしたimageの形式。よくわかっていないが、Defaultの 'Docker' よりOpenな規格のようである( https://qiita.com/mamomamo/items/ed5db2ab1555078f8a24 )
    }
}

普通はこれで終わり、既存部分を変更しなくて良い。以下タスクが追加されている

  • jib
  • jibBuildTar
  • jibDockerBuild

task treeを見ればわかるが jib は基本コンパイルをしてImageに追加しているだけ。

$ ./gradlew taskTree jib
Starting a Gradle Daemon (subsequent builds will be faster)

> Task :taskTree

------------------------------------------------------------
Root project
------------------------------------------------------------

:jib
+--- :classes
     +--- :compileJava
     |    \--- :compileKotlin
     \--- :processResources

ローカルでとりあえず起動させる

ローカルでImageを作成する


$ ./gradlew jibDockerBuild

> Task :compileKotlin

> Task :jibDockerBuild
Setting image creation time to current time; your image may not be reproducible.

Containerizing application to Docker daemon as gcr.io/${ログインしたGCPのプロジェクト名}/${アプリケーションの名前}...
The base image requires auth. Trying again for openjdk:alpine...

Container entrypoint set to [java, -Dspring.profiles.active=product, -cp, /app/resources:/app/classes:/app/libs/*, jp.john.test.MainKt]

Built image to Docker daemon as gcr.io/${ログインしたGCPのプロジェクト名}/${アプリケーションの名前}
Executing tasks:
[==============================] 100.0% complete

BUILD SUCCESSFUL in 36s
3 actionable tasks: 3 executed

Imageができたらdockerコマンドで起動する(ポート8080でアプリケーションで起動するものとする)

$ docker container run --rm -it -p 8080:8080 gcr.io/${ログインしたGCPのプロジェクト名}/${アプリケーションの名前}

ブラウザから確認して動いたらOK

GCRにdeployする

ここは前準備(gcloudの設定)ができていれば全く難しくない

$ ./gradlew jib

GCR上で作成されていたらOK(自分でレポジトリを作るなどしなくても良い)

GCEで動かす

前にGCRにdeployできていれば全く難しくない(2回目

GCRに上がったImageをclickして「GCEにデプロイ」を選択する

設定についてはDocker部分は全くいじらなくて良いはず。(コンテナのタグがlatestになっているはず
リージョンなどは適宜変更する。
ファイアウォールはポート8080を開けているものを指定する。(dockerで利用した-pオプションはファイアウォール対応する形になる
https://cloud.google.com/compute/docs/containers/configuring-options-to-run-containers?hl=ja#publishing_container_ports

http://${ip}:8080 に対してアクセスして対象のアプリケーションが確認できれば、OK
ファイアウォールについては、GCEの設定について説明している記事を参考にしてもらえれば。

補足

deployしたimageをつかって再起動してほしいのだが?

defaultのGCEの設定で再起動するとpullするので、GCEの対象のコンテナを再起動するだけでよい。
もし指定したタグのもので再起動したい場合は、コンテナのタグをGCRの対象のタグにする。

ローカルで動いたのに、GCE上で動かないんだが?

GCEのインスタンスにsshでの接続をして以下のコマンドを叩くと、エラーが出ていることを確認できる

$ sudo journalctl -u docker-events-collector
2019-05-23 04:11:31 +0000 [warn]: #0 Dropping 33 log message(s) error="User unauthorized to access {なんかのid}" error_code="7"
2019-05-23 04:11:36 +0000 [warn]: #0 Dropping 1 log message(s) error="User unauthorized to access {なんかのid}" error_code="7"
2019-05-23 04:11:42 +0000 [warn]: #0 Dropping 1 log message(s) error="User unauthorized to access {なんかのid}" error_code="7"
2019-05-23 04:11:48 +0000 [warn]: #0 Dropping 1 log message(s) error="User unauthorized to access {なんかのid}" error_code="7"
2019-05-23 04:11:53 +0000 [warn]: #0 Dropping 1 log message(s) error="User unauthorized to access {なんかのid}" error_code="7"
2019-05-23 04:12:00 +0000 [warn]: #0 Dropping 21 log message(s) error="User unauthorized to access {なんかのid}" error_code="7"
2019-05-23 04:12:05 +0000 [warn]: #0 Dropping 1 log message(s) error="User unauthorized to access {なんかのid}" error_code="7"

どうやら権限が無いようです。詰まってた人が同じようにいた。

新しく作成していたサービスアカウントに対して Monitor などに対しての権限がなかったから動かないらしい。
なのでGCEのサービスアカウントはデフォルトで用意されているらしい {プロジェクト名}@appspot.gserviceaccount.com を使ってやるととりあえず動く。