Snowpark Container Services : considérations supplémentaires pour les services

Connexion à Snowflake depuis l’intérieur d’un conteneur

Lorsque vous démarrez un service (y compris les services de tâches), Snowflake fournit des identifiants de connexion aux conteneurs en cours d’exécution, ce qui permet au code de votre conteneur d’utiliser des pilotes pour se connecter à Snowflake et exécuter SQL (comme tout autre code sur votre ordinateur se connectant à Snowflake). Les identifiants de connexion fournis permettent de s’authentifier en tant que rôle propriétaire (le rôle qui a créé le service). Snowflake fournit certaines informations sous forme de variables d’environnement dans vos conteneurs.

Chaque objet dans Snowflake a un rôle de propriétaire. Le rôle du propriétaire du service détermine les capacités que votre service est autorisé à exécuter lorsqu’il interagit avec Snowflake. Cela comprend l’exécution de SQL, l’accès à des zones de préparation et la mise en réseau de service à service.

Note

Le rôle de propriétaire d’un service fait référence au rôle qui a créé le service. Vous pouvez également définir un ou plusieurs rôles de service pour gérer l’accès aux points de terminaison exposés par le service. Pour plus d’informations, voir Gestion de l’accès aux points de terminaison de service.

Lorsque vous créez un service, Snowflake crée également un utilisateur de service spécifique à ce service. Lorsque le service exécute une requête, il le fait en tant qu’utilisateur du service, en utilisant le rôle de propriétaire du service. Ce que l’utilisateur du service peut faire est déterminé par le rôle du propriétaire.

Le rôle de propriétaire du service ne peut être aucun des rôles privilégiés, tels que ACCOUNTADMIN, SECURITYADMIN et ORGADMIN. Il s’agit de limiter ce qu’un service qui se comporte mal peut faire, en demandant aux clients d’être plus intentionnels pour permettre à un service d’effectuer des opérations administratives.

Pour voir les requêtes émises par un utilisateur de service spécifique, vous pouvez utiliser le rôle ACCOUNTADMIN pour voir l”historique des requêtes. Le nom d’utilisateur de l’utilisateur du service apparaît sous les formes suivantes :

  • Pour un service créé avant la version du serveur 8.35, le nom d’utilisateur du service est au format SF$SERVICE$unique-id.

  • Pour un service créé après la version du serveur 8.35, le nom d’utilisateur du service est le même que le nom du service.

Connexion à Snowflake

Snowflake fournit les variables d’environnement suivantes pour vous permettre de configurer un client Snowflake dans votre code de service :

  • SNOWFLAKE_ACCOUNT : fournit le localisateur de compte du compte Snowflake sous lequel le service est actuellement exécuté.

  • SNOWFLAKE_HOST : fournit le nom d’hôte utilisé pour se connecter à Snowflake.

Snowflake fournit également un jeton OAuth dans le conteneur dans un fichier nommé /snowflake/session/token. Lors de la création d’une connexion, vous fournissez ce jeton au connecteur.

Lorsque vous créez une connexion à Snowflake à partir d’un conteneur, vous devez utiliser les jetons SNOWFLAKE_HOST, SNOWFLAKE_ACCOUNT et OAuth. Vous ne pouvez pas utiliser le jeton OAuth sans utiliser également SNOWFLAKE_HOST, et vous ne pouvez pas utiliser le jeton OAuth en dehors de Snowpark Container Services. Pour plus d’informations, voir Utilisation d’un jeton OAuth pour exécuter SQL.

Note

L’utilisation d’intégrations d’accès externes pour accéder à Snowflake peut signifier l’envoi d’informations potentiellement sensibles sur Internet. Dans la mesure du possible, les services doivent utiliser le jeton OAuth pour accéder au nom d’hôte SNOWFLAKE_HOST. Cela évite d’avoir à accéder à Snowflake depuis Internet.

Pour des exemples de code utilisant divers pilotes Snowflake, voir Exemples de connexion Snowflake.

Exemples

  • Dans le tutoriel 2 (voir main.py), le code lit les variables d’environnement comme suit :

    SNOWFLAKE_ACCOUNT = os.getenv('SNOWFLAKE_ACCOUNT')
    SNOWFLAKE_HOST = os.getenv('SNOWFLAKE_HOST')
    
    Copy

    Le code transmet ces variables à un code de création de connexion pour le client Snowflake choisi. Le conteneur utilise ces identifiants de connexion pour créer une nouvelle session, avec le rôle de propriétaire comme rôle principal, afin d’exécuter des requêtes. L’exemple de code suivant est le minimum requis pour créer une connexion Snowflake en Python :

    def get_login_token():
      with open('/snowflake/session/token', 'r') as f:
        return f.read()
    
    conn = snowflake.connector.connect(
      host = os.getenv('SNOWFLAKE_HOST'),
      account = os.getenv('SNOWFLAKE_ACCOUNT'),
      token = get_login_token(),
      authenticator = 'oauth'
    )
    
    Copy
  • Lorsque vous utilisez l’hôte par défaut (c’est-à-dire que vous n’incluez pas l’argument host lors de la création d’une connexion), la connexion à Snowflake en utilisant d’autres formes d’authentification (telles que le nom d’utilisateur et le mot de passe) est prise en charge. Par exemple, la connexion suivante spécifie le nom d’utilisateur et le mot de passe pour l’authentification :

    conn = snowflake.connector.connect(
      account = os.getenv('SNOWFLAKE_ACCOUNT'),
      user = '<user-name>',
      password = <password>
    )
    
    Copy

    L’utilisation d’un hôte par défaut nécessite l’intégration d’accès externe avec une règle de réseau permettant l’accès de votre service au nom d’hôte Snowflake pour votre compte. Par exemple, si le nom de votre compte est MyAccount, le nom d’hôte sera moncompte.snowflakecomputing.com. Pour plus d’informations, voir Sortie réseau.

    • Créez une règle de réseau qui correspond au nom d’hôte d’API Snowflake de votre compte :

      CREATE OR REPLACE NETWORK RULE snowflake_egress_access
        MODE = EGRESS
        TYPE = HOST_PORT
        VALUE_LIST = ('myaccount.snowflakecomputing.com');
      
      Copy
    • Créez une intégration à l’aide de la règle de réseau précédente :

      CREATE EXTERNAL ACCESS INTEGRATION snowflake_egress_access_integration
        ALLOWED_NETWORK_RULES = (snowflake_egress_access)
        ENABLED = true;
      
      Copy

Configuration de la base de données et du contexte du schéma pour l’exécution de SQL

Cette section présente deux concepts :

  • La logique utilisée par Snowflake pour déterminer la base de données et le schéma dans lesquels créer votre service.

  • La méthode par laquelle Snowflake transmet cette information à vos conteneurs, permettant ainsi au code du conteneur d’exécuter SQL dans le même contexte de base de données et de schéma.

Snowflake utilise le nom du service pour déterminer la base de données et le schéma dans lesquels créer un service :

  • Exemple 1 : dans les commandes CREATE SERVICE et EXECUTE JOB SERVICE suivantes, le nom du service ne spécifie pas explicitement une base de données et un nom de schéma. Snowflake crée le service et le service de tâche dans la base de données et le schéma actuels.

    -- Create a service.
    CREATE SERVICE test_service IN COMPUTE POOL ...
    
    -- Execute a job service.
    EXECUTE JOB SERVICE
      IN COMPUTE POOL tutorial_compute_pool
      NAME = example_job_service ...
    
    Copy
  • Exemple 2 : dans les commandes CREATE SERVICE et EXECUTE JOB SERVICE suivantes, le nom du service comprend une base de données et un nom de schéma. Snowflake crée le service et le service de tâche dans la base de données (test_db) et le schéma (test_schema) spécifiés, quel que soit le schéma actuel.

    -- Create a service.
    CREATE SERVICE test_db.test_schema.test_service IN COMPUTE POOL ...
    
    -- Execute a job service.
    EXECUTE JOB SERVICE
      IN COMPUTE POOL tutorial_compute_pool
      NAME = test_db.test_schema.example_job_service ...
    
    Copy

Lorsque Snowflake démarre un service, Snowflake fournit les informations relatives à la base de données et au schéma aux conteneurs en cours d’exécution en utilisant les variables d’environnement suivantes :

  • SNOWFLAKE_DATABASE

  • SNOWFLAKE_SCHEMA

Le code de votre conteneur peut utiliser des variables d’environnement dans le code de connexion pour déterminer la base de données et le schéma à utiliser, comme le montre cet exemple :

conn = snowflake.connector.connect(
  host = os.getenv('SNOWFLAKE_HOST'),
  account = os.getenv('SNOWFLAKE_ACCOUNT'),
  token = get_login_token(),
  authenticator = 'oauth',
  database = os.getenv('SNOWFLAKE_DATABASE'),
  schema = os.getenv('SNOWFLAKE_SCHEMA')
)
Copy

Exemple

Dans le tutoriel 2, vous créez un service de tâche Snowflake qui se connecte à Snowflake et exécute des instructions SQL. Les étapes suivantes résument la manière dont le code du tutoriel utilise les variables d’environnement :

  1. Dans la configuration commune (voir la section Configuration commune), vous créez des ressources, notamment une base de données et un schéma. Vous définissez également la base de données et le schéma actuels pour la session :

    USE DATABASE tutorial_db;
    ...
    USE SCHEMA data_schema;
    
    Copy
  2. Après avoir créé un service de tâche (exécution de EXECUTE JOB SERVICE), Snowflake démarre le conteneur et définit les variables d’environnement suivantes dans le conteneur en fonction de la base de données et du schéma actuels de la session :

    • SNOWFLAKE_DATABASE est défini sur « TUTORIAL_DB »

    • SNOWFLAKE_SCHEMA est défini sur « DATA_SCHEMA »

  3. Le code de la tâche (voir main.py dans le tutoriel 2) lit ces variables d’environnement :

    SNOWFLAKE_DATABASE = os.getenv('SNOWFLAKE_DATABASE')
    SNOWFLAKE_SCHEMA = os.getenv('SNOWFLAKE_SCHEMA')
    
    Copy
  4. Le code de la tâche définit la base de données et le schéma comme le contexte dans lequel exécuter les instructions SQL (fonction run_job() dans main.py) :

    {
       "account": SNOWFLAKE_ACCOUNT,
       "host": SNOWFLAKE_HOST,
       "authenticator": "oauth",
       "token": get_login_token(),
       "warehouse": SNOWFLAKE_WAREHOUSE,
       "database": SNOWFLAKE_DATABASE,
       "schema": SNOWFLAKE_SCHEMA
    }
    ...
    
    Copy

    Note

    SNOWFLAKE_ACCOUNT, SNOWFLAKE_HOST, SNOWFLAKE_DATABASE, SNOWFLAKE_SCHEMA sont des variables d’environnement que Snowflake génère pour le conteneur d’application, mais SNOWFLAKE_WAREHOUSE ne l’est pas (le code de l’application du tutoriel 2 a créé cette variable parce que Snowflake ne transmet pas de nom d’entrepôt à un conteneur).

Spécification de l’entrepôt pour votre conteneur

Si votre service se connecte à Snowflake pour exécuter une requête dans un entrepôt Snowflake, vous disposez des options suivantes pour spécifier un entrepôt :

  • Spécifiez un entrepôt dans votre code d’application. Spécifiez un entrepôt dans le cadre de la configuration de la connexion lorsque vous démarrez une session Snowflake pour exécuter des requêtes dans votre code. Pour un exemple, voir le tutoriel 2.

  • Spécifiez un entrepôt par défaut lors de la création d’un service. Spécifiez le paramètre facultatif QUERY_WAREHOUSE dans la commande CREATE SERVICE (ou EXECUTE JOB SERVICE) pour fournir un entrepôt par défaut. Si votre code d’application ne fournit pas d’entrepôt dans le cadre de la configuration de la connexion, Snowflake utilise l’entrepôt par défaut. Utilisez la commande ALTER SERVICE pour modifier l’entrepôt par défaut.

Si vous spécifiez un entrepôt à l’aide des deux méthodes, c’est l’entrepôt spécifié dans le code d’application qui est utilisé.

Utilisation d’un jeton OAuth pour exécuter SQL

Tous les clients fournis par Snowflake prennent en charge OAuth comme moyen d’authentification. Les conteneurs de services utilisent également le mécanisme OAuth pour s’authentifier auprès de Snowflake. Par exemple, lorsqu’un conteneur veut exécuter SQL, il crée une connexion à Snowflake, comme n’importe quel autre client Snowflake :

def get_login_token():
  with open('/snowflake/session/token', 'r') as f:
    return f.read()

conn = snowflake.connector.connect(
  host = os.getenv('SNOWFLAKE_HOST'),
  account = os.getenv('SNOWFLAKE_ACCOUNT'),
  token = get_login_token(),
  authenticator = 'oauth'
)
Copy

Lorsque vous créez un service, Snowflake exécute les conteneurs et fournit un jeton Oauth pour les conteneurs à utiliser à l’emplacement suivant dans le conteneur : /snowflake/session/token.

Notez les détails suivants concernant ce jeton OAuth :

  • Vous devez lire le contenu du fichier /snowflake/session/token immédiatement avant de l’utiliser, car le contenu expire au bout de 10 minutes, et Snowflake actualise ce fichier toutes les quelques minutes. Une fois qu’un conteneur s’est connecté avec succès à Snowflake, le délai d’expiration ne s’applique pas à la connexion (comme pour les sessions créées directement par les utilisateurs).

  • Ce jeton OAuth n’est valable que pour le service Snowflake concerné. Vous ne pouvez pas copier le jeton OAuth et l’utiliser en dehors du service.

  • À l’aide du jeton OAuth, les conteneurs se connectent à Snowflake en tant qu’utilisateur du service et utilisent le rôle de propriétaire du service.

  • L’utilisation du jeton OAuth pour se connecter créera une nouvelle session. Le jeton OAuth n’est associé à aucune session SQL existante.

    Note

    Une différence importante entre l’exécution de procédures stockées et l’exécution d’un service est que les procédures stockées s’exécutent dans la même session que le SQL qui les exécute. Mais chaque fois qu’un conteneur établit une nouvelle connexion, vous créez une nouvelle session.

Configuration des capacités réseau

Les sections suivantes expliquent comment configurer les capacités réseau (entrée et sortie réseau) pour votre service (y compris des services de tâche).

Entrée réseau

Pour permettre à quiconque d’interagir avec votre service depuis Internet, vous déclarez les ports réseau sur lesquels votre service écoute en tant que points de terminaison dans le fichier de spécification du service. Ces points de terminaison contrôlent l’entrée.

Par défaut, les points de terminaison de service sont privés. Seules les fonctions de service et les communications de service à service peuvent adresser des requêtes aux points de terminaison privés. Vous pouvez déclarer un point de terminaison comme étant public afin d’autoriser les requêtes vers un point de terminaison depuis Internet. Le rôle de propriétaire de service doit avoir le privilège BIND SERVICE ENDPOINT sur le compte.

endpoints:
- name: <endpoint name>
  port: <port number>
  protocol : < TCP / HTTP / HTTPS >
  public: true
Copy

Pour un exemple, voir le tutoriel 1.

Note

Actuellement, seuls les services, et non les services de tâche, prennent en charge l’entrée dans le réseau.

Entrée et sécurité des applications web

Vous pouvez créer un service Snowpark Container Services pour l’hébergement web en utilisant la prise en charge de point de terminaison public (entrée réseau). Pour plus de sécurité, Snowflake utilise un service proxy pour contrôler les requêtes entrantes des clients vers votre service et les réponses sortantes de votre service vers les clients. Cette section explique ce que fait le proxy et comment il affecte un service déployé dans Snowpark Container Services.

Note

Lorsque vous testez un service localement, vous n’utilisez pas le proxy Snowflake et il y aura donc des différences entre votre expérience d’exécution d’un service en local et celle d’un service déployé dans Snowpark Container Services. Passez en revue cette section et mettez à jour votre configuration locale pour améliorer les tests.

Par exemple :

  • Le proxy ne transmet pas une requête HTTP entrante si la requête utilise une méthode HTTP interdite.

  • Le proxy envoie une réponse 403 au client si l’en-tête Content-Type de la réponse indique qu’elle contient un exécutable.

En outre, le proxy peut également injecter de nouveaux en-têtes et modifier les en-têtes existants dans la demande et la réponse, en tenant compte de votre conteneur et de la sécurité des données.

Par exemple, à la réception d’une requête, votre service peut envoyer du contenu HTML, JavaScript, CSS et d’autres contenus d’une page web au navigateur du client dans la réponse. La page web dans le navigateur fait partie de votre service et agit en tant qu’interface utilisateur. Pour des raisons de sécurité, si votre service est soumis à des restrictions (par exemple, l’interdiction d’établir des connexions réseau avec d’autres sites), vous voudrez peut-être que la page web de votre service soit soumise aux mêmes restrictions.

Par défaut, les services disposent d’autorisations limitées pour accéder à Internet. Le navigateur doit également empêcher l’application cliente d’accéder à Internet et de partager potentiellement des données dans la plupart des cas. Si vous avez mis en place une intégration d’accès externe (EAI) pour permettre à votre service d’accéder à example.com (voir Sortie réseau), la page web de votre service devrait également être en mesure d’accéder à example.com via votre navigateur.

Le proxy Snowflake applique les mêmes restrictions de réseau au service et à la page web en ajoutant un en-tête Content-Security-Policy (CSP) dans la réponse. Par défaut, le proxy ajoute une CSP de base dans la réponse pour se protéger contre les menaces de sécurité courantes. La sécurité du navigateur est le meilleur effort pour équilibrer la fonctionnalité et la sécurité. Il s’agit d’une responsabilité partagée qui garantit que cette base de référence est appropriée à votre cas d’utilisation. En outre, si votre service est configuré pour utiliser une EAI, le proxy applique les mêmes règles de réseau du EAI à la CSP pour la page web. Cette CSP permet à la page web du navigateur d’accéder aux mêmes sites que ceux auxquels le service peut accéder.

Les sections suivantes expliquent comment le proxy Snowflake gère les requêtes entrantes pour votre service et modifie les réponses sortantes de votre service aux clients.

Requêtes reçues par le service

Lorsqu’une requête arrive, le proxy effectue les opérations suivantes avant de transmettre la requête au service :

  • Requêtes entrantes avec des méthodes HTTP interdites : Si une requête HTTP entrante utilise l’une des méthodes HTTP interdites suivantes, le proxy ne transmet pas la requête à votre service :

    • TRACE

    • OPTIONS

    • CONNECT

Réponses envoyées aux clients

Le proxy Snowflake applique ces modifications à la réponse envoyée par votre service avant de la transmettre au client.

  • Nettoyage des en-têtes : le proxy Snowflake supprime ces en-têtes de réponse, s’ils sont présents :

    • X-XSS-Protection

    • Server

    • X-Powered-By

    • Public-Key-Pins

  • En-tête de réponse Content-Type : Si la réponse de votre service inclut l’en-tête Content-Type avec l’une des valeurs de type MIME suivantes (qui indiquent un exécutable), le proxy Snowflake ne transmet pas cette réponse au client. Au lieu de cela, le proxy envoie une réponse 403 Forbidden.

    • application/x-msdownload : exécutable Microsoft.

    • application/exe : exécutable générique.

    • application/x-exe : autre exécutable générique.

    • application/dos-exe : exécutable DOS.

    • application/x-winexe : exécutable Windows.

    • application/msdos-windows : exécutable Windows MS-DOS.

    • application/x-msdos-program : exécutable MS-DOS.

    • application/x-sh : script shell Unix.

    • application/x-bsh : script shell Bourne.

    • application/x-csh : script shell C.

    • application/x-tcsh : script shell Tcsh.

    • application/batch : fichier batch Windows.

  • En-tête de réponse X-Frame-Options : Pour prévenir les attaques par détournement de clics, le proxy Snowflake définit cet en-tête de réponse sur DENY, empêchant d’autres pages web d’utiliser un iframe vers la page web de votre service.

  • En-tête de réponse Cross-Origin-Opener-Policy (COOP) : Snowflake définit l’en-tête de réponse COOP sur same-origin pour empêcher les fenêtres cross-origin d’accéder à votre onglet de service.

  • En-tête de réponse Cross-Origin-Resource-Policy (CORP) : Snowflake définit l’en-tête CORP sur same-origin pour empêcher les sites externes de charger les ressources exposées par le point de terminaison d’entrée (par exemple, dans un iframe).

  • En-tête de réponse X-Content-Type-Options : Snowflake proxy définit ce header sur nosniff pour s’assurer que les clients ne changent pas le type MIME indiqué dans la réponse par votre service.

  • En-tête de réponse Cross-Origin-Embedder-Policy (COEP) : le proxy Snowflake définit l’en-tête de réponse COEP sur credentialless, ce qui signifie que lors du chargement d’un objet cross-origin tel qu’une image ou un script, si l’objet distant ne prend pas en charge le protocole Cross-Origin Resource Sharing (CORS), Snowflake n’envoie pas les identifiants de connexion lors du chargement de l’objet.

  • En-tête de réponse Content-Security-Policy-Report-Only : le proxy Snowflake remplace cet en-tête de réponse par une nouvelle valeur indiquant au client d’envoyer les rapports CSP à Snowflake.

  • En-tête de réponse Content-Security-Policy (CSP) : par défaut, le proxy Snowflake ajoute la CSP de base suivante pour se protéger contre les attaques web courantes.

    default-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data:; object-src 'none'; connect-src 'self'; frame-ancestors 'self';
    
    Copy

    Il existe deux considérations relatives à la politique de sécurité du contenu :

    • En plus de la politique de sécurité du contenu de base que le proxy ajoute, le service lui-même peut explicitement ajouter une CSP dans la réponse. Un service peut choisir de renforcer la sécurité en ajoutant une CSP plus stricte. Par exemple, un service peut ajouter la CSP suivante pour n’autoriser que les scripts provenant de self.

      script-src 'self'
      
      Copy

      La réponse envoyée au client contiendra deux en-têtes CSP. Lorsqu’ils reçoivent la réponse, les navigateurs clients appliquent alors la politique de sécurité du contenu la plus stricte qui inclut les restrictions supplémentaires spécifiées par chaque politique.

    • Si vous configurez une intégration d’accès externe (EAI) pour permettre à votre service d’accéder à un site externe (Sortie réseau), le proxy Snowflake crée une CSP qui permet à votre page web d’accéder à ce site. Par exemple, supposons qu’une règle de réseau associée à un EAI permette à votre service d’accéder à la sortie example.com. Ensuite, le proxy Snowflake ajoute cet en-tête de réponse CSP :

      default-src 'self' 'unsafe-inline' 'unsafe-eval' http://example.com https://example.com blob: data:; object-src 'none'; connect-src 'self' http://example.com https://example.com wss://example.com; frame-ancestors 'self';
      
      Copy

      Les navigateurs respectent la politique d’accès au contenu reçue dans la réponse. Dans cet exemple, les navigateurs autorisent l’application à accéder à example.com mais pas à d’autres sites.

Sortie réseau

Le code de votre application peut nécessiter un accès à Internet. Par défaut, les conteneurs d’application n’ont pas la permission d’accéder à Internet. Vous devez activer l’accès à Internet en utilisant les intégrations d’accès externe (EAIs).

En règle générale, vous souhaitez qu’un administrateur de compte crée des EAIs pour gérer l’accès externe autorisé à partir des services (y compris des services de tâche). Les administrateurs de comptes peuvent alors accorder l’utilisation d’EAI à des rôles spécifiques que les développeurs utilisent pour exécuter des services.

L’exemple suivant décrit les étapes de la création d’une EAI qui autorise le trafic de sortie vers des destinations spécifiques spécifiées à l’aide de règles de réseau. Vous vous référez ensuite à l’EAI lorsque vous créez un service pour autoriser des requêtes vers des destinations Internet spécifiques.

Exemple

Supposons que vous souhaitiez que le code de votre application envoie des requêtes aux destinations suivantes :

  • Requêtes HTTPS vers translation.googleapis.com

  • Requêtes HTTP et HTTPS vers google.com

Suivez ces étapes pour permettre à votre service d’accéder à ces domaines sur Internet :

  1. Créez une intégration d’accès externe (EAI). Pour ce faire, il faut disposer des autorisations nécessaires. Par exemple, vous pouvez utiliser le rôle ACCOUNTADMIN pour créer une EAI. Il s’agit d’un processus en deux étapes :

    1. Utilisez la commande CREATE NETWORK RULE pour créer une ou plusieurs règles de réseau de sortie répertoriant les destinations externes auxquelles vous souhaitez autoriser l’accès. Vous pouvez réaliser cet exemple avec une seule règle de réseau, mais pour l’illustration, nous créons deux règles de réseau :

      1. Créez une règle de réseau nommée translate_network_rule :

        CREATE OR REPLACE NETWORK RULE translate_network_rule
          MODE = EGRESS
          TYPE = HOST_PORT
          VALUE_LIST = ('translation.googleapis.com');
        
        Copy

        Cette règle autorise les connexions TCP vers la destination translation.googleapis.com. Le domaine dans la propriété VALUE_LIST ne spécifie pas le numéro de port optionnel, de sorte que le port par défaut 443 (HTTPS) est supposé. Cela permet à votre application de se connecter à n’importe quelle URL qui commence par https://translation.googleapis.com/.

      2. Créez une règle de réseau nommée google_network_rule :

        CREATE OR REPLACE NETWORK RULE google_network_rule
          MODE = EGRESS
          TYPE = HOST_PORT
          VALUE_LIST = ('google.com:80', 'google.com:443');
        
        Copy

        Cela permet à votre application de se connecter à n’importe quelle URL qui commence par http://google.com/ ou https://google.com/.

      Note

      Pour le paramètre VALUE_LIST vous devez fournir un nom d’hôte complet. Les caractères génériques (par exemple, *.googleapis.com) ne sont pas pris en charge.

      Snowpark Container Services ne prend en charge que les règles de réseau qui autorisent les ports 22, 80, 443 et 1024+. Si une règle de réseau référencée autorise l’accès à d’autres ports, la création du service échouera. Contactez votre représentant de compte si vous avez besoin d’utiliser des ports supplémentaires.

      Note

      Pour permettre à votre service d’envoyer des requêtes HTTP ou HTTPS à n’importe quelle destination sur Internet, vous spécifiez « 0.0.0.0 » comme domaine dans la propriété VALUE_LIST. La règle de réseau suivante permet d’envoyer des requêtes « HTTP » et « HTTPS » n’importe où sur Internet. Seuls les ports 80 ou 443 sont pris en charge avec « 0.0.0.0 ».

      CREATE NETWORK RULE allow_all_rule
        TYPE = 'HOST_PORT'
        MODE= 'EGRESS'
        VALUE_LIST = ('0.0.0.0:443','0.0.0.0:80');
      
      Copy
    2. Créez une intégration d’accès externe (EAI) qui spécifie que les deux règles de réseau de sortie précédentes sont autorisées :

      CREATE EXTERNAL ACCESS INTEGRATION google_apis_access_integration
        ALLOWED_NETWORK_RULES = (translate_network_rule, google_network_rule)
        ENABLED = true;
      
      Copy

      L’administrateur du compte peut désormais accorder l’utilisation de l’intégration aux développeurs pour leur permettre d’exécuter un service qui peut accéder à des destinations spécifiques sur Internet.

      GRANT USAGE ON INTEGRATION google_apis_access_integration TO ROLE test_role;
      
      Copy
  2. Créez le service en fournissant l’EAI comme indiqué dans les exemples suivants. Le rôle de propriétaire qui crée le service a besoin du privilège USAGE sur l’EAI et du privilège READ sur les secrets référencés. Notez que vous ne pouvez pas utiliser le rôle ACCOUNTADMIN pour créer un service.

    • Créez un service :

      USE ROLE test_role;
      
      CREATE SERVICE eai_service
        IN COMPUTE POOL MYPOOL
        EXTERNAL_ACCESS_INTEGRATIONS = (GOOGLE_APIS_ACCESS_INTEGRATION)
        FROM SPECIFICATION
        $$
        spec:
          containers:
            - name: main
              image: /db/data_schema/tutorial_repository/my_echo_service_image:tutorial
              env:
                TEST_FILE_STAGE: source_stage/test_file
              args:
                - read_secret.py
          endpoints:
            - name: read
              port: 8080
        $$;
      
      Copy

      Cet exemple de requête CREATE SERVICE utilise une spécification de service en ligne et spécifie la propriété facultative EXTERNAL_ACCESS_INTEGRATIONS pour inclure l’EAI. L’EAI spécifie les règles de réseau qui autorisent le trafic de sortie du service vers les destinations spécifiques.

    • Exécuter un service de tâche :

      EXECUTE JOB SERVICE
        IN COMPUTE POOL tt_cp
        NAME = example_job_service
        EXTERNAL_ACCESS_INTEGRATIONS = (GOOGLE_APIS_ACCESS_INTEGRATION)
        FROM SPECIFICATION $$
        spec:
          container:
          - name: curl
            image: /tutorial_db/data_schema/tutorial_repo/alpine-curl:latest
            command:
            - "curl"
            - "http://google.com/"
        $$;
      
      Copy

      Cet exemple de commande EXECUTE JOBSERVICE spécifie la spécification en ligne et la propriété facultative EXTERNAL_ACCESS_INTEGRATIONS pour inclure l’EAI. Cela autorise le trafic de sortie de la tâche vers les destinations spécifiées dans les règles de réseau que l’EAI autorise.

Communications réseau entre les conteneurs

Il y a deux considérations à prendre en compte :

  • Communications entre les conteneurs d’une instance de service : si une instance de service utilise plusieurs conteneurs, ces conteneurs peuvent communiquer entre eux via localhost (il n’est pas nécessaire de définir des points de terminaison dans la spécification du service).

  • Communication entre les conteneurs appartenant à différents services ou plusieurs instances de services : les conteneurs appartenant à différents services (ou à différentes instances d’un même service) peuvent communiquer en utilisant des points de terminaison définis dans des fichiers de spécification. Pour plus d’informations, voir Communications de service à service.

Transmission d’identifiants de connexion à un conteneur à l’aide de secrets Snowflake

Il existe de nombreuses raisons pour lesquelles vous pourriez vouloir transmettre des identifiants de connexion gérés par Snowflake à votre conteneur. Par exemple, votre service peut communiquer avec des points de terminaison externes (en dehors de Snowflake), auquel cas vous devrez fournir des identifiants de connexion dans votre conteneur pour que votre code d’application puisse les utiliser.

Pour fournir des identifiants de connexion, stockez-les d’abord dans des objets de secrets Snowflake. Ensuite, dans la spécification du service, utilisez containers.secrets pour spécifier quels objets de secrets utiliser et où les placer à l’intérieur du conteneur. Vous pouvez soit transmettre ces identifiants de connexion à des variables d’environnement dans les conteneurs, soit les rendre disponibles dans des fichiers locaux dans les conteneurs.

Spécification des secrets Snowflake

Spécifiez un secret Snowflake par nom ou référence (la référence n’est applicable que dans le cas de Native Application) :

  • Transmettez le secret Snowflake par nom : vous pouvez transmettre un nom de secret comme valeur de champ snowflakeSecret.

    ...
    secrets:
    - snowflakeSecret:
        objectName: '<secret-name>'
      <other info about where in the container to copy the secret>
      ...
    
    Copy

    Notez que vous pouvez éventuellement spécifier <secret-name> directement comme la valeur snowflakeSecret.

  • Transmettez le secret Snowflake par référence : lorsque vous utilisez les services de conteneurs Snowpark pour créer une Native App (une application avec des conteneurs), le producteur et les consommateurs de l’application utilisent des comptes Snowflake différents. Dans certains contextes, une Native App Snowflake installée doit accéder à des objets de secrets existants dans le compte consommateur et qui existent en dehors de l’objet APPLICATION. Dans ce cas, les développeurs peuvent utiliser la syntaxe de spécification « secrets par référence » pour gérer les identifiants de connexion comme indiqué :

    containers:
    - name: main
      image: <url>
      secrets:
      - snowflakeSecret:
          objectReference: '<reference-name>'
        <other info about where in the container to copy the secret>
    
    Copy

    Notez que la spécification utilise objectReference au lieu de objectName pour fournir un nom de référence secret.

Spécification du placement des secrets à l’intérieur du conteneur

Vous pouvez demander à Snowflake de placer les secrets dans les conteneurs en tant que variables d’environnement ou de les écrire dans des fichiers de conteneur locaux.

Transmettre des secrets en tant que variables d’environnement

Pour transmettre des secrets Snowflake aux conteneurs en tant que variables d’environnement, incluez envVarName dans le champ containers.secrets.

containers:
- name: main
  image: <url>
  secrets:
  - snowflakeSecret: <secret-name>
    secretKeyRef: username | password | secret_string |  'access_token'
    envVarName: '<env-variable-name>'
Copy

La valeur secretKeyRef dépend du type de secret Snowflake. Les valeurs possibles sont les suivantes :

  • username ou password si le secret Snowflake est de type password.

  • secret_string si le secret Snowflake est de type generic_string.

Notez que Snowflake ne met pas à jour les secrets transmis en tant que variables d’environnement après la création d’un service.

Exemple 1 : transmission de secrets de type password comme variables d’environnement

Dans cet exemple, vous créez l’objet secret Snowflake suivant de type password :

CREATE SECRET testdb.testschema.my_secret_object
  TYPE = password
  USERNAME = 'snowman'
  PASSWORD = '1234abc';
Copy

Pour fournir cet objet secret Snowflake aux variables d’environnement (par exemple, LOGIN_USER et LOGIN_PASSWORD) dans votre conteneur, ajoutez le champ containers.secrets suivant dans le fichier de spécification :

containers:
- name: main
  image: <url>
  secrets:
  - snowflakeSecret: testdb.testschema.my_secret_object
    secretKeyRef: username
    envVarName: LOGIN_USER
  - snowflakeSecret: testdb.testschema.my_secret_object
    secretKeyRef: password
    envVarName: LOGIN_PASSWORD
Copy

Dans cet exemple, la valeur snowflakeSecret est un nom d’objet complet, car les secrets peuvent être stockés dans un schéma différent de celui du service en cours de création.

Dans cet exemple, le champ containers.secrets est une liste de deux objets snowflakeSecret :

  • Le premier objet fait correspondre username de l’objet secret de Snowflake à la variable d’environnement LOGIN_USER de votre conteneur.

  • Le second objet fait correspondre password de l’objet secret de Snowflake à la variable d’environnement LOGIN_PASSWORD de votre conteneur.

Exemple 2 : passer des secrets de type generic_string comme variables d’environnement

Dans cet exemple, vous créez l’objet secret Snowflake suivant de type generic_string :

CREATE SECRET testdb.testschema.my_secret
  TYPE=generic_string
  SECRET_STRING='
       some_magic: config
  ';
Copy

Pour fournir cet objet secret Snowflake aux variables d’environnement (par exemple, GENERIC_SECRET) dans votre conteneur, vous ajoutez le champ containers.secrets suivant dans le fichier de spécification :

containers:
- name: main
  image: <url>
  secrets:
  - snowflakeSecret: testdb.testschema.my_secret
    secretKeyRef: secret_string
    envVarName: GENERIC_SECRET
Copy

Écrire des secrets dans des fichiers de conteneurs locaux

Pour rendre les secrets Snowflake disponibles pour votre conteneur d’application dans des fichiers de conteneur locaux, incluez un champ containers.secrets : pour rendre les secrets Snowflake disponibles pour votre conteneur d’application dans les fichiers de conteneur locaux, incluez directoryPath dans containers.secrets :

containers:
- name: <name>
  image: <url>
  ...
  secrets:
  - snowflakeSecret: <snowflake-secret-name>
    directoryPath: '<local directory path in the container>'
Copy

Snowflake renseigne les fichiers nécessaires pour le secret dans ce directoryPath spécifié ; la spécification de secretKeyRef n’est pas nécessaire. Selon le type de secret, Snowflake crée les fichiers suivants dans le conteneur sous le chemin de répertoire que vous avez fourni :

  • username et password si le secret Snowflake est de type password.

  • secret_string si le secret Snowflake est de type generic_string.

  • access_token si le secret Snowflake est de type oauth2.

Note

Après la création d’un service, si l’objet secret de Snowflake est mis à jour, Snowflake mettra à jour les fichiers secrets correspondants dans les conteneurs en cours d’exécution.

Exemple 1 : transmission de secrets de type password dans des fichiers de conteneurs locaux

Dans cet exemple, vous créez l’objet secret Snowflake suivant de type password :

CREATE SECRET testdb.testschema.my_secret_object
  TYPE = password
  USERNAME = 'snowman'
  PASSWORD = '1234abc';
Copy

Pour que ces identifiants de connexion soient disponibles dans les fichiers conteneurs locaux, ajoutez le champ containers.secrets suivant dans le fichier de spécification :

containers:
- name: main
  image: <url>
  secrets:
  - snowflakeSecret: testdb.testschema.my_secret_object
    directoryPath: '/usr/local/creds'
Copy

Lorsque vous démarrez votre service, Snowflake crée deux fichiers à l’intérieur du conteneur : /usr/local/creds/username et /usr/local/creds/password. Votre code d’application peut alors lire ces fichiers.

Exemple 2 : transmission de secrets de type generic_string dans des fichiers de conteneurs locaux

Dans cet exemple, vous créez l’objet secret Snowflake suivant de type generic_string :

CREATE SECRET testdb.testschema.my_secret
  TYPE=generic_string
  SECRET_STRING='
       some_magic: config
  ';
Copy

Pour fournir cet objet de secret Snowflake dans les fichiers de conteneurs locaux, vous ajoutez le champ containers.secrets suivant dans le fichier de spécifications :

containers:
- name: main
  image: <url>
  secrets:
  - snowflakeSecret: testdb.testschema.my_secret
    directoryPath: '/usr/local/creds'
Copy

Lorsque vous démarrez votre service, Snowflake crée ce fichier à l’intérieur des conteneurs : /usr/local/creds/secret_string.

Exemple 3 : transmission de secrets de type oauth2 dans des fichiers de conteneurs locaux

Dans cet exemple, vous créez l’objet secret Snowflake suivant de type oauth2 :

CREATE SECRET testdb.testschema.oauth_secret
  TYPE = OAUTH2
  OAUTH_REFRESH_TOKEN = '34n;vods4nQsdg09wee4qnfvadH'
  OAUTH_REFRESH_TOKEN_EXPIRY_TIME = '2023-12-31 20:00:00'
  API_AUTHENTICATION = my_integration;
Copy

Pour que ces identifiants de connexion soient disponibles dans les fichiers conteneurs locaux, ajoutez le champ containers.secrets suivant dans le fichier de spécification :

containers:
- name: main
  image: <url>
  secrets:
  - snowflakeSecret: testdb.testschema.oauth_secret
    directoryPath: '/usr/local/creds'
Copy

Snowflake récupère le jeton d’accès de l’objet secret OAuth et crée /usr/local/creds/access_token dans les conteneurs.

Lorsqu’un service utilise des secrets de type oauth2, le service est censé utiliser ce secret pour accéder à une destination Internet. Un secret oauth doit être autorisé par l’intégration d’accès externe (EAI) ; sinon CREATE SERVICE ou EXECUTE JOB SERVICE échoueront. Cette exigence d’EAI supplémentaire ne s’applique qu’aux secrets de type oauth2 et non aux autres types de secrets.

En résumé, les étapes typiques de la création d’un tel service sont les suivantes :

  1. Créez un secret de type oauth2 (voir plus haut).

  2. Créez un EAI pour permettre l’utilisation du secret par un service. Par exemple :

    CREATE OR REPLACE EXTERNAL ACCESS INTEGRATION example_eai
      ALLOWED_NETWORK_RULES = (<name>)
      ALLOWED_AUTHENTICATION_SECRETS = (testdb.testschema.oauth_secret)
      ENABLED = true;
    
    Copy
  3. Créez un service qui inclut un champ containers.secrets dans la spécification. Il spécifie également la propriété facultative EXTERNAL_ACCESS_INTEGRATIONS pour inclure un EAI afin de permettre l’utilisation du secret oauth2.

    Exemple de commande CREATE SERVICE (avec spécification en ligne) :

    CREATE SERVICE eai_service
      IN COMPUTE POOL MYPOOL
      EXTERNAL_ACCESS_INTEGRATIONS = (example_eai)
      FROM SPECIFICATION
      $$
      spec:
        containers:
          - name: main
            image: <url>
            secrets:
            - snowflakeSecret: testdb.testschema.oauth_secret
              directoryPath: '/usr/local/creds'
        endpoints:
          - name: api
            port: 8080
      $$;
    
    Copy

Pour plus d’informations sur la sortie, voir Sortie réseau.

Lignes directrices et limitations

  • Limitations générales : si vous rencontrez des problèmes avec ces limitations, contactez votre représentant de compte.

    • Vous pouvez créer jusqu’à 200 services dans votre compte Snowflake.

    • Chaque service peut exposer jusqu’à 100 points de terminaison.

    • Les limitations suivantes s’appliquent lorsque vous activez l’accès Internet (voir Sortie réseau) en utilisant des intégrations d’accès externes (EAIs).

      • Chaque service peut prendre en charge jusqu’à 10 EAIs (voir CREATE SERVICE et ALTER SERVICE).

      • Chaque EAI peut avoir jusqu’à 100 noms d’hôtes.

  • Exigences en matière de plateforme pour les images : actuellement, Snowpark Container Services nécessite des images de plateforme linux/amd64.

  • Les conteneurs de services ne sont pas privilégiés : les conteneurs de services ne s’exécutent pas avec des privilèges et ne peuvent donc pas modifier la configuration du matériel sur l’hôte et ne peuvent modifier que des configurations OS limitées. Les conteneurs de services ne peuvent effectuer que les configurations du système d’exploitation qu’un utilisateur normal (c’est-à-dire un utilisateur qui n’a pas besoin d’être rooté) peut effectuer.

  • Renommage de la base de données et du schéma :

    • Ne renommez pas les bases de données et les schémas pour lesquels vous avez déjà créé un service. Renommer revient à déplacer un service vers une autre base de données et un autre schéma, ce qui n’est pas pris en charge. Par exemple :

      • Le nom du service DNS continuera à refléter l’ancien nom de la base de données et du schéma.

      • Les informations sur les bases de données et les schémas fournies par Snowflake aux conteneurs de service en cours d’exécution continueront à se référer aux anciens noms.

      • Les nouveaux journaux que les services intègrent dans la table d’événements continueront à faire référence aux anciens noms de base de données et de schéma.

      • La fonction de service continuera à référencer le service dans l’ancienne base de données et l’ancien schéma, et lorsque vous appellerez la fonction de service, elle échouera.

    • Une spécification de service peut faire référence à des objets tels que des zones de préparation de Snowflake et les référentiels d’images. Si vous renommez les noms des bases de données ou des schémas dans lesquels résident ces objets, vous devez mettre à jour manuellement les noms des bases de données et des schémas des objets référencés dans la spécification de service.

  • Suppression et annulation de la suppression d’une base de données et d’un schéma :

    • Lorsque vous supprimez la base de données ou le schéma parent, les services sont supprimés de manière asynchrone. Cela signifie qu’un service peut continuer à fonctionner pendant un certain temps avant que des processus internes ne le suppriment.

    • Si vous tentez d’annuler la suppression d’une base de données ou d’un schéma précédemment supprimé, il n’y a aucune garantie que les services seront restaurés.

  • Transfert de propriété : le transfert de propriété pour les services (y compris les services de tâche) n’est pas pris en charge.

  • Réplication : en ce qui concerne la réplication dans Snowflake, notez ce qui suit :

    • Les objets Snowpark Container Services, comme les services, les pools de calcul et les référentiels, ne peuvent pas être répliqués.

    • Si vous créez un référentiel au sein d’une base de données, la base de données entière ne peut pas être répliquée. Si la base de données contient d’autres ressources, comme des services, ou des pools de calcul, le processus de réplication de base de données aboutira, mais ces objets individuels au sein de la base de données ne seront pas répliqués.

  • Délai d’expiration des services de tâches : les services de tâches Snowpark Container Services s’exécutent de manière synchrone. En cas d’expiration d’une instruction, le service de tâche est annulé. Le délai d’expiration par défaut d’une instruction est de deux jours. Les clients peuvent modifier le délai d’expiration en définissant le paramètre STATEMENT_TIMEOUT_IN_SECONDS à l’aide de ALTER SESSION.

    ALTER SESSION SET statement_timeout_in_seconds=<time>
    
    Copy

    Il doit être défini avant l’exécution de la commande EXECUTE JOB SERVICE.