Xcodeを使いAWS Lambda上でSwiftを使用する – WWDC2020

Session概要

サーバレス機能はクラウド上でイベント駆動型、またはその他の目的が限定されたタスクを実行することでますます一般的になっており、開発者がより容易に計算コストを見積もったり、管理したりすることを可能にします。
Swiftでサーバレス機能を構築し、Xcodeを使いローカルでデバッグし、これらの機能をAWS Lambdaプラットフォームに追加するための新しいSwift AWS Lambda Runtimeパッケージの使用方法を学びましょう。
低メモリフットプリント(システム稼働時におけるメモリの使用量)、決定姓パフォーマンス、素早い起動時間のおかげで、AWS Lambda上でSwiftがどのように活躍するかについて https://developer.apple.com/videos/play/wwdc2020/10644/

背景

近年多くのシステムではサーバコンポーネントに並び、iOS, macOS, tvOSまたはwatchOSアプリケーションなどのクライアントコンポーネントを備えている。
サーバコンポーネントによりクライアントアプリがその機能をクラウドに拡張することができます。
例えば、デバイスで扱えないデータへのアクセスやbackgroundで実行されるタスクのオフロード(負荷軽減)や思い演算処理を伴うタスクのオフロードなどです。
サーバコンポーネントでは、多様なツールやメソッドを使用して構築されるため、クライアントとサーバのチームの間に垣根ができてしまう。
サーバレス機能はサーバとクライアントの間を近づけるためのプログラミングモデルを提供する

サーバレス機能の利点

  • クラウド上でイベント駆動型やアドホック(一時的、その場限り)な演算タスクを実行する場合に有用
  • より動的にリソースを割り当てるシステムに置換することで、専用リソースを実行する必要性を解決している
  • 多くの場合、サーバレス機能によって開発者はスケーリング(拡大縮小)と演算コストの管理が容易にできるようになる
  • AWS Lambdaはイベント駆動型サーバレスプラットフォームの一つ

サーバレス機能の注意点

  • 細心の注意を払うのは、システム全体のコストに直接的なインパクトを与える演算リソースの使用
  • Swiftは開発者に優しく、リソース使用量(メモリフットプリント)が低いことからサーバレス機能の構築において良い選択肢

Swift on AWS Lambda

上記背景からAppleは、サーバレス機能をXcodeで構築、デバッグでき、AWS Lambdaにデプロイ可能にした。
これはXcode上でiOSやmacOSクライアントアプリを記述しつつ、Lambdaスクリプトを実行、確認できることを意味する

サンプルコード

import AWSLambdaRuntime

// コンテキスト、イベントのペイロード、completion handlerを受け取る
Lambda.run { (_, name: String, callback) in
    callback(.success("Hello, \(name)!"))
}
import AWSLambdaRuntime
import NIO

// SwiftNIOのEventLoopを公開し、Lambda関数が同一スレッドを共有できるようにするAPI
// これによりネットワーキング処理スタックなどでコンテキストの問題を回避する
// Lambda関数がEventLoopをブロックしないように配慮する必要がある
struct Handler: EventLoopLambdaHandler {
    typealias In = String
    typealias Out = String

    func handle(context: Lambda.Context, event: String) -> EventLoopFuture<String> {
       context.eventLoop.makeSucceededFuture("Hello, \(event)!")
    }
}

Lambda.run(Handler())
import AWSLambdaRuntime

// ほとんどのケースでLambdaのペイロードはJSONベースであるため、ライブラリの典型的な使用方法を示している
struct Request: Codable {
    let name: String
    let password: String
}

struct Response: Codable {
    let message: String
}

Lambda.run { (_, request: Request, callback) in
    callback(.success(Response(message: "Hello, \(request.name)!")))
}

Xcodeプロジェクトのサンプル

  • GitHubの swift-server/ swift-aws-lambda-runtime にLambda関数およびサンプルプロジェクトがあります
  • Xcode TargetにLambdaターゲットを指定することで、Xcodeでプログラムを実行可能
  • AWS Lambda RuntimeではAWSのランタイムエンジンをシミュレータでシミュレート可能
    • Xcodeのscheme設定で LOCAL_LAMBDA_SERVER_ENABLEDtrueに設定することで設定可能
    • LocalLambdaServerがlocalhostのport:7000で起動して実行可能

How does it work

  • 下記の例では、ローカルでiOSアプリとLambda関数のmock環境(AWS Lambda Runtime)の2つのプロセスが起動し実行している

  • AWS Lambdaをデプロイすると、AWS Lambda Runtimeエンジンがプログラムのライフサイクルを制御する
  • Lambdaはランタイムエンジンをポーリングし、使用不可の場合は使用可能になるまで、演算リソースがハイバネート(休止状態になる)される
    • ハイバネーションとは
      • コンピュータの実行状態を保存して電源を切り、次に起動したときに迅速に休止前の状態を再現する機能
  • 下記の例のフロー
    • クライアントアプリ → AWS API Gateway → Lambda Queueに追加 → Lambda実行
  • Swift on Lambdaを稼働させるためには、
    • SwiftプログラムをAmazon Linux 2で構築、実行できるようにしておく必要がある
      • EC2とAWS LambdaではAmazon Linux 2環境は用意されている
    • また、Swift Lambda Runtimeライブラリ(AWS Lambda Runtime API)の構築が必要
  • プログラムのライフサイクルはライフサイクルのループで管理されライブラリで提供される

Lambda関数のデプロイ方法

  • AWS SAM(Serverless Application Model) CLI or AWS CLI or Amazon Management Consoleを使用する
  • Lambda関数を .zip ファイルにまとめる
  • AWSにアップロードして、新しいコードバージョンに更新する
  • デプロイ方法の詳細はAWS公式ドキュメントに網羅的に記載されているのでこちらを参照ください

Lambda関数コードの注意点

  • サーバレス機能はステートレスな設計であるため、グローバルな可変状態を回避するようにする
    • 具体的には必要以上に長くメモリを保持しないようにする
最新情報をチェックしよう!