Amazon Web Services ブログ

NVIDIA Omniverse NucleusのAmazon EC2へのデプロイ方法

Introduction

この記事では、Amazon Elastic Compute Cloud (Amazon EC2) 上の NVIDIA Omniverse Enterprise Nucleus Server をユーザーが利用できるようにする方法を解説します。また、エンタープライズ Nucleus Server のデプロイメント要件の概要を説明し、アマゾンウェブサービス (AWS) 環境で Nucleus を稼働させるための方法について詳しく説明します。

Omniverseとは

NVIDIA Omniverseは、ピクサーのUniversal Scene Description(USD)とNVIDIA RTXテクノロジーに基づいた、メタバースアプリケーションを構築および運用するためのスケーラブルでマルチGPUのリアルタイムプラットフォームです。

NVIDIA Omniverse Nucleus は、オムニバースのデータベースおよびコラボレーションエンジンです。Omniverse Nucleusを使用すると、チームは異なるアプリケーションを使用して複数のユーザーがコラボレーションできます。これにより、ユーザーは最も使い慣れたアプリケーションを使用しつつ、効率よくコラボレーションを行うことができます。詳細については、NVIDIA Omniverse 入門ドキュメントをご覧ください。

Nucleus はパブリッシュ・アンド・サブスクライブ・モデルで動作し、NVIDIA Omniverse アプリケーション間の効率的なライブ同期を可能にします。USD シーンへの変更は、接続されている Omniverse クライアント間でリアルタイムで管理されます。クライアントはパブリッシュ・アンド・サブスクライブ・パターンを使用して接続します。これにより、送信された変更をほぼリアルタイムで受け取ることができます。

Nucleus のその他の機能には、ユーザーとグループの管理、きめ細かなアクセス制御のためのアセットアクセス制御リスト (ACL)、チェックポイントによるバージョン管理、SAML 認証によるシングルサインオン (SSO)、TLS 暗号化サポートなどがあります。

Why AWS?

Nucleus を AWS グローバルクラウドインフラストラクチャにデプロイする理由は複数あります。AWS を使用すると、世界中の分散したユーザーをつなぐことができます。AWS のセキュリティ、ID、およびアクセス管理制御により、お客様はデータを完全に管理することができます。また、AWS が提供するさまざまなコンピューティングインスタンスタイプとストレージソリューションにより、インフラストラクチャのサイズを適切に設定し、必要に応じてパフォーマンスを微調整できます。

ソリューションの概要

以下の手順は、Nucleus デプロイメントの基本コンポーネントの実装方法の概要となります。エンドユーザーからの通信を処理するために、NGINX リバースプロキシとして設定された Amazon EC2 インスタンスがパブリックサブネットにデプロイされます。リバースプロキシは TLS トラフィックを受け入れ、 Amazon Certificates Manager (ACM) からの TLS 証明書を持ちます。通常この工程では、 Elastic Load Balancer (ELB)が使われますが、Nucleus サーバでは現在 ELB ではサポートされていないので、リバースプロキシ上でのパスの書き換えを行います。

Enterprise Nucleus Server は、リバースプロキシサブネットからのトラフィックのみを受け入れる、プライベートサブネットにデプロイされた Amazon EC2 インスタンスです。Enterprise Nucleus Serverは Nucleus エンタープライズスタックを実行しており、これは Docker Compose スタックとしてデプロイされています。Nucleus インスタンスが NVIDIA NGC と通信するには NAT ゲートウェイとインターネットゲートウェイが必要です。この手順では、TLS をサポートする標準的なNucleus スタックを扱います、SSO は利用しません。

前提条件

AWS Command Line Interface (CLI) – Installing or updating the latest version of the AWS CLI
AWS Cloud Development Kit (CDK) – Install the AWS CDK
Python 3.9 、それ以上 – Python Downloads
NVIDIA Enterprise Omniverse Nucleus packages – Enterprise Nucleus Server Quick Start Tips
Nitro Enclaves Marketplace Subscription – AWS Certificate Manager for Nitro Enclaves

Omniverse NucleusのAmazon EC2へのデプロイ方法

Amazon Route 53 でドメインを登録してホストゾーンを作成

まず、Nucleus Server のホストゾーンとドメインが必要です。 Amazon Route 53 (Route 53) では、my-omniverse.com などのドメインを登録したり、Nucleus Server 用に nucleus.my-omniverse.com などのサブドメインを作成したりできます。ドメインを登録すると、ドメインレジストラとの通信が行われます。このステップは手動で行い、その後の設定ステップで Route 53 によって作成されたホストゾーン ID を参照するのが良い方法です。

ドメインの登録とホストゾーンの作成の詳細については、こちらのページを参照してください。

CDK スタックの設定

次に、Nucleus デプロイメントの基本リソースを含む CDK スタックを設定します。

ステップ 1.ターミナルを開き、CDK アプリ用のプロジェクトフォルダーを作成

フォルダーの名前がアプリケーション名になります。この手順では、nucleus-app という名前が使用します。

ステップ 2.ステップ 1 で作成したフォルダーにディレクトリを変更し、次のコマンドで CDK アプリを初期化

cdk init sample-app --language=typescript

これで、プロジェクト構造は次のようになっているはずです。

nucleus-app/
     bin/
         nucleus-app.ts
     lib/
         nucleus-app-stack.ts
 
additional config files …

nucleus-app.ts はアプリのメインエントリポイントであり、後続の CDK コマンドが参照するファイルです。このファイルを表示すると、lib/nucleus-app-stack.ts がインポートされていることがわかります。このファイルに、デプロイ用のカスタムコードを配置します。

ステップ 3. 最初の「Hello World」テストを実行

cdk deploy を使用してスターター CDK スタックをデプロイします。これにより基本的なスタックが生成され、CLIとCDKが適切に設定されていることを確認できます。

ステップ 4.デフォルトアカウントと AWS リージョンの環境値を設定

bin/nucleus-app.ts を開き、デフォルトのアカウントとリージョンを環境 (env)に設定します。なお、ファイルの内容は以下のようになっています。

#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { NucleusAppStack } from '../lib/nucleus-app-stack';

const app = new cdk.App();
new NucleusAppStack(app, 'NucleusAppStack', {
    env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
});

ステップ 5.サンプルリソースを削除

lib/nucleus-app-stack.ts を開き、 Amazon Simple Queue Service (SQS)Amazon Simple Notification Service (SNS) のサンプルリソースを削除します。これで、ファイルは次のようになっているはずです。

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class NucleusAppStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);
    
    // custom resources go here

  }
}

ステップ 6.後続の手順で必要になる CDK ライブラリを追加

import * as route53 from 'aws-cdk-lib/aws-route53'
import { Tags, CfnOutput } from 'aws-cdk-lib'
import * as s3 from 'aws-cdk-lib/aws-s3'
import * as ec2 from 'aws-cdk-lib/aws-ec2'
import * as iam from 'aws-cdk-lib/aws-iam'
import * as acm from 'aws-cdk-lib/aws-certificatemanager'

スタックリソースの定義

次に、デプロイに必要なカスタムインフラストラクチャのリソースを定義します。このセクションのコードは、NucleusAppStack クラスのコンストラクター内に追加します。

ステップ 1.アーティファクト用の Amazon Simple Storage Service (Amazon S3) バケットを作成

まず、ローカルクライアントから Amazon EC2 インスタンスにアーティファクトを転送するために使用するシンプルな Amazon S3 バケットを作成します。セキュリティのベストプラクティスに従って、暗号化を有効にし、SSLを適用し、パブリックアクセスをブロックします。次に、バケットを一覧表示したり、バケットからオブジェクトを取得したりするためのアクセスを許可するAWS Identity and Access Management (IAM) ポリシーを作成します。このポリシーは Amazon EC2 インスタンスプロファイルのロールとして利用されます。

const artifactsBucket = new s3.Bucket(this, 'artifactsBucket', {
    encryption: s3.BucketEncryption.S3_MANAGED,
    enforceSSL: true,
    blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
});

const getBucketObjectsPolicy = new iam.PolicyDocument({
    statements: [
    new iam.PolicyStatement({
        actions: [
            "s3:GetObject",
            "s3:ListBucket",
        ],
        resources: [
            `${artifactsBucket.bucketArn}`,
            `${artifactsBucket.bucketArn}/*`
        ]
    })]
});

ステップ 2.Amazon Virtual Private Cloud (VPC) 設定の追加

インターネットへのルートを持つ NAT ゲートウェイを含むプライベートサブネットを指定します。次に、プロキシサーバと Nucleus Server が使用する 2 つのセキュリティグループをプロビジョニングします。

const eip = new ec2.CfnEIP(this, 'natGatewayElasticIP', {
      domain: 'vpc'
    });

    const vpc = new ec2.Vpc(this, 'nucleusVpc', {
      cidr: '10.0.0.0/20',
      natGateways: 1,
      subnetConfiguration: [{
        name: 'publicSubnetNatGateway',
        subnetType: ec2.SubnetType.PUBLIC,
        cidrMask: 24
      }, {
        name: 'privateSubnet',
        subnetType: ec2.SubnetType.PRIVATE_WITH_NAT,
        cidrMask: 24
      }],
      natGatewayProvider: ec2.NatProvider.gateway({
          eipAllocationIds: [eip.attrAllocationId]
      }),
    });

    const proxySG = new ec2.SecurityGroup(this, 'reverseProxySG', {
      vpc: vpc,
      allowAllOutbound: true,
      description: "Reverse Proxy Security Group"
      });

    const nucleusSG = new ec2.SecurityGroup(this, 'nucleusSG', {
      vpc: vpc,
      allowAllOutbound: true,
      description: "Nucleus Server Security Group"
    });

ステップ 3.セキュリティグループにイングレスルールを追加

必要なポートでトラフィックを許可するようにプロキシと Nucleus セキュリティグループを設定します。Nucleus セキュリティグループは、プロキシセキュリティグループからのトラフィックのみを許可します。プロキシセキュリティグループは、特定の CIDR 範囲からのトラフィックを許可します。これは、サーバーへの接続に使用する範囲に設定する必要があります。たとえば、接続元のクライアントマシンの IP アドレスを使用できます。次に、その IP にネットワークマスクを付加して CIDR 範囲として入力します。このソリューションでは、推奨ネットワークマスクは /32 です。

const allowedCidrRange = 'ip-address/network-mask'
    proxySG.addIngressRule(
      ec2.Peer.ipv4(allowedCidrRange), ec2.Port.tcp(443), "HTTPS Traffic");
    proxySG.addIngressRule(
      ec2.Peer.ipv4(allowedCidrRange), ec2.Port.tcp(3180), "Auth login");
    proxySG.addIngressRule(
      ec2.Peer.ipv4(allowedCidrRange), ec2.Port.tcp(3100), "Auth Service");
    proxySG.addIngressRule(
      ec2.Peer.ipv4(allowedCidrRange), ec2.Port.tcp(3333), "Discovery Service");
    proxySG.addIngressRule(
      ec2.Peer.ipv4(allowedCidrRange), ec2.Port.tcp(3030), "LFT");
    proxySG.addIngressRule(
      ec2.Peer.ipv4(allowedCidrRange), ec2.Port.tcp(3019), "Core API");
    proxySG.addIngressRule(
      ec2.Peer.ipv4(allowedCidrRange), ec2.Port.tcp(3020), "Tagging Service");
    proxySG.addIngressRule(
      ec2.Peer.ipv4(allowedCidrRange), ec2.Port.tcp(3400), "Search Service");

    const proxySGId = proxySG.securityGroupId
    nucleusSG.addIngressRule(
      ec2.Peer.securityGroupId(proxySGId), ec2.Port.tcp(8080), "HTTP Traffic");
    nucleusSG.addIngressRule(
      ec2.Peer.securityGroupId(proxySGId), ec2.Port.tcp(3180), "Auth login");
    nucleusSG.addIngressRule(
      ec2.Peer.securityGroupId(proxySGId), ec2.Port.tcp(3100), "Auth Service");
    nucleusSG.addIngressRule(
      ec2.Peer.securityGroupId(proxySGId), ec2.Port.tcp(3333), "Discovery Service");
    nucleusSG.addIngressRule(
      ec2.Peer.securityGroupId(proxySGId), ec2.Port.tcp(3030), "LFT");
    nucleusSG.addIngressRule(
      ec2.Peer.securityGroupId(proxySGId), ec2.Port.tcp(3019), "Core API");
    nucleusSG.addIngressRule(
      ec2.Peer.securityGroupId(proxySGId), ec2.Port.tcp(3020), "Tagging Service");
    nucleusSG.addIngressRule(
      ec2.Peer.securityGroupId(proxySGId), ec2.Port.tcp(3400), "Search Service");

ステップ 4.TLS 証明書を追加し、ステップ 1 のドメインを検証用に設定

注:root-domain 変数は、Route 53 ホストゾーンに登録されたドメインに設定する必要があります。

const rootDomain = root-domain;
    const fullDomain = 'nucleus.'+rootDomain;
    const hostedZone = route53.HostedZone.fromLookup(this, 
        'PublicHostedZone', {domainName: rootDomain}
    );

    const certificate = new acm.Certificate(this, 'PublicCertificate', {
      domainName: rootDomain,
      subjectAlternativeNames: [`*.${rootDomain}`],
      validation: acm.CertificateValidation.fromDns(hostedZone),
    });

注:現在、この CNAME レコードの追加管理はありません。不要になったら、Route 53 ホストゾーンから手動で削除する必要があります。

ステップ 5.リバースプロキシリソースの追加

リバースプロキシの場合は、Nitro Enclaves を有効にして設定します。Enclaves には、機密性の高いデータを保護して安全に処理するための独立したコンピューティング環境を構築する機能が備わっています。この場合は TLS 証明書です。さらに、Nitro Enclaves は Amazon 証明書マネージャーとの統合をサポートしています。つまり、証明書マネージャーは証明書のローテーションを自動的に処理できます。詳細については、AWS Nitro Enclaves User Guideを参照してください。

Nitro Enclaves AMI の証明書マネージャーから始めて、32 GB のストレージを備えた c5.xlarge インスタンスを作成します。Amazon SSM マネージドインスタンスコアポリシーを使用して基本的なインスタンスロールを設定します。これにより、AWS Systems Manager (SSM) でインスタンスに接続でき、インターネット経由の SSH トラフィックに対してインスタンスのポートを開く必要がなくなります。

最後に、「ダミー」の IAM ポリシーをリバースプロキシにアタッチします。これは空のポリシーで、設定スクリプトで更新されます。

ご使用のリージョンが以下のリージョンのリストにない場合は、AWS Certificate Manager for Nitro Enclaves の AMI リストまたは AWS ドキュメント Finding AMI IDs を確認して、正しい AMI ID を見つけてください。

// AWS Certificate Manager for Nitro Enclaves AMI
    const proxyServerAMI = new ec2.GenericLinuxImage({
      'us-west-1': 'ami-0213075968e811ea7',    //california
      'us-west-2': 'ami-01c4415fd6c2f0927',    //oregon
      'us-east-1': 'ami-00d96e5ee00daa484',    //virginia
      'us-east-2': 'ami-020ea706ac260de21',    //ohio
      'ca-central-1': 'ami-096dd1150b96b6125', //canada
      'eu-central-1': 'ami-06a2b19f6b97762cb', //frankfurt
      'eu-west-1': 'ami-069e205c9dea19322',    //ireland
      'eu-west-2': 'ami-069b79a2d7d0d9408'     //london
    })

    const proxyInstanceRole = new iam.Role(this, 'proxyInstanceRole', {
      assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
      description: 'EC2 Instance Role',
      managedPolicies: [
          iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore')
      ],
      inlinePolicies: {
        getBucketObjectsPolicy: getBucketObjectsPolicy
      }
    });

    const cfnInstanceProfile = new iam.CfnInstanceProfile(this, 'proxyInstanceProfile', {
      roles: [proxyInstanceRole.roleName],
    });

    // using CfnInstance because it exposes enclaveOptions
    const cfnInstance = new ec2.CfnInstance(this, 'reverseProxyServer',  {
      blockDeviceMappings: [{
          deviceName: '/dev/xvda',
          ebs: {
            encrypted: true,
            volumeSize: 32,
          }
        }],
      enclaveOptions: {
          enabled: true,
      },
      imageId: proxyServerAMI.getImage(this).imageId,
      instanceType: 'c5.xlarge',
      securityGroupIds: [proxySG.securityGroupId],
      subnetId: vpc.selectSubnets({subnetGroupName: 'publicSubnetNatGateway'}).subnetIds[0],
      tags: [{
          key: 'Name',
          value: 'Nucleus-ReverseProxy',
        }],
      iamInstanceProfile: cfnInstanceProfile.ref
    })

    new route53.CnameRecord(this, `CnameApiRecord`, {
      recordName: fullDomain,
      zone: hostedZone,
      domainName: cfnInstance.attrPublicDnsName,
    });


    const revProxyCertAssociationPolicy = new iam.ManagedPolicy(this, 'revProxyCertAssociationPolicy', {
        statements: [
        new iam.PolicyStatement({
          actions: ["s3:GetObject"], resources: ["*"]
        })
      ]
    })
    proxyInstanceRole.addManagedPolicy(revProxyCertAssociationPolicy)

ステップ 6.Nucleus サーバリソースの追加

次に、Nucleus サーバを設定します。インスタンスタイプとして c5.4xlarge を使用する Ubuntu 20.04 LTS AMI を利用します。C5 インスタンスは計算量の多いワークロードに最適化されており、費用対効果の高い高いパフォーマンスを低料金で実現します。インスタンスには 16 個の vCPU と 32 GB のメモリがあり、Amazon Elastic Block Store (EBS) ボリュームが 512 GB のストレージを持つインスタンスにアタッチされます。これらの仕様は、概念実証に適切なサイズを選定しました。

インスタンスユーザーデータスクリプトは、Docker、docker-compose、および AWS CLI をインストールするように設定されています。

const nucleusServerAMI = new ec2.GenericLinuxImage({
      'us-west-1': 'ami-0dc5e9ff792ec08e3',    //california
      'us-west-2': 'ami-0ee8244746ec5d6d4',    //oregon
      'us-east-1': 'ami-09d56f8956ab235b3',    //virginia
      'us-east-2': 'ami-0aeb7c931a5a61206',    //ohio
      'ca-central-1': 'ami-0fb99f22ad0184043', //canada
      'eu-central-1': 'ami-015c25ad8763b2f11', //frankfurt
      'eu-west-1': 'ami-00c90dbdc12232b58',    //ireland
      'eu-west-2': 'ami-0a244485e2e4ffd03'     //london
    })

    const nucleusEbsVolume: ec2.BlockDevice = {
      deviceName: '/dev/sda1',
      volume: ec2.BlockDeviceVolume.ebs(512, {
          encrypted: true,
      })
    };

    const nucleusInstanceRole = new iam.Role(this, 'nucleusInstanceRole', {
      assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
      description: 'EC2 Instance Role',
      managedPolicies: [
          iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore')
      ],
      inlinePolicies: {
        getBucketObjectsPolicy: getBucketObjectsPolicy
      }
    });

    const nucleusUserData = `
    #!/bin/bash
    sudo apt-get update

    # docker
    sudo apt-get -y install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
    sudo apt-get -y update
    sudo apt-get -y install docker-ce docker-ce-cli containerd.io

    # docker compose
    sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    sudo chmod +x /usr/local/bin/docker-compose

    # aws cli
    sudo curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
    sudo apt-get install unzip
    sudo unzip awscliv2.zip
    sudo ./aws/install
    sudo rm awscliv2.zip
    sudo rm -fr ./aws/install
    `

    const nucleusServerInstance = new ec2.Instance(this, "NucleusServer", {
      instanceType: new ec2.InstanceType("c5.4xlarge"),
      machineImage: nucleusServerAMI,
      blockDevices: [nucleusEbsVolume],
      vpc: vpc,
      role: nucleusInstanceRole,
      securityGroup:  nucleusSG,
      userData: ec2.UserData.custom(nucleusUserData),
      vpcSubnets: vpc.selectSubnets({subnetGroupName: 'privateSubnet'}),
      detailedMonitoring: true,
    });

    Tags.of(nucleusServerInstance).add("Name", "Nucleus-Server");

ステップ 7.スタック出力を設定

次に、実行後に参照できるように出力値を追加します。

new CfnOutput(this, 'region', {
      value: this.region
    }).overrideLogicalId('region');

    new CfnOutput(this, 'artifactsBucketName', {
      value: artifactsBucket.bucketName
    }).overrideLogicalId('artifactsBucketName');

    new CfnOutput(this, 'tlsCertifcateArn', {
      value: certificate.certificateArn
    }).overrideLogicalId('tlsCertifcateArn');

    new CfnOutput(this, 'proxyInstanceRoleArn', {
      value: proxyInstanceRole.roleArn
    }).overrideLogicalId('proxyInstanceRoleArn');

    new CfnOutput(this, 'proxyCertAssociationPolicyArn', {
      value: revProxyCertAssociationPolicy.managedPolicyArn
    }).overrideLogicalId('proxyCertAssociationPolicyArn');

    new CfnOutput(this, 'nucleusServerPrivateDnsName', {
      value: nucleusServerInstance.instancePrivateDnsName
    }).overrideLogicalId('nucleusServerPrivateDnsName');

    new CfnOutput(this, 'domain', {
      value: fullDomain
    }).overrideLogicalId('domain');

ステップ 8.スタックのデプロイ

cdk deploy

これが完了すると、必要な基本リソースが用意されるので、次はそれらを構成します。

以下の CDK デプロイエラーが発生した場合:

[Error at /NucleusAppStack] Found zones: [] for dns:DOMAIN, privateZone:undefined, vpcId:undefined, but wanted exactly 1 zone

正しいドメインが指定されていることと、ホストゾーンが Route 53 コンソールの Route 53 ホストゾーンに存在することを確認してください。

ステップ 9.スタックの出力値を書き留めておく(次のセクションで使用します)

Outputs: NucleusAppStack.*artifactsBucketName* = nucleusappstack-artifactsbucket… NucleusAppStack.*domain* = nucleus.my_omniverse.com 
NucleusAppStack.*nucleusServerPrivateDnsName* = ip-...us-west-2.compute.internal 
NucleusAppStack.*proxyCertAssociationPolicyArn* = arn:aws:iam::...:policy/...
NucleusAppStack.*proxyInstanceRoleArn* = arn:aws:iam::...:role/... 
NucleusAppStack.*region* = ... 
NucleusAppStack.*tlsCertifcateArn* = arn:aws:acm:...:...:certificate/...

リバースプロキシサーバーの設定

ステップ 1.Enclave 証明書をプロキシインスタンスの IAM ロールに関連付ける

リバースプロキシで最初に行う必要があるのは、Nitro Enclave が使用する IAM ロールに証明書を関連付けることです。次のコードでは、以下のスクリプトの tls-certificate-arn、proxy-instance-role-arn、proxy-cert-association-policy-arn、および region を上記のスタック出力値に置き換えてください。

注:次のスクリプトは Python 3.9 で記述されています。Pythonのバージョンが競合しているという問題がある場合。ローカルの virtualenv を設定することをおすすめします。詳細については、Python Tutorial Virtual Environments and Packagesを参照してください。

CERT_ARN = tls-certificate-arn
ROLE_ARN = proxy-instance-role-arn
ROLE_POLICY_ARN = proxy-cert-association-policy-arn
REGION = region
 
import boto3
import json
 
ec2_client = boto3.client('ec2')
iam_client = boto3.client('iam')
iam_rsrc = boto3.resource('iam')
 
response = ec2_client.associate_enclave_certificate_iam_role(
    CertificateArn=CERT_ARN,
    RoleArn=ROLE_ARN
)
 
print(response)
 
bucket = response['CertificateS3BucketName']
s3object = response['CertificateS3ObjectKey']
kmskeyid = response['EncryptionKmsKeyId']
 
# update policy with association resources
policy = iam_rsrc.Policy(ROLE_POLICY_ARN)
policyJson = policy.default_version.document
cur_version = policy.default_version_id
 
policyJson['Statement'] = [
{
    "Effect": "Allow",
    "Action": [
    "s3:GetObject"
    ],
    "Resource": [f"arn:aws:s3:::{bucket}/*"]
},{
    "Sid": "VisualEditor0",
    "Effect": "Allow",
    "Action": [
        "kms:Decrypt"
    ],
    "Resource": f"arn:aws:kms:{REGION}:*:key/{kmskeyid}"
},{
        "Effect": "Allow",
        "Action": "iam:GetRole",
        "Resource": ROLE_ARN
}]
 
response = iam_client.create_policy_version(
    PolicyArn = ROLE_POLICY_ARN,
    PolicyDocument= json.dumps(policyJson),
    SetAsDefault= True
)
 
print(response)
 
response = iam_client.delete_policy_version(
    PolicyArn = ROLE_POLICY_ARN,
    VersionId = cur_version
)
 
print(response)

このスクリプトは、ID と IAM ロールをAWS Certificate Manager (ACM) 証明書に関連付けます。これにより、証明書をenclave内の ACM for Nitro Enclaves アプリケーションで使用できるようになります。詳細については、アマゾンウェブサービス Nitro Enclaves ユーザーガイドのCertificate Manager for Nitro Enclaves を参照してください。スクリプトは IAM ロールポリシーを更新して、独自のロールを取得し、証明書をダウンロードし、復号化する権限を設定します。

上記のスクリプトをファイルに保存し、ターミナルから実行します。

python ./associate_enclave_cert.py

ステップ 2.Nginx コンフィグレーションの設定

NVIDIA は Nucleus デプロイメント用の Nginx 設定のサンプルを提供しています。付属のアーカイブファイルにパッケージ化されています。執筆時点での、最新のものはnucleus-stack-2022.4.0+tag-2022.4.0-rc.1.gitlab.6522377.48333833.tar.gz です。

アーカイブを開いて ssl/nginx.ingress.router.conf を探してください。

このファイルを更新してから、リバースプロキシインスタンスの /etc/nginx/conf.d/nginx.conf に配置する必要があります。

まず、『Nitro Enclaves 用 AWS 証明書マネージャー』ガイド: Nitro Enclaves application: AWS Certificate Manage で説明されている内容で設定を更新する必要があります。

ファイルの先頭のメインコンテキストに以下を追加します。

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

ssl_engine pkcs11;

セキュリティプラクティスの必要に応じて SSL オプションを設定する行の後に、以下のスニペットを追加します。

ssl_protocols TLSv1.2;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout  10m;
ssl_prefer_server_ciphers on;

# Set this to the stanza path configured in /etc/nitro_enclaves/acm.yaml
include "/etc/pki/nginx/nginx-acm.conf";

次に、Nucleus Server のプライベート DNS アドレスとサーバの完全修飾ドメインを使用して設定ファイルを更新します。my-ssl-nucleus.my-company.com のインスタンスをご利用のドメインに置き換えてください。次に、BASE_STACK_IP_OR_HOST のインスタンスを、上記のスタック出力の nucleusServerPrivateDnsName に置き換えます。

ステップ 3.conf ファイルを Amazon S3 にコピーします

aws s3 cp ssl/nginx.ingress.router.conf s3://artifactBucketName/nginx.conf

ステップ 4.プロキシインスタンスに接続

ウェブブラウザから、AWS コンソールの EC2 Dashboard に移動し、Nucleus-ReverseProxy インスタンスを選択して、接続ボタンを押します。

セッションマネージャタブを選択し、接続ボタンを押します。

ステップ 5.ターミナルで、nginx.conf ファイルのパスを Amazon S3 から /etc/nginx/ にコピー

sudo aws s3 cp s3://*artifactBucketName*/nginx.conf  ./nginx.conf 
sudo mv ./nginx.conf /etc/nginx/conf.d/nginx.conf

ステップ 6.プロキシサーバーターミナルを開いたまま、次のコマンドを使用して、Nitro Enclaves のサンプル ACM 設定ファイルの名前を /etc/nitro_enclaves/acm.example.yaml から /etc/nitro_enclaves/acm.yaml に変更

sudo mv /etc/nitro_enclaves/acm.example.yaml /etc/nitro_enclaves/acm.yaml

ステップ 7.acm.yamlcertificate_arn の値を更新

お好みのテキストエディターを使用して、/etc/nitro_enclaves/acm.yaml を開きます。ACM セクションで、certificate_arn をスタックの証明書の ARN で更新します。これは上記のスタック出力の tls-certificate-arn です。ファイルを保存して閉じます。

ステップ 8.Nginx サーバーを起動

sudo systemctl start  nitro-enclaves-acm.service
sudo systemctl enable  nitro-enclaves-acm

ステップ 9.サーバーがドメインへの TLS リクエストを受け付けていることを確認

curl https://nucleus.my-omniverse.com

汎用の HTML テンプレートが出力として表示されます。

Nucleus サーバの設定

以下の内容の多くは、Nucleus サーバの導入に関する NVIDIA のドキュメントに基づいています。詳細については Enterprise Nucleus Server Quick Start Tips.を参照してください。

ステップ 1.AWS CLI を使用してローカルコンピュータから、Nucleus スタックのアーカイブを Amazon S3 にコピー

aws s3 cp ./nucleus-stack-2022.4.0+tag-2022.4.0-rc.1.gitlab.6522377.48333833.tar.gz s3://*artifactBucketName*/nucleus-stack-2022.4.0+tag-2022.4.0-rc.1.gitlab.6522377.48333833.tar.gz

ステップ 2.EC2 セッションマネージャを使用して Nucleus サーバに接続

ウェブブラウザで、AWS コンソールの EC2 ダッシュボードに移動し、Nucleus-Server インスタンスを選択して 接続 ボタンを押し、セッションマネージャ タブの 接続 ボタンをもう一度押します。

ステップ 3.Nucleus-Server ターミナルで、ディレクトリをホームディレクトリに変更し、S3 から Nucleus スタックをコピー

cd ~ aws s3 cp s3://*artifactBucketName*/nucleus-stack-2022.4.0+tag-2022.4.0-rc.1.gitlab.6522377.48333833.tar.gz ./nucleus-stack-2022.4.0+tag-2022.4.0-rc.1.gitlab.6522377.48333833.tar.gz

ステップ 4.アーカイブを適切なディレクトリに解凍し、そのディレクトリに移動

omniverse_root=/opt/ove
sudo mkdir -p $omniverse_root
sudo tar xzvf  nucleus-stack-2022.4.0+tag-2022.4.0-rc.1.gitlab.6522377.48333833.tar.gz -C  $omniverse_root --strip-components=1
cd ${omniverse_root}/base_stack

ステップ 5.nucleus-stack.env を更新

お好みのテキストエディタで、nucleus-stack.env ファイルを確認してください。このファイル全体をレビューすることをお勧めします。このファイルを使用して、NVIDIA Omniverse エンドユーザー使用許諾契約に同意することを確認します。

次に、必要に応じて次のnucleus-stack.env 変数を更新します。

ACCEPT_EULA          Review the notes in the .env file
SECURITY_REVIEWED    Review the notes in the .env file
SERVER_IP_OR_HOST    Set to the Nucleus Server private DNS name
SSL_INGRESS_HOST     Set as fully qualified domain name eg, nucleus.my_omniverse.com
MASTER_PASSWORD      Omniverse master user password
SERVICE_PASSWORD     Omniverse service user password
INSTANCE_NAME        Omniverse instance name
DATA_ROOT            Omniverse Data root directory
WEB_PORT             NVIDIA recommends that you set this to 8080, this is also what the nginx.conf is configured to expect

ステップ 6.認証に必要なシークレットを生成

現時点では SSO 統合を使用していないため、以下が必要であることに注意してください。詳細については、nucleus-stack.env のセキュリティノートを参照してください。

sudo chmod +x  ./generate-sample-insecure-secrets.sh
sudo ./generate-sample-insecure-secrets.sh

ステップ 7.Nucleus のdocker imageをpull

sudo docker-compose –env-file ${omniverse_root}/base_stack/nucleus-stack.env -f ${omniverse_root}/base_stack/nucleus-stack-ssl.yml pull

ステップ 8.Nucleus スタックを起動

sudo docker-compose --env-file ${omniverse_root}/base_stack/nucleus-stack.env -f ${omniverse_root}/base_stack/nucleus-stack-ssl.yml up -d

使用方法

ローカルマシンに戻り、.env ファイルで指定したドメインを Web ブラウザに入力して Nucleus Server への接続をテストします。次のログインダイアログが表示されます。

ここでは、nucleus-stack.env に設定されているマスターまたはサービスのユーザー名とパスワードを使用するか、アカウントの作成 を押します。すると、Nucleus Server コンテンツのナビゲータービューが表示されます。

クリーンアップ

ステップ 1.dissacociate_enclave_cert.py スクリプトを実行して Nitro Enclave 証明書の関連付けを解除します。

print(response)
CERT_ARN = "*tlsCertifcateArn*"
ROLE_ARN = "*proxyInstanceRoleArn*"
 
import boto3
 
ec2_client = boto3.client('ec2')
 
response = ec2_client. disassociate_enclave_certificate_iam_role(
    CertificateArn=CERT_ARN,
    RoleArn=ROLE_ARN
)

ステップ 2.nucleus-app アプリケーションフォルダからcdk destroy を実行してスタックを削除します。

まとめ

本記事では、Docker Compose コンテナを使用して Amazon EC2 で NVIDIA Omniverse Nucleus を起動して実行するための基本事項について説明しました。Amazon EC2 Nucleus サーバーとリバースプロキシサーバーのセットアップ手順、設定ファイルの保存と取得のための S3、Omniverse データへの安全でプライベートなアクセスのための Route 53 プライベートホストゾーンの実装について説明しました。

Nucleus を Amazon EC2 にデプロイすることで、チームがどこにいても、3D 製品、アプリケーション、エクスペリエンスを構築しながら、リアルタイムでコラボレーションを行うことができます。

AWS でのSpatial Computingの詳細については、Spatial Computing Blog をご覧ください。

その他の参考資料

AWS GitHub リポジトリ、nvidia-omniverse-nucleus-on-amazon-ec2 にサンプルコードがあるので必要に応じてご参照ください。

AWS サービス

Amazon EC2, あらゆるワークロードに対応する安全でサイズ変更可能なコンピューティングリソース
Amazon Route 53, エンドユーザーをインターネットアプリケーションにルーティングする信頼性が高く費用対効果の高い方法
Amazon S3, どこからでも任意の量のデータを取得できるように構築されたオブジェクトストレージ
Amazon EBS, 使いやすく、様々な規模に対応可能な高性能ブロックストレージ
AWS Certificates Manager for Nitro Enclaves, インスタンス上で稼働するウェブサーバーを備えたパブリックおよびプライベート TLS 証明書用 AWS 証明書マネージャ

NVIDIA Omniverse Nucleus

Nucleus Overview
Nucleus Documentation

Kellan Cartledge

Kellan Cartledge

Kellan Cartledge は、アマゾンウェブサービスのSpatial Computingスペシャリストソリューションアーキテクトです。AWS では、クラウド上の没入型テクノロジーによる可能性の定義と探求を行っています。建築、エンターテインメント、コンピューターグラフィックス分野で 10 年以上の経験があります。Kellan は、仮想世界と物理世界の組み合わせ、そして空間体験の未来に情熱を注いでいます。

Adam Harder

Adam Harder

Adam HarderはSr Spatial Computing ソリューション アーキテクトです。拡張現実 (AR) と仮想現実 (VR) のアプリケーションと、それらをサポートする 3D データパイプラインの構築に情熱を注いでいます。彼は自由時間を家族と冒険して過ごします。一緒にビーチで遊んだり、ハイキングやキャンプに行ったり、ロードトリップをしたり、たくさんの映画を見たりします。

翻訳はVR/AR/3D Prototyping ソリューションアーキテクト嶋田が担当しました。原文はこちらです。