Yoshi.dev

技術系の趣味、またはやった仕事やそこから学んだことを忘れないために

grpc-spring-boot-starter使ってるプロジェクトでdockerビルドしたアプリを動かしたらNoSuchMethodErrorに遭遇した

ちょっとよくわからない現象に遭遇した

grpc-spring-boot-starter使ってるプロジェクトでの話だけど ローカルでgradle bootRunして起動した時はgrpc通信部分で問題なく動くのだが dockerイメージにビルドしてコンテナとして動かしたときに下記のようなエラーがでてきた

Exception in thread "grpc-default-executor-0" java.lang.NoSuchMethodError: 'void com.google.protobuf.AbstractMessageLite$Builder.addAll(java.lang.Iterable, java.util.List)'

依存してるライブラリのバージョンがうまく噛み合ってないのかなと思ったので grpc-spring-boot-starterのrelease note確認して、使用してるバージョンで依存してるgrpc-javaのバージョンを特定してから grpc-javaのREADMEに書いてある必要なgradleの依存ライブラリを明示的に書くようにしたら問題解決した

implementation 'io.grpc:grpc-netty-shaded:1.25.0'
implementation 'io.grpc:grpc-protobuf:1.25.0'
implementation 'io.grpc:grpc-stub:1.25.0'

↑自分はgrpc-spring-boot-starter:3.5.0を使っていたので、grpc-java:1.25.0をgradleに記入した

grpc-spring-boot-starterのREADMEにも特にgrpc-javaの依存を書いておけと見当たらなかったので結構ハマってしまった

ローカルでprotocol buffersのjavaファイルを作るときにはgenerateProtoを使用していて、dockerのイメージをビルドするときも同じコマンドが叩かれている認識なので 生成されるクラスファイルが依存するライブラリのバージョンも同じになるはずだからどの環境で動かしてもこんなエラーには遭遇しないと思ってたんだけど 何か勘違い、見落としがありそうだ.. gradleプロジェクトだとdependenciesタスクで依存してるライブラリのバージョンを確認できるけど、 dockerイメージにするとそのへんを確認する方法がわからなくて困った

grpc-javaのヘルスチェック

grpc-javaを使って実装したgrpcサーバーのヘルスチェックについて書いておきます

grpcのヘルスチェックこうやりましょ的なのが公式から出されています

grpc/health-checking.md at master · grpc/grpc · GitHub

これを自分で実装してもいいかもしれませんがめんどくさいので公式から提供されているものを使いましょう

mvnrepository.com

参考までにkotlinのコード

server = ServerBuilder.forPort(port)
                 // こんな感じにHealthCheck用のサービスを追加
                .addService(HealthStatusManager().healthService)
                .build()
                .start()

これでサーバー起動するとGrpcのHealthチェック用の口が用意できます

dockerでchrome動かした時のクラッシュ問題への対処法

--disable-dev-shm-usage

Dockerはデフォルトではメモリスペースに/dev/shmという場所を使うがこれは64MBと小さい。そのためChromeがメモリ不足でクラッシュしてしまう
Chrome 65から起動時に--disable-dev-shm-usageを指定することで、シェアドメモリファイルの保持場所を/dev/shmの代わりに/tmpディレクトリを使うようになる

補足
/dev/shmファイルシステムレベルの共有メモリ
/tmp:ディスク

【QUARKUS】KotlinとGradleでサンプル作ってネイティブ起動するところまで

はじめに

QUARKUS触ってみたいなーと前々から思っていたので、KotlinとGradleを使ったQUARKUSのサンプルを作ってみました QUARKUSのドキュメントではmavenのほうが情報が厚そうだったのですが、pom.xml書きたくない、スクリプトで依存性管理したいと思ったのでGradleを選択しました とりあえず今回はネイティブイメージをビルドするところまでです また、自分はmacを使っているのでmacユーザー向けに書いていきます

作成したコードのリポジトリはこちらになります https://github.com/yoshi10321/sample-quarkus-app

GraalVMのインストール

まずGraalVMをインストールします https://github.com/oracle/graal/releases macの場合 graalvm-ce-darwin-amd64-{バージョン名}.tar.gz というファイルをダンロードして解凍します

解凍したディレクトリを /Library/Java/JavaVirtualMachines に移動しておきます

/usr/libexec/java_home -V コマンドを叩くとインストールしてあるJavaのバージョンが見れます GraalVMがあればOK

環境変数にセットしてGraalVMを使うようにします

export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)
export PATH=$JAVA_HOME/bin:$PATH

次にネイティブイメージ作成用にnative-imageツールをインストールしておきます

gu install native-image

ここまでやったらGraalVMのインストールは完了です

Gradleプロジェクトの作成

mavenだと便利なQUAKUSプロジェクト作成用のコマンドが用意されているようですが、現状Gradleではまだそういった便利なものは用意されてなかったので自分で作っていきます

まず好きなディレクトリを作成してそこに移動しておきます

mkdir sample-quarkus-app
cd sample-quarkus-app

下記のコマンドでgradleのkotlinプロジェクトが作成できます

gradle init --type=kotlin-application

QUARKUSの設定を追加していく

まずQUARKUSのGradle pluginがまだGradle Plugin Portalに上がってないから./settings.gradle.ktsに下記のコードを追加します

pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
    resolutionStrategy {
        eachPlugin {
            if (requested.id.id == "io.quarkus") {
                useModule("io.quarkus:quarkus-gradle-plugin:${requested.version}")
            }
        }
    }
}

次にbuild.gradle.ktsの中身はこんな感じになりました

import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet

plugins {
    // Apply the Kotlin JVM plugin to add support for Kotlin on the JVM.
    id("org.jetbrains.kotlin.jvm").version("1.3.31")
    id("org.jetbrains.kotlin.plugin.allopen") version "1.3.41"
    java
    id("io.quarkus") version "0.19.1"
}

allOpen {
    annotation("javax.ws.rs.Path")
    annotation("javax.enterprise.context.ApplicationScoped")
    annotation("sample.quarkus.app.MyAnnotation")
}

repositories {
    jcenter()
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
    implementation(enforcedPlatform("io.quarkus:quarkus-bom:0.19.1"))
    implementation("io.quarkus:quarkus-resteasy")
    implementation("io.quarkus:quarkus-resteasy-jsonb")
    implementation("io.quarkus:quarkus-kotlin")
    testImplementation("org.jetbrains.kotlin:kotlin-test")
    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
    testImplementation("io.quarkus:quarkus-junit5")
    testImplementation("io.rest-assured:rest-assured:3.1.0")
}

quarkus {
    setSourceDir(project.projectDir.resolve("src/main/kotlin").absolutePath)
    setOutputDirectory(project.projectDir.resolve("build/classes/kotlin/main").absolutePath)
}

kotlin.sourceSets["test"].kotlin.srcDirs("test")

build.gradleの設定で自分が少しハマったところを書いておくと

  • sourceDirを設定しないと./gradlew quarkusDevで起動した時にエラーが発生します
  • implementation("io.quarkus:quarkus-resteasy-jsonb")
    これを入れないと
@GET
@Produces(MediaType.APPLICATION_JSON)
fun hello() = Greeting("hello")

このコードのようなjsonフォーマットでレスポンスを返すときのjsonシリアライズでコケました

  • implementation("io.quarkus:quarkus-kotlin")
    これを入れないとkotlinで書いたコードのホットリロードがうまく動かなかったです
  • kotlinのクラスはデフォルトでfinalクラスになってしまうのでallopenプラグインを使ってopenにします。ただなぜかApplicationScopedアノテーションを付けたクラスでうまく効いてくれなかったので、別途手動でopenをつけたりもしました。(原因調べきれてないです、すいません)

下記のコマンドで起動することで、ホットリロードが有効な状態で起動できます

./gradlew quarkusDev

ブラウザでlocalhost:8080/helloにアクセスするとjsonのレスポンスが返ってくるのが確認できます

Kotlinのコードはgithubの方を参照していただければと思います https://github.com/yoshi10321/sample-quarkus-app

ネイティブイメージのビルド

環境変数GRAALVM_HOMEをセットしておく必要があります macの場合は下記のように設定します

export GRAALVM_HOME=$HOME/Development/graalvm/Contents/Home/

docker向けにネイティブビルドする場合は下記のコマンドを叩きます

./gradlew buildNative --docker-build=true

実際にDockerにデプロイしていきます Dockerfileを作成して

FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY build/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

下記のコマンドでDocker build&runしていきます

docker build -f Dockerfile -t sample/quarkus-app .
docker run -i --rm -p 8080:8080 sample/quarkus-app
-> % docker run -i --rm -p 8080:8080 sample/quarkus-app
2019-07-21 16:50:38,190 INFO  [io.quarkus] (main) Quarkus 0.19.1 started in 0.013s. Listening on: http://0.0.0.0:8080
2019-07-21 16:50:38,191 INFO  [io.quarkus] (main) Installed features: [cdi, kotlin, resteasy, resteasy-jsonb]

0.013秒で起動したというログが確認できました。 Kotlinで書いたコードがこの速さで起動するのは感動ものです

終わりに

少しハマったところもありましたがQUARKUSのアプリケーションをKotlinとGradleを使ってネイティブ起動するところまでいけました ネイティブコードで実行することでJVMの起動が遅い問題が回避できるため、Kubernetes上で起動させたときのオートスケーリング時のパラメータ設定にそこまで気を使わなくても良くなるんじゃないかなということを期待しています 今後も特にQUARKUS, GraalVM周りの情報には期待していきたいですね。

シェアリマインダーツール作る

夫婦間で買物情報の共有(トイレットペーパー切れたーとか)
やることの共有(洗濯するとか)
っていうことをシェアするためのいい感じのツールが欲しくなったので作ってみる(もうあるかもしれないけどあえて自分で作りたい)

とりあえず要件定義

コンセプト

夫婦用リマインダー共有アプリ 一人以上から使用可能

要件定義

  • ユーザー登録機能
  • リマインダー機能
    • タイトル・日時・完了ステータス
      • 日は必須、時は任意
    • 時間になったら通知
      • 時間設定時のみ
    • リマインダー登録
    • リマインダー更新
    • リマインダー削除
  • リマインダー同期機能
    • アプリ起動のタイミングorリアルタイム?
  • プッシュ通知
    • 新しいリマインダーが登録されました
  • パートナー追加する機能
    • パートナー追加
    • パートナー削除
    • パートナー招待
  • 完了にした人によって表現が変わる機能
    • 奥さんならピンクのハート、旦那さんなら青のハート
    • どっちが完了にしたかわかりやすくする
    • ハートが10個たまるとやった側がご褒美をもらえる
      • 好きな食べ物作ってくれるとか、お出かけ連れてってくれるとか
      • あんま可愛すぎるのもあれだけど

ドメインモデル

最近DDD興味あってドメインモデルの作成をしてみたかったのでやってみた

f:id:dabada10:20190716041141p:plain
ドメインモデル

最初はとりあえず作りきりたいからいくつか機能は削ると思う さて、最後まで作りきれるか

読書ログ:SINGLE TASK 一点集中術

デボラ・ザック著のSINGLE TASKを読んだ そもそもこの本を手にとったきっかけはDaiGoさんのこの動画を見た時に紹介されていたから

www.youtube.com

自分が気になった部分のみ書いていく(引用部分は本の文章そのままじゃなくて自分の中で崩してます)

気になったところ

p24 シングルタスクできない原因

第一の理由は頭の中にある。つい他のことを考えてしまう 第二の理由は外部にある。外部からの刺激が集中力を奪う

p38

マルチタスクは情報の流れを遮断し、短期記憶へと分断する。短期記憶に取り込まれなかったデータは長期記憶にならずに記憶から抜け落ちていく。なのでマルチタスクは能率が落ちる

この部分は特に気になった。自分は記憶力が無いなーという実感があって、せっかく技術的なこと調べてもなかなか思い出せないというのが悩みとして持っていた そのために最近では忘れないために、というか後で思い出しやすいようにBoostnote使って各技術の後で思い出したいような事は積極的にノートに取るようにしている(Boostnoteは検索もできたりカテゴライズしやすいので便利) ただやはり忘れにくくするような頭にできたらどんなに良いだろうかとは思っていた マルチタスクは情報の流れを遮断して短期記憶に分断するというのは一つの原因だったのかもしれない 結構調べ物をしていると途中で別の知らないワード・技術があるとそっちに引っ張られて、本来調べたかったことから脱線してしまうというのがよくある。 今後はなるべく脱線せずに一つのことを調べてなるべくその中で更に深ぼって調べていくといったことを意識してみようかな とはいえ技術的なことを調べるにあたってそもそも前提知識が抜けてる場合はそこを埋めないと先に進めないということもあると思うので難しい その場合は必要な前提知識含めてどうやって学習を進めていくか整理してからアクションを進めていくみたいにしないといけないと思う 必要になる前提知識でないけど気になるものは、本書にもあったパーキングロットという手法が使えると思う パーキングロットとはそもそもはMTGの場などで使われるような手法らしい、まあなんのことはない途中で本題の議論と違うけど話し合わなければならないテーマが上がったら一旦ホワイトボードなどにメモしておいて、一旦その事は無視して引き続き本題の議論を進める。メモしたテーマはまた後で議論するというやり方 これは先程書いた途中で気になった別の技術に出会った場合にも使えるので試してみよう

p74

マルチタスクの誘惑に負けるのは他人の期待や要求に答えねばならないという義務感に駆られているとき なるほど、と思った 人間ついマルチタスクしたくなってしまう生き物らしい。その原因がこれ なんとなくしっくり来た でもこの気持に負けない精神力を鍛えなければならないと思った これはもちろん単に他人の期待とかを無視して跳ね除けろって話ではない いつ取り掛かるか自分で制御しろって話

p?(わすれた

用が済んだらブラウザのタブは閉じる まあそうだよね。自分もブラウザタブ開きすぎ状態になることがほんとによくある 悪い癖だから直そう

p160

内省の時間で共感力が上がる

自らの感情と経験に触れれば触れるほど、他の人の頭にどんな考えがよぎるのかを、より正確に、より豊かに想像できるようになる by イタリアの研究

これは聞いたことなかったので「へー」って感じだった 共感力の欠如は自分にも見られる気がしてたのでちょっと意識して取り組みたい 内省って苦手だけど..

p162

難しい問題に直面しているとき、自分を客観的に捉えると良い判断ができる 直面してる問題に対して自分を「彼」などの三人称を守護にして文章を書いたりして自分を客観的に見つめる方法がよい

これも試してみたい。よく自分一人で悩むより友達とかが相談に乗ってくれたほうが良い結論が出せるみたいなのがあると思うけどそれと同じかと 三人称視点で文章を書いてみるのは面白そうだと思った

さいごに

この本は結構具体的な手法とかも書いてあってよかった

SINGLE TASK 一点集中術――「シングルタスクの原則」ですべての成果が最大になる

SINGLE TASK 一点集中術――「シングルタスクの原則」ですべての成果が最大になる

技術日記6/16-6/21

超亀ログだけど手元に作業ログがあるのでブログにも一応書いておく

AWS Lambdaでスクレイピングアプリを動かす方法を模索してたっぽい

Serverless chromeも検討した GitHub - adieuadieu/serverless-chrome: 🌐 Run headless Chrome/Chromium on AWS Lambda (maybe Azure, & GCP later)

ただ今回スクレイピングをするにあたってセキュアな情報を扱う必要があったためやはり公式じゃないものは使いたくなかった、かつリポジトリのメンテが最近されてなさそうだったので使用するのはやめておいた またこういったものを使ったとき使用するchromeブラウザのアップデートが自分でできないというところも気になる スクレイピングをプロダクション環境で使うのなら、ブラウザの脆弱性が見つかったときすぐパッチを当てて対応できるような状態にはしておきたい あとserverless-chromeだとchromeブラウザ限定になってしまうのも引っかかった 現在ではほとんどなくなってきているけど稀にInternetExplolerしか対応してないページもあるかもしれない(そんなページもうほぼないと思うしそんなサイトは相手にしなくていい気もするけど) まあそうじゃなくともChrome系でうまくスクレイピングできなかった時にFirefoxなど別の手段が選べるような状況にはしておきたかった 同じような理由でpuppeteerは使うのをやめた chromeチームがメンテしてるので公式という安心感はあるけどブラウザが縛られてしまう なので今回はwebDriver使っている

AWS Lambdaにデプロイするにあたってアップロードの上限サイズが有る スクレイピングのアプリをtypescriptで書いているがnode_modulesとかもあげようとすると普通に上限に引っかかる? 何か圧縮する方法があったのだろうか?ちょっと調べられていない

そういった事もあって結局AWS Lambdaの使用はやめることにした

ちょっと他の日のログをまとめるのがめんどいのでこれだけにしとく