20220110のdockerに関する記事は12件です。

OpenFaaSでmydns.jpのIPアドレス更新を行うfunctionを作成する

前提 OpenFaaS環境の構築は終わっているものとする faas-cliでfunctionイメージの雛形を作成する faas-cli template pull faas-cli new mydns-update --lang dockerfile Folder: mydns-update created. ___ _____ ____ / _ \ _ __ ___ _ __ | ___|_ _ __ _/ ___| | | | | '_ \ / _ \ '_ \| |_ / _` |/ _` \___ \ | |_| | |_) | __/ | | | _| (_| | (_| |___) | \___/| .__/ \___|_| |_|_| \__,_|\__,_|____/ |_| Function created in folder: mydns-update Stack file written: mydns-update.yml 認証情報用Secret作成 公式ではfaas-cli経由で作成とあるが、gatewayやloginを指定する必要があり煩雑なためkubectlを使用して作成 kubectl create secret generic mydns-username --from-literal mydns-username="(username)" -n openfaas-fn kubectl create secret generic mydns-password --from-literal mydns-password="(password)" -n openfaas-fn コマンド作成 実行するdocker imageはalpineなのでbashはないので、ashを指定する。 /mydns-update/start.sh #!/bin/ash USER=`cat /var/openfaas/secrets/mydns-username` PASS=`cat /var/openfaas/secrets/mydns-password` wget -O- "http://$USER:$PASS@www.mydns.jp/login.html" Dockerfile編集 mydns-update/Dockerfile FROM ghcr.io/openfaas/classic-watchdog:0.2.0 as watchdog FROM alpine:3.12 RUN mkdir -p /home/app COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog RUN chmod +x /usr/bin/fwatchdog + ADD start.sh /home/app/start.sh + RUN chmod +x /home/app/start.sh # Add non root user RUN addgroup -S app && adduser app -S -G app RUN chown -R app /home/app WORKDIR /home/app USER app # Populate example here - i.e. "cat", "sha512sum" or "node index.js" - ENV fprocess="cat" + ENV fprocess="/home/app/start.sh" # Set to true to see request in function logs ENV write_debug="false" EXPOSE 8080 HEALTHCHECK --interval=3s CMD [ -e /tmp/.lock ] || exit 1 CMD ["fwatchdog"] イメージ作成&push faas-cli build -f mydns-update.yml docker login docker tag mydns-update username/mydns-update docker push username/mydns-update function定義 実行結果 INVOKEボタンを押して実行 Connecting to www.mydns.jp (81.4.101.244:80) writing to stdout <html> <head> <title>Free Dynamic DNS (DDNS) for Home Server and VPS etc | MyDNS.JP</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <LINK href="./site.css" rel=stylesheet type=text/css> </head> <BODY BGCOLOR="#FFFFFF" TEXT="#304040" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0" > Login and IP address notify OK.<BR> login_status = 1.<BR> <BR> <DT>MASTERID :</DT><DD>mydns??????</DD> <DT>REMOTE ADDRESS:</DT><DD>xxx.102.yyy.zzz</DD> <DT>ACCESS DAYTIME:</DT><DD>2022/01/10 13:50:44 UTC</DD> <DT>SERVER ADDRESS:</DT><DD>81.4.101.244</DD> <BR> </body> </html> - 100% |********************************| 618 0:00:00 ETA written to stdout 感想 Secret作成・参照するのではなく、実行時に与えるべき
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker Desktop for Macのアプリケーションとその関連ファイル全て削除する

Docker deamonだけでDockerを動かせるようにDocker Desktopをアンインストールして、その関連ファイルを全て削除します。 環境 OS: Mac OSX (Intel CPU) まず以下のコマンドをターミナルで実行してください。(コピペで構いません) terminal docker ps -q | xargs docker rm -f docker ps docker images -q | xargs docker rmi docker system prune 続いて以下のコマンドを実行してください。(こちらもコピペで構いません) terminal sudo rm -Rf /Applications/Docker.app sudo rm -f /usr/local/bin/docker sudo rm -f /usr/local/bin/docker-machine sudo rm -f /usr/local/bin/docker-compose sudo rm -f /usr/local/bin/docker-credential-desktop sudo rm -f /usr/local/bin/docker-credential-ecr-login sudo rm -f /usr/local/bin/docker-credential-osxkeychain sudo rm -Rf ~/.docker sudo rm -Rf ~/Library/Containers/com.docker.docker sudo rm -Rf ~/Library/Application\ Support/Docker\ Desktop sudo rm -Rf ~/Library/Group\ Containers/group.com.docker sudo rm -f ~/Library/HTTPStorages/com.docker.docker.binarycookies sudo rm -f /Library/PrivilegedHelperTools/com.docker.vmnetd sudo rm -f /Library/LaunchDaemons/com.docker.vmnetd.plist sudo rm -Rf ~/Library/Logs/Docker\ Desktop sudo rm -Rf /usr/local/lib/docker sudo rm -f ~/Library/Preferences/com.docker.docker.plist sudo rm -Rf ~/Library/Saved\ Application\ State/com.electron.docker-frontend.savedState sudo rm -f ~/Library/Preferences/com.electron.docker-frontend.plist <参考記事> Docker Desctop for mac を cli から削除する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaデベロッパが知るべきDockerセキュリティ5つの大切なこと

セキュリティのシフトレフトを考えると、開発段階から考えるということが重要ということはわかってはいても、どう実践すれば良いものか、、、 わかってはいつつ、なんとなく不安になりつつも、忙しく作業に没頭せざるをえないJavaデベロッパの方も多いのではないでしょうか。 今回はJavaデベロッパ向けDockerのセキュリティの短めの記事のご紹介です。 参考資料: JavaデベロッパのためのDocker:セキュリティを失敗させないために知っておくべき5つのこと(原文) Docker for Java developers: How not to fail your security(動画) JavaデベロッパのためのDocker:セキュリティを失敗させないために知っておくべき5つのこと Brian Vermeer ブライアンフェルメール 2020年11月20日 Dockerは、アプリケーションをコンテナ化するために最も広く使用されている方法です。Docker Hubを使えば、事前に作っておいたイメージを簡単に作成したりプルできます。Docker Hubにあるイメージを使用して、Javaアプリケーションのイメージをすばやく構築できるため、これは非常に便利です。ただし、Javaアプリケーション用のカスタムDockerイメージを作成する単純な方法には、多くのセキュリティ上の懸念が伴います。では、JavaデベロッパにとってセキュリティをDockerの必須要素にするにはどうすればよいでしょうか。 アプリケーション用の優れたDockerJavaイメージを作成する方法を説明する前に、このトピックに関するよくある質問をいくつか見てみましょう。 JavaアプリケーションをDocker化するには? JavaアプリケーションをDockerコンテナで実行するには、.jarまたは.warファイルをJREベースイメージにコピーするだけですが、その際にはいくつか注意すべき点があります。適切なJVM引数を選択し、コンテナのランタイム設定を一致させることは、対策の半分にすぎません。選択によっては、脆弱性があるため、どのベースイメージを使用するかは、セキュリティの観点から非常に重要です。 この記事は、ベースイメージの選択による影響をより深く理解するための情報を提供し、アプリケーションに利用可能な最も安全なイメージを見つけるためのガイドです。 JavaデベロッパにとってDockerはどのように役立ちますか? Javaアプリケーションをコンテナにパッケージ化することで、JRE、構成設定、OSレベルの依存関係、ビルドアーティファクトを含む完全なアプリケーションをコンテナイメージと呼ばれる自己完結型のデプロイ可能なアーティファクトに定義することができます。これらのイメージはソフトウェアで定義されており、作成に完全な再現性があり、開発者はすべての環境で同じプラットフォームを実行することができます。さらに、コンテナを利用することで、開発者は特別な権限を必要とせずに、デスクトップ上で新しいプラットフォームのリリースやその他の変更を簡単に試すことができます。 Javaアプリケーションに適したDockerベースイメージの選択 Dockerイメージを作成する際には、DockerHubから取得したいくつかのイメージをベースにしてイメージを作成します。これをベースイメージと呼びます。ベースイメージは、Javaアプリケーションのビルドに関する新しいイメージの基盤です。ベースイメージを選択することで、そのイメージで利用可能なすべてのものを利用することができるため、ベースイメージは不可欠です。ただし、これには代償が伴います。ベースイメージに脆弱性がある場合、新しく作成するイメージにもそれが引き継がれてしまうのです。 ベースイメージを見てみると、脆弱性の多くは、このベースイメージが使用するオペレーティングシステム(OS)レイヤーの一部です。2019年の以前の調査Dockerセキュリティのシフトレフトでは、OSレイヤーによってもたらされる脆弱性は、何を選択しているかによって大きく変わる可能性があることが示されています。 2019年のレポート–Dockerセキュリティのシフトレフト AdoptopenjdkのDockerJavaベースイメージの人気セット、openjdk11を見てみましょう。デフォルトタグを使用すると、このイメージはubuntuディストリビューションの上に構築されます。ただし、たとえばDebian、Centos、Alpineをベースにした特定のバージョンのタグを選択することもできます(注意:Alpineはglibcベースではないため、ネイティブのJNIコールを行うアプリケーションとの互換性がない可能性があります)。 正しいベースイメージを選択することは、セキュリティの観点から非常に重要であると結論づけることができます。おそらく、完全なオペレーティングシステムに付属するすべてのバイナリは必要ないでしょう。最アプリケーション用に新しいDocker Javaイメージを構築するには、最小限のベースイメージをベースにすることをお勧めします。持っていないバイナリが害を及ぼすことはできません。 セキュリティの面に加えて、最小限のベースイメージは、新しく作成するイメージのサイズを小さくします。Dockerイメージが小さければ、フットプリントも小さくなり、おそらく起動時間も速くなるでしょう。もう1つの考慮できる事項として、Dockerfileを必要としない最小限のJavaイメージを作成するjibでビルドすることも考えられます。 JDKではなくJREを使用する Dockerイメージを作成する際には、正しく機能するために必要なリソースのみを割り当てるべきです。つまり、本番イメージは、完全なJava Development Kit (JDK)ではなく、適切なJava Runtime Environment (JRE)を使用することから始める必要があります。さらに、本番イメージにはMavenやGradleなどのビルドシステムを含めべきではありません。ビルドの成果物、たとえばjarファイルがあれば十分です。 Dockerコンテナ内でアプリケーションをビルドする場合でも、マルチステージビルドを使用して、ビルドイメージを本番イメージから簡単に分離できます。 例: java-code-workshopアプリケーション用にDockerJavaイメージを作成したいと思います。これはmavenで構築されたspring-bootベースのアプリケーションで、Javaバージョン8を必要とします。 このDockerJavaイメージを作成する単純な方法は、次のようなものです。 mavenとopenjdk8を含むベースイメージを選択し、そのイメージにソースをコピーし、mavenを呼び出してアプリケーションをビルドして実行します。この例は全く問題なく動作します。アプリケーションは起動し、スムーズに動作します。しかし、先ほど作成したDockerイメージのサイズが631MBになっています。 このDockerfileを変更して、マルチステージビルドを使用してみましょう。 ここで何が起こっているのかというと、プロジェクトのビルドにmaven-openjdk8のイメージを使用しているということです。しかし、これはアウトプットではありません。かなり小さいjava 8 JREイメージをベースにした新しいイメージを作成し、実行可能なspring-boot jarだけをコピーします。あとは、そのjar-file、を実行するだけで完了です。その結果、JDKやmavenを含まない、JREのみのDockerイメージが作成されます。イメージサイズは132MBと劇的に小さくなりました。 イメージ小さいほど、アップロードが簡単で起動時間を短縮できるだけでなく、より安全です。もし何らかの理由で、JDK、ソースコード、ビルドツールが入った稼働中のコンテナに攻撃者がアクセスしたらどうなるか想像できますか? プライベートリポジトリにアクセスするためのシークレットを含める必要がある場合にも、これを使用できます。これらの種類のシークレットを本番用イメージのキャッシュに入れたくありません。本番環境ではビルドイメージを使用しないため、シークレットを使用してもまったく問題ありません。このテクニックを使えば、他のイメージから必要なものをチェリーピックして、必要なリソースだけを持つ製品Dockerイメージを作ることができます。 Dockerコンテナをrootで実行しない Dockerコンテナを作成する際、デフォルトではrootとして実行されます。これは開発時には便利ですが、本番用のイメージではこのようなことは避ける必要があります。何らかの理由で、攻撃者が端末にアクセスできたり、コードを実行できたとします。その場合、実行中のコンテナに対して重要な権限を持っているだけでなく、不適切に高いアクセス権でファイルシステムバインドマウントを介してホストファイルシステムにアクセスできる可能性があります。 これを防ぐための最も簡単な方法は、以下のように特定のユーザーを作成することです。 3行目では、新しいグループを作成し、ユーザーを追加しています。このユーザーは、パスワードとホームディレクトリを持たないシステムユーザー(-r)です。また、新しく作成したグループにも追加しています。 次に6行目で、このユーザーにアプリケーションフォルダへのアクセス権を与えています。7行目も忘れないでください。ここでは、使用したいユーザーを設定しています。こうすることで、新しく作成された制限付きユーザーが最後の行のコマンドを実行します。 開発中のDockerイメージとJavaアプリケーションのスキャン DockerfileからDockerイメージを作成し、イメージを再構築することでさえ、システムに新しい脆弱性をもたらす可能性があります。開発中のDockerイメージをスキャンすることは、脆弱性を可能な限り早期に発見するためのワークフローの一部であるべきです。 Snyk CLIを使えば、Dockerイメージを簡単にスキャンできます。ローカルマシンでも、パイプラインの一部として、またはその両方で使えます。Snyk CLIをインストールして認証した後イメージをスキャンするためにしなければならないことはただ一つです。 $ snykコンテナテスト 最初のセクションで説明したように adoptopenjdkのイメージをスキャンしたい場合、コマンドは次のようになります。 $ docker pull adoptopenjdk/opendjdk11:latest $ snyk container test adoptopenjdk/opendjdk11:latest アウトプット: Docker イメージのテストとモニタリングの両方が可能です。モニタリングには snyk container monitor <image> を使用します。モニタリングでは、スナップショットを取り、時間の経過とともにイメージに新しい脆弱性や修正プログラムが利用できるかどうかを監視します。 イメージをスキャンしてDockerfile(新しいDocker Javaイメージを作成)がある場合は、フラグ--Dockerfile=<dockerfile>をsnyk container testかsnyk container monitor`のいずれかに追加する必要があります。これで、より適切な修復アドバイスが得られます。たとえば、利用可能な脆弱性の数を減らす利用可能なベースイメージがある場合、それを知ることができます。 例: $ snyk container test myImage:mytag --Dockerfile=path/Dockerfile $ snyk container monitor myImage:mytag --Dockerfile=path/Dockerfile Javaアプリケーションのスキャン 構築しているDockerJavaイメージには、アプリケーションも含まれています。明らかに、これは攻撃のポイントとなり得ます。あなたのJavaアプリケーションにセキュリティ脆弱性がないことを確認する必要があり、JavaデベロッパのDocker環境において、最初の段階でセキュアな方法を採用したことになります。アプリケーションに、 あなたのアプリケーションが、REST エンドポイントを呼び出すときにリモートでコードの実行を可能にするライブラリを含んでいると想像してください。あなたのイメージの残りの部分に何の脆弱性もないとしても、これは悲惨な結果になる可能性があります。 Dockerイメージに挿入するJavaバイナリの大部分は、おそらくインポートするコードです。アプリケーションが持つライブラリとフレームワークは依存関係と考えることができます。Snyk CLIを使用で、依存関係を簡単に確認できます。これは、さきほどイメージをスキャンするために使用したものと同じCLIです。ルートフォルダでsnyk testまたはsnyk monitorを呼び出すと、ライブラリのセキュリティの脆弱性についてアプリケーションをスキャンまたは監視することができます。 自分が書いたコードには、コード分析ツールまたはSonarLint、PMD、spotbugsなどのリンターを使用することをお勧めします。これらのツールは、より優れたコードを作成するための汎用ツールですが、明らかなセキュリティミスを防ぐのにも役立ちます。 再構築するためのビルド Dockerイメージ用のJavaアプリケーションを、いつでも破棄して再構築できるようにビルドします。実行中のコンテナに問題があることに気付いたとしましょう。単にkillし(プロセス停止)し、新しいインスタンスを起動することができれば最高です。そのためには、データがコンテナの外部に格納されるように、ステートレスJavaアプリケーションを設計する必要があります。次のことが考えられます。 コンテナ内でデータベースのデータストアを実行しない。 コンテナ内にファイル(ログ)を保存しない。 自動回復のキャッシュを確認する(該当する場合)。 アプリケーションを破棄して、いつでも新しいインスタンスを起動できるようにビルドしている場合、Dockerイメージ全体も安全に再構築することができます。脆弱なDockerイメージの20%は、イメージを再構築するだけで、1つ以上のセキュリティ問題を修正できることをご存知ですか?Dockerイメージは、多くの場合、ベースイメージの「最新の」タグに基づいています。この「最新」は時間の経過とともに変化し、より新しく改良されたバージョンに置き換えられます。aptやyumなどのパッケージマネージャーを使用してコンテナーにインストールされたキーバイナリについても同様です。もちろん、最新バージョンを使用することは、最新のセキュリティ修正が自動的に適用されるため、セキュリティの観点からは良いことです。しかし、ベースイメージは時間の経過とともに変化し、結果として特定の時間のスナップショットでイメージを再現することが難しくなるということとのバランスを取る必要があります。 アプリケーションに変更がなかったとしても、定期的にDockerイメージを再構築し、場合によってはベースイメージのバージョンタグを新しくしたり、最新のものにしたりしてください。OSレイヤーのような基礎的なレイヤーの改善は、イメージの品質を向上させ、セキュリティの脆弱性を減らすことができます。 最後に、一般的な最適なDockerイメージまたはJavaアプリケーションを構築するためのセキュリティのベストプラクティスについて把握しておきたい方は、以下をご覧ください。 10 best practices to build a Java container with Docker<原文> - Javaアプリケーション用に安全でパフォーマンスの高いDockerイメージを構築する方法を、ステップバイステップで詳細に説明しています。 Javaセキュリティ・ベスト・プラクティス10<原文>→<翻訳記事coming soon> - あらゆる環境でJavaアプリケーションを構築する際に従うべきセキュリティ・プラクティスです。 Contents provided by: Jesse Casman, Fumiko Doi, Content Strategists for Snyk, Japan, and Randell Degges, Community Manager for Snyk Global
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エンジニアじゃない人にも認識器を使ってもらいたい!!!そんな人向けに、Flask+Docker+pytorchで、認識webアプリの作り方!【簡単、webで単眼Depth推定】

なにこれ? 単眼Depth推定をWebアプリで認識できる簡単なツールです。 エンジニアじゃない人でも簡単に認識器を試してもらえるようにエイヤで作ってみました! 改造すればいろいろ拡張できるかな~と思います。 どうやってるの? dockerでサーバーを立ててます。 フロントエンドはflask、バックエンドはpytorchです。 動作環境 OS: Ubuntu PC 18 GPU: GTX 3090 ソースコード ツリー構成 こんな感じのツリーで作成していきます。 各ソースコードは下の方にすべて記載してます。 ├── app.py ├── docker │   ├── Dockerfile │   └── build.sh ├── results │   └── <ここに認識結果のデータを格納します。> ├── run_docker.sh ├── run_server.sh ├── src │   ├── configs.py │   ├── dnn │   │   └── MiDaS.py │   └── recognizer.py ├── static │   └── <ここにアップロードされた画像が格納されます> └── templates └── index.html 使い方はこんな感じになります。 ./run_docker.shでdocker起動。 ./run_server.sh [GPU No]で起動。 ブラウザで44444にアクセス。 参考にしたHP https://qiita.com/oyngtmhr/items/433974829015abac753c https://gray-code.com/blog/flask-on-docker/ ソースコードすべて(gitにアップしなくてごめんなさい。。) こだわり: いろんな認識器を差し替えられるように拡張性を持たせてます。 configの値を変えると、モデルを差し替えられるようにしています。 recognizer classで、MiDaSのモデルをロードしています。 MODELをtestにすると、ただのグレースケール化する処理が走ります。 ラジオボタンでセグメンテーションに切り替えられるようにとかしたいなぁ app.py from flask import Flask, request, redirect, url_for,flash, render_template from flask import send_from_directory from werkzeug.utils import secure_filename import os import argparse from src.configs import configs from src.recognizer import recognizer_class ###### app = Flask(__name__) app.config['UPLOAD_FOLDER'] = configs.UPLOAD_FOLDER recognizerClass = recognizer_class(configs.MODEL , configs) ###### def allwed_file(filename): # .があるかどうかのチェックと、拡張子の確認 # OKなら1、だめなら0 return '.' in filename and filename.rsplit('.', 1)[1].lower() in configs.ALLOWED_EXTENSIONS ##### @app.route('/', methods=['GET', 'POST']) def uploads_file(): # リクエストがポストかどうかの判別 if request.method == 'GET': return render_template("index.html") elif request.method == 'POST': # ファイルがなかった場合の処理 if 'file' not in request.files: flash('ファイルがありません') return redirect(request.url) # データの取り出し file = request.files['file'] # ファイル名がなかった時の処理 if file.filename == '': flash('ファイルがありません') return redirect(request.url) # ファイルのチェック if file and allwed_file(file.filename): # 危険な文字を削除(サニタイズ処理) filename = secure_filename(file.filename) filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) # ファイルの保存 file.save(filepath) # 認識 resultpath = recognizerClass.recognize(filepath) # アップロード後のページに転送 return render_template("index.html", filepath=filepath, resultpath=resultpath, result =True) #### @app.route('/results/<filename>') # ファイルを表示する def uploaded_file(filename): return send_from_directory(configs.RESULTS_FOLDER, filename) #### if __name__ == "__main__": if not os.path.exists("static"): os.makedirs("static") if not os.path.exists("results"): os.makedirs("results") parser = argparse.ArgumentParser() parser.add_argument('--gpu', '-g', default=0, type=int, help='GPU ID (negative value indicates CPU)') args = parser.parse_args() if args.gpu==-2: multigpuFlag=True else: os.environ["CUDA_VISIBLE_DEVICES"] = str(args.gpu) app.run(host="0.0.0.0", port=5000, debug=True) templates/index.html <html> <head> <meta charset="utf-8"> <title>flask recognizer</title> </head> <body> <h1> ファイルをアップロードして認識しよう(MiDaS:DPT_Hybrid) </h1> Created by sugupoko <BR> {% if result %} 入力画像 <BR> <IMG SRC="{{filepath}} " BORDER="1"> <BR> <HR> 出力結果 <BR> 認識結果を0-1に線形で正規化、疑似カラーの処理をして表示しています。<BR> <IMG SRC="{{resultpath}} " BORDER="1"> <BR> <HR> {% endif %} ファイルを選択して送信してください<BR> <form action = "./" method = "POST" enctype = "multipart/form-data"> <input type = "file" name = "file" /> <input type = "submit"/> </form> </body> </html> src/config.py class configs(): #PATH =============== ROOT="./" MODEL = "MiDaS" # 画像のアップロード先のディレクトリ UPLOAD_FOLDER = ROOT + 'static/' # 結果が収納される場所 RESULTS_FOLDER = ROOT + "results/" # HTMLの場所(デフォルト変更するには、要調査) HTML_FOLDER = ROOT + 'templates/' # アップロードされる拡張子の制限 ALLOWED_EXTENSIONS = set(['png', 'jpg', 'gif']) src/recognizer.py import os import cv2 from src.dnn.MiDaS import MiDaS class recognizer_class: def __init__(self,mode, configs): print("init ==================================") self.mode = mode self.configs = configs if self.mode == "MiDaS": print("run MiDaS") self.model_class = MiDaS() self.func = self.model_class.run elif self.mode == "test": print("run test") self.func = eval("rgb2gray") def __del__(self): print("del class ==================================") def some_function(self): print(self.mode) def recognize(self, filepath): filename = os.path.basename(filepath) resultpath = os.path.join(self.configs.RESULTS_FOLDER, filename) img = cv2.imread(filepath) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img_out = self.func(img) cv2.imwrite(resultpath, img_out) return resultpath def rgb2gray(image): img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) return img_gray src/dnn/MiDaS.py from glob import glob from PIL import Image import numpy as np from torchvision import transforms import torch import timm import cv2 class MiDaS: def __init__(self): self.model_type = "DPT_Hybrid" self.model = torch.hub.load("intel-isl/MiDaS", self.model_type) self.device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu") self.model.to(self.device) self.model.eval() midas_transforms = torch.hub.load("intel-isl/MiDaS", "transforms") if self.model_type == "DPT_Large" or self.model_type == "DPT_Hybrid": self.transform = midas_transforms.dpt_transform else: self.transform = midas_transforms.small_transform def __del__(self): print("del") def run(self, input_image): input_batch = self.transform(input_image).to(self.device) # move the input and model to GPU for speed if available if torch.cuda.is_available(): input_batch = input_batch.to('cuda') self.model.to('cuda') with torch.no_grad(): prediction = self.model(input_batch) prediction = torch.nn.functional.interpolate( prediction.unsqueeze(1), size=input_image.shape[:2], mode="bicubic", align_corners=False, ).squeeze() result = prediction.cpu().numpy() result = normalize_depth(result, bits=1) result = cv2.applyColorMap(result, cv2.COLORMAP_JET) del prediction, input_batch return result def normalize_depth(depth, bits): depth_min = depth.min() depth_max = depth.max() max_val = (2**(8*bits))-1 if depth_max - depth_min > np.finfo("float").eps: out = max_val * (depth - depth_min) / (depth_max - depth_min) else: out = np.zeros(depth.shape, dtype=depth.type) if bits == 1: return out.astype("uint8") elif bits == 2: return out.astype("uint16") docker/Dockerfile FROM nvidia/cuda:11.4.2-cudnn8-devel-ubuntu20.04 ARG use_cudnn=1 ENV DEBIAN_FRONTEND=noninteractive ENV HOME=/root \ DEBIAN_FRONTEND=noninteractive \ LANG=ja_JP.UTF-8 \ LC_ALL=${LANG} \ LANGUAGE=${LANG} \ TZ=Asia/Tokyo RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ echo $TZ > /etc/timezone RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential \ sudo \ cmake \ git \ wget \ libatlas-base-dev \ libboost-all-dev \ libgflags-dev \ libgoogle-glog-dev \ libhdf5-serial-dev \ libleveldb-dev \ liblmdb-dev \ libprotobuf-dev \ libsnappy-dev \ protobuf-compiler \ python3-dev \ python3-pip \ python3-setuptools \ python3-tk \ python3-matplotlib \ less \ aptitude \ software-properties-common \ ssh \ unzip \ qt5-default \ qttools5-dev-tools \ libqt5widgets5 \ glew-utils \ libglew-dev \ libglm-dev \ tcsh \ aptitude \ freeglut3-dev \ libqt5opengl5-dev \ libcanberra-gtk-dev \ libcanberra-gtk-module \ libgtest-dev \ emacs \ spyder \ valgrind && \ rm -rf /var/lib/apt/lists/* # cmake RUN pip3 install numpy==1.18.2 RUN pip3 install pillow==7.1.1 ipython==7.13.0 matplotlib==1.5.1 WORKDIR /workspace # for python profiling RUN pip3 install line_profiler==3.0.2 && \ pip3 install pyprof2calltree==1.4.4 RUN pip3 install scipy==1.4.1 RUN pip3 install --upgrade pip==20.0.2 setuptools==46.1.3 RUN pip3 install tensorflow-gpu==2.7.0 RUN pip3 install keras==2.7.0 RUN pip3 install opencv-python RUN pip3 install opencv-contrib-python RUN pip install torch==1.10.0+cu113 torchvision==0.11.1+cu113 torchaudio===0.10.0+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html RUN pip install pandas RUN pip install tqdm RUN pip install albumentations RUN pip install sklearn RUN pip install seaborn RUN pip install tensorboard RUN pip install hydra-core RUN pip install tensorflow-model-optimization RUN pip install timm #=============================================================== WORKDIR /app RUN pip install Flask docker/build.sh nvidia-docker build --no-cache -t flask_dnn . run_docker.sh #!/bin/bash SCRIPT_DIR=$(cd $(dirname $0); pwd) DOCKSHARE=${SCRIPT_DIR} DOCKIMG="flask_dnn:latest" INITDIR="/home/dockshare" docker run -it --runtime=nvidia \ -p 44444:5000 \ -w ${INITDIR} --rm - \ -v ${DOCKSHARE}:${INITDIR} ${DOCKIMG} bash run_server.sh if [ $# -ne 1 ]; then echo "usage $0 [GPU_ID]" exit 1 fi GPU_ID=$1 if [ $GPU_ID != -2 ]; then echo "GPU : $GPU_ID" export CUDA_VISIBLE_DEVICES=$GPU_ID else echo "multi GPU train" fi python3 ./app.py -g $GPU_ID
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

インフラ素人が3週間で、Webサービスのインフラを0から構築して起きた変化

目次 1. インフラ設計 2. ネットワーク構築 3. サーバ・DB構築 4. Lambdaによる自動化 5. Amazon SES・SNS 6. 開発環境構築(docker) 7. jenkins・phpmyadminなど開発ツール設定 8. VPN接続 9. 起きた変化 10. おまけ はじめに インフラ素人同然レベルだった私が実務において、Webアプリケーションのインフラを0から構築することになりました。 期間は約3週間、インフラ設計から行い、構築したインフラ上でWebサービスが稼働できる状態まで持っていくことがゴールになります。  約3週間の間、AWSやミドルウェア周りの技術に触れ、今まで考えてもみなかった知識の習得や、多くのつまづきを通して貴重な経験をすることができました。 チームメンバーと分担しながら進めたので、あくまで自分のやったことベースにはなりますが、3週間どんなことをやったのか、特につまづいたことを中心に書いていきたいと思います。 ※基本的なインフラ構成はほぼ全てAWSのリソースを使用しました。 【使用技術】 ・ AWS(EC2, RDS, VPC, Route53, Lambda, IAM, SES, SNS, ACM, CloudWatch, etc.) ・ docker ・ Nginx(Webサーバ) ・ Puma(アプリケーションサーバ) ・ Ruby on Rails 開始前の筆者のレベルを3行で! ・自社開発エンジニア歴5ヶ月の駆け出しエンジニア ・AWS Cloud Practitionerの資格は持っているが実務でのインフラ(AWS)経験は無し ・ミドルウェア周りの知識はほぼゼロ(Webサーバとアプリケーションサーバってどう違うの?レベル) 1. インフラ設計 まずやったこととして、Webサービスを稼働させる上でどのようなインフラ環境が必要か理解し、可視化することです。 今回、実装するのは、Web開発では基本となる開発環境(development)、ステージング環境(staging)、本番環境(production)からなる構成です。 なんとなくの理解だったので、各環境の役割について、改めて理解するのに良い記事があったので共有します。 事前学習 とはいっても、 VPCってなんぞや? サブネットの切り方ってどうするの? NATゲートウェイとは? ALBとロードバランサーなにが違うの? など、私にとってネットワーク設計やサーバ構築が初めての経験だったので、知識の習得のため最初の数日はとにかくインプット量を意識して手を動かしながら学習しました。 VPC構築、サブネットの設定、EC2構築など実際にAWSマネジメントコンソール上でポチポチやりながら、「こことここが繋がるのか〜」「こうすれば良いのね」といった感じで、体系的にAWSのリソースを理解しながら、知識を蓄えていったところが大きいです。 一部、以下AWSCloudTechのサービスを使ったりなんかもしました。 実装するインフラ構成を図に落とし込む 理解が少し曖昧なところがありながらもなんとか、今回実装するインフラ構成を図に落とし込みました。 ここまででおおよそ3日です。 Webサーバはアクセスを考慮して、現時点では冗長構成にせず単一のインスタンスのみで運用を行い、batchサーバの役割も持たせます。 本番環境では、バウンスメール等はSES,SNSを使用し、通知を行いレートの管理をします。 他ネットワークからのアクセスも可能にするため、AWSClientVPNを使用したVPN接続も実装する予定です。 Excelにインフラ設計情報の詳細をドキュメント化 インフラ設計情報として、開発する上で必要になってくる細かな設定に関しては、都度Excelに記載し、後ほど忘れないためにドキュメント化し管理しました。 ・RDSの詳細設定 ・各インスタンスの詳細設定 ・pemキー情報、IAM情報、各種ログイン情報  ・ssh接続コマンドチートシート などなど 2. ネットワーク構築  ここから実際に手を動かしていきます。上記インフラ構成を実現するためにまずは、サーバを設置する箱となるネットワークの構築(VPC)から始めました。 ざっくり手順は以下のとおり実現しました。 1.VPC設定 2.VPC内にサブネット構築 3.インターネットゲートウェイの設置 4.NATゲートウェイの設置 5.VPC間のピアリング接続の設定 6.サブネットのルートテーブルの設定 具体的な設定についてここでは詳細省きますが、これでおおよそ必要なるネットワーク構成は出来あがります。 1つ注意点として、 VPCやサブネットを作成する際のIPv4CIDRブロックは、以下のプライベートIPアドレスの範囲内で作成する必要があります。 プライベートアドレスは以下のように範囲が定められており、この範囲外のIPアドレスはグローバルアドレスとなってしまうからです。 クラス 範囲 ネットワーク数 クラスA 10.0.0.0 ~ 10.255.255.255 (10.0.0.0/8) 1 クラスB 172.16.0.0 ~ 172.31.255.255 (172.16.0.0/12) 16 クラスC 192.168.0.0 ~ 192.168.255.255 (192.168.0.0/16) 256 パブリックサブネットとプラベートサブネット 自分も勉強していく中で、パブリックサブネットとプライベートサブネットってどう違うのと疑問に思っていました。 サブネット作成のコンソール画面でもパブリック/プライベートの選択などなかったはずです。 【結論】 サブネットのルートテーブルの記述内容で変わる パブリックサブネット すべてのIPアドレス宛のターゲットがインターネットゲートウェイに向いているもの。 送信先 ターゲット 0.0.0.0/0 インターネットゲートウェイ  プライベートサブネット すべてのIPアドレス宛のターゲットがNATゲートウェイに向いているもの。 プライベートサブネットがインターネットと通信するためには、NATゲートウェイのルートテーブルに0.0.0.0/0がインターネットゲートウェイに向かうように設定する必要があります。 送信先 ターゲット 0.0.0.0/0 NATゲートウェイ  3. サーバ・DB構築  サブネット内に必要となるEC2インスタンス、またはRDSを作成していきます。 (EC2,RDSの詳細な作成設定については記述を省略します) パブリックサブネットに配置するインスタンスには、インターネットに接続するため、ElasticIPを関連づける必要があります。 パブリックサブネットに配置したインスタンスは通常、パブリックIPv4アドレスが割り当てられます。 しかし、これだとインスタンスの起動停止のたびに毎回IPアドレスが変わってしまいます。 それでは大変なので、EIPを取得し、インスタンスと関連づけることでEIPを固定のパブリックIPアドレスとして使用することができます。 EC2インスタンス作成とNginxインストール 必要となるEC2インスタンスを作成します。 アプリケーションを動かすインスタンスには、RailsアプリケーションサーバであるPumaと通信するためのWebサーバ(Nginx)を事前にインストールします。 アプリケーションデプロイ後には、NginxとPumaを通信させる必要があります。 EIPの上限はデフォルトだと5個な話 インスタンスにEIPを割り当てるときに、画像のようなエラーがでてきました。 これはEIPの数が制限に達したという警告です。 EIPは、サポートプランにも寄りますが、Developerプランではデフォルトでは5個までしか使用できません。(デフォルトVPC内に2つ使用されているので、実質3個) 5個以上使用する場合には、Service Quatasのダッシュボードから申請を行う必要があります。 https://ap-northeast-1.console.aws.amazon.com/servicequotas なお、一気に20個とか申請すると、「申請する理由を詳しく教えてください」などとAWS側から怒られてしまうので、まずは5個ぐらいから始めると良いと思います。(※申請は全て英語です) 私は、デフォルトVPCに関連づいているEIP2個を外したのと、追加申請5個で足りました。 RDSのフェイルオーバー設定 RDSを作成していきます。 RDSの詳細設定に関しては、各自RDS使用用途によって異なるかと思うので、詳細は省きます。 障害時にはRDSを他AZで起動させるフェイルオーバーをさせる必要があります。 RDSのダッシュボードからサブネットグループの作成を行い、フェイルオーバーす先となるAZをグルーピングします。 フェイルオーバーの確認については、以下記事を参考にテストしてみました。 EC2-RDS間の疎通確認 アプリケーションが動く、EC2インスタンスからRDSへ接続ができることを確認します。 RDSのエンジンとしてMySQLを選択していた場合、EC2インスタンス上で以下コマンドでRDS内のMySQLと接続ができます。 RDSに設定しているセキュリグループのインバウンドルールに接続元EC2からのMySQL通信(3306番ポート)の許可を忘れずに! 接続元ec2 $ mysql -u マスターユーザー名 -pパスワード -h エンドポイント マスターユーザー名,パスワードは、それぞれRDS作成時に決定したものです。エンドポイントはRDS作成後に確認できます。 ALBの使用 一台のEC2ならロードバランサーは使用しなくても良いのでは思ったのですが、単なる負荷分散以外の機能もAWSのロードバランサーは提供してくれるので、そちらのメリットを加味してALBを使用します。 またALBには、Amazon Certificate Manager(ACM)からSSL証明書を取得し、HTTPSでの通信を設定します 4. Lambdaによる自動化 Lambda,EventBridge, CloudWatch, AWSChatBot, SNS このあたりのサービスを使用して開発する上で管理しやすくなる自動化を行いました。 Lambda✖︎EventBridgeによるEC2インスタンス起動・停止の自動化 開発ツール系のインスタンスや使用頻度の高いインスタンスについは起動・停止を自動化すると便利です。 ↓記事を参考に、実装しました。 環境ごとのEC2・RDSの停止忘れ防止 staging環境、production環境(運用開始まで)に属するインスタンス、RDSについては毎日↑のLambdaの停止スクリプトのみを実行し、停止忘れ防止策をこうじました。 AWS ChatbotによるSlack通知 Lambdaの実行結果は、CloudWatchLogsに蓄積されます。 インスタンスの停止スクリプトなどはエラー通知を即座に把握したいため、実務のコミュニケーションツールとして使用しているSlackに通知するように設定しました。 すごく便利!! 5. Amazon SES,SNS  本番環境や開発環境において、AWSからメールを送信するにはAmazon SimpleEmailService(SES)を使用します。 ここでもざっくりと手順を説明すると以下になります。 1.メール送信権限付きIAMユーザー作成後、アクセスキーを取得 2.Railsアプリコード内の ~アプリ名/config/environments/development.rb, staging.rb, production.rb配下にアクセスキーを記載 3.SESでドメイン、メールアドレスの認証 4.認証したドメイン・アドレスからのメール送信の確認 5.SNSでメール通知設定 メール送信権限付きユーザーのアクセスキーを取得 メール送信権限を持つIAMユーザーを作成します。(既存のユーザーでも大丈夫です) アタッチするポリシーは、AmazonSesSendingAccessというデフォルトポリシーがあるのでそれをユーザーにアタッチすれば大丈夫です。 AmazonSesSendingAccess { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "ses:SendRawEmail", "Resource": "*" } ] } テストメール送信 ドメイン・メールアドレスの認証を行います。 認証からテストメールの送信の仕方までは↓記事が参考になります。 ※ドメインに関しては、Route53のほうで事前に認証済みのドメインでないとSES上で認証されないので注意が必要です。(経験あり?) バウンスメールのテストについては、上記テストメールの送信先をbounce@simulator.amazonses.comに設定してあげると、バウンスレートを上げることなくバウンスメールのテストを行なってくれます。 その他、通常の成功メールや苦情メールも同様で↓に記載のテスト用アドレスを使用するとレートとは無関係にテストができます。 バウンスメール・苦情メール等については、SNSとSESを連携させて通知設定を行います。 これも↓記事が参考になるので、簡単に設定できます。 ここまでだいたい2週間ぐらいでした。 6. 開発環境構築(docker)  ローカルの開発環境と検証用のdevelopment環境はdockerで構築を行いました。 Dockerfile, docker-compose.ymlを作成し、シェルスクリプトとして開発環境構築用のスクリプトを用意しました。 このスクリプトを叩くだけで、dockerでの開発環境ができあがります。 合わせて、初期データとして必要なrakeタスクも実装しました。 ざっと内容を紹介 setup.sh #!/bin/bash # 作業用ディレクトリ作成 if [ ! -d ./workspace ]; then mkdir workspace fi # 作業用dir配下にプロジェクトをclone git clone git@~~ # キャッシュを使用せず、imageをbuild docker-compose build --no-cache # 必要な初期rakeタスクを流す docker-compose run web bundle exec rake rakeタスク # 停止済みのコンテナを削除 docker container prune -f ridgepole導入時にハマったこと 今回、アプリケーションはmigrationファイルでの管理ではなくridgepoleを使用します。 私自身初めてのridgepole導入だったので少し苦戦しました。 後ほどridgepole導入時にハマったことは詳細をQiitaに投稿しようと思っています。 ridgepoleとは↓・・・ 7. jenkins・phpmyadminなど開発ツール設定  開発ツール用のインスタンスにjenkins, phpmyadminをそれぞれインストールします。 それぞれのインスタンスには、EC2FullAccessなどのポリシーを持ったアクセスキーを配置する必要があります。 jenkinsインスタンス # 認証情報を設定 $ aws configure # 4つ聞かれるのでそれぞれ入力 1. アクセスキー 2. パスワード 3. region 4. output形式 # この配下に認証情報(アクセスキー等がある) $ cd ~/.aws $ ls -la => config, credentials jenkinsのジョブが失敗する アクセスキーの設置が終わり、jenkinsのジョブを作成し実行たところ、エラーになりました。。。 jenkisnのコンソール出力を見ると以下のようなエラーメッセージが unable to locate credentials. you can configure credentials by running "aws configure". おいおい、アクセスキーはちゃんと配置したはずだぞ? 設定を確認してもきちんと登録されているはず・・・ # アクセスキー設定情報を確認 aws configure list => aws_access_key_id = ***************** => aws_secret_access_key = ***************** 【結論】 jenkinsのジョブを実行しているのは、ec2の中のjenkinsユーザー だからjenkinsユーザーに認証情報を設定してあげる必要がある(これまでは、ec2ユーザーとrootユーザーに設定していた) # rootユーザーになる $ sudo su # jenkinsユーザーになる $ su - jenkins # 認証情報が確認すると・・・なにも設定されていなかった $ aws configure list => aws_access_key_id => aws_secret_access_key # 再度認証情報を設定 $ aws configure 無事、ジョブが成功した! 余談ですが、画面上で作成したjenkinsのジョブはサーバ内jenkinsユーザーの以下に格納されているので、確認できます。 jenkinsインスタンス # rootユーザーになる sudo su # jenkinstユーザーになる su - jenkins cd workspace ls -la => 作成したjenkinsジョブ一覧が表示 8. VPN接続  別のネットワークから、この環境へのアクセスを可能にするためAWSClientVPNを使用し、VPN接続を試みます。 パブリックサブネット内のNATゲートウェイにアタッチしたEIPを固定IPアドレスとして、インターネットに接続できることをゴールとします。 ↓記事を参考にするとできるかと思います。 が、私がハマったことで一つ注意点! ClientVPVEndpointと関連づけるサブネットはプライベートサブネットでないといけない これに気づかず、数時間ハマりました。。。 また、VPN接続するときに作成されるENIは、接続のたびに新しいものが作成されます。 ENIに割り当てられるパブリックIPアドレスは接続のたびに毎回変わりますが、それをNATゲートウェイのEIPで固定化するというイメージです。 9. 起きた変化 以上作業を約3週間で行いました。 インフラにほとんど触ったことの無かった素人が3週間のインフラ構築を通して、どのような変化があったのか、技術的な側面と価値観・考え方の側面から感じたことを書いていきたいと思います。 技術面 【AWS】 ・AWSを使用して、迷わず基本的なアーキテクチャの設計ができるようになった ・AWS上のサービスをドキュメントを読むだけで使いこなせるようになった ・AWSリソースの管理能力が向上した 【docker】 ・dockerを使用して、開発環境の構築ができるようになった ・Dockerfile, docker-compose.ymlなど記述内容を理解し、自分の実装したいように改修できるようになった 【その他】 ・Webサーバやアプリケーションサーバのどのレイヤーに対してエラーが発生しているのか感覚的にわかるようになった ・ログを見る習慣/ログからエラーの原因を特定する力がついた ・Linuxサーバ内のディレクトリ構造や必要ファイルなどが把握し、行いたい修正を加えられるようになった ・Webの3層構造を体系的に理解することができた ざっとこんな感じになります。 特に自分として変化を感じたことは、基本的なアーキテクチャ設計をする際のAWSリソースに対して「わからない・これどうやるんだ」という感情がなくなったことです。 今までは、事前知識としてなかったのはもちろんですがどこかAWSを気軽に触れないものだと思っていました。 ですが、毎日AWSを触りながら理解していくことで、AWSに対するハードルが低くなっていくのを強く感じました。 今では、初めて使用するAWS上のサービスでも「まずは触ってみる」ということを大切にし、ドキュメントや参考記事を読めばだいたいのことはなんとかなるようになったと思います。 わずか3週間ですが、自分のエンジニアとしての知識・技術が3週間前と比較し格段に上がったのを感じました! 価値観・考え方 インフラ構築を経験してみることで、インフラの楽しさを感じたのと同時に、改めて、AWSの素晴らしさに感動しました。 マネジメントコンソールをいじるだけでサーバ・ネットワークを作れたり、あらゆるサービスを連携することでできることの無限さを感じました。 インフラというのはどこか、モヤに隠れた分かりづらいものという意識があった中、自分で0から設計し図を書き、それを実際に構築するという経験のおかげで構造的にインフラを捉えられるようになりました。 AWSを理解するにはまず図を書いてみる とよく聞きますが、これは本当にそうだと思います。 図を書きながら、「これってどうやって通信する」「この部分にはどういう制御が必要なんじゃないか」と頭の中で考え、それを実際に手を動かしてみることで必要な処理や構成が感覚的に理解できるようになってきます。 この経験をきっかけとして、これからもインフラに関する知識・技術を勉強し、今後はSAA,さらにはその上のSAPの取得を目指していきたいと思います。 そして、実務としてこのような素晴らしい挑戦の機会をいただけたことに感謝し、これからも成長できるように頑張っていきたいと思います。 10. おまけ 約3週間インフラ構築をする上で、ミドルウェアの操作についてはコマンドラインが主となるので、自然とコマンドを使う機会が多くなりました。 そこで備忘録がてら、よく使用したコマンドを残していきたいと思います。 よく使ったコマンドたち ■ コマンド # ファイル削除 rm -rf ディレクトリ # 検索 grep -r 正規表現 # プロセス確認 ps aux | grep XXX # 環境変数確認 env ■ systemctl # start, stop, restart, statusなど # Nginxの起動状態確認 systemctl nginx status # MySQLの起動状態確認 systemctl mysql status ■ crontab # サーバの初期設定をするcrontabの内容確認 crontab -l ■ Git # 接続先のremoteリポジトリを確認 git remote -v # remoteリポジトリを設定 git remote set-url origin git@~~~ ■ ssh ssh -i ~/.ssh/キー.pem ec2-user@IPアドレス
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

インフラ素人が3週間でWebサービスのインフラを0から構築するためにやったこと

目次 1. インフラ設計 2. ネットワーク構築 3. サーバ・DB構築 4. Lambdaによる自動化 5. Amazon SES・SNS 6. 開発環境構築(docker) 7. jenkins・phpmyadminなど開発ツール設定 8. VPN接続 9. 起きた変化 10. おまけ はじめに インフラ素人同然レベルだった私が実務において、Webアプリケーションのインフラを0から構築することになりました。 期間は約3週間、インフラ設計から行い、構築したインフラ上でWebサービスが稼働できる状態まで持っていくことがゴールになります。  約3週間の間、AWSやミドルウェア周りの技術に触れ、今まで考えてもみなかった知識の習得や、多くのつまづきを通して貴重な経験をすることができました。 チームメンバーと分担しながら進めたので、あくまで自分のやったことベースにはなりますが、3週間どんなことをやったのか、特につまづいたことを中心に書いていきたいと思います。 ※基本的なインフラ構成はほぼ全てAWSのリソースを使用しました。 【使用技術】 ・ AWS(EC2, RDS, VPC, Route53, Lambda, IAM, SES, SNS, ACM, CloudWatch, etc.) ・ docker ・ Nginx(Webサーバ) ・ Puma(アプリケーションサーバ) ・ Ruby on Rails 開始前の筆者のレベルを3行で! ・自社開発エンジニア歴5ヶ月の駆け出しエンジニア ・AWS Cloud Practitionerの資格は持っているが実務でのインフラ(AWS)経験は無し ・ミドルウェア周りの知識はほぼゼロ(Webサーバとアプリケーションサーバってどう違うの?レベル) 1. インフラ設計 まずやったこととして、Webサービスを稼働させる上でどのようなインフラ環境が必要か理解し、可視化することです。 今回、実装するのは、Web開発では基本となる開発環境(development)、ステージング環境(staging)、本番環境(production)からなる構成です。 なんとなくの理解だったので、各環境の役割について、改めて理解するのに良い記事があったので共有します。 事前学習 とはいっても、 VPCってなんぞや? サブネットの切り方ってどうするの? NATゲートウェイとは? ALBとロードバランサーなにが違うの? など、私にとってネットワーク設計やサーバ構築が初めての経験だったので、知識の習得のため最初の数日はとにかくインプット量を意識して手を動かしながら学習しました。 VPC構築、サブネットの設定、EC2構築など実際にAWSマネジメントコンソール上でポチポチやりながら、「こことここが繋がるのか〜」「こうすれば良いのね」といった感じで、体系的にAWSのリソースを理解しながら、知識を蓄えていったところが大きいです。 一部、以下AWSCloudTechのサービスを使ったりなんかもしました。 実装するインフラ構成を図に落とし込む 理解が少し曖昧なところがありながらもなんとか、今回実装するインフラ構成を図に落とし込みました。 ここまででおおよそ3日です。 Webサーバはアクセスを考慮して、現時点では冗長構成にせず単一のインスタンスのみで運用を行い、batchサーバの役割も持たせます。 本番環境では、バウンスメール等はSES,SNSを使用し、通知を行いレートの管理をします。 他ネットワークからのアクセスも可能にするため、AWSClientVPNを使用したVPN接続も実装する予定です。 Excelにインフラ設計情報の詳細をドキュメント化 インフラ設計情報として、開発する上で必要になってくる細かな設定に関しては、都度Excelに記載し、後ほど忘れないためにドキュメント化し管理しました。 ・RDSの詳細設定 ・各インスタンスの詳細設定 ・pemキー情報、IAM情報、各種ログイン情報  ・ssh接続コマンドチートシート などなど 2. ネットワーク構築  ここから実際に手を動かしていきます。上記インフラ構成を実現するためにまずは、サーバを設置する箱となるネットワークの構築(VPC)から始めました。 ざっくり手順は以下のとおり実現しました。 1.VPC設定 2.VPC内にサブネット構築 3.インターネットゲートウェイの設置 4.NATゲートウェイの設置 5.VPC間のピアリング接続の設定 6.サブネットのルートテーブルの設定 具体的な設定についてここでは詳細省きますが、これでおおよそ必要なるネットワーク構成は出来あがります。 1つ注意点として、 VPCやサブネットを作成する際のIPv4CIDRブロックは、以下のプライベートIPアドレスの範囲内で作成する必要があります。 プライベートアドレスは以下のように範囲が定められており、この範囲外のIPアドレスはグローバルアドレスとなってしまうからです。 クラス 範囲 ネットワーク数 クラスA 10.0.0.0 ~ 10.255.255.255 (10.0.0.0/8) 1 クラスB 172.16.0.0 ~ 172.31.255.255 (172.16.0.0/12) 16 クラスC 192.168.0.0 ~ 192.168.255.255 (192.168.0.0/16) 256 パブリックサブネットとプラベートサブネット 自分も勉強していく中で、パブリックサブネットとプライベートサブネットってどう違うのと疑問に思っていました。 サブネット作成のコンソール画面でもパブリック/プライベートの選択などなかったはずです。 【結論】 サブネットのルートテーブルの記述内容で変わる パブリックサブネット すべてのIPアドレス宛のターゲットがインターネットゲートウェイに向いているもの。 送信先 ターゲット 0.0.0.0/0 インターネットゲートウェイ  プライベートサブネット すべてのIPアドレス宛のターゲットがNATゲートウェイに向いているもの。 プライベートサブネットがインターネットと通信するためには、NATゲートウェイのルートテーブルに0.0.0.0/0がインターネットゲートウェイに向かうように設定する必要があります。 送信先 ターゲット 0.0.0.0/0 NATゲートウェイ  3. サーバ・DB構築  サブネット内に必要となるEC2インスタンス、またはRDSを作成していきます。 (EC2,RDSの詳細な作成設定については記述を省略します) パブリックサブネットに配置するインスタンスには、インターネットに接続するため、ElasticIPを関連づける必要があります。 パブリックサブネットに配置したインスタンスは通常、パブリックIPv4アドレスが割り当てられます。 しかし、これだとインスタンスの起動停止のたびに毎回IPアドレスが変わってしまいます。 それでは大変なので、EIPを取得し、インスタンスと関連づけることでEIPを固定のパブリックIPアドレスとして使用することができます。 EC2インスタンス作成とNginxインストール 必要となるEC2インスタンスを作成します。 アプリケーションを動かすインスタンスには、RailsアプリケーションサーバであるPumaと通信するためのWebサーバ(Nginx)を事前にインストールします。 アプリケーションデプロイ後には、NginxとPumaを通信させる必要があります。 EIPの上限はデフォルトだと5個な話 インスタンスにEIPを割り当てるときに、画像のようなエラーがでてきました。 これはEIPの数が制限に達したという警告です。 EIPは、サポートプランにも寄りますが、Developerプランではデフォルトでは5個までしか使用できません。(デフォルトVPC内に2つ使用されているので、実質3個) 5個以上使用する場合には、Service Quatasのダッシュボードから申請を行う必要があります。 https://ap-northeast-1.console.aws.amazon.com/servicequotas なお、一気に20個とか申請すると、「申請する理由を詳しく教えてください」などとAWS側から怒られてしまうので、まずは5個ぐらいから始めると良いと思います。(※申請は全て英語です) 私は、デフォルトVPCに関連づいているEIP2個を外したのと、追加申請5個で足りました。 RDSのフェイルオーバー設定 RDSを作成していきます。 RDSの詳細設定に関しては、各自RDS使用用途によって異なるかと思うので、詳細は省きます。 障害時にはRDSを他AZで起動させるフェイルオーバーをさせる必要があります。 RDSのダッシュボードからサブネットグループの作成を行い、フェイルオーバーす先となるAZをグルーピングします。 フェイルオーバーの確認については、以下記事を参考にテストしてみました。 EC2-RDS間の疎通確認 アプリケーションが動く、EC2インスタンスからRDSへ接続ができることを確認します。 RDSのエンジンとしてMySQLを選択していた場合、EC2インスタンス上で以下コマンドでRDS内のMySQLと接続ができます。 RDSに設定しているセキュリグループのインバウンドルールに接続元EC2からのMySQL通信(3306番ポート)の許可を忘れずに! 接続元ec2 $ mysql -u マスターユーザー名 -pパスワード -h エンドポイント マスターユーザー名,パスワードは、それぞれRDS作成時に決定したものです。エンドポイントはRDS作成後に確認できます。 ALBの使用 一台のEC2ならロードバランサーは使用しなくても良いのでは思ったのですが、単なる負荷分散以外の機能もAWSのロードバランサーは提供してくれるので、そちらのメリットを加味してALBを使用します。 またALBには、Amazon Certificate Manager(ACM)からSSL証明書を取得し、HTTPSでの通信を設定します 4. Lambdaによる自動化 Lambda,EventBridge, CloudWatch, AWSChatBot, SNS このあたりのサービスを使用して開発する上で管理しやすくなる自動化を行いました。 Lambda✖︎EventBridgeによるEC2インスタンス起動・停止の自動化 開発ツール系のインスタンスや使用頻度の高いインスタンスについは起動・停止を自動化すると便利です。 ↓記事を参考に、実装しました。 環境ごとのEC2・RDSの停止忘れ防止 staging環境、production環境(運用開始まで)に属するインスタンス、RDSについては毎日↑のLambdaの停止スクリプトのみを実行し、停止忘れ防止策をこうじました。 AWS ChatbotによるSlack通知 Lambdaの実行結果は、CloudWatchLogsに蓄積されます。 インスタンスの停止スクリプトなどはエラー通知を即座に把握したいため、実務のコミュニケーションツールとして使用しているSlackに通知するように設定しました。 すごく便利!! 5. Amazon SES,SNS  本番環境や開発環境において、AWSからメールを送信するにはAmazon SimpleEmailService(SES)を使用します。 ここでもざっくりと手順を説明すると以下になります。 1.メール送信権限付きIAMユーザー作成後、アクセスキーを取得 2.Railsアプリコード内の ~アプリ名/config/environments/development.rb, staging.rb, production.rb配下にアクセスキーを記載 3.SESでドメイン、メールアドレスの認証 4.認証したドメイン・アドレスからのメール送信の確認 5.SNSでメール通知設定 メール送信権限付きユーザーのアクセスキーを取得 メール送信権限を持つIAMユーザーを作成します。(既存のユーザーでも大丈夫です) アタッチするポリシーは、AmazonSesSendingAccessというデフォルトポリシーがあるのでそれをユーザーにアタッチすれば大丈夫です。 AmazonSesSendingAccess { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "ses:SendRawEmail", "Resource": "*" } ] } テストメール送信 ドメイン・メールアドレスの認証を行います。 認証からテストメールの送信の仕方までは↓記事が参考になります。 ※ドメインに関しては、Route53のほうで事前に認証済みのドメインでないとSES上で認証されないので注意が必要です。(経験あり?) バウンスメールのテストについては、上記テストメールの送信先をbounce@simulator.amazonses.comに設定してあげると、バウンスレートを上げることなくバウンスメールのテストを行なってくれます。 その他、通常の成功メールや苦情メールも同様で↓に記載のテスト用アドレスを使用するとレートとは無関係にテストができます。 バウンスメール・苦情メール等については、SNSとSESを連携させて通知設定を行います。 これも↓記事が参考になるので、簡単に設定できます。 ここまでだいたい2週間ぐらいでした。 6. 開発環境構築(docker)  ローカルの開発環境と検証用のdevelopment環境はdockerで構築を行いました。 Dockerfile, docker-compose.ymlを作成し、シェルスクリプトとして開発環境構築用のスクリプトを用意しました。 このスクリプトを叩くだけで、dockerでの開発環境ができあがります。 合わせて、初期データとして必要なrakeタスクも実装しました。 ざっと内容を紹介 setup.sh #!/bin/bash # 作業用ディレクトリ作成 if [ ! -d ./workspace ]; then mkdir workspace fi # 作業用dir配下にプロジェクトをclone git clone git@~~ # キャッシュを使用せず、imageをbuild docker-compose build --no-cache # 必要な初期rakeタスクを流す docker-compose run web bundle exec rake rakeタスク # 停止済みのコンテナを削除 docker container prune -f ridgepole導入時にハマったこと 今回、アプリケーションはmigrationファイルでの管理ではなくridgepoleを使用します。 私自身初めてのridgepole導入だったので少し苦戦しました。 後ほどridgepole導入時にハマったことは詳細をQiitaに投稿しようと思っています。 ridgepoleとは↓・・・ 7. jenkins・phpmyadminなど開発ツール設定  開発ツール用のインスタンスにjenkins, phpmyadminをそれぞれインストールします。 それぞれのインスタンスには、EC2FullAccessなどのポリシーを持ったアクセスキーを配置する必要があります。 jenkinsインスタンス # 認証情報を設定 $ aws configure # 4つ聞かれるのでそれぞれ入力 1. アクセスキー 2. パスワード 3. region 4. output形式 # この配下に認証情報(アクセスキー等がある) $ cd ~/.aws $ ls -la => config, credentials jenkinsのジョブが失敗する アクセスキーの設置が終わり、jenkinsのジョブを作成し実行たところ、エラーになりました。。。 jenkisnのコンソール出力を見ると以下のようなエラーメッセージが unable to locate credentials. you can configure credentials by running "aws configure". おいおい、アクセスキーはちゃんと配置したはずだぞ? 設定を確認してもきちんと登録されているはず・・・ # アクセスキー設定情報を確認 aws configure list => aws_access_key_id = ***************** => aws_secret_access_key = ***************** 【結論】 jenkinsのジョブを実行しているのは、ec2の中のjenkinsユーザー だからjenkinsユーザーに認証情報を設定してあげる必要がある(これまでは、ec2ユーザーとrootユーザーに設定していた) # rootユーザーになる $ sudo su # jenkinsユーザーになる $ su - jenkins # 認証情報を確認すると・・・なにも設定されていなかった? $ aws configure list => aws_access_key_id => aws_secret_access_key # 再度認証情報を設定 $ aws configure 無事、ジョブが成功した! 余談ですが、画面上で作成したjenkinsのジョブはサーバ内jenkinsユーザーの以下に格納されているので、確認できます。 jenkinsインスタンス # rootユーザーになる sudo su # jenkinstユーザーになる su - jenkins cd workspace ls -la => 作成したjenkinsジョブ一覧が表示 8. VPN接続  別のネットワークから、この環境へのアクセスを可能にするためAWSClientVPNを使用し、VPN接続を試みます。 パブリックサブネット内のNATゲートウェイにアタッチしたEIPを固定IPアドレスとして、インターネットに接続できることをゴールとします。 ↓記事を参考にするとできるかと思います。 が、私がハマったことで一つ注意点! ClientVPVEndpointと関連づけるサブネットはプライベートサブネットでないといけない これに気づかず、数時間ハマりました。。。 また、VPN接続するときに作成されるENIは、接続のたびに新しいものが作成されます。 ENIに割り当てられるパブリックIPアドレスは接続のたびに毎回変わりますが、それをNATゲートウェイのEIPで固定化するというイメージです。 9. 起きた変化 以上作業を約3週間で行いました。 インフラにほとんど触ったことの無かった素人が3週間のインフラ構築を通して、どのような変化があったのか、技術的な側面と価値観・考え方の側面から感じたことを書いていきたいと思います。 技術面 【AWS】 ・AWSを使用して、迷わず基本的なアーキテクチャの設計ができるようになった ・AWS上のサービスをドキュメントを読むだけで使いこなせるようになった ・AWSリソースの管理能力が向上した 【docker】 ・dockerを使用して、開発環境の構築ができるようになった ・Dockerfile, docker-compose.ymlなど記述内容を理解し、自分の実装したいように改修できるようになった 【その他】 ・Webサーバやアプリケーションサーバのどのレイヤーに対してエラーが発生しているのか感覚的にわかるようになった ・ログを見る習慣/ログからエラーの原因を特定する力がついた ・Linuxサーバ内のディレクトリ構造や必要ファイルなどが把握でき、行いたい修正を加えられるようになった ・Webの3層構造を体系的に理解することができた ざっとこんな感じになります。 特に自分として変化を感じたことは、基本的なアーキテクチャ設計をする際のAWSリソースに対して「わからない・これどうやるんだ」という感情がなくなったことです。 今までは、事前知識としてなかったのはもちろんですがどこかAWSを気軽に触れないものだと思っていました。 ですが、毎日AWSを触りながら理解していくことで、AWSに対するハードルが低くなっていくのを強く感じました。 今では、初めて使用するAWS上のサービスでも「まずは触ってみる」ということを大切にし、ドキュメントや参考記事を読めばだいたいのことはなんとかなるようになったと思います。 わずか3週間ですが、自分のエンジニアとしての知識・技術が3週間前と比較し格段に上がったのを感じました! 価値観・考え方 インフラ構築を経験してみることで、インフラの楽しさを感じたのと同時に、改めて、AWSの素晴らしさに感動しました。 マネジメントコンソールをいじるだけでサーバ・ネットワークを作れたり、あらゆるサービスを連携することでできることの無限さを感じました。 インフラというのはどこか、モヤに隠れた分かりづらいものという意識があった中、自分で0から設計し図を書き、それを実際に構築するという経験のおかげで構造的にインフラを捉えられるようになりました。 AWSを理解するにはまず図を書いてみる とよく聞きますが、これは本当にそうだと思います。 図を書きながら、「これってどうやって通信してるの?」「この部分にはどういう制御が必要なんじゃないか」と頭の中で考え、それを実際に手を動かしてみることで必要な処理や構成が感覚的に理解できるようになってきます。 この経験をきっかけとして、これからもインフラに関する知識・技術を勉強し、今後はSAA,さらにはその上のSAPの取得を目指していきたいと思います。 そして、実務としてこのような素晴らしい挑戦の機会をいただけたことに感謝し、これからも成長できるように頑張っていきたいと思います。 10. おまけ 約3週間インフラ構築をする上で、ミドルウェアの操作についてはコマンドラインが主となるので、自然とコマンドを使う機会が多くなりました。 そこで備忘録がてら、よく使用したコマンドを残していきたいと思います。 よく使ったコマンドたち ■ コマンド # ファイル削除 rm -rf ディレクトリ # 検索 grep -r 正規表現 # プロセス確認 ps aux | grep XXX # 環境変数確認 env ■ systemctl # start, stop, restart, statusなど # Nginxの起動状態確認 systemctl nginx status # MySQLの起動状態確認 systemctl mysql status ■ crontab # サーバの初期設定をするcrontabの内容確認 crontab -l ■ Git # 接続先のremoteリポジトリを確認 git remote -v # remoteリポジトリを設定 git remote set-url origin git@~~~ ■ ssh ssh -i ~/.ssh/キー.pem ec2-user@IPアドレス
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

今更ながらDocker基礎をまとめてみた

はじめに コンテナ入門の勉強会資料を一部修正し、公開した記事です。 以下を目的としています。 コンテナを利用するメリットが理解できる状態 コンテナイメージの作成から実行までの一連の流れを理解できる…
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker-compose exec コマンド で OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "bash": executable file not found in $PATH

問題 docker-compose exec app bash としたら OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: "bash": executable file not found in $PATH というエラーが出た 解決法 bashではなく sh か /bin/sh を使う docker-compose exec app sh または docker-compose exec app /bin/sh
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel,MySQL】

概要 本記事は、PHPフレームワークLaravel入門 第2版で学習している中の疑問・つまづきの備忘録です。 今回はLaravelからDBへの接続エラーの原因と解決策についてまとめます。 なお、PHPフレームワークLaravel入門 第2版ではSQLiteを利用していますが、MySQLに読み替えて同じことを行なっています。 発生したエラー peopleテーブルのレコードを全件検索しようとした際に、下記のエラーが発生しました。 ソースコード HelloController.php /////////一部抜粋 use Illuminate\Support\Facades\DB; class HelloController extends Controller { public function index(Request $request) { $items = DB::select('select * from people'); ////ここでエラー発生 return view('hello.index', ['items' => $items,]); } PHPフレームワークLaravel入門 第2版 原因 .envに設定していたDB_HOSTが誤っていました。 #.env DB_CONNECTION=mysql DB_HOST=127.0.0.1 #######ここが原因 DB_PORT=3306 DB_DATABASE=laravel_practice DB_USERNAME=sail DB_PASSWORD=password なぜDB_HOST=127.0.0.1ではだめだったのか 127.0.0.1は、自分自身を指す特別なIPアドレスです。ここではLaravelが動いているホスト自身を指します。 しかしMySQLが動いているのは別のホストであり、そちらを指定してやる必要がありました。 MySQLのホストを調べるには、docker psコマンドを使用します。 出力結果のNAMESを確認すると、別コンテナで動いていることがわかります。 $ docker ps ////出力結果は一部抜粋 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 286f413a566c sail-8.1/app "start-container" 8 days ago Up 8 days 0.0.0.0:80->80/tcp, 8000/tcp laravel-practice_laravel.test_1 77c6251fe186 mysql/mysql-server:8.0 "/entrypoint.sh mysq…" 8 days ago Up 8 days (healthy) 0.0.0.0:3306->3306/tcp, 33060-33061/tcp laravel-practice_mysql_1 解決策 .envに設定していたDB_HOSTを修正します。 #.env DB_CONNECTION=mysql DB_HOST=laravel-practice_mysql_1 #######ここをMySQLが動くコンテナ名に修正 DB_PORT=3306 DB_DATABASE=laravel_practice DB_USERNAME=sail DB_PASSWORD=password ちなみに、DB_HOST=mysqlに設定することでも接続可能です。 コンテナを起動したら、アプリケーションの.envファイル内のDB_HOST環境変数をmysqlに設定することで、アプリケーション内のMySQLインスタンスに接続できます。 Laravel 8.x Laravel Sail 参考文献 PHPフレームワークLaravel入門 第2版 Laravel 8.x Laravel Sail
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel,MySQL】LaravelからDBへの接続エラーの原因と解決策

概要 本記事は、PHPフレームワークLaravel入門 第2版で学習している中の疑問・つまづきの備忘録です。 今回はLaravelからDBへの接続エラーの原因と解決策についてまとめます。 なお、PHPフレームワークLaravel入門 第2版ではSQLiteを利用していますが、MySQLに読み替えて同じことを行なっています。 発生したエラー peopleテーブルのレコードを全件検索しようとした際に、下記のエラーが発生しました。 ソースコード HelloController.php /////////一部抜粋 use Illuminate\Support\Facades\DB; class HelloController extends Controller { public function index(Request $request) { $items = DB::select('select * from people'); ////ここでエラー発生 return view('hello.index', ['items' => $items,]); } PHPフレームワークLaravel入門 第2版 原因 .envに設定していたDB_HOSTが誤っていました。 #.env DB_CONNECTION=mysql DB_HOST=127.0.0.1 #######ここが原因 DB_PORT=3306 DB_DATABASE=laravel_practice DB_USERNAME=sail DB_PASSWORD=password なぜDB_HOST=127.0.0.1ではだめだったのか 127.0.0.1は、自分自身を指す特別なIPアドレスです。ここではLaravelが動いているホスト自身を指します。 しかしMySQLが動いているのは別のホストであり、そちらを指定してやる必要がありました。 MySQLのホストを調べるには、docker psコマンドを使用します。 出力結果のNAMESを確認すると、別コンテナで動いていることがわかります。 $ docker ps ////出力結果は一部抜粋 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 286f413a566c sail-8.1/app "start-container" 8 days ago Up 8 days 0.0.0.0:80->80/tcp, 8000/tcp laravel-practice_laravel.test_1 77c6251fe186 mysql/mysql-server:8.0 "/entrypoint.sh mysq…" 8 days ago Up 8 days (healthy) 0.0.0.0:3306->3306/tcp, 33060-33061/tcp laravel-practice_mysql_1 解決策 .envに設定していたDB_HOSTを修正します。 #.env DB_CONNECTION=mysql DB_HOST=laravel-practice_mysql_1 #######ここをMySQLが動くコンテナ名に修正 DB_PORT=3306 DB_DATABASE=laravel_practice DB_USERNAME=sail DB_PASSWORD=password ちなみに、DB_HOST=mysqlに設定することでも接続可能です。 コンテナを起動したら、アプリケーションの.envファイル内のDB_HOST環境変数をmysqlに設定することで、アプリケーション内のMySQLインスタンスに接続できます。 Laravel 8.x Laravel Sail 参考文献 PHPフレームワークLaravel入門 第2版 Laravel 8.x Laravel Sail
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS App Runner と ECS それぞれで同じアプリをデプロイして比較してみる

こんにちは、Masuyama です。 別な記事では、AWS App Runner を使って手軽にコンテナ式アプリをデプロイできることを学びました。 その時は確かに少ないステップでデプロイできることは分かっていただけたと思うのですが、別なサービス (ECSとか) でデプロイする時との違いが分かりにくかったかもしれません。 そこで、App Runner と ECS のそれぞれで同じコンテナアプリをデプロイし、App Runner がどれだけ簡単かを分かっていただこうかと思います。 前回は Workshop に従いましたが、今回はPython の軽量 Web フレームワークの一つである Flask を App Runner でデプロイしてみましょう。 0. 前準備 (共通手順) App Runner 版、ECS 版のどちらでも共通となる コンテナイメージの準備 だけは共通手順として実施します。 0-1. ECR 作成 コンテナイメージをプッシュするためのリポジトリを先に用意しておきます。 [リポジトリを作成] を選択します。 リポジトリ名は今回は app-runner-flask-ecr と設定します。 リポジトリを作成できたら [プッシュコマンドを表示] を選択し、画面に表示される指示に従いローカル端末からイメージのビルド、プッシュコマンドを確認しておきます。 次の手順でサクッと Flask アプリコードを準備し、コンテナイメージを作成したらプッシュコマンドでイメージをリポジトリに上げていきます。 0-2. コンテナイメージ作成 続いて ECR にプッシュするためのコンテナイメージを作成しましょう。 apprunner-flask という名前のディレクトリを用意し、その中に以下の 2 つのファイルを用意します。 app.py requirements.txt それぞれのコードの中身はこのように記述します。 app.py FROM python:3.8 # 以降の RUN, CMD で使われる作業ディレクトリ WORKDIR /app # カレントディレクトリにある情報をコンテナ内の 「/app」 にコピー ADD . /app # requirements.txt に従いパッケージをインストール RUN pip install --trusted-host pypi.python.org -r requirements.txt # 待受ポート指定 EXPOSE 5000 # コンテナが起動時に実行するコマンド CMD ["python", "app.py"] requirements.txt Flask ここまで準備できたら、ECR にリポジトリを作成した時に確認したプッシュコマンドを apprunner-flask ディレクトリ内で実行していきます。 プッシュまで終わったら、リポジトリにイメージが上がっていることを確認しておきましょう。 なお、プッシュする前にローカルでコンテナを起動させて動作確認をする場合は以下のコマンドを実行します。 $ docker run -d -p 5000:5000 app-runner-flask-ecr その後、ブラウザで localhost:5000 にアクセスすると app.py 内で指定した文字が表示されているはずです。 1. App Runner 版 ではイメージを用意できたので、まずは App Runner でイメージから Flask アプリをデプロイしてみます。 1-1. サービス作成 細かい手順は 別な記事で紹介もしているので一部割愛しますが、App Runner ではサービスを作成するという手順だけで済んでしまいます。 なお、サービス作成時に指定する待ち受けポートは Dockerfile 内で指定した通り 5000 と設定しましょう。 ※Dockerfile より抜粋 ... EXPOSE 5000 ... 設定するところといったらこれぐらいです。早い。 さて、少し待ってデプロイが完了したら表示されるデフォルトドメインにアクセスしてみましょう。 Hello ~ が表示されていれば成功です。 2. ECS (Fargate) 版 次に、これまでコンテナアプリのデプロイに多く使われてきた ECS (Fargate) で Flask アプリをデプロイする手順を試してみましょう。 基本的にはできるだけデフォルト設定で簡単に済ませる方針です。 2-1. クラスター作成 ECS のコンソールから [クラスター]->[クラスターの作成] を選択します。 次にクラスターテンプレートを選択していきます。 今回はサクッと Fargate を使いたいので [ネットワーキングのみ] を選択します。 次はクラスターの設定です。 新しく VPC を作成するよう選択し、VPC とサブネットの CIDR を設定していきます。 2-2. ECS タスク定義作成 クラスターの作成が完了するまでの間、タスク定義も作成していきます。 ECSの管理画面から[タスク定義]→[新しいタスク定義の作成]を選択します。 起動タイプは前述の通り Fargate を選択します。 EC2 管理までマネージドになるのは素敵ですね。 さらに実行ロールやタスク定義名を設定しつつ、、、 タスクサイズも適当に、とりあえず最小要件で設定しつつ、、、 [コンテナの定義]->[コンテナの追加] から、先ほど ECR にアップロードしたイメージを指定します。 ここでも待受ポートは Dockerfile で指定した通り、5000 と設定するのを忘れないようにしましょう。 App Runner は 1 手順のみでデプロイできたので、ECS は既に工程が多いようにみえてきました…。 2-3. ECS サービス作成 さて、そろそろ先ほどのクラスターが作成完了しているはずなので、ここからサービスを立ち上げます。 クラスターの画面から [デプロイ] を選択し、サービス名などを適当に設定して進みます。 デプロイが始まり、しばらく待つとデプロイ完了です。 ではデプロイした Flask アプリにアクセスしてみます。 作成したクラスターの [タスク]->[ネットワーキング]->[パブリックIP] から Flask コンテナの IP を確認できるので、これにブラウザでアクセスします。 ポートマッピングで指定した :5000 を付けるのを忘れないようにしましょう。 無事にアクセスできましたね。 おわりに さて、いかがでしたでしょうか。 App Runner を簡単に見せたいという気持ちは少なからずあったかもしれませんが、それでも手順の数でいうと以下のように差があることが分かっていただけたかと思います。 (ECR へのイメージプッシュは共通手順のため除く) App Runner の場合 サービス作成 ECS (Fargate) の場合 クラスター作成 タスク定義作成 サービス作成 最小構成のデプロイでこの差でしたが、Auto Scaling 等、もう少し設定を付加するとなればもっと違ってくるはずです。 (ECS だと CloudWatch アラーム + Auto Scaling の設定が必要です。) App Runner はサービス作成時に、一緒にぽちぽちっとするだけで Auto Scaling まで気軽に設定できるので楽ちんです。 (参考:サービス作成時 or 作成後に赤枠内で設定するだけ) また、ECR 上に新しいイメージがプッシュされた時にも、App Runner では CI/CD 構成を自分で用意せずとも設定項目一つで自動デプロイまで出来ちゃいます。 もちろん細かく要件に合わせた設定にするにはまだまだ ECS や k8s が適任かもしれませんが、少なくとも小〜中規模開発においては、まさに 「開発に集中」 を実現できるサービスだと実感しました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

mac m1 dockerでエラーが起こるときに試したいこと

結論 dockerでパッケージをインストールしているときに、• directory not foundというエラーが出てしまったことがあった。 結論として、今まではarm64を使っていたのだが、m1からamd64を使用していることからコンテナ内のディレクトリ構成が変わってしまったことが原因であった。 そのため、Dockerfileを作成する際には以下のように記述をする必要がある。 # --platform=linux/x86_64 をつける FROM --platform=linux/x86_64 イメージ名
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む