20211129のAWSに関する記事は25件です。

文系卒の新人が伝えたいエンジニアへの道 社内新人研修編

はじめに ここでは僕が新人研修で学んだことを伝えたいと思います。研修期間は3ヶ月です。会社としては上流工程から下流工程までの案件を引き受けている SIer 企業です。その企業がどのような新人研修をしたかというのを参考にしていただけたらなと思います。 新人研修新人研修で行ったことは以下の通りです。 基本的な知識のインプット(SQL, Java, Git, etc..) 社内サービス改善 社内サービス作成 そしてここでは3つ目の社内サービスの作成について話していきたいと思います。 サービス概要 サービスの内容 社内向け書籍購入ウェブアプリケーション 使用用途 社員が読んだ書籍のレビューをし書籍の情報共有をしあう 使用した言語 JavaScript フロントエンド:React バックエンド:Node.js インフラ環境 AWS この開発は新人8人(フロントエンド:4人、バックエンド:4人)で行いました。僕はバックエンド側の担当していたためバックエンド側の話をしたいと思います。またバックエンドの中でもAPI実装チームとインフラ構築チームの2人ずつに分かれて開発を進めていきました。 インフラ環境 ここではこのサービスのインフラ環境の説明をしていきます。MicrosoftやGoogleもクラウドサービスを提供していますが今回はAWSのクラウドコンピューティングシステムを利用してインフラ環境を構築しました。 環境としてはこんな感じです。 流れとしては、クライアントからリクエストを送信し、そのリクエストを Route 53 が受信します。その受け取った情報を Amazon CloudFront に流し Nginx へとリクエストを送ります。その受け取った情報をもとに戻り値を作成しクライアントに適切な情報を返します。 それでは今回使用したツールの紹介をしていきます。 Route 53 Route 53 はAWSの一つです。特徴としては以下があります。 SLA 100% ドメイン登録 低価格 • SLA SLA は Service Level Agreement の略です。サービスレベル合意書やサービス品質保証などと日本語では訳されます。サービス提供者と利用者の間で結ばれるサービスレベル(定義、範囲、内容など)に関する水準を細かく規定しておき、これを破った場合のペナルティについての契約書です。主に通信サービスやクラウドサービス、連絡サーバーで直っでよく用いられます。 • ドメイン登録 Route 53 では新しくドメイン名を登録することができます。また他のレジスト等で登録したドメインをRoute 53 上に移管することもできます。しかし、一度登録したドメイン名は変更できません。 • 低価格 Route 53 の場合使用した分だけ支払う仕組みなので使う側としては安心できるサービスとなっています。 そして Route 53 では負荷が高くなれば自動的にサーバの台数を増やしたりサーバを強化する機能が備わっています。このような性能の良さが Route 53 には備わっています。 Amazon CloudFront Amazon CloudFront はCDN(Contents Delivery Network)の一つで、高い安全性とデベロッパーの利便性を備えています。 CloudFront を導入しているサービスとして以下の事例があります。 Amazon Prime Video Hulu この用に多量のアクセスやデータのリクエストが来たとして対処できるような機能が備わっています。CloudFront の場合、使用した量だけ支払う料金システムになっています。また50GBのデータ送信、200万件のHTTPSリクエストなら1年間無料となっているのでAWS初心者の方でも始めやすいかなと思います。 VPC Virtual Private Cloud の略でAWSアカウント内に構築できる仮想ネットワークです。図にようにVPC内でEC2などのAWSのサービスを配置することができます。またVPCは複数のVPC間の接続も可能です。ネットワーク環境の構築の際、ハードウェア、回線、データセンターなど用意すべきものが多いためコストと時間がかかります。しかし、AWSでVPCをネット環境として準備すると数分でネットワークを構築することでができます。 EC2 AWSを利用する際によく使われるのが EC2 です。EC2は「Amazon Elastic Compute Cloud」の略でクラウド環境において様々な役割を担ってくれます。EC2を利用することで得られるメリットをいくつか挙げてみました。 使用した分だけ課金 インフラ環境構築時間の短縮 冗長化の負荷分散 • 使用した分だけ課金 EC2は従量課金になっているため、使用した分だけの請求が行われます。また3つの料金形態が存在するためそれぞれのサービスの目的によって料金のお得度が変わるため使用する際は料金形態のチェックも必要になるでしょう。 • インフラ環境構築時間の短縮 インフラ環境の構築においてハードウェア設定が必要な物理サーバーなどを使用する場合、設定完了まで数日かかることもありますが、EC2は数分あれば立ち上げが完了します。この圧倒的スピードで環境作成ができるEC2はプロジェクトにメリットを与えるでしょう。 • 冗長化の負荷分散 仮想サーバの複製などが容易にできるため複数の台数を用意することで安定したサーバの運用が可能です。そのためシステムのトラブルで、万が一サーバがダウンしたとしても他のサーバでサービスを運用することが可能です。 Nginx NginxはWebサーバの一つで、Apacheと比べて高速で大量処理が得意といったメリットがあります。静的コンテンツ処理には適しているためBookclubではNginxを採用しました。また設定も簡単にできるため管理者側としては使いやすいサービスになっています。しかし、大量の動的コンテンツの処理には向いないため、動画を扱ったWebサイトなどの運営には適さないでしょう。また初心者に対しての情報が少ないというデメリットもあります。 2021年6月時点でのWebサーバソフトウェアのシェア(出典:W3Techs.com)ではApacheを抜きNginxが1位となっています。 PM2 PM2はNode.jsをアプリケーションごとに本番環境で起動するためのツールです。またログ管理やモニタリング(CPUやメモリ)が可能です。Node.jsアプリケーションのプロセスの状態を常に監視しており、プロセスを自動で再起動させる機能を備えています。PM2は3つのプロダクトがあり、今回のBookclubではPM2 Runtimeを使用しています。 ↑PM2 Runtimeはチェックが必要 Node.js Node.jsはJavaScriptをサーバサイドでも動くようにしてくれるツールです。詳しくいうと、ブラウザ上で動作するJavaScriptをOS上でも動くようにしたのがNode.jsです。しかし、Node.jsはDjangoやLaravelなどのWebフレームワークではなくあくまでもJavaScriptの実行環境です。 Node.jsの特徴は「軽量」であるということです。例えば、リアルタイムで複数の人が使用する場合でもスムーズに動作してくれるという魅力があります。 開発 開発ではAPI作成からテストまでを行いました。順序としては、API仕様書を作成し、それに沿ったコードを実装をしていきました。そしてその実装が正しいか、またサーバの強度は十分に足りているかというテストをしました。 開発の中で僕が印象に残ったものをピックアップし紹介していきます。 Swagger Advanced REST client / Postman Jmeter Swagger 開発で一番最初に躓いた場面がこのSwaggerでした。Swagger とは REST API を構築するためのオープンソースのフレームワークのことです。APIも知らなかった僕はこれがどう開発でいかされるのか全く想像がつきませんでした。書き方が全然わからない中で講師の方に紹介していただいたのがSwagger Editorでした。ここではすでに Swagger のコードが書かれているため、これを参考にサービスの API を作っていきました。 Advanced REST client / Postman ローカル環境で開発をしている際にHTTPのリクエスト送信やレスポンス確認をするために使用したのが Advanced REST client と Postman です。curlをたたいて実行はできますが Advanced REST client これは Google の拡張機能で色んな処理をブラウザ上で実行することができるため便利です。また頻繁に使うリクエストの保存、履歴参照の機能も備わっています。 Postman これは有名な Web API のテストクライアントのひつとです。様々なテストのシナリオを実行することができます。また、認証、テスト、ドキュメント作成、バージョン管理などいろんな種類の要素を統合してテスト開発ができるため、ログイン、非ログインのユーザの実装も簡単にできる便利なツールです。API設計・開発する際は是非使ってみて下さい。 Jmeter Jmeter の説明をしていく前にテそもそもストではどういったことを行なっていったのかの説明から始めます。 テスト 開発で欠かせないのがテストです。テストはもちろんしたことがなかったので、何をどのようにしたらいいか、またどのようなエビデンスを取らなければいけないのかすらわかっていませんでした。 ここで僕たちが行ったテストで必要であった要素を紹介します。 全ての想定される実装の洗い出し 想定された戻り値を返しているか サーバの負荷強度 全ての想定される実装の洗い出し これに関しては自分たちが書いたコードの中で実装されているAPIの洗い出しをしました。 しかし、今思うとディシジョンテーブルを使用するべきだったなと思います。ディシジョンテーブルを使用することでテストの抜け漏れを防ぐことができます。また、テスト直前にディシジョンテーブルを作成するのではなく開発前、設計の段階でディシジョンテーブルを作成する方がよりベターです。 想定された戻り値を返しているか この想定された戻り値に関しては先ほど紹介した Swagger を参照し、どのような戻り値を返さなければいけないのかの確認が必要です。このAPI開発ではAPI仕様書をもとに開発が進められているため、フロント側もバックエンド側も開発を進めてAPI仕様書を見て実装していかなければいけません。ここで使用するツールは Advanced REST client や Postman などももちろん使えますが、この後に紹介する Jmeter を使うことも可能です。 サーバの負荷強度 このサーバの負荷強度でとても役に立ったのが Jmeter です。この Jmeter は良いところは自動的にいくつもの実装を行なってくれる、いわゆる自動化です。この自動化テストを行うことでサーバの強度を図ることができます。 例えば1分間の間にn回サーバにアクセスが来た場合(サイトを開いた、投稿した、投稿を削除した、etc...)の実行結果を出すことができます。この数が多い方がサーバの強度が高いことを意味します。しかし、ここで重要なのがCDNを含まないようにすることです。僕たちは最初いくらnの回数を上げても全てのリクエストが正常に返ってくるため、サーバの強度高いなと勘違いしていました。しかしこれは間違いで僕たちが測っていた強度はサーバではなく CloudFront の強度を測っていたのです。そのため、サーバの負荷テストを行うときはCDNを外すという作業を忘れないようにしなければなりません。 最後に 今回は新人研修で作ったサービスについて書いてみました。いかがでしたでしょうか? インフラ周りなどの担当していない部分についてはまだまだ勉強不足な面がありもっともっと知識をつけなければいけないなとこの投稿を書きながら思いました。 この研修での1番の収穫は「知らないことばかりだな」と知らないことを知ることができました。この先も勉強を続けて知らない部分知り、いろんなものを吸収していこうと思います。 最後まで読んでいただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CLIで Web サイトを構築、管理、運用する(4日目)

3日目では、更新作業後の公開設定の簡略化を目的としたバケットポリシーについてお伝えしました。 4日目は、2日目で設定したログファイルのリテンションを設定し、無尽蔵にログファイルが増え続けないようにします。 4日目の要約 ログファイルが無尽蔵に増えないようにするよ! AWS CLI の準備 このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。 なんなら、 AWS CloudShell で実行するのも楽でよいと思います。 この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。 バージョン1 https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html バージョン2 https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html 概要 ライフサイクルを設定して一定期間が過ぎたログファイルを削除する。 さあ、やってみよう! ログファイルを収集し始めたはよいが、その後どうするかを考える際の用件というのは多々あります。 30日過ぎたら、アクセス頻度が低くなるので安い階層に移す? 業界基準でン年は保存しておかないとならない? 常にセキュリティ監視のためにいじらない? このあたりを自動的にやってくれるのが、ライフサイクル設定です。 ライフサイクルを設定する ライフサイクルのルール(設定)は JSON 形式で指定します。3日目にお伝えした通り、AWS CLI は JSON ファイルを読み込むことができるので、ルールを記述した JSON ファイルを先に作成しておくと作業がしやすいです。 ライフサイクルルールのファイルを作成する このルールは、ログファイルが格納されたら30日後に削除されるものです。 必要に応じて期間を長くしたり、階層(Glacerに移行など)設定などを盛り込むとよいでしょう。 lifecycle.json { "Rules": [ { "Filter": { "Prefix": "/" }, "Status": "Enabled", "Expiration": { "Days": 30 }, "ID": "logfile" } ] } コマンドを実行する s3api put-bucket-lifecycle-configuration コマンドでライフサイクルを設定します。 aws s3api put-bucket-lifecycle-configuration --bucket <LogsBUCKETNAME> --lifecycle-configuration file://lifecycle.json このコマンドも正常終了しても特に何も返しません。 念のため、 s3api get-bucket-lifecycle-configuration コマンドで設定が入っていることを確認します。 aws s3api get-bucket-lifecycle-configuration --bucket <LogsBUCKETNAME> ライフサイクル設定がなされていると、以下の様に JSON 形式で設定内容が出力されます。順序が前後しているかもしれませんが、同等の内容が出力されます。 { "Rules": [ { "Expiration": { "Days": 30 }, "ID": "logfile", "Filter": { "Prefix": "/" }, "Status": "Enabled" } ] } また、このライフサイクル設定が入っていると、ログファイル(オブジェクト)に有効期限日時が設定されます。 この有効期限日時を過ぎると、ライフサイクルで設定された動作を行います(この例であれば削除される)。 この有効期限日時は、s3api head-object コマンドで確認できます。 aws s3api head-object --bucket <LogsBUCKETNAME> --key <OBJECTNAME/LogFile> 有効期限日時が設定されていると、Expiration に日時が設定されています。 { "AcceptRanges": "bytes", "Expiration": "expiry-date=\"Wed, 30 Dec 2021 00:00:00 GMT\", rule-id=\"logfile\"", "LastModified": "2021-11-29T05:12:48+00:00", "ContentLength": 4551, "ETag": "\"*******************************\"", "ContentType": "text/plain", "Metadata": {} } 削除動作はこの日時が過ぎ去るまで待つか、試用として、有効期限を1日程度にしてみて確認するのもよいでしょう。 まとめ S3 で配信している Web サイトへのアクセスログを無尽蔵に増やすことなく一定期間保持できるようになりました。ログだけではなく、IoT のセンシングデータを一定期間保持して削除するなどといった使い方もできますね。 しかし、まだまだWeb サイトの構築や管理、運用については問題や課題がありますよね。 引き続き見ていきます。 今回使ったコマンド s3api put-bucket-lifecycle-configuration s3api get-bucket-lifecycle-configuration s3api head-object
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[AWS] AWS Single Sign-On で Organizations 管理外の AWS アカウントにアクセスする

複数の AWS アカウントを使い分けている場合、AWS Organizations と、AWS Single Sign-On を組み合わせてユーザ管理するってことが多いと思います。 AWS Single Sign-On では、AWS Organizations 配下ではない AWS アカウントに対してもアクセスできるように設定することができるので、必要に応じて使ってみましょう。 なお Organizations 配下の AWS アカウントに対しては提供されているコマンドラインアクセス用のトークン( "Command line or programmatic access" のリンクから取得できるもの )は提供されず、AWS ダッシュボードにアクセスできるだけとなりますので、ご注意ください。 SSO 側の AWS アカウント アプリケーションとして External AWS Account を追加 Applications から New Application として External AWS Account を追加します。 次に表示される画面で AWS SSO SAML metadata file をダウンロードしておきます。 他の箇所は特に必要する必要はないです。 Display name と Description だけ、お好みで変更してください。 SSO される側の AWS アカウント SSO される側の AWS アカウントでは、IAM で Identity Providor を追加し、それに割り当てる IAM ロールを作成します。 AWS SSO からアクセスした際に、その IAM ロールが利用されます。 Identity Providor の追加 IAM のサイドメニューから Identity providers を選択して Add providor しましょう。 Providor name を適当に設定し Metadata document として、先ほど SSO 側の AWS アカウントでダウンロードした AWS SSO SAML metadata file をアップロードします。 作成した Identity providor をクリックして詳細を表示し、ARN をコピーしておきます。 arn:aws:iam::{AWS account ID}:saml-provider/{Providor name} のような形式になっていると思います。 これは後で使います。 IAM ロールの割り当て 作成した Identity providor をクリックして詳細を表示し、Assign role をクリックします。 新しく IAM Role を作ってもいいし、既存の IAM Role を使ってもいいです。 作成した IAM Role の ARN もコピーしておいてください。 arn:aws:iam::{AWS account ID}:role/{Role name} のような形式になっていると思います。 これも後で使います。 SSO 側の AWS アカウント 再び SSO側の AWS アカウントに戻ってきます。 ここで、先ほど作った Identity providor と IAM Role の情報を設定します。 Attribute mapping の追加 作成したアプリケーションをクリックして詳細を表示し、Attribute mappings タブで Add new attribute mapping をクリックして追加します。 内容は以下の通り 項目 値 User attribute in the application https://aws.amazon.com/SAML/Attributes/Role Maps to this string value or user attribute in AWS SSO arn:aws:iam::{AWS account ID}:saml-provider/{Providor name},arn:aws:iam::{AWS account ID}:role/{Role name} Format unspecified Maps to this string value or user attribute in AWS SSO は Identity Providor の ARN と IAM Role の ARN を , 区切りで書いてください。 アプリケーションへのユーザ( or グループ )の割り当て あとは、このアプリケーションを使用できるユーザ(or グループ)を割り当ててあげれば良いです。 Organizations 配下の AWS アカウントのようにパーミションセットを設定して複数の権限を割り当てることはできないので、複数の権限を割り当てたい時は、複数のアプリケーションを作成してください。 これで AWS SSO のスタート画面に External AWS Account が追加されているはずです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでポケモン図鑑をつくってAWSでデプロイまでしてみた

株式会社やどかりのカイノと申します。 アドベントカレンダーを作ってみんなに書いてください〜って言ってやってもらってる張本人です。 今年もう少しで終わりだー今年もクソお世話になりましたー! やどかり&ネッコス Advent Calendar 2021の1日目を担当させていただきます。 明日は圭ちゃんです はじめに 2020年8月に入社して2021年6月までWeb開発をやってました。 2021年の7月からAndroid開発とWeb開発の2足のワラジでやってます。 今年はエンジニアとしてだけでなくマネジメント系の仕事もかなり増えててそっちもめちゃくちゃ面白いのですがやっぱりコード書いている時間は楽しいなとこの開発をしている時に改めて思いました。 去年も似たような記事を書いたのですが以前よりはレベルアップしているなー 、というか分かったこと増えたなーと感じました。 とはいえまだまだわからないところは多いので精進していきます。 ということで学んだことの棚卸しもしながら作りました 使用言語 Laravel 6.20 php 7.4 ポケモン図鑑つくりました 作ったことなかったので一度作りたかったんですよね。 仕事の合間に結構急ぎで作ったので色々と雑になってます。ドメインもまだ設定してないです。 csv配布 ポケモン各種情報が入ったDBです。 これ使ってぜひみなさんもポケモンの何かアプリ作ってみてください。 クリックしたらダウンロードできます サイトはこんな感じです 一覧画面 レイアウトはテキトーです。 詳細画面 フシギダネは最初に仲間にしたポケモンなのでめちゃめちゃ覚えてるし好きですね。 種族値も取れることを知ったので徹底攻略みたいに表示してみました。 やったこと Laravelでコマンド作成 ポケモンAPIを叩く 取得したAPIを各パラメータに入れる DB設計&作成 ポケモンごとにDBにインサート 画面に表示 種族値も表示してみた AWSでサーバ構築してデプロイ Laravelでコマンド作成 app/Console/Commands/GetPokemonInfo.php このpathに下記のようにコマンド作成 今回はphp artisan command:getpokemoninfoで実行できるようにしました。 <?php namespace App\Console\Commands; require "vendor/autoload.php"; use Illuminate\Console\Command; use App\Models\Pokemon; use GoogleTranslate\GoogleTranslate; class GetPokemonInfo extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'command:getpokemoninfo'; /** * The console command description. * * @var string */ protected $description = 'Command description'; /** * Create a new command instance. * * @return void */ public function __construct() { parent::__construct(); } /** * Execute the console command. * * @return mixed */ public function handle() { try { $p_no_array = range(1, 898); foreach ($p_no_array as $no) { // すでに取得済みの場合はスキップ $pokemon = Pokemon::where('p_id', $no)->first(); if (!empty($pokemon)) { continue; } // ポケモンごとの情報をcurlで取得 $url = 'https://pokeapi.co/api/v2/pokemon/'.$no; $curl = curl_init($url); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET'); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); $r = curl_exec($curl); $data = json_decode($r, true); $p_info = self::getPokemonInfo($data); print_r($p_info['id']); print_r($p_info['jp_name']."\n"); print_r($p_info['en_name']."\n"); print_r("\n"); $p_info = Pokemon::createPokemon($p_info); sleep(1); } curl_close($curl); } catch(Exception $e) { echo $e->getMessage(); } } static function getPokemonInfo($d) { $p_info = []; // パラメータを設定 $p_info['id'] = $d['id']; $p_info['en_name'] = $d['name']; $from = "en"; // English $to = "ja"; // 日本語 $st = new GoogleTranslate($p_info['en_name'], $from, $to); $p_info['jp_name'] = $st->exec(); $p_info['type1'] = $d['types'][0]['type']['name']; if (isset($d['types'][1])) { $p_info['type2'] = $d['types'][1]['type']['name']; } else { $p_info['type2'] = null; } $p_info['ability1'] = $d['abilities'][0]['ability']['name']; if (isset($d['abilities'][1]) && !$d['abilities'][1]['is_hidden']) { $p_info['ability2'] = $d['abilities'][1]['ability']['name']; $p_info['hidden_ability'] = $d['abilities'][2]['ability']['name']; } else { $p_info['ability2'] = null; if (isset($d['abilities'][1]['is_hidden'])) { $p_info['hidden_ability'] = $d['abilities'][1]['ability']['name']; } else { $p_info['hidden_ability'] = null; } } $p_info['hp'] = $d['stats'][0]['base_stat']; $p_info['attack'] = $d['stats'][1]['base_stat']; $p_info['defense'] = $d['stats'][2]['base_stat']; $p_info['special_attack'] = $d['stats'][3]['base_stat']; $p_info['special_defense'] = $d['stats'][4]['base_stat']; $p_info['speed'] = $d['stats'][5]['base_stat']; $p_info['total_stats'] = $p_info['hp'] + $p_info['attack'] + $p_info['defense'] + $p_info['special_attack'] + $p_info['special_defense'] + $p_info['speed']; $p_info['front_default'] = $d['sprites']['front_default']; $p_info['back_default'] = $d['sprites']['back_default']; if (isset($d['sprites']['other']['dream_world'])) { $p_info['dream_world_front_default'] = $d['sprites']['other']['dream_world']['front_default']; } else { $p_info['dream_world_front_default'] = null; } if (isset($d['sprites']['other']['home'])) { $p_info['home_front_default'] = $d['sprites']['other']['home']['front_default']; } else { $p_info['home_front_default'] = null; } if (isset($d['sprites']['other']['official-artwork'])) { $p_info['official_artwork_front_default'] = $d['sprites']['other']['official-artwork']['front_default']; } else { $p_info['official_artwork_front_default'] = null; } $p_info['height'] = $d['height']; $p_info['weight'] = $d['weight']; return $p_info; } } このコマンドでやりたいことは 「ポケモンAPIから取得した各ポケモン情報をパラメータに格納してDBにインサートさせる」ことです。 ポケモンAPIを叩く $p_no_array = range(1, 898); foreach ($p_no_array as $no) { // すでに取得済みの場合はスキップ $pokemon = Pokemon::where('p_id', $no)->first(); if (!empty($pokemon)) { continue; } // ポケモンごとの情報をcurlで取得 $url = 'https://pokeapi.co/api/v2/pokemon/'.$no; $curl = curl_init($url); curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET'); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); $r = curl_exec($curl); $data = json_decode($r, true); $p_info = self::getPokemonInfo($data); print_r($p_info['id']); print_r($p_info['jp_name']."\n"); print_r($p_info['en_name']."\n"); print_r("\n"); $p_info = Pokemon::createPokemon($p_info); sleep(1); } curl_close($curl); ポケモンAPIのurlにcurlでアクセスして受け取ったJSONをデコードしてます。 これを1~898回繰り返します。 図鑑No.1のフシギダネからNo.898バドレックスまでです。 サーバに攻撃にならないようにsleep()をいれて間隔を少しだけ空けてます。 取得したAPIを各パラメータに入れる getPokemonInfoというfunctionを呼び出して 各パラメータの中にポケモン情報を格納しています。 https://pokeapi.co/api/v2/pokemon/1 を実行してみるとわかるのですが情報がめちゃめちゃあってどれを使おうかを決めて各パラメータにいれねばなりません。 地味な作業でした。。。 static function getPokemonInfo($d) { $p_info = []; // パラメータを設定 $p_info['id'] = $d['id']; $p_info['en_name'] = $d['name']; $from = "en"; // English $to = "ja"; // 日本語 $st = new GoogleTranslate($p_info['en_name'], $from, $to); $p_info['jp_name'] = $st->exec(); $p_info['type1'] = $d['types'][0]['type']['name']; if (isset($d['types'][1])) { $p_info['type2'] = $d['types'][1]['type']['name']; } else { $p_info['type2'] = null; } $p_info['ability1'] = $d['abilities'][0]['ability']['name']; if (isset($d['abilities'][1]) && !$d['abilities'][1]['is_hidden']) { $p_info['ability2'] = $d['abilities'][1]['ability']['name']; $p_info['hidden_ability'] = $d['abilities'][2]['ability']['name']; } else { $p_info['ability2'] = null; if (isset($d['abilities'][1]['is_hidden'])) { $p_info['hidden_ability'] = $d['abilities'][1]['ability']['name']; } else { $p_info['hidden_ability'] = null; } } $p_info['hp'] = $d['stats'][0]['base_stat']; $p_info['attack'] = $d['stats'][1]['base_stat']; $p_info['defense'] = $d['stats'][2]['base_stat']; $p_info['special_attack'] = $d['stats'][3]['base_stat']; $p_info['special_defense'] = $d['stats'][4]['base_stat']; $p_info['speed'] = $d['stats'][5]['base_stat']; $p_info['total_stats'] = $p_info['hp'] + $p_info['attack'] + $p_info['defense'] + $p_info['special_attack'] + $p_info['special_defense'] + $p_info['speed']; $p_info['front_default'] = $d['sprites']['front_default']; $p_info['back_default'] = $d['sprites']['back_default']; if (isset($d['sprites']['other']['dream_world'])) { $p_info['dream_world_front_default'] = $d['sprites']['other']['dream_world']['front_default']; } else { $p_info['dream_world_front_default'] = null; } if (isset($d['sprites']['other']['home'])) { $p_info['home_front_default'] = $d['sprites']['other']['home']['front_default']; } else { $p_info['home_front_default'] = null; } if (isset($d['sprites']['other']['official-artwork'])) { $p_info['official_artwork_front_default'] = $d['sprites']['other']['official-artwork']['front_default']; } else { $p_info['official_artwork_front_default'] = null; } $p_info['height'] = $d['height']; $p_info['weight'] = $d['weight']; return $p_info; } DB設計&作成 今回はpokemonテーブルのみ作りました。 Laravelのマイグレーションで作っています。 <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreatePokemonsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('pokemons', function (Blueprint $table) { $table->bigIncrements('id'); $table->integer('p_id')->nullable(); $table->string('jp_name')->nullable(); $table->string('en_name')->nullable(); $table->string('type1')->nullable(); $table->string('type2')->nullable(); $table->string('ability1')->nullable(); $table->string('ability2')->nullable(); $table->string('hidden_ability')->nullable(); $table->integer('hp')->nullable(); $table->integer('attack')->nullable(); $table->integer('defense')->nullable(); $table->integer('special_attack')->nullable(); $table->integer('special_defense')->nullable(); $table->integer('speed')->nullable(); $table->integer('total_stats')->nullable(); $table->string('front_default')->nullable(); $table->string('back_default')->nullable(); $table->string('dream_world_front_default')->nullable(); $table->string('home_front_default')->nullable(); $table->string('official_artwork_front_default')->nullable(); $table->integer('height')->nullable(); $table->integer('weight')->nullable(); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('pokemons'); } } ポケモンごとにDBにインサート インサートするファンクションをModel内で作成しました。 ここはシンプル。 <?php namespace App\Models; use Illuminate\Database\Eloquent\Model; class Pokemon extends Model { protected $table = 'pokemons'; protected $dates = ['publication_date', 'expiration_date', 'registration_date']; protected $fillable = [ 'p_id', 'jp_name', 'en_name', 'type1', 'type2', 'ability1', 'ability2', 'hidden_ability', 'hp', 'attack', 'defense', 'special_attack', 'special_defense','speed', 'total_stats', 'front_default', 'back_default', 'dream_world_front_default', 'home_front_default', 'official_artwork_front_default', 'height', 'weight' ]; static function createPokemon($p_info) { if (empty($pokemon)) { self::create([ 'p_id' => $p_info['id'], 'jp_name' => $p_info['jp_name'], 'en_name' => $p_info['en_name'], 'type1' => $p_info['type1'], 'type2' => $p_info['type2'], 'ability1' => $p_info['ability1'], 'ability2' => $p_info['ability2'], 'hidden_ability' => $p_info['hidden_ability'], 'hp' => $p_info['hp'], 'attack' => $p_info['attack'], 'defense' => $p_info['defense'], 'special_attack' => $p_info['special_attack'], 'special_defense' => $p_info['special_defense'], 'speed' => $p_info['speed'], 'total_stats' => $p_info['total_stats'], 'front_default' => $p_info['front_default'], 'back_default' => $p_info['back_default'], 'dream_world_front_default' => $p_info['dream_world_front_default'], 'home_front_default' => $p_info['home_front_default'], 'official_artwork_front_default' => $p_info['official_artwork_front_default'], 'height' => $p_info['height'], 'weight' => $p_info['weight'] ]); } return true; } } 画面に表示 <div id="pokemon_block"> <div class="wrap-inner"> <ul class="poke_list flexlist"> <?php foreach ($pokemons as $pokemon) : ?> <li class="item"> <a href="{{ route('poke_show', $pokemon['id']) }}"> No.<?php echo $pokemon['id'] ?> <?php echo $pokemon['jp_name'] ?> <div class="imgarea"> <img src="<?php echo $pokemon['front_default'] ?>" alt=""> </div> </a> </li> <?php endforeach ?> </ul> </div> </div> 一旦全部のポケモンを表示させました。 'front_default'は画質が低いので大きな遅延はないのですが、他の重めの画像だと表示までに30秒くらいはかかってました。 今回は'front_default'を選んで遅延がないようにしました。 ただ、すこーしだけ遅いので全件表示よりは各世代ごとに分けて出すとかにする方が現実的ですね。 種族値も表示してみた <?php for($i = 0; $i < $pokemon['hp']/10; $i++): ?> <span class="single_gauge">▓</span> <?php endfor ?> "▓"という文字列を種族値/10の回数分だけ表示するように出しています。 これでこのポケモンの種族値の高さざっくりと見やすくなりましたね。 さてはこの種族値は何でしょう? ↓下のリンクが答えです。 AWSでサーバ構築してデプロイ サーバ構築はシンプルにしています。 ・ec2 linux/apache ・EIP ・RDS ハッサムが好き! かっこいい系のポケモンが好きなんですよね。 ハッサムはアルファサファイアやってた時にサイクルパでめっちゃ使ってましたね~ 使いやすいしかっこいいし(語彙力) おわりに これで終わりです。 もっと色々やりたかったのですが、とりあえずここまでにしました。 今回でポケモンDBを作れたので今後色々なアプリに使えるなーと思ってます。 勉強がてら遊びがてら使っていこうと思います。 今年も仕事納め前で絶賛忙殺中な毎日ですがわずかな合間を縫って10時間以内で全部作りました。 0から開発してサーバにアップするまでのスピード感も少しずつ付いてきた気がします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CLIで Web サイトを構築、管理、運用する(3日目)

2日目ではアクセスロギングの設定を入れて、記録が取れるようにしました。 Web サイトは公開したら終わりじゃないですよね?定期的に更新しますよね。 というわけで、更新時にひっかかる問題を解決していきましょう。 3日目の要約 記事更新時のちょっとひと手間を解消していくよ! AWS CLI の準備 このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。 なんなら、 AWS CloudShell で実行するのも楽でよいと思います。 この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。 バージョン1 https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html バージョン2 https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html 概要 Amazon Simple Storage Service(Amazon S3)のバケットポリシーを AWS CLI で設定、構築する さあ、やってみよう! その前に 1日目で開設した Web サイトにアクセスしてみましょ curl http://<BUCKETNAME>.s3-website-ap-northeast-1.amazonaws.com 以下の様に出力されるはずです。 <!DOCTYPE html> <html lang="ja"> <head> <title>Advent calendar 2021</title> </head> <body> <h1>Hello world!!</h1> </body> </html> 1日目に作成したファイルが残っていれば以下の様に編集ください。なければ、以下をコピペでも構いません。 h2 タグの行を追加しています。 index.html <!DOCTYPE html> <html lang="ja"> <head> <title>Advent calendar 2021</title> </head> <body> <h1>Hello world!!</h1> <h2>Advent calendar 2021 DAY 3</h2> </body> </html> このファイルを s3 にアップロードします。 s3 cp コマンドを使用します。 aws s3 cp ./index.html s3://<BUCKETNAME>/index.html upload: ./index.html to s3://<BUCKETNAME>/index.html アップロードができたら、更新できているか curl コマンドでアクセスしてみます。 curl http://<BUCKETNAME>.s3-website-ap-northeast-1.amazonaws.com すると、どうでしょう。HTTP403 Forbidden が返ってくるではないですか! <html> <head><title>403 Forbidden</title></head> <body> <h1>403 Forbidden</h1> <ul> <li>Code: AccessDenied</li> <li>Message: Access Denied</li> <li>RequestId: ***********</li> <li>HostId: ***********************************************</li> </ul> <hr/> </body> </html> 1日目を振り返っていただくとわかると思いますが、「コンテンツを公開する」をしないとならないのです。 同名のファイルといえど、オブジェクトストレージの仕様によりオブジェクト(ファイル)がごっそりと書き換えられるで、公開設定が無い状態になってしまいます。 アップロードと合わせて公開設定を有効化するようにしてもよいですが、バケットポリシーを設定することで、恒久的に公開状態(読み取りOK)状態にすることが可能です。 ただし、対象のバケット内が公開状態になるので、機密情報や個人情報が入ったファイルの格納はしないようにしてください。また、そういったファイルが入る可能性があるのであれば、個別に公開設定をいれるようにしてください。 以上を踏まえてつづきをどうぞ。 バケット ポリシー を設定する バケットポリシーは JSON 形式で指定します。AWS CLI は JSON ファイルを読み込むことができるので、ポリシーを記述した JSON ファイルを先に作成しておくと作業がしやすいです。 バケットポリシーファイルを作成する 好きなテキストエディタで以下の内容のJSONファイル(テキストファイル)を作成します。 <BUCKETNAME> の箇所はS3 静的 WEB サイトホスティングを行っているバケット名を指定します。 bucketpolicy.json { "Version":"2012-10-17", "Statement":[ { "Sid":"PublicWebHosting", "Effect":"Allow", "Principal": "*", "Action":"s3:GetObject", "Resource":"arn:aws:s3:::<BUCKETNAME>/*" } ] } コマンドを実行する s3api put-bucket-policy コマンドでバケットポリシーを設定します。 aws s3api put-bucket-policy --bucket <BUCKETNAME> --policy file://bucketpolicy.json このコマンドも正常終了しても特に何も返しません。 Web サイトへアクセスする バケットポリシーを設定したので、改めて、 curl コマンドでアクセスしてみます。 curl http://<BUCKETNAME>.s3-website-ap-northeast-1.amazonaws.com バケットポリシーが意図した通りに設定できていれば、以下の様に出力されます。 <!DOCTYPE html> <html lang="ja"> <head> <title>Advent calendar 2021</title> </head> <body> <h1>Hello world!!</h1> <h2>Advent calendar 2021 DAY 3</h2> </body> </html> h2 タグの行が入ったコンテンツが無事に取得できました。 適宜、ファイルを更新しアップロードを行って動作確認をしてみてください。 まとめ S3 で配信している Web サイトのコンテンツを更新する場合でも公開設定が必要なことがわかりました。 機密情報や個人情報、センシティブな情報がない Web サイトの場合は本設定をいれることで運営・運用がしやすくなると思います。 ※個別公開も公開手順の整備やスクリプト化すればよいだけの話ではありますが? 今回使ったコマンド s3 cp s3api put-bucket-policy
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「DNS_PROBE_FINISHED_NXDOMAIN」の切り分け

chromeで「このサイトにアクセスできません」と書かれている下DNS_PROBE_FINISHED_NXDOMAINと書いてあった場合の切り分け備忘録。 AWSでの構築前提。 1.ALBのDNS名へのアクセスを試す これでアクセスできれば2.へ進む アクセスできないならALBより先の切り分けを行う 2.NSレコードの状態を見る こうなればNSレコードが消えているのでドメイン管理している先を確認 $host -tNS testapp.com Host testapp.com not found: 3(NXDOMAIN) こうなるとドメインに問題がない為Route53のレコードにNSレコードが登録されているか確認する $host -tNS testapp.com testapp.com name server ns-xxx.awsdns-xx.org. testapp.com name server ns-xxx.awsdns-xx.com. testapp.com name server ns-xxx.awsdns-xx.net. testapp.com name server ns-xxx.awsdns-xx.co.uk.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

terraformが実行可能か速攻で実験したい場合の手順

概要 時々、localでterraformの動きを確認したいことがあります。 その時に早く実験を開始できる方法を書いておきます AWSの設定をする。 ターミナルでterraformは実行すると思いますが、ターミナル上でのAWSの設定がそのままterraformのデプロイ先になります。 ~/.aws/configや環境変数を利用してaws cliが叩けるようにしておきます。 そこで、以下のコマンドで現在のユーザーを確認しておきます。 aws sts get-caller-identity ユーザーを間違えて、商用アカウントなどにデプロイしないように注意しましょう terraformのファイルを記述 適当な場所にディレクトリを作ります。 その中にファイルを作っていきます。 必要なのは2つのファイルです provider.tf provider "aws" { region = "ap-northeast-1" } iam.tf resource "aws_iam_group" "test" { name = "testgroup" } IAM User Groupの作成で動作確認するのがおすすめ。 terraform init terraform initで初期設定をおこないます。aws providerなど勝手に設定してくれます。 適用 以下の順番で terraform fmtでフォーマットを整える terraform validで変なとこがないか見ておく terraform planで適用内容を確認 terraform applyで適用し、実行できたことをaws consoleなどで確認する。 後片付け terraform plan --destroyで適用内容を確認 terraform destroyで適用し、リソースがなくなったことをaws consoleなどで確認する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

OpenID Connectを使い、Github ActionsからAWSをより安全に利用する

この記事は、マイナビ Advent Calendar 2021 4日目の記事です。 概要 本記事は、2021年11月1日にGitHubから発表された、GitHub ActionsによるOpenID Connectの概要や実装方法等をまとめた記事になります。 GihHub ActionsでAWSの永続的なクレデンシャル(IDやパスワードをはじめとする、ユーザ等の認証に用いられる情報の総称)を渡すことなく、IAM Roleが利用できるようになったので、その紹介です。 前提知識 GitHub Actions GitHubのCI/CDツール。この記事では、ワークフロー(リポジトリに追加する自動化された手順。 スケジュールまたはイベントによって動く。 ワークフローを使用して、GitHubでプロジェクトをビルド、テスト、パッケージ、リリース、またはデプロイできる。)が分かっていれば読み進められると思います。 詳しい内容はこちらの記事を参考にしてください。 OAuth 権限譲渡のプロトコル。OAuthによって、ユーザのアクセス権限を様々なアプリに渡すことが出来る。その際は、アクセストークンを発行することでアクセス権限を移譲するため、IDやパスワードなどの情報をアプリ側に渡さずに済む。 詳しい内容はこちらの記事を参考にしてください。 OIDC(OpenID Connect) OAuthを基にして作られた、より安全に認証することが出来る技術。アクセス権を移譲する際に、アクセストークンと同じタイミングでIDトークンも発行する。 詳しい内容はこちらの記事を参考にしてください。 はじめに 今年の9月頃、以下のツイートが話題になりました。 Ok I blogged about it. That's how excited I am. 1. Deploy this CFN template2. Write this GHA workflow3. Never worry about IAM users again https://t.co/KJrr2Jw4bE pic.twitter.com/9IcocgurxP— Aidan W Steele (@__steele) September 15, 2021 その後11月に正式にリリースされました。 これにより、GitHub ActionsからAWSを利用するときに必要だった、GitHub Actions実行用のアクセスキーが不要になります。 理由は、IAMでIDプロバイダを作り、GitHub側のOIDCで許可することにより、GitHub側からの権限の呼び出しを行えるからです。 詳しい設定内容等は実装を確認してください。 Github ActionsによるOIDC Github ActionsによるOIDCは上記のダイアグラムのようになっています(ダイアグラム参照記事)。 ① AWSにアクセスするリソースやIAM Roleなどがこれにあたります。 ② GitHub Actionsのワークフローが実行されるたびに、GitHub Actionsの環境変数から、OIDCプロバイダーがIDトークンを自動生成します。 ③ GitHubのOIDCプロバイダーが自動生成したトークンを基に、AWSリソースへのアクセスをリクエストします。 ④ ③のリクエストが問題ない場合、短期間だけ利用できるクラウドアクセストークンをGithub側に返すことで、Github ActionsからAWSリソースへアクセスすることが出来ます。 詳しくはこちらをご覧ください(英語)。 実装 ワークフローを実装するにあたり、 リポジトリを作成し 利用するIAMロールを作り ワークフローを作成する という順番で進めていきます。 1. リポジトリの作成 Githubで適当にリポジトリを作成します。 2. IAM Roleの作成 Github Actionsで使用する、IAM Roleと認証連携のためのIDプロバイダを作成します。 2.1 IDプロバイダの作成 IAM>IDプロバイダに画面遷移する 「プロバイダを追加」を押下する プロバイダのタイプを「OpenID Connect」にする プロバイダのURLにhttps://token.actions.githubusercontent.comと入力し、「サムプリントを取得」を押下する 対象者には、sts.amazonaws.comと入力する 右下の「プロバイダを追加」を押下する。 2.2 IAM Roleの作成 IAM>ロールに画面遷移する 「ロールを作成」を押下する 「信頼されたエンティティの種類を選択」で「ウェブID」を選択する 「IDプロバイダー」のプルダウンで、上記で設定したIDプロバイダを選択する 「Audience」で、上記で設定した「sts.amazonaws.com」を選択する 「次のステップ:アクセス権限」を押下する。 「Attachアクセス権限ポリシー」「タグの追加」は特に変更せず次のステップに進む 「確認」画面で任意の名前を入力し、「ロールの作成」を押下する 特定アカウントやリポジトリに対する設定はこちらを確認してください。 3. ワークフローの作成 今回私は、GithubのGUIでファイルを作成したので、その方法を記載します。 すでにあるリポジトリ内にワークフローを作成する場合は、リポジトリの.github/workflow配下に、yml形式のファイルを作成してください。そのファイルに、下記で記載するoidc_test.ymlのコードを入力して下さい。その後pushすることで、Github Actionsが走ります。 1.で作成したリポジトリの画面で「Actions」画面に遷移する 「Set up this workflow」を押下する ファイル名を任意で入力し、ファイル内に以下のコードを記載する 「Start commit」を押下して、create oidc_test.ymlなどのコミットメッセージを入力し、「Commit new file」を押下する。 oidc_test.yml name: OIDC test on: push: jobs: build: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - run: sleep 5 - name: Configure AWS run: | export AWS_ROLE_ARN=arn:aws:iam::XXXXXXXXXXXX:role/github-actions-oidc-test export AWS_WEB_IDENTITY_TOKEN_FILE=/tmp/awscreds export AWS_DEFAULT_REGION=ap-northeast-1 echo AWS_WEB_IDENTITY_TOKEN_FILE=$AWS_WEB_IDENTITY_TOKEN_FILE >> $GITHUB_ENV echo AWS_ROLE_ARN=$AWS_ROLE_ARN >> $GITHUB_ENV echo AWS_DEFAULT_REGION=$AWS_DEFAULT_REGION >> $GITHUB_ENV curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE - run: aws sts get-caller-identity ポイントはpermissions: id-token: writeの部分です。これを指定することで、IDトークンの取得に必要な情報が環境変数に設定されます。 run: sleep 5の部分は合っても無くても大丈夫です。 Configure AWS: runの部分では、IAMロール・トークンファイルパス・リージョンを、Github Actionsの環境変数にセットし、Githubに対してトークンリクエストを投げています。 awscliでは、AWS_ROLE_ARNに作成したIAMロールのarnを、AWS_WEB_IDENTITY_TOKEN_FILEにトークンファイルのファイルパスをセットしておくことで、トークンファイルを読み込み、設定したIAMロールをAssumeRoleWithWebIdentityで呼び出して使用してくれます(AWS CLIのドキュメント)。 最後のcurl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com"で、Githubに対してトークンを発行するためのリクエストを投げています。 参考にした記事では、GitHub Actionsが持っている$ACTIONS_ID_TOKEN_REQUEST_TOKENの値を使って$ACTIONS_ID_TOKEN_REQUEST_URLにリクエストを送り、一時トークンを発行しているというようなことが記載されていました。 4. 動作確認 それでは実際にGithub Actionsを実行してみます。 「Actions」>「OIDC test(ymlファイルに設定したワークフロー名)」>「create oidc_test.yml(コミットメッセージ)」>「build」と遷移すると以下の画面に行きつくと思います。 もし記事の通り実装した場合、ファイルを編集してコミットしたので、すでにワークフローが実行されている状態になります。 緑色のチェックマークが表示されていれば成功です。 マスクしていますが、UserId、Account、Arnに正しい情報が表示されていました。 補足 configure-aws-credentialsライブラリの利用 本記事のワークフローの作成では、一通りの処理の流れを追うために、runにコマンドを直書きして実装しました。 現在はコマンド直書きで利用するよりも、aws-actions/configure-aws-credentialsライブラリを利用したほうがスッキリするのでその方法も紹介したいと思います。 IDプロバイダやロールは実装で作成したままの設定で問題ありません。 oidc_test.yml name: AWS Deploy on: push env: AWS_ROLE_ARN: arn:aws:iam::XXXXXXXXXXXX:role/github-actions-oidc-test permissions: id-token: write contents: read jobs: aws-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: aws-actions/configure-aws-credentials@master with: role-to-assume: ${{ env.AWS_ROLE_ARN }} aws-region: ap-northeast-1 - run: aws sts get-caller-identity この方法では、envにロールのarnを設定し、permissions.id-tokenを同じくwriteにすることで、同じようにAssumeRoleし、AWSリソースにアクセスできます。 成功しました。 特定のアカウントやリポジトリに対するロールの設定 IDプロバイダの作成で、対象者にsts.amazonaws.comを対象者として設定しました。 この場合、AWSアカウントIDとIAMロール名の二つが分かっている人であれば、誰でもAssumeRoleが可能になってしまいます。 IAM Roleを以下のように設定することで、指定したOrganization、もしくはAccount配下のリポジトリのみで該当ロールを使用することが出来ます。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::XXXXXXXXXXXX:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringLike": { "token.actions.githubusercontent.com:sub": "repo:${Organization名 or Account名}/*" } } } ] } さらにrepo:${Organization名 or Account名}/${リポジトリ名}:*と指定すれば、指定したリポジトリ内でしか使用できません(*はブランチ名を指定できます)。指定したリポジトリのGithub Actionsで発行したトークンだけが、ロールを引き受けるようにしているため、安全に使用できます。 まとめ この機能によって、Github ActionsからセキュアにAWSにアクセスできるようになりました。 OIDCはGithub Actionsに実装された機能なのでGCPでも利用できるようです。 参考になれば幸いです。 参照 https://dev.classmethod.jp/articles/github-actions-without-permanent-credential/ https://zenn.dev/yutaro1985/articles/b012f69b49bec095b9f1 https://zenn.dev/mryhryki/articles/2021-09-19-access-aws-by-github-actions https://tech.guitarrapc.com/entry/2021/11/05/025150
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS re:Invent 2021で発表された新サービス/アップデートまとめ

AWS re:Invent 2021の会期中に発表された新サービス/アップデートのまとめです。 今年も、後から出来るだけ素早く簡単に振り返ることができるようにまとめました! ※以下、会期中、随時更新いたします。 凡例 (無印) 新サービス (Update) 既存サービスのアップデート (APN) パートナー制度に関連したリリース/アップデート 日時は日本時間で表記します。 11/29(月) 今年の開幕は「Amazon IoT RoboRunner」でした。 Midnight Madness (15:30-) Amazon IoT RoboRunner ロボットと作業管理システムを接続し、単一のシステムビューを介した業務作業全体のオーケストレーションが可能 詳細はYouTubeでも公開 https://youtu.be/q8POXvJ4nMI AWS Management Consoleで1つの施設(サイト名と場所など)を作成すると、AWS IoT RoboRunnerが施設、ロボット、目的地、タスクのデータを保存するリポジトリを自動的に作成 ロボットが「Fleet」として設定され、個々のロボットはFleet内の「Robot」としてAWS IoT RoboRunnerに設定 Fleet Gateway Libraryを利用し、個々のFleetからデータを送受信するためのアプリケーションを開発可能 ロボットの状態、システムのステータス、タスクの進捗状況を取得するAPIを利用し、複数のロボットを一元管理するアプリケーションを開発可能 Others Amazon CloudWatch RUM アプリケーションのクライアントサイドのパフォーマンスを監視 Webアプリケーションのクライアントサイドの問題を特定してデバッグし、エンドユーザーのデジタルエクスペリエンスを向上させることができるリアルユーザーモニタリング機能 CloudWatch RUMを使用すると、異なるロケーション、ブラウザ、およびデバイス間でアプリケーションのパフォーマンスをほぼリアルタイムで確認でき、パフォーマンスの最適化が可能 ページロードステップ、コアウェブバイタル、およびJavaScriptとHttpエラーを含むアプリケーションのパフォーマンスの異常を表示可能 問題によって影響を受けたセッションの数を把握し、修正すべき問題の優先順位付けも可能 CloudWatch ServiceLensやAWS X-Rayとの統合により、クライアントサイドからバックエンドのインフラストラクチャノードへのトレースを簡単に相関して確認可能 CloudWatch RUMの登場で、CloudWatchをエンドツーエンドのモニタリングに利用可能となった Amazon CloudWatch Evidently アプリケーション開発者がアプリケーションスタック全体で新機能を安全に検証するための新機能 新機能を立ち上げる際、開発者は一部のユーザーにその機能を公開し、ページのロード時間やコンバージョンなどの主要な指標を監視した後、一般的な使用のために安全にトラフィックを開放可能 主要なビジネス指標やパフォーマンス指標を監視しながら、制御された方法でトラフィックを開放するための詳細なスケジューリング機能を提供 起動時に問題が発生した場合、安全な状態にロールバックするためのアラームを定義可能 Amazon CloudWatch Metrics Insights CloudWatchの各メトリクス履歴に対してSQLベースでクエリ可能 例えば傾向やパターンをリアルタイムに特定し、分析することにより、課題解決までの時間を短縮することが期待 Metrics Insightsには標準的なSQL言語が付属 ビジュアルクエリビルダを使ってMetrics Insightsを始めることも可能 Metrics Insightsは、Amazon Managed Grafanaコンソールでも利用可能 Amazon EC2 M6a インスタンス オールコアターボ周波数3.6GHzの第3世代AMD EPYC(コードネーム:Milan)プロセッサを搭載しており、M5aインスタンスと比較して最大35%の価格性能の向上、および同等のx86ベースのEC2インスタンスと比較して10%のコスト削減を実現 AWS Nitro System M5aよりも2つ多いインスタンスサイズ(32xlargeおよび48xlarge)を提供し、48xlargeサイズでは最大192のvCPUと768GiBのメモリを搭載 M6aは、M5aの2倍以上となる、最大50Gbpsのネットワーク速度と、Amazon Elastic Block Storeへの40Gbpsの帯域幅を提供 48xlargeサイズでは、Elastic Fabric Adapterを使用でき、低遅延でスケーラブルなノード間通信が可能 Amazon EC2 G5g インスタンス ゲームのストリーム配信、費用対効果の高い推論を求めている機械学習、CPUの性能に敏感なMLモデルの実行、NVIDIAのAIライブラリを活用するワークロードなどに最適 64ビットのArm Neoverse N1コアを使用 AWS Graviton2プロセッサは、第一世代のAWS Gravitonプロセッサと比較して、パフォーマンスが7倍、コンピュートコア数が4倍、キャッシュ数が2倍、メモリ数が5倍となり、パフォーマンスと機能が大きく向上 AWS Graviton2プロセッサは、第一世代のAWS Gravitonプロセッサと比較して、常時256ビットのDRAM暗号化機能を備え、コアごとの暗号化性能が50%向上 AWS Nitro Systemをベースに構築 AWS AI for data analytics (AIDA) データサイエンスの経験が少ない人でも予測分析を導入できるよう支援するAWSパートナーのソリューション群 Amplitude, Anaplan, Causality Link, Domo, Exasol, Interworks, Pegasystems, Provectus, Qlik, Snowflake, Tableau, TIBCO, and Workato (Update) AWS Migration Hub Refactor Spaces 既存のアプリケーションをリファクタリングし、マイクロサービスをベースにした分散型アプリケーションにマイグレーションするための、AWS Migration Hubの新しい機能 (Update) Amazon Braket Hybrid Jobs Variational Quantum Eigensolver(VQE)やQuantum Approximate Optimization Algorithm(QAOA)などのクラシカルなハイブリッド量子アルゴリズムを簡単に実行でき、クラシカルな計算リソースと量子コンピューティングデバイスを組み合わせて、今日の量子システムのパフォーマンスを最適化 (Update) 継続的な脆弱性管理のための新しい「Amazon Inspector」を発表 新しいAmazon Inspectorでは、ワンクリックで組織全体のサービスを有効にすることが可能 Inspectorは自動的にすべてのワークロードを検出し、ソフトウェアの脆弱性や意図しないネットワークへの露出がないかを継続的にスキャン可能 Amazon Elastic Container Registry(ECR)をサポートしたことで、ECRに格納されているコンテナイメージの脆弱性を把握することが可能 Amazon Systems Manager(SSM)エージェントを、EC2の脆弱性スキャンに使用 情報は、Amazon Security Hubに転送され、Amazon EventBridgeにプッシュされることで、パートナーソリューションとの連携の自動化を実現し、解決までの平均時間(MTTR)を短縮可能 (Update) AWS Compute Optimizerがリソース効率のメトリクスを提供 コスト削減機会とパフォーマンス改善機会という2つの新しいダッシュボードレベルのメトリクスにより、優先順位をつけ、コスト最適化機会を素早く特定することが可能 コスト削減機会メトリクス (saving oppotunity) は、Compute Optimizerの推奨事項を採用することで、アカウントレベル、リソースタイプレベル、リソースレベルで達成できるAmazon EC2、Amazon EBS、AWS Lambdaの月間節約額を定量化 パフォーマンス改善機会メトリクスは、アカウントレベルおよびリソースタイプレベルで、プロビジョニング不足のリソースの割合と数を定量化 リソースのボトルネックのリスクに対処するパフォーマンス改善の機会を評価し、優先順位を決定可能 (Update) AWS Compute Optimizerがインフラストラクチャメトリクスを強化し、ルックバック期間を3ヶ月に延長 インフラストラクチャのメトリクスを強化し、EC2インスタンスとAuto Scalingグループの推奨品質を高める有料の推奨環境設定機能を新たに提供 この機能を有効にすると、メトリクスのルックバック期間が3カ月に延長 個々のリソース、またはAWSアカウントやAWS組織レベルで有効化可能 (Update) Amazon CodeGuru Reviewer、JavaおよびPythonリポジトリでハードコードされたシークレットを検出可能に ユーザー名やパスワード、データベースの接続文字列、トークン、AWSや他のサービスプロバイダーのAPIキーなどがコード内にあるかどうかを特定可能 新しいリポジトリをAmazon CodeGuru Reviewerに追加すると、secrets detectorがPythonとJavaのソースを自動的に検索し、設定ファイルやドキュメントファイルに加えて、シークレットを探索 (Update) Amazon Personalizeにインテリジェントなユーザーセグメンテーションを導入 機械学習を用いて、アイテムとユーザーの関係を学習 Amazon Personalizeは、異なる商品、カテゴリー、ブランドなどに対するユーザーの好みに基づいて、ユーザーをセグメント化 マーケティングキャンペーンでの高いエンゲージメントの促進、ターゲットを絞ったメッセージングによるリテンションの向上、マーケティング費用の投資対効果の向上などが期待 (Update) Amazon Personalize、メディア&エンタテインメントおよびリテール向けにパーソナライズされた体験を提供するために最適化されたレコメンダーを導入 MLの専門知識がなくても、アプリケーションに高性能なパーソナライズされたユーザー体験を迅速かつ簡単に提供 (Update) AWS ChatbotがSlackでのAWSリソースの管理に対応 AWS ChatbotでAWS CLIコマンドを使用したAWSリソース管理が可能 SlackのチャネルからAWSリソース管理が可能 例えば、EC2インスタンスのスケール、AWS Systems Managerランブックの実行、AWS Lambdaの同時実行制限の変更など (Update) Amazon ECR、キャッシュリポジトリのプルスルー機能を提供 パブリックにアクセス可能なレジストリからのイメージを自動的に同期するために設計された新機能「pull through cache repositories」をサポート 使用することで、パブリックイメージがECRレジストリにキャッシュされ、パブリックイメージの同期に伴う運用負荷が低下する パブリックレジストリをソースとするコンテナイメージを最新の状態に保つことができ、パブリックイメージとプライベートイメージの両方にECRの機能を利用可能 (Update) AWS Well-Architected ReviewでCustom Lensesが利用可能に 独自の柱、質問、ベストプラクティス、有用なリソース、改善計画を作成し、カスタムレビューツールとして利用可能 (Update) AWS Systems Managerを使用したAWS IoT Greengrassのエッジデバイス管理 エッジデバイスの管理者は、「AWS IoT Greengrass」を使ってエッジアプリケーションスタックを管理するとともに、「AWS Systems Manager」を使ってOSのアップグレードやメンテナンスタスクのスケジュール設定、エッジデバイス群へのリモートアクセスが可能 (Update) Amazon Timestream、スケジュールされたクエリなど3つの新機能 スケジュールされたクエリ、マルチメジャーレコード、および磁気ストレージへの書き込みという3つの新機能を追加 マルチメジャーレコードは、行ごとに1つのメジャーを格納するのではなく、複数の時系列メジャーを1つのテーブル行に格納 最適化されたデータレイアウトにより、テーブルに格納されるデータ量が減少するため、データストレージにかかるコストを削減し、クエリのパフォーマンスを向上させ、分析クエリにかかるコストを最小限に抑えることが可能 マルチメジャーレコードにより、既存のリレーショナルデータベースからAmazon Timestreamへの時系列データおよびクエリの移行を最小限の変更で簡単に行うことが可能 磁気 (Magnetic) ストアは、到着が遅れたデータの非同期処理や、長期的なデータ保存、高速な分析クエリなどに利用 (Update) AWS Ground Station、Software Defined Radiosのサポート拡大 SDRの対応周波数をワイドバンドの400Mhzに拡大することで、SDRのパートナーが新たな変調方式や符号化方式を提供できるようになり、アースイメージング事業者や大学、政府などの運用コストの最適化に貢献 11/30(火) Global Partner Summit Keynote (8:00-10:00) AWS Data Exchange for APIs AWS SDKを使用した一貫したアクセス、および一貫したAWSネイティブの認証とガバナンスにより、サードパーティのAPIを見つけ、サブスクライブし、使用できる新しい機能 AWS SDKを使用して、お好みのプログラミング言語で、RESTfulまたはGraphQLのAPIコールをAWS Data Exchangeに直接行い、必要な情報を含む同期レスポンスを受け取ることが可能 AWS Marketplace for Containers Anywhere サードパーティのKubernetesアプリケーションを、Amazon EKS Anywhereを使用したオンプレミス環境や、Amazon Elastic Compute Cloud(Amazon EC2)内のお客様が自己管理するKubernetesクラスターにデプロイ可能 最終的にどこにデプロイする予定かに関わらず、単一のカタログを使用してコンテナイメージを見つけることが可能 (APN) AWS Partner Paths 2022年1月28日より、新しいAWSパートナーパスを導入 ソフトウェアパス(旧ISV)、ハードウェアパス、トレーニングパス、ディストリビューションパス、サービスパス(コンサルティング、プロフェッショナル、マネージド、付加価値再販サービス) の5つのパートナーパスとなる 各パートナー企業は複数のパスを取得可能 (APN) AWS Graviton Ready Program OSやプラットフォームサービス、セキュリティ、モニタリングやオブザーバビリティ、CI/CD、データやアナリティクス、クラウドデバイスなど、AWSのお客様にGraviton対応のソフトウェア製品を提供するパートナーを識別しフィーチャーするプログラム AWS Graviton Ready Programに参加する企業には、AWS Gravitonベースのインスタンス上で、ソリューションの検証、最適化、サポートを行うことを期待 (APN) AWS Energy Competency エネルギー業界の課題を解決するための技術的な専門知識を持ち、顧客に繰り返し成功を収めている専門性の高いAWSパートナーを差別化するためのプログラム (APN) AWS Migration and Modernization Competency ビジネスケース分析、アプリケーションモビリティ、データモビリティ、アプリケーションの監視とオーケストレーション、レコメンデーションなどを実行するお客様のソリューションを開発・実装する専門知識を持つ、検証済みのAWSパートナーを差別化するためのプログラム Others Karpenter AWSで構築されたオープンソースの柔軟で高性能なKubernetesクラスタオートスケーラー アプリケーションの負荷の変化に応じて適切なサイズのコンピュートリソースを迅速に起動することで、アプリケーションの可用性とクラスターの効率を向上 AWSを利用しているKubernetesのお客様の半数近くが、Kubernetes Cluster Autoscalerを使用してクラスタのオートスケーリングを設定することは困難であり、制限が多いと報告していたことに対応 Amazon EBS Snapshots Archive EBSボリュームのスナップショットを長期保存するための新しいストレージ層 EBS Snapshots Archiveは、規制やコンプライアンス上の理由、または将来のプロジェクトリリースのために90日以上保持する必要があるEBSスナップショットの完全なポイントインタイムコピーをアーカイブするための低コストのストレージ層を提供 EBSスナップショットのアーカイブと管理が容易になり、スナップショットを管理するためのカスタムスクリプトやサードパーティのツールが不要 アクセス頻度の低いスナップショットをEBS Snapshots Archiveに移行することで、ストレージコストを最大75%削減し、さらにサードパーティ製ツールのライセンスコストを回避 アーカイブされたスナップショットを24~72時間以内に取得し、復元した後にスナップショットを使用してEBSボリュームをリカバリ可能 保存されたデータは1GB月あたり0.0125ドル、データを1GB取得するのに0.03ドルのコストが発生 課金対象は最低でも90日分 スナップショットのアーカイブを作成してから90日以内に削除したり、永久にリストアした場合でも、90日分の料金を請求 Recycle Bin for EBS Snapshots EBSスナップショットを削除すると、Binと呼ばれるゴミ箱に移動するようになり、Bin内のスナップショットはポリシーに従って一定期間後に削除されるようになる EBSスナップショットの誤削除に伴うトラブルを回避することが可能 AWS Control Tower Account Factory for Terraform TerraformとAWS Control Towerを統合するソフトウェアリリースパイプラインを使用し、すべてのアカウントで一貫したガバナンスとコンプライアンスの要件をシンプルに達成するための機能 開発パイプラインを使用してTerraformを通じてAWSアカウントのプロビジョニングとカスタマイズを行うことが可能 (Update) Amazon EventBridgeでAmazon S3のイベント通知が利用可能 S3イベント通知をEventBridgeに直接配信するように設定可能 オブジェクトサイズ、キー名、時間範囲など、多くの追加メタデータフィールドでフィルタリング可能 (Update) Apache Icebergを利用したAmazon Athena ACIDトランザクション Athena ACIDトランザクションにより、複数の同時ユーザーがAthenaのコンソール、API、ODBCおよびJDBCドライバーからAmazon S3データに対して信頼性の高い行レベルの変更を行うことが可能 Apache Icebergテーブルフォーマットに基づいて構築されたAthena ACIDトランザクションは、Amazon EMRやApache Sparkなど、Icebergテーブルフォーマットをサポートする他のサービスやエンジンとの互換性あり 12/1(水) Adam Selipsky Keynote (1:30-3:30) Graviton3 AWS Graviton3は、Amazon EC2のワークロードで最高のコストパフォーマンスを実現するためにAWSがカスタム設計した、Gravitonファミリーの最新プロセッサ AWS Graviton2プロセッサと比較して、最大25%の計算性能、最大2倍の浮動小数点性能、最大2倍の暗号ワークロード性能を提供 Graviton3プロセッサは、bfloat16命令とfp16命令をサポートすることで、CPUベースの機械学習ワークロードに対して、Graviton2プロセッサと比較して最大3倍のパフォーマンスを実現します Graviton3プロセッサは、AWS Graviton2で利用可能な常時256ビットのメモリ暗号化に加えて、セキュリティ強化のためのポインタ認証をサポート Amazon EC2 C7g インスタンス 最新世代のAWS Graviton3プロセッサを搭載 クラウドで初めて最先端のDDR5メモリ技術を採用しており、DDR4メモリと比較して+50%の帯域幅を提供 AWS Graviton2プロセッサを搭載した前世代のC6gインスタンスと比較して、ネットワーク帯域幅が20%向上 EFA(Elastic Fabric Adapter)をサポート ハイパフォーマンスコンピューティング(HPC)、ゲーム、ビデオエンコーディング、CPUベースの機械学習の推論など、計算量の多いワークロード向き Amazon EC2 Trn1 インスタンス AWS Trainiumベース AWS Trainiumは、AWSがゼロから構築した2番目の機械学習チップで、MLトレーニングを加速するために最適化 Trn1インスタンスは、自然言語処理、物体検出、画像認識、レコメンデーションエンジン、インテリジェントサーチなどのユースケースにおける深層学習モデルのトレーニングに、最高の価格性能を提供 最大16台のTrainiumアクセラレータ、最大800GbpsのEFAネットワークスループット(GPUベースのインスタンスで利用可能なネットワーク帯域幅の2倍)、インスタンス内の超高速接続をサポートしており、クラウドで最速のMLトレーニングを実現 Trn1 UltraClustersは、ペタビットスケールのノンブロッキングネットワーキングを備えた数万台のTrainiumアクセラレータに拡張可能なEC2 UltraClustersに配置 Trn1 UltraClustersは、前世代のEC2 UltraClustersよりも2.5倍大きく、最も複雑な深層学習モデルを迅速にトレーニングするための強力なスーパーコンピュータとして機能 AWS Mainframe Modernization AWS Mainframe Modernizationは、メインフレームの移行と近代化のためのユニークなプラットフォーム このサービスにより、オンプレミスのメインフレームのワークロードを、AWS上のマネージドで可用性の高いランタイム環境に移行し、モダナイゼーションが可能 現在、リプラットフォームと自動リファクタリングの2つの移行パターンをサポート AWS Private 5G 企業が自社施設内にプライベート5Gモバイルネットワークを数ヶ月ではなく数日で立ち上げ、拡張することを支援する新しいマネージドサービス AWSコンソールを数回クリックするだけで、モバイルネットワークを構築したい場所と、デバイスに必要なネットワーク容量を指定 AWSは、Private 5Gネットワークの構築とデバイスの接続に必要なスモールセル無線ユニット、サーバ、5Gコアおよび無線アクセスネットワーク(RAN)ソフトウェア、加入者IDモジュール(SIMカード)を提供し、保守 AWS Private 5Gは、ネットワークのセットアップと展開を自動化し、デバイスの追加やネットワークトラフィックの増加をサポートするために、必要に応じて容量を拡張 AWS Private 5Gでは、初期費用やデバイスごとのコストは発生せず、要求したネットワーク容量とスループットに対してのみコストが発生 (Update) AWS Lake Formation、Governed Tables、ストレージの最適化、行レベルのセキュリティをサポート データの追加や変更が行われると、Lake Formationが自動的にコンフリクトやエラーを管理し、すべてのユーザーがデータの一貫したビューで確認可能 Governed Tablesがデータの保存方法を監視し、自動的に最適化するため、クエリ時間が一貫して高速化 行レベルとセルレベルのパーミッションをサポート Amazon Redshift Serverless データウェアハウスクラスタをプロビジョニングおよび管理することなく、アナリティクスを実行して拡張するためのサーバーレスオプションを提供 データウェアハウスの容量を自動的にプロビジョニングし、インテリジェントにスケーリングすることで、すべてのアナリティクスにクラス最高のパフォーマンスを提供 コストは秒単位のコンピュート時間に課金 Amazon EMR Serverless クラスターの設定や最適化、セキュリティの確保をすることなく、これらのフレームワークを使用して構築されたアプリケーションを数回のクリックで実行可能 アプリケーションが必要とするコンピュートリソースとメモリーリソースは自動的にプロビジョニングおよびスケーリングされ、使用したリソースに対してのみ料金を支払う Amazon MSK Serverless 開発者がApache Kafkaの容量を管理することなく簡単に実行できる、新しいタイプのAmazon MSKクラスター AWSのマネジメントコンソールで数回クリックするだけで、アプリケーションのI/Oの規模に応じて自動的に拡張する、安全で可用性の高いクラスタを設定 Amazon Kinesis Data Streams On-Demand Kinesis Data Streamsの新しいキャパシティモードで、キャパシティプランニングなしで1分間にギガバイトの書き込みおよび読み込みのスループットを提供可能 ワンクリックで新しいオンデマンドデータストリームを作成したり、既存のデータストリームをオンデマンドモードに変換したりすることができ、サーバー、ストレージ、スループットのプロビジョニングや管理を行う必要がなくなる オンデマンドモードでは、プロビジョニングされたリソースではなく、消費されたスループットに対してコストが発生 Amazon SageMaker Canvas ビジネスアナリストが、コーディング不要のポイント&クリック式のビジュアルなインターフェースを使用して、正確な機械学習(ML)モデルを作成し、予測を生成可能 技術的なスキルに関係なく、企業内のアナリストが異なるデータセットから正確な機械学習モデルを作成し、データサイエンティストとより効果的にコラボレーションすることが可能 Goldman Sachs Financial Data for Cloud AWS IoT TwinMaker 開発者が実世界のシステムのデジタルツインを作成して使用することで、運用の監視と最適化をより迅速かつ容易に行うことができる新サービス デジタルツインは、建物、工場、生産ライン、機器などの物理システムを仮想的に表現したもので、実世界のデータを定期的に更新することで、表現したシステムの構造、状態、動作を模倣 機器のセンサー、ビデオフィード、ビジネスアプリケーションなどの異なるデータソースからのデータを接続することで、単一のリポジトリにデータを移動させることなく、機器、プロセス、設備のデジタルツインの作成を迅速に開始 既存の3Dモデル(CADファイル、点群スキャンなど)をインポートし、簡単な3Dツールを使用して、物理的な空間とそのコンテンツ(工場とその設備など)の3Dシーンを構成・配置可能 * 接続されたデータソースからのインタラクティブなビデオやセンサーデータ、接続された機械学習(ML)やシミュレーションサービスからのインサイト、機器のメンテナンス記録やマニュアルなどを追加することで、オペレーションの空間的な可視化を実現 AWS IoT FleetWise 自動車メーカーが車両データを収集し、変換し、ほぼリアルタイムでクラウドに転送することを、より簡単かつコスト効率よく実現する新サービス データを、個々の車両の問題をリモートで診断したり、車両の健康状態を分析することによる潜在的な保証請求やリコールの防止に役立てることが可能 自律走行や先進運転支援システム(ADAS)を向上させる機械学習モデルのトレーニング用に豊富なセンサーデータを収集することも可能 自動車の固有のデータフォーマットにアクセスし、データの構造化と標準化を行うことで、自動車メーカーがカスタムデータ収集システムを構築する必要がなくなる 収集・分析したいデータ(例:車種に関連したハードブレーキングイベントのセンサーデータ)をフィルタリングする条件付きルールを作成することで、ユースケースに必要なデータだけを正確に選択するキャンペーンを構築可能 収集されたデータはリアルタイムで確認可能 Others 12/2(木) Sivasubramanian Keynote (1:30-3:30) Peter DeSantis Keynote (8:00-9:30) Others 12/3(金) Werner Vogels Keynote (1:30-3:30) Others 会場の様子 関連記事 2019年 AWS re:Invent 2019で発表された新サービス/アップデートまとめ 2020年 AWS re:Invent 2020で発表された新サービス/アップデートまとめ AWS re:Invent公式 AWS re:Invent AWS公式サイト What's New with AWS?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS CLIで Web サイトを構築、管理、運用する(2日目)

1日目では、AWS CLIを使って、Amazon S3 の静的 Web ホスティング機能を設定、構築しました。 Web サイトということなので、どのくらいアクセスがあるのかなどをロギングしたくなりますよね? ということで、今回は、 ログ設定をいれます。 2日目の要約 アクセスログを有効化するよ! AWS CLI の準備 このあたりをみて、好きなバージョンとお使いのOSにあった環境設定をしてくださいね。 なんなら、 AWS CloudShell で実行するのも楽でよいと思います。 この記事シリーズは、AWS CloudShell で実行し、実行例を載せています。 バージョン1 https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv1.html バージョン2 https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2.html 概要 Amazon Simple Storage Service(Amazon S3)のアクセスロギング機能を AWS CLI で設定、構築する さあ、やってみよう! ログ格納用の S3 バケットを作成する 1日目と同様に作ります。 わかりやすく、1日目のバケット名に「-logs」とつけます。 BUCKETNAME=<BUCKETNAME> aws s3api create-bucket --bucket ${BUCKETNAME}-logs \ --region ap-northeast-1 \ --create-bucket-configuration LocationConstraint=ap-northeast-1 アクセスロギング機能のアクセス権限を付与する 格納先に対して読み書きが行えるように、アクセスロギング機能に対して、アクセス権限を付与します。 s3api put-bucket-acl コマンドを使用します。 # 同じシェルをそのまま使える場合は環境変数を使う aws s3api put-bucket-acl --bucket ${BUCKETNAME}-logs --grant-write URI=http://acs.amazonaws.com/groups/s3/LogDelivery --grant-read-acp URI=http://acs.amazonaws.com/groups/s3/LogDelivery # 閉じてしまったなどで使えない場合は、バケット名を自分で指定してください。 #aws s3api put-bucket-acl --bucket <BUCKETNAME>-logs --grant-write URI=http://acs.amazonaws.com/groups/s3/LogDelivery --grant-read-acp URI=http://acs.amazonaws.com/groups/s3/LogDelivery アクセスロギング機能を有効化する 格納先の作成とアクセス権限の付与ができたので、実際にアクセスロギング機能を有効化します。 使用するコマンドは、 s3api put-bucket-logging コマンドです。 # 同じシェルをそのまま使える場合は環境変数を使う aws s3api put-bucket-logging --bucket ${BUCKETNAME} \ --bucket-logging-status LoggingEnabled='{TargetBucket="${BUCKETNAME}-logs", TargetPrefix="/"}' # 閉じてしまったなどで使えない場合は、バケット名を自分で指定してください。 #aws s3api put-bucket-logging --bucket <BUCKETNAME> \ #--bucket-logging-status LoggingEnabled='{TargetBucket="<BUCKETNAME>-logs", TargetPrefix="/"}' このコマンドは正常終了した場合、特に何も出力されません。 s3api get-bucket-logging コマンドで設定されているか確認します。 # 同じシェルをそのまま使える場合は環境変数を使う aws s3api get-bucket-logging --bucket ${BUCKETNAME} # 閉じてしまったなどで使えない場合は、バケット名を自分で指定してください。 #aws s3api get-bucket-logging --bucket <BUCKETNAME> 以下の様に出力されます。 { "LoggingEnabled": { "TargetBucket": "<BUCKETNAME>-logs", "TargetPrefix": "/" } } ただし、実際にログが配信されるまでに時間がかかります バケットのログ記録ステータスの変更がログファイルの配信に反映されるまでには時間がかかります。 https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/ServerLogs.html#BucketLoggingStatusChanges ログを確認する ログ設定を行ってから数時間経過後に S3 上でホスティングしている Web サイトへアクセスしてみます。 その後、 s3 ls コマンドを使ってログファイルの存在確認と s3 cp コマンドによる ログファイルの取得を行い、内容を確認します。 # 同じシェルをそのまま使える場合は環境変数を使う aws s3 ls s3://${BUCKETNAME}-logs/ --recursive # 閉じてしまったなどで使えない場合は、バケット名を自分で指定してください。 #aws s3 ls s3://$<BUCKETNAME>-logs/ --recursive ログが正常に配信されていると以下の様に出力されます。 2021-11-29 05:12:48 4551 /2021-11-29-05-12-47-1F874BD281BCA551 2021-11-29 05:13:14 671 /2021-11-29-05-13-13-B30D92FD34EE693B 2021-11-29 05:13:20 680 /2021-11-29-05-13-19-4FD246B03ECBB15E 2021-11-29 05:15:22 2054 /2021-11-29-05-15-21-F3FB67F11280DBA7 2021-11-29 05:15:37 2070 /2021-11-29-05-15-36-35E13AD08B82A5E1 2021-11-29 05:17:10 670 /2021-11-29-05-17-09-A10B6030DACECF2F 2021-11-29 05:18:38 3749 /2021-11-29-05-18-37-48C2ACBC4FFB4E44 2021-11-29 05:18:59 446 /2021-11-29-05-18-57-3A6B2EF748058ED3 2021-11-29 05:19:24 715 /2021-11-29-05-19-23-169A2DD111112197 2021-11-29 05:20:45 1149 /2021-11-29-05-20-44-424EEDAA35B73FC3 2021-11-29 05:21:08 688 /2021-11-29-05-21-07-28B7033F0E050A2A 2021-11-29 05:21:34 682 /2021-11-29-05-21-33-72B9CBF90A63BFC2 次に、ログファイルの中身を確認します。 # 同じシェルをそのまま使える場合は環境変数を使う # 取得したいログファイル(オブジェクト名)は、先ほど s3 ls コマンドで列挙されたものを選択、指定します。 aws s3 cp s3://${BUCKETNAME}-logs/<OBJECTNAME> ./ # 閉じてしまったなどで使えない場合は、バケット名を自分で指定してください。 #aws s3 ls s3://$<BUCKETNAME>-logs/<OBJECTNAME> ./ ※注意:オブジェクト名の頭に"/"がついている場合はそれも併せて指定します。 ※例:s3://hogefugapiyo//2021-12-02-00-01-02-****** ダウンロードできたログファイルはテキストファイルなので、catやテキストエディタで表示させることができます。 cat <OBJECTNAME/ログファイル名> <バケット所有者> <BUCKETNAME> [29/Nov/2021:04:10:56 +0000] アクセス元IP.v4.ADD.RESS - <リクエストID> WEBSITE.GET.OBJECT index.html "GET / HTTP/1.1" 304 - - 134 21 - "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36" - <ホストID> - - - <BUCKETNAME>.s3-website-ap-northeast-1.amazonaws.com - - ※省略 ログファイルのフォーマットは以下にまとめられています。 https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/LogFormat.html まとめ これで S3 で配信している Web サイトへのアクセスログが記録できるようになりました。 しかし、まだまだ問題や課題がありますよね。引き続き3日目以降も見ていきます。 今回使ったコマンド s3api create-bucket s3api put-bucket-acl s3api put-bucket-logging s3api get-bucket-logging s3 ls s3 cp
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者】AWS Wavelengthを使ってみる #5 (Amazon EKS + F5 BIG-IP によるコンテナ負荷分散)

1. 目的 AWS Wavelength では、2021/11現在ではELB(CLB/ALB/NLB全て)が未提供。そのため、Wavelength Zoneにてインスタンスやコンテナの負荷分散を行う場合、自分で仕組みを用意する必要がある。例えば、北米でAWS Wavelengthに接続可能な回線を提供するVerizon社の、Wavelengthに関するQAサイトにも、ELBの代替例としてHAProxyが推奨されている。 What can I use in place of AWS Elastic Load Balancing to automatically distribute incoming application traffic across multiple targets, such as Amazon EC2 instances? Consider using Amazon EC2 as a proxy server and configuring HAProxy, a free, open source software implementation of a high availability load balancer. To learn more about HAProxy, visit the HAProxy configuration manual here. 今回は、ELBの代替として、商用プロダクトであり信頼性も高い、F5社のBIG-IP Virtual Edition(AWSなどのクラウドで動作するロードバランサ)を用いて、コンテナの負荷分散が実現可能なことを確認する。 2. やったこと Wavelength上にEKS環境を構築し、コンテナを起動する。 Wavelength上にBIG-IPをデプロイし、コンテナの負荷分散が可能なことを確認する。以下の2方式で確認する。 Ingress Service type LoadBalancer BIG-IPには、EKS環境にデプロイするコントローラ(CIS: F5 Container Ingress Services) から動的に設定を投入する。 なお今回、Wavelength環境での検証を行っているが、Wavelengthに依存する設定はないため、同様の内容を東京リージョンなどのEKS環境で実現することも可能。 3. 構成 全体の構成 Wavelength上にEKSのノードグループを用意し、コントローラ(CIS)、サンプルアプリをデプロイする。 ノードグループと同じサブネットにBIG-IPをデプロイし、外部からBIG-IP経由でサンプルアプリにアクセスできるようにする。 Ingress の場合 EKS上ではIngressリソースを作成する(BIG-IPがそれに合わせて自動設定される)。 BIG-IP経由でサンプルアプリのPodまでアクセスできることを確認する。 Service type LoadBalancerの場合 EKS上ではService type Loadbalancer リソースを作成する(BIG-IPがそれに合わせて自動設定される)。 BIG-IP経由でサンプルアプリのPodまでアクセスできることを確認する。 4. 手順 4.1 環境の準備 EKS環境の構築 以前の記事「【初心者】AWS Wavelengthを使ってみる #2 (Amazon EKSの利用)」を参考に、Wavelength上にEKS環境を構築する。 Kubernetesのバージョンはv1.21(2021/11時点のEKSでの最新バージョン) 通常時のワーカーノード数は2(Wavelength Zoneに t3.mediumのインスタンスが2個ある状態) ワーカーノードへキャリアIPを付与する(今回は直接外部にコンテナイメージを取得に行かせるため)。 ワーカーノードはサブネット内(10.0.10.0/24)からのアクセスを許可する(後で同一サブネットに作成するF5 BIG-IPからコンテナへのアクセスを許可するため)。 テスト用ウェブサービスの作成 後でF5 BIG-IPを用いてインターネットへ公開する用の、テスト用のウェブサービスを作成する。(httpアクセスすると、ノード名、pod名が返されるだけのシンプルなサービス) nginxが提供するサンプル(cafe.yaml) をそのままデプロイする。結果として以下の状態になる。 [ec2-user@ip-10-0-0-151 ~]$ kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE coffee 2/2 2 2 26h tea 3/3 3 3 26h [ec2-user@ip-10-0-0-151 ~]$ kubectl get pods NAME READY STATUS RESTARTS AGE coffee-6f4b79b975-b7tch 1/1 Running 0 26h coffee-6f4b79b975-n422v 1/1 Running 0 26h tea-6fb46d899f-5vqlx 1/1 Running 0 26h tea-6fb46d899f-f98zc 1/1 Running 0 26h tea-6fb46d899f-x7wkv 1/1 Running 0 26h [ec2-user@ip-10-0-0-151 ~]$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE coffee-svc ClusterIP 172.20.73.10 <none> 80/TCP 26h kubernetes ClusterIP 172.20.0.1 <none> 443/TCP 2d6h tea-svc ClusterIP 172.20.179.79 <none> 80/TCP 26h このcoffee-svc/tea-svcについて、BIG-IPを用いて外部からのアクセスを可能にするよう設定していく。 4.2 BIG-IPの設定 インスタンスの起動 BIG-IP VEをWavelength上のサブネット(EKSのワーカーノード2個が起動しているのと同じサブネット)にデプロイする。 イメージ名: F5 BIG-IP VE - ALL (BYOL, 2 Boot Locations) ※ライセンス込みのタイプ(PAYG)もあるが、今回はライセンスはBYOLとし、別途F5社より検証用ライセンスを発行頂いたものを用いる。 インスタンスタイプ: t3.medium バージョン: 16.1.1 Build 0.0.16 キャリアIPを付与し、外部からのアクセスを可能にする。 同一サブネット(10.0.10.0/24)からのアクセスを許可する。(ワーカーノード上に配置されるF5のコントローラからのアクセスを受けるため) 管理画面用パスワード設定 ssh(user: admin, 秘密鍵) で接続し、管理画面用のパスワードを設定、保存する。 admin@(ip-10-0-10-63)(cfg-sync Standalone)(NO LICENSE)(/Common)(tmos)# modify auth user admin password PASSWORD admin@(ip-10-0-10-63)(cfg-sync Standalone)(NO LICENSE)(/Common)(tmos)# save sys config   ライセンスの設定 管理画面 ( https://BIG-IPインスタンスのキャリアIP:8443 )にアクセスし、user: admin, password: 上記で設定したパスワードで、ログインする。 F5社から発行頂いた検証用ライセンスを設定する。 Partition作成 専用のPartition(BIG-IP内の管理領域のようなもの)を作成する。 System -> Users: Partition List -> New Partition で、新規に「k8s]というPartitionを作成する。 AS3の追加 「AS3(Application Services 3 Extension)」は、BIG-IPを外部から操作することを可能とする拡張モジュールのようなもの。 F5社のGitHubから、rpmファイル(f5-appsvcs-3.28.0-3.noarch.rpm)を作業用PCなどにダウンロードする。今回は v3.28.0 を使用する。 BIG-IPの管理画面で、iApps -> Package Management LX から、rpmファイルをインポートする。 4.3 Ingress での設定 CIS 用権限などの作成 CISは、k8sなどのコンテナ環境とF5 BIG-IPを連携させるような仕組み。 まずはEKS上でCISのコントローラ(k8s-bigip-ctlr)を動作させるための権限などを用意する。(もう少し細かく権限付与も可能だが、今回は検証のためざっくりclusteradmin権限を付与) [ec2-user@ip-10-0-0-151 ~]$ kubectl create secret generic bigip-login -n kube-system --from-literal=username=admin --from-literal=password=PASSWORD secret/bigip-login created [ec2-user@ip-10-0-0-151 ~]$ kubectl create serviceaccount k8s-bigip-ctlr -n kube-system serviceaccount/k8s-bigip-ctlr created [ec2-user@ip-10-0-0-151 ~]$ kubectl create clusterrolebinding k8s-bigip-ctlr-clusteradmin --clusterrole=cluster-admin --serviceaccount=kube-system:k8s-bigip-ctlr clusterrolebinding.rbac.authorization.k8s.io/k8s-bigip-ctlr-clusteradmin created CIS (k8s-bigip-ctlr) の作成 EKS環境でIngressリソースを作成した時に、BIG-IPに適切な設定が自動投入されるようにするため、CIS (k8s-bigip-ctlr) を作成する。これがIngress Controllerとして動作する。 以下のマニフェストを用いる。「--bigip-url=https://10.0.10.63:8443」のところは、BIG-IPのインスタンスのローカルIPアドレスを指定する。 今回は他環境で動作実績のあるバージョン(v2.5.1)を使用している。 cluster-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: k8s-bigip-ctlr namespace: kube-system spec: replicas: 1 selector: matchLabels: app: k8s-bigip-ctlr template: metadata: name: k8s-bigip-ctlr labels: app: k8s-bigip-ctlr spec: serviceAccountName: k8s-bigip-ctlr containers: - name: k8s-bigip-ctlr image: "f5networks/k8s-bigip-ctlr:2.5.1" imagePullPolicy: IfNotPresent env: - name: BIGIP_USERNAME valueFrom: secretKeyRef: name: bigip-login key: username - name: BIGIP_PASSWORD valueFrom: secretKeyRef: name: bigip-login key: password command: ["/app/bin/k8s-bigip-ctlr"] args: [ "--bigip-username=$(BIGIP_USERNAME)", "--bigip-password=$(BIGIP_PASSWORD)", "--bigip-url=https://10.0.10.63:8443", "--insecure=true", "--bigip-partition=k8s", "--pool-member-type=cluster" ] [ec2-user@ip-10-0-0-151 ~]$ kubectl apply -f cluster-deployment.yaml deployment.apps/k8s-bigip-ctlr created [ec2-user@ip-10-0-0-151 ~]$ kubectl get pods -n kube-system NAME READY STATUS RESTARTS AGE aws-node-8jlzx 1/1 Running 0 55m aws-node-tb5kn 1/1 Running 0 15h coredns-76f4967988-5wvjr 1/1 Running 0 14h coredns-76f4967988-vfxsv 1/1 Running 0 27h k8s-bigip-ctlr-6989b8bf6c-z9jc9 1/1 Running 0 25s kube-proxy-85tgh 1/1 Running 0 15h kube-proxy-qkkbk 1/1 Running 0 55m 成功すると、k8s-bigip-ctlrのpodがRunningになる。(BIG-IPのアクセスURLやポートが間違っていたり、BIG-IPへのアクセスがsecuritygroupで拒否されていたりして、BIG-IPにアクセスできない場合、podの作成に失敗する。) IngressClassの作成 CISで使用するIngressClassを作成する。 [ec2-user@ip-10-0-0-151 ~]$ kubectl apply -f https://raw.githubusercontent.com/F5Networks/k8s-bigip-ctlr/master/docs/config_examples/ingress/networkingV1/example-default-ingress-class.yaml ingressclass.networking.k8s.io/f5 created Ingress リソースの作成 以下のマニフェストを用いて、Ingress リソースを作成する。「https://BIG-IPのキャリアIP/tea」 の場合はteaのサービスに、「https://BIG-IPのキャリアIP/coffee」の場合はcoffeeのサービスに転送するような設定としている) ingress-cafe.yaml apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: cafe-ingress annotations: virtual-server.f5.com/partition: "k8s" virtual-server.f5.com/ip: 10.0.10.63 virtual-server.f5.com/http-port: "80" virtual-server.f5.com/ssl-redirect: "false" virtual-server.f5.com/balance: "round-robin" spec: ingressClassName: f5 rules: - http: paths: - path: /tea backend: serviceName: tea-svc servicePort: 80 - path: /coffee backend: serviceName: coffee-svc servicePort: 80 [ec2-user@ip-10-0-0-151 ~]$ kubectl apply -f ingress-cafe.yaml Warning: networking.k8s.io/v1beta1 Ingress is deprecated in v1.19+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress ingress.networking.k8s.io/cafe-ingress created [ec2-user@ip-10-0-0-151 ~]$ kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE cafe-ingress f5 * 10.0.10.63 80 7s BIG-IPの自動設定投入確認 Ingress リソース作成後、BIG-IPに必要な設定が入ることを確認する。 Virtual Server (待ち受けするアドレス・ポート、転送先のPoolなどの設定のセット)が作成される。 Virtual Server に紐づくPolicy(URIの部分(tea もしくは coffee)を見てそれぞれ別のPoolに転送する)が設定される。 動作確認 手元のPC(auスマホでテザリング)のブラウザ(FirefoxとEdge)から、「http://BIG-IPのキャリアIP/coffee」 にアクセスする。coffeeサービス用の2つのPodのどちらかで処理されることを確認する。(アクセス時、以下のように処理を行ったホストのIPアドレスやPod名が表示されることで判別可能) 同様に 「http://BIG-IPのキャリアIP/tea」 にアクセスすると、tea サービスを処理するPod(3つのどれか)へアクセス可能。URIに応じて別々のサービスにアクセスできていることが確認できる。 BIG-IPのStatisticsの画面で、/coffee にアクセスした場合、coffeeサービス用の2つのPodに処理が分散されていることが確認できる。 4.4 Service Type LoadBalancer での設定 FIC(F5 IPAM Controller)の作成 Service Type LoadBalancerでの設定の場合、IPアドレスの割り当て管理管理が必要となるため、「FIS(IPAM Controller)」を追加で作成する。 以下のマニフェストを使用する。 今回はv0.1.4(2021/11時点の最新版はv0.1.5)を使用している。v0.1.5から信頼性の向上のためか、PVの使用が必要となっており検証用としては煩雑だったため。 「--ip-range」として、この環境のサブネットの中から、配布したいIPアドレスのレンジを指定する。 ipam-controller.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: name: ipam-controller name: ipam-controller namespace: kube-system spec: replicas: 1 selector: matchLabels: app: ipam-controller template: metadata: labels: app: ipam-controller spec: containers: - args: - --orchestration - kubernetes - --ip-range - '{"test" : "10.0.10.101-10.0.10.105","prod" : "10.0.10.111-10.0.10.115"}' - --log-level - DEBUG command: - /app/bin/f5-ipam-controller image: f5networks/f5-ipam-controller:0.1.4 imagePullPolicy: IfNotPresent name: ipam-controller terminationMessagePath: /dev/termination-log terminationMessagePolicy: File dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler securityContext: {} serviceAccount: k8s-bigip-ctlr serviceAccountName: k8s-bigip-ctlr terminationGracePeriodSeconds: 30 [ec2-user@ip-10-0-0-151 ~]$ kubectl apply -f ipam-controller.yaml deployment.apps/ipam-controller created [ec2-user@ip-10-0-0-151 ~]$ kubectl get pod -n kube-system NAME READY STATUS RESTARTS AGE aws-node-8jlzx 1/1 Running 0 3d10h aws-node-tb5kn 1/1 Running 0 4d1h coredns-76f4967988-5wvjr 1/1 Running 0 3d23h coredns-76f4967988-vfxsv 1/1 Running 0 4d12h ipam-controller-5f5cd96b54-4gd2r 1/1 Running 0 8s k8s-bigip-ctlr-6989b8bf6c-z9jc9 1/1 Running 0 3d9h kube-proxy-85tgh 1/1 Running 0 4d1h kube-proxy-qkkbk 1/1 Running 0 3d10h CRD作成 BIG-IPの設定値をk8s側で管理するためのCRDを作成する。 F5社のGitHubの「customresourcedefinitions.yml」 を以下の2行を修正して使用する。(長いのでyamlファイルの全体の転記は省略) name: externaldnses.cis.f5.com -> externaldnss.cis.f5.com plural: externaldnses -> externaldnss 現在(2021/11)時点で、CIS(このCRDの利用側)と、CRDを作成するyamlで、externaldnsに関する名称の不一致(externaldnss/externaldnses)が発生している。そのうち修正されるのではと想定。 customresourcedefinitions-fixed.yaml apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: externaldnss.cis.f5.com spec: group: cis.f5.com names: kind: ExternalDNS plural: externaldnss shortNames: - edns singular: externaldns [ec2-user@ip-10-0-0-151 ~]$ kubectl apply -f customresourcedefinitions-fixed.yaml customresourcedefinition.apiextensions.k8s.io/virtualservers.cis.f5.com created customresourcedefinition.apiextensions.k8s.io/tlsprofiles.cis.f5.com created customresourcedefinition.apiextensions.k8s.io/transportservers.cis.f5.com created customresourcedefinition.apiextensions.k8s.io/externaldnss.cis.f5.com created customresourcedefinition.apiextensions.k8s.io/ingresslinks.cis.f5.com created customresourcedefinition.apiextensions.k8s.io/policies.cis.f5.com created CIS(k8s-bigip-ctlr)の修正 Ingressの実施手順の際に作成したCIS(k8s-bigip-ctlr)の設定を修正して、podを再作成する。修正点は、「"--custom-resource-mode=true","--ipam=true"」の2行の追加。 cluster-deployment.yaml args: [ "--bigip-username=$(BIGIP_USERNAME)", "--bigip-password=$(BIGIP_PASSWORD)", "--bigip-url=https://10.0.10.63:8443", "--insecure=true", "--bigip-partition=k8s", "--pool-member-type=cluster", "--custom-resource-mode=true", "--ipam=true" ] BIG-IP インスタンスへのセカンダリIPの追加 IPAM ControllerのIPレンジで定義したIPアドレスがBIG-IPに紐づくように、BIG-IPのインスタンスの設定でセカンダリIPとしての設定を行う。 マネージメントコンソールのEC2の画面にて、BIG-IPのインスタンスを選択し、アクション -> ネットワーキング -> IPアドレスの管理 を選択する。 指定したIPレンジをセカンダリIPとして追加する。 Service type LoadBalancerリソース作成 coffee-svcのほうを外部公開するようなLoadBalancerリソースを作成する。 現時点(2021/11)で、EKS環境で何もannotationsを付けずにLoadBalancerリソースを作成するとCLBが作成される。「service.beta.kubernetes.io/aws-load-balancer-type: "external"」を記載することで、CLBが作成されなくなる。 「cis.f5.com/ipamLabel: test」を記載することで、「test」ラベルに紐づくIPレンジの中のIPアドレスが使用される。 typelb.yaml apiVersion: v1 kind: Service metadata: name: coffee-typelb annotations: service.beta.kubernetes.io/aws-load-balancer-type: "external" cis.f5.com/ipamLabel: test spec: ports: - port: 8080 protocol: TCP selector: app: coffee type: LoadBalancer [ec2-user@ip-10-0-0-151 ~]$ kubectl apply -f typelb.yaml service/coffee-typelb created [ec2-user@ip-10-0-0-151 ~]$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE coffee-svc ClusterIP 172.20.73.10 <none> 80/TCP 5d8h coffee-typelb LoadBalancer 172.20.172.14 10.0.10.101 8080:30304/TCP 4s kubernetes ClusterIP 172.20.0.1 <none> 443/TCP 6d12h tea-svc ClusterIP 172.20.179.79 <none> 80/TCP 5d8h キャリアIP付与 今回はIPレンジの中から 10.0.10.101 が 使用されたため、このプライベートアドレスに対してキャリアIPを付与する。(BIG-IPへの管理用アクセスに用いているキャリアIPとは別のものとなる。) キャリアIPをインスタンスに関連付けする際、プライベートIPアドレスは先の手順でセカンダリIPとして登録したものの中から選択可能。今回はLoadBalancer用に設定された10.0.10.101を選択する。 BIG-IPの自動設定投入確認 LoadBalancerのリソース作成後、BIG-IP側でVirtual Server、Poolなどが自動設定されることを確認する。 動作確認 ブラウザで「https://キャリアIP:8080/」にアクセスし、coffee-svcに接続できることを確認する。 2つのpodに負荷分散されていることを確認する。 5. 所感 Wavelength環境においても、F5社が提供するCISの仕組みを用いることにより、BIG-IPをk8s環境にうまく組み込む形で、ELBの代替として利用可能なことが確認できた。BIG-IPには今回検証した内容以外にも様々な機能があり、機会があればより深堀してみたい。 6. 参考URL Overview of F5 BIG-IP Container Ingress Services Class 1: Kubernetes with F5 Container Ingress Service
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS上のシステムを維持管理して得たハマりポイント

はじめに 現在、とあるAWSのシステムのインフラの維持管理を担当しているが、AWSやパブリッククラウドならではのところでハマったポイントについてとりとめもなくご紹介したい。 WEBではAWSの新機能の紹介や構成検討の情報は結構載っているものの、このあたりの地味な情報はなかなか見た事がなかったので記載しようと思った次第。 特にオンプレからAWSへの移行を検討されている方に参考なれば幸いです。 記載内容に誤りがありましたら、ご指摘頂けれると大変ありがたいです! ハマりポイント1. Auroraはバージョンダウンできない! Auroraはバージョンダウンの機能が(今のところ)無い。 バージョンアップ作業における業務閉塞中のコンチプランとして、事前に取得したスナップショットからAuroraクラスタの再作成等が考えられるが、業務閉塞解除後だとスナップショット取得以降の全ての更新を手動で追い付く必要があるため、実質不可能。 そのため、私の担当しているシステムでは業務閉塞解除後に問題が発生した場合は、対策前進の方針としている。 業務閉塞解除後もコンチ発動できる様にするためには、DMSで同期させておく、等の個別の検討が必要。 ハマりポイント2. Aurora(PostgreSQL)のバージョンを指定できない! Aurora (PostgreSQL)のパッチバージョン(2.5.1の1の部分)はDBエンジンのバージョンアップ時に明示的に指定できず、常にバージョンアップ時点の最新のものが適用される。 そのためバージョンアップ対応の際、開発環境でのノンデグテスト実施時とその後の本番環境でのリリース時で異なるパッチバージョンとなる可能性がある。 私が担当しているシステムではノンデグテストを実施していないパッチバージョンでのリリースを避けるため以下の対応方針とした。 リリース直前に開発環境Auroraでバージョンアップを実施し、万が一パッチバージョンがテスト時より新しいものになったらリリース中止。新しいパッチバージョンで再度ノンデグテスト実施。 バージョンアップリリース後の確認でパッチバージョンが新しいものとなっていたらコンチ発動で旧戻し。 どこまで神経質になるのかという話ではあるが、、、テストしていないものはリリースできないという原則に従った方針。 Aurora(MySQL)だとパッチレベルまでユーザが指定できるらしい。Aurora(PostgreSQL)でも指定できる様にしてほしい!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DatabricksにおけるAssumeRoleポリシーを用いたS3バケットに対するセキュアなクロスアカウントアクセス 

Secure access to S3 buckets across accounts using instance profiles with an AssumeRole policy | Databricks on AWS [2021/7/27時点]の翻訳です。 AWSにおいては、あるアカウントの計算処理が別のアカウントのバケットにアクセスできるように、クロスアカウントアクセスをセットアップすることができます。アクセスを許可する方法の一つは、Databricksにおけるインスタンスプロファイルを用いたS3バケットへのセキュアなアクセスで説明されているように、アカウントに対して、別のアカウントのバケットへのアクセスを直接許可するというものです。バケットへのアクセスを許可する別の方法は、アカウントが別のアカウントのロールを引き受ける(assume a role)ことを許可するというものです。 アカウントIDが<deployment-acct-id>であるAWSアカウントAと、アカウントIDが<bucket-owner-acct-id>であるAWSアカウントBを考えます。アカウントAはDatabricksにサインアップする際に用いたものであり、EC2サービスとDBFSルートバケットはこのアカウントによって管理されています。アカウントBはバケット<s3-bucket-name>を保有しています。 本書では、アカウントAがアカウントBのロールとして<s3-bucket-name>のファイルにアクセスするために、AWSのAssumeRoleアクションを使用するように設定するステップを説明します。このアクセスを有効にするために、アカウントA、アカウントB、そしてDatabricksのAdmin Consoleでの設定が必要となります。また、バケットにアクセスするノートブックや、Databricksクラスターに設定を行う必要があります。 要件 DatabricksをデプロイしたAWSアカウントとS3バケットのAWSアカウントでIAMロール、ポリシーにアクセスできるAWS管理者 ターゲットのS3バケット S3バケットに対する暗号化を有効化する場合には、設定で指定されるKMSキーに対するKey Userとしてインスタンスプロファイルを追加する必要があります。Configure KMS encryption for s3a:// pathsをご覧ください。 ステップ1: アカウントAでMyRoleAを作成し、ポリシーをアタッチする アカウントAでMyRoleAというロールを作成します。インスタンスプロファイルのARNはarn:aws:iam::<deployment-acct-id>:instance-profile/MyRoleAとなります。 アカウントAのロールががアカウントBのMyRoleBを引き受けることを許可するポリシーを作成します。ポリシーをMyRoleAにアタッチします。をクリックし、ポリシーに貼り付けます。 JSON { "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1487884001000", "Effect": "Allow", "Action": [ "sts:AssumeRole" ], "Resource": [ "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB" ] } ] } MyRoleAに対してiam:PassRoleを追加することで、クラスターを作成する際に用いるアカウントAのロールに対するポリシーを更新します。 JSON { "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1403287045000", "Effect": "Allow", "Action": [ "ec2:AssociateDhcpOptions", "ec2:AssociateIamInstanceProfile", "ec2:AssociateRouteTable", "ec2:AttachInternetGateway", "ec2:AttachVolume", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", "ec2:CancelSpotInstanceRequests", "ec2:CreateDhcpOptions", "ec2:CreateInternetGateway", "ec2:CreateKeyPair", "ec2:CreateRoute", "ec2:CreateSecurityGroup", "ec2:CreateSubnet", "ec2:CreateTags", "ec2:CreateVolume", "ec2:CreateVpc", "ec2:CreateVpcPeeringConnection", "ec2:DeleteInternetGateway", "ec2:DeleteKeyPair", "ec2:DeleteRoute", "ec2:DeleteRouteTable", "ec2:DeleteSecurityGroup", "ec2:DeleteSubnet", "ec2:DeleteTags", "ec2:DeleteVolume", "ec2:DeleteVpc", "ec2:DescribeAvailabilityZones", "ec2:DescribeIamInstanceProfileAssociations", "ec2:DescribeInstanceStatus", "ec2:DescribeInstances", "ec2:DescribePrefixLists", "ec2:DescribeReservedInstancesOfferings", "ec2:DescribeRouteTables", "ec2:DescribeSecurityGroups", "ec2:DescribeSpotInstanceRequests", "ec2:DescribeSpotPriceHistory", "ec2:DescribeSubnets", "ec2:DescribeVolumes", "ec2:DescribeVpcs", "ec2:DetachInternetGateway", "ec2:DisassociateIamInstanceProfile", "ec2:ModifyVpcAttribute", "ec2:ReplaceIamInstanceProfileAssociation", "ec2:RequestSpotInstances", "ec2:RevokeSecurityGroupEgress", "ec2:RevokeSecurityGroupIngress", "ec2:RunInstances", "ec2:TerminateInstances" ], "Resource": [ "*" ] }, { "Effect": "Allow", "Action": "iam:PassRole", "Resource": [ "arn:aws:iam::<deployment-acct-id>:role/MyRoleA" ] } ] } 注意 お使いのアカウントがDatabricksプラットフォームのE2バージョンの場合、ec2:CreateKeyPairとec2:DeleteKeyPairを削除することはできません。アカウントバージョンが不明の場合には、Databricks担当者にお問い合わせください。 ステップ2: アカウントBでMyRoleBを作成し、ポリシーをアタッチする MyRoleBというロールを作成します。ロールのARNはarn:aws:iam::<bucket-owner-acct-id>:role/MyRoleBとなります。 アカウントAのロールMyRoleAが、アカウントBのロールを引き受けられるように、MyRoleBの信頼リレーションシップを編集します。IAM > Roles > MyRoleB > Trust relationships > Edit trust relationshipを選択し、以下を入力します。 JSON { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<deployment-acct-id>:role/MyRoleA" ] }, "Action": "sts:AssumeRole" } ] } バケット<s3-bucket-name>に対するバケットポリシーを作成します。S3 > > Permissions > Bucket Policyを選択し、以下を入力します。 JSON { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetBucketLocation", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::<s3-bucket-name>" ] }, { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:PutObjectAcl", "s3:GetObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::<s3-bucket-name>/*" ] } ] } バケットポリシーにロール(プリンシパル)MyRoleBを追加します。 JSON { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB" ] }, "Action": [ "s3:GetBucketLocation", "s3:ListBucket" ], "Resource": "arn:aws:s3:::<s3-bucket-name>" }, { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB" ] }, "Action": [ "s3:PutObject", "s3:PutObjectAcl", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::<s3-bucket-name>/*" } ] } ティップス Principal errorが表示された場合には、Trust relationshipだけを変更したことを確認してください。 ステップ3: MyRoleAをDatabricksワークスペースに追加する DatabricksのAdmin Consoleで、ステップ1で取得したMyRoleAのインスタンスプロファイルARNarn:aws:iam::<deployment-acct-id>:instance-profile/MyRoleAを用いてMyRoleAのインスタンスプロファイルをDatabricksに追加します。 ステップ3: MyRoleAでクラスターを設定する クラスターを選択、作成します。 Advanced Optionsセクションを展開します。 Instancesタブで、MyRoleAのインスタンスプロファイルを選択します。 Sparkタブで、オプションとしてassumeRoleの認証タイプと、ロールを委任するMyRoleBのARNを指定します。 fs.s3a.credentialsType AssumeRole fs.s3a.stsAssumeRole.arn arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB fs.s3a.acl.default BucketOwnerFullControl クラスターを起動します。 ノートブックをクラスターにアタッチします。 以下のいずれかを実行します。 オプションとして、追加設定を行い、DBFSに<s3-bucket-name>をマウントします。 Scala dbutils.fs.mount("s3a://<s3-bucket-name>", "/mnt/<s3-bucket-name>", extraConfigs = Map( "fs.s3a.credentialsType" -> "AssumeRole", "fs.s3a.stsAssumeRole.arn" -> "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB", "fs.s3a.acl.default" -> "BucketOwnerFullControl" ) ) 注意 これは推奨のオプションです。 クラスターのSpark設定に認証タイプassumeRoleと委任ロールのARNを設定しなかった場合、ノートブックの先頭で設定することもできます。 Scala sc.hadoopConfiguration.set("fs.s3a.credentialsType", "AssumeRole") sc.hadoopConfiguration.set("fs.s3a.stsAssumeRole.arn", "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB") sc.hadoopConfiguration.set("fs.s3a.acl.default", "BucketOwnerFullControl") 以下のコマンドを用いて<s3-bucket-name>にアクセスできることを確認します。 Python dbutils.fs.ls("/mnt/<s3-bucket-name>") あるいは、 Python dbutils.fs.ls("s3a://<s3-bucket-name>/") Databricks 無料トライアル Databricks 無料トライアル
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DatabricksにおけるAssumeRoleポリシーを用いたS3バケットに対するセキュアなクロスアカウントアクセス

Secure access to S3 buckets across accounts using instance profiles with an AssumeRole policy | Databricks on AWS [2021/7/27時点]の翻訳です。 注意 本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては英語ドキュメントを参照ください。 AWSにおいては、あるアカウントの計算処理が別のアカウントのバケットにアクセスできるように、クロスアカウントアクセスをセットアップすることができます。アクセスを許可する方法の一つは、Databricksにおけるインスタンスプロファイルを用いたS3バケットへのセキュアなアクセスで説明されているように、アカウントに対して、別のアカウントのバケットへのアクセスを直接許可するというものです。バケットへのアクセスを許可する別の方法は、アカウントが別のアカウントのロールを引き受ける(assume a role)ことを許可するというものです。 アカウントIDが<deployment-acct-id>であるAWSアカウントAと、アカウントIDが<bucket-owner-acct-id>であるAWSアカウントBを考えます。アカウントAはDatabricksにサインアップする際に用いたものであり、EC2サービスとDBFSルートバケットはこのアカウントによって管理されています。アカウントBはバケット<s3-bucket-name>を保有しています。 本書では、アカウントAがアカウントBのロールとして<s3-bucket-name>のファイルにアクセスするために、AWSのAssumeRoleアクションを使用するように設定するステップを説明します。このアクセスを有効にするために、アカウントA、アカウントB、そしてDatabricksのAdmin Consoleでの設定が必要となります。また、バケットにアクセスするノートブックや、Databricksクラスターに設定を行う必要があります。 要件 DatabricksをデプロイしたAWSアカウントとS3バケットのAWSアカウントでIAMロール、ポリシーにアクセスできるAWS管理者 ターゲットのS3バケット S3バケットに対する暗号化を有効化する場合には、設定で指定されるKMSキーに対するKey Userとしてインスタンスプロファイルを追加する必要があります。Configure KMS encryption for s3a:// pathsをご覧ください。 ステップ1: アカウントAでMyRoleAを作成し、ポリシーをアタッチする アカウントAでMyRoleAというロールを作成します。インスタンスプロファイルのARNはarn:aws:iam::<deployment-acct-id>:instance-profile/MyRoleAとなります。 アカウントAのロールがアカウントBのロールMyRoleBを引き受けることを許可するポリシーを作成します。ポリシーをMyRoleAにアタッチします。をクリックし、ポリシーに貼り付けます。 JSON { "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1487884001000", "Effect": "Allow", "Action": [ "sts:AssumeRole" ], "Resource": [ "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB" ] } ] } MyRoleAに対してiam:PassRoleを追加することで、クラスターを作成する際に用いるアカウントAのロールに対するポリシーを更新します。 JSON { "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1403287045000", "Effect": "Allow", "Action": [ "ec2:AssociateDhcpOptions", "ec2:AssociateIamInstanceProfile", "ec2:AssociateRouteTable", "ec2:AttachInternetGateway", "ec2:AttachVolume", "ec2:AuthorizeSecurityGroupEgress", "ec2:AuthorizeSecurityGroupIngress", "ec2:CancelSpotInstanceRequests", "ec2:CreateDhcpOptions", "ec2:CreateInternetGateway", "ec2:CreateKeyPair", "ec2:CreateRoute", "ec2:CreateSecurityGroup", "ec2:CreateSubnet", "ec2:CreateTags", "ec2:CreateVolume", "ec2:CreateVpc", "ec2:CreateVpcPeeringConnection", "ec2:DeleteInternetGateway", "ec2:DeleteKeyPair", "ec2:DeleteRoute", "ec2:DeleteRouteTable", "ec2:DeleteSecurityGroup", "ec2:DeleteSubnet", "ec2:DeleteTags", "ec2:DeleteVolume", "ec2:DeleteVpc", "ec2:DescribeAvailabilityZones", "ec2:DescribeIamInstanceProfileAssociations", "ec2:DescribeInstanceStatus", "ec2:DescribeInstances", "ec2:DescribePrefixLists", "ec2:DescribeReservedInstancesOfferings", "ec2:DescribeRouteTables", "ec2:DescribeSecurityGroups", "ec2:DescribeSpotInstanceRequests", "ec2:DescribeSpotPriceHistory", "ec2:DescribeSubnets", "ec2:DescribeVolumes", "ec2:DescribeVpcs", "ec2:DetachInternetGateway", "ec2:DisassociateIamInstanceProfile", "ec2:ModifyVpcAttribute", "ec2:ReplaceIamInstanceProfileAssociation", "ec2:RequestSpotInstances", "ec2:RevokeSecurityGroupEgress", "ec2:RevokeSecurityGroupIngress", "ec2:RunInstances", "ec2:TerminateInstances" ], "Resource": [ "*" ] }, { "Effect": "Allow", "Action": "iam:PassRole", "Resource": [ "arn:aws:iam::<deployment-acct-id>:role/MyRoleA" ] } ] } 注意 お使いのアカウントがDatabricksプラットフォームのE2バージョンの場合、ec2:CreateKeyPairとec2:DeleteKeyPairを削除することはできません。アカウントバージョンが不明の場合には、Databricks担当者にお問い合わせください。 ステップ2: アカウントBでMyRoleBを作成し、ポリシーをアタッチする MyRoleBというロールを作成します。ロールのARNはarn:aws:iam::<bucket-owner-acct-id>:role/MyRoleBとなります。 アカウントAのロールMyRoleAが、アカウントBのロールを引き受けられるように、MyRoleBの信頼リレーションシップを編集します。IAM > Roles > MyRoleB > Trust relationships > Edit trust relationshipを選択し、以下を入力します。 JSON { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<deployment-acct-id>:role/MyRoleA" ] }, "Action": "sts:AssumeRole" } ] } バケット<s3-bucket-name>に対するバケットポリシーを作成します。S3 > > Permissions > Bucket Policyを選択し、以下を入力します。 JSON { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetBucketLocation", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::<s3-bucket-name>" ] }, { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:PutObjectAcl", "s3:GetObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::<s3-bucket-name>/*" ] } ] } バケットポリシーにロール(プリンシパル)MyRoleBを追加します。 JSON { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB" ] }, "Action": [ "s3:GetBucketLocation", "s3:ListBucket" ], "Resource": "arn:aws:s3:::<s3-bucket-name>" }, { "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB" ] }, "Action": [ "s3:PutObject", "s3:PutObjectAcl", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::<s3-bucket-name>/*" } ] } ティップス Principal errorが表示された場合には、Trust relationshipだけを変更したことを確認してください。 ステップ3: MyRoleAをDatabricksワークスペースに追加する DatabricksのAdmin Consoleで、ステップ1で取得したMyRoleAのインスタンスプロファイルARNarn:aws:iam::<deployment-acct-id>:instance-profile/MyRoleAを用いてMyRoleAのインスタンスプロファイルをDatabricksに追加します。 ステップ3: MyRoleAでクラスターを設定する クラスターを選択、作成します。 Advanced Optionsセクションを展開します。 Instancesタブで、MyRoleAのインスタンスプロファイルを選択します。 Sparkタブで、オプションとしてassumeRoleの認証タイプと、ロールを委任するMyRoleBのARNを指定します。 fs.s3a.credentialsType AssumeRole fs.s3a.stsAssumeRole.arn arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB fs.s3a.acl.default BucketOwnerFullControl クラスターを起動します。 ノートブックをクラスターにアタッチします。 以下のいずれかを実行します。 オプションとして、追加設定を行い、DBFSに<s3-bucket-name>をマウントします。 Scala dbutils.fs.mount("s3a://<s3-bucket-name>", "/mnt/<s3-bucket-name>", extraConfigs = Map( "fs.s3a.credentialsType" -> "AssumeRole", "fs.s3a.stsAssumeRole.arn" -> "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB", "fs.s3a.acl.default" -> "BucketOwnerFullControl" ) ) 注意 これは推奨のオプションです。 クラスターのSpark設定に認証タイプassumeRoleと委任ロールのARNを設定しなかった場合、ノートブックの先頭で設定することもできます。 Scala sc.hadoopConfiguration.set("fs.s3a.credentialsType", "AssumeRole") sc.hadoopConfiguration.set("fs.s3a.stsAssumeRole.arn", "arn:aws:iam::<bucket-owner-acct-id>:role/MyRoleB") sc.hadoopConfiguration.set("fs.s3a.acl.default", "BucketOwnerFullControl") 以下のコマンドを用いて<s3-bucket-name>にアクセスできることを確認します。 Python dbutils.fs.ls("/mnt/<s3-bucket-name>") あるいは、 Python dbutils.fs.ls("s3a://<s3-bucket-name>/") Databricks 無料トライアル Databricks 無料トライアル
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS SES。もう自前バウンス対応は不要かも。

KINTO Technologies Advent Calendar 2021 - Qiita の1日目の記事です。 2021年12月現在、AWS SESでバウンスを処理するためには、どんな作業が必要/不要なのか。 AWSのドキュメントやサポートに確認した内容に基づいて説明します。 TL;DR 基本的に バウンス処理を自前で構築する必要はありません。 特に、バウンスレートを下げることだけが目的の場合は、 Account-level Suppression List が有効かどうか確認するだけでOKです。 AWS SNSからバウンス情報を取得して処理したり、自前のRDB/NoSQLを用意してメールアドレスのリストを保存することは不要です。 最新情報に基づいて設計を見直し、車輪の再発明はやめましょう。 この記事を読むために必要な前提知識 メールのバウンスとはなにか メールを送るときは、バウンスと苦情の処理がセットで必要であること バウンスには、ハードバウンスとソフトバウンスの2種類があること Suppression List とは? バウンスを適切に処理しようとすると、バウンスしたメールアドレスを溜めておくためのリストが欲しくなるかもしれません。 でも、リストを自前DBで構築しないでください。 SESがすでに Suppression List という名前で提供してくれています。 Suppression List には、Global Supression List と Acount-level Suppression List の2種類があります。 Global Suppression List 2013年5月から提供されています。 全てのAWSアカウントで共通のリストです。 リストに登録済みのメールアドレスへの送信を試みると、バウンスレートが上がります。 メールアドレスは最大で14日間保存され、その後削除されます。 登録されたメールアドレス一覧をAPI等で取得することは出来ません。 SESを使う限り、利用必須 のリストです。 利用停止はできません。 Account-level Suppression List 2019年11月から提供されています。 単一のAWSアカウントの中でSESを利用している単一リージョンごとのリストです。 リストに登録済みのメールアドレスへの送信を試みても、バウンスレートは上がりません1。 メールアドレスは自動削除されません。 登録されたメールアドレス一覧をAPIで取得することが出来ます。 利用は任意です。 2019年11月25日以降にSESを利用開始した場合は、デフォルトで有効 になっています。 2つの Suppression List の共通点 ハードバウンス(と苦情)は 自動的に 登録されます2 3。 SNS・SQS・処理バッチ等をセットアップする必要はありません。 ソフトバウンスは登録されません。 リストに登録されていれば、実際のメール送信は行われません。 リストに登録されていても、メール送信は成功したように見えます。 ちなみに、2013年5月まで提供されていた Blacklist は、メール送信時に失敗する仕様でした4。 バウンスレートを下げるために適切な構成は? 2つの Suppression List の仕様を理解すると、基本的にデフォルトのままで問題ないことがわかります。 Account-level Suppression List を使うべき 必須の Global Suppression List に加えて、任意の Account-level Suppression List も使うべきです。 なぜなら、Global Suppression List だけに登録されたメールアドレスへ送信を試みるとバウンスレートが上がるのに対し、Account-level Suppression List にも登録されている場合はバウンスレートは上がらないからです。 使い方は簡単です。 Account-level Suppression List は、2019年11月25日以降にSESを利用開始した場合は、デフォルトで有効になっています。 有効になっていなくても、1度だけAPIを叩けば有効にすることができます。 他に必要な作業はありません。 SNSによるバウンス情報受け取りは不要 SNSを構成してバウンス情報を受け取る必要はありません。 なぜなら、Account-level Suppression List に(自動的に)登録されたメールアドレスは、APIで取得できるからです。 もう不要かもしれない AWS SNS や 自前DB構成の見直しを SNS (+ SQS) + EC2/ECS/Lambda でバウンスを処理したり、RDB/NoSQL を使って自前の Suppression List を構築しているシステムは多いのではないでしょうか。 たしかに、Global Suppression List だけ提供されていた時期(2013年5月〜2019年11月)は、リストに登録されてしまったメールアドレスを確実に送信先から除外する必要があり、それを実現するための唯一の正しい構成でした(そうしないとバウンスレートが上がってしまいました)。 でも、当時と比べてSESは進化しているので、このような構成は不要になっているかもしれません。 要件/目的と照らして、一度見直しましょう。 バウンスされた事実をリアルタイムに把握して、サービスの運用者/利用者にフィードバックしたい プッシュ型で把握する方法は現在でもこれしかありませんので、この目的のためにSNSを構築するのは妥当です。 なお、SNS + Lambda の構成では、間にSQSを挟むほうが望ましいです。 自動リトライの恩恵を受けられるため5です。 ハードバウンスの種類・回数・時刻を厳密に把握したい Account-level Suppression List からはこれらの情報を取得できませんから、この目的のためにSNSを構築するのも妥当です。 しかし殆どのシステムでは、厳密に把握する必要はないと考えられるので、仕様を再検討できるのではないでしょうか。 ソフトバウンスを再送したい ソフトバウンスはどちらの Suppression List にも登録されないものの、SESが勝手に再送してくれます6から、この目的のためにSNSを構築するべきではありません。 なお、ソフトバウンスは最大12時間ほど再送が指数関数的バックオフでリトライ7され、それでも成功しなかったときにようやく、SNSでバウンス情報を送ってくるようです6。 メールを送信する前にリストをチェックして、プログラムレベルで送信を防ぎたい Account-level Suppression List なら自動でハードバウンスが登録され、かつ、APIでアクセスできるので、SNSも独自DBも不要です。 まとめ 今回、数年ぶりにSESでバウンスレートを下げるための検討/調査を行い、「自前処理は基本不要」という結論に至るまで少々時間がかかりました。 私自身が Global Suppression List だけが提供されていた頃の古い知識/経験にとらわれていたし、ドキュメントからは明確に読み取れない重要なポイント3 6もあったためです。 時間をかけて念入りに調査した分、正確性は上がっていると思いますので、お役立ていただけると幸いです。 また、2021年2月から Amazon SES の管理コンソール画面やドキュメントが一新され、従来のものには Amazon SES Classic という名称(レトロニム)が与えられていたことがわかりました。 そのため、Classic の機能は変更/廃止になったのかどうかの確認も必要でした。 どうやら機能追加だけだったようですが、旧 SES API も Classic に含まれ、廃止までの期間限定(ただし廃止時期は未定)とされているので、SES API v2 への移行は近いうちに済ませたほうが良いかもしれません。 SESに限らず、AWSには便利な機能がどんどん追加されています。 最新情報を調べることで、無駄な作業を避け、本質的な開発に注力したいですね。 当社では、トヨタ車のサブスク「KINTO」等の企画/開発を行っており、エンジニアを募集中です。 KINTO Technologies コーポレートサイト Using the account-level suppression list より、"Amazon SES doesn't count the messages that you send to addresses on the account-level suppression list toward the bounce rate for your account."。Account-level Suppression List に登録されているメールアドレスに送信試行しても、バウンスレートに影響しないことが明記されています。 ↩ Using the Amazon SES global suppression list より、"When any Amazon SES customer sends an email that results in a hard bounce, Amazon SES adds the email address that produced the bounce to a global suppression list."。Global Suppression List では、ハードバウンスは自動で登録されることが明記されています。 ↩ Using the account-level suppression list より、"When you configure the account-level suppression list, you specify whether addresses should be added to the list when they result in hard bounces, when they result in complaints, or both. You can manually add or remove individual or bulk addresses from the account-level suppression list by using the Amazon SES API v2."。Account-level Suppression List のほうは少し分かりづらくて、手動またはAPIを使ったバルク処理でのみ、ハードバウンスされたアドレスが追加されるものと誤解しそうなのですが、実動作とAWSサポートへの問い合わせで、「自動追加されること」を確認しました。 ↩ Goodbye blacklist. Introducing the suppression list! より、 "The main difference between the blacklist and the suppression list is that if you tried to send an email to an address on the blacklist, the call was rejected with an “Address Blacklisted” error. Now, the call to Amazon SES succeeds, but Amazon SES treats the email as if it was a hard bounce."。Blacklist に登録されていたメールアドレスへのメール送信のAPIコールは失敗する仕様だったことがわかります。 ↩ SNS to Lambda vs SNS to SQS to Lambda への回答  ↩ Amazon SES email-sending processより、"Soft bounce—The ISP cannot deliver the email to the recipient because of a temporary condition, such as the ISP is too busy to handle the request or the recipient's mailbox is full. A soft bounce can also occur if the domain does not exist. The ISP sends a soft bounce notification back to Amazon SES, or, in the case of a nonexistent domain, Amazon SES cannot find an email server for the domain. In either case, Amazon SES retries the email for an extended period of time."。なお、What is considered a soft bounce on Amazon SES, and how can I monitor soft bounces? の "Generally, soft bounces should be retried after some time because they are caused by temporary issues." からは、SESではなくAWS利用者がリトライをすべきという印象を受けますが、AWSサポートの回答は「"SESが"リトライする」とのことでした。 ↩ Does SES resend email if soft bounce occurs? より、"When attempting to deliver an email, Amazon SES will continue making delivery attempts until receiving a successful response, or until 12 hours elapse. If the receiving ISP returns a temporary error code (e.g., a 4xx SMTP code), then SES will keep trying. There is no limit to the number of attempts; however, SES will apply exponential backoff between retries for up to 12 hours."。 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSの分散負荷テストソリューション及び負荷テストのノウハウ

この記事は、NTTテクノクロス Advent Calendar 2021 の8日目です。 こんにちは、NTTテクノクロスの堀江です。普段はAWSやAzure上でのシステム設計・構築や調査検証系の案件を担当しています。 はじめに 本記事では、AWSが公式に公開している分散負荷テストソリューションの使い方に関するノウハウ、およびそれを使用してAWS上で負荷試験を実施する際の注意点を述べていきます。 ※「分散負荷テスト(Distributed Load Testing)」だと長いので、以降は「dlt」と呼称します。 今年、とある案件でAWS上でのWEBアプリケーション(WEB層にALB、アプリ層にEC2のASGという一般的な構成)の性能試験を実施する機会があり、アプリケーションにリクエスト負荷を掛ける為のソリューションとして、dltを使用してみました。全体的に非常に便利なソリューションでしたが、思い通りの試験シナリオと性能を実現するために詰まったり試行錯誤したりした点もありました。本記事では、それらを踏まえたdltの使い方に関するノウハウと、AWS上で性能試験を実施する上での注意点を述べていきます。 本記事で扱うdltは、2021年9月30日にリリースされたv2.00の仕様に準拠しています。 AWSの分散負荷テストソリューションとは dltの特徴は大きく以下の3点です。 負荷を発生させるためのクライアントをサーバレスな構成で管理可能 GUIのテスト管理コンソール上から試験を作成/実行/確認することが可能 Jmeterのスクリプトを使用して、テストの挙動やロギング設定をカスタマイズ可能 dltの全体的なアーキテクチャーは下図の通りです。事前の作業も特に不要で、全てのリソースを、公式が提供しているCloudFormationテンプレート一枚でデプロイ可能です。 画像は公式サイトから拝借 https://aws.amazon.com/jp/solutions/implementations/distributed-load-testing-on-aws/ フロントエンド Amplifyによって管理コンソールアプリケーション(静的サイト)がデプロイされます。 コンソール上から、負荷テストAPIを介してテストの作成・実行を行うことができます。 コンソールへのログインおよびAPIの使用はCognitoによる認証で守られています。 テストが完了すると、下図のようにグラフィカルかつ直感的な結果を確認することが可能です。 バックエンド テストの実行がリクエストされると、指定した数・設定のECS Fargateのタスクが負荷クライアントとして起動されます。 自分のアカウントでは、デフォルトで最大50までタスクを起動することが可能でした(デフォルトの上限数はアカウント毎に異なっている可能性あり)。後述するように上限緩和申請を行えば最大1000まで増加可能なようです。また、各タスクごとにさらに並行数を設定することが可能です。(推奨されている最大数は200) 最大数百タスクの起動・停止に関するオーケストレーションはStep FunctionsとLambdaが担当してくれます。 dltを使用することで、金銭的・作業的コストを抑えつつ、アプリケーションに対して手軽に大規模な性能試験を実施することができます。 dltを使用した負荷試験実施時の注意点 上記の通り、非常に便利なdltですが、AWS上で実際に負荷試験を行う際にはいくつか注意すべき点、事前に確認しておくべき点がありました。(何点かはdltと無関係ですが、)具体的には以下の通りです。 AWSのテストポリシーを照会すること AWSのサービスクォータを確認すること 負荷テストツールの性能を把握すること ネットワークの経路には注意すること ALBの性能を把握しておくこと AWSのテストポリシーを照会すること AWS上のリソースに対してネットワーク負荷テストを実行する際に、ユーザが遵守すべきポリシーが公式に定められています。 負荷試験時に想定されるネットワークのトラフィック量がこのポリシーに違反していないかを事前に確認し、必要があれば事前にAWSに相談する必要があります。 AWSのサービスクォータを確認すること 例えばEC2インスタンスをバックエンドサーバとして使用しているアプリケーションに対してdltを使用して負荷試験を行う場合、事前に最低でも以下のサービスクォータの値を確認し、必要であれば早めに上限緩和申請を行っておくのが良いでしょう。 「Running On-Demand All Standard (A, C, D, H, I, M, R, T, Z) instances」といった、vCPU数に関するクォータ 自分が使用していた環境では、このクォータの設定値が36vCPUしかなく、(かつ他のアプリケーションとの共用環境であったため、)いざ試験を行おうとしても必要な台数のバックエンドサーバを起動することができませんでした。(上限緩和申請が通るまで数時間試験が停滞) Fargate On-Demand resource count 自分が使用していた環境では、このクォータの設定値が50でした。しかしそれでは計画していた量の負荷を発生させることができず、上限緩和申請が必要となりました。(上限緩和申請が通るまで数日間試験が停滞) 特にハマったのは、Service Quotasのダッシュボード上では「適用されたクォータ値」が100となっていたのに、実際には50までしかタスクを作成できない点でした。AWSではService Quotasのダッシュボード上に表示される「適用されたクォータ値」と実際に適用されているクォータ値が乖離していることが偶にあるので、しっかり事前に動確までしておく必要がありました。 負荷テストツールの性能を把握すること 負荷試験の結果が芳しくない場合、それがアプリケーション(サーバ)側の問題なのか、テストツール(クライアント)側の問題なのか、もしくはそれ以外(ネットワークやロードバランサ)の問題なのかの切り分けができていることが重要です。dltに関しても、1Fargateタスク(クライアント)あたりの負荷性能の限界を事前に把握しておくことが大事です。 タスクあたりの負荷性能(ここでは1秒間に可能なリクエスト数とします)を左右する要因は以下の2点です。 タスク内の並行数 タスクに割り当てられるvCPU数 タスクの並行数は、テスト作成時に1~200の値で自由に設定することが可能です。 タスクに割り当てられるvCPU数は、デフォルトで2048となっています。この設定値はパラメータ化されていませんが、以下のようにCloudFormationテンプレートを修正することで、上限一杯(4096)までスケールアップ可能です。 # ... 省略 DLTEcsDLTTaskDefinition6BFC2400: Type: AWS::ECS::TaskDefinition Properties: ContainerDefinitions: - Essential: true Image: public.ecr.aws/aws-solutions/distributed-load-testing-on-aws-load-tester:v2.0.0 LogConfiguration: LogDriver: awslogs Options: awslogs-group: Ref: DLTEcsDLTCloudWatchLogsGroupFE9EC144 awslogs-stream-prefix: load-testing awslogs-region: Ref: AWS::Region # Memory: 4096 Memory: 8192 Name: Fn::Join: - "" - - Ref: AWS::StackName - -load-tester # Cpu: "2048" Cpu: "4096" ExecutionRoleArn: Fn::GetAtt: - DLTEcsDLTTaskExecutionRoleDE668717 - Arn # Memory: "4096" Memory: "8192" NetworkMode: awsvpc RequiresCompatibilities: - FARGATE TaskRoleArn: Fn::GetAtt: - DLTEcsDLTTaskExecutionRoleDE668717 - Arn # ... 省略 自分が試しに測定してみた限り、並行数とvCPU数ごとのタスクの負荷性能の目安はおおよそ以下の通りに想定しておくと良さそうです。 vCPU数\並行数 1 100 200 2048 50req/s 150req/s 150req/s 4096 100req/s 300req/s 300req/s ※t2.mediumインスタンス4台(nginxのウェルカムページをレスポンスするだけ)をターゲットに持つALBに対する負荷結果を基にしています。 上記より、例えば並行数100、vCPU数2048で負荷を掛けているのにアプリケーションが毎秒100リクエスト程度しか捌けていない場合は、アプリケーション側にボトルネックがある可能性が高いです。 一方で、秒間1000リクエストの負荷をかけたいような場合には、並行数100、vCPU数2048のタスクを最低10個は用意する必要がありそうです。 (補足)dltで秒間リクエスト数を特定の値に固定する方法 上記の性能目安から、例えば並行数100、vCPU数2048のタスクを5つ作成すれば、アプリケーションに掛かる負荷は概ね毎秒750リクエスト前後になりそうです。しかし、もっと正確に毎秒リクエスト数を固定したいような場合には、Jmeterのシナリオファイルを使用する必要があります。 Jmeterのシナリオには、ConstantThroughputTimerという設定項目があり、クライアントごとの毎分あたりのリクエスト数をコントロールしてくれます。 最終的に実現される秒間リクエスト数は、(ConstantThroughputTimerの設定値 / 60s) * タスク数 * タスクあたりの並行数となります。 例えば下図のシナリオファイルでは毎分あたりのスループットを180にしているので、3並行のタスクを10個作成すれば、トータルでアプリケーションに掛かる秒間リクエスト数は(180 / 60) * 3 * 10 = 90となります。 ネットワークの経路には注意すること dltのFargateタスク(クライアント)のリクエストを、Nat Gateway経由でアプリケーションにルーティングする際には負荷量が制限されることに注意しましょう。 そもそも自分の試験実施時にNat Gatewayを経由させたかった理由ですが、アプリケーション(のALB)にアクセス可能なIPアドレスをセキュリティグループで制限したかったためでした。通常であればdltはテストが実行されるたび、Fargateタスク毎ににその都度パブリックIPアドレスが払い出されるため、アプリケーションのALBのセキュリティグループを0.0.0.0/0でオープンにしておく必要がありました。そこでFargateタスクをプライベートサブネット上に起動させ、Nat Gatewayを経由してアプリケーションにリクエストするように修正すれば、Nat GatewayにアタッチされたElastic IPアドレス以外からのALBへのリクエストをセキュリティグループで制限することが出来ます。 上記の修正自体はそれほど難しくありません。具体的には、Nat Gatewayへのルーティングが設定された2つのプライベートサブネットを含むVPCを予め作成しておき、dltのスタック作成時に設定可能なパラメータExistingVPCId,ExistingSubnetA,ExistingSubnetBにそれぞれ値を設定すれば可能です。 しかし上述の設定を実施した上で大規模な負荷を掛けようとすると、以下に記載されているようなNAT Gateway側の性能限界に引っかかってしまいます。 NAT ゲートウェイは送信先別に最大 55,000 の同時接続をサポートできます。この制限は、単一の送信先に 1 秒あたり約 900 の接続 (1 分あたり約 55,000 の接続) を作成する場合にも適用されます。送信先 IP アドレス、送信先ポート、またはプロトコル (TCP/UDP/ICMP) が変更された場合は、追加の 55,000 の接続を作成できます。55,000 を超える接続の場合は、ポートの割り当てエラーによる接続エラーの可能性が高くなります。 https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-nat-gateway.html#nat-gateway-basics 自分の場合は秒間10000リクエストの負荷を掛けようとしたものの、上記の制限に引っかかって期待通りのスループットが出ず、解析に丸一日を要しました。 少ないリクエストまでであれば問題はないかもしれませんが、大規模な負荷をアプリケーションに掛けたい場合は、Nat Gatewayは使用すべきではないでしょう。(自分の場合は、大規模負荷試験の際は一時的に0.0.0.0/0を許可する必要性を顧客に説いて納得してもらいました。) ALBの性能を把握しておくこと 例えば、Elastic Load Balancingの特徴を紹介している以下の公式ページでは、下記の様にロードバランサーの高スループット性を説明しています。 Elastic Load Balancing は、トラフィックが増大した場合でも処理できるよう設計されており、1 秒間に数百万件ものリクエストでも負荷分散できます。また、突発的で不安定なトラフィックパターンにも対処できます。 この記述だけを読めば、ELB(ALB)は暖気申請などせずとも、突発的で大量の(毎秒数千~数万件の)リクエストも簡単に捌くことが可能で、負荷試験時のボトルネックにはなり得ないと判断出来るかも知れません。恥ずかしながら自分はそうでした。 しかし実際に(暖気申請無しで)ALBとそのバックエンドサーバ群に対して高負荷試験を実施してみると、事前の想像とは大分異なるALBの挙動を確認することが出来ました。 以下は、新規構築直後のALBに対してdltから秒間10000リクエスト相当(※)の負荷を60分間掛けた際にCloudWatchで記録された、ALBのRequestCountメトリクスの推移です。 ※JmeterシナリオのConstantThroughputTimerを120に設定し、(毎分120リクエスト / 60秒) * 50タスク * 100並行 = 毎秒10000リクエスト(= 毎分600000リクエスト)とした。 この結果から、ALBのスループットは約10分間隔で段階的にスケーリングされる、ということが言えそうです。(構築直後のALBが捌けるのは秒間2000リクエスト前後。秒間10000リクエストを捌けるようになるまでに約30~40分程度掛かる。) なお、試しに上記の1時間後に再度同じALBに同じ負荷を掛けてみると、今度は最初から秒間10000リクエスト捌けているようでした。スケーリング状況は暫くの間(少なくとも1時間は)継続するようです。 ALBのスループットのスケーリングに関する仕様は明らかにされていないので、この挙動の再現性や普遍性を保証することは出来ません。しかし実際にALBを使用して試験を行う際には(そして実際に本番運用する際には)この挙動のことを覚えておいて損は無いでしょう。 (補足)ALBの暖気申請について (多くのAWS技術者は資格試験問題等を通してご存知かと思いますが、)ALBの暖気申請をAWSサポートに対して行うことで、上記のような段階的なスケーリングに時間を掛けず、いきなり高スループットを実現出来るとされています。 ただ、暖気申請に関しては公式の情報が殆ど存在しないのですよね。本当に利用可能なのか、個人的には都市伝説めいた存在に感じてしまいます。 まとめ AWSではdltを使用することで、簡単に高負荷の性能試験を実施することが出来ます。一方で、dltやALBのようなマネージドサービスの性能限界や挙動を正しく把握していないと、期待通りの性能が得られなかったり、解析で思わぬドツボにはまってしまったりする可能性もあります。本記事で挙げた内容がそういったトラブルを回避する助けになればと思います。 明日のNTTテクノクロス Advent Calendar 2021は、 @thetakei さんによる、標準化活動に関する記事です。 お楽しみに!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

振り返り②(これいる?作っとく?の会話記録)

振り返り2回目です。 今回もどうしようね?って3人で話した結果の記録です。 決まったこと サーバーのお金、どうする? →会社の人に要相談(AWS1年間無料プランがあったけど…) →後日菊池さんに聞いたところ、お金は気にしなくていいよとイケメンな回答された toolとfrontいる? →stgとprodいる?金銭的な問題のため自分たちでは決められないねえ。。。 →菊池さん「dev環境だけ作ればいいよ」 アカウント制限どうする?いる? →必要ないかと思ったけど、Laravelでアカウントの権限のところを作るのは調べたところ、  容易そうなので、やってみたいかもってことで作ることに マスター権限だけでできることって何かある!? →アカウント削除とかユーザー一覧が見られるくらいになりそう →とりあえず作っておこ! 画面仕様書とかテーブル設計書とかワイヤー作る? →そのほうが管理しやすいと思われる →個人的にドキュメント作るのはいい経験だと思うので、作っておきたい →菊池さん「簡易的なシステム構成図とか書くと整理できていいかもね」 私「じゃあ作ってみよう〜」 進めていくうちに他にも決めることがいろいろ出てくるだろうけど、 とりあえず始まったばかりはこんな感じだろって思いました。 written by 大森
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

複数のAWSアカウントにあるAWS CodeCommitリポジトリを切り替えて使用する

Goal AWS CodeCommitで複数のAWSアカウントにあるAWS CodeCommitリポジトリを適宜切り替えて使用する際の手順を示します。 Authoring Guidelines 後日、修正する予定です。 References 本文の手順は、次の文書を参考に作成しました。 "I need to access CodeCommit repositories in multiple Amazon Web Services accounts with SSH credentials". Amazon Web Services, Inc. Retrieved 29 November 2021. Pre-Requisites 本文の手順は、次の環境で実施、検証しました。 Windows 10, build 19043.1348 How To 以下の場所にあるSSHのコンフィグレーションファイルを任意のエディタで編集します。 Linux, macOS - ~/.ssh/config Windows - %USERPROFILE%.ssh\config コンフィグレーションファイルに書き込む内容は、次の通りです。 Host codecommit-1 Hostname git-codecommit.ap-northeast-1.amazonaws.com # 1つ目のAWSアカウントで実際の接続に使用するホスト名 User xxxxxxxxxxxxxxxxxxxxx # 1つ目のAWSアカウント用のSSHキーID IdentityFile ~/.ssh/codecommit_rsa_1 # 1つ目のAWSアカウント用のSSH秘密鍵へのパス Host codecommit-2 Hostname git-codecommit.ap-northeast-1.amazonaws.com # 2つ目のAWSアカウントで実際の接続に使用するホスト名 User xxxxxxxxxxxxxxxxxxxxx # 2つ目のAWSアカウント用のSSHキーID IdentityFile ~/.ssh/codecommit_2_rsa # 2つ目のAWSアカウント用のSSH秘密鍵へのパス Gitコマンド発行時に、これまで指定していたホスト名部分をコンフィグレーションファイルに記載した "Host" の値で指定します。 REM これまでの書き方 git clone ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/%REPOSITORY_NAME% REM これからの書き方 git clone ssh://codecommit-1/v1/repos/%REPOSITORY_NAME%
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS Lambda + S3 + Python で PDF を扱う際のTips

初めに PDF を AWS で扱う際に、ローカルの環境と Lambda や S3 では少し扱いに注意が必要だったので、忘れないようにまとめておきます。 1. S3 から PDF を読み込む Python で PDF を扱う場合は PyPDF2 を使うことが多いかと思います。 ローカル環境で読み込む場合は # example code from PyPDF2 import PdfFileReader PDF_PATH = 'pdf/xxx.pdf' # ローカルのディレクトリ pdf = PdfFileReader(PDF_PATH) とパスを指定するだけで簡単に読み込むことができます。 S3 から PDF を読み込む場合は少し工夫が必要です。 # example code import boto3 from PyPDF2 import PdfFileReader from io import BytesIO PDF_PATH = 'data/pdf/xxx.pdf' # S3バケットのディレクトリ BUCKET_NAME = 'your-bucket-name' bucket = boto3.resource('s3').Bucket(BUCKET_NAME) pdf_obj = bucket.Object(path).get()['Body'].read() # S3からの取り出し bytes = BytesIO(pdf_obj) # 変換する pdf = PdfFileReader(bytes) バケットからただ読み込むだけでは機能せず、BytesIO で 変換することで扱うことができます。 2. Lambda上で PDF の一時編集する これは PDF に限らずですが、Lambda で書き込む際は /tmp/ 配下でないとエラーになります。 実際のエラーログ 他のディレクトリを指定すると、このようなエラーになります。 3. Lambda から PDF ファイルを取得する API として PDF を返却する場合があると思います。 # example code with open(output_file, 'rb') as data: pdf = data.read() PDF を開いて Lambda 上でクライアント側に return した場合、自動で utf-8 に変換されるようで、日本語など無効な文字があるとエラーが発生します。 [ERROR] UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 そこで、base64で変換します。そうすることでエラーを回避することができます。 # example code import base64 with open(output_file, 'rb') as data: pdf = data.read() return base64.b64encode(pdf).decode('utf-8') 当然ながらクライアント側でも対応が必要になるので注意してください。 HTMLで表示する場合 デコードは必要ないですが、特定の書き方が必要です。 <html lang="ja"> <head> <meta charset="UTF-8" /> <title>Document</title> </head> <body style="height: 100%; width: 100%; overflow: hidden; margin:0px; background-color: rgb(82, 86, 89);"> <embed style="position:absolute; left: 0; top: 0;" width="100%" height="100%" type="application/pdf" src="data:application/pdf;base64,/* base64 の文字列を入れる */" /> </body> </html> (引用:ブラウザでbase64エンコードしたPDFファイルを表示する) iOS(Swift)で表示する場合 クライアント側では、変換した後に PDFKit の PDFDocument に Data型でセットします。 import PDFKit let pdf = PDFView() pdf.autoScales = true pdf.displayMode = .singlePageContinuous // 変換する if let data = Data(base64Encoded: str) { pdfView.document = .init(data: data) } pdfView.translatesAutoresizingMaskIntoConstraints = false view.addSubview(pdfView) NSLayoutConstraint.activate([ pdfView.leadingAnchor.constraint(equalTo: view.leadingAnchor), pdfView.trailingAnchor.constraint(equalTo: view.trailingAnchor), pdfView.topAnchor.constraint(equalTo: view.topAnchor), pdfView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) 終わりに Lambda 上で実行しないと分からないエラーがそれなりにあるので、1つずつクリアしていくのが苦労しました。また、知見が増えたら追加していこうと思います。 参考文献
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Gitリポジトリを古いAWSアカウントから新しいAWSアカウントへ移行する

Goal AWS CodeCommitに既にあるGitリポジトリを古いAWSアカウントから新しいAWSアカウントへ移行する際の手順を示します。 Authoring Guidelines AWS CodeCommitに限らず、GitHubからAWS CodeCommitへの移行、GitHubからGitHubへの移行でも、読み替えによって手順の大筋が把握できるようにAWS Management ConsoleとGitコマンドでの手順で主な流れを記載します。 自動化のため、将来的にAWS CodeCommitのAPIをコールするAWS CLIによる手順も追記する予定です。 References 本文の手順は、次の文書を参考に作成しました。 "Migrate a Git repository to AWS CodeCommit". Amazon Web Services, Inc. Retrieved 29 November 2021. "How to Rename the master branch to main in Git". Tower. Retrieved 29 November 2021. Pre-Requisites 本文の手順は、次の環境で実施、検証しました。 Windows 10, build 19043.1348 How To 新しいリポジトリを作成する 次の任意の手段で、新しいリポジトリを作成します。 AWS Management Console AWS CLI AWS CloudFormation 古いリポジトリの内容を新しいリポジトリへ反映する [!IMPORTANT] この手順では、一度もコミットされていない新しいリポジトリへ古いリポジトリの内容を移行します。既に何らかのコミットがなされている新しいリポジトリへ古いリポジトリの内容を増分移行する手順ではありません。 古いリポジトリのベアリポジトリをローカルに作成し、古いリポジトリの内容を新しいリポジトリへ反映します。 git clone --mirror %OLD_REPOSITORY_PATH% CD %OLD_REPOSITORY_NAME%.git git push %NEW_REPOSITORY_PATH% --all git push %NEW_REPOSITORY_PATH% --tags [!IMPORTANT] git push --all だけではタグが反映されないため、別途 git push --tags を実行する必要があります。 成功したら、ローカルに作成した古いリポジトリのベアリポジトリを削除します。 CD .. RD /S %OLD_REPOSITORY_NAME%.git 新しいリポジトリのデフォルトブランチ名を変更する BLM後の世界情勢に合わせて、新しいリポジトリのデフォルトブランチ名を "master" から "main" へ変更します。 新しいリポジトリをローカルへクローンし、ローカルリポジトリでブランチ名を変更します。 git clone %NEW_REPOSITORY_PATH% CD %NEW_REPOSITORY_NAME% git branch -m master main ローカルリポジトリでブランチ名変更により作成したmainブランチをリモートリポジトリに反映します。 > git push -u origin main Total 0 (delta 0), reused 0 (delta 0), pack-reused 0 To %NEW_REPOSITORY_PATH% * [new branch] main -> main Branch 'main' set up to track remote branch 'main' from 'origin'. 次の任意の手段で、新しいリポジトリのディフォルトブランチを "master" から "main" へ変更します。 AWS Management Console AWS CLI ローカルリポジトリでブランチ名変更により削除したmasterブランチをリモートリポジトリに反映します。 > git push origin --delete master To %NEW_REPOSITORY_PATH% - [deleted] master [!IMPORTANT] ディフォルトブランチを "master" から "main" へ変更せずにmasterブランチの削除をリモートリポジトリに反映しようとすると、エラーにより失敗します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

aws_iam_openid_connect_providerのthumbprint_listの計算方法

GitHub ActionsがOIDCプロバイダとして使えるようになりました。 これによりAWSのアクセスキーを埋めることなくロールベースでのアクセスができるようになりました。やったね。 この話自体はなぜか公式リリース前にサンプルコードが出回ったりして、みんなサンプルコードコピペして動いた〜って話題になってたので今さら感があるかもですが、例えばTerraformでaws_iam_openid_connect_providerの設定をするならこんなかんじでしょうか。 resource "aws_iam_openid_connect_provider" "github" { url = "https://token.actions.githubusercontent.com" client_id_list = ["sts.amazonaws.com"] thumbprint_list = ["a031c46782e6e6c662c2c87c76da9aa62ccabd8e"] } ふむふむ〜ってかんじですが、いざ使おうとするとこの thumbprint_list がなんなのかが気になって仕方がなかったので調べました。 thumbprint_list - (Required) A list of server certificate thumbprints for the OpenID Connect (OIDC) identity provider's server certificate(s). terraform-provider-awsのドキュメントにはOIDCプロバイダの公開鍵のなんかのハッシュ値との記載があり、公式のGitHub ActionsにもCloudFormationのテンプレートの例が載っていますが、特に説明もなくマジックナンバーが埋まっています。 GithubOidc: Type: AWS::IAM::OIDCProvider Condition: CreateOIDCProvider Properties: Url: https://token.actions.githubusercontent.com ClientIdList: - sts.amazonaws.com ThumbprintList: - a031c46782e6e6c662c2c87c76da9aa62ccabd8e この a031c46782e6e6c662c2c87c76da9aa62ccabd8e はどこから来たのか?気になりますよね?え、気にならない?気にならない人は別にこれ以降は読む必要はありません。私は気になる方の人だったので調べました。 まずGitHub Actionsの公式ドキュメントを見ましたが、特に記載が見つかりませんでした。 AWSの公式ドキュメント見たら、以下の記載を見つけました。OIDCプロバイダのCAの証明書のハッシュ値です。 When you create an OpenID Connect (OIDC) identity provider in IAM, you must supply a thumbprint. IAM requires the thumbprint for the top intermediate certificate authority (CA) that signed the certificate used by the external identity provider (IdP). The thumbprint is a signature for the CA's certificate that was used to issue the certificate for the OIDC-compatible IdP. よく使われそうなGitHubぐらい例として載せておいてくれよと思いつつ、計算したらたしかにそうなった。 まず、OIDCプロバイダのjwks_uriのドメインを調べます。 $ curl -s https://token.actions.githubusercontent.com/.well-known/openid-configuration | jq . { "issuer": "https://token.actions.githubusercontent.com", "jwks_uri": "https://token.actions.githubusercontent.com/.well-known/jwks", "subject_types_supported": [ "public", "pairwise" ], "response_types_supported": [ "id_token" ], "claims_supported": [ "sub", "aud", "exp", "iat", "iss", "jti", "nbf", "ref", "repository", "repository_owner", "run_id", "run_number", "run_attempt", "actor", "workflow", "head_ref", "base_ref", "event_name", "ref_type", "environment", "job_workflow_ref" ], "id_token_signing_alg_values_supported": [ "RS256" ], "scopes_supported": [ "openid" ] } jwks_uri の証明書を取得します。 $ openssl s_client -servername token.actions.githubusercontent.com -showcerts -connect token.actions.githubusercontent.com:443 CONNECTED(00000005) depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert High Assurance EV Root CA verify return:1 depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = DigiCert SHA2 High Assurance Server CA verify return:1 depth=0 C = US, ST = California, L = San Francisco, O = "GitHub, Inc.", CN = *.actions.githubusercontent.com verify return:1 --- Certificate chain 0 s:/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=*.actions.githubusercontent.com i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA -----BEGIN CERTIFICATE----- MIIHEDCCBfigAwIBAgIQDydo1w3Zr+2V8IN+I3bC+TANBgkqhkiG9w0BAQsFADBw MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMS8wLQYDVQQDEyZEaWdpQ2VydCBTSEEyIEhpZ2ggQXNz dXJhbmNlIFNlcnZlciBDQTAeFw0yMDA0MDMwMDAwMDBaFw0yMjA0MDgxMjAwMDBa MHsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T YW4gRnJhbmNpc2NvMRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xKDAmBgNVBAMMHyou YWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQDICQluw+gpsxgWSHEbyqwoTKV29C0CyU11Dc/sVMEXsLw+ DjIgxYTxKum1xkhOoewajpBKE9Xe+EWlOOLpsn8i+rU7HXBeSnTYZeh8Z5lzZk0H iu9HeKNdfmpbzlszFdY1wJwQLOBSL+/8eQJfdAgvWxWTsONxsq5dzV/rtSfhEx8G dG5ZJDSIUvjsf/xDRQeFFE+SvmooC/RzAL8Q1BXGfh8lWbfgnPylte3wZ8RSPeh8 XEEGxKUE9kgAjS7B1APZxBPGg0EWkEvbkMFjD0b4EhWHN98Fw2iaZpwOiH+NF4Bu q2EYK3Rq/Vq/YMjYqWlpvDNUIsGcCwCQqj3wM7krAgMBAAGjggOZMIIDlTAfBgNV HSMEGDAWgBRRaP+QrwIHdTzM2WVkYqISuFlyOzAdBgNVHQ4EFgQUlWwX+zmXWLbh Nzfku+3tBl9hdCAwSQYDVR0RBEIwQIIfKi5hY3Rpb25zLmdpdGh1YnVzZXJjb250 ZW50LmNvbYIdYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wDgYDVR0PAQH/ BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB1BgNVHR8EbjBs MDSgMqAwhi5odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1oYS1zZXJ2ZXIt ZzYuY3JsMDSgMqAwhi5odHRwOi8vY3JsNC5kaWdpY2VydC5jb20vc2hhMi1oYS1z ZXJ2ZXItZzYuY3JsMEwGA1UdIARFMEMwNwYJYIZIAYb9bAEBMCowKAYIKwYBBQUH AgEWHGh0dHBzOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQICMIGDBggr BgEFBQcBAQR3MHUwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv bTBNBggrBgEFBQcwAoZBaHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD ZXJ0U0hBMkhpZ2hBc3N1cmFuY2VTZXJ2ZXJDQS5jcnQwDAYDVR0TAQH/BAIwADCC AX4GCisGAQQB1nkCBAIEggFuBIIBagFoAHYAKXm+8J45OSHwVnOfY6V35b5XfZxg Cvj5TV0mXCVdx4QAAAFxQK0GWgAABAMARzBFAiEAtOsN2nTCsYy3vcEOmue5jALN QT3PSbyBo1CrfqB7PZcCIHYxknrxHm79ozyK99Ywp1fzkOvcDQwsDspAz3eEGC4i AHYAIkVFB1lVJFaWP6Ev8fdthuAjJmOtwEt/XcaDXG7iDwIAAAFxQK0GkQAABAMA RzBFAiEA6zg3TWCuOMEVFfCDGqQ6DIFdKmvQUu1qifvtTnkgBF8CIE8Ge6Emyx35 AjPxzImIcQLmKK2/m4hCxlQ7J0Pqgf5WAHYAUaOw9f0BeZxWbbg3eI8MpHrMGyfL 956IQpoN/tSLBeUAAAFxQK0G2gAABAMARzBFAiEAwOa3wkbpA5PngKBLno5LFylS KcFI63Do1hZFDCMH7U0CIEv4HZ0BXmVoFoS4k1AH3ZR1zLv1Suddppgeien5nGYA MA0GCSqGSIb3DQEBCwUAA4IBAQCZkidhrGI4fWaTGPzelZEYo9SA3ZfdKowMJwgc pmGS6x9GxhkxlT/ziGMUsTkKJc61yXv0tbKr0H4ZfG4CvxefS6YRIYR4Laum5YWB siXCVdL1hnDDvWI9wg14zKJiRUjv2RpM8JpkWxvGzi0SsdKJBUkFGUWgY3Dns4/v YrpNQkfrBKVZLRz70yXvbdegSHxGe9WivMB+ac7lAbUjcMy8o93HDZqoCgw0PreJ tHw2YwfG+tj0P6m/HqPK6TMjkQQysaQ3YTkGIIe3HeU52z4RQW0YcC/EnWDwPF++ l74SjPAIXDU8ApIKs1EW9b/8WkDNIjynDaFe4BmOJMkBK51Q -----END CERTIFICATE----- 1 s:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA i:/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert High Assurance EV Root CA -----BEGIN CERTIFICATE----- MIIEsTCCA5mgAwIBAgIQBOHnpNxc8vNtwCtCuF0VnzANBgkqhkiG9w0BAQsFADBs MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j ZSBFViBSb290IENBMB4XDTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcDEL MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 LmRpZ2ljZXJ0LmNvbTEvMC0GA1UEAxMmRGlnaUNlcnQgU0hBMiBIaWdoIEFzc3Vy YW5jZSBTZXJ2ZXIgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC2 4C/CJAbIbQRf1+8KZAayfSImZRauQkCbztyfn3YHPsMwVYcZuU+UDlqUH1VWtMIC Kq/QmO4LQNfE0DtyyBSe75CxEamu0si4QzrZCwvV1ZX1QK/IHe1NnF9Xt4ZQaJn1 itrSxwUfqJfJ3KSxgoQtxq2lnMcZgqaFD15EWCo3j/018QsIJzJa9buLnqS9UdAn 4t07QjOjBSjEuyjMmqwrIw14xnvmXnG3Sj4I+4G3FhahnSMSTeXXkgisdaScus0X sh5ENWV/UyU50RwKmmMbGZJ0aAo3wsJSSMs5WqK24V3B3aAguCGikyZvFEohQcft bZvySC/zA/WiaJJTL17jAgMBAAGjggFJMIIBRTASBgNVHRMBAf8ECDAGAQH/AgEA MA4GA1UdDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw NAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy dC5jb20wSwYDVR0fBEQwQjBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNlcnQuY29t L0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDA9BgNVHSAENjA0MDIG BFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQ UzAdBgNVHQ4EFgQUUWj/kK8CB3U8zNllZGKiErhZcjswHwYDVR0jBBgwFoAUsT7D aQP4v0cB1JgmGggC72NkK8MwDQYJKoZIhvcNAQELBQADggEBABiKlYkD5m3fXPwd aOpKj4PWUS+Na0QWnqxj9dJubISZi6qBcYRb7TROsLd5kinMLYBq8I4g4Xmk/gNH E+r1hspZcX30BJZr01lYPf7TMSVcGDiEo+afgv2MW5gxTs14nhr9hctJqvIni5ly /D6q1UEL2tU2ob8cbkdJf17ZSHwD2f2LSaCYJkJA69aSEaRkCldUxPUd1gJea6zu xICaEnL6VpPX/78whQYwvwt/Tv9XBZ0k7YXDK/umdaisLRbvfXknsuvCnQsH6qqF 0wGjIChBWUMo0oHjqvbsezt3tkBigAVBRQHvFwY+3sAzm2fTYS5yh+Rp/BIAV0Ae cPUeybQ= -----END CERTIFICATE----- --- Server certificate subject=/C=US/ST=California/L=San Francisco/O=GitHub, Inc./CN=*.actions.githubusercontent.com issuer=/C=US/O=DigiCert Inc/OU=www.digicert.com/CN=DigiCert SHA2 High Assurance Server CA --- No client certificate CA names sent Server Temp Key: ECDH, P-384, 384 bits --- SSL handshake has read 3540 bytes and written 398 bytes --- New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES256-GCM-SHA384 Session-ID: 7720000046A9ADA2FA16E4BAB2BE93AED2816789B890B48CB89CB7BDAB4F9600 Session-ID-ctx: Master-Key: 3FC5B951311CE6DBD68371558BD84270B92AC1D9DC17232EF1C30AEFCA1889348DC7F5A77F50D19989B34FA2E7145041 Start Time: 1638148905 Timeout : 7200 (sec) Verify return code: 0 (ok) --- ^C 中間証明書が見えますが、一番下の証明書の -----BEGIN CERTIFICATE----- から -----END CERTIFICATE----- のところをファイルとして保存して、fingerprintを計算します。 $ openssl x509 -in certificate.crt -fingerprint -noout SHA1 Fingerprint=A0:31:C4:67:82:E6:E6:C6:62:C2:C8:7C:76:DA:9A:A6:2C:CA:BD:8E シェル芸で = の右側の値から : を取り除き、小文字にするとたしかに一致しました。 $ openssl x509 -in certificate.crt -fingerprint -noout | cut -f2 -d'=' | tr -d ':' | tr '[:upper:]' '[:lower:]' a031c46782e6e6c662c2c87c76da9aa62ccabd8e ちなみにこの証明書の有効期限は2028年のよう。 $ openssl x509 -noout -dates -in certificate.crt notBefore=Oct 22 12:00:00 2013 GMT notAfter=Oct 22 12:00:00 2028 GMT マジックナンバーの計算方法がわかってスッキリしました。 これで突然証明書がなんらかの理由でローテーションされてハッシュ値が変わっても自分で再計算できて安心ですね。おしまい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

チュートリアル「AWS Amplifyを使用してシンプルなiOSアプリケーションを構築する」をやる際に隣で開いておくとヒントになるかもしれないページ

目的 このページでは、AWSから提供されている以下のチュートリアルを試行錯誤の末にクリアした時の情報を共有致します。 公式ページの情報が一部古く、今だと書いてある通りにはできない箇所もあったので、その辺りでどなたかの助けになればいいなと思って書きます。 前提 AWSのアカウントは既に持っていること Mac端末を持っていること Xcodeがインストール済みであること AnacondaもしくはDocker等で仮想環境を作成できること 私の環境 MacBook Pro (macOS Big Sur ver. 11.5.2) Xcode ver. 12.4 (12D4e) Anaconda Navigator 2.0.3 1/7 今回開発に使う仮想環境を作成する。 必須ではないと思いますが、クリーンな環境で開発した方が無駄なつまづきを防げます。 ライブラリやパッケージの依存関係ではできるだけ悩みたくないですからね。 ちなみに私は、Anacondaを使って「iOSAmplify」という名前の仮想環境を作成しました。Python 3.8です。 2/7 「はじめに」を実施する。 下記のリンクのページ内容を実施します。 公式ページの右側に、以下のような表示があると思います。 リンクになっている「手順をご確認ください。」をクリックしてください。GitHubのページが開きます。 Mac端末でTerminalを起動します。仮想環境切り替える必要がある方はお忘れなく。 Anacondaで仮想環境作った方は、以下のコマンドを実行すれば切り替えられます。 <>自体は入力する必要ありません。 conda activate <作った仮想環境名> Terminal上で、先ほど開いたGitHubのページに書かれているコマンドを brew install node まで実行します。 ここで、一つ確認してください。 Xcodeを起動して、Preferences -> Locations と開くと、 Command Line Tools という欄が空欄になっていないでしょうか? 空欄になっている方は、設定してください。私は、Xcode 12.4 (12D4e) を設定しました。 これが設定できていないと sudo gem install cocoapods がエラーになる場合があるようです。 この辺りで一度、端末の再起動をおすすめします。 さて、保留しておいた sudo gem install cocoapods を実行しましょう。 その後のバージョン確認も実施して、公式より新しいバージョンがインストールされていることを確認してください。 ちなみに、私の環境では以下のようになっていました。 # brew --verison Homebrew 3.3.4 # python3 --version Python 3.8.12 # aws --version aws-cli/2.4.0 Python/3.9.8 Darwin/20.6.0 source/x86_64 prompt/off # node --version v17.0.1 # pod --version 1.11.2 もし、sudo gem install cocoapods ができなかった場合は、brew install cocoapods でならインストールできるかも知れません。 gemは、Ruby言語用のパッケージ管理システムで、Homebrewは、MacOS用のパッケージ管理システムです。 3/7 「iOSアプリを作成する」を実施する。 下記のリンクのページ内容を実施します。 iOS アプリケーションを作成する Xcode起動 -> Create a new Xcode project を選択します。 公式では[iOS], [Application], [Single View App]を選択するように書かれていますが、 今のXcodeでは、[Single View App]ではなく、[App]と表示されていますので、[App]を選択して[Next]をクリックします。 その後は、公式に従います。プロジェクト名は自由につけてください。私は iOSAmplify としました。 言語とUIはそれぞれ Swift, SwiftUI としてください。設定できたら[Next]をクリックします。 Project Name: iOSAmplify Language: Swift User Interface: SwiftUI 公式に従い、ディレクトリを選択して、[Create]をクリックします。 そうすると、Xcodeでプロジェクトが開かれると思います。 メインビューを更新する ここは公式に従います。ContentView.swift を丸ごと置き換えて保存します。 構築およびテストする ここも公式に従います。 シミュレーターの選択の仕方が記載されていなかったので、補足します。 Xcode上で Product -> Destination -> 好きな端末を選択する。 という流れです。ちなみに私はiPhone SE (2nd generation) を選択しました。 4/7 「Amplifyを初期化する」を実施する。 下記のリンクのページ内容を実施します。 Amplify CLI をインストールする 公式に従います。 # Amplify CLI をインストール npm install -g @aws-amplify/cli # バージョン確認 amplify --version ちなみに、私の環境では、バージョンは7.4.4でした。 Amplify バックエンドを初期化する 公式に従って作業を進める前に、今回使うAWSのIAMユーザが用意できていない方は、作成しておきます。 既に作成済みのIAMユーザを使う場合は、Access key ID, Secret key ID をご用意ください。 厳密には、公式に書かれた手順を進めると、たとえIAMユーザが作られていなくてもTerminal上で作成させてくれます。ですので、それでも構いません。 以下、公式に従って作業を進めます。 補足です。 「ターミナルを開き、ディレクトリをプロジェクトディレクトリに変更します。」とありますが、要するに、Terminal上で cd をして3/7で作ったプロジェクトフォルダに移動してくださいという意味です。 移動した場所で ls -al コマンドを実行して、公式のような出力が確認できれば正しく移動できています。 次に、amplify init コマンドを実行すると、複数の質問が表示されてそれに回答する形で設定が進められていきます。質問が公式と異なる部分がありますので補足しておきます。 この後、実行するコマンドで選択ミスをした場合、ctrl + c で一度プロセスを停止して、再度 amplify init コマンドを実行すれば大丈夫ですよ。 ? Enter a name for the project # (自身のプロジェクト名)が薄く表示されていると思います。デフォルトのまま Enter キーを押してください。 # 次の質問が公式の2,3,4番目の質問をまとめたものになっています。 ? Initialize the project with the above configuration? # Environmentはdev, App typeはiosです。 # Default editorはお好きなものを選んでください。私は、Xcodeを選択しました。 # Default editorを変更したい場合は、一度 n で答えると一つずつ質問されて設定できます。 ? Select the authentication method you want to use # AWS profile ? Setup new user # 新規でIAMユーザを作りたい方は yes と答え、指示に従ってください。 # 私は既存のIAMユーザを使うため、No と答えました。 ? accessKeyId: # IAMユーザのAccess key ID を入力してください。 ? secretAccessKey: # IAMユーザのSecret access key を入力してください。 ? region: # お好きなリージョンを選択してください。 # 私は ap-northeast-1 を選択しました。東京リージョンです。 プロジェクトに Amplify ライブラリを追加する 公式にも書かれていますが、必ずXcodeを終了させてください。 pod init コマンドの実行まで公式に従ってください。 「c.Podfileを更新して次のポッドが含まれるようにしてから、プラトフォームを更新します。」を補足します。 この Podfile は、あなたのプロジェクトフォルダの中に作られています。このファイルを開いて、以下の作業をして保存します。 ・platform :ios, '9.0' の行をアンコメントし、13.0に変更 ・公式と同じ位置に「pod 'Amplify', '~> 1.0'」「pod 'Amplify/Tools', '~> 1.0'」を追加 公式に従って作業を進めてください。 xed . でXcodeが開かなかった方は、Xcode -> Preferences -> Locations -> Command Line Tools が設定されているか確認してください。 実行時に Amplify を初期化する 「a.Amplify 設定ファイルをプロジェクトに追加する」を補足します。 「awsconfiguration.json」「amplifyconfiguration.json」をXcodeプロジェクトにドラッグアンドドロップする際に、ポップアップ画面が出てきます。そこで「Create folder references」ではなく「Create Groups」が選択されていることを確認してください。 「b.実行時に Amplify クラスを読み込む」を補足します。 新しく作る「Backend.swift」は、「awsconfiguration.json」「amplifyconfiguration.json」と同じ階層に作ってください。 Xcode 12以降のお使いの方には、「AppDelegate.swift」は作られていません。代わりとなるのが「プロジェクト名App.swift」ファイルです。 このファイルを開き、ファイルの最後に以下のコードを追加してください。公式が追記してくださいと言っている Backend.initialize() を含んだ内容になっています。 class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { // initialize Amplify let _ = Backend.initialize() return true } } 上記のコードを追加した直後、「Backend クラスが見つからない」といった類の警告が表示される場合がありますが、一旦無視して次の「設定を検証する」を実施すると警告が消えると思います。 設定を検証する うまく行きましたでしょうか? 5/7 「認証を追加する」を実施する。 下記のリンクのページ内容を実施します。 認証サービスを作成する amplify add auth コマンドを実行すると、質問が開始されます。 以下に、質問と回答を記載しておきます。 Do you want to use the default authentication and security configuration? # Default configuration with Social Provider (Federation) How do you want users to be able to sign in? # Username Do you want to configure advanced settings? # No, I am done. What domain name prefix do you want to use? # 何もせずデフォルトのまま Enter キーを押してください。 Enter your redirect signin URI: # gettingstarted:// ? Do you want to add another redirect signin URI # N Enter your redirect signout URI: # gettingstarted:// ? Do you want to add another redirect signout URI # N Select the social providers you want to configure for your user pool: # 何も選ばずに Enter キーを押してください。 認証サービスをデプロイする 特に書くことがありません。 ここまで順調に進んでおりますでしょうか? Amplify 認証ライブラリをプロジェクトに追加する ここも特に書くことがありません。 Podfile はあなたのプロジェクトフォルダの中にありましたね。 Amplify 認証ライブラリを実行時に設定する 「ビルド時に"development team"を選んでください」というようなエラーが出た方は、Xcodeでプロジェクト名(下の画像の部分です)-> Signing & Capabilities とクリックして Team を設定してください。 認証を実行時にトリガーします 「a.サインインとサインアウトのコードを追加バックエンドクラスの任意の場所に次の 3 つのメソッドを追加します。」を補足します。 公式で追加するように書かれているコードを、 private init() { と書かれた行の上に追記してください。 インデントは private init() { に合わせてください。 「b.認証ハブのリスナーを追加」を補足します。 下記のコードブロックの中で「// ここに追記してください。」の箇所に追記してください。 private init() { // initialize amplify do { try Amplify.add(plugin: AWSCognitoAuthPlugin()) try Amplify.configure() print("Initialized Amplify"); } catch { print("Could not initialize Amplify: \(error)") } // ここに追記してください。 } } 「c.ユーザーインターフェイスコードを更新する」を補足します。 SignInButtonビューとSignOutButtonビューは「ContentView.swift」の最後に追記してください。 「e.ビルドとテスト」を補足します。 最初は、Sign up を選択すると思いますが、メールアドレスの入力の際に「@」が入力できない場合は、Shift + 2 で入力できます。 6/7 「APIとデータベースを追加する」を実施する。 下記のリンクのページ内容を実施します。 GraphQL API サービスおよびデータベースを作成する amplify add api コマンドを実行すると質問が開始されます。 下記に、質問と回答を記載しておきます。 ? Select from one of the below mentioned services: # GraphQL ? Here is the GraphQL API that we will create. Select a setting to edit or continue # Continue ? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description) ? Do you want to edit the schema now? # Y テキストエディタが開きますので、全体を公式に従って置き換えて保存してください。 クライアントサイドコードを生成する 特に補足はありません。 公式の動画がとても分かりやすいですね。 API サービスおよびデータベースをデプロイする amplify push コマンドを実行すると、質問が開始されます。 質問と回答を記載しておきます。公式とは異なる質問になっています。 ? Do you want to generate code for your newly created GraphQL API # Y ? Enter the file name pattern of graphql queries, mutations and subscriptions # デフォルトのまま Enter キーを押してください。 ? Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions # Yes ? Enter maximum statement depth [increase from default if your schema is deeply nested] # デフォルトのまま Enter キーを押してください。 ? Enter the file name for the generated code # デフォルトのまま Enter キーを押してください。 API クライアントライブラリを Xcode プロジェクトを追加する 特に補足はありません。 実行時に Amplify ライブラリを初期化する 特に補足はありません。 GraphQL データモデルとアプリモデル間のブリッジングを追加する 補足します。コードブロック中の「// ここに追記してください。」の位置に追記してください。 // the data class to represents Notes class Note : Identifiable, ObservableObject { var id : String var name : String var description : String? var imageName : String? @Published var image : Image? init(id: String, name: String, description: String? = nil, image: String? = nil ) { self.id = id self.name = name self.description = description self.imageName = image } // ここに追記してください。 } API CRUD メソッドをBackend クラスに追加する 「Backend.swift ファイルを開き、Backend クラスの末尾に次のスニペットを追加します。」を補足します。「クラスの末尾に」ということですので、「Backend.swift」の最後の } の一行上に追記してください。 メモを追加するための編集ボタンを追加する 「a.ContentView 構造体に、ユーザーインターフェイスにバインドされた状態変数を追加します。」を補足します。追加した後の構造体は以下のようになります。 struct ContentView: View { @ObservedObject private var userData: UserData = .shared @State var showCreateNote = false @State var name : String = "New Note" @State var description : String = "This is a new note" @State var image : String = "image" var body: some View { ZStack { if (userData.isSignedIn) { NavigationView { List { ForEach(userData.notes) { note in ListRow(note: note) } } .navigationBarTitle(Text("Notes")) .navigationBarItems(leading: SignOutButton()) } } else { SignInButton() } } } } 「b.ファイルの任意の場所に View 構造体を追加して、ユーザーが新しいメモを作成できるようにします。」を補足します。コードの末尾に追加してください。 「スワイプして削除」の操作を追加する 特に補足はありません。 構築およびテストする 特に補足はありません。 7/7 「画像保存機能を追加する」を実施する。 下記のリンクのページ内容を実施します。 ストレージサービスを作成する amplify add storage コマンドを実行すると質問が開始されます。 質問と回答を記載しておきます。 ? Select from one of the below mentioned services: # Content (Images, audio, video, etc.) ? Provide a friendly name for your resource that will be used to label this category in the project: # image ? Provide bucket name: # デフォルトのまま Enter キーを押します。 ? Who should have access: # Auth users only ? What kind of access do you want for Authenticated users? # create/update, read, delete を space キー で全て選択して Enter キーを押してください。 ? Do you want to add a Lambda Trigger for your S3 Bucket? # N ストレージサービスをデプロイする 特に補足はありません。 Amplify ストレージライブラリを Xcode プロジェクトに追加する 特に補足はありません。 Amplify ストレージプラグインを実行時に初期化する 特に補足はありません。 画像の CRUD メソッドを Backend クラスに追加する 「クラスの任意の場所に」ということですので、「Backend.swift」の最後の } の一行上に追記してください。 API からデータを取得するときに画像を読み込む 公式ページにも書かれていますが、8行目から17行目までを、ContentView.swiftの「convenience init」の中に追記します。 UI コードを追加して画像をキャプチャする 補足します。「CaptureImageView.swift」は、「ContentView.swift」と同じディレクトリに作成します。 メモが作成されたら画像を保存する 補足します。公式ページに書かれているコードは「ContentView.swift」に2箇所に分けて追記します。 // at the start of the Content View struct @State var image : UIImage? // replace the previous declaration of image @State var showCaptureImageView = false 上記のコードは、「ContentView.swift」の「AddNoteView」内の @State var image : String = "image" の行を置き換える形で追記します。 一方、 Section(header: Text("PICTURE")) { VStack { Button(action: { self.showCaptureImageView.toggle() }) { Text("Choose photo") }.sheet(isPresented: $showCaptureImageView) { CaptureImageView(isShown: self.$showCaptureImageView, image: self.$image) } if (image != nil ) { HStack { Spacer() Image(uiImage: image!) .resizable() .frame(width: 250, height: 200) .clipShape(Circle()) .overlay(Circle().stroke(Color.white, lineWidth: 4)) .shadow(radius: 10) Spacer() } } } } 上記のコードは、「ContentView.swift」で Section(header: Text("PICTURE")) { TextField("Name", text: $image) } の行を置き換える形で追記します。 次の「Create Note(メモの作成)セクションを変更して、画像とメモを保存します。」の部分は、「ContentView.swift」内の下記のコードを置き換える形で追記します。 Section { Button(action: { self.isPresented = false let noteData = NoteData(id : UUID().uuidString, name: self.$name.wrappedValue, description: self.$description.wrappedValue) let note = Note(from: noteData) // asynchronously store the note (and assume it will succeed) Backend.shared.createNote(note: note) // add the new note in our userdata, this will refresh UI self.userData.notes.append(note) }) { Text("Create this note") } } 構築およびテストする 問題なく「Run(実行)」できましたでしょうか? 複数のプロジェクト間でバックエンドを共有する 私は他のプロジェクトを作成しておりませんので、ここは省略します。 バックエンドを削除する 特に補足はありません。 終わりに 公式ページが更新されるまでの短い命の記事かもしれませんが、 どなたかのお役に立てたなら嬉しいです! ありがとうございました。 参考にした情報 [1] 公式ページ  参考にしたというか、このページをサポートする目的で本記事を執筆しました。 [2] [Xcode 12]アプリの起動について変更になった部分まとめ [3] UnityでiOSビルドするとエラーが出る
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pulumi (TypeScript) で作成した RDS (Aurora) を `pulumi destroy` 出来ないときに設定する項目

はじめに Pulumi で作成した RDS(Aurora) を pulumi destroy で削除しようとしたときにエラーが発生し削除出来ない場合の対応方法 pulumi-aws の裏側は terraform なので terraform と同じ対応方法です RDS Cluster FinalSnapshotIdentifier is required when a final snapshot is required 対応手順 RDS cluster に対して 不足しているオプションを付与 applyImmediately に true を設定 backupRetentionPeriod に 0 を設定 skipFinalSnapshot に true を設定 const testRdsCluster = new aws.rds.Cluster('test-cluster', { ... + applyImmediately: true, + backupRetentionPeriod: 0, + skipFinalSnapshot: true, ... }) 上記設定後、 pulumi destroy を実行し削除できていることを確認してください。 注意 上記設定は、削除ために付与した条件です。 リソースを削除する必要がない場合は適切な設定を行ってください。 Reference
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Public preview として公開中の AWS Native package について

Pulumi AWS Native package について Pulumi 社から AWS Native package が公開され約 4 カ月経過しました。 なぜ AWS Native package が作成されたか等この記事で紹介します。 AWS Native package が作成された理由 AWS Native package の公開と同時に Amazon Web Services から、AWS のリソースを操作するための新しいインターフェース AWS Cloud Control API が公開されています。 下記記事内に記載されている通り Pulumi と HashiCorp (Terraform) がリリースパートナーとして AWS Cloud Control API と連携しながら各することで、より早く提供されたサービスを Pulumi や HashiCorp (Terraform) から操作することができるようになります。 原文: 日本語訳: 今までの pulumi-aws は AWS Terraform Provider に依存する形で作成されており、いままでは AWS が新しい機能・サービスの追加を行っても Terraform の更新を待つ必要がありました。 (まだプレビューですが) AWS Cloud Control API で リソースを操作する AWS Native package は、特別な事情がない限り AWS Cloud Control API に変更が入った当日に提供されます。 Pulumi や Terraform が対応していないため 別のツールやサービス (CloudFormation) などで管理することもありましたがその可能性が格段と低くなりました。 AWS Native package はいつプレビュー版でなくなるか 現時点正式バージョンの公開は発表されていません。 AWS Native package は利用しても良いのか? For new projects, we recommend using AWS Native and AWS Classic side-by-side so you can get the speed and correctness benefits of AWS Native where possible. For existing projects, AWS Classic remains fully supported; at this time, we recommend waiting to migrate existing projects to AWS Native. pulumi-aws-native repository の README には以下のように記載されています。 新規で作成するプロジェクト AWS Native と AWS Classic を両方を利用できるように設定 AWS Native が利用できるリソースは AWS Native を利用して構築 既存のプロジェクト AWS Classic は引き続きサポートするため AWS Native に急いで移行することは推奨していない。 ただし、今後移行することを求められることは容易に想像できるため、新規でリソースを作成する場合はAWS Nativeを利用した方がよいと思います。 さいごに 今回は、Pulumi の AWS Native package について紹介しました。 AWS Cloud Control API の登場でさらに IaC が進んでいくのではととてもわくわくしています。 Reference
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS DVAに10時間で合格した話

はじめに 先日AWS DVAを受検し、無事に合格することができました! これでCLF + アソシエイト3冠となりました。 諸事情により試験2日前から勉強開始し、準備期間が計10時間でしたが、無事なんとかなったので投稿します。 普段からAWSでのWebアプリ開発を行っていることと、1ヶ月前にSOAを受検していたことが大きな助け舟となりました。 受検当時のスペック AWS使用歴:1年半 AWS保有資格:CLF、SAA、SOA (SOAは1ヶ月前に取得) 普段使うAWSサービス:VPC、EC2、ECS、RDS、S3、IAM、Cognito 触ったことのあるAWSサービス:Cloudwatch、Route53、ELB、System manager、DynamoDB、Elasticache、SES 勉強方法 AWS Web問題集を利用しました。 3ヶ月パックで4480円(税抜)かかりますが現状AWS資格対策における最強のコンテンツだと思います。 AWS DVAの問題は現在7問×44セット(計308問)掲載されており、これらを2周して試験に挑みました。 具体的な回し方としては、1セット7問を2周し、完了したら次のセットに進むイメージです。 各問題について詳細な解説がのっていますが、今回は時間がなかったので速度重視でほとんど目は通せませんでした。 1ヶ月前にSOAを受検していたのでこの恩恵が大きく、2割程度はSOAと重複した分野からの問題が出ていたと思われます。 あとは同サービスの合格記に目を通しておくと具体的な難易度が見えてくるので勉強開始前に見ておくとよいかと思われます。 私は普段からAWSでWebアプリ開発を行っており、さらに1ヶ月前にSOAの勉強をしていたのでこの勉強量ですみました。 AWSでの開発経験があまり無い方や初学者の方は別途Udemyのハンズオン動画を実践したり、書籍での勉強も検討したほうが良いかと思います! 試験本番での注意点 いつもAWS試験は日本語で受検するのですが、今回の試験では翻訳が致命的に間違っている問題がありました。 詳細は省略しますが、選択肢中の.warファイルという単語が日本語版では.wasファイルとなっていました。この誤記により頭を悩まされ時間をだいぶ使ってしまいました。 画面左上に「Englishで表示」みたいなボタンがありますので、問題文に違和感等を感じたらすぐに原文を確認してみることをおすすめします! 他にも本番では機械翻訳で訳された怪しい単語も多くありますので、勉強の際に聞き慣れない英単語があったら日本語の意味を調べておくといいかもしれません(エクスポネンシャルバックオフとか) 受検後の感想 今までの試験対策の時よりも圧倒的に勉強時間が足りない状況での受検となりました。 実際試験を受けている時は、「全くわからない問題」が5〜6問、「自信を持って解けない問題」が15問程度あり、かなり不安な状態で試験終了ボタンを押しました。 結果としては「927点」と、初めて900点を超えるぐらいの好成績だったため、これまでの実務や試験を得てAWSの実力が上がっているな、と大きな自信になりました。 また今後もAWS試験にチャレンジを続けてAWS12冠を目指したいと思います。 前回の試験から間をあけずに受検することで効率UPすると学んだので、短期間のうちに全制覇目指します!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む