⚙️
Rust製APIをECSで動かすまで
GOAL
- RustでAPIを作る
- GitHub Actions & AWS CDKでデプロイ
- APIのDockerイメージをECRに登録
- ECS & Fargateで動かす
API
まずはECSで動かすサンプルAPIを作ります。
今回のメインではないので、Actix Webのスタートくらいの実装に留めます。
lib.rs
use actix_web::{get, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Serialize, Debug)]
pub struct User {
id: u32,
name: String,
}
#[get("/")]
pub async fn users() -> impl Responder {
HttpResponse::Ok().json(User {
id: 1,
name: "Mosu".into(),
})
}
main.rs
extern crate rust_aws_sample;
use actix_web::{App, HttpServer};
use std::io::Result;
#[actix_web::main]
async fn main() -> Result<()> {
HttpServer::new(|| {
App::new().service(rust_aws_sample::users)
})
.bind(("0.0.0.0", 8080))? // Docker用の設定です
.run()
.await
}
Docker
作成したAPIをDocker上で起動できるようにします。
Dockerfile
FROM rust:1.67.1
WORKDIR /usr/src/app
COPY . .
RUN cargo build --release
EXPOSE 8080
ENTRYPOINT ["cargo", "run", "-r"]
.dockerignore
.gitignore
.github
.vscode
target
tests
cdk
README.md
一度動作確認しておきます。
shell
docker build . -t rust-app
docker run -d --rm -p 8080:8080 rust-app
curl "http://localhost:8080/"
Hello worldが表示されればOKです。
CDK
デプロイをGitHub Actions経由で行いたいので、今回はCDK(TypeScript)を利用します。
まずはTypeScriptとCDKの準備をします。
Code
Rustプロジェクトと同階層にCDK用のディレクトリを作って、そこで作業していきます。
./cdk
npx cdk init -l typescript
テストコードなどが生成されますが、不要な場合は適宜削除してください。
また、DockerイメージをECRにアップロード後、ECSで稼働させるまで行うので、こちらを使います。
./cdk
npm i cdk-docker-image-deployment
準備が整ったので、CDKのコードを書いていきます。
.cdk/lib/cdk-stack.ts
import * as cdk from "aws-cdk-lib";
import { Vpc } from "aws-cdk-lib/aws-ec2";
import { Repository } from "aws-cdk-lib/aws-ecr";
import { Cluster, ContainerImage } from "aws-cdk-lib/aws-ecs";
import { ApplicationLoadBalancedFargateService } from "aws-cdk-lib/aws-ecs-patterns";
import {
Destination,
DockerImageDeployment,
Source,
} from "cdk-docker-image-deployment";
import { Construct } from "constructs";
export class CdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// ECRに登録するTAGを環境変数から取得します
const tag = process.env.CURRENT_TAG || "latest";
const repository = new Repository(this, "RustAppRepository", {
repositoryName: "rust-app",
imageScanOnPush: true,
});
new DockerImageDeployment(this, "RustAppDeploy", {
source: Source.directory("../"),
destination: Destination.ecr(repository, { tag }),
});
const vpc = new Vpc(this, "RustAppVpc", { maxAzs: 2 });
const cluster = new Cluster(this, "RustAppCluster", {
vpc,
clusterName: "rust-app-cluster",
});
new ApplicationLoadBalancedFargateService(this, "RustFargateService", {
cluster,
taskImageOptions: {
image: ContainerImage.fromEcrRepository(repository, tag),
containerPort: 8080, // DockerfileでEXPOSEしたポート
},
});
}
}
GitHub Actions
以下のように作ります。
- mainにpushされたらテスト
- tagがpushされたらCDKデプロイ
Test
.github/workflow/test.yml
name: Rust test
on:
push:
branches: ["main"]
env:
CARGO_TERM_COLOR: always
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Run tests
run: cargo test --verbose
AWS
さて、あとはデプロイするようにymlを書くだけですが、ここでAWSの設定が必要になります。
というのも、GitHub上でAWSのリソースにアクセスするための情報が、何も設定していないからです。
GitHubの設定でSecretsにAWSの機密情報を登録して、yml上でenvに設定する方法もありますが、好ましくないのでOIDCを使った方法にします。
手順は公式ドキュメントに記載されているので、ここでは割愛します。
Deploy
ようやく準備が整ったので、デプロイ用のWorkflowを作成します。
Assume Roleには用意されているActionを利用します。
.github/workflow/deploy.yml
name: Rust deploy
on:
push:
tags:
- 'v*' # v0.1.0 のようなtagをpushした際にデプロイ実行
env:
CARGO_TERM_COLOR: always
CURRENT_TAG: ${{ github.ref_name }} # pushされたtagを環境変数に設定
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
- name: Install package
run: npm ci
working-directory: cdk
- name: Assume Role
uses: aws-actions/configure-aws-credentials@v1-node16
with:
# FIXME 適宜修正してください、OIDCの設定をしたRoleです
role-to-assume: arn:aws:iam::<account_id>:role/<role name>
role-session-name: deploysession
aws-region: ap-northeast-1
- name: CDK Deploy
run: npx cdk deploy --require-approval never
working-directory: cdk
まとめ
以上で、CDKとGitHub Actionsを使って
Releaseを作成すると、ECSへデプロイされる仕組みが完成しました。
Discussion