はじめに
学習を兼ねて、AWS上で、ネットワークとサーバーを構築して、Web APIの機能を持つアプリケーションを動かすデモを作成したので、手順をまとめておきます。
APIアプリケーションはKotlin製で、Ktorを利用しています。
システム構成
AWSの構成は以下の図の通りです。
APIアプリケーションを動かすだけであれば、この構成よりも最低限の内容で事足りるかと思いますが、実務で使用されていそうなAWSのサービスをいくつか試してみたかったので、このような構成としています。

- 東京リージョンにAmazon VPCを構築
- ap-northeast-1aに Public / Private サブネットを1つずつ構築
- ap-northeast-1cに Public / Private サブネットを1つずつ構築
- Amazon EC2インスタンスをそれぞれのPrivate Subnetに構築
- Application Load Balancerで、インターネットからのリクエストをそれぞれのEC2インスタンスへルーティング
- Kotlin + Ktor による Web API サーバーをそれぞれのEC2インスタンス上で動かす
- Amazon S3 に Web API アプリケーションの実行ファイルを格納し、EC2インスタンスからアクセスしてダウンロードする
- EC2インスタンスへのアクセスはAmazon Systems Manager による踏み台レス運用とする
ネットワーク構築
1. Amazon VPCを作成
- IPv4 CIDRを
10.0.0.16で作成
2. サブネットを作成
2-1. パブリック用に2つ作成
- 先述で作成したVPCに関連づける
- 東京リージョン内の別々のアベイラビリティゾーンで2つ作成
ap-northeast-1a- IPv4 CIDRを
10.0.1.0/24
- IPv4 CIDRを
ap-northeast-1c- IPv4 CIDRを
10.0.2.0/24
- IPv4 CIDRを
2-2. プライベート用に2つ作成
- 先述で作成したVPCに関連づける
- 東京リージョン内の別々のアベイラビリティゾーンで2つ作成
ap-northeast-1a- IPv4 CIDRを
10.0.3.0/24
- IPv4 CIDRを
ap-northeast-1c- IPv4 CIDRを
10.0.4.0/24
- IPv4 CIDRを
3. パブリックサブネットをインターネットに接続
- インターネットゲートウェイを作成
- ルートテーブルを作成
- 先述で作成したVPCに関連づける
- サブネットを関連づける
- 先述で作成したパブリックサブネット2つを関連づける
- ルートを編集する
- 送信先のCIDRを
0.0.0.0/0とし、ターゲットを先述で作成したインターネットゲートウェイを指定する
- 送信先のCIDRを
4. ロードバランサーを作成
4-1. ターゲットグループを2つ作成
- ターゲットタイプは
インスタンスとする - ロードバランサーとターゲット間の通信するプロトコルは
Http - ターゲットがトラフィックを受信するポートを、
- 1つ目のターゲットグループでは
8080と指定 - 2つ目のターゲットグループでは
8081と指定
- 1つ目のターゲットグループでは
- ヘルスチェック設定
- 対応させるプロトコルは
Http - パスは、
- 1つ目のターゲットグループでは
/api/statusと指定 - 2つ目のターゲットグループでは
/admin/statusと指定
- 1つ目のターゲットグループでは
- 対応させるプロトコルは
4-2. Application Load Balancerを作成
- スキーム設定
- インターネット向け
- ロードバランサーのIPタイプは
IPv4
- ネットワークマッピング設定
- VPCは、先述で作成したVPCを指定
- アベイラビリティゾーンとサブネット
ap-northeast-1aを指定し、作成したパブリック用のサブネットを指定ap-northeast-1cを指定し、作成したパブリック用のサブネットを指定
- セキュリティグループを設定
- ALB用のセキュリティグループを作成し指定する
- 作成するセキュリティグループはインバウンドで全てのIPからのHttpを受け付けるようにする
- ALB用のセキュリティグループを作成し指定する
- リスナーとルーティング
- リスナーはプロトコルを
Httpとし、ポートを80とする - リッスンしたリクエストをルーティングするターゲットに先述で作成したターゲットグループを指定
- リスナーはプロトコルを
サーバー構築
1. Amazon EC2インスタンスを2つ作成
- AMIは
Amazon Linux 2023 kernel-6.1 - インスタンスタイプは
t3.micro - キーペアはRSAのpemファイルを作成して使用する
- ネットワーク設定
- VPCは先述で作成したVPCを指定
- サブネットは先述で作成したプライベートサブネットを指定する
- 1つ目のEC2インスタンスでは、アベイラビリティゾーンAのプライベートサブネットを指定
- 2つ目のEC2インスタンスでは、アベイラビリティゾーンCのプライベートサブネットを指定
- ファイアーウォールで、セキュリティグループを設定
- セキュリティグループを作成し指定する
- 1つ目のEC2インスタンスでは
- インバウンドで
ALB用のセキュリティグループをソースとして、カスタムTCPで8080ポートで待ち受けるようにする - アウトバウンドで、全てのプロトコルで全てのIPを送信先として指定する
- インバウンドで
- 2つ目のEC2インスタンスでは
- インバウンドで
ALB用のセキュリティグループをソースとして、カスタムTCPで8081ポートで待ち受けるようにする - アウトバウンドで、全てのプロトコルで全てのIPを送信先として指定する
- インバウンドで
- 1つ目のEC2インスタンスでは
- セキュリティグループを作成し指定する
- プライマリIPを
- 1つ目のEC2インスタンスでは
10.0.3.10に設定 - 2つ目のEC2インスタンスでは
10.0.4.10に設定
- 1つ目のEC2インスタンスでは
2. EC2インスタンスにAmazon System Managerを使用してアクセスできるようにする
- SSM Agentのインストール
- 今回はAmazon Linux 2023を利用していて、プリインストールされているため不要
- IAMロールを作成
AmazonSSMManagedInstanceCoreポリシーを許可する
- 作成したIAMロールを2つのEC2インスタンスにアタッチする
- セキュリティグループを作成
- 作成するセキュリティグループはインバウンドで先述で作成したVPCに割り当てたIP CIDRからのHttpsを受け付けるようにする
- VPCエンドポイントを4つ作成
- タイプは
AWSのサービスを指定 - サービスは以下の4つを指定(VPCエンドポイントをサービス毎に1つ作成する)
com.amazonaws.region.ssm com.amazonaws.region.ec2messages com.amazonaws.region.ssmmessages com.amazonaws.region.s3- VPCは先述で作成したVPCを指定する
- エンドポイントを作成するサブネットは
- アベイラビリティゾーンAのプライベートサブネットを指定
- アベイラビリティゾーンCのプライベートサブネットを指定
- タイプは
APIアプリケーション実装
1. Ktorを利用するための設定
val ktorVersion by extra("2.3.12")
dependencies {
implementation("io.ktor:ktor-server-netty:${ktorVersion}")
implementation("io.ktor:ktor-server-core:${ktorVersion}")
}
2. Ktorを利用して、Http通信ができるようにする
- アベイラビリティゾーンAで動かすAPIサーバ
- ポート
8080 - IP
0.0.0.0 - エンドポイント
/api/status
- ポート
- アベイラビリティゾーンCで動かすAPIサーバ
- ポート
8081 - IP
0.0.0.0 - エンドポイント
/admin/status
- ポート
以下はアベイラビリティゾーンAで動かすAPIサーバのソースです。
アベイラビリティゾーンCで動かすAPIサーバとの差分はポートとエンドポイントが異なるだけです。
package com.example
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
fun main() {
embeddedServer(
factory = Netty,
port = 8080,
host = "0.0.0.0",
module = { module() }
).start(wait = true)
}
fun Application.module() {
routing {
get("/api/status") {
call.respondText("{\"status\":\"ok\"}", io.ktor.http.ContentType.Application.Json)
}
}
}
APIアプリケーションをEC2上で動くようにする
1. アプリケーションのJarファイルをEC2インスタンス上に格納する
1-1. Amazon S3上にJarファイルをアップロードする
2つのAPIアプリケーションで、依存関係を全て含めてフルビルドしたJarファイルを作成するcom.github.johnrengelman.shadowを利用するので、build.gradleに依存関係を追加
plugins {
id("com.github.johnrengelman.shadow") version "8.1.1"
}
./gradlew shadowJarコマンドでFat Jarを作成する。
ビルドするとbuild/libs配下にJarファイルが生成される。
次にS3でバケットを作成し、JarファイルをS3のコンソール画面上からアップロードする。
EC2インスタンス上で、S3に格納されているJarファイルをダウンロードする
EC2インスタンスにアタッチしているIAMロールに、S3の読み取り権限を付与するAmazonS3ReadOnlyAccess
System Managerを使用して2つのEC2インスタンスに接続する。
接続したらコンソール上で、以下のコマンドで、S3からJarファイルをダウンロードする
mkdir -p /opt/myapp
sudo chown ec2-user:ec2-user /opt/myapp
sudo aws s3 cp s3://api-server-test-20260104/KtorApiApp-1.0-SNAPSHOT.jar /opt/myapp
2. EC2インスタンス上にJDKをインストールする
アプリケーションはKotlinなので、2つのEC2インスタンス上で実行するためにJDKをインストールする
sudo yum update -y
sudo yum install java-21-amazon-corretto -y
3. EC2インスタンス上で、アプリケーションを起動する
2つのEC2インスタンスにSystem Managerで接続後に、以下のコマンドを叩き、Jarファイルを実行する。
java -jar your-app.jar
4. APIアプリケーションに対してHttpリクエストを送信してレスポンスが返るか確認する
Webブラウザで以下のUrlにアクセスして、疎通していることを確認する
// request
http://<アプリケーションロードバランサーのDNS名>/api/status
// response
{"status":"ok"}
// request
http://<アプリケーションロードバランサーのDNS名>/admin/status
// response
{"status":"ok"}
参考
https://ktor.io/docs/server-engines.html
https://qiita.com/kmdsbng/items/ab093146a62afa34b739
https://qiita.com/hisato_imanishi/items/9993c86e02f5cbf4deae
![[AWS] [Ktor] クラウド上でAPIサーバーを動かしてみる入門](/_next/image?url=https%3A%2F%2Fimages.microcms-assets.io%2Fassets%2F08d0380f37204cc9a8ae9db8eccb2fb1%2F736056d0c9d24921ad85a6af74880f31%2FHP%25E8%25A8%2598%25E4%25BA%258B%25E3%2582%25B5%25E3%2583%25A0%25E3%2583%258D%25E3%2582%25A4%25E3%2583%25AB%2520(1200%2520x%2520630%2520px)%2520(11).png&w=3840&q=75)