Введение в Serverless на Swift с AWS Lambda
Serverless-архитектура становится стандартом современной облачной разработки. Благодаря AWS Lambda разработчики могут запускать код без управления серверами, платя только за фактическое время выполнения. До недавнего времени Swift оставался в стороне от serverless-мира, но с появлением официальной поддержки Swift на AWS Lambda ситуация кардинально изменилась.
В этой статье мы разберем полный цикл работы с serverless функциями на Swift: от настройки окружения до деплоя в production. Вы узнаете, как использовать свой опыт iOS/macOS разработки для создания эффективных облачных решений.
Почему Swift для Serverless?
Swift — это не только язык для мобильных приложений. Его сильные стороны делают его отличным выбором для serverless:
- Безопасность типов — компилятор отлавливает ошибки на этапе сборки
- Высокая производительность — нативный код работает быстрее интерпретируемых языков
- Современный синтаксис — код легко читать и поддерживать
- Общая кодовая база — можно переиспользовать модели и логику из iOS/macOS проектов
Для запуска Swift на AWS Lambda используется официальный образ Amazon Linux 2 с предустановленным Swift 5.10. Это гарантирует полную совместимость и минимальный холодный старт.
Настройка окружения и создание первой функции
Установка необходимых инструментов
Прежде чем начать, убедитесь, что у вас установлены:
- Swift 5.10 или новее
- Docker (для сборки в Linux-окружении)
- AWS CLI с настроенными credentials
- Homebrew (для macOS)
# Установка Swift через Homebrew
brew install swift
# Проверка версии
swift --version
# Установка AWS CLI
brew install awscli
# Настройка AWS
aws configure
Создание проекта Swift Package
Создадим новый исполняемый пакет Swift:
mkdir MyLambdaFunction
cd MyLambdaFunction
swift package init --type executable
Теперь обновим Package.swift для работы с AWS Lambda Runtime:
// swift-tools-version:5.10
import PackageDescription
let package = Package(
name: "MyLambdaFunction",
platforms: [.macOS(.v13)],
dependencies: [
.package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "1.0.0")
],
targets: [
.executableTarget(
name: "MyLambdaFunction",
dependencies: [
.product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime")
]),
.testTarget(
name: "MyLambdaFunctionTests",
dependencies: ["MyLambdaFunction"]),
]
)
Код функции-обработчика
Заменим содержимое Sources/MyLambdaFunction/main.swift:
import AWSLambdaRuntime
// Определяем структуру запроса
struct Request: Codable {
let name: String
let age: Int
}
// Определяем структуру ответа
struct Response: Codable {
let message: String
let isAdult: Bool
}
// Основной обработчик
@main
struct MyHandler: SimpleLambdaHandler {
typealias Event = Request
typealias Output = Response
func handle(_ event: Request, context: LambdaContext) async throws -> Response {
let isAdult = event.age >= 18
return Response(
message: "Привет, \(event.name)!",
isAdult: isAdult
)
}
}
Сборка и деплой на AWS Lambda
Сборка для Linux
Поскольку Swift на macOS собирает бинарники для macOS, нам нужно использовать Docker:
# Создаем Dockerfile
cat << EOF > Dockerfile
FROM swift:5.10-amazonlinux2 AS build
WORKDIR /app
COPY . .
RUN swift build -c release --static-swift-stdlib
FROM amazonlinux:2
RUN yum install -y libcurl-openssl1.0
COPY --from=build /app/.build/release/MyLambdaFunction /main
ENTRYPOINT ["/main"]
EOF
# Собираем образ
docker build -t my-lambda-function .
# Извлекаем бинарник
docker create --name extract my-lambda-function
docker cp extract:/main ./bootstrap
docker rm extract
# Упаковываем
zip function.zip bootstrap
Деплой через AWS CLI
# Создаем Lambda функцию
aws lambda create-function \
--function-name MySwiftFunction \
--runtime provided.al2 \
--role arn:aws:iam::YOUR_ACCOUNT_ID:role/lambda-execution-role \
--handler main \
--zip-file fileb://function.zip
# Тестируем
aws lambda invoke \
--function-name MySwiftFunction \
--payload '{"name": "Иван", "age": 25}' \
response.json
# Смотрим результат
cat response.json
# {"message":"Привет, Иван!","isAdult":true}
Продвинутые техники: работа с API Gateway и базой данных
Интеграция с API Gateway
Для создания REST API обернем нашу функцию в обработчик HTTP-запросов:
import AWSLambdaRuntime
import AWSLambdaEvents
@main
struct APIGatewayHandler: SimpleLambdaHandler {
typealias Event = APIGatewayV2Request
typealias Output = APIGatewayV2Response
func handle(_ event: APIGatewayV2Request, context: LambdaContext) async throws -> APIGatewayV2Response {
// Парсим тело запроса
guard let body = event.body,
let data = body.data(using: .utf8),
let request = try? JSONDecoder().decode(Request.self, from: data) else {
return APIGatewayV2Response(statusCode: .badRequest, body: "Invalid request")
}
// Формируем ответ
let response = Response(
message: "Hello, \(request.name)!",
isAdult: request.age >= 18
)
let jsonData = try JSONEncoder().encode(response)
let jsonString = String(data: jsonData, encoding: .utf8) ?? "{}"
return APIGatewayV2Response(statusCode: .ok, body: jsonString)
}
}
Работа с DynamoDB
Для сохранения данных используем AWS SDK для Swift:
import AWSLambdaRuntime
import AWSDynamoDB
struct User: Codable {
let id: String
let name: String
let email: String
}
@main
struct DynamoDBHandler: SimpleLambdaHandler {
typealias Event = Request
typealias Output = Response
let dynamoDB = DynamoDBClient()
func handle(_ event: Request, context: LambdaContext) async throws -> Response {
let user = User(id: UUID().uuidString, name: event.name, email: event.email)
// Сохраняем в DynamoDB
let input = PutItem