20210607のAWSに関する記事は19件です。

KMSを用いて機密情報をプログラム実行時に復元する

センシティブなデータを暗号化してソースコードに埋め込む。 手順 暗号化文字列を作成する方法 import boto3 import base64 def main(): key_id = 'マスターキーのARN' Plaintext = '暗号化対象の文字列' response = kms.encrypt( KeyId = key_id, Plaintext = Plaintext )['CiphertextBlob'] Ciphertext = base64.b64encode(response).decode('utf-8') print('Ciphertext='+Ciphertext) if __name__ == '__main__': main() 上記処理で得られたCiphertextはKMSのマスターキーによって暗号化されている。 プログラム中で使用する場合は以下のようになる key_id = 'マスターキーのARN' Ciphertext = '上記処理で得られた暗号化文字列' response = kms_client.decrypt( CiphertextBlob=Ciphertext, KeyId=key_id ) plaintext = response['Plaintext'].decode('utf-8') KMSで暗号化しているため、仮にソースコードが流出したとしても、KMSのDecrypt API実行権限 がなければ機密情報が漏れる心配がなく安心ですね。 おわりに 暗号化文字列の複合化にKMSの権限が必要なため、プログラムを実行するサービスに対して 忘れずにKMS実行権限を付与しましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

別アカウントからS3にアップロードされたファイルの所有者を変更する(S3マウントツール使用)

はじめに 先日以下のような記事を書きました。 ・別アカウントからS3にアップロードされたファイルの所有者を変更する 記事の中では、コマンドを使用して別アカウントからアップロードされたファイルの所有者をS3バケットの所有者に変更する方法について試しました。 ただ、S3を使用する場合はコマンド以外にも方法はあります。 今回はS3マウントツールのCloudBerryDrive(MSP360)とJPCyberを使用して、別アカウントのEC2からファイルをアップロードした際の所有者変更方法について試してみます。 マウントツールについて知らない方は以下をご覧ください。 ・有名なS3マウントツール2つを比較してみた 準備 S3バケットのオブジェクト所有者は希望するバケット所有者にします。 前回作成したEC2にCloudBerryDriveとJPCyberをインストールします。 アカウントの設定も同時に行っておきます。権限はEC2にアタッチされている別アカウントのIAMロールのものを使用します。 インストール方法は過去の記事に書いています。 ・JPCYBER S3 Driveを使ってWindowsにS3をマウントしてみた ・CloudBerry Driveを使用してEC2にS3をマウントしてみた S3バケットは前回のものを流用します。 JPCyberで実施 事前確認 JPCyberで対象のS3バケットがマウントされていることを確認します。 エクスプローラーからファイルを配置します。 コンソールで確認するとファイルがアップロードされていることが確認できます。 ただ、このままでは別アカウントが所有者のままで設定されてしまっています。 ACL設定 JPCyberのツールからオプションの設定を選択します。 オプションの設定に「親バケットのアクセス権をアップロードするファイルに適用する」というチェックボックスがあるので、チェックをつけて保存します。 自動でJPCyberが再起動されるため、再度エクスプローラーからファイルをアップロードしてみます。 これで所有者が変更されるはず...と思ったのですが、何故か変わっていません。 JPCyberのログを見てみるとAccess Deniedが出ていることがわかりました。 アップロード自体は成功しているところを見るとACLを操作しようとして権限の問題で失敗しているように見えます。 2021/06/07 10:49:08.241 [W] Access Denied 2021/06/07 10:49:09.824 [I] Uploading: s3://test-tmp-20210603/test/test2.txt 2021/06/07 10:49:10.888 [I] Upload completed: s3://test-tmp-20210603/test/test2.txt 2021/06/07 10:49:10.935 [W] Access Denied 試しにとACLのチェックを外してアップロードしたらAccess Deniedは出なかったのでACLが関係していることは確定です。 2021/06/07 10:51:06.121 [I] Uploading: s3://test-tmp-20210603/test/test3.txt 2021/06/07 10:51:06.278 [I] Upload completed: s3://test-tmp-20210603/test/test3.txt 念のため同じIAMロールの権限使ってCLIでアップロードしてみます。 PS C:\Users\Administrator\Desktop> aws s3 cp .\test4.txt s3://test-tmp-20210603/test/ --acl bucket-owner-full-control upload: .\test4.txt to s3://test-tmp-20210603/test/test4.txt CLIでは問題なくアップロードされて所有者もS3バケットと一緒になっていることが確認できました。 ちょっと色々と調べてみましたが、答えが見つかりそうにないので一旦置いておきます。 もしエラー原因に心当たりある方がいればコメントください。 CloudBerry Driveで実施 事前確認 CloudBerry Driveでマウントされていることを確認します。 エクスプローラーからファイルをアップロードしてみます。 所有者はアップロード元のAWSアカウントのままです。 ACL設定 CloudBerry DriveでACL設定をしていくのですが、実はCloudBerry DriveにはACLの設定をする項目は存在しません。 その代わりCloudBerry Driveから送られるHTTPSリクエストに指定したヘッダーを追加することができます。 CLIのデバッグモードで「--acl bucket-owner-full-control」を実行するとわかりますが、そのオプションを指定した場合はリクエストのヘッダーに「'x-amz-acl': b'bucket-owner-full-control'」が追加されるため、これをCloudBerry Driveのリクエストヘッダーに追加します。 オプションを開いて、Addボタンを押下します。 Add Rule設定画面が表示されるので、上部の「Specify HTTP Headers」を選択します。 表示された画面でさらにAddボタンを押下して、先ほど記載した設定を追加しOKボタンを押下します。 以下の真ん中を選択してOKボタンを押します。 これで設定が完了しました。 さらにOKを押下するとマウントしなおすポップアップが出てくるので、OKを押しておきます。 別のファイルをアップロードしてみます。 S3コンソールから見ると所有者が変更されていました。 これでエクスプローラーからファイルをアップロードする際は、自動でACLの権限を追加することが可能になりました。 おわりに JPCyberの設定が上手くできていないので不完全燃焼感はありますが、CloudBerryDriveでは無事できたので良かったです。 どちらのツールにも言えることですが、アップロードする際にオブジェクトに対してアップロード以外の操作が実行されることになるため、大量のデータや大規模なサイズのデータを扱う場合には注意が必要です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

突然「このサイトにアクセスできません」と表示された際の対応

お名前.comにてドメイン取得していたサイトが突然表示されなくなったので、その際の対処法です。 結論としては、メール認証が上手くできていないことが原因でした。 突然のことだったので何が原因か分からなかったのですが、IPアドレスではサイト表示ができたのでドメイン関係であると推測できました。 まずはお名前.comでのDNS状態を確認します。 トップページから、ログイン→利用ドメイン一覧→情報ダウンロードにて確認します。 すると現在、利用制限されていることがわかりました。 この場合、メール認証する必要があるので、登録しているメールアドレスに届いているメールを探してください。 以下のタイトルのメールが届いているかと思うので、URLをクリックして認証させる必要があります。 これをクリックした後に完了のメールが届いているはずなので、確認しておいてください。 再度お名前.comから利用制限の状態を確認してみてください。 完了通知にも記載がありますが、インターネット全体に反映されるまで最大72時間程度かかる可能性があるみたいなので、その点は注意が必要です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

redashをECS Fargateに乗せて安定化を図りPythonで分析する環境を整えた話

はじめに 以前、社内のRedashをEC2からECSに移行してv5からv8にした話という記事を投稿しました。redashの古いバージョンを最新まで上げつつ、EC2からECS Fargateに載せ替えたという内容です。 当時の記事ではバージョンアップ方法や環境移行方法に焦点を当てていた為、今回はECS Fargateでredashを利用する方法、メリットについてまとめたいと思います。また、当社ではこの環境でPythonを用いており、記事の後半ではPython利用時の方法、ライブラリの扱いについても記載します。 1. 当社の環境 上図は当社のredash環境を簡単に表した図です。redashはFargateでホスティングしており、redash_serivceの中でredash_worker、redash_serverコンテナが稼働しています。redashのダッシュボードやクエリ等のメタデータを管理するPostgreSQLはRDS、キューイングを管理するRedisはElastiCacheと、それぞれマネージドサービスに逃しています。図では割愛していますが、別で設定したALBのターゲットグループにコンテナを指定して利用しています。 1.1 この環境のメリット 移行前はredash本体、Redis、PostgreSQLを全て同一のEC2インスタンスでホスティングしていました。加えてredashで参照する分析用DBのホスティングや同DBへのマスキング処理も同じインスタンスで行っていました。お察しの通り、たびたびリソースが逼迫してredashが機能しなくなっていました。 Fargate環境にしてからはそういった事は起きていません。redash_serviceを登録する際の必要タスク数を1としており、仮にコンテナが落ちても必要なタスク数に応じて起動し直してくれます。その際メタデータやキューイングデータはそれぞれマネージドサービスに逃しているので運用上問題はありません。いつredashが落ちるか分からない、落ちたら手動でどうにかしなければならない、といった悩みの種を駆逐できる恩恵は非常に大きなものだと言えます。 2. 環境の詳細 2.1 利用サービスの一覧 Secrets Manager ALB RDS ElastiCache ECS ECSで用いるredashコンテナは公式のイメージを利用し、バージョンは現行(2021年6月現在)の最新(redash/redash:8.0.0.b32245)です。 2.2 設定や注意点 2.2.1 Secrets Manager RDSやElastCacheの認証情報をSecrets Managerに保存しておき、ECSのタスク定義時に呼び出すことが可能です。変数入力欄に認証情報を直接記述しないで済みます。詳細は公式ドキュメントをご覧下さい。 2.2.2 ALB 利用する場合は事前に設定する必要がありますが、Route53を用いて任意のレコード名とredashを紐付けできたり、あれば何かと便利です。基本的には環境に応じて作成すれば問題ありませんが、手順4のルーティング設定ではターゲットの種類をIPで設定しましょう。ヘルスチェックは/pingで通ります。 当然ですが、VPC・サブネット・セキュリティグループ等の周辺も事前に設計、設定しておく必要があります。 2.2.3 RDS redashのメタデータを保存するDBとしてPostgreSQLインスタンスを作成する必要があります。環境にも依存するかとは思いますが、そこまで大きなインスタンスタイプにする必要はなさそうな印象です。スモールスタートして、リソースが足りなさそうであれば少しずつインスタンスタイプを変更していく運用で良いかと思います。IDやPWはECSタスク定義時に利用するので、控えておくかSecrets Managerに保存しましょう。 参考までに、当社のredashは以下の様な利用状況ですが、PostgreSQLインスタンスはdb.t3.mediumタイプで安定稼働しています。 2.2.4 ElastiCache Redisでクラスターを作成します。スモールスタートする場合は、まずは1台構成からでも良いかもしれません。デフォルトだと大きめのノードタイプが選択されているので注意が必要です。 2.2.5 ECS クラスター作成 利用するクラスターを作成します。Fargateを利用する場合はネットワーキングのみで問題ありません。 タスク定義 当社ではECSでredashを利用する場合のタスク定義を2種類用意しています。初回構築時のみ利用する、Redis及びPostgreSQLの利用準備を行うcreate_dbタスクとredash本体を稼働させるredashタスクです。 create_dbタスク create_dbタスクはredashのCLIで/app/manage.py database create_tablesコマンドを発行するタスクです。コマンドの内容はここに記載されており、この辺りに記述されているテーブルを作成するものと思われます。このコマンドはredashのdocker-composeなどでも呼び出されているものです。 タスク定義の詳細は次の通りです。 タスクメモリを0.5GiBに設定したところコンテナが落ちてしまったので3GiBとしています。 コンテナ定義は次の通りです。 環境の欄にcreate_dbを記入します。 環境変数の欄にREDASH_DATABASE_URL(PostgreSQLの認証情報)、REDASH_REDIS_URL(Redisの認証情報)を記入します。記法は以下の通りですが、Secrets Managerを利用する場合はそちらの値を記入します。記入を終えたら完成です。 postgresql://user:password@hostname:port/redash redis://hostname:port/0 タスク定義が完成したら、クラスターの新しいタスクの実行から実行しましょう。このタスクは初回構築時にのみ必要となるので、サービスとして実行する必要はありません。 redashタスク こちらはECSクラスターのサービスとして起動することになる、redash本体のタスクです。当社ではserverとworkerの2つのコンテナを立ち上げています。定義は次の通りです。 serverコンテナ コマンド欄にserverと記入。各種変数は環境に合わせて読み替えてください。 workerコンテナ コマンド欄にscheduler,workerと記入。各種変数は環境に合わせて読み替えてください。 こちらのタスクはサービスとして登録したいので、サービスの作成を押下して設定します。 サービスの設定時、パブリックIP割り当ての項目があります。ALB経由の設定を行う場合は必要はありませんが、redashが起動しているのか確認する等のデバッグ用であれば割り当てても良いかもしれません。 3. redash on ECS環境でPythonを使う redashでPythonを利用する為にはredash本体での設定とECSタスク定義での設定がそれぞれ必要です。ライブラリの導入も同じ手順で行います。 注意点 2021年6月現在、redash v8で利用できるのは既にサポートが終了したPython2系です。v9になるとPython3が利用できるようなので正式リリースが非常に楽しみです。 3.1 ECS側の設定 実施する設定は、redashでデータソースとしてPython自体を利用可能にするものと、Pythonで利用するライブラリをコンテナにインポートするものの2種類があります。 3.1.1 Pythonデータソースを利用可能にする タスク定義にて、server、workerそれぞれのコンテナの環境変数欄にREDASH_ADDITIONAL_QUERY_RUNNERSを追加し、redash.query_runner.pythonを記入します。 作成後はリビジョンを新しいものにしてタスクを再起動すればredashでPythonが利用可能になります。 3.1.2 Pythonライブラリを導入する 分析を行う中で利用したいライブラリが出てくる場合もあるかと思いますが、その場合もタスク定義にて導入することになります。 タスク定義のworkerコンテナのコマンド欄にて、worker、schedulerに続いてインストールコマンドを記入します。 記法はbash -c 'pip install [ライブラリ名]==[バージョン] --user'です。複数導入する場合はカンマを挟んで続けて記入します。上記はstatsmodelsのv0.9.0とscipyのv0.14を導入する例です。作成後はリビジョンを新しいものにしてタスクを再起動すれば記述したライブラリがコンテナにインストールされます。 3.2 redash側の設定 データソース設定からPythonを作成し、利用予定のライブラリをModules to import prior to running the scriptに記入します。AdditionalModulesPathsの欄には、インポート先である/home/redash/.local/lib/python2.7/site-packagesを記入します。 まとめ redash on ECS Fargate環境を構築する方法についてまとめました。この環境であればそこまでリソースに対して気を配る必要がなくなるため、運用上の負荷が軽減されます。後半ではpythonを利用する方法についてまとめました。redash上でPythonを利用できるようになると分析の幅が広がっていく為、非常に有用だと思います。反面、段々と増えていくダッシュボードやクエリの管理が煩雑になってしまう点については新たな課題となってしまいました。これに関しては良い解決策を発見次第、記事にまとめたいと考えています。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Amazon SageMakerとAzure MLにおける機械学習モデルのサービング技術比較: AWS/Azureが提供するコンテナイメージを利用する場合

初版: 2021年6月7日 著者: 橋本恭佑、柿田将幸, 株式会社 日立製作所 はじめに 本投稿では、学習済みの機械学習モデルをAWS/Azureが提供するコンテナイメージを用いてサービングする方法を紹介します。 Amazon SageMakerとAzure Machine Learning (Azure ML)で実際にサービングを試して分かった比較結果を解説します。 今回の検証では、AWS/Azureが提供するコンテナイメージをコンテナ基盤にデプロイするときに、必要なライブラリの追加またはバージョン変更を行い、作成済みの前処理・モデル・後処理を追加する方法(下記表1の赤枠部分)を紹介します。 表1: オンプレミス環境で学習させた機械学習モデルをAWSおよびMicrosoft Azure上でサービングする際のサービス一覧(2021年2月現在) 投稿一覧 AWSとMicrosoft Azureにおける機械学習モデルのサービング技術の概要 Amazon SageMakerとAzure MLにおける機械学習モデルのサービング技術比較: AWS/Azureが提供するコンテナイメージを利用する場合・・・本投稿 Amazon SageMakerとAzure MLにおける機械学習モデルのサービング技術比較: 自作コンテナイメージを利用する場合・・・近日公開 AWS FargateとAzure Container Instancesにおける機械学習モデルのサービング技術比較・・・近日公開 AWSとAzureにおけるパターン1の実現技術の違い パターン1を実現する場合の手順を図1に、パターン1を実現する場合に利用する技術をAWSとAzureで比較した結果を表2に示します。 Amazon SageMakerとAzure MLの双方で、機械学習モデルのサービングに必要なサービスが提供されておりますが、AWSには手順2を実現する手段がないことや、モデルのアップロード先が双方のパブリッククラウドで異なるなどの差があります。本稿ではこの手順に沿ってパターン1が実現可能かを実機検証した結果を紹介します。 図1: パブリッククラウドにおいてパターン1を実現する場合の手順 表2: AWSとAzureでパターン1を実現する場合に利用する技術の比較 項番 手順 AWSで手順を実現する技術 Microsoft Azureで手順を実現する技術 1 コンテナイメージを用意する 機械学習モデルの用途に合ったコンテナイメージをAmazon ECR(Elastic Container Registry)から検索する 機械学習モデルの用途に合ったコンテナイメージをAzure Container Registryから検索する 2 不足するライブラリがあれば追加する 対応技術なし(Amazon SageMakerのノートブックインスタンス上でライブラリを追加できない) Azure MLのノートブックインスタンス上でライブラリを追加する 3 モデルと前処理・モデル・後処理を呼び出す推論コードをアップロードする Amazon S3にモデルをアップロードし、Amazon SageMakerのノートブックインスタンス上に推論コードをアップロードする Azure MLのノートブックインスタンス上にモデルと推論コードをアップロードする 4 コンテナイメージ・モデル・推論コード・コンテナ基盤を指定しコンテナを立ち上げる Amazon SageMakerのノートブックインスタンス上でコンテナイメージ・モデル・推論コード・コンテナ基盤を指定する Azure MLのノートブックインスタンス上でコンテナイメージ・モデル・推論コード・コンテナ基盤を指定する 本検証の事前準備 本投稿では、以前の投稿の検証シナリオで作成した下記ファイルを利用しますので、事前に用意してください。 学習済みの手書き文字認識モデル: model.joblib 前処理・モデル利用・後処理を記載したスクリプト: api_server.py Amazon SageMakerでサービングする場合 学習済みの手書き文字認識モデルを、AWSが提供するコンテナイメージでサービングできるか検証しました。 Amazon SageMakerのノートブックインスタンスを起動して、作成した機械学習モデルをノートブックインスタンスへアップロードします。 それから、下記コードをノートブックインスタンス上のJupyter Notebookから実行すると、ノートブックインスタンスからS3ストレージへ機械学習モデルをアップロードできます。下記コードを利用せずに機械学習モデルを直接S3ストレージへアップロードしても問題ありません。 from sagemaker import get_execution_role from sagemaker.sklearn.model import SKLearnModel import sagemaker sagemaker_session = sagemaker.Session() role = get_execution_role() # ノートブックインスタンスを起動したときにアタッチされるS3ストレージのimportディレクトリへ、 # 機械学習モデルをアップロードする uploaded_model = sagemaker_session.upload_data(path='model.tar.gz', key_prefix='import') print(uploaded_model) # 実行後、機械学習モデルがアップロードされたディレクトリ名を確認できる。例えば下記など。 # 's3://sagemaker-ap-northeast-1-aws_account_id/import/model.tar.gz' 次に、以前の投稿に示した推論用のソースコード(api_server.py)を、SageMakerで実行可能な形式に書き換えます。下記に書き換え後のソースコード(entry.py)を示します。 entry.py import os import joblib from sklearn.neural_network import MLPClassifier import numpy as np import sys from six import BytesIO from PIL import Image def model_fn(model_dir): clf = joblib.load(os.path.join(model_dir, "model.joblib")) return clf def _npy_loads(data): """ Deserializes npy-formatted bytes into a numpy array """ stream = BytesIO(data) return np.load(stream) def input_fn(request_body, request_content_type): if request_content_type == 'application/x-npy': return _npy_loads(request_body) elif request_content_type == "image/jpeg": image_number = np.array(Image.open(request_body).convert('L')) array = [image_number.reshape(RESHAPED)] return array else: raise ValueError('Content type must be application/x-npy') def predict_fn(input_data, model): prediction = model.predict(input_data) return prediction[0] ここでポイントとなるのは以下の2点です。 1. 機械学習モデルのロード、入力データの変換、推論をmodel_fn, input_fn, predict_fnと各々で別の関数に分けて書きます。前処理はinput_fn、後処理はpredict_fnへ記入することで、それぞれ前処理と後処理を推論コンテナへ実装できます。元のapi_server.pyから書き換える必要があります。 2. input_fn関数の引数に、入力データ(request_body)だけでなく、入力データで受け付けるデータの形式(request_content_type)をとります。そして、入力データで受け付けるデータの形式(たとえばapplication/x-npyやimage/jpeg)ごとに前処理を書きます。こうすることで、サービングした後の機械学習モデルが複数の形式の入力データを受け付けることが可能となります。 次に、AWSが提供するコンテナイメージの中から、今回サービングに利用するOSSの入ったコンテナイメージをデプロイします。AWSではApache MXNet、tensorflow、scikit-learnなど複数の機械学習向けOSSについて、推論環境に利用できるコンテナイメージを提供しています。今回はscikit-learnのサービング用コンテナを利用しました。前処置・後処理・モデルを内包するscikit-learnの入った推論用コンテナをデプロイするには、ノートブックインスタンス上のJupyter Notebookから下記のコードを実行し、上記の推論用コード(entry.py)・モデルのファイル・scikit-learnのバージョンを指定します。 from sagemaker.sklearn.model import SKLearnModel # SKLearnModelのコンテナイメージを呼び出して、推論用コードを内包させる sklearn_model = SKLearnModel(model_data='s3://sagemaker-ap-northeast-1-aws_account_id/import/model.tar.gz', role=role, entry_point="./entry.py", framework_version="0.20.0") # 推論用コードを内包させたコンテナイメージをデプロイする predictor = sklearn_model.deploy(instance_type="ml.c4.xlarge", initial_instance_count=1) これでサービング処理の記述は完了です。推論コンテナのデプロイが成功するかを確認します。 # CloudWatchから確認できるログ Traceback (most recent call last): File "/miniconda3/lib/python3.7/site-packages/sagemaker_containers/_modules.py", line 258, in import_module module = importlib.import_module(name) File "/miniconda3/lib/python3.7/importlib/__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 1006, in _gcd_import File "<frozen importlib._bootstrap>", line 983, in _find_and_load File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 677, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 728, in exec_module File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed File "/miniconda3/lib/python3.7/site-packages/fail_entry.py", line 7, in <module> from PIL import Image ModuleNotFoundError: No module named 'PIL' 上記の実行結果より、AWSが提供するコンテナイメージには画像の前処理に用いるPillowのライブラリが含まれず、処理が完了していないことがわかります。エラーを解消するために、Pillowと同等の機能を持つ画像処理ライブラリ(例えばscikit-image)を利用可能か試しましたが、2021年2月現在、AWSの提供するscikit-learn用イメージコンテナには、scikit-imageなどの画像処理ライブラリが含まれていませんでした。 また、コンテナイメージをコンテナ基盤にデプロイするときに、pip, condaといったパッケージ管理ツールやAWSが提供するAPIを用いて必要なライブラリ(今回の場合は例えばpillowなど)を追加できるかを調査しましたが、2021年2?現在、そのような機能は提供されていませんでした。 以上の検証から、AWSにおいて「パブリッククラウドが提供するコンテナイメージをコンテナ基盤にデプロイするときに、ライブラリの追加またはバージョン変更して、前処理・モデル・後処理を追加する」ことはできないことがわかりました。 Azure MLでサービングする場合 学習済みの手書き文字認識モデルを、Azureが提供するコンテナイメージでサービングできるか検証しました。 Azure Machine Learningのノートブックインスタンスを起動し、ノートブックインスタンスへ作成した機械学習モデルをアップロードします。アップロードはOSSのJupyterと同様に、ブラウザからアップロード可能でした。 次に、前回の投稿に示した推論用のソースコード(api_server.py)を、Azure Machine Learningで実行可能な形式に書き換えます。下記に書き換え後のソースコード(score.py)を示します。 score.py import os import joblib import numpy as np import json from PIL import Image from io import BytesIO import base64 # 推論開始時に呼び出される関数 def init(): global model # モデルのPATHを指定 model_path = os.path.join(os.getenv('AZUREML_MODEL_DIR'), 'model3.joblib') # モデルをロード model = joblib.load(model_path) # 推論リクエストがあった場合の処理を定義 def run(raw_data): try: # JSONリクエストのtext propertyを抽出 data = json.loads(raw_data)['data'] img = base64.b64decode(data) # base64に変換された画像データを元のバイナリデータに変換 img = BytesIO(img) # _io.BytesIO pillowで扱えるように変換 RESHAPED = 784 image_number = np.array(Image.open(img).convert('L')) array = [image_number.reshape(RESHAPED)] prediction = model.predict(array) # このpredictionの型はarray型 return str(prediction[0]) # array型がJSONでシリアライズ不可のためstr型に変換 except Exception as e: error = str(e) print('array: {0}'.format(array)) return json.dumps({"error": error}) ここでポイントとなるのは以下の2点です。 1. 機械学習モデルのロードと、入力データの変換・推論をinit, runの2つの関数に分けて書きます。Amazon SageMakerにおける書き換え時と比べて、前処理や後処理を厳密に分ける必要がないといえます。 2. Azure MLの仕様上、run関数の入力データ(raw_data)および出力データの型はJSONのみです。したがって、今回の様に画像を扱う場合は、JSON化された入力データに含まれる画像のデータを読み込む前処理が必要です。同様に、推論結果もJSONでシリアライズ可能な形式で出力する必要があります。 次に、推論用のスクリプト(score.py)と、推論に利用するライブラリ群を紐づけた、前処理・後処理・ライブラリに関連するコンフィグ(inference_config)のオブジェクトを生成します。 Azure Machine Learningのノートブックインスタンス上のJupyter Notebookで下記のコードを実行します。 from azureml.core import Workspace from azureml.core.model import Model from azureml.core import Workspace, Environment ws = Workspace.from_config() from azureml.core.model import InferenceConfig from azureml.core.environment import Environment from azureml.core.conda_dependencies import CondaDependencies myenv = Environment(name="oss_center") conda_dep = CondaDependencies() # 推論に利用するライブラリ群を定義 conda_dep.add_conda_package("numpy") conda_dep.add_conda_package("scikit-learn") conda_dep.add_conda_package("pillow") conda_dep.add_conda_package("joblib") myenv.python.conda_dependencies=conda_dep # 推論用のスクリプトと推論に利用するOSS群を紐づけた、前処理・後処理・ライブラリに関連するコンフィグ(inference_config)のオブジェクトを生成 inference_config = InferenceConfig(entry_script="score.py", environment=myenv) 最後に、推論のためのコンテナのスペックのコンフィグ(deployment_config)のオブジェクトを生成し、上記で生成した、前処理・後処理・ライブラリに関連するコンフィグ(inference_config)と紐づけて、推論のためのコンテナをデプロイします。 Azure Machine Learningのノートブックインスタンス上のJupyter notebookから下記のコードを実行すると、推論のためのコンテナをAzure Container Instancesで起動できます。 from azureml.core.webservice import AciWebservice, Webservice # 推論のためのコンテナのスペックのコンフィグのオブジェクトを生成 deployment_config = AciWebservice.deploy_configuration(cpu_cores = 1, memory_gb = 1) model = Model(ws, name='hash-mnist') # 前処理・後処理・ライブラリに関連するコンフィグ(inference_config)とdeploymet_configを紐づけて、推論のためのコンテナをデプロイ service = Model.deploy(ws, 'hash-mnist-first', [model], inference_config, deployment_config) service.wait_for_deployment(True) print(service.state) print("scoring URI: " + service.scoring_uri) # 下記の様な出力が得られることを確認する # Running............................................................................ # Succeeded # ACI service creation operation finished, operation "Succeeded" # Healthy # scoring URI: http://7032b584-13d9-4bcb-a096-9fe0d9dc4941.japaneast.azurecontainer.io/score これでサービング完了です。推論が成功するかを確認します。SageMakerの場合と違って、画像送信時に画像をJSONで送信可能な型に変換する必要があることに注意してください。 import requests from PIL import Image import json import base64 from io import BytesIO img = Image.open("x_test_50.jpeg") # PillowImageをbytesに変換してさらにbase64に変換 buffered = BytesIO() img.save(buffered, format="JPEG") img_byte = buffered.getvalue() # bytes img_base64 = base64.b64encode(img_byte) # base64でエンコードされたbytes ※strではない # まだbytesなのでjson.dumpsするためにstrに変換(jsonの要素はbytes型に対応していないため) img_str = img_base64.decode('utf-8') # str files = { "data":img_str } response = requests.post( 'http://7032b584-13d9-4bcb-a096-9fe0d9dc4941.japaneast.azurecontainer.io/score', json.dumps(files), headers={'Content-Type': 'application/json'}) pprint.pprint(response.json()) # ‘6’と返答が返ってくることを確認する これで動作検証が完了し、サービングが成功したことを確認できました。以上の検証結果から、Azure Machine Learningでは「パブリッククラウドが提供するコンテナイメージをコンテナ基盤にデプロイするときに、ライブラリの追加またはバージョン変更して、前処理・モデル・後処理を追加する」が可能であることを確認できました。 検証結果の考察 パブリッククラウドの提供する汎用のコンテナイメージに推論に必要なライブラリが含まれない場合も、後からライブラリの追加が可能であることから、「パブリッククラウドが提供するコンテナイメージをコンテナ基盤にデプロイするときに、ライブラリの追加またはバージョン変更して、前処理・モデル・後処理を追加する」パターンの実現手段はAzure Machine Learningに限られることがわかりました。この手法は、初回デプロイ時に追加で開発する項目が最も少なく、モデル訓練後からランタイム作成までのリードタイムを短くできるため、顧客にすぐに推論システムを提供する必要あるユースケースに最も適しています。 おわりに 次回の投稿において、パブリッククラウドが提供するコンテナイメージを利用せず、オンプレミス環境で必要なライブラリを含んだコンテナイメージを作成して、コンテナ基盤へデプロイするときに、前処理・モデル・後処理を追加する手法について、Amazon SageMakerとAzure Machine Learningで実証した結果を解説します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AppSync】Cloudフィールド

Amplify+AppSyncの話です。 AppSyncのフィールドにリゾルバを付けるに当たって、少し詰まったので備忘録 Amplify pushでAPIを作っていく際、amplify/backend/api/appName/stacks配下にCloudFormationのテンプレートを置かないと、AppSyncのコンソールからリゾルバを追加してもAmplify pushを叩いたタイミングで消えちゃいます。 なんでamplify/backend/api/appName/stacks配下にリゾルバ追加用のテンプレートを追加することに 下記のようなスキーマのhogeにリゾルバを当てたい場合、 type Sample @model{ hoge: String! huga: String!#こいつにリゾルバを当てたい hogehoge: String! } CloudFormationのテンプレートで追加する場合、"Resources"の下に下記を追加することで Amplify push時にリゾルバを追加することができました。 "hugaField": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { "Ref": "AppSyncApiId" }, "DataSourceName": "SampleTable", "TypeName": "Sample", "FieldName": "huga", "RequestMappingTemplateS3Location":"リクエストマッピングテンプレート", "ResponseMappingTemplateS3Location":"レスポンスマッピングテンプレート" } } CloudFormationからQueryにリゾルバを付与するやり方を考えてみれば当たり前のことでした。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AppSync】Amplify push でAppSyncのフィールドにリゾルバを追加する

Amplify+AppSyncの話です。 AppSyncのフィールドにリゾルバを付けるに当たって、少し詰まったので備忘録 Amplify pushでAPIを作っていく際、amplify/backend/api/appName/stacks配下にCloudFormationのテンプレートを置かないと、AppSyncのコンソールからリゾルバを追加してもAmplify pushを叩いたタイミングで消えちゃいます。 なんでamplify/backend/api/appName/stacks配下にリゾルバ追加用のテンプレートを追加することに 下記のようなスキーマのhogeにリゾルバを当てたい場合、 type Sample @model{ hoge: String! huga: String!#こいつにリゾルバを当てたい hogehoge: String! } CloudFormationのテンプレートで追加する場合、"Resources"の下に下記を追加することで Amplify push時にリゾルバを追加することができました。 "hugaField": { "Type": "AWS::AppSync::Resolver", "Properties": { "ApiId": { "Ref": "AppSyncApiId" }, "DataSourceName": "SampleTable", "TypeName": "Sample", "FieldName": "huga", "RequestMappingTemplateS3Location":"リクエストマッピングテンプレート", "ResponseMappingTemplateS3Location":"レスポンスマッピングテンプレート" } } CloudFormationからQueryにリゾルバを付与するやり方を考えてみれば当たり前のことでした。。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

S3にファイルがアップされたらLambdaでメール通知する設定

S3にファイルがアップされたらLambdaでメール通知する設定 この手の設定記事も何番煎じか分かりませんが、今回はついにAWS Lambdaに手を出していこうと思います。PythonとかRubyとか全然できないので敬遠してたんですけど、諸事情あり勉強がてらの記事投稿です。 今回のゴール S3の特定バケットに特定の拡張子ファイル(今回はテキストファイル)をアップロードしたら、Lambdaでpythonコードが実行されからAmazon SNS経由でメール通知が届く。 以下イメージです。 設定項目 ①テキストファイルを格納するS3バケットとサブフォルダを作成 ②Lambda関数を作成 ③LambdaにIAMロールを設定 ④Lambda関数のトリガーを設定 ⑤SNSのトピック、サブスクリプションを作成、設定 ⑥Lambda関数にpythonコードを記述 実際にやってみた まず①ですがLambdaを設定するリージョンと同じリージョンを選択して、S3バケットを作成します。基本的にデフォルトの設定のままでOKです。 続いて②のLambda関数を作成ですが、AWSが用意したテンプレートを使うこともできますが今回は「一から作成」で進めていきます。 「関数名」は任意の名前を、「ランタイム」はPython3.7を選択します。 実行ロールは「基本的なLambdaアクセス権限で新しいロールを作成」を選択します。 これでいったんLambda関数の作成は完了です。 次に③のLambdaにIAMロールを設定を行います。 Lambdaは作成された時点の状態では以下の図のようにCloudWatch Logs以外のアクセス権がついていない状態となります。 今回のゴールが「Amazon SNS でメール通知する」のため、LambdaからSNSを呼びさせるようにアクセス権を設定する必要があります。 アクセス権の設定方法については色々なやり方があると思いますが今回は単純に「新しく作成されたロール」にSNSのアクセス権をアタッチするやり方で進めます。 「リソースの概要」の上部に「実行ロール」の表示があり、LambdaにアタッチされたIAMロールが表示されています。このロールにSNSの権限を以下のようにアタッチします。※別にFullAccessでなくてもOKです これでIAMロールの設定はOKです。 次に④Lambda関数のトリガーを設定をしていきます。トリガーとは、Lambdaが実行されるきっかけとなるアクションのことです。今回の場合、「トリガー」はS3バケットへのデータアップロードになります。 トリガーの設定は下図の赤丸の「トリガーを追加」をクリックして、以下のように選択していきます。 トリガーはS3 バケットは①で作成したバケットを指定 イベントタイプはとりあえず今回は「全てのオブジェクト作成イベント」を選択 プレフィックスはサブフォルダを指定   ※「サブフォルダ/」 という形で最後の”/”も記載する サフィックスは今回はテキストファイルなので、「.txt」を選択 これでトリガーの設定は完了です。S3の指定バケットのテキストファイルがアップロードされたらLamdba関数が実行されるようになります。 続いて⑤Amazon SNSのトピック、サブスクリプションを作成、設定をしていきます。Lambdaが呼び出すSNSを設定し、通知ができるように有効化しておきます。 下図の赤い丸の「トピック」をクリックし、まずトピックを作成します。 「名前」、「表示オプション」は任意のものでOKです。 トピックが作成されるとARNが発行されますので、ARNをメモしておいてください。Pythonコードに後ほど埋め込んで使います。 次にサブスクリプションを設定していきます。下図の赤い丸の「サブスクリプションの作成」をクリックします。 プロトコルは「Eメール」を選択し、エンドポイントに「通知したいメールアドレス」を設定します。 サブスクリプションを作成するとエンドポイントで設定したメールアドレスに確認依頼のようなメールが届きます。そのメールの「Confirm subscription」をクリックすると確認OKになり、以下ような画面が表示されます。 これでSNSの準備が整いました。 最後にいよいよ⑥のLambda関数にコード記載していきます。lambda_function.py となっているファイルをダブルクリックして、既存コードを削除して以下のコードを記述してください。コードを書く場所やlambda_function.py の場所は下図参照。 import boto3 def lambda_handler(event, context): client = boto3.client('sns') TOPIC_ARN = 'Amazon SNSのトピックのARNを記載' msg = 'Pythonからのテストメール\nS3バケットにファイルがアップロードされました' subject = 'S3バケットを確認してください!!!!' response = client.publish( TopicArn = TOPIC_ARN, Message = msg, Subject = subject ) return response エラー表示がでなければ、「Deploy」をクリックして、コードを保存してください。 もし、Lambda関数に適切なアクセス権が設定されていない場合はテスト実行すると以下のようなResponse結果になります。IAMロールにアタッチしているポリシーを見直しましょう。 あとは該当のS3バケットのサブフォルダにテキストファイルをアップロードしてみましょう。 こんな感じでメール通知が出ると思います。 注意点 トリガーを追加する画面の右側に「送信先を追加」というものがあり、ここにAmazon SNSを設定するのかな?...と思って設定すると通知は来るのですが、タイムスタンプやリクエストIDなどの情報が羅列されたものが通知されるだけでした。 トリガーの設定で、プレフィックスにサブフォルダを記載しますが、サブフォルダ名の最後に"/"をつけ忘れると上手く動作しなくなります。 またサブフォルダを作成せず、バケット直下を指定してしてもなぜか上手く動作しませんので、適当にバケット下にサブフォルダを作成してください。 参考にしたサイト https://qiita.com/tsumita7 https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-s3-example.html https://blog.smtps.jp/entry/2019/10/04/171230 https://recipe.kc-cloud.jp/archives/10035 https://dev.classmethod.jp/articles/lambda-my-first-step/ https://nopipi.hatenablog.com/entry/2019/01/11/224201 終わりに Lambda が使えるようになると一気にAWSで構築できる内容が広がります。何より楽しくなってきます(笑) pythonをそこそこ書けるようになったら、さぞ楽しいことでしょう...精進しますm(_ _)m
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ECSタスクスケジュールでmysqldumpを日次で実行してs3にアップ

はじめに EC2上でcron実行していたMySQLのdump実行環境をECSに移行しました。 ecsのタスクスケジュールでdumpを取得してS3にアップした備忘録になります。 利用するコンテナイメージ 作業の流れ MySQLコンテナイメージの作成とAWSCLIのインストール コンテナのbuildとイメージをECRにpush パラメータストアにDBのアクセス情報を登録 ECSタスク定義に登録 タスクスケジュールに登録 1. MySQLコンテナイメージの作成とAWSCLIのインストール 公式からcloneして、既存のDockerfileとdocker-entrypoint.sh を書き換えます MySQL+AWSCLIを入れます。 $ git clone https://github.com/mysql/mysql-docker.git $ cd mysql-docker/mysql-server/8.0 $ vim Dockerfile 以下に置き換えます。 FROM oraclelinux:7-slim ARG MYSQL_SERVER_PACKAGE=mysql-community-server-minimal-8.0.20 ARG MYSQL_SHELL_PACKAGE=mysql-shell-8.0.20 # Install server RUN yum install -y https://repo.mysql.com/mysql-community-minimal-release-el7.rpm \ https://repo.mysql.com/mysql-community-release-el7.rpm \ && yum-config-manager --enable mysql80-server-minimal \ && yum install -y \ $MYSQL_SERVER_PACKAGE \ $MYSQL_SHELL_PACKAGE \ libpwquality \ && yum clean all \ && mkdir /docker-entrypoint-initdb.d #AWSCLI install RUN yum install -y python3 RUN pip3 install awscli VOLUME /var/lib/mysql COPY docker-entrypoint.sh /docker-entrypoint.sh CMD ["/docker-entrypoint.sh"] docker-entrypoint.sh を以下に書き換えます。 BACKUP_DB_PASSWORD DB_USER DB_HOST BACKUP_S3_BUCKET については、後にパラメータストア等で定義します。 $ vim docker-entrypoint.sh docker-entrypoint.sh #!/bin/bash BACKUP_DIR='/tmp' DATE=$(date '+%Y%m%d') mysqldump -R -u${DB_USER} -p${BACKUP_DB_PASSWORD} -h ${DB_HOST} --single-transaction --all-databases --default-character-set=binary --set-gtid-purged=off --skip-column-statistics | gzip > ${BACKUP_DIR}/${DATE}_mysqldump.sql.gz aws s3 cp ${BACKUP_DIR}/${DATE}_mysqldump.sql.gz $BACKUP_S3_BUCKET/${DATE}_mysqldump.sql.gz --region ap-northeast-1 2. コンテナのbuildとイメージをECRにpush ECRにログインします。 xxxxの部分は自AWS環境のECRレポジトリに置き換えてください。 $ aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com buildとpushを行います。 $ docker build --no-cache -t dbbackup-task . $ docker tag dbbackup-task:latest xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/dbbackup-task:latest $ docker push xxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/dbbackup-task:latest 3.パラメータストアにDBのアクセス情報を登録 BACKUP_DB_PASSWORD BACKUP_S3_BUCKET DB_USER DB_HOST docker-entrypoint.shで変数に設定した上記をパラメータストアに登録します。 passwordについてはSecureStringで、その他はStringで良いと思います。 このあたりの登録方法は割愛します。 4.ECSタスク定義に登録 先程作成したコンテナイメージをタスク定義に登録します。 タスク定義の登録方法は通常の作成方法と同じですので割愛します。 以下に変更ポイントを記載します。 ・コンテナイメージの指定 先程ECRにアップしたコンテナイメージを設定します。 ・DBへのアクセス情報をパラメータストアに登録したと思うのですが、そちらを環境変数に登録します。 Valueについては右側の黒い部分にテキストを入力します。 ValueFromは先程設定したパラメータストアの名前を入力します。 ・タスクのソフトメモリ制限を設定 DBの容量によってはメモリを消費する可能性があるので増やします。 512MBもあれば十分かと思います。 ・タスク実行ロール、タスクロールの設定 コンテナ内から、パラメータストア、S3にアクセスできるように、 タスク定義内のタスク実行ロール及びタスクロールの設定を行います 5.タスクスケジュールに登録 タスクスケジュールを利用するために登録をします。 実行したいクラスタを選択して、図のようにタスクのスケジューリングを選択して、作成をクリックします。 この画面になります。 名前を入力 スケジュールルールタイプでcronを選択 cronの時間はUTCで指定します。 ターゲットIDは名前と同様で良いと思います。 タスク定義は先程作成したタスク定義を指定して、リビジョンも選択します。 タスクの数は1で。 最後に環境変数の上書き設定を行います。 ▼をクリックすると環境変数の上書きができます。 先程設定したBACKUP_S3_BUCKETを変更したい場合は変更先のバケットのURLを記載します。 以上で設定完了です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Athena】enumを使ったパーティショニング

概要 AWSのAthenaで、Partition Projectionを使ってパーティショニングする際のenumを使った例をまとめます。 enumは、連続しておらず、列挙可能なカーディナリティが低いカラムに適しています。 今回はinstance_idに対して適用しています。 S3バケットの構成は以下の通りです。 s3://backet-name/object-name/instance_id/year=yyyy/month=mm/day=dd/ 例: s3://backet-name/object-name/i-xxxxxxxxxxxxxxxxx/year=2021/month=5/day=1/ instance_id, year, month, dayでパーティショニングします。 テーブル作成の構文 テーブル作成の構文の例は以下の通りです。 PARTITIONED BYとTBLPROPERTIESにパーティショニングに必要な情報です。 TBLPROPERTIESの中身は参考記事に記載されています。 CREATE EXTERNAL TABLE `table_name`( `foo` string, `bar` string ) PARTITIONED BY (instance_id string, year int, month int, day int) ROW FORMAT DELIMITED FIELDS TERMINATED BY ' ' MAP KEYS TERMINATED BY 'undefined' LOCATION 's3://backet-name/object-name/' TBLPROPERTIES ( 'projection.enabled' = 'true', 'projection.instance_id.type'='enum', 'projection.instance_id.values'='i-xxxxxxxxxxxxxxxxx,i-yyyyyyyyyyyyyyyyy', 'projection.year.type' = 'integer', 'projection.year.range' = '2021,2030', 'projection.year.interval' = '1', 'projection.month.type' = 'integer', 'projection.month.range' = '1,12', 'projection.month.interval' = '1', 'projection.day.type' = 'integer', 'projection.day.range' = '1,31', 'projection.day.interval' = '1', 'storage.location.template' = 's3://backet-name/object-name/${instance_id}/year=${year}/month=${month}/day=${day}/' ) 参考URL
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

作りながら覚えるTerraform入門(3) - VPC編

作りながら覚えるTerraform入門シリーズの第3回です。 第2回の基本編ではとりあえずVPCを作成してみましたが、今回はサブネットやルートテーブルなど含めて改めてVPC関連のリソースを作成してみましょう。 作りながら覚えるTerraform入門シリーズ インストールと初期設定 基本編 VPC編 => 今回はコチラ EC2編(準備中) Route53 + ACM編(準備中) ELB編(準備中) RDS編(準備中) 今回の学習ポイントは以下です。 変数の使い方 リソース間の参照方法 関数の使い方 Stateファイルの管理 VPCの作成 まず、改めてVPCを作成します。 network.tfを以下のように書き換えます。 network.tf ################################ # VPC ################################ resource "aws_vpc" "vpc" { cidr_block = var.vpc_cidr enable_dns_support = true enable_dns_hostnames = true tags = { Name = "${var.prefix}-vpc" } } DNS関連の以下の2つのパラメータは基本的にどちらとも有効にしておく場面が多いと思います。割りと重要な設定なので、true/falseいずれの場合であっても明示的に記述しておくのが良いと思います。 DNS解決: enable_dns_support DNSホスト名: enable_dns_hostnames 省略した場合、デフォルトではenable_dns_supportはtrue、enable_dns_hostnamesはfalseになります。パラメータの意味は、公式ドキュメントの VPC での DNS の使用 を参照しましょう。 instance_tenancyは省略していますがdefaultが設定されます。 tagsの中にKeyValueの組み合わせをマップで記述することでタグを指定できます。指定しないとコンソール画面での識別に困るリソースはNameタグを指定します。 変数の使い方 プレフィックスのように何度も出現する文字列や、環境ごとに変動する値は変数化しておきましょう。 変数を定義するにはvariableブロックを使います。既存のnetwork.tfファイルに記述しても動作しますが、別ファイルに切り出してvariables.tfに記述するのが一般的です。 variables.tf # Common variable "prefix" { description = "Project name given as a prefix" type = string default = "cloud02" } # VPC variable "vpc_cidr" { description = "The CIDR block of the VPC" type = string default = "10.0.0.0/16" } variableに続けて変数の名前を定義し、波括弧{}で囲った中に説明(description)、型(type)、デフォルト値などを記述します。説明、型は以降は省きますが、省略せずに記述することが推奨されています。 network.tfでは、定義した変数を以下のように記述して呼び出しています。 cidr_block = var.vpc_cidr Name = "${var.prefix}-vpc" var.変数名で呼び出すことができますが、文字列の中に変数を記述する場合は"${var.変数名}-vpc"のように${}で括った形で変数を呼び出して、文字列全体はダブルクオーテーションで囲みます。 variables.tfでデフォルト値を指定しない場合、planやapply実行時にプロンプトが表示されて入力を求められます。変数が宣言されているのに値がわからない状態、ということですね。 terraform plan var.prefix Enter a value: terraform plan -var="prefix=cloud02"のように-varを使って引数で渡す方法もあります。 terraform plan -var="prefix=cloud02" -var="vpc_cidr=10.0.0.0/16" terraform.tfvarsというファイル名に変数の値を記述すると、自動的に読み込んでくれます。 terraform.tfvars prefix = "cloud02" vpc_cidr = "10.0.0.0/16" また、環境変数で指定することもできます。TF_VAR_<変数名>=<値>の形式で宣言します。 export TF_VAR_prefix=cloud02 export TF_VAR_vpc_cidr=10.0.0.0/16 整理すると、変数のデフォルト値を書いていない場合、 コマンド実行時にプロンプトで入力する コマンド実行時に-varオプションで指定する terraform.tfvarsファイルで指定する 環境変数で指定する ということになります。同時に指定された場合の 優先順位 は、 -varオプション -> terraform.tfvars -> 環境変数 となり、どれも指定されていなければプロンプトで入力となります。 初めは variables.tf と terraform.tfvars の使い分けがわかりにくいかもしれませんが、 variables.tfには変数の宣言を書く terraform.tfvarsには変数の値を書く というように理解しています。 variables.tfのデフォルト値で全ての変数値を指定するとterraform.tfvarsは不要になりますが、開発/検証/本番など環境ごとに変動する値を定義したり、パスワードなど機密情報を書いて.gitignoreでバージョン管理対象外とする場合にterraform.tfvarsは役立ちます。 なお、terraform.tfvars以外の名前で定義する場合、例えば、dev.tfvarsというように開発環境向けの変数を記述したファイルを利用する場合は-var-fileで指定すると読み込むことができます。 terraform plan -var-file="dev.tfvars" 変数の基本的な使い方は以上です。 ローカル変数を宣言するための locals ブロックもありますが、基本的な使い方は同じです。 variables.tf と terraform.tfvarsは以下のようにして続きを進めていきます。 variables.tf # Common variable "prefix" {} # VPC variable "vpc_cidr" { default = "10.0.0.0/16" } terraform.tfvars prefix = "cloud02" この段階でいったんapplyしてVPCの作成を確認しても構いませんし、そのまま続きのコードを記述して最後にapplyしてもOKです。 インターネットゲートウェイの作成 次はインターネットゲートウェイを作成します。 network.tfに以下のコードを追記します。 network.tf ################################ # Internet Gateway ################################ resource "aws_internet_gateway" "igw" { vpc_id = aws_vpc.vpc.id tags = { Name = "${var.prefix}-igw" } } vpc_id = aws_vpc.vpc.idでは、VPCのIDを参照しています。 今回のIGW→VPCのように他のリソースと関連付ける場合には、<リソースタイプ>.<リソースの識別名>.<属性> という形で関連リソースを指定することができます。 属性は今回の例ではidです。 リファレンスの「Attributes Reference」を参照するとどういった属性があるかわかりますが、たいていの場合はidやarnといったリソースを一意に特定するためのものが利用されます。 サブネットの作成 続いてサブネットを作成します。 network.tfに以下のコードを追記します。 network.tf ################################ # Subnet ################################ resource "aws_subnet" "public_subnet_1a" { vpc_id = aws_vpc.vpc.id availability_zone = "ap-northeast-1a" cidr_block = cidrsubnet(var.vpc_cidr, 8, 11) # 10.0.11.0/24 map_public_ip_on_launch = true tags = { "Name" = "${var.prefix}-public-subnet-1a" } } resource "aws_subnet" "public_subnet_1c" { vpc_id = aws_vpc.vpc.id availability_zone = "ap-northeast-1c" cidr_block = cidrsubnet(var.vpc_cidr, 8, 12) # 10.0.12.0/24 map_public_ip_on_launch = true tags = { "Name" = "${var.prefix}-public-subnet-1c" } } resource "aws_subnet" "private_subnet_1a" { vpc_id = aws_vpc.vpc.id availability_zone = "ap-northeast-1a" cidr_block = cidrsubnet(var.vpc_cidr, 8, 21) # 10.0.21.0/24 map_public_ip_on_launch = false tags = { "Name" = "${var.prefix}-private-subnet-1a" } } resource "aws_subnet" "private_subnet_1c" { vpc_id = aws_vpc.vpc.id availability_zone = "ap-northeast-1c" cidr_block = cidrsubnet(var.vpc_cidr, 8, 22) # 10.0.22.0/24 map_public_ip_on_launch = false tags = { "Name" = "${var.prefix}-private-subnet-1c" } } システム構成 を見て頂くとわかりやすいと思いますが、パブリックサブネットとプライベートサブネットをAZ-1aとAZ-1cにそれぞれ作成しています(計4つ) パブリックサブネットはmap_public_ip_on_launchをtrueにしてパブリックIPアドレスの自動割当を有効にしています。また、cidr_blockは cidrsubnet という関数を使ってVPCのCIDRである「10.0.0.0/16」を元に算出しています。 構文はcidrsubnet(prefix, newbits, netnum)です。 一見難しそうに見えるかもしれませんが、それぞれの関数部分をterraform consoleで確認するとわかりやすいです。 terraform console > cidrsubnet("10.0.0.0/16", 8, 11) "10.0.11.0/24" > cidrsubnet("10.0.0.0/16", 8, 12) "10.0.12.0/24" > cidrsubnet("10.0.0.0/16", 8, 21) "10.0.21.0/24" > cidrsubnet("10.0.0.0/16", 8, 22) "10.0.22.0/24" Terraformには他にも様々な組み込み関数がありますが、動作確認をするにはterraform consoleが便利です。 ルートテーブルの作成 最後にルートテーブルを作成してサブネットに関連付けます。 network.tfに以下のコードを追記します。 network.tf ################################ # Public Route Table ################################ resource "aws_route_table" "public_rt" { vpc_id = aws_vpc.vpc.id tags = { Name = "${var.prefix}-public-route" } } resource "aws_route" "to_internet" { route_table_id = aws_route_table.public_rt.id gateway_id = aws_internet_gateway.igw.id destination_cidr_block = "0.0.0.0/0" } resource "aws_route_table_association" "public_rt_1a" { subnet_id = aws_subnet.public_subnet_1a.id route_table_id = aws_route_table.public_rt.id } resource "aws_route_table_association" "public_rt_1c" { subnet_id = aws_subnet.public_subnet_1c.id route_table_id = aws_route_table.public_rt.id } ################################ # Private Route Table ################################ resource "aws_route_table" "private_rt" { vpc_id = aws_vpc.vpc.id tags = { Name = "${var.prefix}-private-route" } } resource "aws_route_table_association" "private_rt_1a" { subnet_id = aws_subnet.private_subnet_1a.id route_table_id = aws_route_table.private_rt.id } resource "aws_route_table_association" "private_rt_1c" { subnet_id = aws_subnet.private_subnet_1c.id route_table_id = aws_route_table.private_rt.id } aws_route_tableで空のルートテーブルを作成 aws_routeでルートを追加、 aws_route_table_associationでサブネットに関連付け という構成になっています。 パブリックサブネット向けのルートにはインターネット宛のルートとしてインターネットゲートウェイと関連付けています。プライベートサブネットには今回は特にルートを追加しませんが、NATGWが存在する場合などは同じようにルートを追加すればOKです。 terraform applyを実行して、VPC、インターネットゲートウェイ、サブネット、ルートテーブルが作成、関連付けされていることを確認しましょう。 StateファイルをS3へ移行 さて、Terraformの実行計画 で解説したようにTerraformでは差分を簡単に知ることができて便利ですが、これはこれから実行されるコードと terraform.tfstate というファイルを比較することで実現しています。 terraform.tfstateはapplyやdestroyするたびに自動的に作成・更新されるファイルで、その名のとおり、Terraformによって管理されるインフラの状態が書かれています。中身はJSON形式です。 例えばterraform.tfstateを誤って削除してしまった場合、全てのリソースが新規扱いになるためapplyを実行するとさきほど作成したVPC関連のリソースが再び一式作成されてしまいます。つまり、インフラの状態を維持・管理していく上で、非常に重要なファイルということです。 チーム開発では複数人で共有しながら進める必要があるため、terraform.tfstateをS3などのストレージで管理するのが一般的です。学習の範囲ではterraform.tfstateがローカルに保存されていても問題ありませんが、この記事ではterraform.tfstateをS3に移行してみたいと思います。 terraform.tfstateが保存される場所などの定義をTerraformでは「バックエンド」と呼び、backendブロックに記述します。provider.tfにbackendブロックを追加しましょう。S3バケットの名前は読み替えてください。 provider.tf terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 3.0" } } backend "s3" { bucket = "cloud02-tf-remote-state-bucket" key = "ap-northeast-1/dev/terraform.tfstate" region = "ap-northeast-1" } } provider "aws" { region = "ap-northeast-1" } backend "s3"と記述することで保存先をS3とすることを宣言しています。 他にもbackend "remote"と書けばTerraform Cloudを利用することになります。 bucket key regionにはそれぞれバケット名、tfstateのパス(ファイル名)、リージョンを指定します。 Backend Configuration に書かれている通り、backendブロックでは変数が使えないので注意しましょう。 A backend block cannot refer to named values (like input variables, locals, or data source attributes). バックエンドで指定するS3バケットは予めコンソール画面から作成しています。 S3バケット自体もTerraformで作成することもできますが、その場合は同じ管理下に含めず、別途作成するのが良さそうです。 書き換えできたらterraform planを実行してみます。 terraform plan Error: Backend initialization required, please run "terraform init" │ │ Reason: Initial configuration of the requested backend "s3" │ │ The "backend" is the interface that Terraform uses to store state, │ perform operations, etc. If this message is showing up, it means that the │ Terraform configuration you're using is using a custom configuration for │ the Terraform backend. │ │ Changes to backend configurations require reinitialization. This allows │ Terraform to set up the new configuration, copy existing state, etc. Please run │ "terraform init" with either the "-reconfigure" or "-migrate-state" flags to │ use the current configuration. │ │ If the change reason above is incorrect, please verify your configuration │ hasn't changed and try again. At this point, no changes to your existing │ configuration or state have been made. そうすると、バックエンドの構成が変更されたのでterraform initを実行するよう促されます。 terraform init Initializing the backend... Do you want to copy existing state to the new backend? Pre-existing state was found while migrating the previous "local" backend to the newly configured "s3" backend. No existing state was found in the newly configured "s3" backend. Do you want to copy this state to the new "s3" backend? Enter "yes" to copy and "no" to start with an empty state. Enter a value: yes terraform initを実行すると、ローカルのtfstateをS3にコピーするか聞かれますので「yes」を入力して進めます。そうすると、ローカルのterraform.tfstateはクリアされ、S3バケット上にファイルが作成されます。 この状態で改めてterraform planを実行してみます。 terraform plan ───────────────────────────────────────────────────────────────────────────────────────────────── No changes. Your infrastructure matches the configuration. Your configuration already matches the changes detected above. If you'd like to update the Terraform state to match, create and apply a refresh-only plan: terraform apply -refresh-only No changesと表示されているので、S3のterraform.tfstateを参照して差分チェックできていることがわかります。これ以降、変更を加えるたびにS3にあるterraform.tfstateが更新されることになります。 S3に保存することで複数人で共有できるようになりましたが、厳密には同時に変更が加えられないよう更新中にロックしておく仕組みが必要です。DynamoDB State Locking にあるようにDynamoDBを使えば簡単に実装できますが、まだ試せていないので今回は省略させて頂きます。 今回は以上です。次回はEC2編ということで、Webサーバを2台構築してみたいと思います! 参考リンク VPC での DNS の使用 Input Variables Local Values cidrsubnet Function S3 Backend
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IAM・VPC(AWS SAA試験対策編)

IAM 公式ページへ IAMとは、AWSのユーザーとグループを作成および管理し、アクセス権を使用してAWSサービスへのアクセスを許可および拒否できます。 ポリシー ポリシーとは、権限をJSON形式で書いた設定ファイルです。 例えば、S3のアクセスを許可しているポリシーをユーザーにアタッチすることで そのユーザーは、S3のアクセスすることが出来ます。 主に3つの権限の管理方法があり、 ユーザー、グループ、ロールがあります。 ユーザー ユーザーを作成し、ユーザーに対してアクセスの権限を付与させて管理します。 グループ ユーザーを作成し、ユーザーをグループに分け、アクセスの権限を付与させて管理します。 開発チームや運用チームなどの分け管理する事で誤操作でのミスを防ぐことが出来ます。 ロール AWSのサービス間に対して権限を許可をさせて管理することが出来ます。 例えば、EC2からS3へのアクセス権限を許可させてサービス間の連携することができます。 AWS Organizations 公式ページへ 類似サービス: IAM AWS Organizationsとは、複数のアカウントを利用している場合に マスターアカウントとして統合管理を実施することができます。 組織単位 (OU) にアカウントをまとめることで組織単位で管理することが出来ます。 AWS Organizationsを使用するメリット S3などの一部のサービスでは、ユーザーがサービスをより多く使用すればするほど低価格となるボリューム価格が利用されています。したがって、ストレージの利用量を統合して請求することによって、コストを削減できる可能性ありAWS Organizationsを使用するメリットがあります。 VPC編 今回は、セキュリティーグループ設定とネットワークACL設定の違いを書きたいため VPCについて説明しませんが簡単に言うとEC2とかRDSを置くための仮想ネットワークです。 詳しくは、公式ページを確認してください。 セキュリティーグループ設定とネットワークACL設定の違い セキュリティーグループは、ステートフルであり、 サーバー単位にインバウンドとアウトバウンドのアクセス権限を決めれます。 ネットワークACLは、ステートレスであり、 VPC/サブネット単位でアクセス権限できます。 インバウンドとアウトバウンドの設定を別々に反映させることが出来ません。 参考 Udemy これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座(SAA-C02試験対応版)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Privatelink for S3でクロスアカウントアクセスを試してみた

今年の2月にGAとなったS3用のPrivatelinkを使用してクロスアカウントでのアクセスを試してみました。 AWS PrivateLink for Amazon S3 の一般提供を開始 概要図 書くまでもないといえば無いですが、このようなイメージです。 シンプルな構成で検証してます。 気になっていたこと 検証前の段階では、以下の点を気にしていました。 Privatelinkはどちらのアカウントで作成必要か? S3のバケットポリシーへの許可の書き方は通常通りでいいのか? サーバーサイド暗号化されている場合、明示的な許可は必要か? IAMへの許可は通常通り必要か? 他のPrivatelinkは構築したことがあったので大丈夫だろうとは思いつつも、S3用のPrivatelinkは他と少し使用が違うので検証はしておこうと思いやってみました。 参考: Amazon S3 の PrivateLink が他のサービスと異なるたった1つの点 なお、当記事ではPrivatelink、EC2、S3といったサービス自体の説明は割愛します。 検証 では、検証した内容です。 1.アカウントB側のS3バケット作成 アカウントB側で適当な名前でS3バケットを作成します。 まずは東京リージョン(ap-northeast-1)で作成。 設定は基本デフォルトですが、「サーバーサイドの暗号化(SSE-S3)」は有効にしました。 また、バケットポリシーは以下の通り設定し、クロスアカウントするロールからのアクセスを許可します。 バケットポリシー { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "<アカウントAのRole-ARN>" }, "Action": [ "s3:GetObject", "s3:PutObject", "s3:PutObjectAcl", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::<アカウントBのdest-bucket-name>", "arn:aws:s3:::<アカウントBのdest-bucket-name>/*" ] } ] } 2.アカウントA側でEC2インスタンス作成 Administratorアクセスを付与したIAMロールを先に作成してありましたので、そちらを付与しました。 3.アカウントA側でS3用のPrivatelinkを作成 本命の手順です。 通常のPrivatelink同様、アカウントA側で作成してみます。 サービスではInterface型を選択。 他のサービスだとプライベートDNSの有効/無効を設定することが出来ますが、S3用のPrivatelinkは設定できません。 詳しくは以下。めっちゃ参考にさせていただきました。 「Amazon S3 インターフェースエンドポイント(PrivateLink)ではプライベート DNS をサポートしていません」 の意味を絵をかいて腹落ちさせてみた あと、セキュリティーグループの設定がありますが今回は インバウンドはEC2にアタッチしたセキュリティーグループのIDからのHTTPS通信を許可 アウトバウンドは全許可 で検証しています。 実運用の際はセキュリティーポリシーに則って設計してください。 上記で無事に作成できました。 4.動作確認(Privatelink) 一旦Privatelinkの動作確認をします。 EC2から作成時に払い出されたDNS名宛に名前解決ができるかを確認。 PrivatelinkDNS名確認 [ec2-user@test-instance ~]$ dig *.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com +short 172.31.32.xx 172.31.1.xxx 172.31.22.xxx IPアドレスが返却されればOK。 そうでない場合は、セキュリティーグループの設定等見直しましょう。 Privatelinkを経由してS3を参照する際は--endpointurlオプションで指定します。 https://bucket〜のリンクを付与してから始まり、以下はDNS名を参照させる点が注意。 アカウントAバケット参照 [ec2-user@test-instance ~]$ aws s3 ls --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com 2021-06-07 00:51:48 test-bucket 2021-03-25 08:56:17 hogehoge 2021-06-07 01:00:57 fugafuga 5.動作確認(クロスアカウント) では、クロスアカウントでの動作を確認します。 アカウントBバケット参照 [ec2-user@test-instance ~]$ aws s3 ls s3://dest-bucket --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com [ec2-user@test-instance ~]$ まだファイルは何も置いていないので、エラーが出なければ成功です! バケットポリシー、IAMなどが許可できていないと以下のようなエラーが発生します。 An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied 続いて、書き込み等も続けて試験。 [ec2-user@test-instance tmp]$ echo This is test. > test.txt [ec2-user@test-instance tmp]$ ll 合計 4 drwx------ 3 root root 17 6月 7 01:03 systemd-private-1f89a79cd0fa4491b31ca1d48e75b2d9-chronyd.service-gTobSU -rw-rw-r-- 1 ec2-user ec2-user 14 6月 7 01:28 test.txt [ec2-user@test-instance tmp]$ aws s3 cp ./test.txt s3://dest-bucket --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com upload: ./test.txt to s3://dest-bucket/test.txt [ec2-user@test-instance tmp]$ [ec2-user@test-instance tmp]$ aws s3 ls s3://dest-bucket --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com 2021-06-07 01:29:47 14 test.txt [ec2-user@test-instance tmp]$ [ec2-user@test-instance tmp]$ aws s3 rm s3://dest-bucket/test.txt --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com delete: s3://dest-bucket/test.txt [ec2-user@test-instance tmp]$ [ec2-user@test-instance tmp]$ aws s3 ls s3://dest-bucket --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com [ec2-user@test-instance tmp]$ すべての動作が問題なく行えました! 結論…? Privatelinkはどちらのアカウントで作成必要か? →アカウントA(参照元)側で作成必要でした。 S3のバケットポリシーへの許可の書き方は通常通りでいいのか? →通常通り、アカウントA側EC2にアタッチしたロールへのアクセス許可でOKでした。 サーバーサイド暗号化されている場合、明示的な許可は必要か? →明示的な許可は不要でした。 IAMへの許可は通常通り必要か? →…あれ? 「めっちゃ早く検証終わったじゃん、ラッキー」 なんて考えていたのですが、そういえばIAMへの許可出来て無くないか? 内容整理 再度確認しましたが、アカウントAのEC2にはAWSマネージドのAdministrator Accessを許可したポリシーしかありませんでした。 アカウント跨ぎの場合は許可必須かと思っていたのですが… 参考: S3のバケットポリシーでハマったので、S3へのアクセスを許可するPrincipalの設定を整理する 挙動的には問題なく見える。 ということは、バケットポリシーさえ設定できていれば、Privatelink経由ならアカウント跨ぎでもIAMでの明示的な許可は不要…??? バケットポリシーを外したところエラーが出たので、ここで許可を与えていることは間違いなさそうです。 散々考えてますが今の所はっきりとこれだから出来たんだ!という答えは見つかっておらず、、 もしどなたか詳しい方がいらっしゃいましたら、教えて下さい。 結論+補足 Privatelinkはどちらのアカウントで作成必要か? →アカウントA(参照元)側で作成必要でした。 S3のバケットポリシーへの許可の書き方は通常通りでいいのか? →通常通り、アカウントA側EC2にアタッチしたロールへのアクセス許可でOKでした。 サーバーサイド暗号化されている場合、明示的な許可は必要か? →明示的な許可は不要でした。 IAMへの許可は通常通り必要か? →無くても動作はしましたが、実運用の際は事前に検証することをおすすめします。 おまけ(別リージョンへのアクセス) バケットを別リージョンに作成し直してアクセスしてみました。 元バケットを消してすぐ同じ名前で作成し直そうとしたところ、怒られました、、 A conflicting conditional operation is currently in progress against this resource. Please try again. 気を取り直して、2をつけてオレゴン(us-west-2)で再作成します。 条件は同様です。 同じPrivatelinkから書き込み等試しまして、問題なく動作しました。 クロスアカウント・クロスリージョンアクセス [ec2-user@test-instance tmp]$ aws s3 ls s3://dest-bucket2 --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com [ec2-user@test-instance tmp]$ [ec2-user@test-instance tmp]$ aws s3 cp ./test.txt s3://dest-bucket2 --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com upload: ./test.txt to s3://dest-bucket2/test.txt [ec2-user@test-instance tmp]$ [ec2-user@test-instance tmp]$ aws s3 ls s3://dest-bucket2 --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com 2021-06-07 01:36:50 14 test.txt [ec2-user@test-instance tmp]$ [ec2-user@test-instance tmp]$ aws s3 rm s3://dest-bucket2/test.txt --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com delete: s3://dest-bucket2/test.txt [ec2-user@test-instance tmp]$ [ec2-user@test-instance tmp]$ aws s3 ls s3://dest-bucket2 --endpoint-url https://bucket.vpce-xxxxxxxxxxxxxxx-xxxxxxx.s3.ap-northeast-1.vpce.amazonaws.com [ec2-user@test-instance tmp]$ [ec2-user@test-instance tmp]$ [ec2-user@test-instance tmp]$ aws s3 ls s3://dest-bucket2 [ec2-user@test-instance tmp]$ S3用のPrivatelink、IAMの件以外は普通に使えそうで良かったです。 余裕があれば後日再検証して追加記事投稿するかもしれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LINE MessageAPIで作った天気予報アプリをAWSでデプロイしてみた!

以前作成したアプリのデプロイに関する記事です。 無料で使いたいのでherokuへデプロイしましたが、Flex Messageが届きませんでした。 ということでお金がかかりますが、AWSにデプロイすることにします。 アプリ作成までの記事はこちらです。 それではAWSへのデプロイをやっていきましょう! 今回のアプリはDBを使っていませんが、もしかしたら使うこともあると思うので一応RDSも使います。 ハンズオン 1. AWSへログイン 2. 東京リージョンになっていることを確認 3. VPCを作成(IP:10.0.0.0/21) ■ルート コンソールで「VPC」と検索→左ペイン内の「VPC」を選択→「VPCを作成」を選択 4. サブネットを作成(IP:10.0.0.0/24, 10.0.1.0/24, 10.0.2.0/24, 10.0.3.0/24) ■ルート コンソールで「VPC」と検索→左ペイン内の「サブネット」を選択→「サブネットを作成」を選択 Public Subnetを作成(IP:10.0.0.0/24, 10.0.1.0/24) Laravelで使うサブネットを作成します。 Private Subnetを作成(IP:10.0.2.0/24, 10.0.3.0/24) RDS(MySQL)で使うサブネットを作成します。 5. Publicサブネットをインターネットに接続するためにInternet Gatewayを作成しアタッチする ■ルート コンソールで「VPC」と検索→左ペイン内の「インターネットゲートウェイ」を選択→「インターネットゲートウェイの作成」を選択 6. EC2がインターネット接続するためにルートテーブルを作成 ■ルート コンソールで「VPC」と検索→左ペイン内の「サブネット」を選択→「Laravel-Pub-1」を選択→「ルートテーブル」を選択→「ルート」へ移動→「ルートを編集」を選択 7. Publicサブネット内にEC2を構築 ■ルート コンソールで「EC2」と検索→左ペイン内の「インスタンス」を選択→「インスタンスを起動」を選択 ■ステップの流れ ①Amazon Linux 2 AMI (HVM), SSD Volume Typeを選択 ②t2.microを選択 ③インスタンスの詳細の設定の、ネットワーク, サブネット, 自動割り当てパブリックIPを変更 ④ストレージはそのままでOK ⑤キーをNameで追加(値は任意でOK) ⑥セキュリティーグループを変更 EC2は、HTTP通信でリクエスト、レスポンスを行うためHTTPを許可します。 ⑦キーペアの作成 私は事前に作成しているキーペアがあるのでそちらを使います。 このキーペアはdesktopにでも保存しておきましょう。 以前書いたEC2の記事です。 基本的な内容になっているのでEC2ってなんやねんって方は見てもらえると! 8. EC2のIPアドレスを固定するためにElastic IPを導入する IPアドレスを固定する理由としては、インスタンスを再起動しても同じIPアドレスに固定しておきたいためです。 通常、インスタンスを再起動するとIPアドレスが変わってしまいます。 それでは再起動する度にSSH接続の設定を変えないといけないため、IPアドレスを固定します。 ちなみに、Elastic IPは関連づけを行わなければ費用が発生します。 その点はご注意ください。 ■ルート コンソールで「EC2」と検索→左ペイン内の「Elastic IP」を選択→「Elastic IPアドレスの割り当て」を選択 作成できたら次は関連づけを行います。 9. SSH接続でログイン ターミナルを開きましょう。 ターミナル // ①キーペアがあるところへ移動 $ cd desktop // ②キーペア発見 $ ls -l Keypair.pem -rw-r--r--@ 1 ryo staff 1704 5 1 14:38 Keypair.pem // ③権限を400に変更 $ chmod 400 Keypair.pem // ④SSH接続 $ ssh -i Keypair.pem ec2-user@(パブリックIPアドレス) __| __|_ ) _| ( / Amazon Linux 2 AMI ___|\___|___| これでEC2にログインできました。 それでは動作するために必要なものをまとめてみましょう。 パッケージのアップデート PHP7.4のインストール PHPの拡張機能のインストール Composerのインストール GitHubからソースコードをクローン vendorのインストール Laravelの初期設定 環境設定ファイルの準備 アプリ固有のキーを発行 ディレクトリの権限変更 nginxのインストール nginxの起動と再起動 ドキュメントルートなどのnginx設定 結構やることは多いですがそんなに難しくないので頑張りましょう! パッケージのアップデート まずはパッケージのアップデートを以下のコマンドで行います。 ターミナル $ sudo yum update -y PHP7.4のインストール php7.4やnginxのインストールには、Amazon Linuxで提供しているamazon-linux-extrasというコマンドを使用します。 amazon-linux-extrasとはあらかじめAWSでインストールされているソフトウェアを起動させることで簡単に利用できるシステムです。 さっそく以下のコマンドを入力してみましょう。 ターミナル // amazon-linux-extras $ amazon-linux-extras 0 ansible2 available \ [ =2.4.2 =2.4.6 =2.8 =stable ] 2 httpd_modules available [ =1.0 =stable ] 3 memcached1.5 available \ [ =1.5.1 =1.5.16 =1.5.17 ] 5 postgresql9.6 available \ [ =9.6.6 =9.6.8 =stable ] 6 postgresql10 available [ =10 =stable ] 9 R3.4 available [ =3.4.3 =stable ] 10 rust1 available \ [ =1.22.1 =1.26.0 =1.26.1 =1.27.2 =1.31.0 =1.38.0 =stable ] 11 vim available [ =8.0 =stable ] 15 php7.2 available \ [ =7.2.0 =7.2.4 =7.2.5 =7.2.8 =7.2.11 =7.2.13 =7.2.14 =7.2.16 =7.2.17 =7.2.19 =7.2.21 =7.2.22 =7.2.23 =7.2.24 =7.2.26 =stable ] 17 lamp-mariadb10.2-php7.2 available \ [ =10.2.10_7.2.0 =10.2.10_7.2.4 =10.2.10_7.2.5 =10.2.10_7.2.8 =10.2.10_7.2.11 =10.2.10_7.2.13 =10.2.10_7.2.14 =10.2.10_7.2.16 =10.2.10_7.2.17 =10.2.10_7.2.19 =10.2.10_7.2.22 =10.2.10_7.2.23 =10.2.10_7.2.24 =stable ] 18 libreoffice available \ [ =5.0.6.2_15 =5.3.6.1 =stable ] 19 gimp available [ =2.8.22 ] 20 docker=latest enabled \ [ =17.12.1 =18.03.1 =18.06.1 =18.09.9 =stable ] 21 mate-desktop1.x available \ [ =1.19.0 =1.20.0 =stable ] 22 GraphicsMagick1.3 available \ [ =1.3.29 =1.3.32 =1.3.34 =stable ] 23 tomcat8.5 available \ [ =8.5.31 =8.5.32 =8.5.38 =8.5.40 =8.5.42 =8.5.50 =stable ] 24 epel available [ =7.11 =stable ] 25 testing available [ =1.0 =stable ] 26 ecs available [ =stable ] 27 corretto8 available \ [ =1.8.0_192 =1.8.0_202 =1.8.0_212 =1.8.0_222 =1.8.0_232 =1.8.0_242 =stable ] 28 firecracker available [ =0.11 =stable ] 29 golang1.11 available \ [ =1.11.3 =1.11.11 =1.11.13 =stable ] 30 squid4 available [ =4 =stable ] 31 php7.3 available \ [ =7.3.2 =7.3.3 =7.3.4 =7.3.6 =7.3.8 =7.3.9 =7.3.10 =7.3.11 =7.3.13 =stable ] 32 lustre2.10 available \ [ =2.10.5 =2.10.8 =stable ] 33 java-openjdk11 available [ =11 =stable ] 34 lynis available [ =stable ] 35 kernel-ng available [ =stable ] 36 BCC available [ =0.x =stable ] 37 mono available [ =5.x =stable ] 38 nginx1 available [ =stable ] 39 ruby2.6 available [ =2.6 =stable ] 40 mock available [ =stable ] 41 postgresql11 available [ =11 =stable ] 42 php7.4 available [ =stable ] 43 livepatch available [ =stable ] 44 python3.8 available [ =stable ] 45 haproxy2 available [ =stable ] 46 collectd available [ =stable ] 47 aws-nitro-enclaves-cli available [ =stable ] 48 R4 available [ =stable ] 49 kernel-5.4 available [ =stable ] 50 selinux-ng available [ =stable ] 51 php8.0 available [ =stable ] 52 tomcat9 available [ =stable ] 53 unbound1.13 available [ =stable ] 54 mariadb10.5 available [ =stable ] 55 kernel-5.10 available [ =stable ] 56 redis6 available [ =stable ] 現時点では合計56個のパッケージとそのバージョンが表示されています。 では、amazon-linux-extrasを利用したPHPのインストールをしましょう。 ターミナル // PHP7.4のインストール $ sudo amazon-linux-extras install -y php7.4 // バージョンを確認(OKです) $ php -v PHP 7.4.19 (cli) (built: May 13 2021 22:36:40) ( NTS ) Copyright (c) The PHP Group Zend Engine v3.4.0, Copyright (c) Zend Technologies PHPの拡張機能のインストール Laravelのインストール時やプロジェクト作成時にこれらの拡張機能がないとうまくいきません。 ターミナル $ sudo yum install -y php-mbstring php-dom php-zip composerのインストール PHPのパッケージマネージャーであるcomposerをインストールしていきましょう。 curlコマンドはインターネット上のファイルを取得するコマンドで、ダウンロードしたcomposerのインストーラーをphpで実行するコマンドになります。 どこでもcomposerコマンドを使えるようにパスを通しましょう。 mvコマンドを使って、/usr/bin/に移動させることにより、どのディレクトリからでもcomposerコマンドを使えるようになります。 ターミナル // Composerのインストール $ curl -sS https://getcomposer.org/installer | php // Composerがどこでも使えるようにパスを通す $ sudo mv composer.phar /usr/bin/composer // バージョンを確認(OKです) $ composer -v ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 2.1.1 2021-06-04 08:46:46 GitHubからソースコードをクローン ターミナル // GitHubからソースコードをクローンをする準備 // ディレクトリを作成して移動する $ sudo mkdir /var/www $ cd /var/www // アクセス権限を変更 $ sudo chmod 777 . // gitをインストール $ sudo yum install -y git // GitHubからソースコードをクローン $ git clone https://github.com/ssk9597/LINE-Weather.git // アクセス権限を変更して誰でも書き込みできないようにする sudo chmod 0755 . vendorのインストール GitHubからクローンしてもvendorディレクトリは付属してきません。 その理由としてはそもそもvendorはフレームワークの根幹機能がまとまったディレクトリになります。 そのため、バージョンが同じであれば同じ内容になります。 しかも容量が大きいためGitHubにアップロードするのは非効率です。 JavaScriptなどでいうと、node_modulesと一緒です。 vendorはLaravelを利用する環境で、composerを使って個別にダウンロードする必要があります。 ターミナル // ディレクトリに移動する $ cd LINE-Weather $ cd src // vendorのインストール $ composer install Laravelの初期設定 ターミナル // 環境設定ファイルの準備 $ cp .env.example .env // アプリ固有のキーを発行 $ php artisan key:generate // ディレクトリの権限変更 $ chmod 0777 -R storage $ chmod 0777 -R bootstrap nginxのインストール nginxはapacheと似た機能を提供するWebサーバーです。 主な違いとしては駆動方式です。 apacheはユーザーからのアクセスの度にプロセスを立ち上げる駆動方式に対して、nginxはプロセスは増えずループによってユーザーからのアクセスを捌く方式です。 そのため、nginxの方がメモリ効率良く、近年においてはnginxのシェアが高まってきています。 ターミナル // nginxをインストール $ sudo amazon-linux-extras install -y nginx1 // バージョンを確認(OKです) $ nginx -v nginx version: nginx/1.20.0 // nginxが再起動後も自動的に動くようにする $ sudo systemctl enable nginx // nginxをスタート状態に sudo service nginx start nginxを起動したら、EC2のIPアドレスにブラウザからアクセスしてみましょう。 以下のようなnginxの初期画面が表示されていれば、正常に動作しています。 ドキュメントルートなどのnginx設定 ターミナル $ sudo vi /etc/nginx/nginx.conf etc/nginx/nginx.conf server { listen 80; listen [::]:80; server_name _; # ==========ここを編集する========== root /var/www/LINE-Weather/src/public; # ==========ここを編集する========== # Load configuration files for the default server block. include /etc/nginx/default.d/*.conf; # ==========ここを編集する========== location / { try_files $uri $uri/ /index.php?$query_string; } # ==========ここを編集する========== error_page 404 /404.html; location = /404.html { } error_page 500 502 503 504 /50x.html; location = /50x.html { } } ここまで編集できましたら、nginxを再起動します。 ターミナル $ sudo service nginx restart 10. AMIの作成 今回は冗長化されたウェブサービスを作るので上記と同じ内容のEC2インスタンスを作る必要があります。 しかし、またゼロからポチポチするのもめんどくさいので、上記で作ったEC2インスタンスからコピーしましょう。 その仕組みをAMIと言います。 作成した001-LINE-Weatherを停止してAMIを作成します。 ■ルート コンソールで「EC2」と検索→左ペイン内の「インスタンス」を選択→「インスタンスの状態」から停止を選択 コンソールで「EC2」と検索→左ペイン内の「インスタンス」を選択→「アクション」から「イメージを作成」を選択 11. 作成したAMIを使ってインスタンスを作成 12. EC2のIPアドレスを固定するためにElastic IPを導入する 新しく作成したEC2インスタンスの002-LINE-WeatherにElastic IPを導入しましょう。 上記でやったことを繰り返すだけなので省略します。 13. PrivateサブネットにRDSを構築 AWSのサービスであるRDSを利用してMySQLを立ち上げLaravelと連携できるようにします。 以前書いたRDSの記事です。 サブネットグループの作成 RDSには、エンドポイント通信という機能があります。 もし通信障害などが発生して、親のRDSが使えなくなった時子のRDSに接続してくれる機能です。 つまり、このことから異なるAZにサブネットがないとRDSは使えません。 ということで複数のサブネットにまたがる形でRDSは構築されるので、 サブネットグループというものが必要になります。 ■ルート コンソールで「RDS」と検索→左ペイン内の「サブネットグループ」を選択→「DB サブネットグループを作成」を選択 DBの作成 ■ルート コンソールで「RDS」と検索→左ペイン内の「データベース」を選択→「データベースの作成」を選択 追記する部分のみスクショしました。 セキュリティグループを修正 ■ルート コンソールで「VPC」と検索→左ペイン内の「セキュリティグループ」を選択→「RDS-SG-1」を選択 DBは重要な情報が入っているため誰でも彼でもアクセスできると困ります。 なので、PublicサブネットのEC2(セキュリティグループ→EC2-LINE-Weather-SG)からしか触れないように修正します。 LaravelのDB接続設定 EC2(Laravel)からRDSに接続するための設定を行っていきます。 EC2は2台あるので、両方ともに同じ設定をする必要があります。 2台ともSSH接続を行ってください。 ターミナル // SSH接続 $ ssh -i Keypair.pem ec2-user@(パブリックIP) // .envファイルを編集 $ vi /var/www/LINE-Weather/src/.env /var/www/LINE-Weather/src/.env DB_CONNECTION=mysql DB_HOST=line-weather.cjsmw2zqsiga.ap-northeast-1.rds.amazonaws.com DB_PORT=3306 DB_DATABASE=line_weather DB_USERNAME=admin DB_PASSWORD=password // LINE LINE_CHANNEL_SECRET=??? LINE_CHANNEL_ACCESS_TOKEN=??? // OpenWeatherAPI WEATHER_API=??? ターミナル // 移動 $ cd /var/www/LINE-Weather/src // migrate $ php artisan migrate 14. ロードバランサーを導入 ロードバランサーは、負荷のバランスを取る装置です。 例えばWebサーバーに負荷がかかりやすいアプリケーションの場合、複数のサーバーに負荷を振り分けます。 これを冗長化と言います。 今回も2台のEC2を使っており冗長化しております。 ロードバランサーの主な機能としては3つです。 ①ELBにはDNSのアクセスポイントが付与されます。 つまり、アクセスポイントを1つにして負荷を分散させることができます。 ②ヘルスチェック機能がある 異常なインスタンスを発見したら通信をキャンセルしてくれます。 ③ELBに証明書を付与することでSSL通信の終端になってくれる HTTPS通信を復号する処理を担当してくれます。 つまり、internet gatewayの段階ではHTTPSだが、サブネット内ではHTTP通信となります。 以前書いたELBの記事です。 ELBに関して詳しく知りたい方はどうぞ。 ■ルート コンソールで「EC2」と検索→左ペイン内の「ロードバランサー」を選択→「ロードバランサーの作成」を選択 15. EC2のセキュリティグループのアクセス許可をロードバランサーからに変更する ■ルート コンソールで「EC2」と検索→左ペイン内の「セキュリティグループ」を選択→「EC2-LINE-Weather-SG」を選択 16. Freenomでドメインを購入する Freenomは1年間ドメインを無料で使えます。 なのでこちらでドメインを購入しましょう! どうやればいいかわからない方は以下の記事がわかりやすいので以下の記事を参考にしてみてください。 今回私は、line-weather-fashion.tkというドメインを取得しました。 17. Route53と独自ドメインの紐付け ■ルート コンソールで「Route53」と検索→左ペイン内の「ホストゾーン」を選択→「ホストゾーンの作成」を選択 作成が正常に完了すると、NSレコードと呼ばれる値が割り振られます。 この値をFreenomの方に書き写していきます。 以前書いたS3とRoute 53の記事です。 S3とRoute 53に関して詳しく知りたい方はどうぞ。 18. ドメインとロードバランサーを紐付ける ■ルート コンソールで「Route53」と検索→左ペイン内の「ホストゾーン」を選択→「line-weather-fashion.tk」を選択→「レコードを作成」を選択 これでロードバランサーとline-weather-fashion.tkは結びつきました。 成功ですね! 19. ロードバランサーでリスナーを追加する AWS Certificate Manager(ACM) で証明書を取得します。 現状http通信でしかアプリケーションを利用できない状態です。 近年は常時SSL化(常に暗号化したデータをユーザーとサーバーでやりとり)が推奨されていて、 Google Chromeではhttp通信のままだと保護されていない通信と出てしまいます。 ■ルート コンソールで「EC2」と検索→左ペイン内の「ロードバランサー」を選択→「LINE-Weathe-LB」を選択→「リスナーの追加」を選択 「新しいACM証明書をリクエスト」で発行を行います。 Route 53でレコードの作成を行います。 ありましたね! 証明書の検証に時間がかかるので成功になるまで待ちましょう。 成功に変わったら、次はリスナーの追加に戻ってSSL証明書を取得しましょう。 20. セキュリティグループの修正でHTTPSへのリダイレクト設定 ■ルート コンソールで「EC2」と検索→左ペイン内の「ロードバランサー」を選択→「LB-LINE-Weather-SG」を選択→「セキュリティグループ」を選択 最後にロードバランサーのリスナーをHTTPSのみにしましょう。 終わりに HTTPSでアクセスできました。 だが...動かぬ Flex MessageってどれくらいCPUとメモリ食うんでしょうね。。 t2.microからバランスの取れたCPUとバランスの取れたメモリ数のM系に変えて検証してみます。 このアプリに、M系はお財布痛すぎるので、最終的にはFlex Messageをやめて無料で運用できるHerokuにしようと思います。 こんな未完成なハンズオン記事はねーだろと思いますが、「一応デプロイはできたぞ」という記事でした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

エンド企業とSlerの関わり方:システム開発の内製化のポイント@ANGEL Dojo

目次 1.はじめに 2.全体スケジュール 3.ワークショップの内容-エンドユーザーとSIの関わり方- 4.キックオフを終えてみて 1-はじめに ANGEL Dojoって何? ANGEL Dojoでは次世代を担うAPNの若手のエンジニアの方々に、擬似プロジェクトを通じてアジャイル、DevOps、モダンなアプリケーション開発などのクラウドネイティブな手法と、様々なInnovationを作ってきたAmazonの文化と考え方を体験いただくことで、お客様にクラウドの価値を100%届けるための基礎的なスキルを実践を通して身につけていただきます。参加者の皆様はここで培ったスキルと、各パートナーの皆様がお持ちのそれぞれの強みを活かすことでお客様のビジネスを成功に導き、日本のITや経済をさらに成長させる主役、すなわち「APN Next Generation Engineer Leader」になっていただきます。 ( 「日本を元気にする」APN Next Generation Engineer Leaders(ANGEL) Dojo のご紹介 )より 簡潔に言うと「AWSを使い、3か月間、少人数のチームで企画から実装までを体験する疑似プロジェクト」になります。 同じ企業内の5名が1チームとなり、様々な講義やトレーニングを受け、企画開発、実装へと進んでいきます。 参加条件については、IT経験が1~3年目であることなど、幾つかあるので、気になる方は上記リンクで確認してみて下さい。 余談ですが、キックオフの説明で「ANGEL Dojo」はいろんなチャレンジができる場です。」というのがとても印象に残っていて、 私自身、インフラメインの業務に携わっていたので、フロント側など、普段業務では経験できないことをやってみようと思いました。 2-全体スケジュール ANGEL Dojoの全体スケジュールとしては以下の2つに分かれています  1 : 企画フェーズ     6/3~6/25  2 : 設計・開発フェーズ    7/1~7/30(中間発表) 8/5~9/3(最終発表) それぞれ詳細に説明すると、 1:企画フェーズ  ゴール:企画書の作成  期間:6/3~6/25  ここでは、Amazonの文化の1つでもある「Working Backwards(お客様から考える)」の手法をもとに、  疑似プロジェクトで開発するサービスコンセプトを決定します。 「Working Backwards」の考え方  1. お客様は誰ですか ?  2. お客様が抱える課題や改善点は明確ですか ?  3. お客様が受けるメリットは明確ですか ?  4. お客様のニーズやウォンツをどのように知りましたか ?  5. お客様の体験が描けていますか ? 2:設計・開発フェーズ  ゴール:設計及び開発の完了  期間:7/1~9/3  プレスリリース及び、FAQから実際に設計及び開発を行っていきます。 1週間のスケジュール  毎週金曜日を1日ワークDayとし、プロジェクトを進めていきます。  その他月曜日から水曜日までは通常業務を行っています。   → 講義やワークショップもある為、毎週1日~2日がプロジェクトに充てる時間になります   → 場合によってはその他の時間でタスク管理や進捗確認を行います  また、週に2回程度、講義やワークショップが実施されます。(必須参加or任意参加)   例)エンドユーザーとSIの関わり方、Working BackWordsの考え方 など ※上記はある週のスケジュール メンバー体制 <AWSからの支援体制>  ・各チームにAWSジャパン のSolution Architect1名がメンターとしてサポート  ・AWSの様々なトレーニングを提供(講義やワークショップ)  ・疑似プロジェクトで用いるAWSリソースに対する$1000のAWSクレジット  → 技術的な不安や、プロジェクトを進めるにあたって、AWSの方々に相談しやすい環境になっています <参加企業のメンバー体制>  ・1年目から3年目の若手社員5名 + 社内メンター(サポート役)1人 の計6名で参加  → 各メンバーのバックグラウンドは自動化やフロントメインに携わってきた方、インフラに関わってきた方などそれぞれです。 若手が集まったチームということもあり、MTGでも気兼ねなくそれぞれが発言できていました 3-ワークショップの内容 今回は、初回ワークショップで行われた「エンドユーザーとSIの関わり方」について簡単に紹介できればと思います。 このワークショップでは「内製化」をキーワードにユーザー企業の抱える課題、パートナー企業が可能な支援方法をディスカッションしました。 1.内製化の背景 説明する前に、内製化がなぜ求められているのか、簡単に説明したいと思います。 昨今、コロナ禍で様々な環境が変化していく中で、ぽっと出のビジネスモデルが急激に求められたり、従来のビジネスモデルが急激に衰退したりと、良くも悪くもビジネススピードの高速化が顕著に表れています。 そこで企業に求められるのが、「素早いシステム開発」です。 従来、多くのユーザー企業はシステム開発をする際、開発のほとんどをSIに委託するシステムでした。 しかし、このようなスタイルでは、企業間の距離や、SIのビジネスとの距離が遠い、などシステム開発までの時間がかかります。そこで注目されたのが「内製化」です。 そもそも「内製化」とは... ITシステムの内製化とは「ITシステムの根幹の開発を自社内で行う」ことを指します。 ITシステムの内製化とは「ITシステムの根幹の開発を自社内で行う」ことを指します。 ?ITがビジネスの場に登場してから四半世紀ほどの間、日本企業の多くは自社システムの開発をSIer(システムインテグレーター)に外注していまし>た。資金的余裕もなく、IT人材を自社内で育成することもできない日本企業は、人材外注先の企業とのやり取りを繰り返しながら、自社システム>の改善を続けてきたのです。 しかし、それではデータ活用などを見越して社内にIT人材を囲い込んでいる海外企業との競争には勝つことが出来ない、イノベーションが生まれ>ないなどの弊害が出てきたため、そのデメリットに気が付いた日本企業が、少しずつITシステムを内製化する動きに転換し始めているのです。 (ITシステムの内製(インハウス)化とは)より 2.現在のユーザー企業の内製化状況 参考資料2018年:(https://www.i-learning.jp/service/dbiz/topics/dbiz_itt10.html) ユーザー企業の内製化状況としては、全体の50%が内製化を進めているという状況  → 上流工程から下流工程の内、企画・設計などの上流の内製化を進めている企業が多い 参考資料:(https://mynavi-agent.jp/it/geekroid/2021/03/post-330.html) ユーザー企業のIT人材の”質”に関する調査では、上流、下流含め大幅に不足しているといった状況 3.内製化のポイント ここでは、内製化が上手くいった事例について、大切なポイントを3つほど、紹介します。 1.壁のないコミュニケーション(相互理解)  内製化が行われていない場合、システムを開発する上では様々な壁があります   例えばユーザー企業とSIの壁、開発と運用の壁、営業側とIT部門の壁など  スムーズに内製化が進んだ事例では、ユーザー企業、Slが同一の場所で開発、運用を行っていました  その為、スピーディに意志決定、情報の連携が行えることが、内製化では大切です 2.教育コストを加味した計画  ユーザー企業側は上流工程に対し強みがあるが、下流工程に関して深く理解していないことがあります   例えば、開発やテストにかかる工数など  この課題を解決するためには、ユーザー企業側はSIに対し、SIはユーザー企業側に対し、相互理解を深めることが大切です  お互いの文化等を理解した上で計画することが、内製化を上手く進めるためのポイントになります 3.進め方に対する意思決定権を持つ人の理解  意思決定権を持つ人が内製化に対し、柔軟に理解していること  ユーザー企業側の意思決定者が現場(ユーザー企業、SI)に対し、理解が深められていないと、ここにも壁が生じます  その為、意思決定者が柔軟に判断し、理解することがスムーズに内製化を進めるうえでの鍵になります   4-キックオフを終えてみて ここからは私がキックオフ、初回ワークショップを終えてみての感想です。 参加する前は「3か月で企画から開発、、大変そう」というのが率直な意見でした。 初日を終えてみて、1つ貴重な経験をしたなと思ったのが、普段はあまり聞くことが出来ない「ユーザー企業」の生の声が聞けた事です。 普段はシステムの開発に注力している為、ユーザーの声というものはあまり実感する機会がありませんでした。 ただ、今回のワークショップで実際にどのような部分にユーザー企業は不安を感じるのか、ユーザー企業側の課題を知ることで、 今後のシステム開発へのアプローチの方法も考える余地があるなと感じました。 それと単純に面白く、楽しかったです。(まだ初日ですが...) これから企画、開発と進んでいきますが、"Learn and Be Curious"をスローガンに挑戦をしてこれからも頑張っていきます。    
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

テザリング利用時の IP をセキュリティグループに登録

前提 テザリングを利用していると、 ifconfig 等で調べたアドレスとSSHで利用するIPアドレスが異なる場合があるらしい。 外で作業しているとき、セキュリティグループにIPを追加してサーバーにSSH接続したい場合がよくあるので作業を簡略化した。 どうやるか 以下をターミナルで実行 #!/bin/sh IP=`curl -s https://www.cman.jp/network/support/go_access.cgi | ggrep -oP '<div class="outIp".*?</div>' | sed 's#<div class="outIp">\(.*\)</div>#\1#'` SECURITY_GROUP_NAME='YOUR_SECURITY_GROUP_NAME' echo ${IP} aws ec2 authorize-security-group-ingress --group-name ${SECURITY_GROUP_NAME} --port 22 --protocol tcp --cidr ${IP}/32 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CloudFormationで別アカウントのリソースをパラメータで受け取る際にやるべきこと

何をするべきか 最初に結論。クロスアカウントのリソースをパラメータで受け取る際には、CloudFormationテンプレートのパラメータのタイプをString, CommaDelimitedListのどちらかにする必要がある。 Parameters: VPC: Type: String SecurityGroups: Type: CommaDelimitedList なぜStringなどしか指定できないのか CloudFormationテンプレートのパラメータとしてVPCやSecurityGroupを受け取ろうとした場合、下記のように書くことが多いと思う。 Parameters: VPC: Type: AWS::EC2::VPC::Id SecurityGroups: Type: List<AWS::EC2::SecurityGroup::Id> 単一アカウント上のリソースをパラメータに渡す際は上記がベストなのだが、クロスアカウントでリソースを参照しようとした場合、これではデプロイできない。 実際に、上記のようなCloudFormationテンプレートを用意して、デプロイ時にパラメータに別アカウントのリソースを入力した場合、下記エラーが発生してしまう。 Parameter validation failed: parameter value xxxx for parameter name yyy does not exists. CloudFormation はデプロイ前に入力値と指定されたパラメータタイプが一致しているかなどのバリデーションを実施してくれる。 この際、パラメータにAWS固有パラメータを指定していた場合は、対象のリソースが存在するかを確認してくれるのだが、ここで指定されたリソースが存在しないと判断された場合は、上記エラーが報告される。 「別アカウントには存在するリソースなのに、なぜないと判断される?」と考えそうになるが、そもそもあるアカウント上から許可なく別アカウントのリソースの存在有無を確認できたらおかしい。 なので、CloudFormationは実行されたアカウント内に対象のリソースが存在するかどうかを確認する。 というわけで、クロスアカウントでリソースを参照する場合、 CloudFormationのパラメータタイプにはリソースの存在確認が入るAWS 固有パラメータ(AWS::EC2::VPC::Id, List<AWS::EC2::SecurityGroup::Id>など)ではなく、StringかCommaDelimitedListを利用する必要がある。 なお、この話はしっかりとAWS公式のドキュメントに記載されている。 テンプレートユーザーが異なる AWS アカウントからの入力値を入力できるようにする場合は、AWS 固有のタイプでパラメータを定義することはできません。代わりに、String タイプ (または CommaDelimitedList) タイプのパラメータを定義してください。 パラメータ - CloudFormation 結論 CloudFormationのパラメータバリデーションは、単一アカウントのみであればとても助かる機能であり、パラメータの入力ミスを減らしてくれる。 しかし、クロスアカウントでリソース参照する場合には使うことができないので、 Parameters: VPC: Type: AWS::EC2::VPC::Id SecurityGroups: Type: List<AWS::EC2::SecurityGroup::Id> ではなく、 Parameters: VPC: Type: String SecurityGroups: Type: CommaDelimitedList としましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS認定ソリューションアーキテクトプロフェッショナル(SAP)を、再認定したふりかえりまとめ

はじめに 認定をぐるっと回って、SAPの3年の有効期限が来てしまいました。 前回は旧バージョンの試験の時で、今は出題傾向も変わってきているのなと思い、少しだけ情報整理をして臨みました。 本格的な試験対策の記事は多くの方が投稿されているかと思いますので、記録として、事前準備と再認定を受けてみて感じたことについて簡単に記載してみたいと思います。 本記事の主な対象者 AWS認定の他の試験区分は取得済みで、ソリューションアーキテクトプロフェッショナルの受験を検討している方 取得に向けて有効な学習方法などの情報収集したい方 筆者のAWS認定履歴 AWS認定 取得日 再認定日 ソリューションアーキテクト - アソシエイト 2018-05-13 (同SAP) デベロッパー - アソシエイト 2018-06-03 - SysOpsアドミニストレーター - アソシエイト 2018-06-10 - ソリューションアーキテクト - プロフェッショナル 2018-07-29 2021-06-05 DevOpsエンジニア - プロフェッショナル 2018-08-26 - データアナリティクス専門知識(ビッグデータ専門知識) 2019-08-25 2020-11-21 セキュリティ専門知識 2019-12-08 - 機械学習専門知識 2020-02-23 - 高度なネットワーキング専門知識 2020-06-20 - データベース専門知識 2020-07-04 - Alexaスキルビルダー専門知識 2020-07-24 - クラウドプラクティショナー 2020-07-27 - 事前準備 これまでの受験の過去の経験則から、ベストプラクティスを抑えるのは正答への近道です。 下記のドキュメントを、業務を振り返りながら読み返しました。 AWS Well-Architected フレームワーク 再認定を受けてみて感じたこと EC2ベースの3層アーキテクチャの分野は主たる戦場なので、ベストプラクティスを信じて選択しました。 サーバレス関連も同上。Lambdaまわりは日頃触れているので、正誤の取捨選択しやすい内容だったような気がします。 S3関連は、アクセスポイントやら何やらここ近年でサービスインしたキーワードがチラホラ散見され、3年前とは出題内容が違ってきているのかなと感じました。 オンプレ移行、DR要件は頻出な感じがしました。ゆえに原則・普遍的なパターンを抑えているのが肝要です。 Organizations、コスト関連は日頃あまり触っていないものあり、ここは事前に重点的にドキュメントを読み返しました。 Direct Connect も同上。ANSを取ったとはいえ、確信を持って回答できるものが少なかったです(XXXゲートウェイなど) 各専門知識の認定区分を一通り学習していると、類題シナジーがあったような気がします。 落ち着いて設問を読みましょう。キーワードが隠れている(はず) おわりに 定期的な知識の棚卸しや、新サービスのキャッチアップという観点で、3年の有効期限は意外と有用かなと思いました。今回新しく得られた知識もそれなりにありました。 今後受験を検討される方の一助になれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】ワンボタンで Lambda & API Gateway をディプロイする環境づくり(AWS-SAM + CloudFormation + CodeBuild)

はじめに Lambda関数をはじめとするAWSリソースの設置/更新はAWSコンソールから直接操作もできる。 とはいえ、事故リスクを減らすためにもできる限り自動化しておいたほうが安心安全。 ワンボタンでgithubからコード取得 〜 CloudFormationでLambdaと API Gatewayを自動更新する環境をつくる手順をまとめる。 サンプルコード↓ AWS-SAM + CloudFormation + CodeBuildを使うメリット AWSコンソールから各種リソースの設定を意図せずに変更するリスクを潰せる AWSリソースに関する設定変更の履歴を全てgitで管理できる Jenkinsなど別途環境を用意しなくても、AWSコンソール内でディプロイ作業を完結できる 更新作業を簡易化 & 効率化して、ディプロイ頻度を上げられる CloudFormation自体は無料(設置したリソースは課金される)、CodeBuildは100分/月までジョブ実行無料 やること AWS-SAMのテンプレートを用意して、CloudFormationで自動ディプロイする CodeBuildでディプロイジョブを用意して、ワンボタンでCloudFormationを呼べるようにする 必要なIAMの権限を整理する 設置したLambdaが動くことを確認する 前提 ちゃんと動くLambda関数のコードが用意されていること 本ページではTypescriptでnodejsランタイムを利用するが、 Lambda関数の処理内容やTypescriptのトランスパイル手順については触れない。 また、ここで紹介する手順はPythonをはじめとする他のランタイムにも応用できる。 本ページ上部のサンプルコードも参考に。 もくじ ディプロイジョブの成果物を保管するS3 bucketを作成する Lambdaに紐付けるIAMロールを作成する AWS-SAMのテンプレートファイルを作成する CloudFormationを動かすshellスクリプトを作成する CodeBuildのビルドジョブを作成する IAMの権限を整理する ビルドジョブを実行して、CloudFormationを見守る 生成されたLambdaを実行してみる 1. ディプロイジョブの成果物を保管するS3 bucketを作成する 1. uni-cloudformation-artifact-dev 2. uni-codebuild-artifact 本ページでは上記の名称でS3 bucketを2つ新規作成する。 それぞれ名称の通り、CloudFormationとCodeBuildが走った際に生成される成果物を保存するのに使う。 S3 bucketを作成する手順(1-1を参照)↓ 2. Lambdaに紐付けるIAMロールを作成する 新規設置するLambdaに紐付けるIAMロールを作成する。 (IAMロールの自動生成もたぶんできるが、個人的には事故を起こしそうで怖いため権限周りは手動でやる。) IAMのコンソールを開いたら、Create roleをクリック。 Lambdaのテンプレートを選択して次へ。 Lambda関数の処理内容に応じて必要な権限を追加して次へ。 (本ページで設置するLambda関数はpingを返すだけなので、 なにも権限を付与しない空のIAMロールを作成する。) 作成するIAMロールの名称を設定して、完了。 ここではlambda-sam-deploy-ts-roleとした。 IAMロール作成完了したら、arnを控えておく(手順3で使う)。 3. AWS-SAMのテンプレートファイルを作成する そもそもAWS-SAMとは↓ 下記の通り、CloudFormationに読み込ませるテンプレートファイル(template.yaml)を用意する。 注意すべきはRole, CodeUri, Handler, Runtimeの4箇所。 Roleには手順2で作成したIAMロールのarnを書く。 CodeUriとHandlerにはLambdaに配置する関数コードのファイルパスを書くが、 template.yamlを起点とする相対パスで書く点に注意。 下記のtemplate.yamlの場合、各ファイルの位置関係の例は下記の通り。 index.jsではhandler関数が定義されており、これがLambda関数として設置される想定。 template.yamlとindex.jsのファイルパス ocean_lambda_sam_deploy_ts/ ├ aws/ - sam/ - dev/ - template.yaml └ built/ - dev/ - ping/ - index.js template.yaml AWSTemplateFormatVersion: "2010-09-09" Transform: AWS::Serverless-2016-10-31 Globals: Function: Timeout: 2 Api: OpenApiVersion: 3.0.2 Resources: UniLambdaBasicApi: # API Gatewayの設定 Type: AWS::Serverless::Api Properties: Name: ApigwSamDeployTypescript # 設置するリソース名(任意) StageName: dev GatewayResponses: DEFAULT_4xx: ResponseParameters: Headers: Access-Control-Expose-Headers: "'WWW-Authenticate'" Access-Control-Allow-Origin: "'*'" UniPingFunc: # Lambdaの設定 Type: AWS::Serverless::Function Properties: FunctionName: PingLambdaSamDeployTypescript # 設置するリソース名(任意) Runtime: nodejs14.x # 利用するランタイム Role: arn:aws:iam::${12桁のAWS-ID}:role/lambda-sam-deploy-ts-role # 手順2で作ったIAMロール CodeUri: ../../../built/dev/ping # Lambda関数が置いてあるファイルパス Handler: index.handler # Lambda関数の ${ファイル名.メソッド名} MemorySize: 128 Events: Vote: Type: Api Properties: Path: /ping # Lambdaを実行するパス Method: get RestApiId: !Ref UniLambdaBasicApi # 紐付けるAPI Gateway 4. CloudFormationを動かすshellスクリプトを作成する そもそもCloudFormationとは↓ shellスクリプトでaws-cliからCloudFormationを呼んで、手順3で用意したtemplate.yamlを渡す。 このshellスクリプト(lambda_sam_deploy_ts.sh)を下記のファイルパスに設置する。 lambda_sam_deploy_ts.shのファイルパス ocean_lambda_sam_deploy_ts/ ├ aws/ - sam/ - dev/ - template.yaml | └ - code_build/ - lambda_sam_deploy_ts.sh | └ built/ - dev/ - ping/ - index.js lambda_sam_deploy_ts.sh #!/bin/sh if [ $# < 1 ]; then echo "[ERROR!] one or more arguments are required" exit -1 fi STAGE=$1 # このサンプルではdev/prdを第一引数として受け取って代入する npm i npm run build-${STAGE} aws cloudformation package \ --template-file aws/sam/${STAGE}/template.yaml \ --s3-bucket uni-cloudformation-artifact-${STAGE} \ # 手順1で作成したS3 bucket --s3-prefix sam/lambda_sam_deploy_ts/${STAGE}/package \ # 任意のファイルパス --output-template-file packaged.yaml aws cloudformation deploy \ --template-file packaged.yaml \ --stack-name lambda-sam-deploy-ts # 任意のスタック名 5. CodeBuildのビルドジョブを作成する 5-1. buildspec.yamlを作成する CodeBuildのジョブで処理される内容はbuildspec.yamlで定義される。 このyamlファイルをgitにpushしておいて、後ほどジョブ新規作成時に参照パスを指定すると、 定義された処理内容を順に実行してくれる。 本ページの例では、手順4で作成したlambda_sam_deploy_ts.shを実行するような buildspec.yamlを作成し、下記のファイルパスに設置した。 (ややこしくなってしまったので、ページ上部のサンプルコードも参考に) buildspec.yamlのファイルパス ocean_lambda_sam_deploy_ts/ ├ aws/ - sam/ - dev/ - template.yaml | └ - code_build/ - lambda_sam_deploy_ts.sh | └ - dev/ - buildspec.yaml └ built/ - dev/ - ping/ - index.js buildspec.yaml version: 0.2 phases: pre_build: commands: - echo "start->lambda_sam_deploy_ts" build: commands: - sh aws/code_build/lambda_sam_deploy_ts.sh dev # 手順4で作成したshellを実行(引数devを渡す) post_build: commands: - echo "finish->lambda_sam_deploy_ts" artifacts: files: - built/dev/**/* # 手順1で作成したS3 bucketに設置する成果物 5-2. CodeBuildのコンソールからプロジェクトを新規作成する CodeBuildのコンソール画面を開いて、Create build projectをクリック。 任意のプロジェクト名を設定する。 ① ジョブで使うコードの参照元を選択する(本ページではGitHubを選択)。 ② 本ページではパブリックリポジトリを選択(プライベートリポジトリでは、アカウント認証が必要) ③ リポジトリのURLを指定 ④ 参照するブランチを指定 (参照先にLambda関数のコード、shellスクリプト、template.yaml、buildspec.yamlがあること) ① 実行環境を選択(よしなに) ② 新規作成するプロジェクトに紐付けるIAMロールを指定 (本ページではcodebuild-sam-deoloy-service-roleという名称でIAMロールを新規作成) ① 実行するジョブの処理内容を定義する方法を選択 ② buildspec.yamlのファイルパスを指定 (本ページでは手順5-1で作成したbuildspec.yamlを選択) ビルドの成果物を保存する設定(よしなに) (本ページでは手順1で作成したS3 bucketにトランスパイル済のjsファイルを保存) CloudWatchにログを出力する設定(よしなに) 全て設定したら、Create build projectをクリック これでCloudFormationを起動するプロジェクトを作成できた(まだ実行しない)。 6. IAMの権限を整理する IAMロールのコンソールを開いて、手順5-2で作成したCodeBuildのIAMロールを選択 このIAMロールに紐付いたIAMポリシーを選択 Edit policyをクリック 画面上部のJSONタブをクリックする。 必要な権限に応じてJSONの内容を書き換えたら、Review policyをクリック (本ページでは下記LambdaSamCreationPolicyDev.jsonの通りIAMポリシーを設定した。) LambdaSamCreationPolicyDev.json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Resource": [ "arn:aws:logs:ap-northeast-1:260006775748:log-group:/aws/codebuild/lambda_sam_deploy_ts", "arn:aws:logs:ap-northeast-1:260006775748:log-group:/aws/codebuild/lambda_sam_deploy_ts:*" ], "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ] }, { "Effect": "Allow", "Resource": ["arn:aws:s3:::codepipeline-ap-northeast-1-*"], "Action": [ "s3:PutObject", "s3:GetObject", "s3:GetObjectVersion", "s3:GetBucketAcl", "s3:GetBucketLocation" ] }, { "Effect": "Allow", "Resource": [ "arn:aws:s3:::uni-codebuild-artifact", "arn:aws:s3:::uni-codebuild-artifact/*" ], "Action": ["s3:PutObject"] }, { "Effect": "Allow", "Resource": [ "arn:aws:s3:::uni-cloudformation-artifact-dev", "arn:aws:s3:::uni-cloudformation-artifact-dev/*" ], "Action": ["s3:GetObject", "s3:PutObject"] }, { "Effect": "Allow", "Action": [ "codebuild:CreateReportGroup", "codebuild:CreateReport", "codebuild:UpdateReport", "codebuild:BatchPutTestCases", "codebuild:BatchPutCodeCoverages" ], "Resource": [ "arn:aws:codebuild:ap-northeast-1:260006775748:report-group/lambda_sam_deploy_ts-*" ] }, { "Effect": "Allow", "Action": ["iam:PassRole"], "Resource": ["arn:aws:iam::260006775748:role/lambda-sam-deploy-ts-role"] }, { "Effect": "Allow", "Action": [ "cloudformation:CreateStack", "cloudformation:CreateChangeSet", "cloudformation:DescribeStacks", "cloudformation:DescribeChangeSet", "cloudformation:DescribeStackEvents", "cloudformation:ExecuteChangeSet", "cloudformation:GetTemplateSummary" ], "Resource": ["*"] }, { "Effect": "Allow", "Action": [ "lambda:CreateFunction", "lambda:DeleteFunction", "lambda:GetFunction", "lambda:AddPermission", "lambda:RemovePermission", "lambda:UpdateFunctionCode" ], "Resource": ["*"] }, { "Effect": "Allow", "Action": [ "apigateway:GET", "apigateway:POST", "apigateway:PATCH", "apigateway:DELETE" ], "Resource": ["*"] } ] } 付与した権限の一覧に誤りが無いか確認して、Save changesをクリック。 7. ビルドジョブを実行して、CloudFormationを見守る これでジョブ実行の準備が整った。 CodeBuildのコンソールに戻って、Start buildをクリック。 buildspec.yamlの内容に沿って、順番に処理が実行される。 ジョブ実行中もしくは終了後に、CloudFormationのコンソールを開いて、新規作成したStackを選択。 画面上部のEventタブをクリックすると、CloudFormationくんが頑張っている様子を覗ける。 Statusが全てCREATE_COMPLETEになって、CodeBuildのジョブが完了したら、おわり。 8. 生成されたLambdaを実行してみる Lambdaのコンソールを開いて、新規作成されたFunctionをクリック。 Lambdaに紐付けられたAPI Gatewayをクリック。 Detailsタブをクリックすると展開されるAPI endpointのURLをクリック。 Lambda関数が実行されて、レスポンスが返ってきた✌? トラブルシュート CloudFormationが途中でコケる だいたいIAMロールの権限が足りていない/権限の付与対象が間違っている。 手順7に従ってCloudFormationののEventのエラーログを見れば足りていない権限がわかる場合が多い。 あるいはCodeBuildやCloudWatchに出力されたログが役に立つかもしれない。 CloudFormationのstackが中途半端なStatusのまま残る リソース作成に失敗したとき、stackのStatusがUPDATE_ROLLBACK_FAILEDから変わらなくなることがある。 これはリソースの作成に失敗したが、失敗したリソースを消すこともできずに立ち往生している状態を指す。 この状態では、CodeBuildからビルドジョブを再実行してもstackを生成できずにジョブがコケる。 CloudFormationのコンソールから、DeleteをクリックしてStackを削除すれば良い。 (stackに紐付けられたリソースが全削除されるため注意が必要) 参考↓ webpack5でトランスパイルしたTypescriptで、index.handlerが認識されない 個人的にハマったところ。 詳細は下記ページで議論されている。 とりあえず、webpack4を使うか、CloudFormationの代わりにserverlessを使うのが手っ取り早い(?) (本ページの根底から揺らぐけど)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む