シークレットを管理し、Streamlitアプリを設定する

Streamlitアプリは、しばしば API キー、パスワード、およびその他の認証情報などの機密情報にアクセスする必要があります。Streamlitアプリでシークレットをどのように管理するかは、使用しているランタイム環境によって異なります。Streamlit in Snowflake は、ウェアハウスとコンテナランタイムの両方でシークレットにアクセスするための安全な組み込みメカニズムを提供します。Streamlitの構成では、各ランタイムにも異なる制限があります。

Streamlitライブラリでは、アプリは .streamlit/ ディレクトリを使用して構成とシークレットを保存します。

  • .streamlit/config.toml:テーマ、レイアウト、サーバーの動作などのアプリの設定をカスタマイズします。

  • .streamlit/secrets.toml:API キーや認証情報などの機密情報を保管します(ローカル開発の場合)

Streamlit in Snowflake はこれらのファイルをサポートしていますが、ランタイム環境に応じていくつかの制限があります。次のテーブルは、ウェアハウスとコンテナランタイムにおけるこれらのファイルのサポートをまとめたものです。

機能

ウェアハウスランタイム

コンテナランタイム

config.toml サポート

構成オプションの限られたサブセット

構成オプションのより広範なサブセット

secrets.toml サポート

サポート対象外

サポートされていますが、非シークレットの環境変数に対してのみ推奨されます。

secrets.toml の場合、 Streamlit in Snowflake は、機密情報の管理に推奨される、より安全な組み込みのシークレット管理システムを提供します。次のセクションでは、アプリでSnowflakeのシークレットを使用する方法について説明します。

Snowflakeへの接続の管理

Snowflakeへの接続を管理するには、 st.connection("snowflake") を使用します。これにより、ローカルの開発環境とデプロイされたアプリの両方からSnowflakeに接続できます。

import streamlit as st

conn = st.connection("snowflake")
session = conn.session()

session.sql("SELECT 1").collect()

ウェアハウスのランタイムでは、Snowparkのアクティブなセッションを取得する get_active_session() 関数を使用できます。

import streamlit as st
from snowflake.snowpark.context import get_active_session

# ONLY IN WAREHOUSE RUNTIMES
session = get_active_session()
session.sql("SELECT 1").collect()

重要

get_active_session() はスレッドセーフではなく、コンテナランタイムでは使用できません。

コンテナランタイムのシークレット

コンテナランタイムの|sis|アプリでは、|st.secrets|_を使用してSnowflakeシークレットにアクセスできます。これにより、APIキー、認証情報、その他の構成値などの機密情報を安全に保存して取得できます。ローカル開発でStreamlitが:file:`.streamlit/secrets.toml`に対して行うのと同様に、|sis|でもシークレットが環境変数に設定されます。

注釈

コンテナランタイムは:code:`_snowflake`モジュールにアクセスできません。:code:`_snowflake`のシークレット関数を使用する従来のウェアハウスランタイムアプリを移行する場合は、このセクションで説明しているように、これらの呼び出しを|st.secrets|_に置き換えてください。

コンテナランタイムでシークレットにアクセスする

  1. 次のPythonファイルを``@my_stage/app_folder/streamlit_app.py``にステージングします。ファイルのステージングについて詳しくは、:doc:`/user-guide/data-load-local-file-system-stage-ui`を参照してください。

    import streamlit as st
    
    secret_value = st.secrets["my_secret_name"]
    
  2. Snowflakeアカウントでシークレットを作成します。

    CREATE OR REPLACE SECRET my_secret
      TYPE = GENERIC_STRING
      SECRET_STRING = 'my_secret_value';
    

    詳細については、 CREATE SECRET をご参照ください。

  3. 外部アクセス統合(EAI)を作成し、それにシークレットを割り当てます。

    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION my_eai
      ALLOWED_AUTHENTICATION_SECRETS = (my_secret)
      ENABLED = TRUE;
    
  4. SECRETSパラメーターを使用してシークレットを参照するStreamlitアプリを作成します。

    CREATE STREAMLIT my_container_app
      FROM '@my_stage/app_folder'
      MAIN_FILE = 'streamlit_app.py'
      RUNTIME_NAME = 'SYSTEM$ST_CONTAINER_RUNTIME_PY3_11'
      COMPUTE_POOL = my_compute_pool
      QUERY_WAREHOUSE = my_warehouse
      EXTERNAL_ACCESS_INTEGRATIONS = (my_eai)
      SECRETS = ('my_secret_name' = my_secret);
    
    ALTER STREAMLIT my_container_app ADD LIVE VERSION FROM LAST;
    

    注釈

    EAIとシークレットの両方をStreamlitオブジェクトに割り当てる必要があります。Streamlitオブジェクトに単独でシークレットを割り当てることはできません。

    一般的な文字列のシークレット``my_secret``は、SECRETSパラメーターで文字列``"my_secret_name"``に関連付けられているため、Streamlitアプリのコードでは:code:`st.secrets["my_secret_name"]`を使用して、このシークレットにアクセスできます。

サポートされているシークレットのタイプと環境変数

コンテナランタイムは、一般的な文字列と基本認証のシークレットをサポートしています。|sis|は、シークレットを:code:`st.secrets`にマッピングすることに加えて、シークレットを環境変数にもマッピングします。環境変数名では大文字と小文字を区別します。基本認証シークレットの場合は、ユーザー名用(``_USERNAME``サフィックス)とパスワード用(``_PASSWORD``サフィックス)の2つの環境変数が作成されます。

シークレットタイプ

:code:`st.secrets`でのアクセス

環境変数でのアクセス

一般的な文字列

st.secrets["my_secret_name"]

os.environ["my_secret_name"]

基本認証(ユーザー名)

st.secrets["my_secret_name"]["username"]

os.environ["my_secret_name_USERNAME"]

基本認証(パスワード)

st.secrets["my_secret_name"]["password"]

os.environ["my_secret_name_PASSWORD"]

注釈

クラウドプロバイダー、対称キー、OAuthのシークレットタイプは、現在サポートされていません。

一般的な文字列シークレット

一般的な文字列シークレットは、:code:`st.secrets`のトップレベルキーとして保存されます。

ALTER STREAMLIT my_container_app
  SET SECRETS = ('my_generic_secret_name' = my_generic_secret);

このシークレットには、ディクショナリ表記または属性表記を使用してアクセスできます。

import streamlit as st

api_key = st.secrets["my_generic_secret_name"]
api_key = st.secrets.my_generic_secret_name

基本認証シークレット

基本認証シークレットは、"username"``属性と"password"``属性を持つdictのようなオブジェクトとして保存されます。

ALTER STREAMLIT my_container_app
  SET SECRETS = ('my_basic_auth_secret_name' = my_basic_auth_secret);

このシークレットには、ディクショナリ表記または属性表記を使用してアクセスできます。

import streamlit as st

username = st.secrets["my_basic_auth_secret_name"]["username"]
password = st.secrets["my_basic_auth_secret_name"]["password"]

username = st.secrets.my_basic_auth_secret_name.username
password = st.secrets.my_basic_auth_secret_name.password

認証付きパッケージリポジトリのシークレット

シークレットは環境変数として自動的に公開されます。これにより、JFrog Artifactoryのようなプライベートパッケージリポジトリでの認証が可能になります。

ほとんどの認証付きパッケージリポジトリでは、基本認証シークレットを使用します。このシークレットは、``_USERNAME``および``_PASSWORD``のサフィックスが付いた環境変数に自動的に変換されます。別の命名規則が必要な場合は、一般的な文字列シークレットを使用し、各環境変数の名前を手動で設定します。uvが使用する環境変数について詳しくは、uvドキュメントの`パッケージインデックス<https://docs.astral.sh/uv/concepts/indexes/#providing-credentials-directly>`_をご参照ください。

例:プライベートなJFrog Artifactoryリポジトリへの認証

  1. アプリのソースファイルを``@my_stage/app_folder``にステージングします。アプリのソースファイルには、``[[tool.uv.index]]``テーブルでプライベートパッケージインデックスを構成する:file:`pyproject.toml`ファイルを含める必要があります。

    [[tool.uv.index]]
    name = "my_jfrog_repo"
    url = "https://my-org.jfrog.io/artifactory/api/pypi/pypi-local/simple"
    

    pyproject.toml`ファイルでアプリの依存関係を宣言する方法について詳しくは、:doc:/developer-guide/streamlit/app-development/dependency-management`をご参照ください。

  2. JFrogの認証情報を使用して基本認証シークレットを作成します。

    CREATE OR REPLACE SECRET jfrog_creds
      TYPE = PASSWORD
      USERNAME = 'my_username'
      PASSWORD = 'my_api_token';
    
  3. プライベートリポジトリの外部アクセス統合を作成します。

    CREATE OR REPLACE NETWORK RULE jfrog_network_rule
      TYPE = HOST_PORT
      MODE = EGRESS
      VALUE_LIST = ('my-org.jfrog.io');
    
    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION jfrog_eai
      ALLOWED_NETWORK_RULES = (jfrog_network_rule)
      ALLOWED_AUTHENTICATION_SECRETS = (jfrog_creds)
      ENABLED = TRUE;
    

    注釈

    DNSエラーを回避するために、必要に応じてリポジトリのクラウドプロバイダーをネットワークルールの値リストに含める必要があります。たとえば、リポジトリがAWSにある場合は、ネットワークルールに次の値リストが必要になる場合があります。

    VALUE_LIST = ('my-org.jfrog.io', '<jfrog-server-name>.s3.amazonaws.com');
    
  4. EAIとシークレットをStreamlitアプリにアタッチします。

    CREATE STREAMLIT my_app
      FROM '@my_stage/app_folder'
      MAIN_FILE = 'streamlit_app.py'
      RUNTIME_NAME = 'SYSTEM$ST_CONTAINER_RUNTIME_PY3_11'
      COMPUTE_POOL = my_compute_pool
      QUERY_WAREHOUSE = my_warehouse
      EXTERNAL_ACCESS_INTEGRATIONS = (jfrog_eai)
      SECRETS = ('UV_INDEX_MY_JFROG_REPO' = jfrog_creds);
    
    ALTER STREAMLIT my_app ADD LIVE VERSION FROM LAST;
    

    基本認証シークレット``jfrog_creds``はSECRETSパラメーターで文字列``"UV_INDEX_MY_JFROG_REPO"``に関連付けられているため、ランタイムはuvで必要な``UV_INDEX_MY_JFROG_REPO_USERNAME``および``UV_INDEX_MY_JFROG_REPO_PASSWORD``の環境変数を自動的に注入します。

ローカルの:file:`.streamlit/secrets.toml`ファイルの優先順位

Snowflakeが管理するシークレットと、アプリのソースディレクトリにあるローカルの:file:.streamlit/secrets.toml`ファイルを組み合わせることができます。両方が存在する場合、Streamlitライブラリはそれらをマージします。ローカルで定義された:file:.streamlit/secrets.toml`ファイルは、Snowflakeが管理するシークレットよりも優先されます。

.streamlit/secrets.toml`はステージングされたファイルにプレーンテキストとして保存されるため、実際のシークレットを保存することはセキュリティのベストプラクティスではありません。機密性の高い認証情報には、Snowflakeの組み込みシークレット管理を使用してください。ローカルで定義された:file:.streamlit/secrets.toml`ファイルは、機密性のない構成値や環境固有の設定を保存するために使用してください。

Streamlitアプリのシークレットの削除または変更

  • |sis|アプリからすべてのシークレットを削除するには、:doc:`/sql-reference/sql/alter-streamlit`を指定したUNSET SECRETS句を使用します。

    ALTER STREAMLIT my_database.my_schema.my_app
      UNSET SECRETS;
    

    これにより、|sis|アプリからすべてのシークレットの関連付けが削除されます。基になるシークレットオブジェクトはSnowflakeアカウントに保持され、後で再び割り当てることができます。EAIの関連付けも削除するには、EXTERNAL_ACCESS_INTEGRATIONSプロパティの設定も解除します。

  • アタッチされているシークレットを更新または変更するには、SET SECRETSを指定した:doc:`/sql-reference/sql/alter-streamlit`を使用します。

    ALTER STREAMLIT my_database.my_schema.my_app
      SET SECRETS = ('new_secret' = my_new_secret);
    

例:認証付き外部APIを使用したコンテナランタイムStreamlitアプリの作成

この例では、シークレットのAPIキーを使用して外部APIを呼び出す|sis|アプリの作成方法について説明します。

  1. 次のPythonファイルを``@my_stage/weather_app/streamlit_app.py``にステージングします。

    import streamlit as st
    import requests
    
    api_key = st.secrets["weather_api_name"]
    
    response = requests.get(
        "https://api.weather.com/v1/current",
        headers={"Authorization": f"Bearer {api_key}"}
    )
    
    st.write(response.json())
    

    ``requests``は``streamlit``の依存関係であるため、ランタイムのベースイメージに含まれています。そのため、依存関係ファイルを含めたり、パッケージインデックスを構成したりしなくても、ランタイムによって自動的にインストールされます。

  2. シークレット、ネットワークルール、EAIを作成します。

    CREATE OR REPLACE SECRET weather_api_key
      TYPE = GENERIC_STRING
      SECRET_STRING = 'secret_value';
    
    CREATE OR REPLACE NETWORK RULE weather_api_rule
      TYPE = HOST_PORT
      MODE = EGRESS
      VALUE_LIST = ('api.weather.com');
    
    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION weather_eai
      ALLOWED_NETWORK_RULES = (weather_api_rule)
      ALLOWED_AUTHENTICATION_SECRETS = (weather_api_key)
      ENABLED = TRUE;
    
  3. Streamlitオブジェクトを作成します。

    CREATE STREAMLIT weather_app
      FROM '@my_stage/weather_app'
      MAIN_FILE = 'streamlit_app.py'
      RUNTIME_NAME = 'SYSTEM$ST_CONTAINER_RUNTIME_PY3_11'
      COMPUTE_POOL = my_compute_pool
      QUERY_WAREHOUSE = my_warehouse
      EXTERNAL_ACCESS_INTEGRATIONS = (weather_eai)
      SECRETS = ('weather_api_name' = weather_api_key);
    
    ALTER STREAMLIT weather_app ADD LIVE VERSION FROM LAST;
    

コンテナランタイム内でのCortex Agentの呼び出し

コンテナランタイムアプリでCortex Agentを呼び出すには、基になる Snowpark Container Services コンテナからセッショントークンを読み取り、 requests ライブラリを使用します。これは、 _snowflake.send_snow_api_request() に推奨される交換です。

import requests
import json
import os

SNOWFLAKE_HOST = os.getenv("SNOWFLAKE_HOST")
SNOWFLAKE_ACCOUNT = os.getenv("SNOWFLAKE_ACCOUNT")
ANALYST_ENDPOINT = "/api/v2/cortex/analyst/message"
URL = "https://" + SNOWFLAKE_HOST + ANALYST_ENDPOINT

def get_token() -> str:
    """Read the oauth token embedded into SPCS container"""
    return open("/snowflake/session/token", "r").read()

def send_request(semantic_model_file, prompt):
    """Sends the prompt using the semantic model file """
    headers = {
        "Content-Type": "application/json",
        "accept": "application/json",
        "Authorization": f"Bearer {get_token()}",
        "X-Snowflake-Authorization-Token-Type": "OAUTH"
    }
    request_body = {
        "messages": [
            {
                "role": "user",
                "content": [{"type": "text", "text": prompt}],
            }
        ],
        "semantic_model_file": semantic_model_file,
    }
    return requests.post(URL, headers=headers, data=json.dumps(request_body))

ウェアハウスランタイムのシークレット

ウェアハウスのランタイムでは、 _snowflake モジュールを使用して、Streamlitアプリコード内のシークレットに直接アクセスできます。ウェアハウスランタイムは、ストアドプロシージャから _snowflake モジュールへのアクセスを継承し、これによりStreamlitオブジェクトで参照されるシークレットを取得できるようになります。

ウェアハウスランタイムでシークレットを使用するには次のようにします。

  1. Snowflakeでシークレットオブジェクトを作成します。詳細については、 CREATE SECRET をご参照ください。

    CREATE OR REPLACE SECRET my_secret
      TYPE = GENERIC_STRING
      SECRET_STRING = 'my_secret_value';
    
  2. 外部アクセス統合を作成し、それにシークレットを割り当てます。

    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION my_eai
      ALLOWED_AUTHENTICATION_SECRETS = (my_secret)
      ENABLED = TRUE;
    
  3. SECRETS パラメーターを使用して、Streamlitオブジェクト内のシークレットを参照します。

    ALTER STREAMLIT my_warehouse_app
      SET EXTERNAL_ACCESS_INTEGRATIONS = (my_eai)
      SECRETS = ('my_secret_key' = my_secret);
    

    外部アクセス統合とシークレットの両方をStreamlitオブジェクトに割り当てる必要があります。Streamlitオブジェクトに単独でシークレットを割り当てることはできません。

  4. Streamlitアプリのコードで、 _snowflake モジュールをインポートしてシークレットを取得します。

    import streamlit as st
    import _snowflake
    
    # Retrieve an API key from a generic string secret
    my_secret = _snowflake.get_generic_secret_string('my_secret_key')
    

_snowflake モジュールを使用したシークレットへのアクセスの詳細については、 シークレットアクセスのためのPython API をご参照ください。

Streamlit構成

Streamlitアプリには、構成ファイル(.streamlit/config.toml)を含めることができます。このファイルを使うと、テーマ、レイアウト、動作など、アプリの様々な側面をカスタマイズできます。構成ファイルは TOML 形式で書き込まれます。使用可能な構成オプションの詳細については、Streamlitドキュメントの config.toml をご参照ください。

構成オプションのサポートは、ランタイム環境によって異なります。コンテナランタイムは通常、特に静的サービングのために、ウェアハウスランタイムよりも広範な構成オプションのサポートを提供します。次のテーブルは、ウェアハウスとコンテナランタイムでサポートされている構成セクションを示しています。

構成セクション

ウェアハウスランタイム

コンテナランタイム

[global]

サポート対象外

限定サポート(disableWidgetStateDuplicationWarning

[logger]

サポート対象外

サポート対象外

[client]

サポート対象外

限定サポート(showErrorDetailsshowSidebarNavigation

[runner]

サポート対象外

サポート対象

[server]

サポート対象外

サポート対象外

[browser]

サポート対象外

サポート対象外

[mapbox]

サポート対象外

サポート対象(非推奨、代わりに環境変数を使用)

[theme]

サポート対象

サポート対象

[theme.sidebar]

サポート対象

サポート対象

[secrets]

サポート対象外

サポート対象(ただし、非シークレット環境変数の場合のみ推奨)

[snowflake.sleep]

サポート対象

該当なし

[snowflake.sleep] セクションを使用してウェアハウスランタイムでスリープタイマーを構成する方法については、 Streamlitアプリのカスタムスリープタイマー を参照してください。

次のディレクトリ構造は、構成ファイルを使用したStreamlitアプリの例です。

source_directory/
├── .streamlit/
│   └── config.toml
├── pyproject.toml
├── streamlit_app.py
└── uv.lock