Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spring BootをKotlinで作成しAmazon Elastic Container Service (ECS) で稼働させる

Spring BootをKotlinで作成しAmazon Elastic Container Service (ECS) で稼働させる

Spring BootをKotlinで作成しAmazon Elastic Container Service (ECS) で稼働させる
(JSUG勉強会 2019その9 Spring&AWS)

Fumihiko Shiroyama

September 20, 2019
Tweet

More Decks by Fumihiko Shiroyama

Other Decks in Programming

Transcript

  1. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Application Prototyping Solutions Architect
    Fumihiko Shiroyama
    September 20, 2019
    Spring BootをKotlinで作成し
    Amazon Elastic Container Service (ECS) で稼働させる
    JSUG 勉強会 2019 その9 Spring&AWS

    View Slide

  2. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    スライドURL
    http://bit.ly/2lW4MTT

    View Slide

  3. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    ⾃⼰紹介
    名前:⽩⼭ ⽂彦(しろやま ふみひこ)
    所属:アマゾン ウェブ サービス ジャパン株式会社
    アプリケーションプロトタイピングソリューションアーキテクト
    経歴:インフラエンジニア、バックエンド開発者
    モバイルアプリ開発者、クラウドアーキテクト
    趣味:⼦育て!、懸垂、⾃動テスト#

    View Slide

  4. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    ⾃動テスト⼤好きです!
    • Androidテスト全書という本を出しました
    • ⾃動テストの種類やカバーする範囲、モック
    (テストダブル)の考え⽅、CI/CDとの統合
    など、Androidに限らずJava/Kotlin開発全般
    に使える知識を解説しました
    ☞テストコードは全編Kotlinで解説しています!

    View Slide

  5. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Why Kotlin?

    View Slide

  6. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    What’s Kotlin
    • いわゆる”AltJava”
    • 2011年に登場した⽐較的新しい⾔語
    • 完結で強⼒な記法
    • Null安全
    • Javaとの相互運⽤性が⾼い
    • JetBrains製でIntelliJとの相性も良い

    View Slide

  7. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    public class Task {
    final long id;
    final String content;
    final boolean done;
    public Task(long id, String content, boolean done) {
    this.id = id;
    this.content = content;
    this.done = done;
    }
    @Override
    public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Task task = (Task) o;
    return id == task.id &&
    done == task.done &&
    Objects.equals(content, task.content);
    }
    @Override
    public int hashCode() {
    return Objects.hash(id, content, done);
    }
    @Override
    public String toString() {
    return "Task{" +
    "id=" + id +
    ", content='" + content + '\'' +
    ", done=" + done +
    '}';
    }
    }

    View Slide

  8. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    data class Task(val id: Long,
    val content: String,
    val done: Boolean)
    データクラス

    View Slide

  9. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    data class Task(val id: Long,
    val content: String,
    val done: Boolean)

    View Slide

  10. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    data class Task(val id: Long,
    val content: String,
    val done: Boolean)

    View Slide

  11. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    data class Task(val id: Long,
    val content: String,
    val done: Boolean)

    View Slide

  12. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    fun hasItem(list: List, predicate: (item: Int) -> Boolean): Boolean {
    for (i in list) {
    if (predicate(i)) return true
    }
    return false
    }
    関数リテラル

    View Slide

  13. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    fun hasItem(list: List, predicate: (item: Int) -> Boolean): Boolean {
    for (i in list) {
    if (predicate(i)) return true
    }
    return false
    }

    View Slide

  14. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    fun hasItem(list: List, predicate: (item: Int) -> Boolean): Boolean {
    for (i in list) {
    if (predicate(i)) return true
    }
    return false
    }

    View Slide

  15. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    fun hasItem(list: List, predicate: (item: Int) -> Boolean): Boolean {
    for (i in list) {
    if (predicate(i)) return true
    }
    return false
    }

    View Slide

  16. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    fun hasItem(list: List, predicate: (item: Int) -> Boolean): Boolean {
    for (i in list) {
    if (predicate(i)) return true
    }
    return false
    }

    View Slide

  17. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    val isEven: (item: Int) -> Boolean = { it % 2 == 0 }
    hasItem(listOf(1, 2, 3), isEven)

    View Slide

  18. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    val isEven: (item: Int) -> Boolean = { it % 2 == 0 }
    hasItem(listOf(1, 2, 3), isEven)

    View Slide

  19. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    val isEven: (item: Int) -> Boolean = { it % 2 == 0 }
    hasItem(listOf(1, 2, 3), isEven)

    View Slide

  20. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    val isEven: (item: Int) -> Boolean = { it % 2 == 0 }
    hasItem(listOf(1, 2, 3), isEven)

    View Slide

  21. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    hasItem(listOf(1, 2, 3), { it % 2 == 0 })

    View Slide

  22. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    hasItem(listOf(1, 2, 3)) { it % 2 == 0 }

    View Slide

  23. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    fun sum(nums: List): Int {
    tailrec fun go(nums: List, acc: Int): Int =
    if (nums.isEmpty()) acc
    else go(nums.drop(1), acc + nums.first())
    return go(nums, 0)
    }
    ⾼階関数

    View Slide

  24. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    fun sum(nums: List): Int {
    tailrec fun go(nums: List, acc: Int): Int =
    if (nums.isEmpty()) acc
    else go(nums.drop(1), acc + nums.first())
    return go(nums, 0)
    }

    View Slide

  25. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    fun sum(nums: List): Int {
    tailrec fun go(nums: List, acc: Int): Int =
    if (nums.isEmpty()) acc
    else go(nums.drop(1), acc + nums.first())
    return go(nums, 0)
    }

    末尾再帰最適化

    View Slide

  26. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    fun sum(nums: List): Int {
    tailrec fun go(nums: List, acc: Int): Int =
    if (nums.isEmpty()) acc
    else go(nums.drop(1), acc + nums.first())
    return go(nums, 0)
    }

    View Slide

  27. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Null安全
    val nullable: String? = "foo"
    val str = nullable?.let { it.toUpperCase() } ?: "bar"

    View Slide

  28. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    val nullable: String? = "foo"
    val str = nullable?.let { it.toUpperCase() } ?: "bar"

    View Slide

  29. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    val nullable: String? = "foo"
    val str = nullable?.let { it.toUpperCase() } ?: "bar"

    View Slide

  30. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    val nullable: String? = "foo"
    val str = nullable?.let { it.toUpperCase() } ?: "bar"

    View Slide

  31. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    val nullable: String? = "foo"
    val str = nullable?.let { it.toUpperCase() } ?: "bar"

    View Slide

  32. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    val nullable: String? = "foo"
    val str = nullable?.let { it.toUpperCase() } ?: "bar"

    View Slide

  33. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    object Singleton {
    @JvmStatic
    fun staticFunc() {
    }
    }
    // from Java
    Singleton.staticFunc();
    Java相互運⽤

    View Slide

  34. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    data class Task @JvmOverloads constructor(val id: Long,
    val content: String,
    val done: Boolean = false)
    // from Java
    new Task(1, "foo");
    new Task(2, "bar", true);

    View Slide

  35. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    data class Task @JvmOverloads constructor(val id: Long,
    val content: String,
    val done: Boolean = false)
    // from Java
    new Task(1, "foo");
    new Task(2, "bar", true);

    View Slide

  36. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    data class Task @JvmOverloads constructor(val id: Long,
    val content: String,
    val done: Boolean = false)
    // from Java
    new Task(1, "foo");
    new Task(2, "bar", true);

    View Slide

  37. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    data class Task @JvmOverloads constructor(val id: Long,
    val content: String,
    val done: Boolean = false)
    // from Java
    new Task(1, "foo");
    new Task(2, "bar", true);

    View Slide

  38. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Spring with Kotlin

    View Slide

  39. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Spring Initializr
    https://start.spring.io/

    View Slide

  40. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    interface Greeter {
    fun hello(name: String): String
    }

    View Slide

  41. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    @Component
    class JapaneseGreeter : Greeter {
    override fun hello(name: String): String = "こんにちは、$name!"
    }

    View Slide

  42. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    @RestController
    class HelloController(val greeter: Greeter) {
    @GetMapping("hello")
    fun hello(@RequestParam("name") name: String): String = greeter.hello(name)
    }

    View Slide

  43. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Docker化する

    View Slide

  44. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Docker化するメリット
    • 簡単に捨てられる開発環境
    • ステートレスなアーキテクチャの強制
    • 本番運⽤を⾒据えられる

    View Slide

  45. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    FROM amazoncorretto:8
    LABEL maintainer="Fumihiko Shiroyama "
    RUN mkdir /app
    WORKDIR /app
    COPY ./build/libs/*.jar /app/app.jar
    ENTRYPOINT [ "sh", "-c", "java -jar /app/app.jar"]
    Dockerfile
    docker build -t spring-demo .
    docker container run --rm --name spring-test --net=web --env-file=default.env -d -p 8080:8080 spring-demo:latest

    View Slide

  46. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    DBもDocker化しよう

    View Slide

  47. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    XCTestのはじめ⽅
    % docker container run --rm --name mysql-test
    --net=web -e MYSQL_ROOT_PASSWORD=testtest \
    -d -p 3306:3306 mysql:latest \
    --character-set-server=utf8mb4
    --collation-server=utf8mb4_unicode_ci
    # prepare setup.sql
    % docker exec mysql-test mysql \
    -u root --password='testtest' -e"$(cat setup.sql)"

    View Slide

  48. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    application.yml
    環境変数

    View Slide

  49. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    interface SampleRepository : JpaRepository
    @Controller
    @RequestMapping("tasks")
    class TaskController(private val repository: SampleRepository) {
    @GetMapping("")
    fun index(model: Model): String {
    val tasks = repository.findAll()
    model.addAttribute("tasks", tasks)
    return "tasks/index"
    }
    }
    ORMはJPAを利⽤

    View Slide

  50. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    DBに接続完了!

    View Slide

  51. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    AWSにデプロイする

    View Slide

  52. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    今回利⽤するコンポーネント
    • Amazon Aurora
    • Amazon Elastic Container Service
    • CodeStar

    View Slide

  53. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon Aurora

    View Slide

  54. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon Aurora
    • エディションはMySQL互換
    • 1つのライターと複数のリーダ
    • 書き込みエンドポイントと読み込みエンドポイントをメモ
    ☞基本的なアクセス⽅法はこれまでと変わらない

    View Slide

  55. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon Elastic Container Service

    View Slide

  56. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECS
    • Amazon ECRにイメージの登録
    • タスクの作成
    • クラスタの作成
    • サービスの作成

    View Slide

  57. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECR

    View Slide

  58. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECR
    $(aws ecr get-login --no-include-email --region ap-
    northeast-1)
    docker build -t spring-demo .
    docker tag spring-demo:latest xxxxxx.dkr.ecr.ap-
    northeast-1.amazonaws.com/spring-demo:latest
    docker push xxxxxx.dkr.ecr.ap-northeast-
    1.amazonaws.com/spring-demo:latest
    ☝解説に従うだけ!

    View Slide

  59. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECSの概念
    • Dockerコンテナを簡単にデプロイ・管理するためのマ
    ネージド・サービス
    • コントロールプレーンにはECS/EKSが選べる
    • データプレーンにはEC2/Fargateが選べる
    • Fargateを使うとリソースについて意識する必要がない

    View Slide

  60. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECSの登場⼈物
    • タスク
    • Dockerコンテナと考えればよい。タスク定義から作る
    • サービス
    • 複数のタスクからなる。「APIサーバ」「HTTPサーバ」など
    の役割が1単位となる。
    • クラスタ
    • 複数のサービスを束ねたもの

    View Slide

  61. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECS – クラスター

    View Slide

  62. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECS – タスク

    View Slide

  63. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECS – タスク

    View Slide

  64. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECS – タスク
    ☝環境変数にDB接続情報を格納

    View Slide

  65. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Amazon ECS – サービス

    View Slide

  66. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.

    View Slide

  67. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    まとめ
    • ローカルから段階的にDocker化することで簡単にECS化できた
    • Fargateは下回りを意識しなくてもコンテナが扱えて便利
    • サービスをALBに接続することでロードバランシングなども可能
    • CodeStar等のCI/CDツールと連携すると、コードチェックインを契
    機にビルド、ECRの作成、ECSへのデプロイなども⾃動化できる

    View Slide

  68. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    ご清聴ありがとございました(

    View Slide

  69. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    質疑応答

    View Slide

  70. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    Appendix

    View Slide

  71. © 2019, Amazon Web Services, Inc. or its Affiliates. All rights reserved.
    参考⽂献
    • ⻑澤太郎(2017)『Kotlin Web アプリケーション – 新しいサーバ
    サイドプログラミング』リックテレコム.

    View Slide