Развёртывание на AWS Lambda с помощью Serverless Application Model (SAM)
Это руководство демонстрирует, как развернуть рабочую нагрузку Swift на стороне сервера на AWS с помощью инструментария AWS Serverless Application Model (SAM). Рабочая нагрузка представляет собой REST API для отслеживания списка задач. Он развёртывает API с помощью Amazon API Gateway. Методы API хранят и извлекают данные в базе данных Amazon DynamoDB с использованием функций AWS Lambda.
Архитектура
- Amazon API Gateway получает запросы API
- API Gateway вызывает функции Lambda для обработки событий PUT и GET
- Функции Lambda используют AWS SDK for Swift и Swift AWS Lambda Runtime для извлечения и сохранения элементов в базе данных
Предварительные условия
Для создания этого примера приложения вам потребуются:
- AWS учётная запись
- AWS Command Line Interface (AWS CLI) - установите CLI и настройте его с вашими учётными данными AWS
- AWS SAM CLI - командная утилита для создания серверных нагрузок на AWS
- Docker Desktop - для компиляции кода Swift в образ Docker
Шаг 1: Создание нового проекта SAM
Проект SAM создаёт ресурсы (функции Lambda, API Gateway и таблицу DynamoDB) в вашей учётной записи AWS. Вы определяете ресурсы в шаблоне YAML.
Создайте папку для вашего проекта и новый файл template.yml.
mkdir swift-lambda-api && cd swift-lambda-api
touch template.yml
Откройте файл template.yml и добавьте следующий код. Просмотрите комментарии в коде, чтобы определить, что создаётся в каждом разделе.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
# DynamoDB table to store your data
SwiftAPITable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: id
Type: String
# Lambda function to put items to the database
PutItemFunction:
Type: AWS::Serverless::Function
Properties:
# package the function as a Docker image
PackageType: Image
Policies:
# allow function to read and write to database table
- DynamoDBCrudPolicy:
TableName: !Ref SwiftAPITable
Environment:
# store database table name as an environment variable
Variables:
TABLE_NAME: !Ref SwiftAPITable
Events:
# handles the POST /item method of the REST API
Api:
Type: HttpApi
Properties:
Method: post
Path: /item
Metadata:
# location of the code and Docker file for function
DockerContext: ./src/put-item
Dockerfile: Dockerfile
DockerBuildArgs:
TARGET_NAME: put-item
# Lambda function to retrieve items from database
GetItemsFunction:
Type: AWS::Serverless::Function
Properties:
# package the function as a Docker image
PackageType: Image
Policies:
# allow function to read and write to database table
- DynamoDBCrudPolicy:
TableName: !Ref SwiftAPITable
Environment:
# store database table name as an environment variable
Variables:
TABLE_NAME: !Ref SwiftAPITable
Events:
# handles the GET /items method of the REST API
Api:
Type: HttpApi
Properties:
Method: get
Path: /items
Metadata:
# location of the code and Docker file for function
DockerContext: ./src/get-items
Dockerfile: Dockerfile
DockerBuildArgs:
TARGET_NAME: get-items
# print API endpoint and name of database table
Outputs:
SwiftAPIEndpoint:
Description: "APIGatewayendpointURLforyourapplication"
Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com"
SwiftAPITable:
Description: "DynamoDBTableName"
Value: !Ref SwiftAPITable
Шаг 2: Инициализация функций Lambda с помощью SwiftPM
Функции Lambda, написанные на Swift, обрабатывают события API. Функция PutItem обрабатывает запросы POST для добавления элементов в базу данных. Функция GetItems обрабатывает запросы GET для извлечения элементов из базы данных.
Используйте Swift Package Manager для инициализации проекта для каждой функции. Также добавьте Dockerfile в каждую папку.
mkdir -p src/put-item
cd src/put-item
swift package init --type executable
touch Dockerfile
cd ../..
mkdir -p src/get-items
cd src/get-items
swift package init --type executable
touch Dockerfile
Шаг 3: Обновление Dockerfile
Docker используется для компиляции вашего кода Swift и развертывания образа в Lambda. Скопируйте следующий код в Dockerfile, который вы создали в каждой папке функции.
# image used to compile your Swift code
FROM--platform=linux/amd64 public.ecr.aws/docker/library/swift:5.7.2-amazonlinux2asbuilder
ARG TARGET_NAME
RUN yum -y install git jq tar zip openssl-devel
WORKDIR /build-lambda
RUN mkdir -p /Sources/$TARGET_NAME/
RUN mkdir -p /Tests/$TARGET_NAME/
ADD /Sources/ ./Sources/
ADD /Tests/ ./Tests/
COPY Package.swift .
RUN cd /build-lambda && swift package clean && swift build --static-swift-stdlib -c release
# image deplpoyed to AWS Lambda with your compiled executable
FROM public.ecr.aws/lambda/provided:al2-x86_64
ARG TARGET_NAME
RUN mkdir -p /var/task/
RUN mkdir -p /var/runtime/
COPY --from=builder /build-lambda/.build/release/$TARGET_NAME /var/task/lambdaExec
RUN chmod 755 /var/task/lambdaExec
RUN ln -s /var/task/lambdaExec /var/runtime/bootstrap
RUN chmod 755 /var/runtime/bootstrap
WORKDIR /var/task
CMD ["/var/task/lambdaExec"]
Шаг 4: Обновление зависимостей Swift
Ваш проект требует 3 библиотек.
- swift-aws-lambda-runtime
- swift-aws-lambda-events
- aws-sdk-swift
Вы определяете их в файле Package.swift. Замените содержимое файла Package.swift в каждой папке функции следующим кодом.
src/put-item/Sources/put-item/Package.swift
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "put-item",
platforms: [.macOS(.v12)],
dependencies: [
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime", branch: "main"),
.package(url: "https://github.com/swift-server/swift-aws-lambda-events", branch: "main"),
.package(url: "https://github.com/awslabs/aws-sdk-swift", from: "0.9.1")
],
targets: [
.executableTarget(
name: "put-item",
dependencies: [
.product(name: "AWSLambdaRuntime",package: "swift-aws-lambda-runtime"),
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
.product(name: "AWSDynamoDB", package: "aws-sdk-swift")
]),
.testTarget(
name: "put-itemTests",
dependencies: ["put-item"]),
]
)
src/get-items/Sources/get-items/Package.swift
// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "get-items",
platforms: [.macOS(.v12)],
dependencies: [
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime", branch: "main"),
.package(url: "https://github.com/swift-server/swift-aws-lambda-events", branch: "main"),
.package(url: "https://github.com/awslabs/aws-sdk-swift", from: "0.9.1")
],
targets: [
.executableTarget(
name: "get-items",
dependencies: [
.product(name: "AWSLambdaRuntime",package: "swift-aws-lambda-runtime"),
.product(name: "AWSLambdaEvents", package: "swift-aws-lambda-events"),
.product(name: "AWSDynamoDB", package: "aws-sdk-swift")
]),
.testTarget(
name: "get-itemsTests",
dependencies: ["get-items"]),
]
)
Шаг 5: Обновление исходного кода функции Lambda
Замените содержимое основного файла кода для каждого Swift-проекта следующим кодом.
src/put-item/Sources/put-item/put_item.swift
// import the packages required by our function
import Foundation
import AWSLambdaRuntime
import AWSLambdaEvents
import AWSDynamoDB
// define Codable struct for function response
struct Item : Codable {
var id: String?
let itemName: String
}
enum FunctionError: Error {
case envError
}
@main
struct PutItemFunction: SimpleLambdaHandler {
// Lambda Function handler
func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws -> Item {
print("event received:\(event)")
// create a client to interact with DynamoDB
let client = try await DynamoDBClient()
// obtain DynamoDB table name from function's environment variables
guard let tableName = ProcessInfo.processInfo.environment["TABLE_NAME"] else {
throw FunctionError.envError
}
// decode data from APIGateway POST into a codable struct
var item = try JSONDecoder().decode(
Item.self,
from: event.body!.data(using: .utf8)!
)
// generate a unique id for the key of the item
item.id = UUID().uuidString
// use SDK to put the item into the database and return the item with key value
let input = PutItemInput(item: ["id": .s(item.id!), "itemName": .s(item.itemName)], tableName: tableName)
_ = try await client.putItem(input: input)
return item
}
}
src/get-items/Sources/get_items/get_items.swift
// import the packages required by our function
import Foundation
import AWSLambdaRuntime
import AWSLambdaEvents
import AWSDynamoDB
// define Codable struct for function response
struct Item : Codable {
var id: String = ""
var itemName: String = ""
}
enum FunctionError: Error {
case envError
}
@main
struct GetItemsFunction: SimpleLambdaHandler {
// Lambda Function handler
func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws -> [Item] {
print("event received:\(event)")
// create a client to interact with DynamoDB
let client = try await DynamoDBClient()
// obtain DynamoDB table name from function's environment variables
guard let tableName = ProcessInfo.processInfo.environment["TABLE_NAME"] else {
throw FunctionError.envError
}
// use SDK to retrieve items from table
let input = ScanInput(tableName: tableName)
let response = try await client.scan(input: input)
// return items in an array
return response.items!.map() {i in
var item = Item()
if case .s(let value) = i["id"] {
item.id = value
}
if case .s(let value) = i["itemName"] {
item.itemName = value
}
return item
}
}
}
Шаг 6: Сборка проекта SAM
Сборка вашего проекта SAM использует Docker на вашем компьютере для компиляции кода Swift в образы Docker. Из корневой папки вашего проекта (swift-lambda-api) выполните следующую команду.
sam build
Шаг 7: Развертывание проекта SAM
Развертывание вашего проекта SAM создаёт функции Lambda, API Gateway и базу данных DynamoDB в вашей учётной записи AWS.
sam deploy --guided
Примите значения по умолчанию для всех запросов, за исключением следующих двух:
PutItemFunction may not have authorization defined, Is this okay? [y/N]: y
GetItemsFunction may not have authorization defined, Is this okay? [y/N]: y
Проект создаёт публичный доступный конечный пункт API. Это предупреждения, которые сообщают вам, что у API нет авторизации. Если вы хотите добавить авторизацию в API, обратитесь к документации SAM.
Шаг 8: Использование вашего API
В конце развертывания SAM отобразит конечную точку вашего API Gateway:
Outputs
----------------------------------------------------------------------------------------
Key SwiftAPIEndpoint
Description API Gateway endpoint URL for your application
Value https://[your-api-id].execute-api.[your-aws-region].amazonaws.com
----------------------------------------------------------------------------------------
Используйте cURL или инструмент, такой как Postman, для взаимодействия с вашим API. Замените [your-api-endpoint] значением SwiftAPIEndpoint из вывода развертывания.
Добавление элемента в список задач
curl --request POST 'https://[your-api-endpoint]/item' --header 'Content-Type: application/json' --data-raw '{"itemName": "my todo item"}'
Получение элементов из списка задач
curl https://[your-api-endpoint]/items
Очистка
После завершения работы с приложением используйте SAM для удаления его из вашей учётной записи AWS. Ответьте Да (y) на все запросы.
sam delete
The Swift Programming Language, Copyright © 2014-2025 Apple Inc.
Swift and the Swift logo are trademarks of Apple Inc.
Documentation for Swift 6.0.3
https://www.swift.org/documentation/server/guides/deploying/aws-sam-lambda.html