AWSLab

AIでパワポを作るOSS「Presenton」をAWSに構築する

スポンサーラベル
PresentonをAWSに1台構成で立てる。社内の端末からセキュリティグループで送信元を絞ったEC2(t3.medium/Amazon Linux 2023)に接続し、コンテナ内のPresentonが外部のLLM APIを呼ぶ。CloudFormationでEC2・EIP・SG・IAMロールを一括作成する AWS

当サイトはアフィリエイト広告を利用しています。

社内向けの資料作成は、中身を考える時間よりも体裁を整える時間のほうが長くなりがちです。生成AIでスライドを作るサービスはいくつもありますが、業務資料をそのまま外部のSaaSに投げられないという事情で見送った方も多いのではないでしょうか。

Presenton はApache-2.0で公開されているAIプレゼン生成ツールです。Dockerコンテナ1個で動き、使うLLMも自分で選べます。自社のAWSアカウントに立てて、自分のAPIキーで動かせば、資料もモデルも管理下に置いたまま試せます。

本記事では、東京リージョンにCloudFormationで検証環境を構築し、実際にスライドを1本生成するまでに確かめた内容をまとめます。公式ドキュメントには書かれていない、つまずいた点を中心に扱います。

1. Presentonとは何か

テーマを1行渡すと、構成を考えて本文まで書き、スライドとして組み上げてくれるWebアプリケーションです。手元で9枚のスライドを生成したところ、表紙・沿革・事業の柱・まとめまで、話の流れが通った状態で出てきました。

特徴は3点です。

項目内容
ライセンスApache-2.0。Dockerコンテナ1個で動く
対応プロバイダOpenAI、Anthropic、Google Gemini、Amazon Bedrock、Azure OpenAI、Ollama、LM Studio など14系統
出力編集可能なPPTXとPDF。画像に焼き込まれていない

特に効くのが出力形式です。生成物が画像化されていないため、受け取った側がPowerPointでそのまま直せます。既存のPPTXをアップロードして、その体裁をテンプレートとして再利用することもできます。

ただし、OSS版には無いものもあります。 SSO(OAuth / OIDC / SCIM)と自動プロビジョニング、集中管理コンソール、監査ログはEnterprise版の機能です。OSS版のユーザー管理は、管理者が管理画面から1件ずつ払い出す方式になります。

2. 構成と費用

EC2を1台立てて、その上でコンテナを1個動かすだけの構成です。

PresentonをAWSに1台構成で立てる。社内の端末からセキュリティグループで送信元を絞ったEC2(t3.medium/Amazon Linux 2023)に接続し、コンテナ内のPresentonが外部のLLM APIを呼ぶ。CloudFormationでEC2・EIP・SG・IAMロールを一括作成する

2.1 t3.mediumで足りる

生成を1本流した直後のメモリ使用量は、3,835MB中869MBでした。ロードアベレージも0.05で、4GBのt3.mediumに余裕があります。ディスクは30GB中5.6GBの使用でしたが、コンテナイメージが大きいので20GBだと心もとありません。

同時に何本も生成する運用なら、PPTX書き出しにChromiumが動く分を見てt3.largeを検討します。まずはt3.mediumで始めて、実際の頻度を見てから判断すれば十分です。

2.2 費用

AWS Pricing APIから取得した東京リージョンのオンデマンド単価です(2026年8月時点)。

項目単価
t3.medium$0.0544 / 時
EBS gp3$0.096 / GB・月
パブリックIPv4$0.005 / 時

この構成(t3.medium+gp3 30GB+IPv4 1個)の月額は、1USD=150円換算で次のようになります。

稼働パターン月額
常時稼働(730時間)約6,900円
平日9〜19時のみ(220時間)約2,800円
止めっぱなし約1,000円

ここで見落としやすいのがパブリックIPv4の料金です。2024年2月以降、パブリックIPv4は稼働中でも停止中でも1アドレスあたり$0.005/時がかかります。 「EIPはインスタンスに関連付けている間は無料」という以前の常識は通用しません。インスタンスを止めても月1,000円ほどは残るので、長く使わないならスタックごと削除したほうが安く済みます。

なお「パブリックIPを外してプライベートIPだけにすれば安いのでは」と考えたくなりますが、逆効果です。プライベートIPは無料ですが、外向き通信の経路が消えるためdocker pullもLLM APIの呼び出しもできなくなります。代わりにNATゲートウェイを置くと月6,800円、SSM用のVPCエンドポイント3種でも月4,600円かかります。パブリックIPv4の550円が、他の選択肢より8〜12倍安いという結論になりました。

3. CloudFormationで構築する

テンプレートは次のとおりです。EC2の起動からDockerの導入、コンテナの起動までをUserDataで完結させ、cfn-signalで完了を通知します。

AWSTemplateFormatVersion: "2010-09-09"
Description: Presenton をシングルインスタンス構成で構築する

Parameters:
  VpcId:
    Type: AWS::EC2::VPC::Id
  SubnetId:
    Type: AWS::EC2::Subnet::Id
  AllowedCidr:
    Type: String
    Description: 画面へのアクセスを許可する CIDR
  InstanceType:
    Type: String
    Default: t3.medium
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64

Resources:
  # SSH 鍵を使わず Session Manager で入るためのロール
  InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal: { Service: ec2.amazonaws.com }
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles: [!Ref InstanceRole]

  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Presenton web UI
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref AllowedCidr

  # パブリック IP を固定する。停止・起動しても変わらない
  Eip:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc

  EipAssociation:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt Eip.AllocationId
      InstanceId: !Ref Instance

  Instance:
    Type: AWS::EC2::Instance
    CreationPolicy:
      # 画面が返るまでスタックを CREATE_COMPLETE にしない
      ResourceSignal:
        Timeout: PT30M
        Count: 1
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      IamInstanceProfile: !Ref InstanceProfile
      SubnetId: !Ref SubnetId
      SecurityGroupIds: [!Ref SecurityGroup]
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: 30
            VolumeType: gp3
            DeleteOnTermination: true
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          # 失敗したらそこで止め、原因をログに残す
          set -xe
          exec > >(tee -a /var/log/presenton-setup.log) 2>&1

          # cfn-signal を使うために必要
          dnf install -y aws-cfn-bootstrap docker
          systemctl enable --now docker
          usermod -aG docker ec2-user

          # 生成物と設定の置き場。コンテナを作り直しても残るようホスト側に置く
          mkdir -p /opt/presenton/app_data
          docker pull ghcr.io/presenton/presenton:latest

          docker run -d --name presenton --restart unless-stopped \
            -p 80:80 \
            -e DISABLE_IMAGE_GENERATION="true" \
            -e DISABLE_ANONYMOUS_TRACKING="true" \
            -v /opt/presenton/app_data:/app_data \
            ghcr.io/presenton/presenton:latest

          # 画面が返るまで待ってから完了を通知する
          for i in $(seq 1 90); do
            if curl -fsS http://localhost/ >/dev/null 2>&1; then break; fi
            sleep 10
          done

          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} \
            --resource Instance --region ${AWS::Region}

Outputs:
  PresentonUrl:
    Value: !Sub http://${Eip}/

CreationPolicycfn-signalを組み合わせているのがポイントです。これを入れておくと「スタックはできたがアプリが起動していない」という状態を防げます。 CREATE_COMPLETEになった時点で、ブラウザで開ける状態になっています。

適用はこのコマンドです。

# テンプレートを適用する(パラメータは自分の環境に読み替える)
aws cloudformation create-stack \
  --stack-name presenton-verify \
  --template-body file://presenton-cloudformation.yaml \
  --capabilities CAPABILITY_IAM \
  --region ap-northeast-1 \
  --parameters \
    ParameterKey=VpcId,ParameterValue=vpc-xxxxxxxx \
    ParameterKey=SubnetId,ParameterValue=subnet-xxxxxxxx \
    ParameterKey=AllowedCidr,ParameterValue=203.0.113.10/32

実測で6分38秒でCREATE_COMPLETEになりました。大半はコンテナイメージの取得時間です。

なお、AWS CLIのWindows版はテンプレート内の日本語をうまく送れず、スタックに保存されるコメントが????に化けます。日本語コメントを残したい場合は、boto3からTemplateBodyにUTF-8の文字列を渡してください。この件はCloudFormationでEC2にElastic IPを後付けするでも触れています。

4. 初回アクセスでつまずく点

構築が終わったらhttp://<EIP>/を開きます。ここで必ず管理者アカウントの作成を求められます。

これを知らずにAPIから叩くと、次のように返ります。

{"detail":"Login setup is required","setup_required":true}

HTTPステータスは428 Precondition Requiredです。Dockerの起動オプションだけでは完結せず、一度は画面を開く必要があります。 無人構築したい場合はAUTH_USERNAMEAUTH_PASSWORDを環境変数で渡せば、初回起動時に管理者を作れます。

もうひとつ、ユーザー管理まわりで押さえておきたい点があります。招待メールやパスワードリセットのメールは飛びません。 データベースのスキーマを確認したところ、userテーブルの列はこうなっていました。

id, username, admin_slot, hashed_password, is_active,
is_superuser, is_verified, created_at, auth_version

メールアドレスを保存する列がそもそも存在しません。 SMTP関連の設定項目も見当たらないため、管理者が作ったパスワードを別の手段で本人に渡す運用になります。人数が増えると、この受け渡しが地味な負担になります。

5. Bedrockはインスタンスロールで使えない

ここが今回いちばんはっきりした点です。

AWS上に立てるなら、アクセスキーを発行せずにインスタンスロールでBedrockを呼びたくなります。DifyでAWS Bedrockを設定するで扱ったように、DifyのBedrockプラグインは認証方式としてIAMロールを選べます。同じ発想でPresentonにもロールを付けて試しました。

コンテナ内のboto3はIMDS経由でインスタンスロールの資格情報を拾いConverseに成功するが、Presentonの設定画面にはリージョン・モデル・アクセスキー・シークレットの入力欄しかなく、IAMロールを使う選択肢が存在しない

まず、環境としてはロール認証が使えることは確認できました。コンテナに同梱されているboto3から直接Converse APIを叩くと、キーなしで成功します。

import boto3
c = boto3.client("bedrock-runtime", region_name="ap-northeast-1")
r = c.converse(
    modelId="jp.anthropic.claude-sonnet-4-5-20250929-v1:0",
    messages=[{"role": "user", "content": [{"text": "1文で自己紹介してください。"}]}],
    inferenceConfig={"maxTokens": 80},
)
# usage: {'inputTokens': 19, 'outputTokens': 61, 'totalTokens': 80}
print(r["output"]["message"]["content"][0]["text"])

この前提として、IMDSのホップ数が2である必要があります。 EC2のHttpPutResponseHopLimitが既定の1だと、Dockerのブリッジを1ホップ挟む分だけコンテナからメタデータに届きません。1になっている場合はテンプレートに次を足します。

      MetadataOptions:
        HttpTokens: required
        HttpPutResponseHopLimit: 2

ところが、Presenton側にこれを使う導線がありませんでした。 LLM=bedrockで起動しても、設定画面に出るのはリージョン・モデル・アクセスキー・シークレットの入力欄だけで、「IAMロールを使う」という選択肢が存在しません。公式ドキュメントもBEDROCK_AWS_ACCESS_KEY_IDBEDROCK_AWS_SECRET_ACCESS_KEYを必須としています。

つまり、AWS側でいくら権限を整えても、アプリ側に受け口がなければ使えません。 Bedrockを使うならアクセスキーを発行して画面から入力することになります。それを避けたいなら、AnthropicやOpenAIのAPIキーを直接使うほうが構成としては素直です。実際、手元の検証ではその方針に切り替えました。

6. 運用で気づいた点

実機を動かして分かった、ドキュメントに書かれていない点をまとめます。

閉域網では起動に失敗する可能性があります。 プレゼン単位の記憶を保持するmem0機能が既定で有効になっており、起動時にHuggingFaceから埋め込みモデルを取得しにいきます。外部へ出られない環境では止まる恐れがあるため、MEM0_ENABLED=falseで無効化しておくのが安全です。

データをS3に置くことはできません。 S3やオブジェクトストレージ用の設定項目は150個ある環境変数の中に1つもなく、ソースにもS3クライアントの記述はありませんでした。外に出せるのはDATABASE_URLで指定するデータベースだけで、生成物・テンプレート・アップロードファイルはapp_data配下に残ります。バックアップはapp_dataaws s3 syncで退避するか、EBSスナップショットを取る形になります。

トークン使用量はログに出ません。 LLMの費用を把握するには、プロバイダ側のコンソールで実測するしかありません。9枚のスライド1本あたりのディスク使用量は約1.04MBでした(画像生成を無効にした場合)。

7. まとめ

PresentonをAWS上に構築して確かめた内容を整理します。

  • Dockerコンテナ1個の構成で、CloudFormationなら6分程度で立ち上がる。 cfn-signalを入れておけば、CREATE_COMPLETE=使える状態になる
  • t3.mediumで足りる。 常時稼働で月およそ6,900円。パブリックIPv4は停止中でも課金される点に注意する
  • 初回は画面から管理者アカウントを作る必要がある。 APIだけでは完結しない
  • Bedrockはインスタンスロールでは使えない。 AWS側の権限が通っていても、アプリ側に選択肢がない
  • SSOと利用上限はOSS版にない。 全社展開を前提にするなら、この2点が判断の分かれ目になる

社内資料の下書きをAIに任せたいが、中身を外に出したくない。そうした要件には十分応えられる選択肢だと感じました。まずは検証環境を1台立てて、実際の資料で試してみることをおすすめします。使い終わったらスタックごと削除すれば、費用は止まります。

次回は、Presentonが備えるREST APIとMCPサーバーを使って、資料生成を既存の仕組みに組み込む方法について解説します。

AWSを効率的に学習する方法

私が効率的にAWSを学習するために実施した方法は以下の通りです。
 ①最初に書籍(ハンズオンができる)を購入、座学でAWSの基礎を学習
 ②AWS資格試験を取得ための学習

※私の場合は①と②を合わせて2か月でソリューションアーキテクトを取得できました。

①AWS基礎学習

最初に購入した書籍は「Amazon Web Services 基礎からのネットワーク&サーバー構築」です。

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂4版

この本では、AWSの基本サービスを利用したハンズオンを通じて、AWSの基礎を学習することができます。

また、タイトル通りAWSのネットワークやインフラに関しても網羅しているため、もともとインフラ系の技術者ではない人たちにとっても分かりやすい内容だと思います。

とりあえずAWS上にサーバーを設定して開発を行うための準備までするには最良の一冊です。

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂4版

②AWS資格取得

AWSの基礎をある程度学習することができたら、次はAWS資格を取得しましょう。まずはAWSソリューションアーキテクトを目指しましょう。

資格勉強のための問題集をひたすら解きながら、AWSの知識を積み重ねて習得していきましょう!

苦行ではありますがこの問題集を3周ほどすればAWS用語や構成に関しても習得できているはずです。

AWS認定ソリューションアーキテクト-アソシエイト問題集

独学だと手が止まってしまう場合は、ササエル のようなインフラエンジニア向けのオンラインスクールを使う手もあります。ネットワークやサーバーの分野に絞ったカリキュラムなので、業務でインフラを触る人が学び直すのにも使えます

コメント

タイトルとURLをコピーしました