PLAY DEVELOPERS BLOG

HuluやTVerなどの日本最大級の動画配信を支える株式会社PLAYが運営するテックブログです。

HuluやTVerなどの日本最大級の動画配信を支える株式会社PLAYが運営するテックブログです。

Pulumi AIでAWS Media Servicesを使用したライブ配信環境は作成できるのか?

こんにちは。プラットフォーム技術部開発第2グループの村井です。

最近ではAWS CDKやTerraformなどのInfrastructure as Code(IaC)ツールを使ってインフラをコードで管理することも珍しくないですよね。

今回はIaCツールの一つ、Pulumiのコードを今流行りのChatGPTのように対話ベースで作成することができるPulumi AIについて紹介していきます。

Pulumiとは

Pulumi - Infrastructure as Code in Any Programming Language

Code and ship faster

Author infrastructure code using programming languages you know and love. Write statements to define infrastructure using your IDE with autocomplete, type checking, and documentation.

Test your code with unit tests and deliver it through CI/CD pipelines to validate and deploy to any cloud.

要約すると「プログラミング言語を使用してインフラをコードで管理してデプロイできます」とのことです。 説明を見ると先程挙げたAWS CDKやTerraformと同じような印象を受けますね。 TypeScript/Python/Golang/C#/Java/YAMLをサポートしており、また2023年7月現在ではAWS/GCP/Azureなどの主要なインフラとマイナーなインフラ合わせて142のサービスをサポートしているようです。

個人利用は無料ですが、チームでの使用には料金がかかります。

Pulumi AIとは

Pulumi AIは2023年4月にリリースされたサービスで、PulumiのコードをChatGPTのようにAIとの対話ベースで作成していくことができます。 Pulumi AIは以下Webサイトから無料で使うことができます。 中身はChatGPTのようで2023年7月現在ではGPT-3かGPT-4から選べます。無料でGPT-4を使えてるようなものなので嬉しいですね。 www.pulumi.com

www.pulumi.com

UI

UIは以下のような感じです。左下にテキストを入力する欄があり、どのようなインフラを構成したいのか記載していきます。画面右側の記述はPulumi AIでコードを作成したあとにどのようにインフラ環境にデプロイするのか手順が記載されています。

※本記事ではデプロイの流れは省略します。

Pulumi AIを使ってみた

では早速Pulumi AIを使ってみようと思います。

弊社は動画配信を生業としていますので、せっかくなのでAWS Media Servicesを使用した動画配信環境を作成してみたいと思います。

構成はAWSのハンズオンにある簡単なもので試してみます。 ローカル環境からライブ配信ソースをAWS Elemental MediaLiveに打ち上げてAWS Elemental MediaStore→CloudFront経由でHLSで配信することを目指します。

catalog.us-east-1.prod.workshops.aws

ではPulumi AIに実際に命令してみます。 Pulumi AIは英語で命令を出さないといけないのでご注意ください。見やすいように以下では命令を日本語で表記しています。

まずは以下。AWSなどのインフラなどを知らない体で、システム構成などは指定せずにざっくり命令してみます。

命令文

ライブ配信環境を作ってください

回答

To proceed, I need a bit more information:

− Which environment do you plan to capture your stream in?
− Which environment do you plan on sending your stream to?

環境などの情報をもう少し詳細に教えてくれと回答されました。どのインフラ環境かという指定は必要のようです。 AWSでという条件を追加してみます。

追加の命令文

AWSで環境を作ってください

回答

Certainly! Your goal seems aligned to creating a CloudFront Distribution for serving your content, and perhaps an Amazon S3 bucket for storing that live streaming data.

Here's a simple Pulumi program that creates a CloudFront Distribution (AWS CDN service) to serve content stored in an S3 bucket on AWS:

Please replace "mybucket" and "myorigin" with your desired bucket and origin names.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// Create an AWS S3 bucket
const s3Bucket = new aws.s3.Bucket("mybucket");

// Create a CloudFront Origin Access Identity
const originAccessIdentity = new aws.cloudfront.OriginAccessIdentity("myoai");

const s3BucketPolicy = new aws.s3.BucketPolicy("buckPolicy", {
    bucket: s3Bucket.id, // relate bucket policy to the created bucket
    policy: pulumi
        .all([s3Bucket.arn, originAccessIdentity.iamArn])
        .apply(([bucketArn, oaiIamArn]) => JSON.stringify({
            Version: "2012-10-17",
            Statement: [{
                Effect: "Allow",
                Principal: {
                    AWS: oaiIamArn,
                },
                Action: "s3:GetObject",
                Resource: bucketArn,
            }],
        })),
});

const distribution = new aws.cloudfront.Distribution("mydistribution", {
    enabled: true,
    defaultCacheBehavior: {
        allowedMethods: ["GET", "HEAD", "OPTIONS"],
        cachedMethods: ["GET", "HEAD", "OPTIONS"],
        targetOriginId: "myorigin        forwardedValues: {
            queryString: false,
            cookies: { forward: "none" },
        },
        viewerProtocolPolicy: "allow-all",
        minTtl: 0,
        defaultTtl: 3600,
        maxTtl: 86400,
    },
    origins: [{
        domainName: s3Bucket.bucketRegionalDomainName,
        originId: "myorigin",
        s3OriginConfig: {
            originAccessIdentity: originAccessIdentity.cloudfrontAccessIdentityPath,
        },
    }],
});

// Export the bucket name and distribution domain name
export const bucketName = s3Bucket.id;
export const cdnDomainName = distribution.domainName;

This aws.s3.Bucket resource creates an Amazon S3 bucket to store our content. This aws.cloudfront.Distribution resource creates an Amazon CloudFront distribution providing fast, secure delivery of data with features like geo-restriction, custom domain SSL certificates, among others.

This distribution is using the S3 bucket as it's origin for files to be served over the CDN. AWS CloudFront will securely deliver this data to customers globally with low latency and high transfer speeds.

I hope this helps you accomplish your goal!

なるほど、S3に動画ファイルをおいてそれをCloudFront経由で配信する方法を提示されました。 期待していたライブ配信環境のインフラ構成ではないですが、ある程度出来上がっているコードが出力されました。

CloudFrontの必須の設定が足りていなかったので追加で設定し、バケット名とオリジン名を少し修正すると問題なくデプロイできインフラ環境を作成できることが確認できました。

このやり取りから期待通りのインフラ構成を出力するためには具体的に命令する必要があることがわかりました。全くインフラを知らない人には厳しそうです。

仕切り直してハンズオンの構成でAWSでインフラを構成するように命令してみます。

命令文(二回目)

AWSでライブ配信するための設定を作成してください。
OBSでビデオをAWS MediaLiveにrtmp(push)で打ち上げ、AWS MediaStoreとCloudFrontを経由してHLSで日本にいるエンドユーザーに配信したいです。

回答(二回目)

Here is a TypeScript program using the Pulumi AWS SDK, that sets up a configuration for live video delivery in AWS. The video is pushed to AWS MediaLive via an RTMP input, and the output is delivered for end users in HLS format via AWS MediaStore and CloudFront.

In this program, we will create:

An AWS MediaLive input for the RTMP stream
An AWS MediaLive channel
An AWS MediaPackage channel for HLS packaging
An AWS MediaStore container to store the HLS chunks
An AWS CloudFront distribution to deliver the HLS stream to end users

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// Create a MediaLive Input for the RTMP stream
const rtmpInput = new aws.medialive.Input("rtmpInput", {
    destinations: [{
        streamName: "exampleStreamName",
    }],
    type: "RTMP_PUSH",
});

// Create a MediaLive channel
const rtmpChannel = new aws.medialive.Channel("rtmpChannel", {
    destinations: [{
        id: "destination1",
        settings: [{
            passwordParam: "destination1Password",
            streamName: "destination1Stream",
            url: "rmtp://destination1Url",
        }],
    }],
    encoderSettings: {
        // Fill in the required Encoder settings as per your requirements
    },
    inputAttachments: [{
        inputAttachmentName: "inputAttachment1",
        inputId: rtmpInput.id,
    }],
    inputSpecification: {
        codec: "AVC",
        maximumBitrate: "MAX_10_MBPS",
        resolution: "SD",
    },
    name: "exampleChannel",
});

// Create a MediaPackage Channel to package the video into HLS formatconst mediapackageChannel = new aws.mediapackage.Channel("mediapackageChannel", {
    channelId: "exampleChannel",
    description: "An example MediaPackage channel",
});

// Create a MediaStore Container to store the HLS chunks of video
const mediaStoreContainer = new aws.mediastore.Container("mediaStoreContainer", {
    name: "exampleContainer",
});

// Create a CloudFront Distribution to serve the HLS stream to the end users
const cloudfrontDistribution = new aws.cloudfront.Distribution("cloudfrontDistribution", {
    origin: [{
        domainName: mediaStoreContainer.endpoint,
        originId: mediaStoreContainer.name,
        customOriginConfig: {
            httpPort: 80,
            httpsPort: 443,
            originProtocolPolicy: "https-only",
            originSslProtocols: ["TLSv1.2"],
        },
    }],
    defaultCacheBehavior: {
        allowedMethods: ["GET", "HEAD", "OPTIONS"],
        cachedMethods: ["GET", "HEAD", "OPTIONS"],
        targetOriginId: mediaStoreContainer.name,
        forwardedValues: {
            queryString: false,
        },
        viewerProtocolPolicy: "redirect-to-https",
    },
    comment: "An example CloudFront Distribution for HLS streaming",
    enabled: true,
});

// Export the MediaLive Input and Channel end points
export const mediaLiveInputEndPoint = rtmpInput.id;
export const mediaLiveChannelEndPoint = rtmpChannel.id;

// Export the MediaPackage Channel, MediaStore, and CloudFront end points
export const mediaPackageChannelEndPoint = mediapackageChannel.id;
export const mediaStoreEndPoint = mediaStoreContainer.endpoint;
export const cloudFrontDistributionEndPoint = cloudfrontDistribution.domainName;

Please note that the encoderSettings for the aws.medialive.Channel needs to be filled in as your requirements. This can include audio, video, and caption selectors, output groups, and other settings. You can refer to the MediaLive Channel page on the Pulumi Registry for more details.

Also note that this example assumes that the RTMP stream is being pushed to the specified streamName under destinations in the aws.medialive.Input. You would need to set that up in the OBS settings.

This setup forms a complete video delivery workflow on AWS using Pulumi.

Please replace "exampleStreamName", encoderSettings, "destination1Password", "destination1Stream", "rmtp://destination1Url", and "exampleChannel" with appropriate values according to your use case.

先程よりも具体的な命令にしたので欲しい構成要素はある程度カバーされたコードが出力されたように見えます。 ただし、よくよく見ると普通に構文エラーの箇所もありますし、必須パラメータが指定されていない箇所もありました。 MediaStoreでと言っているのにMediaPackageの処理もなぜか入っている...。

このソースコードを自分で直していくのは骨が折れそうなので仕切り直してさらに具体的に命令してみます。 ハンズオンで設定する内容を文字で詳細に書き起こしていきます。

命令文(三回目)

AWSでライブ配信するためのインフラ構成を作成してください。
OBSでMP4のファイルをAWS MediaLiveにrtmp(push)で打ち上げ、MediaStoreとCloudFrontを経由してHLSで日本にいるエンドユーザーに配信することを想定しています。
以下の指定に従ってPulumiのソースコードを作成するようにしてください。

・OBSでrtmp(push)してMediaLiveに映像を打ち上げる(これはローカルPCからの打ち上げについての記述なのでインフラのコードでは考慮する必要なし)
・MediaLiveのチャンネルの設定は以下とすること。
 ・チャンネルテンプレートはLive Event HLS
 ・チャンネル名はHandsonLiveChannelmurai1
 ・IAMロールはテンプレートロールから作成したMediaLiveAccessRoleとすること
 ・チャンネルクラスはSTANDARDとすること
 ・入力仕様の設定は、入力コーデックがAVC、入力解像度をHD、最大入力ビットレートをMAX_20_MBPSとすること
 ・出力配信の配信方法はパブリックとすること
・MediaLiveのInputの設定は以下とすること
 ・入力名はHandsonInputChannelmurai1とすること
 ・入力タイプはRTMP(プッシュ)とすること
 ・入力の送信先のについては送信先Aのアプリケーション名をHandsonA、インスタンスをAとし送信先Bのアプリケーション名をHandsonB、インスタンスをBとすること。
・入力セキュリティーグループの設定は以下とすること
 ・CIDRは0.0.0.0/0とすること
・出力グループの設定は以下とすること
 ・HD(HLS)を選択する
 ・HLS設定のCDN設定はhls media storeとすること
 ・Manifest and Segmentsのセグメント長は2,セグメント数は3を指定すること
・MediaStoreの設定は以下とすること
 ・コンテナ名はHandsonContainermurai1とすること
 ・Container policyのPrincipalの設定はAWS: “*”とすること
 ・Container CORS policyはdefault policyとすること
・CloudFrontの設定は以下とすること
 ・オリジンドメインには先述のMediaStoreのHandsonContainermurai1を指定すること
 ・プロトコルはHTTPSを指定すること
 ・それ以外はデフォルト値を指定すること

回答(三回目)

Based on your description, this is a step-by-step Pulumi TypeScript program for setting up AWS MediaLive, AWS MediaStore, and Amazon CloudFront for your live streaming infrastructure.

 

import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";

const inputSecurityGroup = new aws.medialive.InputSecurityGroup("SecurityGroup", {
  whitelistRules: [{ cidr: '0.0.0.0/0' }], // CIDR for the Input security group
});

const medialiveInput = new aws.medialive.Input("medialiveInput", {
  destinations: [
    { streamName: 'HandsonA' }, // The Input destination A
    { streamName: 'HandsonB' }  // The Input destination B
  ],
  type: 'RTMP_PUSH', // Input type
  inputSecurityGroups: [inputSecurityGroup.id], 
});

const liveChannel = new aws.medialive.Channel("liveChannel", {
  encoderSettings: { /* Note: Tune this to the desired values for your case */ },
  inputAttachments: [{
    inputAttachmentName: 'HandsonInputChannelmurai1',
    inputId: medialiveInput.id,
  }],
  inputSpecification: {
    codec: 'AVC',
    inputResolution: 'HD',
    maximumBitrate: 'MAX_20_MBPS',
  },
  channelClass: 'STANDARD', 
  name: 'HandsonLiveChannelmurai1',
});

const mediastoreContainer = new aws.mediastore.Container("mediastoreContainer", {
  name: 'HandsonContainermurai1',
});

const mediastorePrincipalPolicy = new aws.mediastore.ContainerPolicy("mediastoreContainerPolicy", {
  containerName: mediastoreContainer.name,
  policy: `{
    "Version": "2012-10-17",
    "Statement": [{
      "Sid": "MediaStoreDefaultPolicy",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "mediastore:*",
      "Resource": "arn:aws:mediastore:${pulumi.getRegion()}:*:container/${mediastoreContainer.name}/*"
    }]
  }`,
});

// Create a CloudFront distribution for the MediaStore container
const cloudfrontDistribution = new aws.cloudfront.Distribution("cloudfrontDistribution", {
  enabled: true,
  defaultCacheBehavior: { /* Defaults as per your needs */ },
  origins: [
    {
      domainName: mediastoreContainer.endpoint,
      originId: mediastoreContainer.arn,
      customOriginConfig: {
        httpPort: 80,
        httpsPort: 443,
        originProtocolPolicy: "https-only"
      },
    },
  ],
  restrictions: {
    geoRestriction: {
      restrictionType: "none"
    },
  },
  viewerCertificate: {
    cloudfrontDefaultCertificate: true,
  }
});

You will need to fill in the encoderSettings field for the aws.medialive.Channel resource as per your actual live streaming setting requirements.

The created resources will represent your live streaming infrastructure. The RTMP sources will send streams to the MediaLive input you've set up, which will in turn forward it to the live channel. Once stored in MediaStore, it can be distributed via CloudFront to your viewers.

For additional information on using aws.medialive.Channel, aws.medialive.Input, aws.medialive.InputSecurityGroup, aws.mediastore.Container, and aws.cloudfront.Distribution, please refer to the following Pulumi Registry documentation:

aws.medialive.Channel
aws.medialive.Input
aws.medialive.InputSecurityGroup -aws.mediastore.Container](https://www.pulumi.com/registry/packages/aws/api-docs/mediastore/container/)
aws.cloudfront.Distribution

先程よりも良さげなコードが出力されました。詳細に指定した項目は基本的に記述されており、MediaPackageに関する記述はなくなっています。

ただ、詳細に指定していないところや全く命令で触れていない箇所については自分で調べて設定してねというスタンスのようで、何も記載されていない状態になっています。 このあとデフォルト値でいいので動くコードを作成してくれと命令してみましたが、上記とほとんど変わらないソースコードが返ってきました。。。 結局多くの部分を自分で実装することになりました。

まとめ、触ってみた所感

初めてPulumiとPulumi AIを触ってみましたが、慣れ親しんだコードで実装できるという点はAWS CDKと似てるという印象を受けました。 Pulumi自体は良いツールと感じましたが、Pulumi AIにはまだまだ課題があると感じました。

組み合わせるサービスが多い、あるいは設定するパラメータが多いサービスを使用する場合は出力の精度が低くなる傾向にありそうです。 また、必須パラメータの中でも毎回ある程度詳細に書き起こしてくれる項目と、そうでない項目がありました。

生成AI系のツールに求めているものはある程度ざっくりとした命令でも動くものが返ってくることだと思います。Pulumi AIはまだその域に達していないのかなと。Pulumi AIの出力結果でわからないところをPulumi AIに聞き直すのとGPT-3に聞くのとでは、GPT-3に聞いたほうがわかりやすい回答が返ってくることもしばしば。

もう少し精度が上がれば使えるツールになることは間違いないと思うので、今後のアップデートなどに注目したいと思います。