𝕏 f B! L
案件・求人数 12,345
案件を探す(準備中) エージェントを探す(準備中) お役立ち情報 ログイン
案件・求人数 12,345
Cloud Functions入門|Google Cloudサーバーレス開発の実践ガイド

Cloud Functions入門|Google Cloudサーバーレス開発の実践ガイド

Google CloudCloud FunctionsサーバーレスCloud Functions 入門SES
目次

📚 この記事は「Google Cloud 完全攻略シリーズ」の Episode 11 です。

Google Cloud Functions は、Google Cloudが提供するサーバーレスコンピューティングサービスです。AWSのLambdaに相当するサービスで、サーバー管理なしでコードを実行できます。

SESエンジニアにとって、Google Cloudのサーバーレス技術を習得することは案件の幅を大きく広げる武器になります。本記事では、Cloud Functionsの基礎から実践的な開発パターンまでを解説します。

Cloud Functionsサーバーレス開発の全体像

Cloud Functionsとは

第2世代(Gen 2)の特徴

Cloud Functionsは2024年以降、**第2世代(Gen 2)**が標準になりました。Gen 2はCloud Runの基盤上に構築されており、従来のGen 1に比べて多くの改善があります。

項目Gen 1Gen 2
基盤独自基盤Cloud Run
最大実行時間9分60分
メモリ最大8GB最大32GB
同時接続1リクエスト/インスタンス最大1000リクエスト/インスタンス
トラフィック分割非対応対応(カナリアデプロイ)
イベントソース限定的Eventarc(90+ソース)
最小インスタンス非対応対応(コールドスタート回避)

Gen 2の最大の利点は同時接続数の向上です。1つのインスタンスで最大1000リクエストを処理できるため、コスト効率が大幅に改善されます。

Cloud Functions vs Cloud Run vs App Engine

Google Cloudには複数のサーバーレス/マネージドサービスがあります。

サービス用途デプロイ単位スケール
Cloud Functions単機能のAPI・イベント処理関数0からスケール
Cloud Runコンテナベースのアプリコンテナ0からスケール
App EngineフルスタックWebアプリアプリケーション自動スケール

選定基準: 単機能のAPIやイベントハンドラならCloud Functions。複雑なAPIやカスタムランタイムが必要ならCloud Run。フルスタックWebアプリならApp Engineが適しています。

Cloud Functions開発:ハンズオン

HTTP関数の作成(Node.js)

// index.js
const functions = require('@google-cloud/functions-framework');

functions.http('helloWorld', (req, res) => {
  const name = req.query.name || req.body.name || 'World';
  
  res.json({
    message: `Hello, ${name}!`,
    timestamp: new Date().toISOString(),
    region: process.env.FUNCTION_REGION || 'unknown',
  });
});
// package.json
{
  "name": "hello-world",
  "version": "1.0.0",
  "main": "index.js",
  "dependencies": {
    "@google-cloud/functions-framework": "^3.0.0"
  }
}

HTTP関数の作成(Python)

# main.py
import functions_framework
from datetime import datetime
import json

@functions_framework.http
def hello_world(request):
    name = request.args.get('name', 'World')
    
    return json.dumps({
        'message': f'Hello, {name}!',
        'timestamp': datetime.now().isoformat(),
    }), 200, {'Content-Type': 'application/json'}

gcloud CLI でのデプロイ

# Gen 2 HTTP関数のデプロイ
gcloud functions deploy hello-world \
  --gen2 \
  --runtime nodejs22 \
  --trigger-http \
  --allow-unauthenticated \
  --region asia-northeast1 \
  --memory 256MB \
  --timeout 60s

# デプロイ後のURL確認
gcloud functions describe hello-world \
  --gen2 \
  --region asia-northeast1 \
  --format='value(serviceConfig.uri)'

# 関数のテスト
curl "https://hello-world-xxxxx-an.a.run.app?name=SES"

イベント駆動型関数

Cloud Storageトリガー

ファイルアップロード時に自動処理を実行します。

const functions = require('@google-cloud/functions-framework');
const { Storage } = require('@google-cloud/storage');
const sharp = require('sharp');

const storage = new Storage();

functions.cloudEvent('processImage', async (cloudEvent) => {
  const file = cloudEvent.data;
  const bucket = storage.bucket(file.bucket);
  const srcFile = bucket.file(file.name);
  
  // 画像ファイルのみ処理
  if (!file.contentType?.startsWith('image/')) {
    console.log(`Skipping non-image: ${file.name}`);
    return;
  }
  
  // 画像を読み込んでリサイズ
  const [buffer] = await srcFile.download();
  const resized = await sharp(buffer)
    .resize(800, 600, { fit: 'inside' })
    .webp({ quality: 80 })
    .toBuffer();
  
  // リサイズ後の画像を保存
  const destName = `resized/${file.name.replace(/\.[^.]+$/, '.webp')}`;
  await bucket.file(destName).save(resized, {
    metadata: { contentType: 'image/webp' },
  });
  
  console.log(`Resized: ${file.name} -> ${destName}`);
});
# Cloud Storageトリガーでデプロイ
gcloud functions deploy process-image \
  --gen2 \
  --runtime nodejs22 \
  --trigger-event-filters="type=google.cloud.storage.object.v1.finalized" \
  --trigger-event-filters="bucket=my-images-bucket" \
  --region asia-northeast1

Firestoreトリガー

Firestoreのドキュメント変更時に関数を実行します。

const functions = require('@google-cloud/functions-framework');
const { Firestore } = require('@google-cloud/firestore');

const db = new Firestore();

functions.cloudEvent('onUserCreated', async (cloudEvent) => {
  const data = cloudEvent.data;
  const userId = data.value.name.split('/').pop();
  const userData = data.value.fields;
  
  console.log(`New user created: ${userId}`);
  
  // ウェルカムメール送信のキューに追加
  await db.collection('emailQueue').add({
    to: userData.email.stringValue,
    template: 'welcome',
    userId: userId,
    createdAt: Firestore.Timestamp.now(),
  });
});

Pub/Subトリガー

非同期メッセージ処理に最適です。

const functions = require('@google-cloud/functions-framework');

functions.cloudEvent('processPubSubMessage', (cloudEvent) => {
  const message = cloudEvent.data.message;
  const data = JSON.parse(
    Buffer.from(message.data, 'base64').toString()
  );
  
  console.log('Received message:', data);
  
  // メッセージの処理
  switch (data.type) {
    case 'ORDER_CREATED':
      handleNewOrder(data.payload);
      break;
    case 'PAYMENT_RECEIVED':
      handlePayment(data.payload);
      break;
    default:
      console.log(`Unknown message type: ${data.type}`);
  }
});

Firestore連携のCRUD API

実践的なREST API構築

const functions = require('@google-cloud/functions-framework');
const { Firestore } = require('@google-cloud/firestore');

const db = new Firestore();

functions.http('usersApi', async (req, res) => {
  // CORS設定
  res.set('Access-Control-Allow-Origin', '*');
  if (req.method === 'OPTIONS') {
    res.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
    res.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    return res.status(204).send('');
  }
  
  const pathParts = req.path.split('/').filter(Boolean);
  const userId = pathParts[1]; // /users/:id
  
  try {
    switch (req.method) {
      case 'GET':
        if (userId) {
          const doc = await db.collection('users').doc(userId).get();
          if (!doc.exists) {
            return res.status(404).json({ error: 'User not found' });
          }
          return res.json({ id: doc.id, ...doc.data() });
        }
        
        const snapshot = await db.collection('users')
          .orderBy('createdAt', 'desc')
          .limit(50)
          .get();
        
        const users = snapshot.docs.map(doc => ({
          id: doc.id,
          ...doc.data(),
        }));
        return res.json(users);
      
      case 'POST': {
        const newUser = {
          ...req.body,
          createdAt: Firestore.Timestamp.now(),
          updatedAt: Firestore.Timestamp.now(),
        };
        const ref = await db.collection('users').add(newUser);
        return res.status(201).json({ id: ref.id, ...newUser });
      }
      
      case 'PUT': {
        if (!userId) return res.status(400).json({ error: 'User ID required' });
        
        await db.collection('users').doc(userId).update({
          ...req.body,
          updatedAt: Firestore.Timestamp.now(),
        });
        return res.json({ id: userId, updated: true });
      }
      
      case 'DELETE': {
        if (!userId) return res.status(400).json({ error: 'User ID required' });
        await db.collection('users').doc(userId).delete();
        return res.status(204).send('');
      }
      
      default:
        return res.status(405).json({ error: 'Method not allowed' });
    }
  } catch (error) {
    console.error('Error:', error);
    return res.status(500).json({ error: 'Internal server error' });
  }
});

Cloud Functionsのコスト最適化

料金体系

Cloud Functions Gen 2 の料金構成は以下の通りです。

  1. 呼び出し回数: 200万回/月まで無料、以降100万回あたり $0.40
  2. コンピューティング時間: GB-秒 $0.0000025、GHz-秒 $0.0000100
  3. ネットワーク: 受信無料、送信 $0.12/GB(5GBまで無料)

コスト削減テクニック

1. 適切なメモリ設定

# メモリを必要最小限に設定
gcloud functions deploy my-function \
  --memory 128MB  # 軽量処理なら128MBで十分

2. 最小インスタンス数の調整

# コールドスタート回避が不要なら0に設定
gcloud functions deploy my-function \
  --min-instances 0  # デフォルト

3. 同時実行数の活用(Gen 2)

# 1インスタンスで複数リクエストを処理
gcloud functions deploy my-function \
  --concurrency 100  # 同時に100リクエストまで

ローカル開発環境

Functions Frameworkでのローカル実行

# Node.js
npx @google-cloud/functions-framework --target=helloWorld --port=8080

# Python
functions-framework --target=hello_world --port=8080

# テスト
curl http://localhost:8080?name=test

エミュレータの活用

# Firebase Emulatorでローカル開発
firebase emulators:start --only functions,firestore

# Firestore + Functionsの統合テスト
npm test  # ローカルエミュレータに接続してテスト

セキュリティベストプラクティス

認証の設定

# 認証必須(デフォルト)
gcloud functions deploy my-function --no-allow-unauthenticated

# 認証トークンの取得
TOKEN=$(gcloud auth print-identity-token)
curl -H "Authorization: Bearer $TOKEN" https://my-function-xxx.a.run.app

Secret Managerとの連携

const { SecretManagerServiceClient } = require('@google-cloud/secret-manager');

const client = new SecretManagerServiceClient();
let cachedApiKey;

async function getApiKey() {
  if (!cachedApiKey) {
    const [version] = await client.accessSecretVersion({
      name: `projects/${process.env.PROJECT_ID}/secrets/api-key/versions/latest`,
    });
    cachedApiKey = version.payload.data.toString();
  }
  return cachedApiKey;
}

IAMの最小権限

# サービスアカウントに必要最小限の権限を付与
gcloud projects add-iam-policy-binding my-project \
  --member="serviceAccount:[email protected]" \
  --role="roles/datastore.user"

SES現場での活用パターン

パターン1:Webhook処理サーバー

外部サービスからのWebhookを受信して処理する、SES案件で頻出のパターンです。

functions.http('webhookHandler', async (req, res) => {
  // 署名検証
  const signature = req.headers['x-webhook-signature'];
  if (!verifySignature(req.body, signature)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // 非同期処理のためPub/Subに投入
  const { PubSub } = require('@google-cloud/pubsub');
  const pubsub = new PubSub();
  
  await pubsub.topic('webhook-events').publish(
    Buffer.from(JSON.stringify(req.body))
  );
  
  return res.status(200).json({ received: true });
});

パターン2:定期バッチ処理

Cloud SchedulerとCloud Functionsを組み合わせた定期バッチ処理です。

# Cloud Schedulerジョブの作成
gcloud scheduler jobs create http daily-report \
  --schedule="0 9 * * *" \
  --uri="https://my-function-xxx.a.run.app" \
  --http-method=POST \
  --time-zone="Asia/Tokyo" \
  --oidc-service-account-email="[email protected]"

パターン3:リアルタイムデータパイプライン

// Pub/Subからデータを受信し、BigQueryに書き込む
const { BigQuery } = require('@google-cloud/bigquery');

const bigquery = new BigQuery();

functions.cloudEvent('streamToBigQuery', async (cloudEvent) => {
  const data = JSON.parse(
    Buffer.from(cloudEvent.data.message.data, 'base64').toString()
  );
  
  await bigquery
    .dataset('analytics')
    .table('events')
    .insert([{
      ...data,
      ingested_at: new Date().toISOString(),
    }]);
});

AWS Lambda との比較

SES案件ではAWSとGoogle Cloudの両方に対応できると有利です。

項目AWS LambdaCloud Functions Gen 2
ランタイムNode.js, Python, Java, etc.Node.js, Python, Java, Go, etc.
最大実行時間15分60分
同時接続1/インスタンス最大1000/インスタンス
API連携API Gateway直接HTTP or API Gateway
IaCSAM / CDKTerraform / Deployment Manager
無料枠100万回/月200万回/月

SESエンジニアとしてのGoogle Cloudスキルの価値

Google Cloud関連のSES案件は増加傾向にあり、特に以下の領域で需要があります。

  • データ分析基盤: BigQuery + Cloud Functions
  • モバイルバックエンド: Firebase + Cloud Functions
  • AI/ML: Vertex AI + Cloud Functions
  • Kubernetes: GKE + Cloud Run + Cloud Functions

関連Google Cloud認定資格

  • Cloud Digital Leader: クラウドの基礎概念
  • Associate Cloud Engineer: 実装・運用スキル
  • Professional Cloud Developer: 開発者向け上級

まとめ

Cloud Functionsは、Google Cloudでサーバーレス開発を始めるための最も手軽なエントリーポイントです。

学習ロードマップ

  1. 基礎: HTTP関数の作成・デプロイ(本記事)
  2. イベント駆動: Cloud Storage・Firestore・Pub/Subトリガー
  3. API構築: Firestore連携のREST API
  4. 運用: Cloud Logging・Cloud Monitoringでの監視
  5. 応用: Cloud Run との使い分け、マイクロサービス設計

関連記事


Google Cloud 完全攻略シリーズ一覧はこちら

SES案件をお探しですか?

SES記事をもっと読む →
🏗️

SES BASE 編集長

SES業界歴10年以上のメンバーが在籍する編集チーム。SES企業での営業・エンジニア経験、フリーランス独立経験を持つメンバーが、業界のリアルな情報をお届けします。

📊 業界データに基づく記事制作 🔍 IPA・経済産業省データ参照 💼 SES実務経験者が執筆・監修