はじめに
現代のクラウドコンピューティングの世界では、アプリケーションのデプロイや管理の方法が多様化しています。特に注目されるのがサーバーレスとコンテナ技術です。これらの技術の違いや、それぞれの適切な利用シーンについて解説します。
サーバーレスとは
サーバーレスは、開発者がインフラの管理を意識することなく、コードの実行に集中できるようにするものです。主な特徴として以下の点が挙げられます。
- サーバーの抽象化:開発者はサーバーを直接管理する必要がありません。
- イベント駆動型の実行:イベントが発生したときにコードが実行されます。
- 自動スケーリング:需要に応じて自動的にスケールします。
- 料金体系:実行時間と使用リソースに基づいて料金が発生します。
代表的なものは、AWS Lambda、Azure Functions、Google Cloud Functionsなどがあります。
コンテナとは
コンテナは、アプリケーションとその依存関係を一緒にパッケージ化し、一貫した実行環境を提供する技術です。主な特徴として以下の点が挙げられます。
- 軽量でポータブル:どの環境でも一貫して動作します。
- 隔離されたアプリケーション実行:他のアプリケーションから隔離された環境で実行されます。
- 開発と本番環境の一貫性:開発環境と本番環境で同じコンテナを使用できます。
代表的なものは、AWS ECS、EKS、Kubernetesなどがあります。
比較
サーバーレスとコンテナは、それぞれの特徴によって異なるメリットとデメリットがあります。以下にそれぞれの比較を示します。
デプロイと管理
- サーバーレス:クラウドプロバイダーによって管理され、インフラストラクチャの管理は不要です。
- コンテナ:コンテナオーケストレーションの管理が必要で、任意のインフラストラクチャで実行可能です。
スケーラビリティ
- サーバーレス:需要に応じて自動的にスケールします。
- コンテナ:オーケストレーションツールでスケールしますが、設定が必要です。
コスト
- サーバーレス:実行ごとに課金され、変動するワークロードに対してコスト効果が高いです。
- コンテナ:稼働中のインスタンスに対して課金され、安定した高スループットのワークロードに対してコスト効果が高いです。
ユースケース
- サーバーレス:短期間での実行、ステートレスなアプリケーション、イベント駆動型アーキテクチャ、リアルタイム処理、マイクロサービスに適しています。
- コンテナ:長期間の実行、複雑なマイクロサービスアーキテクチャ、レガシーアプリケーションのモダナイズ、一貫したランタイムが必要な環境に適しています。
メリットとデメリット
サーバーレス
- メリット:サーバー管理不要、自動スケーリング、バーストワークロードに対してコスト効果が高い。
- デメリット:コールドスタート遅延、実行時間の制限、環境の制御が少ない。
コンテナ
- メリット:環境の制御が高い、一貫した駆動環境。
- デメリット:管理の複雑さ、運用のオーバーヘッドが高い。
サーバーレスとコンテナの選択は、アプリケーションの要件、開発者のスキル、予算、セキュリティ要件などに依存します。特に、サーバーレスは短期間の実行やイベント駆動型アーキテクチャに適しています。一方、コンテナは長期間の実行や複雑なマイクロサービスアーキテクチャに適しています。インフラプロバイダーによって提供されるサーバーレスプラットフォームやコンテナオーケストレーションプラットフォームを比較し、適切な選択を行うことが重要です。
ハイブリッドアプローチ
システムによっては、サーバーレスとコンテナの両方を組み合わせることが有効な場合があります。例えば、サーバーレス関数を使用してイベント駆動型の処理を行い、コンテナを使用して長期間の実行や複雑なマイクロサービスを実行することができます。このようなハイブリッドアプローチは、アプリケーションの要件に応じて柔軟に対応できる利点があります。
その実装の例を少し紹介すると、サーバーレス関数をトリガーとしてコンテナを起動することができます。このようなアーキテクチャは、サーバーレスとコンテナの両方の利点を組み合わせることができます。
実装サンプル
以下は、TerraformとECSでコンテナを管理し、Serverless FrameworkとAPI GatewayでAPIを提供するサーバーレスとコンテナのハイブリッドアーキテクチャのコードのサンプルです。その間にSQSを使用してイベント駆動型の処理を行います。
# serverless.yml
service: example-service
provider:
name: aws
runtime: nodejs12.x
stage: dev
region: us-east-1
functions:
longRunningTask:
handler: index.longRunningTaskHandler
events:
- sqs:
arn:
Fn::GetAtt:
- LongRunningTaskQueue
- Arn
shortRunningTask:
handler: index.shortRunningTaskHandler
events:
- http:
path: short-task
method: post
resources:
Resources:
LongRunningTaskQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: long-running-task-queue
ShortRunningTaskApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: short-running-task-api
# main.tf
provider "aws" {
region = "us-east-1"
}
resource "aws_ecs_cluster" "example" {
name = "example-cluster"
}
resource "aws_ecs_task_definition" "example" {
family = "long-task"
container_definitions = jsonencode([
{
name = "long-task-container"
image = "some-long-task-image"
cpu = 256
memory = 512
}
])
}
resource "aws_ecs_service" "example" {
name = "long-task-service"
cluster = aws_ecs_cluster.example.id
task_definition = aws_ecs_task_definition.example.arn
desired_count = 1
}
// index.js
const AWS = require('aws-sdk');
const ecs = new AWS.ECS();
const env = process.env;
exports.longRunningTaskHandler = async (event) => {
// some logic to handle long running task
// ...
// params for starting ECS Task
const params = {
cluster: env.ECS_CLUSTER_NAME,
taskDefinition: env.ECS_TASK_DEFINITION_ARN,
count: 1,
launchType: 'FARGATE',
networkConfiguration: {
awsvpcConfiguration: {
subnets: [env.SUBNET_ID],
},
},
};
try {
// start ECS Task
const data = await ecs.runTask(params).promise();
console.log('ECS Task started:', data);
// return response
return {
statusCode: 200,
body: JSON.stringify('ECS Task started successfully'),
};
} catch (error) {
// handle error
console.error('Error starting ECS Task:', error);
// return error response
return {
statusCode: 500,
body: JSON.stringify('Error starting ECS Task'),
};
}
};
exports.shortRunningTaskHandler = async (event) => {
// some logic to handle short running task
// ...
// if you need to run long running task in handler, send message to SQS
// you can consider using AWS Step functions, SNS or EventBridge as well
const sqs = new AWS.SQS();
const params = {
MessageBody: JSON.stringify({ key: 'value' }),
QueueUrl: env.LONG_TASK_QUEUE_URL,
};
await sqs.sendMessage(params).promise();
// return response
const response = {
statusCode: 200,
body: JSON.stringify('Short running task completed successfully'),
};
return response;
};
まとめ
- サーバーレスはサーバーの管理を意識せずにコードを実行できる。
- コンテナはアプリケーションとその依存関係をパッケージ化し、一貫した実行環境を提供する。
- サーバーレスとコンテナの選択はアプリケーションの要件に依存する。
- ハイブリッドアプローチはサーバーレスとコンテナの両方を組み合わせることができる。
両方の技術を理解し、適切に利用することで、クラウドネイティブなアプリケーションを設計・構築することができます。サーバーレスとコンテナの適切な利用とハイブリッドアプローチを検討し、アプリケーションの要件に応じて最適な選択を行いましょう。