注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。
このページでは、Compute Module で使用する Docker の基本について説明します。詳しい説明については、Docker ドキュメント ↗を参照してください。
イメージをビルドして公開するには、まず Docker をインストールする必要があります。Docker ドキュメント ↗の公式手順に沿って進めてください。
Docker が実行されていることを確認するには、docker info コマンドを実行できます。Cannot connect to the Docker daemon と表示された場合は、トラブルシューティングガイド ↗を参照して問題を解決してください。
Docker は、アプリケーションをパッケージ化してデプロイするためのツールです。Docker を使用すると、配布が容易になり、ランタイム環境間での実行の一貫性と、分離によるセキュリティを確保できます。これは、コンテナ化と呼ばれるプロセスによって実現されます。このプロセスでは、アプリケーションの実行に必要なものをすべてパッケージ化し、どこにデプロイしても一貫して実行されるようにします。Docker のコンテナ化には、イメージとコンテナという2つの基本要素があります。
Docker でイメージを作成する際は、アプリケーションを依存関係、ライブラリ、設定ファイルとともに、単一の移植可能な単位にパッケージ化します。パッケージ化の手順は Dockerfile に定義します。
Dockerfile は、アプリケーションの設定と実行の方法を指示するコマンドを順番に記述したテキストドキュメントです。次のリストは、Compute Module 用のイメージを作成する際に必要となる主なコマンドの概要です。詳しいガイドについては、Dockerfile リファレンスドキュメント ↗を参照してください。
FROM は Dockerfile の最初のステートメントである必要があります。--platform linux/amd64 フラグを追加して、対象プラットフォームを指定することもできます。latest _以外_の任意のタグを使用する必要があります。Compute Module に対応するイメージを用意したら、次の手順に従って Foundry にアップロードできます。詳しい手順については、アーティファクトの公開に関するドキュメントを参照してください。
Compute Module としてデプロイするアプリケーションがあり、その構成は次のとおりです。
プロジェクト構成
myapplication
├── Dockerfile
├── requirements.txt
└── src
└── application.py
次の手順で、Dockerfile を1行ずつ作成できます。
FROM python:3.12
WORKDIR /app
COPY ./requirements.txt /app
RUN pip install --no-cache-dir -r requirements.txt
RUN adduser --uid 5001 user
USER 5001
COPY ./src/application.py /app
ENTRYPOINT ["python", "application.py"]
Dockerfile は次のようになります。
FROM python:3.12
WORKDIR /app
COPY ./requirements.txt /app
RUN pip install --no-cache-dir -r requirements.txt
RUN adduser --uid 5001 user
USER 5001
COPY ./src/application.py /app
ENTRYPOINT ["python", "application.py"]
これで、次のコマンドを実行して、Dockerfile から 0.0.0 タグの付いた myimage という名前のイメージをビルドできます。
docker build . -t myimage:0.0.0 --platform linux/amd64
ログ記録はコンテナ単位で設定でき、コンテナごとに有効または無効にできます。このきめ細かな制御により、リソース使用量を最適化し、最も関連性の高いログデータに集中できます。特定のコンテナのログ設定にアクセスするには、コンテナセクションでそのコンテナの行を選択します。サイドパネルが開き、ログ設定を調整できます。

SLS 形式は、一貫性があり、容易にパースできるログを提供する構造化ログ形式です。SLS ログ記録は、ログエントリごとに追加のメタデータを含められるように設計されています。
以下は、SLS 形式でログを記録する例です。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33package myproject; import com.palantir.interactive.module.api.SafeArg; import com.palantir.interactive.module.api.UnsafeArg; import com.palantir.interactive.module.tasks.deployedapps.DeployedAppRuntime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; final class DeployedApp { private static final Logger log = LoggerFactory.getLogger(DeployedApp.class); public static void main(String[] _args) { DeployedAppRuntime.builder() .addQueryRunner(DeployedApp::hello, String.class, String.class, "hello") .buildAndStart(); } static String hello(String name) { // エラー用の SLS 形式 log.error("This is an SLS error log with unsafe parameter", UnsafeArg.of("name", name)); // これは SLS 形式ではありません。そのため、SLS 形式が選択されている場合はログに記録されません。 System.out.println("This message will not be logged iff SLS format"); // 情報用の SLS 形式 log.info("This is an SLS info log with safe parameter", SafeArg.of("name", name)); return "Hello, " + name + "!"; } private DeployedApp() {} }
ログ記録では、以下のスタイルと制約に従っていることに注目してください。
UnsafeArg を、機密性のないデータには SafeArg を使用します。Standard System.out.println() 文は SLS 形式では記録されません。以下は、SLS 形式のログエントリを JSON で表した例です。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13{ "type": "service.1", "level": "INFO", "time": "2024-01-15T12:00:00.000Z", "origin": "com.example.MyClass", "thread": "main", "message": "Processing request", "params": { "requestId": "abc-123" }, "uid": "unique-log-id", "sid": "session-id" }
Python Compute Modules SDK を使用している場合は、組み込みのログユーティリティを使用できます。get_logger 関数は SLS 形式に設定されたロガーを返し、set_internal_log_level は SDK 内部ログの詳細度を制御します。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23import logging from dataclasses import dataclass from compute_modules.annotations import function from compute_modules.logging import get_logger, set_internal_log_level set_internal_log_level(logging.INFO) logger = get_logger(__name__) logger.setLevel(logging.INFO) @dataclass class AddPayload: x: int y: int @function def add(context, event: AddPayload) -> int: try: logger.info("Received add request") result = event.x + event.y return result except Exception as e: logger.error("There was an error") raise e
以下のログレベルを利用できます。詳細度の高い順に記載しています。
DEBUG:トラブルシューティングのための詳細な診断情報。INFO:通常の動作に関する一般的な情報メッセージ。WARNING:予期しない事象が発生した、または近い将来に発生する可能性があることを示すメッセージ。ERROR:特定の操作を完了できなかった重大な問題。CRITICAL:アプリケーションの実行を継続できない可能性があることを示す深刻なエラー。プレーンテキスト形式では、特定の構造を持たず、人が読めるログが生成されます。プレーンテキストのログはそのまま読むのが容易ですが、プログラムでパースするのは難しい場合があります。
プレーンテキストを設定すると、出力が SLS ログのメッセージフィールドに挿入されます。これにより、読みやすさを維持しながら、既存の SLS ベースのツールとの互換性を確保できます。
プレーンテキストのログ記録をデフォルトとして使用すると、プレーンテキストと SLS の両方のログが記録され、SLS ログはメッセージフィールドに JSON 形式で表示されます。
コンテナログは、2つの主要なソースから収集できます。ログを効果的に収集するために、ソースごとに固有の要件と設定があります。
標準出力(stdout)ソースは、コンテナの標準出力ストリームから直接ログを収集します。このログ記録方法を有効にするには、コンテナが以下の要件を満たしていることを確認してください。
/bin/sh にシェル実行可能ファイルがあることset と tee をサポートしていること標準エラーの包含: オプションで標準エラー(stderr)をログに含めることができます。true に設定すると、stdout と stderr が同期され、単一のストリームにマージされます。
ログファイルソースは、コンテナ内の特定のファイルからログを収集します。設定パラメーターは2個あります。
例: /var/log/foo/ 内のすべての .log ファイルと、特定のファイル /var/log/bar.txt からログを収集するには、directoryPath を /var/log に、filePathPatterns を /var/log/foo/*.log と /var/log/bar.txt に設定します。
ログファイルを使用する場合、Compute Module の起動時に、指定したディレクトリパスが空である必要があります。
Docker の環境変数は、Dockerfile やコンテナイメージを変更せずに Docker コンテナやアプリケーションの動作をカスタマイズできる、名前付きの動的な値です。環境変数は、次のようなさまざまな目的に使用できます。
production と test の2つのコードパスがあり、test はリクエストに関する追加のメタデータを返す場合があるとします。コードを変更して再デプロイしなくても、コードが読み取って異なるパスを実行するために使用できる production 環境変数を作成できます。
Copied!1 2 3 4 5 6 7# app.py import os if os.environ["production"] == "true": ... else: ...
一部の環境変数名は予約されています。予約済み環境変数には、Compute Module を作成する際に重要な情報が含まれている場合があるため、上書きできません。以下の予約済み環境変数の一覧を確認してください。
CLIENT_ID string
038120ac-ac39-4d91-be0e-55afabbb0912Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15# app.py import requests import os token_response = requests.post("https://{FOUNDRY_URL}/multipass/api/oauth2/token", data={ "grant_type": "client_credentials", "client_id": os.environ["CLIENT_ID"], "client_secret": os.environ["CLIENT_SECRET"], "scope": "compass:edit" }, headers={ "Content-Type": "application/x-www-form-urlencoded", } ) access_token = token_response.json()["access_token"]
CLIENT_SECRET string
038120ac-ac39-4d91-be0e-55afabbb0912Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15# app.py import requests import os token_response = requests.post("https://{FOUNDRY_URL}/multipass/api/oauth2/token", data={ "grant_type": "client_credentials", "client_id": os.environ["CLIENT_ID"], "client_secret": os.environ["CLIENT_SECRET"], "scope": "compass:edit" }, headers={ "Content-Type": "application/x-www-form-urlencoded", } ) access_token = token_response.json()["access_token"]
RUNTIME_HOST host
localhostCopied!1 2 3# app.py import os runtime_host = os.environ["RUNTIME_HOST"]
RUNTIME_PORT port
8945Copied!1 2 3# app.py import os runtime_port = os.environ["RUNTIME_PORT"]
RUNTIME_API hostname
localhost:8945Copied!1 2 3# app.py import os runtime_api = os.environ["RUNTIME_API"]
GET_JOB_PATH uri path
/interactive-module/api/internal-query/jobCopied!1 2 3# app.py import os get_job_path = os.environ["GET_JOB_PATH"]
GET_JOB_URI uri
https://localhost:8945/interactive-module/api/internal-query/jobCopied!1 2 3# app.py import os get_job_uri = os.environ["GET_JOB_URI"]
POST_RESULT_PATH uri path
/interactive-module/api/internal-query/resultsCopied!1 2 3# app.py import os post_result_path = os.environ["POST_RESULT_PATH"]
POST_RESULT_URI uri
https://localhost:8945/interactive-module/api/internal-query/resultsCopied!1 2 3# app.py import os post_result_uri = os.environ["POST_RESULT_URI"]
POST_SCHEMA_URI uri
https://localhost:8945/interactive-module/api/internal-query/schemasCopied!1 2 3# app.py import os post_schema_uri = os.environ["POST_SCHEMA_URI"]
MAX_CONCURRENT_TASKS integer
1Copied!1 2 3# app.py import os max_concurrent_tasks = os.environ["MAX_CONCURRENT_TASKS"]
SOURCE_CREDENTIALS file path
/opt/source-credentials/SOURCE_CREDENTIALSCopied!1 2 3 4 5 6 7 8 9# app.py import json import os with open(os.environ['SOURCE_CREDENTIALS'], 'r') as f: credentials = json.load(f) # 特定のシークレットにアクセスします secret = credentials["<Source API Name>"]["<Secret Name>"]
SOURCE_CONFIGURATIONS_PATH file path
/opt/source-credentials/SOURCE_CONFIGURATIONS_PATHCopied!1 2 3 4 5 6 7 8 9 10# app.py import json import os with open(os.environ['SOURCE_CONFIGURATIONS_PATH'], 'r') as f: credentials = json.load(f) # 特定のシークレットにアクセスします secrets = credentials["secrets"] url = credentials["httpConnectionConfig"]["url"]
DEFAULT_CA_PATH file path
/etc/ssl/rubix-ca/ca.pemCopied!1 2 3# app.py import os default_ca_path = os.environ["DEFAULT_CA_PATH"]
BUILD2_TOKEN file path
/opt/build2-token/BUILD2_TOKENCopied!1 2 3# app.py import os build_2_token = os.environ["BUILD2_TOKEN"]
COMPUTE_SESSION_ID string
ses-7f3a9c2e-4b8d-4e1f-a5c6-9d2e8f4a3b1cCopied!1 2 3# app.py import os session_id = os.environ["COMPUTE_SESSION_ID"]
FOUNDRY_COMPUTE_MODULE_RID rid
ri.foundry.main.deployed-app.3d460200-8861-4e2f-a70c-1be19cd79ae5Copied!1 2 3# app.py import os compute_module_rid = os.environ["FOUNDRY_COMPUTE_MODULE_RID"]
MODULE_AUTH_TOKEN file path
/opt/module-auth-token/MODULE_AUTH_TOKENCopied!1 2 3 4# app.py import os with open(os.environ["MODULE_AUTH_TOKEN"], "r") as f: module_auth_token = f.read()
FOUNDRY_ONTOLOGY_RID rid
ri.ontology.main.ontology.a1b2c3d4-e5f6-7890-abcd-ef1234567890Copied!1 2 3# app.py import os ontology_rid = os.environ["FOUNDRY_ONTOLOGY_RID"]