WEBアプリをクラウドでデプロイ(Cloud Run + Streamlit or Flask)
WEBアプリを色々な方に使って貰うようにするには、クラウド等にアプリをデプロイする必要があります。
過去 AWSのサービスを使って、アプリのデプロイを試みてみましたが、Google CloudのCloud Runというサービスが、非常に良いなぁと思ったので、こちらを使ってやったことを備忘録として残します。
ちなみに、、PythonライブラリであるStreamlitをAWSでデプロイをしようとして、AWS AppRunnerというサービスを使ってみたのですが、、デプロイ完了、起動まではできるが、その後画面が、遷移せず(動かず)という感じで断念しました。
→調べてみると、、WebSocketが使えないから駄目だとか。コンテナ化をしてFargateを使ったらできるのかな?と思っています。
目次
Cloud Runについて
GCP Cloud Run(クラウド ラン)は、Google Cloud Platform(GCP)上で提供されているサーバーレスなコンテナ実行環境を構築できるサービスです。
"コンテナ"か、と思って最初は構えていたのですが、GCPのCloud Buildサービスと組み合し、Pythonコードをコンテナ化する作業もクラウド側で実行してもらえることには感動しました。(Dockerを使うのは過去苦労した経験があるので・・・WindowsのPCに入れたくない)
つまり、WEBアプリのPythonコードを書いて、(Docker fileは作るけど)、それをクラウドのサービスに渡すだけでデプロイできるのです。
実装
前準備
GCP CLIではデプロイができなかった件
詰まったところの紹介ですが、以下公式のチュートリアルに従って、進めたところ、デプロイ部分でエラーが出てしまいました。
https://cloud.google.com/run/docs/quickstarts/build-and-deploy/deploy-python-service?hl=ja

エラーの対処について、以下ブログ等の情報を探ると、リージョンをグローバル(非リージョン)にすれば解決するとのこと。
ただし、CLIではうまく設定が分からなかったので、今回はコンソールで実行しています。
↓参考になったブログ
zenn.dev
コード関係準備
コードや必要なファイルはすべてGithubにあげているので、詳細は割愛します。
構成は以下の通りで、ローカルで実行する時との差分はDockerfileが入る程度かなと思います。
github.com
│─ Dockerfile │─ requirements.txt └─ src/ └─ app.py (WEBアプリのコード)
Dockerfileだけ、詳細を載せておきます。
pip が無いとエラーが出たことがあったので、"RUN python -m ensurepip"という行を入れています。
FROM python:3.11.1 WORKDIR /app RUN python -m ensurepip RUN python -m pip install --upgrade pip COPY requirements.txt ./requirements.txt RUN pip install -r requirements.txt COPY . . EXPOSE 8080 CMD streamlit run --server.port 8080 src/app.py
動作確認
デプロイ後に吐き出されるURLにアクセスをすると、Streamlitのアプリが無事動いていることを確認しました。

デプロイしたアプリのコンテナイメージは、Artifact Registryに保存されています。
デプロイ後、使い終えたら、アプリは削除しましょうね!(無料枠はあるけど、、ちょっとお金かかりそうで怖いので)
その他詰まったところ
IAMのエラー
エラーで吐き出されたURLにアクセスして、許可をすることで解決
generic::permission_denied: Identity and Access Management (IAM) API has not been used in project ○○before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/iam.googleapis.com/overview?project=○○ then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
Flask HelloWorldもやってみた
上と準備することが殆ど同じなので、コードのみの紹介になりますが、Google公式のチュートリアルも無事こなすことができました。
github.com
ChatGPT My GPTsを作って気になったこと

個人のニーズに合わせてカスタマイズ可能なChatGPTであるGPTs(ジーピーティーズ)、かなり簡単に作成できるのですが、制御は結構難しい・・
色々と作成して気になったこと、気を付けるべきポイントについて、この記事でまとめます。
目次
自身で作成してみたGPTsについての紹介
とりあえず、自身で作ってみよう!ということで、作ってみました。
作成したMy GPTs君たちは以下です。画像を作らせたり、検索させたり、ファイルを読み込んで分析させたり等、それぞれが違うアプローチ(作業)になるように作ってみました。
これらGPTs は、ChatGPT Plus ユーザーならば誰でも無料で使うことができます。
| No. | GPTs Name | 概要 | URL |
| 1 | GPT use guide for JPN | ChatGPTの使い方、シナリオを考えて複数個を紹介します。 | [Link](https://chat.openai.com/g/g-4aQp2tI1D-gpt-use-guide-for-jpn) |
| 2 | Search MyGPTs for JPN | 日本人向けのMy GPTsを検索して紹介します。 | [Link](https://chat.openai.com/g/g-od9NiNtEy-search-mygpts-for-jpn) |
| 3 | Japanese Picture Book Creator for JPN | 赤ちゃん向けの絵本を作って、1ページごとにページをめくってもらえます | [Link](https://chat.openai.com/g/g-JpRZXiFJO-japanese-picture-book-creator-for-jpn) |
| 4 | Group Discussion Coach For JPN | グループディスカッションのシミュレーター。終了後に評価シートを使って採点します。 | [Link](https://chat.openai.com/g/g-WicD3sdXv-group-discussion-coach-for-jpn) |
| 5 | PinterestSearch for JPN | 日本語のPinterest検索結果を表形式でまとめます | [Link](https://chat.openai.com/g/g-2WH0qXcqG-pinterestsearch-for-jpn) |
| 6 | English Vocabulary Tutor For JPN | TOEICレベル(550点, 720点, 800点)に合わせて、英単語の理解度を確認するテストを出題します。 | [Link](https://chat.openai.com/g/g-YEpEGD9vw-english-vocabulary-tutor-for-jpn) |
| 7 | Prompt Reviser | プロンプトを整理修正してくれます。重複の削除や強調表現の追加など、入力されたプロンプトを修正して出力します。 | [Link](https://chat.openai.com/g/g-0K4EEaJwJ-prompt-reviser) |
| 8 | GIF Creator | ユーザーの要望に従った動作のGIF画像を作成します。 | [Link](https://chat.openai.com/g/g-kinaLWgLv-gif-creator) |
| 9 | DocuSummarize | 入力した(添付した)ドキュメントを要約して、要約結果をテキスト出力します。 | [Link](https://chat.openai.com/g/g-ED65z7m4v-docusummarize) |
| 10 | Thumbnail Creator | サムネイル用の画像を作成します。意図に沿った画像を作成するため、画像設定を細かく決めてから画像生成するしようにしています。 | [Link](https://chat.openai.com/g/g-UUsJyel6B-thumbnail-creator) |
| 11 | iPhone User Helper | Apple公式ページの情報を参照して、iPhone等のApple製品の困りごとに応えてくれます。 | [Link](https://chat.openai.com/g/g-3QnTaBOgF-iphone-user-helper) |
作ってみた感想
対話型で作れるのは良いが、プロンプトがごちゃごちゃする
以下画像が、GPTsのConfigureの画面であるが、プロンプトが入力される"Instructions"が、ユーザーの要望に従って修正される。
「修正してくれる」ことがありがたいのではあるが、、プロンプトを整理した後にごちゃごちゃにされてしまうのが困った

使用制限に結構引っかかる
chatGPT課金ユーザーにも使用制限があり、GPTsを作るためにchatGPTを使うことでも制限がかかってしまう・・・そんな
短時間で何度も修正をすると、結構引っかかってしまう印象。
GPTs作成で気を付けるべきと感じたこと
「GPTs作成で気を付けるべきと感じたこと」というよりは、「GPTに役割を与えるためのプロンプト作成で気を付けた方が良いこと」になるかもしれないが、感じたことをまとめる。
かなり主観が入っている為、ご注意ください。
明確で具体的なプロンプトとする
目的があやふやだと、やはり出力結果がぶれてしまいます。
「文脈」「前後関係」「事情」「背景」「状況」を細かく、100人が100人同じ答えを出せるように書くことが望ましかもしれません。
以下は例になります。
失敗例: 天気はどうですか? 成功例: 2024年1月6日の東京 赤坂の午後9時以降の天気予報は何ですか?
失敗例: 地球温暖化について教えて下さい。 成功例: 地球温暖化について、2023年に報告された研究結果を基に教えて下さい。
失敗例: 最低賃金について教えて。 成功例: 経済学の観点から、最低賃金の引き上げが労働市場に与える影響について、事実に基づいた分析を行い、結果をtextにまとめてください。
また、日本語でプロンプトを書くよりも英語でプロンプトを書いた方が、出力結果にぶれがないような・・・やはり英語で学習をしているから?
シンプルな言い回しに努める
複雑に書くと分かりにくいのか、どうも意図通りに動作しないときがありました。
シンプルで直接的な表現が良いのかもしれません。
失敗例: 経済的な均衡理論における供給と需要の相互作用に関する詳細な説明をしてください。 成功例: 経済学において、供給と需要はどのように働きますか?
作業フローの記載を入れる
スタートからゴールまでの1つ1つの作業を細かく記載することで、途中で動作が止まる等、意図しない動作がなくなりました。
【例】 1. @@@を検索して△△△の情報を取得する 2. 情報をスプレッドシートに記載する 3. スプレッドシートのダウンロードリンクを作成する
プロンプトを整理する
役割に関する情報、作業に関する情報、ユーザーの対応に関する情報など、項目毎にプロンプトを整理して記載した方が、意図しない動作が少なくなると感じました。
以下は赤ちゃん向けの絵本を作成するためのプロンプトであり、記載を分けることで安定した出力ができるようになりました。
【例】 [Objective & Goal] The objective is to generate Japanese picture books specifically designed for infants aged 0, 1, and 2 years. These picture books will have images presented in a 16:9 aspect ratio, accompanied by separate story text below each image. The goal is to create engaging, age-appropriate content that aligns with infants' developmental stages, avoiding complex language or concepts. The interaction will be entirely in Japanese, including narrations and user conversations. The tone and personality are yet to be defined, guiding the interaction style. [Flow] 1. Generate images and story text suitable for infants aged 0, 1, and 2 years. 2. Present each image along with its corresponding story text in a 16:9 aspect ratio. 3. Narrate the story text in Japanese. 4. Prompt the user to input "続きは" to move on to the next page. Continue the process, ensuring that each page of the picture book is engaging and appropriate for the target age group. Maintain a consistent and engaging narrative style throughout the picture book. [Adaptation Strategy] Culturally Sensitive and Inclusive Content: Be aware of cultural sensitivities and strive to include diverse elements in the content. This can involve using a variety of characters and settings that reflect different backgrounds and cultures, fostering inclusivity from an early age. Safety and Comfort: Always prioritize the safety and comfort of the infant audience in every aspect of the content. This includes avoiding any elements that could be potentially startling or uncomfortable for young children, such as loud noises or overly complex imagery.
プロンプトインジェクション対策をする
プロンプトが見られてしまう自体は、現状別に・・と思っていますが、不適切な回答をするのは、AIとしての役割設定がうまくできていないということになるので、避けたいなと思っています。
以下がかなり参考になりましたが、もう少しメカニズム的な解決の仕方が無いのかなぁと思う今日この頃です
GPTs を公開する際には、内部命令を聞き出すプロンプトインジェクション対策は必須です。
— FabyΔ (@FABYMETAL4) November 11, 2023
色々とやり方はありますが、Settingとして分けてInstructionに入れておくのが良いです。このように書くと、汎用エラーメッセージをBodyに書かれたキャラクターの言葉を使って返してくれます。… pic.twitter.com/ysCsLFHvRb
chat-gpt.school
プロンプトエンジニアリングに関して読んだ文献
プロンプト記載の原則についてまとめた文献
かなり詳しく書いているので、おススメです。
arxiv.org
プロンプトを自動的に改善するアルゴリズムについてが記載された文献
arxiv.org
まとめ
GPTsの作成が比較的容易であるものの、その制御が難しいというのは多くの人が感じているのではないでしょうか?
プロンプトの最適化については、研究もされているようなので、目的に沿ったプロンプトが自動で作れる時代もすぐに来るのではないかな‥と、私も勉強しつつ期待をしています。
できるだけPythonだけでWEBアプリを作る④(Streamlit, Gradio, Dash, Reflex)
「できるだけPythonだけでWEBアプリを作る第4弾」として複数のPython ライブラリの比較を行っていきたいと思います。
各ライブラリの使い方はそれぞれまとめている方がいらっしゃるので、比較結果についてのみをまとめます。
第1弾, 第2弾, 第3弾記事は以下の通りです。
できるだけPythonだけでWEBアプリを作る①(Python + AWS + HTML/Java) - まるっとワーク
できるだけPythonだけでWEBアプリを作る②(Python flask/zappa + AWS) - まるっとワーク
できるだけPythonだけでWEBアプリを作る③(Python Streamlit) - まるっとワーク
目次
PythonでWEBアプリが作れるライブラリについて
丁寧にまとめて下さっている方がいらっしゃいました。(感謝です)
まさかこんなにあるなんて・・・最近まで全然知りませんでした。
これらはPyhtonのみでWebアプリが作れるライブラリであり、html, cssを書く必要があるFlask, Djangoは入っていませんね。
zenn.dev
ご参考↓
- FlaskFlaskへようこそ — Flask Documentation (2.2.x)
- DjangoThe web framework for perfectionists with deadlines | Django
今回はまとめて下さっているライブラリの中で、GitHub star数が相対的に多めである4つ(Streamlit, Gradio, Dash, Reflex)の比較を行いました。
ライブラリの比較結果
比較検討のために作成したコードは、以下 Githubで公開をしています。
github.com
結果はかなり所感が入っているので、ご注意下さい。
まとめると、以下の通りの使い分けになると思っています。
とりあえずシンプルなUIでよい!素早くアプリを出したい!:Streamlit, Gradio
UIのカスタマイズをしたい!オリジナリティを出す必要がある!:Dash, Reflex
比較結果を表でまとめています。
| 観点 | 説明 | Streamlit | Gradio | Dash | Reflex |
| 用途 | フレームワークの主な利用シナリオ。 | データ分析や機械学習モデルの可視化、プロトタイピング、簡単なWebアプリケーション | 機械学習モデルのデプロイ、モデルの入力と出力の簡単な設定 | データダッシュボード、高度な可視化、企業向けアプリケーション | リアルタイムアプリケーション、コラボレーション、高度な対話型アプリケーション |
| インタラクティブ性 | ユーザーとの対話性能力(反応性)。 | 高い | 中程度 | 高い | 高い |
| UIカスタマイズ | ユーザーインターフェースの外観と振る舞いを調整する能力。 | 限定的(デフォルトのウィジェットをカスタマイズ可能) | 限定的(デフォルトのウィジェットをカスタマイズ可能 | カスタマイズ可能(HTML / CSS / JavaScriptを使用してUIをカスタマイズ) | カスタマイズ可能(HTML / CSS / JavaScriptを使用してUIをカスタマイズ) |
| シンプルさ | 使用の簡便さ。 | 高い(シンプルなコードでアプリケーションを構築できる) | 高い(簡潔なAPIでモデルデプロイが可能) | 中程度(高度なカスタマイズが必要な場合がある) | 中程度(カスタマイズが必要な場合がある) |
| コミュニティサポート | オンラインコミュニティのサポートとリソースの利用可能性。 | 高い | 中程度 | 高い | 中程度 |
| ドキュメンテーション | フレームワークの公式ドキュメントとチュートリアルの品質と充実度。 | 良い | 良い | 良い | 良い |
それぞれ特徴がありますが、シンプルなUIで良いからとりあえずアプリを出したいということであれば、Streamlit, Gradioという選択になり、後はカスタマイズの希望に応じて、Dash, Reflex, Flask, Django等・・・という感じかなと
GUI比較(ご参考程度ですが)
同じ目的のアプリを作成した時の見た目の差の比較です。
ただし、見た目についての調整はほとんどしておらず、、、最初からある程度デザイン性があるStreamlit, Gradio, Reflexの方が見た目は良いかもですね。
Streamlit

Gradio

Dash

Reflex

まとめ
簡単ですが、以上です。
PythonだけでWEBアプリを作ることができるライブラリがこんなにあるなんて、知りませんでした。
これだけあると、どれを使えば良いのか分からなくなってくるので、特徴などを比較しながら適切に選択していきたいなと思います。
できるだけPythonだけでWEBアプリを作る③(Python Streamlit)
できるだけPythonだけで・・・と進めてきましたが、画期的なフレームワークの存在最近まで知らず・・・
「できるだけPythonだけでWEBアプリを作る第3弾」としてPython ライブラリのStreamlitとStreamlit Cloudを使用した方法をまとめていきます。
この方法は他の方も多くまとめているので、備忘録として簡単なフローだけを示しています。
第1弾, 第2弾記事は以下の通り。
dango-study.hatenablog.jp
dango-study.hatenablog.jp
目次
Streamlitでできること
- PythonだけでWebアプリが作れる
- フロントエンド(UI)の開発スキルが無くても簡単にUIを作ることができる
- 作成したアプリをStreamlit Cloudに(無料)デプロイできる
デプロイ方法は、Streamlit Cloud意外に色々ありますが、Streamlit Cloudではセキュリティーが担保できないので、機密データを処理したい時などは、ローカルサーバーやセキュリティ保護されたサーバーでの運用が望ましいです。
Streamlitを用いたWEBアプリ作成
ライブラリのインストール
以下を実行して必要なライブラリをインストールします。
$ pip install streamlit
アプリ構成
基本的な構成は、「処理等を記載した.pyファイル」「必要ライブラリとバージョン情報を記載したrequirements.txt」の2つの構成になります。
@@@.py requirements.txt
コード作成
任意の名前の.pyファイルには、最低限streamlitのモジュールインポート記載をしておけば、実行確認ができます。
$ import streamlit as st
ローカルで実行/確認
コンソールで以下コードを実行して、
エラーが出なければ「http://localhost:8555」にアクセスして実行結果を確認できます。
$ streamlit run @@@.py
→.pyファイルの記載がstreamlitのモジュールインポート記載のみであれば、白紙のページが表示されるだけです。
コードの書き方などは、ありがたいことに・・先人の皆様のページで詳しく説明されているので割愛します。
- 参考コード(私)
https://github.com/Mya-mori/streamlit_csv/blob/main/test.py
- 参考ページ①
Streamlit入門+応用 ~ データ分析Webアプリを爆速で開発する - Qiita
- 参考ページ②
PythonでDX「Streamlit」簡単ダッシュボード(前編) | コードファミリー
- 参考ページ③
アプリをStreamlit Cloudにデプロイ
Streamlit Cloudにサインイン
https://share.streamlit.io/signup
Googleアカウント、Githubアカウント、メールアドレスのいずれかを利用してアカウントを作成します。
GithubリポジトリにWebアプリのソースコードをアップロード
作成したソースコードをGithubにアップロードします
アップロードするファイルは、最低限以下構成のファイル
@@@.py requirements.txt
requirements.txtは以下コードを実行することで、出力することができる。
必要最低限の情報でないとエラーが出る場合がある為、余計なライブラリ情報は削除する。(ライブラリが多すぎることが原因のエラーが出ました)
$pip freeze > requirements.txt
アプリのデプロイ
Streamlit CloudにStreamlitで作成したWebアプリをデプロイします。
Streamlitにログインして、"New app"と記載されたボタンを押す。
以下の通り設定をして、デプロイボタンを押すことで、デプロイされる。

何か問題があれば、エラーが吐き出されます。
動作確認
デプロイされるアプリURLにアクセスすると、デプロイしたアプリの動作確認ができます。
簡単にデプロイして利用できるのがStereamlit Cloudを用いた利点ですね。
参考までに私が作成したアプリは以下↓
https://webapplicationsample1-rwb1ydqchif.streamlit.app/
【システム同定シミュレーターアプリ】
csvデータを入力して、線形システムのシステム同定シミュレーションができます。
Githubアイコンを押すとコードも参照頂けます。

Amazon API Gatewayを理解する
Amazon API Gatewayは、AWS Lambdaと組み合わせて使用することが一般的で、サーバーレスアーキテクチャを構築するための基盤として利用されています。WEBアプリを色々と作ろうとしている段階で、このAmazon API Gatewayを避けて通ることはないと思うので、この機能を理解して、使う時に引っかかるであろうポイントをまとめようと思います。
目次
- Application Programming Interface(API)とは
- Amazon API Gatewayとは
- Amazon API Gateway(REST API)を使ってみる
- まとめ
Application Programming Interface(API)とは
分かっているようで分かっていない気がするので改めて、確認します。
APIは、アプリケーションやサービスとやり取りをするための標準的なインターフェースのことです。プログラムが他のプログラムやサービスと情報を共有するために使用され、APIを使用することで、異なるプログラムやサービス間でデータを共有することができ、相互運用性や拡張性を向上させることができます。
APIの種類
APIは、異なる種類のアプリケーションやサービスで使用されます。Web API、REST API、SOAP API等の多様な種類のAPIがあります。APIは、標準化された規格に従って設計されることが多く、APIの利用者がアクセスしやすいように、ドキュメント化され、公開されることが一般的です。
Web API
APIというとWeb APIを指すことが大部分で、Webブラウザ経由でアクセスするAPIのことを指します。
HTTPプロトコルを使用してリクエストを送信し、JSON、XML、またはプレーンテキストの形式で応答を受け取ることができます。
APIというと「アプリケーションやサービスとやり取りをするための標準的なインターフェース」のことなので、ローカル環境でもこのワードに該当するサービスを作れば、APIと呼ぶものになるので、単純なAPIとWeb APIだとワードの範囲が違うんだなと理解しました。
Representational State Transfer(REST API)
Web APIと並列で考えてよいのかが怪しいのですが、RESTアーキテクチャスタイルであるWEB APIを指します。
- アドレス指定可能なURIで公開されていること
- インターフェース(HTTPメソッドの利用)の統一がされていること
- ステートレスであること
- 処理結果がHTTPステータスコードで通知されること
ステートレスとは、システムが現在の状態を表すデータなどを保持せず、入力の内容によってのみ出力が決定される方式。同じ入力に対する出力は常に同じになる。
これらの原則に則ったWebサービスをRESTfulなサービスといいます。
Web APIと同じように、HTTPプロトコルを使用して通信してデータをやり取りしますが、REST APIは、リソースの操作を表現するためにHTTPメソッド(後述: POST, GET, PUTなど)を使用する点が、Web APIとは異なるようです。
詳細は以下に載っていました。
RESTful API とは? - RESTful API の説明 - AWS
Amazon API Gatewayとは
Amazon API GatewayはAWS内でのAPIの作成および諸々の管理を行えるサービスのこと。基本これ単体で使うのではなく、AWS Lambda等とつなげて、リクエストに対して結果を返すサービスを作るの目的に使われます。
ちなみに、公式だと以下の説明が載っています。
Amazon API Gateway(規模に応じた API の作成、維持、保護)| AWS
フルマネージド型サービスの Amazon API Gateway を利用すれば、デベロッパーは規模にかかわらず簡単に API の作成、公開、保守、モニタリング、保護を行えます。API は、アプリケーションがバックエンドサービスからのデータ、ビジネスロジック、機能にアクセスするための「フロントドア」として機能します。API Gateway を使用すれば、リアルタイム双方向通信アプリケーションを実現する RESTful API および WebSocket API を作成することができます。API Gateway は、コンテナ化されたサーバーレスのワークロードやウェブアプリケーションをサポートします。
APIのタイプ
APIタイプは以下から選択ができ、それぞれの特徴は以下の通りです。
| HTTP API | Web Socket API | REST API |
| HTTP APIは、HTTPリクエスト/レスポンスを使用してRESTfulなWebサービスを作成するためのAPI。機能を絞ることで、REST APIよりも低レイテンシかつ低コスト(コスト最適化)を実現できるようです。 | WebSocket APIは、リアルタイムなWebアプリケーションを構築するためのAPIです。WebSocket APIは、クライアントとサーバー間で長時間の接続を確立することができ、データの双方向通信を可能にします。WebSocket APIは、リアルタイムな情報の伝達が必要なチャットアプリ、オンラインゲーム、監視システムなどに適しています。 | REST APIは、HTTPプロトコルに基づいたAPIで、HTTP APIの違いは色々とあるが、HTTPメソッド(GET、POST、PUT、DELETEなど)を使用してリソースの操作を実行します点が大きな差かなと思っています。 |
目的に応じて選択が必要です。私の場合、JSON形式でデータ送信したいので、HTTP API/REST APIから選択ができ、HTTPメソッドを用いて簡単に開発したいならばREST APIを選ぶ感じかなと思います。
REST APIのHTTPメソッド
REST APIタイプは以下から選択ができ、それぞれの特徴は以下の通りです。
| メソッド | 説明 |
| GET | Webページや画像などリソースを取得するために使用する |
| POST | リソースを送信するために使用する |
| PUT | リソースの更新や作成をするために使用する |
| DELETE | リソースを削除するために使用する |
| PATCH | リソースの一部を更新するために使用する |
| HEAD | リソースのヘッダ情報のみを取得するために使用する |
| OPTIONS | リソースに対して許可されているHTTPメソッドの一覧を取得するために使用する |
| ANY | 一般的なHTTPメソッドではなくAWS API Gatewayで使われる概念で、すべてのHTTPメソッドを扱うことができる |
なんとなくは分かりましたが・・扱えるようになるため、実際に使ってみます。
Amazon API Gateway(REST API)を使ってみる
実際に使う前に、理解した範囲で知識をまとめます。
統合リクエスト設定
統合リクエストは、API Gatewayがバックエンドに送るHTTPリクエストで、クライアントから送信されたリクエストデータを渡して、必要に応じて変更します。今回はAWS Lambdaに渡すことを前提とします。設定方法は、以下にまとめられています。
API Gateway で Lambda プロキシ統合を設定する - Amazon API Gateway

メソッドリクエストから、クエリパラメータを受け取って、Lambdaに渡したい場合は、クエリパラメータを登録する必要がある。
統合リクエストから、マッピングテンプレートを登録する。今回は、JSON形式で受け取りたいので、以下の通り3つのパラメータ(my_param1, my_param2, my_param3)をもらう設定を追加。

クエリパラメータの設定方法
URLにクエリパラメータを付与する場合は、ルールに従って以下のように書く必要がある。
- パラメータはURLの後に「?」をつけて記載
- 各パラメータは「名前=値」で記載
- 複数パラメータを付与する場合は、「&」で分ける
書き方(3つのパラメータmy_param1, my_param2, my_param3を付与するケース) URL(https://*******)?my_param1=1&my_param2=2&my_param3=3
URLはデプロイしたHTTPメソッドのエンドポイントに設定する。

エンドポイントとは、クライアントとAPIとが通信をするための窓口であり、APIのリクエスト先になる。
プロキシ統合設定について
HTTPメソッドを作成する際に、「Lambdaプロキシ統合の使用」というチェックボックスがあり、この役割を説明する。

詳しくは以下にまとめられていたが、以下のように理解しました。
| 統合 | 非統合 |
| API GatewayはLambda関数に直接接続され、API GatewayがHTTPリクエストをLambda関数に直接ルーティングするため、API GatewayとLambda関数の間の通信が最適化されます。Lambdaから返される値のフォーマットが決まっている | API GatewayとLambdaは独立して動作します。API GatewayはHTTPリクエストを受け取り、Lambda関数を呼び出すためにLambda関数のARN(Amazonリソース名)を設定します。Lambda関数はリクエストを処理し、結果をAPI Gatewayに返します。API GatewayはLambda関数からのレスポンスを返信し、クライアントに返します。Lambdaから返される値にフォーマットはなくそのままの値が返される |
動作を確認
環境設定に関しては、以下の通りで、Lambda関数は「statusCode」「body("Hello World")」「event(リクエスト)」を返す関数であり、API Gateway側ではANYメソッドがリクエストデータをそのままLambda関数に渡す設定になっている。プロキシ統合は非統合の設定。
Lambda関数
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': json.dumps('Hello World'),
'event': json.dumps(event)
}
GET
ローカルにて以下コードを実行して、出力結果を確認した。
import requests
import json
pattern = "GET"
URL = r"エンドポイント"
payload = {'my_param1':"1", 'my_param2':2, 'my_param3':3}
if pattern == "GET":
print("GET")
response = requests.get(URL, params=payload)
data = response.json()
print ("json", json.dumps(data, indent=4))
print("status_code",response.status_code) # ステータスコード取得
print("HTML_text", response.text) # HTMLを文字列で取得
print("binary", response.content) # HTMLをバイナリ形式で取得
出力結果は以下の通りで、eventは何もない状態で返されている。クエリとして、3つのパラメータを送付しているが、クエリパラメータを受け取る設定にしていないため、こういった現象が起きている。
GET
json {
"statusCode": 200,
"body": "\"Hello World\"",
"event": "{}"
}
status_code 200
HTML_text {"statusCode": 200, "body": "\"Hello World\"", "event": "{}"}
binary b'{"statusCode": 200, "body": "\\"Hello World\\"", "event": "{}"}'
上記で説明した通り、統合リクエスト設定にてマッピングテンプレートを設定すると、以下の通り、受け取ることができて、Lambda関数に適切に渡されていることが確認できる。
# マッピングテンプレート設定後
GET
json {
"statusCode": 200,
"event": "{\"my_param1\": \"1\", \"my_param2\": \"2\", \"my_param3\": \"3\"}"
}
status_code 200
HTML_text {"statusCode": 200, "body": "\"Hello World\"", "event": "{\"my_param1\": \"1\", \"my_param2\": \"2\", \"my_param3\": \"3\"}"}
binary b'{"statusCode": 200, "body": "\\"Hello World\\"", "event": "{\\"my_param1\\": \\"1\\", \\"my_param2\\": \\"2\\", \\"my_param3\\": \\"3\\"}"}'
POST
ローカルにて以下コードを実行して、出力結果を確認した。
import requests
import json
pattern = "POST"
URL = r"エンドポイント"
payload = {'my_param1':"1", 'my_param2':2, 'my_param3':3}
if pattern == "POST":
print("POST")
response = requests.post(URL, data=json.dumps(payload))
data = response.json()
print ("json", json.dumps(data, indent=4))
print("status_code",response.status_code) # ステータスコード取得
print("HTML_text", response.text) # HTMLを文字列で取得
print("binary", response.content) # HTMLをバイナリ形式で取得
出力結果は以下の通りで、POSTメソッドの場合はマッピングテンプレートの設定をせずパラメータを送付して、Lambda関数に適切に渡されていることが確認できる。
POST
json {
"statusCode": 200,
"body": "\"Hello World\"",
"event": "{\"my_param1\": \"1\", \"my_param2\": 2, \"my_param3\": 3}"
}
status_code 200
HTML_text {"statusCode": 200, "body": "\"Hello World\"", "event": "{\"my_param1\": \"1\", \"my_param2\": 2, \"my_param3\": 3}"}
binary b'{"statusCode": 200, "body": "\\"Hello World\\"", "event": "{\\"my_param1\\": \\"1\\", \\"my_param2\\": 2, \\"my_param3\\": 3}"}'
HEAD
ローカルにて以下コードを実行して、出力結果を確認した。
import requests
import json
pattern = "HEAD"
URL = r"エンドポイント"
payload = {'my_param1':"1", 'my_param2':2, 'my_param3':3}
if pattern == "HEAD":
print("HEAD")
response = requests.head(URL)
#data = response.json()
#print ("json", json.dumps(data, indent=4))
print(response)
print("status_code",response.status_code) # ステータスコード取得
print("HTML_text", response.text) # HTMLを文字列で取得
print("binary", response.content) # HTMLをバイナリ形式で取得
出力結果は以下の通りで、HEADメソッドでは、レスポンスボディを返さないため、 response.content などでレスポンスボディを取得することはできません。HEADメソッドは、サーバーからのレスポンスボディを取得することがないため、GETメソッドよりも高速にリソースの存在確認を行うことができます。
HEAD <Response [200]> status_code 200 HTML_text binary b''
他のメソッドに関しては、実際にWEBアプリを作った際に色々と確認しようと思う。一旦はここまで。
まとめ
APIに関する言葉の意味からスタートして、色々と勉強になりましたが、まだまだ奥が深く知識を浅いことを痛感しました。
HTTPメソッドは、Webアプリケーション開発において非常に重要な役割を果たしているので、使い方を勉強して、WEBアプリケーション作りに活かしていきたいと思います。
オリジン間リソース共有 (CORS)設定について
WEBアプリ(以下)を作成した時に、ブラウザのCORSというポリシーによるエラーが発生し、エラー解除に苦戦をしたので、COR2の関連知識についてまとめていきます。
dango-study.hatenablog.jp
目次
CORSのエラーについて
色々調べていくと、以下のように書かれていました。
Cross-Origin Resource Sharing(CORS: オリジン間リソース共有)はサーバーが同一オリジンポリシーを緩和できる標準で、異なるオリジンからのリクエストに対してサーバー側が許可を与えなかった場合に発生するエラーです。・・ちょっとまだ完全に理解ができていないので、用語などを調べながら理解を進めようと思います。
Origin(オリジン)とは
オリジンとは、ウェブコンテンツにアクセスされるために使われるURLの中のスキーム(プロトコル)とホスト(ドメイン)、ポート番号を組みわせたもの
| 項目 | 詳細 | 備考 |
| オリジン | https://www.example.com:8080 | 以下3つを組み合わせたもの |
| スキーム(プロトコル) | https:// かhttp:// | 2つの違いは通信が暗号化されているかどうかで、httpsが暗号化されている |
| ホスト(ドメイン) | www.example.com等 | いわゆるインターネット上の住所で自身で好きなドメイン名にしたりする |
| ポート | 8080等 | ポートに関しては、httpsが443番でhttpが80番が既定番号となり、既定番号ら省略ができるので、ポート番号が付けられないこともある |
同一オリジンポリシーとは
上記の通りプロトコル+ドメイン+ポートが同一の場合は同一オリジンとみなして保護をして、異なるオリジンからのアクセスを制限する仕組みのこと。
主な原則として3つがあります。
- ドキュメントオブジェクトモデル(Document ObjectModel:DOM)は、同じオリジンのスクリプトにしかアクセスできない。
DOMは、HTMLのように文章の構造を示すものやJavaScriptのように操作を示すもの
- XMLHttpRequest(XHR)オブジェクトを使用して、異なるオリジンからデータを取得することはできない。
XHRはサーバーとのデータ通信に使い、ウェブページの更新などを行う目的で使用される
- クッキー(HTTP Cookie)は、同じオリジンのWebページのみに送信されます。
クッキーは、クライアントのWebブラウザとWebサーバとの間で、状態を維持・管理する仕組みで、その通信の際にクライアントのWebブラウザに保存された情報。その後のリクエストとともにサーバーに送信され、ログイン状態の維持などの役割で使用される。
つまり、あるオリジン(Webサーバー)の更新は、同一オリジン(内包したプログラム)やクライアント側でのみ行われ、異なるオリジンからの操作/情報取得等を制限する仕組みと思われます。
CORSの制限対象
同一オリジンポリシーの考え方を踏まえるとCORS(オリジン間リソース共有)の考え方は、なんとなく理解ができ、異なるオリジンからのHTTPリクエストが制限対象となります。
今回のエラーケースでいうと、アーキテクチャが以下の通りで、CORSのエラーが初期段階で出ていました。
恐らく、JavaScriptからXMLHttpRequestでAPI Gatewayからデータを取得しようとしていた為、CORSエラーが発生したと思われます。
(追記)アーキテクチャ

CORSエラーの解決方法
全て網羅できていなさそうですが、対処方法を以下にまとめます。
サーバー側で必要なHTTPレスポンスヘッダーを設定する
この方法が、私がWEBアプリを作成時に対応した方法です。サーバー側で、必要なHTTPレスポンスヘッダーを設定することで、異なるオリジンからのリクエストを許可することができます。具体的には、Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-HeadersなどのHTTPレスポンスヘッダーを設定することが必要です。これらのヘッダーは、CORSエラーを回避するために必要な情報を提供するために使用されます。
詳細は、以下に記載されています。
API Gateway からの CORS エラーのトラブルシューティング | AWS re:Post
まとめ
CORSエラーは、異なるオリジン間での通信が必要な場合に発生する問題です。CORSエラーを回避するためには、サーバー側で必要なHTTPレスポンスヘッダーを設定することや、プロキシを使用することができます。開発をしていて障害になるのは、こういった制限ルールなどが多いので、こういった知識をひとつづつ学んでいかないといけないなと思いました。
できるだけPythonだけでWEBアプリを作る②(Python flask/zappa + AWS)
「できるだけPythonだけでWEBアプリを作る第2弾」は、第1弾から少し構成を変えて、拡張性を考慮したやり方で作っていきます。
今回はZappaというフレームワークを使っており、様々なAWSの設定が簡単になる反面、裏で何をやっているのかが完全に理解できなかったので、とりあえずシステムの作成フローをこのページにまとめて、掘り下げて分かったことは追ってまとめようと思います。
第1弾(前回)記事は以下の通り。
dango-study.hatenablog.jp
目次
WEBアプリをクラウド(AWS)で作成するためのアプローチ
前回はとりあえずWEBアプリを作る!という感じだったので、特に気にしていなかったのですが、本来はどんなアプローチ方法がとれるのかを踏まえた上で適切に選択すべきなので、ここで一旦まとめようと思います。調べた限り、(AWS)サービスの組み合わせ方で言うと、かなり色々な方法があったので、主観ですが大きなくくりでまとめていきます。
FaaS(Function as a Service)サービスを使うアプローチ[AWS Lambda + API Gateway等]
FaaSとは「Function as a Service」の略で、サーバーレスでアプリケーション開発ができるクラウドサービスのこと。
AWS Lambda等のインスタンスを返してサーバーを任意のタイミングで利用するパターンで、サーバー管理はサービスベンダーが行うため、その費用などを抑えることができる。
CaaS(Container as a Service)サービスを使うケース[ECS + Fargate等]
CaaSとは「Container as a Service」の略で、コンテナ化をすることで環境に依存しない開発ができるクラウドサービスのこと。
CaaSサービスであるECSはAmazon EC2インスタンスを用いてDockerコンテナ管理することができるが、Amazon EC2のような仮想サーバーの管理は不要なアーキテクチャを構築できる。
IaaS(Infrastructure as a Service
IaaSとは「Infrastructure as a Service」の略で、サーバやストレージなどのインフラを提供するサービスのこと。
仮想サーバーを使用できることから開発自由度は上がるが、サーバー管理が必要であること(作業が増える)、コストが他と比較して高い。
LambdaとECS、EC2を様々な観点で比較結果は以下が詳しくまとめられていました。感謝です
EC2 vs ECS vs Lambda:APIのサービングの観点から | Hakky Handbook
つまり、使い分けは、以下の通りと理解しました。
現時点ではLambdaでできる範囲で問題がなさそうなので、コストを重視してLambdaを使う構成で対応します。
| おすすめサービス | |
| 開発における制限(実行時間やライブラリ利用可否)を重視する場合 | ECSかEC2 |
| コストを重視する場合 | Lambda |
アーキテクチャについて
(おさらい)前回作ったアプリのアーキテクチャ
以下画像のような構成で、Amazon S3に保存したHTMLファイルにアクセス頂き、HTMLページのボタンを押すと、JavaScriptからAmazon API GatewayにHTTPリクエストが送信され、 AWS Lambdaを動かして、計算した結果をJavaScriptでHTMLに上書きするといった動きをします。

今回作るアプリのアーキテクチャ
ここが完全に理解できていないので、現状記載ができない(後述のZappaが理解できていない)。なんで、前回と同じ構成で作らないのか?という疑問が出てくるが、今回はFlaskというPythonのWebアプリケーションフレームワークで作成したWebアプリケーションをAWSでデプロイしたいから、が理由になります。今後も想定すると、1からすべて作る前回の方法よりもフレームワークを使った方がよさそうだなと・・・また、今回はFlaskのWEBアプリケーションをAWSにデプロイするのに、Zappaというフレームワークも使っています。ZappaはAWS LambdaとAPI Gatewayを使って、FlaskやDjangoなどのPython WEBアプリケーションをデプロイすることができます。AWS上の設定などを自動で設定して、構成してくれるのが便利なようです。このような理由から、こちらを使いました。
AWS マネジメントコンソールのAWS Lambda画面では、以下のような画面になっていますが、全体の構成としては、AWS Lambda/EventBridge/API Gatewayの構成になっている?この辺りから怪しいのですが、アクセスするURL(WEBアプリ)はAPI Gatewayで作成するHTTPエンドポイントで、HTTPリクエストをAWS Lambda関数に転送して実行していると思っています。ただ、EventBridgeでAWS Lambdaを定期実行する必要があるのでしょうか?Zappaを使うと、インターフェースの設定を勝手にやってくれるのが便利なのですが、どう設定をしたのかを勉強したい人にとってはつらいですね・・・

分かっていない部分も多いですが、、じゃんけんゲームのアプリを作っていきます。
ちなみに、その他の同じような構成(FaaSサービスを使うケース)例として、以下を紹介しておきます。
AWS での開始方法 | AWS Lambda、Amazon API Gateway、AWS Amplify、Amazon DynamoDB、および Amazon Cognito を使用してサーバーレスウェブアプリケーションを構築する
開発環境
[ローカル]
・Microsoft Windows 10 Pro
・Python 3.9.0
[AWS]
・AWS Lambda ランタイム python3.9
・AWS API Gateway
・Amazon S3
開発手順
- 前準備
- アプリ作成(コード作成)
- コード実行(動作確認)
実装
前準備
AWS CLIを使えるようにする(省略)
ZappaではAWS CLIを使用する為、使えるようにしておく必要がある。
こちらは色々なページで記載があるので省略します。
AWS CLIの設定(以下)をするところまで対応します。
$ aws configure
仮想環境の導入
Zappaを使うには仮想環境の用意が必須となる為、その手順を説明します。
手順は以下の通りコマンドとともに示します。
1. 仮想環境を作る venvモジュールを使用する。pyenvモジュールを使うケースも多いみたいであるが、Windows OSだと問題がありそうなのでこっちを使う(私はpythonバージョン3.9.0が入っていたので、Python 3.9.0の仮想環境ができる) $ python -m venv <好きなファイル名> $ cd <上記コマンドで作成したファイルパス> 2. 仮想環境に入る $ Scripts\activate
必要モジュールのインストール
Zappaでは仮想環境の用意が必須で、アプリを構成するコード類と合わせて仮想環境のモジュールもzip化してAWS Lambdaにアップロードするので、不要なモジュールを入れすぎても負荷を掛けそうなので、最低限のモジュールをインストールする。
ここからの作業は仮想環境に入った状態で対応します。
$ pip install flask $ pip install zappa 私の環境では、この後のZappaモジュール実行時にurlib3に関するエラーが出た為(バージョン指定)、エラーに従って以下の通り対応した。 $pip uninstall urllib3 $pip install "urllib3>=1.25.4,<1.27"
アプリ作成(コード作成)
やっとアプリの作成に移り、じゃんけんゲームのアプリを作っていきます。
htmlは2種類、初期画面/じゃんけん結果画面の2つであり、コードを含めたもろもろの構成は以下の通りです。
<flask_sample> ├ <templates> │ ├ home.html 初期画面 │ └ result.html じゃんけん結果画面 └ __init__.py my_app4.py アプリで最初に実行されるファイル requirements.txt 必要モジュールなどを記載したtxt zappa_settings.json zappa設定ファイル
WEBページの作成
初期画面/じゃんけん結果画面は以下のコードで構成しています。
home.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>じゃんけんゲーム</title>
<style>
body {
font-family: Arial, sans-serif;
text-align: center;
}
h1 {
font-size: 32px;
}
form {
margin-top: 32px;
display: flex;
flex-direction: column;
align-items: center;
}
form label {
font-size: 20px;
margin-bottom: 8px;
}
form input[type="radio"] {
margin-right: 8px;
}
form button {
margin-top: 16px;
font-size: 24px;
padding: 8px 16px;
border: none;
background-color: #1E90FF;
color: #FFFFFF;
border-radius: 8px;
cursor: pointer;
}
form button:hover {
background-color: #4169E1;
}
</style>
</head>
<body>
<h1>じゃんけんゲーム</h1>
<form action="{{ url_for('play') }}" method="POST">
<label><input type="radio" name="choice" value="グー" required>グー</label>
<label><input type="radio" name="choice" value="チョキ">チョキ</label>
<label><input type="radio" name="choice" value="パー">パー</label>
<button type="submit">決定</button>
</form>
</body>
</html>ハマったところとして、画面遷移が最初はうまくいきませんでした・・・("forbidden 403"が表示)
該当箇所は、
pythonコードについて
pythonコードは2ファイル(__init__.pyだけの1ファイルだけでも良かったが・・)に分かれていて、最初にmy_app4.pyが実行される設定にしています。
コードは以下の通りです
my_app4.py
#----------
#Flaskアプリケーション実行ファイル
#----------
from flask_sample import app
if __name__ == "__main__":
app.run()__init__.pyを実行するだけの役割ですね
#----------
#Flaskアプリケーション立ち上げ後に呼び出されるファイル
#my_app4.py→__init__.py
#----------
#Flaskとrender_template(HTMLを表示させるための関数)をインポート
from flask import Flask,render_template, request, url_for
import random
#Flaskオブジェクトの生成
app = Flask(__name__)
#「/」へアクセスがあった場合に、「home.html」を返す
@app.route("/")
def home():
return render_template("home.html")
@app.route("/result", methods=["POST"])
def play():
# ユーザーの選択を取得
print("def_play_start")
user_choice = request.form["choice"]
# コンピュータの選択をランダムに決定
choices = ["グー", "チョキ", "パー"]
computer_choice = random.choice(choices)
print("chiices", choices)
# 勝敗を判定
result = ""
if user_choice == computer_choice:
result = "引き分け"
elif (user_choice == "グー" and computer_choice == "チョキ") or (user_choice == "チョキ" and computer_choice == "パー") \
or (user_choice == "パー" and computer_choice == "グー"):
result = "勝ち"
else:
result = "負け"
# 結果を表示
return render_template("result.html", user_choice=user_choice, computer_choice=computer_choice, result=result)コチラのコードがメインとなり、初期アクセスでは関数: homeが実行され、後述のhome.htmlが表示されます。
コード実行(動作確認)
アプリのデプロイ
作ったアプリは、zappa_settings.jsonファイルがあるディレクトに移動して、以下コードでAWSにデプロイします。
デプロイする $ zappa deploy default ←defaultは自分が設定した値 一度デプロイをして、アップデートをしたい場合は以下を実行 $ zappa update default ←defaultは自分が設定した値 アプリケーションを削除 $ zappa undeploy default ←defaultは自分が設定した値 デプロイ時などのエラーを見たい場合 $ zappa tail
デプロイが完了すると、以下文字が出力され、URLが吐き出される。
Your updated Zappa deployment is live!: https:/*******************
動作確認
吐き出されたURLにアクセスする。

決定をクリックする

どこからでもアクセスができるURLでじゃんけんゲームができるようになりました。


