03-5211-7750 平日|09:30~18:00

Cloudflare Containersがベータリリース!コンテナがサーバーレスになる未来 【使い方から注意点、そして今後の展望まで】

           

サービス資料や
ホワイトペーパーはこちら

           資料を【無料】ダウンロードFREE

はじめに

Cloudflareから新しい機能としてCloudflare Containersがベータリリースされました。
これは以前から2025年6月にベータ版がリリースされると告知されていた通りです。

◆Cloudflare Blog:https://blog.cloudflare.com/containers-are-available-in-public-beta-for-simple-global-and-programmable/

◆Cloudflare Doc:https://developers.cloudflare.com/containers/

◆Architecture:https://developers.cloudflare.com/containers/architecture/

Cloudflare画面内の下記場所にて「コンテナ」が増えてることが確認できました。



これまでCloudflareでコンテナを動かすことは出来ませんでしたが、今後はコンテナアプリケーションも利用可能になります。
もちろんCloudflare環境で動作するため、サーバーレスでサーバー管理も不要です。

そこで、Cloudflare Containersの始め方について検証してみました。

【相談無料】Cloudflareの導入や運用について、こちらからご相談いただけます ✉️

動作検証

今回は、簡単なプログラムを動かす検証を行います。

事前準備


dockerのインストール


コンテナをローカルでビルドし、イメージをPUSHするため、ローカルPCでDockerが使用できる必要があります。

wranglerコマンドの準備


Wranglerコマンドが既にインストールされている環境では、デプロイ時にエラーが発生するため、Wranglerコマンドのアップデートが必要です。このコラム執筆時点では、Wranglerコマンドのバージョン4.23.0で検証しています。

また、アップデート後には `npx wrangler logout` を実行して再ログインしないとデプロイに失敗する可能性があるため、こちらも合わせて実施してください。

Getting Started


公式ドキュメントにあるGetting Startedを試しておくと理解が深まるのでおすすめです。

◆Cloudflare Doc:https://developers.cloudflare.com/containers/get-started/

◆サンプル:https://developers.cloudflare.com/containers/examples/

cronコンテナ


まず簡単なプログラムをコンテナで用意し、cron triggerを使用して動かしてみたいと思います。

構成と動き


 ① WorkersでCron Triggerを使用し、2分ごとに発火させます。
 ② 起動したらSecrets StoreからSlackのWebhook URLを取得します。
 ③ Webhook URLと変数をコンテナに渡し、実行します。
 ④ コンテナがAPIからデータを取得します。
 ⑤ 取得した内容をslackに通知します。



Slackのwebhook urlを登録


Cloudflare Secrets Storeに通知先のURL(SlackのWebhook URL)を登録します。
(※SlackのWebhook URLは、ご自身の環境のURLを入力してください)

$ npx wrangler secrets-store secret create ストアID --name SLACK_WEBHOOK_URL --scopes workers --remote

作成されたことを確認します。
$ npx wrangler secrets-store secret list <span>ストアID</span> --remote<br>...<br>┌───────────────────┬──────────────────────────────────┬─────────┬─────────┬─────────┬───────────────────┬───────────────────┐<br>│ Name              │ ID                               │ Comment │ Scopes  │ Status  │ Created           │ Modified          │<br>├───────────────────┼──────────────────────────────────┼─────────┼─────────┼─────────┼───────────────────┼───────────────────┤<br>│ SLACK_WEBHOOK_URL │ xxxxx │         │ workers │ active  │ 2025/7/7 14:18:25 │ 2025/7/7 14:18:26 │<br>└───────────────────┴──────────────────────────────────┴─────────┴─────────┴─────────┴───────────────────┴───────────────────┘


テンプレートからWorkers作成


作成するディレクトリ名を入力するよう求められるので、「./test-cron-container」とします。
$ npm create cloudflare@latest -- --template=cloudflare/templates/containers-template

...
In which directory do you want to create your application?
│ dir ./test-cron-containers
...


プログラムコード編集


Rustでコンテナを作成するため、一度 `container_src/` ディレクトリを削除してから `cargo new` で作成します。
$ rm -rf container_src/
$ cargo new container_src
$ vi container_src/Cargo.toml


`Cargo.toml` を以下のように編集します。
[package]
name = "container_src"

[package]
name = "test-cron-containers"

...

[dependencies]

[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11", features = ["json", "rustls-tls"] }
serde_json = "1.0"


`container_src/src/main.rs` を以下のように編集します。
use reqwest::Client;
use serde_json::Value;
use std::error::Error;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let client = Client::new();

// Cloudflare API から IP リストを取得
let cf_url = "https://api.cloudflare.com/client/v4/ips";
let resp = client
.get(cf_url)
.send()
.await?
.error_for_status()?
.json::<Value>()
.await?;

// Slack webhook 用ペイロードを作成
let text = format!(
"*Cloudflare IPs*
• IPv4: `{}`
• IPv6: `{}`",
resp["result"]["ipv4_cidrs"]
.as_array()
.unwrap_or(&vec![])
.iter()
.map(|v| v.as_str().unwrap_or(""))
.collect::()
.join(", "),
resp["result"]["ipv6_cidrs"]
.as_array()
.unwrap_or(&vec![])
.iter()
.map(|v| v.as_str().unwrap_or(""))
.collect::()
.join(", "),
);
let payload = serde_json::json!({ "text": text });

// Slack Incoming Webhook に POST
let webhook_url = std::env::var("SLACK_WEBHOOK_URL")
.expect("SLACK_WEBHOOK_URL 環境変数を設定してください");
client
.post(&webhook_url)
.json(&payload)
.send()
.await?
.error_for_status()?;

println!("Slack への通知が完了しました。");
Ok(())
}


`Dockerfile` を以下のように編集します。
ARG RUST_VERSION=1.88.0
ARG ALPINE_VERSION=3.22
ARG APP_NAME=test-cron-containers

FROM rust:${RUST_VERSION}-alpine${ALPINE_VERSION} AS build
ARG APP_NAME
WORKDIR /app

RUN apk add --no-cache
musl-dev
pkgconfig
openssl-dev
openssl-libs-static

RUN --mount=type=bind,source=container_src/src,target=src
--mount=type=bind,source=container_src/Cargo.toml,target=Cargo.toml
--mount=type=bind,source=container_src/Cargo.lock,target=Cargo.lock
--mount=type=cache,target=/app/target/
cargo build --locked --release &&
cp ./target/release/$APP_NAME /bin/server

FROM alpine:${ALPINE_VERSION} AS final

ARG UID=10001
RUN adduser
--disabled-password
--gecos ""
--home "/nonexistent"
--shell "/sbin/nologin"
--no-create-home
--uid "${UID}"
appuser
USER appuser

COPY --from=build /bin/server /bin/

CMD ["/bin/server"]


`src/index.ts` を以下のように編集します。
import { Container, getContainer } from "@cloudflare/containers";

export interface Env {
CRON_CONTAINER: DurableObjectNamespace<CronContainer>;
SLACK_WEBHOOK_URL: SecretsStoreSecret;
}

export class CronContainer extends Container {
manualStart = true;
sleepAfter = "10s";

override onStart() {
console.log("Starting container");
}

override onStop() {
console.log("Stopped container");
}

override onError(error: string) {
console.log("Container error:", error);
}
}

export default {
async scheduled(_controller: any, env: Env) {
const webhookUrl = await env.SLACK_WEBHOOK_URL.get();

if (!webhookUrl) {
console.error(
"Error: webhookUrl is null, undefined, or empty."
);
return;
}

let container = getContainer(env.CRON_CONTAINER);
await container.start({
envVars: {
SLACK_WEBHOOK_URL: webhookUrl,
MESSAGE: "Hello Cloudflare Containers",
},
});
},
};


override onStart()、override onStop()、override onError(error: string)を使用すると、コンテナのstart時、stop時、error時にコードを実行できます。

◆Cloudflare Doc:https://developers.cloudflare.com/containers/examples/status-hooks/

`wrangler.jsonc`を以下のように編集します。
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "test-cron-containers",
"main": "src/index.ts",
"compatibility_date": "2025-05-23",
"compatibility_flags": ["nodejs_compat"],
"triggers": {
"crons": ["*/2 * * * *"]
},
"secrets_store_secrets": [
{
"binding": "SLACK_WEBHOOK_URL",
"store_id": "xxxxxxxxxxxx",
"secret_name": "SLACK_WEBHOOK_URL"
}
],
"observability": {
"enabled": true
},
"containers": [
{
"class_name": "CronContainer",
"image": "./Dockerfile",
"max_instances": 1,
"name": "test-cron-containers"
}
],
"durable_objects": {
"bindings": [
{
"class_name": "CronContainer",
"name": "CRON_CONTAINER"
}
]
},
"migrations": [
{
"new_sqlite_classes": ["CronContainer"],
"tag": "v1"
}
]
}


deploy


準備ができましたのでdeployします。
$ npx wrangler deploy


Cloudflareの管理画面でWorkersが作成されたことを確認します。



コンテナも作成されたことを確認します。


コマンドからコンテナで使用しているイメージを確認することもできます。
$ npx wrangler containers images list

┌ Listing
REPOSITORY TAG
hello-containers 403a196f

コンテナの情報も確認できます。Kubernetesで `describe` コマンドで確認するようなイメージです。
$ npx wrangler containers list
● hello-containers (2025-07-06T09:49:24.750000128Z)
│ id: xxx-xxx-xxx-xxx-xxx
│ created_at: 2025-07-06T09:49:24.750000128Z
│ account_id: xxxxxxxxx
│ name: hello-containers
│ version: 1
│ scheduling_policy: default
│ instances: 7
│ max_instances: 10
│ configuration:
│ image: registry.cloudflare.com/xxxxxxxxx/hello-containers:5cbd2070
│ vcpu: 0.0625
│ memory: 256MB
│ memory_mib: 256
│ disk:
│ size: 2GB
│ size_mb: 2000
│ network:
│ assign_ipv6: none
│ assign_ipv4: none
│ mode: private
│ command:
│ entrypoint:
│ runtime: firecracker
│ observability:
│ logs:
│ enabled: true
│ logging:
│ enabled: true
│ constraints:
│ tier: 1
│ durable_objects:
│ namespace_id: xxx
│ health:
│ instances:
│ healthy: 7
│ failed: 0
│ scheduling: 0
╰ starting: 0

Workersをcronトリガーで動かしているため、2分ごとに以下の内容がSlackに通知されれば成功です。


Cloudflareでコンテナを動かすことが出来ました。

apiコンテナ


次に、簡単なAPIをコンテナで用意してみます。

構成と動き


 ① リクエストを送信します。
 ② Workersがリクエストを受け取ります(エンドポイントは2つ)。
 ③ コンテナがエンドポイントに応じてJSONデータを返します。



テンプレートからWorkers作成


作成するディレクトリ名の入力を求められますので、「./test-api-container」とします。
$ npm create cloudflare@latest -- --template=cloudflare/templates/containers-template

...
In which directory do you want to create your application?
│ dir ./test-api-containers
...


プログラムコード編集


$ rm -rf container_src/
$ cargo new container_src
$ vi container_src/Cargo.toml

`Cargo.toml` を以下のように編集します。
[package]
name = "container_src"

[package]
name = "test-api-containers"

...

[dependencies]

[dependencies]
actix-web = "4"
serde = { version = "1", features = ["derive"] }
serde_json = "1"

`container_src/src/main.rs` を以下のように編集します。
use actix_web::{web, App, HttpResponse, HttpServer};

async fn hello() -> HttpResponse {
HttpResponse::Ok().json(serde_json::json!({ "message": "hello" }))
}

async fn goodbye() -> HttpResponse {
HttpResponse::Ok().json(serde_json::json!({ "message": "goodbye" }))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/api/v1/hello", web::get().to(hello))
.route("/api/v1/goodbye", web::get().to(goodbye))
})
.bind(("0.0.0.0", 8080))?
.run()
.await
}

`Dockerfile` を編集します。
ARG RUST_VERSION=1.88.0
ARG ALPINE_VERSION=3.22
ARG APP_NAME=test-cron-containers

FROM rust:${RUST_VERSION}-alpine${ALPINE_VERSION} AS build
ARG APP_NAME
WORKDIR /app

RUN apk add --no-cache
musl-dev
pkgconfig

RUN --mount=type=bind,source=container_src/src,target=src
--mount=type=bind,source=container_src/Cargo.toml,target=Cargo.toml
--mount=type=bind,source=container_src/Cargo.lock,target=Cargo.lock
--mount=type=cache,target=/app/target/
cargo build --locked --release &&
cp ./target/release/$APP_NAME /bin/server

FROM alpine:${ALPINE_VERSION} AS final

ARG UID=10001
RUN adduser
--disabled-password
--gecos ""
--home "/nonexistent"
--shell "/sbin/nologin"
--no-create-home
--uid "${UID}"
appuser
USER appuser

COPY --from=build /bin/server /bin/

EXPOSE 8080

CMD ["/bin/server"]

`src/index.ts` を編集します。
import { Container, getContainer, getRandom } from "@cloudflare/containers";
import { Hono } from "hono";

const INSTANCE_COUNT = 3;

export class MyContainer extends Container {
defaultPort = 8080;
sleepAfter = "10m";

override onStart() {
console.log("Container successfully started");
}

override onStop() {
console.log("Container successfully shut down");
}

override onError(error: unknown) {
console.log("Container error:", error);
}
}

const app = new Hono<{
Bindings: { MY_CONTAINER: DurableObjectNamespace<MyContainer> };
}>();

app.get("/", (c) => {
return c.text(
"Available endpoints:
" + "GET /api/v1/hello
" + "GET /api/v1/goodbye
"
);
});

app.get("/api/v1/*", async (c) => {
const containerInstance = await getRandom(c.env.MY_CONTAINER, INSTANCE_COUNT);
return await containerInstance.fetch(c.req.raw);
});

export default app;

getRandom()関数を使用して、ランダムにインスタンスにアクセスがいくようにしています。

◆Cloudflare Doc:https://developers.cloudflare.com/containers/scaling-and-routing/

ただ、現在だとインスタンスの数を固定値で指定しなければいけないのでオートスケールには対応してないようです。今後のアップデートで対応されるようです。

`wrangler.jsonc` を編集します。
  "containers": [
{
"class_name": "MyContainer",
"image": "./Dockerfile",
"max_instances": 10,
"name": "hello-containers"
}
],



"containers": [
{
"class_name": "MyContainer",
"image": "./Dockerfile",
"max_instances": 10,
"name": "test-api-containers",
"instance_type": "standard"
}
],

今回はinstance_typeを指定して、スペックを変更してみました。
スペック情報は下記ドキュメントに記載されていますので参考にしてください。

◆Cloudflare Doc:https://developers.cloudflare.com/containers/platform-details/#instance-types

#### deploy

準備ができましたのでdeployします。
$ npx wrangler deploy

確認方法は1つ目のcron triggerのものを参考にしてみてください。

実際にアクセスして確認してみます。
FQDNはWorkersのworkers.devのFQDNを利用して確認していきます。
$ curl http://test-api-containers.xxx.workers.dev/
Available endpoints:
GET /api/v1/hello
GET /api/v1/goodbye

$ curl http://test-api-containers.xxx.workers.dev/api/v1/hello
{"message":"hello"}

$ curl http://test-api-containers.xxx.workers.dev/api/v1/goodbye
{"message":"goodbye"}

「/」はWorkers自体で返してます。
それ以外の「/api/v1/hello」、「/api/v1/goodbye」はコンテナから返してます。
問題なく返答ができていることが確認できました。

ただ、コンテナへのアクセスに関して最初は返答が遅かったです。
原因としては下記ドキュメントに書いてあるとおり、最初はコンテナを起動させるために遅くなるようです。

◆Cloudflare Doc:https://developers.cloudflare.com/containers/get-started/#routing-to-containers

一定の間隔でアクセスがある場合は良いのですが、もしそうでない場合はsleepAfterを長い時間指定して対応しておいたほうが良さそうです。

今後のロードマップ


Cloudflare Containersはベータ版となっており、GAリリース前にいくつかの変更が予定されています。

◆Cloudflare Doc:https://developers.cloudflare.com/containers/beta-info/

自動スケーリングや、負荷分散、ダッシュボードの更新などが予定されています。

制限


ベータ版のため利用に制限がありますので、試される方は下記URLを参考にしてください。

◆Cloudflare Doc:https://developers.cloudflare.com/containers/platform-details/#limits

最後に

簡単ではありますが、Cloudflare Containersを検証してみました。
検証している中で少し気になったところとして、下記を挙げておきます。

 ●コンテナのロケーションの指定方法が不明だったため、海外のロケーションにデプロイされてしまった
 ●変更が反映されるまで時間がかかる

もっと書きたいところもあったのですが、あまりに長くなってしまいますので別の機会にご紹介できればと思っています。

Cloudflareのブログで下記のように書かれており、今後もっと使いやすくなりそうです。
開発者プラットフォームとのさらなる統合

様々なサービス向けに、ファーストパーティAPIを活用した開発者プラットフォームとの統合を継続していきます。R2バケットのマウント、Hyperdriveへのアクセス、KVへのアクセスなどを非常に簡単に行えるようにしたいと考えています。

◆Cloudflare Blog:https://blog.cloudflare.com/containers-are-available-in-public-beta-for-simple-global-and-programmable/

そしてCloudflareなら上記以外でも、

 ・パフォーマンスの向上
 ・サイトの信頼性の向上
 ・セキュリティの向上

が、一つのサービスで実現できますので非常におすすめです。

Cloudflareに、アクセリアの運用サポートをプラスしたCDNサービスを提供しています

アクセリア自社CDNの開発と運用は、20年以上にわたります。それらの経験とノウハウを駆使したプロフェッショナルサポートをパッケージしたサービスが、[Solution CDN]です。
移行支援によるスムーズな導入とともに、お客様の運用負担を最小限に​とどめながら、WEBサイトのパフォーマンスとセキュリティを最大限に高めます。運用サポートはフルアウトソーシングからミニマムサポートまで、ご要望に合わせてご提供します。

Cloudflare(クラウドフレア)の導入や運用について、またそれ以外のことでもなにか気になることがございましたらお気軽にご相談下さい。

Cloudflareの導入・運用について ご相談いただけます。 導入に関するご相談だけでなく、運用についてもご相談ください。

杉木 俊文

アクセリア株式会社 サービス事業本部プラットフォーム部
Contact usお問い合わせ

サービスにご興味をお持ちの方は
お気軽にお問い合わせください。

Webからお問い合わせ

お問い合わせ

お電話からお問い合わせ

03-5211-7750

平日09:30 〜 18:00

Download資料ダウンロード

製品紹介やお役立ち資料を無料でご活用いただけます。

Magazineメルマガ登録

最新の製品情報などタイムリーな情報を配信しています。

Free Service

PageSpeed Insights シミュレータ

CDNによるコンテンツの最適化を行った場合のPageSpeed Insightsのスコアをシミュレートしてレポートします。