20200715のAWSに関する記事は18件です。

クラウドの学習ができるサイトまとめ

久しぶりにQwikLABSでAWSのハンズオンでもしようかなと思ったらいろいろ揃ってたのでまとめました。

はじめに

現在は、おおよその主要なクラウドについては、無料のハンズオンテキストが公開されているようです。

QwikLABS

AWSをハンズオンするのに一番最初に思いつくのはQwikLABSです。ただ後述のGCPのほうが今はアピールされているようでした。というのも、2016年にGoogleに買収されてしまっていました。「ラボを開始」をクリックすると、AWSとGCPの一時的なアカウントが一時的に使えるのが嬉しいです。

参考

Microsoft Learn

Azureをちょっとハンズオンするにはマイクロソフト純正のハンズオンサイトがありました。
無料でサンドボックス的なAPIなどが利用できるようです。

参考

Trailhead

もともとSalesforceの学習用のサイトですが、AWSについてのテキストも少しありました。

参考

Udemy

動画が豊富なサイトです。ただし有料なのがたまにきずです。資格取得に向けたコンテンツが多いと感じました。

参考

おわりに

おおよその主要なクラウドについては、それなりのハンズオンができることがわかりました。やっぱりQwikLABSが良くて、AWSのコンソールが一時的に使えるので、なかなか趣味では使わない壮大なサービスや、一旦使うととてつもない金額を請求されるようなBigData系のサービスも無料でお試しできるのは良いと思いますので、おすすめです。

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSのAMIから構築したredashのmetadataに接続する(操作ログを閲覧する)

AWSのAMIから構築したredashのDatasouceにmetadata(postgresql)を追加してみました。
そのままだとdocker上で動いているpostgresqlに接続できない(ポートが未指定のため)から、docker-compose.ymlを変更して再起動

$ cd /opt/redash/

# Docker停止
$ sudo docker-compose stop server scheduler scheduled_worker adhoc_worker

# postgresqlのポートを指定
$ sudo vi docker-compose.yml
下記の2行を追加
==================
    ports:
      - "5432:5432"
==================

~~~~~
  postgres:
    image: postgres:9.6-alpine
    ports:
      - "5432:5432"
    env_file: /opt/redash/env
    volumes:
      - /opt/redash/postgres-data:/var/lib/postgresql/data
    restart: always
~~~~~

# 更新
$ sudo docker-compose run --rm server manage db upgrade

# Docker起動
$ sudo docker-compose up -d

これでdockerの外から接続可能になる

redashのDatasourceを作成する
Hostはlocalhostだとうまく接続できないため、サーバ自身のプライベートIPを設定
Passwordは/opt/redash/envに記載されているPOSTGRES_PASSWORD=xxxxxxを設定

image.png

これでredash上でmetadataの参照が可能になる
image.png

eventsテーブルに操作ログがある

ちなみにeventsのデータはapiでも取得できるhttps://redash.xxxx/api/events/page=1&page_size=100

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカル環境でLambda開発を進める際の私的ベストプラクティス

TL;DR

  • AWSマネジメントコンソール上ではLambdaのコードを編集しない
  • コードは全てバージョン管理が有効になったローカルIDE上で開発し、docker-lambdaを利用して迅速にテストできるようにする

ローカルでLambdaを開発するフローを考える

最も単純なLambdaの開発プロセス

  • AWSマネジメントコンソールを利用した最も単純なLambda開発のフローを整理すると、以下のフローになります
  1. ローカルでコードを書く
  2. AWSマネジメントコンソールにログイン
  3. 関数を新規作成、ローカルで編集したコードをブラウザ上のエディタにコピー&ペースト
  4. 関数の実行テスト
  5. 失敗した場合は原因を特定してローカルのコードを再編集(ここで直接ブラウザ上のエディタを編集したくなるが、ローカルのコードと同期が取れなくなってしまう)
  6. 修正したコードををブラウザ上のエディタにコピー&ペースト
  7. 成功するまで4〜6を繰り返す必要がある(!)。手作業でコピペするのは辛く、イケてない。
  • Lambda上で動作することが保証されているコードを一回だけ動かすようなケースであれば、上記までのやりかたでもそこまで不便な点は感じないかもしれません。
  • しかし実際の業務においてはコーディングとテスト実行を幾度も繰り返すことになるはずです。その場合、上記フローでの開発作業には以下のような問題があります
    • AWSマネジメントコンソール上でコードを修正してしまうと、バージョン管理されているローカルのコードと同期が取れなくなってしまう
    • Lambda Layersの開発・テストをする場合、圧縮してデプロイなど非効率的で手間のかかる作業が必要になる

dockerコンテナを利用した効率的なlambda開発

  • https://github.com/lambci/docker-lambda
  • リアルなLambda実行環境を模した、Lambdaのローカル開発環境のサンドボックスを提供してくれるOSSプロジェクト。詳細はリンク先を。
  • ローカル環境でコーディングしているLambda function、Layersを都度コンテナ内で実行して確認することができるので、効率的なLambdaの開発が可能になります。
  • リアルなLambda実行環境とは、Lambda関数が実行される際にマネージドでLambdaのコードがデプロイされるAMIのことです。
    • ファイル構造
    • 権限
    • 環境変数
    • ユーザ
    • プロセス
    • など

docker-lambdaを利用したLambdaの開発プロセス

Lambdaが正常に動作することのテストを、以下の2ステップまで圧縮できます。
1. ローカルでコードを書く
2. docker-lambda上で実行テスト(docker runするだけで実行完了!)
3. デプロイ

  • lambda関数のテストが、以下のコマンドだけで完了するのです。感動しますね。
docker run --rm \
  -v <code_dir>:/var/task:ro,delegated \
  [-v <layer_dir>:/opt:ro,delegated] \
  lambci/lambda:<runtime> \
  [<handler>] [<event>]

docker-lambdaを利用したLambda function実行の例

  • docker-lambdaのREADMEに記載にある通り、コンテナ内の/var/taskに関数のハンドラー、/optにLayersのコードをマウントして実行することで、コンテナ内で関数を実行されることができます。
  • これでいちいちLayerを圧縮する必要も、ローカルのコードを都度マネジメントコンソールのエディタ上にペーストする必要もなくなります。
  • 以下のサンプルでは、ハンドラーが/develop/lambda-local-proj/function、Layersが/develop/lambda-local-proj/layerに存在しており、それをdocker-lambdaの指定のディレクトリにマウントして起動しています。
  • ランタイムにPythonを利用しています。
# function、layerのディレクトリをマウントして実行
(venv) 6477:lambda-local-proj sentorei$ docker run --rm -v /Users/sentorei/develop/lambda-local-proj/function:/var/task:ro,delegated -v /Users/sentorei/develop/lambda-local-proj/layer:/opt:ro,delegated lambci/lambda:python3.8 lambda_function.lambda_handler
# ステータスコード200が返され、正常に実行できていることが確認できる
START RequestId: 9f1133d8-9bf4-1629-a2c2-832baf3cc23a Version: $LATEST
END RequestId: 9f1133d8-9bf4-1629-a2c2-832baf3cc23a
REPORT RequestId: 9f1133d8-9bf4-1629-a2c2-832baf3cc23a  Init Duration: 1835.48 ms       Duration: 588.13 ms     Billed Duration: 600 ms Memory Size: 1536 MB    Max Memory Used: 31 MB

{"statusCode":200,"body":"\"Hello from Lambda!\""}
  • 煩雑なテスト作業から脱出し、ぜひ皆さんも快適なLambda開発を始めましょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】TerraformでAWS管理ポリシーを使用する

状況

Terraformでデプロイ用のユーザを作成している。自分の参考書はjson形式で、許可するactionを細かく設定していたが、今回は簡単のためにAWS管理ポリシーを使用したい。すぐに解決したが備忘録として。

解決策

必要なAWS管理ポリシーをAWSコンソールで探す

# AWS管理ポリシー取得
data "aws_iam_policy" "AmazonEC2ContainerRegistryFullAccess" {
  arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess"
}

# IAMユーザー作成
resource "aws_iam_user" "user" {
  name = "test-user"
}

# IAMユーザーにAWS管理ポリシーをアタッチ
resource "aws_iam_user_policy_attachment" "test-attach" {
  user       = aws_iam_user.user.name
  policy_arn = data.aws_iam_policy.AmazonEC2ContainerRegistryFullAccess.arn
}

スクリーンショット 2020-07-15 21.16.26.png

Terraform公式リファレンス
resource "aws_iam_user_policy_attachment"
data "aws_iam_policy"

参考

Terraform: correct way to attach AWS managed policies to a role?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node_modulesをLambdaレイヤーにアップロードしてライブラリを使用する

経緯

  • 同じライブラリを使用しているLambda関数が増えた
  • コンソール画面からコードを編集できるようにしたい
  • 上記の理由から、layerに共通ライブラリを登録し、それを読み込ませることでLambda関数を軽量化する

環境

  • ローカルOSはWindows10(Macでもで順は同様)
  • LambdaはNode.jsを使用

node_modules準備

  • 『nodejs』という名前のフォルダを作成する

※名前がめちゃくちゃ大事です。layerはnodejs/node_modulesのフォルダ構成じゃないと読み込んでくれません(正確に言えばopt直下に生成されるのでopyt/nodejs/node_modulesとなりますが気にしなくて良いです)

① 新規で準備する場合

→作ったフォルダ直下にnode_modulesを作成

npm install 必要なライブラリ名

② すでにnode_modulesフォルダが有る場合

→node_modulesフォルダを先程作ったnodejsフォルダに移動する

  • nodejsフォルダをzip化する

Lambdaレイヤーの作成

  • AWS Lambdaのレイヤーから「レイヤーの作成」をクリック

SnapCrab_NoName_2020-7-15_13-7-27_No-00.png

  • 名前を入力
  • 先程zip化したフォルダをアップロード
  • ランタイムを選択(今回はNode.jsを選択)
  • 「作成」ボタンをクリック

SnapCrab_NoName_2020-7-15_13-10-2_No-00.png

これでレイヤーは完成です

Lambda関数への適用

  • 適応するLambda関数の「設定」→「デザイナー」→「Layers」を選択し、下の「レイヤー」→「レイヤーの追加」をクリック

SnapCrab_NoName_2020-7-15_13-16-44_No-00.png

  • 名前とバージョンを先程作ったレイヤーに設定(新規で作った場合はバージョン1です)

SnapCrab_NoName_2020-7-15_13-21-10_No-00.png

  • 各関数からレイヤーに上げたモジュールを削除する

これでパッケージとして動いていた時と同じ動作をするはずです

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Flask+Docker+Vue.js+AWS...でゲームWebAppを作ってみた。

イントロで曲当てクイズ『イントロドン!』
コロナ自粛中のリモート呑みで遊べるゲームアプリが欲しくて作ってみました!
お酒呑みながら、みんなでガヤガヤ、時にはひとりでじっくりと楽しんでもらえたら嬉しいです。

●こちらのリンクから遊べます♪
http://introdon.akinko.work/

●Githubにソースコード、ゲームルールを公開しました
https://github.com/akiraseto/introdon

スクリーンショット 2020-07-15 14.33.44.pngスクリーンショット 2020-07-15 14.30.52.pngスクリーンショット 2020-07-15 14.30.27.png
スクリーンショット 2020-07-15 14.30.04.pngintrodon.akinko.work_user_entrance(iPhone 6_7_8) (1).png

ゲーム内容

楽曲のイントロ部分を聴いて曲名を当てるゲーム。4択問題で全10問出題。

遊べるモードは2つ

ひとりでじっくりモード

時間制限無しでじっくり解答

みんなで早押しモード

最大5人同時参加の早押し形式
正解順に高得点をGet
勝敗は合計点によるランキング発表!

PCブラウザ・スマホについて

PCブラウザ、スマートフォンともに対応していますが、
スマホ版は、出題ごとに「音楽を再生する」ボタンをタップしないとイントロが流れない仕様です。
(PCブラウザ版は自動で曲が流れる)

モバイル版Chrome、Safariのブラウザポリシーによりメディア要素の自動再生は禁止。
ユーザーの意図的な操作によってメディアを再生する。

残念ながらスマホ版は、みんなモードだとかなり不利になってしまいます。

技術内容

楽曲情報を「itunes api」から取得して問題を作成。
https://itunes.apple.com/search

楽曲情報をDBから取得して問題作成。
該当する楽曲がDB内で少ない場合、itunes APIから楽曲情報を取得し、被った情報を削除した上でDBに記録。同時に問題も作成します。

全体の流れ

  1. ゲームを開始する
  2. DBから該当する楽曲情報を取得
  3. 楽曲がある場合は、選択肢4×10問の問題を作成。
  4. 問題をsessionに渡す
  5. sessionから引き出して出題
  6. 解答内容をDBに記録
  7. 全10問解答後にDBからログを取得し、ランキング、正誤内容を表示

楽曲情報が足りない場合、
3. itunes APIから該当する楽曲情報を取得
4. duplicateする情報を削除
5. API取得した楽曲情報をDBに記録
6. 問題を作成(以下、同じ)

技術構成

主な技術構成は以下となります。

  • Flask
  • Docker
  • Nginx
  • Gunicorn
  • Vue.js + Jinja2
  • MariaDB
  • AWS
  • テスト(pytest, CircleCI)

Flask

MTVフレームワーク

Model, Template, ViewのMTVフレームワークに沿って開発。

ディレクトリ構成

  .
  ├── introdon
  │  ├── __init__.py
  │  ├── config_flask.py
  │  ├── models
  │  │  ├── games.py
  │  │  ├── logs.py
  │  │  ├── songs.py
  │  │  └── users.py
  │  ├── scripts
  │  ├── static
  │  ├── templates
  │  │  ├── _render_field.html
  │  │  ├── admin
  │  │  ├── games
  │  │  ├── layout.html
  │  │  └── users
  │  └── views
  │    ├── __init__.py
  │    ├── config_introdon.py
  │    ├── form.py
  │    ├── games.py
  │    ├── songs.py
  │    ├── users.py
  │    └── views.py
  ├── manage.py
  └── server.py

models

一言で、データベース連携の機能
単純なCRUDだけでなく、ビジネスロジックもModelにコーディングしてデータの取り回しを担当させる。

classmethod

インスタンスを作成して使い回したり、アトリビュートを使用することが無い場合は、その場で使えるクラスメソッドが便利です。

introdon/models/users.py
 # Classの中でデコレータをつけて宣言

@classmethod
def fetch_user_records(cls, users_id_list: list) -> list:
introdon/views/games.py
# インスタンス作らずにその場で使える

users_record_list = User.fetch_user_records(users_id_list)

traceback.print_exc

try exceptで例外表示に使用。例外が発生した際に、エラー詳細を表示させて原因が追いやすくなります。

introdon/models/games.py
#DB書き込み時

this_game = Game(**records)
db.session.add(this_game)
try:
  db.session.commit()
except:
#例外が発生したら

  db.session.rollback()
#DBロールバックし

  traceback.print_exc()
#エラーをスタックトレースして出力

エラーが発生した際、
①スタックトレースを抽出して、
②書式も整えた上で出力してくれます。
エラー解決にはprint(),loggerよりもおすすめです。

templates

TemplateはMTVの中で一番分かり易い描画(render)機能です。
その反面、フロントエンドフレームワークと連携しだすと一番複雑化しやすい箇所でもあります。

Jinja2を通して共通パーツをまとめた方が全体のコードがスッキリします。

introdon/templates/users/index.html
{% from "_render_field.html" import render_field %}
{% extends "layout.html" %}
{% block body %}

# 内容

{% endblock %}

ナビバーや、ヘッダーなど共通のパーツはlayout.htmlから読み込み
フォームなど使い回すパーツはmacroにして_render_field.htmlから読み込んでます

views

viewにビジネスロジックまで書いてしまうと、あっという間に肥大化&複雑化してスパゲティコードになってしまいます。(T_T)
APIのI/Oインターフェイスぐらいの認識にして、なるべくスリム化するように常に意識します。
viewはあくまで

  • 値の入出力
  • 処理全体の制御

この2つの役割に徹した方が良いです。
Flaskは比較的自由が効くフレームワークなのでいくらでもviewに書けてしまいますので。。

static

画像などのstaticなファイル置き場となります。
今回は、正解不正解などのSE音源を格納しました。

app.run設定:config_flask.py

flaskアプリ起動のapp.run()の設定ファイルです。
ファイル場所:introdon/config_flask.py

設定変数をファイルにまとめて一括で読み込ませています。
今回は環境変数によって分岐させて、読み込む変数をモードごとに変えています。

SECRET_KEY

データベースと、セッション情報を暗号化するためのキー。
分かり辛い方がいいので、os.random(24)で毎回値を変えてます。

DEBUG

DEBUGモードをオンにすると

  • ファイル変更したらappが自動リロードされる。
  • ブラウザにエラー内容を出力する
  • ツール:DebugToolBarが使用可能になる

開発では Trueにして本番では必ずFalseにします。

SQLALCHEMY_DATABASE_URI

SQLAlchemyを通してDBを利用する際の接続先です。
SQLAlchemyとMySQL(MariaDB)にはpymysqlのスキーマが必要。

SQLALCHEMY_RECORD_QUERIES

デバッグ出力用にSQL内容を記録します。
DebugToolBarで SQLを確認したいなら設定が必要。

よく使う基本機能

Flaskで高頻度でお世話になる機能をまとめてみました。

ルーター

route

関数とURLをバインドするデコレーター
POST,GETの許可を制御する

errorhandler

サーバーエラー発生時に処理を制御します

introdon/views/views.py
@app.errorhandler(500)
def internal_server_error(error):
 flash('Internal server error')
 return redirect(url_for('index'))

サーバーエラー500が発生したら
①flashメッセージを作り、
②indexメソッドバインドしているURLにリダイレクトさせる。

レスポンス

render_template

テンプレートhtmlを描画します。
jinja2を使ってhtml側に変数を渡すことができます。

flash

Flashメッセージは処理が終わった事や、エラーが発生した場合にユーザーに知らせるメッセージです。

  1. render時にview側でflash()に入力されたメッセージがセットされます。
  2. template側でget_flashed_messages()でメッセージを受け取り描画します。
introdon/templates/layout.html
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
  #内容をhtmlで出力
{% endfor %}
{% endif %}
{% endwith %}

複数受け取りも可能。
layout.htmlですべてのtemplateページにメッセージを出力できるよう設定しています。

redirect

任意のページにredirectすることもできます。
特にurl_forと組み合わせて使うことが多いです。

url_for

メソッド名を引数に取ると、バインドしているURLの文字列を返します。

return redirect(url_for('start_multi'))
  1. start_multiメソッドがバインドしているURLに文字列変換
  2. 文字列変換されたURLにリダイレクト

また、templateのhtmlでよく使うのが、
ディレクトリ名のstaticを第1引数、ファイル名の指定で素材ファイルとリンクすることができます。

url_for('static', filename='right.mp3')

staticディレクトリの'right.mp3'のリンク文字列を作成

jsonify

名前のとおりjson形式にシリアライズします。

request

ユーザーのrequest内容が格納されています。

よく使うのが、ユーザーがformに入力した内容の取得です。

introdon/views/songs.py
# song登録画面
@app.route('/admin/song', methods=['POST'])
def add_song():
 term = request.form['term']

POSTで送信されたformのname=termにユーザーが入力した値を取得します。

flask.requestはユーザーの様々なリクエスト内容を取得できるので使いみちが幅広いです。

# POSTでリクエストされているなら
if request.method == 'POST':
pass

#GETでクエリが'game_id'の内容を取得
game_id = request.args.get('game_id')

などなどあります。

session

ユーザーごとに、リクエスト(ページ)をまたいで情報を利用することができるSessionを手軽に利用できるようになります。
利用するには、app.run設定のSECRET_KEYを設定する必要があります。

DBとの接続

ORMとしてSQLAlchemyを使用しました。
MariaDB(MySQL)との接続にはpymysqlをスキームにかませる必要があります。

flask-script

DB環境の初期化をコマンド実行できるようにしました。
以下のコマンドで必要なTableが作成されDBを初期化します。

 python manage.py init_db

アプリ開始時にコマンド実行します。

flask-marshmallow

オブジェクトをシリアライズ化してくれます。
クイズ作成時に、SQLAlchemyでオブジェクト化された楽曲情報をjsonフォーマットに格納するために使用しました。

ログイン系の処理

flaskの拡張プラグインを利用してコーディング。

flask-login

  • ユーザーのログイン
  • ログアウト
  • そのユーザーはログイン状態か
  • ログインしている場合のユーザー情報取得

password_hash

  • パスワードをハッシュ化:generate_password_hash
  • ハッシュ化パスワードを確認:check_password_hash

フォームの処理

flask-WTF

フォームを作る際の便利な機能が揃っています。

  • form内容のvalidate
  • form項目の設定
  • CSRF対策

管理画面をかんたん設置したい

Adminユーザーから簡単な管理画面を閲覧・操作したい。
でも、イチからコーディングするのはシンドい。。これもflaskのプラグイン使います。

flask_admin

自分で作るのに比べると圧倒的に楽に管理画面が作れます。

DB内TableのCRUD操作がWebの管理画面から可能になります。
細かくカスタマイズすることも可能です。

スクリーンショット 2020-07-15 14.35.14.pngスクリーンショット 2020-07-15 14.35.24.pngスクリーンショット 2020-07-15 14.35.30.png

開発に便利なツール

使うと開発作業が捗るツールで、利用をおすすめします。

flask-debugtoolbar

DEBUGモードで利用できるようになる開発援助ツールです。
Webアプリ上にツールバーとして表示されます。変数や履歴、ヘッダー内容、SQL処理内容をブラウザから確認できるようになります。

Dockerでシステム構築

Webサーバー、アプリサーバー、DBサーバーをそれぞれDockerコンテナでシステム構築しました。
正確には、テスト用のDBコンテナがひとつ追加で4つのコンテナ構成をdocker-composeで構築しています。

システムのモードとして3種類あります。
Flaskコンテナの環境変数FLASK_ENVを変更することで以下の3つのモードでBuildします。

  • PROD
    • 本番モード
    • Gunicorn使用
  • DEV
    • 開発環境モード
    • DEBUGオン
    • debugtoolbarが表示
    • エラー時、内容がWEBに表示
    • コード変更でwebアプリが自動リロード
  • TEST
    • Func,Unitテストが自動実行
    • テスト用DBコンテナを使用
    • テストCoverageを表示

Nginx

Webサーバーに使用しました。

Nginxはメモリ使用量が小さく、処理も早い。
リバースプロキシの機能、ロードバランサ、HTTPキャッシュを持ち合わせています。
また、Gunicornは公式ページでNginxの使用を推奨しています。

Gunicorn

WSGIに使用しました。

uWSGIも検討しましたが、
①Gunicornの方が扱いやすくネット上の使用事例が多く(海外も含めて)、
②公式サイトも解説が丁寧だったのでGunicornを採用しました。

docker-composeを通して、FALSK_ENV=PRODなら以下から起動するようにしています。

flask_env.sh
gunicorn server:app -c gunicorn_setting.py

MariaDB

MySQLと完全互換があり、MySQLより処理が早い、MariaDBを使用しました。
完全互換なのでSQLAlchemyや、スキーマなどMySQLの設定がそのまま使えます。

フロントエンド

Jinja2 + Vue.js + Bootstrapで作成しました。

MPA(Multi Page Application)としてJinja2を基本に、Vue.jsをCDNで読み込んで組み合わせて実装。
CSSはBootstrapを利用してレスポンシブデザインにしてPC、スマホの両ブラウザ対応にしています。

FontAwesomeを初めて使用したのですが、簡単なタグのみで手軽にアイコンを組み込めるのでおすすめです。
https://fontawesome.com/

AWS

1つのEC2インスタンスに全てのDockerコンテナを構築しました。

順当ならECSを使って、コンテナ毎にEC2インスタンスに分配して制御管理するのがベストプラクティスだと思います。
1つのEC2インスタンスに収めた理由として
①AWS無料枠内に収めたい
②クラスタリングが必要なほどのアクセス数ない
③趣味なので強い可用性も堅牢性も求めない
上記理由で、今回は見送りました。

ただ、ECSは思ったよりも導入しやすく便利そうなので今後の別アプリで検討してみたいです。
さらに、Kubenatesを導入しても面白そうです。

テスト

pytestで書いています。
python標準のunittestよりもシンプルにかけて読みやすく、プラグインも豊富なためpytestを採用しました。

CIには CircleCIを利用しました。
DockerのDBコンテナを使ったテストはせずに、mock利用のテストだけをCircleCI上で実行しています。

終わりに

楽しかった。。
Flaskは先人達の優れたプラグインが多くあり、組み合わせることでやれることが加速的に多くなっていく。。
その「やれることが増えていく感」が気持ちいいフレームワークです。皆様も機会があればぜひ触ってみてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

俺でもわかるAWS DMS CASTメモ(随時更新するとおもう)

俺です。

DMSを使う際は同機種または異機種間であってもソース/ターゲットでテーブルの型は一致させておく必要があります。
型が異なる場合は内部で多分CASTが行われていると思われる上に、Full LoadとCDCで挙動が異なります。

ただ型が違っても俺にはレプリしたい理由がそこにある。そういう俺のために無理やりねじ込んだらどうなるのか、DMSがサポートしてないことをAWSサポートに泣きつくことはできないのでメモを残しておきます。

こんな情報誰がほしいのかって?俺だよ!

移行方式

Aurora PostgreSQL 10.X to Aurora MySQL 5.7

レプリケーションタイプ ソーステーブルの型 ターゲットの型 変換結果 dead or alive 対処方法
Full Load boolean boolean(tinyint) false('f')もtrue('t')も0が挿入される ('A`) ドキュメント通りvarchar(1)に移行する
CDC boolean boolean(tinyint) false => 102, true => 116 ('A`)
Full Load int datetime 0になる ('A`)
Full Load int varchar ロード可 J( 'ー`)し
CDC int varchar 10進数の文字コードに変換される ('A`)

参考

DMS内部データ型
PostgreSQLの制限
PostgreSQLのソースデータ型

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【AWS】ACMでDNS検証が検証保留中から変化しない

状況

terraformを使用してAWSの環境構築中、ACMを利用してSSL証明書の取得を試みたところ、証明書の発行状況が検証保留中から変化せず、エラーを吐かれたので備忘録として

原因

CNAME名前解決できていなかった。

対処内容

ホストゾーンのNSレコードに登録されているネームサーバーをdigコマンドで問い合わせた内容に変更して解決

詳細

$ dig ドメイン名. +trace

digコマンドの詳細はこちら

得られた結果
スクリーンショット 2020-07-15 17.54.18.png

AWS Route53にて、値を上記内容に書き換え
スクリーンショット 2020-07-15 17.53.12.png

appendix

terraformでroute53処理とacm処理をまとめて実行すると、CNAMEが作成されないことがある。

実行順序を考えてあげる必要があるのかな。公式リファレンス

applyを2回実行すると解決する。1回で済ませる方法を模索中。

7/16追記
特にデメリットがないので、コンソールで予め証明書を発行しておくことにした。dataで引っ張る。

data "aws_acm_certificate" "domain" {
  domain = "domain.com"
  statuses = ["ISSUED"]
}

data.aws_acm_certificate.domain.arn

参考

AWS Certificate Managerにて、DNS 検証 が失敗

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

aws Amplify Cognito + ReactでwithAuthenticatorのスタイリングが反映されないときの対処法

aws Amplify Cognito + Reactで認証の実装をしていた時に遭遇したので共有しておきます。

本題

Cognito + Reactの情報を探していると、withAuthenticatorは以下のように、'aws-amplify-react'からインポートしている情報が殆どでした。

App.js
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
import { withAuthenticator } from 'aws-amplify-react';

Amplify.configure(awsconfig);

function App() {
  return (
    ...
  );
}

export default withAuthenticator(App); 

しかし、このままログイン画面を表示すると

b36cce9bd137bb8ed9cf68942219cd08.png
このようにスタイリングが反映されていないものになります。

対処法

こちらの公式ドキュメントにあるように'@aws-amplify/ui-react'からインポートしてあげましょう。

  yarn add @aws-amplify/ui-react
App.js
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';
// '@aws-amplify/ui-react'に変更
import { withAuthenticator } from "@aws-amplify/ui-react";

Amplify.configure(awsconfig);

function App() {
  return (
    ...
  );
}

export default withAuthenticator(App); 

スクリーンショット 2020-07-15 0.56.26.png
表示されるようになりました。

UI Componentsが新しいバージョンになったため@aws-amplify/ui-<framework>を使うようにしましょう。

リンク

Amplify Framework announces new, rearchitected UI Component and modular JavaScript libraries

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel バージョン確認コマンド

はじめに

これは初心者エンジニアがフルスタックエンジニアを目指すための備忘録である

Laravelバージョン確認コマンド

php artisan --version

または

php artisan -V

のどちらかです。

php artisan -v

// これが出たらOK!

Laravel Framework 5.8.38 

//←投稿者:現在のバージョン

以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSで設計する SAP NetWeaver 可用性と災害対策 その3

はじめに

前項(その2)ではサポートされる構成例や切り替えモデルについて紹介しました。もし、実現したい可用性モデルが見つからない場合はお気軽にこちらのスレッドにコメント頂くか、ベリタスまでお問合せください!

AWSにおけるSAPインスタンスの最適化

AWSでは開発、テスト、運用にわたって SAPインスタンスを作成および最適化できます。SAPインスタンスが停止した場合、InfoScale はクライアント接続を中断することなく、指定されたSAPシステムへインスタンスをフェールオーバーします。これによりAWS で障害やSAPインスタンスが停止した場合のTCOが削減されます。

次の図は本番インスタンスが失敗した場合に、InfoScale で障害が発生したインスタンスを検出し特定のインスタンスを開発サーバーに移動することを示しています。
image.png
図 12 - SAP のバックアップインスタンスへのフェールオーバー

技術的には開発インスタンスはスイッチオーバー可能ですが、開発インスタンスは本番インスタンスノードに移動できないようにする必要があります。

サンプル構成

このセクションではSAPアプリケーションでオンプレミスと AWS 間の災害復旧 (DR) を提供する InfoScaleの構成例を示します。各構成には、SAP アプリケーションインスタンス、AWS IP エージェント、および AWS Route53 エージェントのサービスグループとリソースが含まれます。

オンプレミスのインフォスケールの構成例
下記はオンプレミスの InfoScaleの構成ファイル(main.cf)のサンプルです。このユースケースでは、通常は全ての SAP 関連のコンポーネントがオンプレミスの2台のホストで稼働します。

include "OracleASMTypes.cf"
include "types.cf"
include "CFSTypes.cf"
include "CRSResource.cf"
include "CSSD.cf"
include "CVMTypes.cf"
include "Db2udbTypes.cf"
include "MultiPrivNIC.cf"
include "OracleTypes.cf"
include "PrivNIC.cf"
include "SAPNWTypes.cf"
include "SybaseTypes.cf"

cluster sapaws_onprem (
        UserNames = { admin = aPQiPKpMQlQQoYQkPN }
        ClusterAddress = "10.209.58.229"
        Administrators = { admin }
        HacliUserLevel = COMMANDROOT
        )
remotecluster sapawsclus (
        ClusterAddress = "10.239.2.75"
        ConnectTimeout = 3000
        SocketTimeout = 3000
        )
heartbeat Icmp (
        ClusterList = { sapawsclus }
        Arguments @sapawsclus = { "10.239.2.75" }
        )
system saprhe7 (
        )
system saprhel27 (
        )
group ClusterService (
        SystemList = { saprhel27 = 0, saprhe7 = 1 }
        AutoStartList = { saprhel27, saprhe7 }
        ClusterFailOverPolicy = Manual
        OnlineRetryLimit = 3
        OnlineRetryInterval = 120
)
Application wac (
        StartProgram = "/opt/VRTSvcs/bin/wacstart"
        StopProgram = "/opt/VRTSvcs/bin/wacstop"
        MonitorProcesses = { "/opt/VRTSvcs/bin/wac" }
        RestartLimit = 3
        )
IP webip (
        Device = ens192
        Address = "10.209.58.229"
        NetMask = "255.255.252.0"
        )
NIC csgnic (
        Device = ens192
        )
wac requires webip
webip requires csgnic

group Oracle_Database (
        SystemList = { saprhe7 = 1, saprhel27 = 0 }
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 }
        ClusterFailOverPolicy = Manual
        )
        IP  IP  (
                Critical = 0
                Device = ens192
                Address = "10.209.58.226"
                NetMask = "255.255.252.0"
                )
        Mount SAP_DB_Mount (
                Critical = 0
                MountPoint = "/oracle"
                BlockDevice = "/dev/vx/dsk/sapdbdg/sapdbdg_vol"
                FSType = vxfs
                MountOpt = rw FsckOpt = "-y"
                )
        NIC NIC (
                Enabled = 0 Device = ens192
                )
        Netlsnr Listener (
                Critical = 0
                Owner = oraqas
                Home = "/oracle/QAS/121"
                )
        Oracle Oracle_resource (
                Critical = 0
                Sid = QAS
                Owner = oraqas
                Home = "/oracle/QAS/121"
                )

        requires group RVG_Owner_SG online local firm
        IP requires NIC
        Listener requires Oracle_resource
        Oracle_resource requires IP
        Oracle_resource requires SAP_DB_Mount

group RVG_Owner_SG (
        SystemList = { saprhel27 = 0, saprhe7 = 1 }
                )
        IP LogOwner_IP_1 (
                Device = ens192
                Address = "10.209.58.225"
                NetMask = "255.255.252.0"
                )
        NIC NIC_1 (
                Device = ens192
                )
        requires group RVGgroup online local firm
        LogOwner_IP_1 requires NIC_1
group RVGgroup (
        SystemList = { saprhel27 = 0, saprhe7 = 1 }
        Parallel = 1
        AutoStartList = { saprhel27, saprhe7 }
        )
        CVMVolDg DB_DiskGroup (
                Critical = 0
                CVMDiskGroup = sapdbdg
                CVMVolume = { sapdbdg_vol, sapsrl_vol }
                CVMActivation = sw
                )

        RVGShared data_rvg (
                Critical = 0
                RVG = sapdbrvg
                DiskGroup = sapdbdg
                )
requires group cvm online local firm
data_rvg requires DB_DiskGroup

group cvm (
        SystemList = { saprhel27 = 0, saprhe7 = 1 }
        AutoFailOver = 0
        Parallel = 1
        AutoStartList = { saprhel27, saprhe7 }
        )
        CFSMount sapmnt_res (
                Critical = 0
                MountPoint = "/sapmnt"
                BlockDevice = "/dev/vx/dsk/sapmntdg/sapmntdg_vol"
                MountOpt = rw
                )
        CFSfsckd vxfsckd (
                )
        CVMCluster cvm_clus (
                CVMClustName = sapaws_onprem
                CVMNodeId = { saprhel27 = 0, saprhe7 = 1 }
                CVMTransport = gab
                CVMTimeout = 200
                )

        CVMVolDg sapmntdg_res (
                CVMDiskGroup = sapmntdg
                CVMVolume = { sapmntdg_vol }
                CVMActivation = sw
                )
        CVMVxconfigd cvm_vxconfigd (
                Critical = 0
                CVMVxconfigdArgs = { syslog }
                )
        ProcessOnOnly vxattachd (
                Critical = 0
                PathName = "/bin/sh"
                Arguments = "- /usr/lib/vxvm/bin/vxattachd root"
                RestartLimit = 3
                )
        cvm_clus requires cvm_vxconfigd
        sapmnt_res requires sapmntdg_res
        sapmnt_res requires vxfsckd
        vxfsckd requires cvm_clus
group sap_aas_sg (
        SystemList = { saprhel27 = 0, saprhe7 = 1 }
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 }
        ClusterFailOverPolicy = Manual
        )
        IP sap_aas_ip (
                Device = ens192
                Address = "10.209.58.228"
                NetMask = "255.255.252.0"
                )
        NIC sap_aas_nic (
                Device = ens192
                )
        SAPNW sap_aas_res (
                Critical = 0
                ResLogLevel = TRACE
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_D02_qasdi"
                SAPAdmin = qasadm
                ProcMon = dw
                EnvFile = "/home/qasadm/.login"
                )
        SAPNW sap_aas_sapstartsrv (
                ResLogLevel = TRACE
                InstType = SAPSTARTSRV
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_D02_qasdi"
                SAPAdmin = qasadm
                ProcMon = sapstartsrv
                EnvFile = "/home/qasadm/.login"
                )

        requires group Oracle_Database online global soft
        sap_aas_ip requires sap_aas_nic
        sap_aas_res requires sap_aas_sapstartsrv
        sap_aas_sapstartsrv requires sap_aas_ip

group sap_ascs_sg (
        SystemList = { saprhe7 = 0, saprhel27 = 1 }
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 }
        ClusterFailOverPolicy = Manual
        PreOnline = 1
        )
        IP sap_ascs_ip (
                Critical = 0
                Device = ens192
                Address = "10.209.58.222"
                NetMask = "255.255.252.0"
                )
        NIC sap_ascs_nic (
                Critical = 0
                Device = ens192
                )
        SAPNW sap_ascs_res (
                ResLogLevel = TRACE
                InstType = ENQUEUE
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_ASCS00_qasascs"
                SAPAdmin = qasadm
                ProcMon = "en ms"
                EnvFile = "/home/qasadm/.login"
                )
        SAPNW sap_ascs_sapstartsrv ( Critical = 0
                ResLogLevel = TRACE
                InstType = SAPSTARTSRV
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_ASCS00_qasascs"
                SAPAdmin = qasadm
                ProcMon = sapstartsrv
                EnvFile = "/home/qasadm/.login" ToleranceLimit = 5
                RestartLimit = 5
                )

        requires group cvm online local firm
        sap_ascs_ip requires sap_ascs_nic
        sap_ascs_res requires sap_ascs_sapstartsrv
        sap_ascs_sapstartsrv requires sap_ascs_ip

group sap_ers_sg (
        SystemList = { saprhe7 = 0, saprhel27 = 1 }
        AutoStart = 0
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 }
        Authority = 1
        ClusterFailOverPolicy = Manual
        PreOnline = 1
        AutoRestart = 0
        )
        IP sap_ers_ip (
                Device = ens192
                Address = "10.209.58.224"
                NetMask = "255.255.252.0"
                )
        NIC sap_ers_nic (
                Device = ens192
                )
        SAPNW sap_ers_res (
                ResLogLevel = TRACE
                InstType = ENQREP
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_ERS10_qasers"
                SAPAdmin = qasadm
                EnqSrvResName = sap_ascs_res
                ProcMon = er
                EnvFile = "/home/qasadm/.login"
                )
        SAPNW sap_ers_sapstartsrv (
                InstType = SAPSTARTSRV
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_ERS10_qasers"
                SAPAdmin = qasadm
                ProcMon = sapstartsrv
                EnvFile = "/home/qasadm/.login"
                )

        requires group cvm online local firm
        sap_ers_ip requires sap_ers_nic
        sap_ers_res requires sap_ers_sapstartsrv
        sap_ers_sapstartsrv requires sap_ers_ip

group sap_pas_sg (
        SystemList = { saprhe7 = 0, saprhel27 = 1 }
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 }
        ClusterFailOverPolicy = Manual
        )
        IP sap_pas_ip (
                Critical = 0
                Device = ens192
                Address = "10.209.58.230"
                NetMask = "255.255.252.0"
                )
        NIC sap_pas_nic (
                Critical = 0
                Device = ens192
                )
        SAPNW sap_pas_res (
                ResLogLevel = TRACE
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_DVEBMGS01_qaspas"
                SAPAdmin = qasadm
                ProcMon = dw
                EnvFile = "/home/qasadm/.login"
                )
        SAPNW sap_pas_sapstartsrv (
                Critical = 0
                ResLogLevel = TRACE
                InstType = SAPSTARTSRV
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_DVEBMGS01_qaspas"
                SAPAdmin = qasadm
                ProcMon = sapstartsrv
                EnvFile = "/home/qasadm/.login"
                )

        requires group Oracle_Database online global soft
        sap_pas_ip requires sap_pas_nic
        sap_pas_res requires sap_pas_sapstartsrv
        sap_pas_sapstartsrv requires sap_pas_ip

AWS におけるInfoScaleの構成のサンプル
下記は、AWS側の InfoScaleの構成ファイル(main.cf)のサンプルです。このユースケースでは、AWS側にSAP関連コンポーネントがフェイルオーバーした場合、6台のホストで分散して処理を行います。このようにオンプレとAWS側でクラスターを構成するホストの数が異なっても構いません。

オンプレとAWSでは、ホストの処理能力が異なることが一般的なので、このようなInfoScaleの柔軟性がお客様にとって大きなメリットになります。

include "types.cf"
include "CFSTypes.cf"
include "CRSResource.cf"
include "CSSD.cf"
include "CVMTypes.cf"
include "Db2udbTypes.cf"
include "MultiPrivNIC.cf"
include "OracleTypes.cf"
include "PrivNIC.cf"
include "SAPNWTypes.cf"
include "SybaseTypes.cf"

cluster sapawsclus (
        UserNames = { admin = GPQiPKpMQlQQoYQkPN }
        ClusterAddress = "10.239.2.75"
        Administrators = { admin }
        )
remotecluster sapaws_onprem (
ClusterAddress = "10.209.58.229"
ConnectTimeout = 3000
SocketTimeout = 3000
)
heartbeat Icmp (
        ClusterList = { sapaws_onprem }
        Arguments @sapaws_onprem = { "10.209.58.229" }
        )
system ip-10-239-2-104 (
)
system ip-10-239-2-201 (
)
system ip-10-239-3-193 (
)
system ip-10-239-3-203 (
)
system ip-10-239-3-238 (
)
system ip-10-239-3-251 (
)
group ClusterService (
        SystemList = {
                ip-10-239-3-251 = 0,
                ip-10-239-3-238 = 1,
                ip-10-239-3-203 = 2,
                ip-10-239-2-104 = 3,
                ip-10-239-2-201 = 4,
                ip-10-239-3-193 = 5
                }
        AutoStartList = {
                ip-10-239-3-251,
                ip-10-239-3-238,
                ip-10-239-2-104,
                ip-10-239-3-203,
                ip-10-239-2-201,
                ip-10-239-3-193
                }
        OnlineRetryLimit = 3
        OnlineRetryInterval = 120
        )
        AWSIP awsgcoip (
        PrivateIP = "10.239.2.75"
        Device = eth0
        AWSBinDir = "/usr/local/bin"
        )
        Application wac (
                StartProgram = "/opt/VRTSvcs/bin/wacstart"
                StopProgram = "/opt/VRTSvcs/bin/wacstop"
                MonitorProcesses = { "/opt/VRTSvcs/bin/wac" }
                RestartLimit = 3
                )
        IP webip (
                Device = eth0
                Address = "10.239.2.75"
                NetMask = "255.255.254.0"
                )
        NIC csgnic (
                Device = eth0
                )

        awsgcoip requires webip
        wac requires webip
        webip requires csgnic

group Oracle_Database (
        SystemList = { ip-10-239-3-251 = 0, ip-10-239-3-238 = 1 }
        AutoFailOver = 0
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 }
        Authority = 1
        AutoStartList = { ip-10-239-3-238, ip-10-239-3-251 }
        ClusterFailOverPolicy = Manual
        AutoRestart = 0
        )
        AWSIP DB_AWS_IP (
                PrivateIP = "10.239.2.72"
                Device = eth0
                AWSBinDir = "/usr/local/bin"
                )
        IP DB_IP (
                Critical = 0
                Device = eth0
                Address = "10.239.2.72"
                NetMask = "255.255.254.0"
                )
        Mount SAP_DB_Mount (
                Critical = 0
                MountPoint = "/oracle"
                BlockDevice = "/dev/vx/dsk/sapdbdg/sapdbdg_vol"
                FSType = vxfs
                MountOpt = rw
                FsckOpt = "-y"
                )
        NIC NIC (
                Device = eth0
                )
        Netlsnr Listener (
                Critical = 0
                Owner = qasadm
                Home = "/oracle/QAS/121"
                )
        Oracle Oracle_resource (
                Critical = 0
                Sid = QAS
                Owner = oraqas
                Home = "/oracle/QAS/121"
                )

        requires group RVG_Owner_SG online local firm
        DB_AWS_IP requires NIC
        DB_IP requires DB_AWS_IP
        Listener requires Oracle_resource
        Oracle_resource requires DB_IP
        Oracle_resource requires SAP_DB_Mount

group RVG_Owner_SG (
        SystemList = {
                ip-10-239-3-251 = 0,
                ip-10-239-3-238 = 1 }
        AutoStartList = {
                ip-10-239-3-251,
                ip-10-239-3-238
                }
        )
        AWSIP LogOwner_AWS_IP_1 (
                PrivateIP = "10.239.2.76"
                Device = eth0
                AWSBinDir = "/usr/local/bin"
                )
        IP LogOwner_IP_1 (
                Device = eth0
                Address = "10.239.2.76"
                NetMask = "255.255.254.0"
                )
        NIC NIC_1 (
                Device = eth0
                )

        requires group RVGgroup online local firm
        LogOwner_AWS_IP_1 requires NIC_1
        LogOwner_IP_1 requires LogOwner_AWS_IP_1

group RVGgroup (
        SystemList = { ip-10-239-3-251 = 0, ip-10-239-3-238 = 1 }
        Parallel = 1
        AutoStartList = { ip-10-239-3-251, ip-10-239-3-238 }
        )
        CVMVolDg DB_DiskGroup (
                Critical = 0
                CVMDiskGroup = sapdbdg
                CVMActivation = sw
                )

        requires group cvm online local hard

group cvm (
        SystemList = {
                ip-10-239-3-251 = 0,
                ip-10-239-3-238 = 1,
                ip-10-239-2-104 = 2,
                ip-10-239-3-203 = 3,
                ip-10-239-2-201 = 4,
                ip-10-239-3-193 = 5
                }
        AutoFailOver = 0
        Parallel = 1
        AutoStartList = {
                ip-10-239-3-251,
                ip-10-239-3-238,
                ip-10-239-2-104,
                ip-10-239-3-203,
                ip-10-239-2-201,
                ip-10-239-3-193
                }
        )
        CFSMount sapmnt_res (
                Critical = 0
                MountPoint = "/sapmnt"
                BlockDevice = "/dev/vx/dsk/sapmntdg/sapmntdg_vol"
                MountOpt = rw
                RestartLimit = 5
                ToleranceLimit = 5
                )
                CFSfsckd vxfsckd (
                )

CVMCluster cvm_clus (
        CVMClustName = sapawsclus
        CVMNodeId = {
                ip-10-239-2-104 = 2,
                ip-10-239-3-203 = 3,
                ip-10-239-3-238 = 1,
                ip-10-239-3-251 = 0,
                ip-10-239-2-201 = 4,
                ip-10-239-3-193 = 5
                }
        CVMTransport = gab
        CVMTimeout = 200
        )
        CVMVolDg sapmnt_dg (
                CVMDiskGroup = sapmntdg
                CVMVolume = { sapmntdg_vol }
                CVMActivation = sw
                )
CVMVxconfigd cvm_vxconfigd (
                Critical = 0
                CVMVxconfigdArgs = { syslog }
                )
                ProcessOnOnly vxattachd (
                        Critical = 0
                        PathName = "/bin/sh"
                        Arguments = "- /usr/lib/vxvm/bin/vxattachd root"
                        RestartLimit = 3
                        )
        cvm_clus requires cvm_vxconfigd
        sapmnt_dg requires cvm_clus
        sapmnt_res requires sapmnt_dg
        sapmnt_res requires vxfsckd
        vxfsckd requires cvm_clus

group sap_aas_sg (
        SystemList = { ip-10-239-3-193 = 0, ip-10-239-3-238 = 1 }
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 }
        Authority = 1
        ClusterFailOverPolicy = Manual
        )
        AWSIP AAS_AWS_IP (
                Critical = 0
                PrivateIP = "10.239.2.74"
                Device = eth0
                AWSBinDir = "/usr/local/bin"
                )
        IP sap_aas_ip (
                Critical = 0 Device = eth0
                Address = "10.239.2.74"
                NetMask = "255.255.254.0"
                )
        NIC sap_aas_nic (
        Device = eth0
                )

        SAPNW sap_aas_res (
                Critical = 0
                ResLogLevel = TRACE
                EnvFile = "/home/qasadm/.login"
                SAPAdmin = qasadm
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_D02_qasdi"
                ProcMon = dw
                )
        SAPNW sap_aas_sapstartsrv (
                Critical = 0
                ResLogLevel = TRACE
                EnvFile = "/home/qasadm/.login"
                SAPAdmin = qasadm
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_D02_qasdi"
                InstType = SAPSTARTSRV
                ProcMon = sapstartsrv
                RestartLimit = 5
                )

        requires group Oracle_Database online global soft
        AAS_AWS_IP requires sap_aas_nic
        sap_aas_ip requires AAS_AWS_IP
        sap_aas_res requires sap_aas_sapstartsrv
        sap_aas_sapstartsrv requires sap_aas_ip

group sap_ascs_sg (
        SystemList = { ip-10-239-2-201 = 0, ip-10-239-3-203 = 1 }
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 }
        Authority = 1
        ClusterFailOverPolicy = Manual
        PreOnline = 1
        )
        AWSIP ASCS_AWS_IP (
                Critical = 0
                PrivateIP = "10.239.2.70"
                Device = eth0
                AWSBinDir = "/usr/local/bin"
                )
        IP sap_ascs_ip (
                Critical = 0
                Device = eth0
                Address = "10.239.2.70"
                NetMask = "255.255.254.0"
                )
        NIC sap_ascs_nic (
                Critical = 0
                Device = eth0
                )
        SAPNW sap_ascs_res (
                ResLogLevel = TRACE
                EnvFile = "/home/qasadm/.login"
                SAPAdmin = qasadm
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_ASCS00_qasascs"
                InstType = ENQUEUE
                ProcMon = "en ms"
                )
        SAPNW sap_ascs_sapstartsrv (
                Critical = 0
                ResLogLevel = TRACE
                EnvFile = "/home/qasadm/.login"
                SAPAdmin = qasadm
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_ASCS00_qasascs"
                InstType = SAPSTARTSRV
                ProcMon = sapstartsrv
                ToleranceLimit = 5
                RestartLimit = 5
                )

        requires group cvm online local firm
        sap_ascs_ip requires ASCS_AWS_IP
        sap_ascs_ip requires sap_ascs_nic
        sap_ascs_res requires sap_ascs_sapstartsrv
        sap_ascs_sapstartsrv requires sap_ascs_ip

group sap_ers_sg (
        SystemList = { ip-10-239-3-203 = 0, ip-10-239-2-201 = 1 }
        AutoStart = 0
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 }ClusterFailOverPolicy = Manual
        PreOnline = 1
        AutoRestart = 0
        )
        AWSIP ERS_AWS_IP (
                Critical = 0
                PrivateIP = "10.239.2.71"
                Device = eth0
                AWSBinDir = "/usr/local/bin"
                )
        IP sap_ers_ip (
                Critical = 0
                Device = eth0
                Address = "10.239.2.71"
                NetMask = "255.255.254.0"
                )
        NIC sap_ers_nic (
                Device = eth0
                )
        SAPNW sap_ers_res (
                ResLogLevel = TRACE
                EnvFile = "/home/qasadm/.login"
                SAPAdmin = qasadm
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_ERS10_qasers"
                InstType = ENQREP
                ProcMon = er
                EnqSrvResName = sap_ascs_res
                )
        SAPNW sap_ers_sapstartsrv (
                EnvFile = "/home/qasadm/.login"
                SAPAdmin = qasadm
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_ERS10_qasers"
                InstType = SAPSTARTSRV
                ProcMon = sapstartsrv
                )

        requires group cvm online local firm
        sap_ers_ip requires ERS_AWS_IP
        sap_ers_ip requires sap_ers_nic
        sap_ers_res requires sap_ers_sapstartsrv
        sap_ers_sapstartsrv requires sap_ers_ip

group sap_pas_sg (
        SystemList = { ip-10-239-2-104 = 0, ip-10-239-3-251 = 1 }
        ClusterList = { sapawsclus = 1, sapaws_onprem = 0 } Authority = 1
        ClusterFailOverPolicy = Manual
        )
        AWSIP PAS_AWS_IP (
                Critical = 0
                PrivateIP = "10.239.2.73"
                Device = eth0
                AWSBinDir = "/usr/local/bin"
                )
        IP sap_pas_ip (
                Critical = 0
                Device = eth0
                Address = "10.239.2.73"
                NetMask = "255.255.254.0"
                )
        NIC sap_pas_nic (
                Critical = 0
                Device = eth0
                )
        SAPNW sap_pas_res (
                Critical = 0
                ResLogLevel = TRACE
                EnvFile = "/home/qasadm/.login"
                SAPAdmin = qasadm
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_DVEBMGS01_qaspas"
                ProcMon = dw
                )
        SAPNW sap_pas_sapstartsrv (
                Critical = 0
                ResLogLevel = TRACE
                EnvFile = "/home/qasadm/.login"
                SAPAdmin = qasadm
                InstProfile = "/usr/sap/QAS/SYS/profile/QAS_DVEBMGS01_qaspas"
                InstType = SAPSTARTSRV
                ProcMon = sapstartsrv
                ToleranceLimit = 3
                RestartLimit = 5
                )

        requires group Oracle_Database online global soft
        sap_pas_ip requires PAS_AWS_IP
        sap_pas_ip requires sap_pas_nic
        sap_pas_res requires sap_pas_sapstartsrv
        sap_pas_sapstartsrv requires sap_pas_ip

※ main.cfの記載につきましては”hacf -verify”コマンドを用いて構文のチェックを行うことをお薦めします。

おわりに

いかがでしたでしょうか。ほとんどコードスニペットでしたね。。ファイルで欲しい方はこちらからダウンロードできますのでぜひ入手いただけますと幸いです。

商談のご相談はこちら

本稿からのお問合せをご記入の際には「お問合せ内容」に#GWCのタグを必ずご記入ください。ご記入いただきました内容はベリタスのプライバシーポリシーに従って管理されます。

その他のリンク

【まとめ記事】ベリタステクノロジーズ 全記事へのリンク集もよろしくお願いいたします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AR顔認証打刻アプリを作る

はじめに

皆さん、仕事の日は毎日打刻していると思いますが。
正直めんどくさいですよね。
それを解決するには、楽にできる方法を考えるのが普通だと思っていましたが。
この記事を読んで、気分を上げる打刻という解決策があるということを教わり、便乗してVRがあるならARもありだろうということで、作ろうと思いました。

作ったもの

動画はこちらです。

追記
ビルドすることができました。

https://twitter.com/akomekagome/status/1283239374348406785

あれARは?と思ったと思いますが
Macが不調でスマホにビルドすることができませんでした...
後日、ビルド出来たら上げ直したいと思います。

コードをあげておきます。(github)

https://github.com/akomekagome/ARAttendanceManagementScript

使用ライブラリ

FreeeForUnity
Unirx
Unitask
Zenject
Dotween
AWS for .net
OVRLipsync

システムの流れ

1.カメラの画像からOpenCV + Unityで顔を検出
2.先ほどの画像をAWS Rekognitionで顔を照合
3.取得した顔情報から、Freeeの従業員データーと紐づけし、打刻を行う

Freeeへの接続

接続は先ほどの記事を書いたtoofusanさんのコードを使わせていただきました。

https://github.com/toofusan/FreeeForUnity

使用するには、client_id, client_secret,code, access token, authorizationCode, employeeIdが必要になります。
ただ、employeeIdは初期のセットアップで取得できないので、個別で取得する必要があるのですが、一番簡単な方法として
Free人事労務フリー/勤怠/従業員名
でアクセスしたurlが

https://p.secure.freee.co.jp/employees#/employeeId/年/月/

となっているので、ここから取得できます。

カメラの画像をOpenCV + Unityで顔を検出

AWS Rekognitionも無料ではないので、できるだけ費用を抑えるためカメラに顔が映ったときだけ顔照合しています。
OpenCVでデフォルトで入っているカスケード型分類器を使って、顔認識を行うのですが、Unityに持っていかないと使えないので、StreamingAssetsに持っていく必要があります。

OpenCvGateway.cs
public class OpenCvGateway
{
    public bool DetectFaceInImage(Mat mat, string cascadePath)
    {
        var cascade = new CascadeClassifier();
    // ここでカスケード型分類器のパスを指定
        cascade.Load(cascadePath);

        var faces = cascade.DetectMultiScale(mat, 1.1, 3, 0, new Size(20, 20));

        Debug.Log(faces.Length);
        return faces.Length > 0;
    }
}

AWS Rekognitionで顔認証 

通常UnityからAWSを使う場合は、AWS for Untiyから使用するのが普通だと思いますが、今回使うAWS Rekognitionが非対応だったため、やむを得ずaws for .netのnugetをunityに持ってきました。
ただ今思えば、AWS Lambaは使えるので、そちらの方でAWS Rekognitionは使ったほうがいい気がします。(未検証)
方法としては、nugetの拡張子をzipに変更し、中身から自分が欲しい.netのバージョンのdllを探し、UnityのPluginフォルダーに突っ込めば、使用できます。nugetは以下から取得できます。

https://www.nuget.org/packages/AWSSDK.Rekognition/3.5.0-beta

使い方は以下が参考になります。

https://docs.aws.amazon.com/ja_jp/sdk-for-net/v3/developer-guide/quick-start.html

AmazonRekognitionへの接続は以下のような形です。

AmazonRekognitionGateway.cs
    public class AmazonRekognitionGateway
    {
        AmazonRekognitionClient rekoClient;

        public AmazonRekognitionGateway(CognitoAWSCredentials credentials)
        {
            rekoClient = new AmazonRekognitionClient(credentials);
        }

        public AmazonRekognitionGateway(string keyId , string secretKey)
        {
            rekoClient = new AmazonRekognitionClient(keyId, secretKey, AwsSettings.Region);
        }

        public AmazonRekognitionGateway(AWSAuthData awsAuthData)
        {
            rekoClient = new AmazonRekognitionClient(awsAuthData.keyId, awsAuthData.secretKey);
        }

        public async UniTask<List<string>> AuthenticateFaceAsync(MemoryStream stream, string collectionId, CancellationToken token = default)
        {
            var image = new Image()
            {
                Bytes = stream
            };
            return await AuthenticateFaceAsync(image, collectionId, token);
        }

        private async UniTask<List<string>> AuthenticateFaceAsync(Image image, string collectionId, CancellationToken token = default)
        {
            var searchFacesByImageRequest = new SearchFacesByImageRequest()
            {
                CollectionId = collectionId,
                Image = image,
                FaceMatchThreshold = 90f,
                MaxFaces = 1
            };

            try
            {
                var searchFacesByImageResponse = await rekoClient.SearchFacesByImageAsync(searchFacesByImageRequest, token);

                var faceIds = searchFacesByImageResponse.FaceMatches
                    .Select(x => x.Face.FaceId)
                    .ToList();

                DebugExtensions.DebugShowList(faceIds);

                return faceIds;
            }
            catch (Exception e)
            {
                Debug.Log(e);
                return default;
            }
        }
    }

AWS for .netでAmazonRekognitionを紹介している記事が日本だとないので、知見を共有すると、カメラから取得したTexture2Dをjpgのbyte配列に変換し、それをMemoryStreamに変換することによって、Amazon S3を介することなく顔照合が行えます。(料金が浮く)

FaceRecognitionProvider.cs
       private async UniTask<List<string>> AuthenticateFace(Mat mat, CancellationToken token = default)
        {
            var tex = webCamClient.GetTexture2D();
            var jpgBytes = tex.EncodeToJPG();
            Destroy(tex);
            var stream = new MemoryStream(jpgBytes);

            return await rekoGate.AuthenticateFaceAsync(
                stream,
                AmazonRekognitionSettings.FaceCollectionId,
                token);
        }

FreeeのAPIから打刻を行う

AmazonRekognitionは顔がFaceId(文字列)と結びついているので、employeeIdを紐づけしておき、打刻を行います。

以下のコードでまず、打刻可能なタイプを取得します

FreeeEventHandler.cs
public void GetTimeClocksAvailableTypes(int employeeId, Client.Callback callback = default) 
{
    var endpoint = "https://api.freee.co.jp/hr/api/v1/employees/" + employeeId +
                   "/time_clocks/available_types";
    var parameter = new Dictionary<string, string>
    {
        {"company_id", company_id.ToString()}
    };
    StartCoroutine(client.Get(endpoint, parameter, callback));
}

それをもとに打刻を行います。

FreeeEventHandler.cs
        public void PostTimeClocks(string type, int employeeId)
        {
            var endpoint = "https://api.freee.co.jp/hr/api/v1/employees/" + employeeId + "/time_clocks";
            var p = TimeClockRequestJson(type, DateTime.Now);
            StartCoroutine(client.Post(endpoint, p, OnPostTimeClocks));
        }

        void OnPostTimeClocks(bool success, string response)
        {
            if (!success)
            {
                var msg = JsonUtility.FromJson<Message>(response).message;
                return;
            }

            var tc = JsonUtility.FromJson<PostTimeClocksResponse>(response).employee_time_clock;
            _postTimeClockSubject.OnNext((FreeeType)Enum.Parse(typeof(FreeeType), tc.type));
        }

OVRLipsyncでリップシンクする

Oculusが作成したOVRLipsyncを使用します。Vtuberとかがよく使ってる口パクできるやつです。
といっても使い方は、とても簡単で使用したいキャラクターにAudioSource, OVRLipSyncContext, OVRLipContextMorphTargetをアタッチするだけです。
BlendShapesがあるSkinned Mesh Renderが必要になります。

それぞれ
aa -> あ
E -> え
ih -> い
oh -> お
ou -> う
という感じに、紐ずいています

2020-07-15 (2).png

まとめ

中途半端な形になってしまい申し訳ないです。
原因を見つけ次第更新したいと思います。
作業中、Unityちゃんの「進捗どうですか?」がぐさぐさ刺さりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

railsアプリをawsでインフラ構築する際に出たエラー

今回はこちらの記事を参考に、始めて作成したrailsのappをAWSを使用してインフラ構築に挑戦した。
https://qiita.com/naoki_mochizuki/items/814e0979217b1a25aa3e

EC2インスタンスの環境構築

$git make gcc-c++ patch
git: 'make' is not a git command. See 'git --help'.
The most similar commands are blame, merge, stage

というエラーが出た。これはコメントを削除し、1行のコマンドとして実行する必要があった。
$sudo yum install git make gcc-c++ patch openssl-devel libyaml-devel libffi-devel libicu-devel libxml2 libxslt libxml2-devel libxslt-devel zlib-devel readline-devel mysql mysql-server mysql-devel ImageMagick ImageMagick-devel epel-release

$sudo mkdir www

mkdir: ディレクトリ `www' を作成できません: File exists
というエラーが出た。既に存在しているらしいが作成した記憶はない、、、
そのまま使用するか迷ったが、一応別名(XYZ)でファイルを作成して代用した。

gitとの連携、アプリのクローン

$cat aws_git_rsa.pub
cat: aws_git_rsa.pub: No such file or directory

これは公開鍵を作成した際に通常のコマンド入力と操作が違ったため、:aws_git_rsaのうしろに半角スペースを付けてしまったのが原因だった。(凡ミス。。)

$ ssh-keygen -t rsa

Enter file in which to save the key ():aws_git_rsa(この部分に半角スペース)
鍵を再度作成したら無事に公開鍵の中身を出現させることができた。

MySQLの設定

Mysqlの起動を試みたところ、次のエラーが発生
$sudo service mysqld start #mysqldの起動
Redirecting to /bin/systemctl start mysqld.service
Failed to start mysqld.service: Unit not found.
以下の記事を参考に次のコマンドを実行したところうまくいった。
https://qiita.com/yuta-38/items/4074f5ada9e22088c8dd
$ sudo yum install -y mariadb-server
$ sudo systemctl enable mariadb
$ sudo service mariadb start

また、Amazon Linux2ではyumでmysqlをインストールしようとするとmariaDBをインストールしようとするらしい。
私は実践しなかったが、以下の記事が参考になるかもしれない。
https://qiita.com/hamham/items/fd77bb0bb167a150dc8e#mysql57%E3%81%AE%E5%B0%8E%E5%85%A5

Nginxの起動

以下のコマンドを入力したがNginxが起動しない。
$ sudo service nginx start
Redirecting to /bin/systemctl start nginx.service
Job for nginx.service failed because the control process exited with error code. See "systemctl status nginx.service" and "journalctl -xe" for details.

エラーの指示通りに以下のコマンドを実行
$ systemctl status nginx.service
nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
Active: failed (Result: exit-code) since 月 2020-07-13 09:10:04 UTC; 57s ago
Process: 11360 ExecStart=/usr/sbin/nginx (code=exited, status=1/FAILURE)
Process: 11356 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
Process: 11355 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)

原因が分からなかったので以下も実行。
$sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

設定ファイルに問題はなさそうだったので、困ったことになった。
色々調べているうちに以下のコマンドを発見。
$ sudo service nginx status -l
Redirecting to /bin/systemctl status -l nginx.service
● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
Active: failed (Result: exit-code) since 月 2020-07-13 09:10:04 UTC; 27min ago
Process: 11360 ExecStart=/usr/sbin/nginx (code=exited, status=1/FAILURE)
Process: 11356 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
Process: 11355 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
7月 13 09:10:02 ip-10-0-10-10.ap-northeast-1.compute.internal nginx[11360]: nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
7月 13 09:10:03 ip-10-0-10-10.ap-northeast-1.compute.internal nginx[11360]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
7月 13 09:10:03 ip-10-0-10-10.ap-northeast-1.compute.internal nginx[11360]: nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
7月 13 09:10:03 ip-10-0-10-10.ap-northeast-1.compute.internal nginx[11360]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
7月 13 09:10:03 ip-10-0-10-10.ap-northeast-1.compute.internal nginx[11360]: nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
7月 13 09:10:04 ip-10-0-10-10.ap-northeast-1.compute.internal nginx[11360]: nginx: [emerg] still could not bind()
7月 13 09:10:04 ip-10-0-10-10.ap-northeast-1.compute.internal systemd[1]: nginx.service: control process exited, code=exited status=1
7月 13 09:10:04 ip-10-0-10-10.ap-northeast-1.compute.internal systemd[1]: Failed to start The nginx HTTP and reverse proxy server.
7月 13 09:10:04 ip-10-0-10-10.ap-northeast-1.compute.internal systemd[1]: Unit nginx.service entered failed state.
7月 13 09:10:04 ip-10-0-10-10.ap-northeast-1.compute.internal systemd[1]: nginx.service failed.

上記によると、設定した80番ポートは既に起動中のようだ。
原因を考えた結果、以前あるweb講座でApacheのインストール及び自動起動設定をしていたのを思い出した。
$ sudo systemctl status httpd.service
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; enabled; vendor preset: disabled)
Active: active (running) since 日 2020-07-12 06:24:44 UTC; 2 days ago
Docs: man:httpd.service(8)
Main PID: 21760 (httpd)
Status: "Total requests: 149; Idle/Busy workers 100/0;Requests/sec: 0.00084; Bytes served/sec: 2 B/sec"
CGroup: /system.slice/httpd.service
├─20766 /usr/sbin/httpd -DFOREGROUND
├─21049 /usr/sbin/httpd -DFOREGROUND
├─21055 /usr/sbin/httpd -DFOREGROUND
├─21056 /usr/sbin/httpd -DFOREGROUND
├─21760 /usr/sbin/httpd -DFOREGROUND
├─21761 /usr/sbin/httpd -DFOREGROUND
├─21762 /usr/sbin/httpd -DFOREGROUND
├─21763 /usr/sbin/httpd -DFOREGROUND
├─21764 /usr/sbin/httpd -DFOREGROUND
├─21765 /usr/sbin/httpd -DFOREGROUND
└─21887 /usr/sbin/httpd -DFOREGROUND
案の定Apacheが既に動いていた。自動起動を無効&停止後に、NginXの起動コマンドを実行したら無事に起動できた。

インフラ構築前にUdemyで購入した以下の講座が効いてテンポよく理解できた。
https://www.udemy.com/course/aws-and-infra/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS認定試験 AWS Developer Associate(認定開発者) の勉強

AWS認定試験 AWS Developer Associate(認定開発者) の学習内容

AWS Developer Associate試験に合格(2020/07/13)したので参考までに実施した学習内容を公開します。

AWS Developer AssociateはAWS Solution Architect Associateほど情報がないので受験にむけた準備がやりづらい試験です。書籍はAWS認定アソシエイト3資格対策しか出ておらず、それも3つの試験に跨がる内容のためこの一冊読んだだけで合格できるか自信が持てないところです。

試験に向けて次の内容を実施しました。

試験問題サンプル実施
・AWSの公式オンライン講義 Exam Readiness: AWS Certified Developer – Associate (Digital) を視聴
AWS認定アソシエイト3資格対策 リックテレコム ¥2,503の章末問題の実施
・DVA-P01: AWS 認定 デベロッパー – アソシエイト模擬 ¥2,200 受験

準備期間は1週間ほど

ただし、直近のプロジェクトでDynamo, ElastiCache Redis, RDS(Aurora)を使ったLambdaアプリケーションを作っていたので一部、開発内容が役にたっています。
また、AWS Developer AssociateはAWS Solution Architect Associateには既に合格済みでした。

試験の感想

試験は140分で65問が出ました。
試験問題は思ったより知らないことが出てこれは無理と思いました。合格できたのは絶対に違う選択肢を落として正しい答えを選択できたのだと思います。ただし、ふるい落とした後の2択で悩んだものもありました。
また、日本語がおかしい選択肢はふるい落とせます。


AWSの公式オンライン講義の内容に沿って試験出題内容との関連性を述べたいと思います。

1. Development

CI/CD

普通に使ったことがあれば試験対策は十分かと思います。

  • CodeCommit

CodeCommitがリポジトリのため、どうやってローカルから接続するのか、認証ルールの設定などを経験しておくと良さそうです。

  • CodeBuild
  • CodePipeline
  • CodeDeploy

buildspecの記載内容を少し見ておくと良いです。

  • X-Ray

X-Rayで取得する情報を絞る方法について出題された。注釈が正解。

  • CloudWatch

CICDに関わらずいっぱい出題されます。

Scalable Application

以下の講義内容でした。AWS Well-Architectedフレームワークを知っておきましょうということです。

  • AZを使う
  • ヘルスチェックを行う
  • ステートレスにしてステータス情報はdbに保存する
  • AWSのことをよく知る

Elastic Beanstalk

今回思ったより多くのElastic Beanstalkの出題がありました。

一度チュートリアルでElastic Beanstalkのアプリケーションを作っておいた方が良さそうです。

定義ファイルの記述内容。
Dockerイメージの取得方法。
Dockerの設定はどこに記述するか。
ebコマンド。
ロードバランサーの切り替え方法。

AWS OpsWorks

出題ありませんでした。

AWS CloudFormation

構築したAWS資源がどの様にスタックと紐づいているのかを問われる試験が出題されました。

2. Security

s3の暗号化、通信内容を暗号化するのか保存ファイルを暗号化するかが複数出題されました。
認証、CognitoやAWS Security Token Serviceで何ができるかを押さえておくと良いでしょう。

応用、ひっかけ問題としてAWSアカウントIDを跨いで利用するときにどうすれば良いのかなどが出題されました。

試験問題サンプルでは、DBのパスワードをAWS Secrets Managerに保存する方法が出てくるが、Systems Managerのパラメータストアに保存するのを答えさせる出題はあったがAWS Secrets Managerは選択肢には出てこなかった。

3. Development with AWS Services

  • S3

s3単体より、他の分野と絡めて多く出てきます。

  • DynamoDB

RCU, WCUの計算は必ず出る様です。覚えておきましょう。

DAXとElastiCacheのどちらを選ばせるのかの試験も出ました。Dynamoを高速化するのはDAXです。

  • SNS

CloudWatchからの通知などで出題されました。

  • SQS

大量データの受信の対策として出てきました。

  • Step Functions
  • API Gateway

キャッシュをクリアする方法

  • CloudFront

常識的なCDNとして使う試験が出題されました。

  • ElastiCache

遅延読み込みのロジックを確認する試験が出題されました。
遅延読み込みと書き込みスルーは必ず出る様です。

  • ECS

aws cliによるecr(Elastic Container Registry)へのログイン方法が出題されました。

  • Lambda

Lambda共通ライブラリをLambdaレイヤーとしてデプロイする問題が出題されました。
Lambdaの2重起動について出題されました。

4. Refactoring

標準的なアプリケーションをAWSではどのサービスを使って構築するか。
ソリューションアーキテクトの試験と同じ内容が出ていました。

5. Monitoring and Troubleshooting

CloudWatch

標準メトリクス、カスタムメトリクスで何を取得できるのか。
CloudWatch Logsはどうすると使える様になるのかを問う問題。ロググループを作らないとCloudWatch Logsに出力される様にならないなど。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWS認定試験 AWS Developer Associate(認定開発者) の学習内容

AWS認定試験 AWS Developer Associate(認定開発者) の学習内容

AWS Developer Associate試験に合格(2020/07/13)したので参考までに実施した学習内容を公開します。

AWS Developer AssociateはAWS Solution Architect Associateほど情報がないので受験にむけた準備がやりづらい試験です。書籍はAWS認定アソシエイト3資格対策しか出ておらず、それも3つの試験に跨がる内容のためこの一冊読んだだけで合格できるか自信が持てないところです。

試験に向けて次の内容を実施しました。

試験問題サンプル実施
・AWSの公式オンライン講義 Exam Readiness: AWS Certified Developer – Associate (Digital) を視聴
AWS認定アソシエイト3資格対策 リックテレコム ¥2,503の章末問題の実施
・DVA-P01: AWS 認定 デベロッパー – アソシエイト模擬 ¥2,200 受験

準備期間は1週間ほど

ただし、直近のプロジェクトでDynamo, ElastiCache Redis, RDS(Aurora)を使ったLambdaアプリケーションを作っていたので一部、開発内容が役にたっています。
また、AWS Developer AssociateはAWS Solution Architect Associateには既に合格済みでした。

試験の感想

試験は140分で65問が出ました。
試験問題は思ったより知らないことが出てこれは無理と思いました。合格できたのは絶対に違う選択肢を落として正しい答えを選択できたのだと思います。ただし、ふるい落とした後の2択で悩んだものもありました。
また、日本語がおかしい選択肢はふるい落とせます。


AWSの公式オンライン講義の内容に沿って試験出題内容との関連性を述べたいと思います。

1. Development

CI/CD

普通に使ったことがあれば試験対策は十分かと思います。

  • CodeCommit

CodeCommitがリポジトリのため、どうやってローカルから接続するのか、認証ルールの設定などを経験しておくと良さそうです。

  • CodeBuild
  • CodePipeline
  • CodeDeploy

buildspecの記載内容を少し見ておくと良いです。

  • X-Ray

X-Rayで取得する情報を絞る方法について出題された。注釈が正解。

  • CloudWatch

CICDに関わらずいっぱい出題されます。

Scalable Application

以下の講義内容でした。AWS Well-Architectedフレームワークを知っておきましょうということです。

  • AZを使う
  • ヘルスチェックを行う
  • ステートレスにしてステータス情報はdbに保存する
  • AWSのことをよく知る

Elastic Beanstalk

今回思ったより多くのElastic Beanstalkの出題がありました。

一度チュートリアルでElastic Beanstalkのアプリケーションを作っておいた方が良さそうです。

定義ファイルの記述内容。
Dockerイメージの取得方法。
Dockerの設定はどこに記述するか。
ebコマンド。
ロードバランサーの切り替え方法。

AWS OpsWorks

出題ありませんでした。

AWS CloudFormation

構築したAWS資源がどの様にスタックと紐づいているのかを問われる試験が出題されました。

2. Security

s3の暗号化、通信内容を暗号化するのか保存ファイルを暗号化するかが複数出題されました。
認証、CognitoやAWS Security Token Serviceで何ができるかを押さえておくと良いでしょう。

応用、ひっかけ問題としてAWSアカウントIDを跨いで利用するときにどうすれば良いのかなどが出題されました。

試験問題サンプルでは、DBのパスワードをAWS Secrets Managerに保存する方法が出てくるが、Systems Managerのパラメータストアに保存するのを答えさせる出題はあったがAWS Secrets Managerは選択肢には出てこなかった。

3. Development with AWS Services

  • S3

s3単体より、他の分野と絡めて多く出てきます。

  • DynamoDB

RCU, WCUの計算は必ず出る様です。覚えておきましょう。

DAXとElastiCacheのどちらを選ばせるのかの試験も出ました。Dynamoを高速化するのはDAXです。

  • SNS

CloudWatchからの通知などで出題されました。

  • SQS

大量データの受信の対策として出てきました。

  • Step Functions
  • API Gateway

キャッシュをクリアする方法

  • CloudFront

常識的なCDNとして使う試験が出題されました。

  • ElastiCache

遅延読み込みのロジックを確認する試験が出題されました。
遅延読み込みと書き込みスルーは必ず出る様です。

  • ECS

aws cliによるecr(Elastic Container Registry)へのログイン方法が出題されました。

  • Lambda

Lambda共通ライブラリをLambdaレイヤーとしてデプロイする問題が出題されました。
Lambdaの2重起動について出題されました。

4. Refactoring

標準的なアプリケーションをAWSではどのサービスを使って構築するか。
ソリューションアーキテクトの試験と同じ内容が出ていました。

5. Monitoring and Troubleshooting

CloudWatch

標準メトリクス、カスタムメトリクスで何を取得できるのか。
CloudWatch Logsはどうすると使える様になるのかを問う問題。ロググループを作らないとCloudWatch Logsに出力される様にならないなど。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RE:DBサーバーの秘密鍵(mykey-private.pem)の作成方法

どうも考え過ぎだったようです。
P148で止まりますが、インスタンス②(DBサーバー)の秘密鍵は、インスタンス①(Webサーバー)の秘密鍵と同じmy-key.pemになっているようです。あるいは、$scp -i my-key.pem my-key.pem ec2-user@..... で秘密鍵をコピーしたせいかもしれませんが。(インスタンス②の秘密鍵が①と同じとセキュリティ上好ましくないでしょうが)
 「キーペア」もAWSコンソールに表示されているんですね。
「インスタンス」の表示で「Webサーバー」を選択して「説明」タブを見ると、「キーペア名」として、「my--key」が表示されていました。同様に、「DBサーバー」を選択すると、「キーペア名」として 「my-key」が表示されていました。そこで、テキスト通り
$ chmod 400 my-key.pem
$ ssh -i my-keypem ec2-user@10.0.2.10

で無事、DBサーバーに接続できました。初心者で、キーペア名が表示されている所を知らず申し訳ありませんでした。お騒がせしました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TerraformでAWS VPCとsubnet、NATインスタンスを作成する

TerraformでAWS VPC, subnet, SecurityGroup,NATインスタンスを作成するコード

実行環境

  • Windows 10 Home (1919)
  • Git Bash (git version 2.25.1.windows.1)
  • AWS CLI (aws-cli/2.0.3 Python/3.7.5 Windows/10 botocore/2.0.0dev7)
  • Terraform (v0.12.26)

作成する構成

まっさらな環境にVPCVPC, subnet, SecurityGroup,NATインスタンスを作成する。
NATで利用するAMI(amzn-ami-vpc-nat)は「amzn-ami-vpc-nat-2018.03.0.20191219.0-x86_64-ebs - ami-0744cc369a48918e2」。
SecurtyGroupは自宅からの接続のみを許可する。

20200713.PNG

main.tf

provider "aws" {
  profile = "prj01-profile"
  region = "us-west-2"
}

resource "aws_vpc" "prj01VPC" {
  cidr_block = "10.10.0.0/16"
  instance_tenancy = "default"
  tags = {
    Name = "prj01VPC"
    CostGroup = "prj01"
  }
}

resource "aws_subnet" "prj01PublicSubnet1a" {
  vpc_id = aws_vpc.prj01VPC.id
  cidr_block = "10.10.1.0/24"
  availability_zone = "us-west-2a"
  tags = {
    Name = "prj01PublicSubnet1a"
    CostGroup = "prj01"
  }
}

resource "aws_internet_gateway" "prj01IGW" {
  vpc_id = aws_vpc.prj01VPC.id
  tags = {
    Name = "prj01IGW"
    CostGroup = "prj01"
  }
}

resource "aws_route_table" "prj01PublicRoute" {
  vpc_id = aws_vpc.prj01VPC.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.prj01IGW.id
  }
}

resource "aws_route_table_association" "prj01public-a" {
  subnet_id = aws_subnet.prj01PublicSubnet1a.id
  route_table_id = aws_route_table.prj01PublicRoute.id
}

resource "aws_security_group" "prj01SGpublic" {
  name = "prj01SGpublic"
  description = "Prj01 Public Subnet SG"
  vpc_id = aws_vpc.prj01VPC.id
  ingress {
    description = "TLS from home"
    from_port = 22
    to_port = 22
    protocol = "tcp"
    cidr_blocks = ["xxx.xxx.xxx.xxx/32"]
  }
  egress {
    from_port = 0
    to_port = 0
    protocol = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "prj01NAT" {
  ami = "ami-0744cc369a48918e2"
  instance_type = "t2.micro"
  key_name = "prj01keypair"
  subnet_id = aws_subnet.prj01PublicSubnet1a.id
  vpc_security_group_ids = [aws_security_group.prj01SGpublic.id]
  associate_public_ip_address = "true"
  tags = {
    Name = "prj01NAT"
    CostGroup = "prj01"
  }
}

output "prj01NAT-EIP" {
  value = "${aws_instance.prj01NAT.public_ip}"
}

(自宅のグローバルIPだけ、xxxでマスク)

実行

前提

前提としてaws cliのprofileおよびkeypairは作成済み。

$ aws configure list --profile prj01-profile
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile            prj01-profile           manual    --profile
access_key     ****************FCES shared-credentials-file
secret_key     ****************4Idw shared-credentials-file
    region                us-west-2      config-file    ~/.aws/config
$ aws ec2 describe-key-pairs
{
    "KeyPairs": [
        {
            "KeyPairId": "key-0ba9cb72d459cbeb4",
            "KeyFingerprint": "e9:ec:b7:f9:3d:d0:6b:81:6d:aa:92:92:70:e5:0c:51:e7:5d:89:e7",
            "KeyName": "prj01keypair",
            "Tags": []
        }
    ]
}

実行前の状態確認

$ aws ec2 describe-vpcs  --region=us-west-2
{
    "Vpcs": []
}
$ aws ec2 describe-subnets  --region=us-west-2
{
    "Subnets": []
}
$ aws ec2 describe-internet-gateways  --region=us-west-2
{
    "InternetGateways": []
}
$ aws ec2 describe-route-tables  --region=us-west-2
{
    "RouteTables": []
}
$ aws ec2 describe-security-groups  --region=us-west-2
{
    "SecurityGroups": [
        {
            "Description": "default group",
            "GroupName": "default",
       :(中略)
        }
    ]
}
$ aws ec2 describe-instances  --region=us-west-2
{
    "Reservations": []
}

plan

$ ../terraform.exe plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.prj01NAT will be created
  + resource "aws_instance" "prj01NAT" {
      + ami                          = "ami-0744cc369a48918e2"
      + arn                          = (known after apply)
      + associate_public_ip_address  = true
      + availability_zone            = (known after apply)
      + cpu_core_count               = (known after apply)
      + cpu_threads_per_core         = (known after apply)
      + get_password_data            = false
      + host_id                      = (known after apply)
      + id                           = (known after apply)
      + instance_state               = (known after apply)
      + instance_type                = "t2.micro"
      + ipv6_address_count           = (known after apply)
      + ipv6_addresses               = (known after apply)
      + key_name                     = "prj01keypair"
      + network_interface_id         = (known after apply)
      + outpost_arn                  = (known after apply)
      + password_data                = (known after apply)
      + placement_group              = (known after apply)
      + primary_network_interface_id = (known after apply)
      + private_dns                  = (known after apply)
      + private_ip                   = (known after apply)
      + public_dns                   = (known after apply)
      + public_ip                    = (known after apply)
      + security_groups              = (known after apply)
      + source_dest_check            = true
      + subnet_id                    = (known after apply)
      + tags                         = {
          + "CostGroup" = "prj01"
          + "Name"      = "prj01NAT"
        }
      + tenancy                      = (known after apply)
      + volume_tags                  = (known after apply)
      + vpc_security_group_ids       = (known after apply)

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_internet_gateway.prj01IGW will be created
  + resource "aws_internet_gateway" "prj01IGW" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + tags     = {
          + "CostGroup" = "prj01"
          + "Name"      = "prj01IGW"
        }
      + vpc_id   = (known after apply)
    }

  # aws_route_table.prj01PublicRoute will be created
  + resource "aws_route_table" "prj01PublicRoute" {
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = [
          + {
              + cidr_block                = "0.0.0.0/0"
              + egress_only_gateway_id    = ""
              + gateway_id                = (known after apply)
              + instance_id               = ""
              + ipv6_cidr_block           = ""
              + nat_gateway_id            = ""
              + network_interface_id      = ""
              + transit_gateway_id        = ""
              + vpc_peering_connection_id = ""
            },
        ]
      + vpc_id           = (known after apply)
    }

  # aws_route_table_association.prj01public-a will be created
  + resource "aws_route_table_association" "prj01public-a" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_security_group.prj01SGpublic will be created
  + resource "aws_security_group" "prj01SGpublic" {
      + arn                    = (known after apply)
      + description            = "Prj01 Public Subnet SG"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "xxx.xxx.xxx.xxx/32",
                ]
              + description      = "TLS from home"
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
            },
        ]
      + name                   = "prj01SGpublic"
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + vpc_id                 = (known after apply)
    }

  # aws_subnet.prj01PublicSubnet1a will be created
  + resource "aws_subnet" "prj01PublicSubnet1a" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "us-west-2a"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.10.1.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block                 = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = false
      + owner_id                        = (known after apply)
      + tags                            = {
          + "CostGroup" = "prj01"
          + "Name"      = "prj01PublicSubnet1a"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_vpc.prj01VPC will be created
  + resource "aws_vpc" "prj01VPC" {
      + arn                              = (known after apply)
      + assign_generated_ipv6_cidr_block = false
      + cidr_block                       = "10.10.0.0/16"
      + default_network_acl_id           = (known after apply)
      + default_route_table_id           = (known after apply)
      + default_security_group_id        = (known after apply)
      + dhcp_options_id                  = (known after apply)
      + enable_classiclink               = (known after apply)
      + enable_classiclink_dns_support   = (known after apply)
      + enable_dns_hostnames             = (known after apply)
      + enable_dns_support               = true
      + id                               = (known after apply)
      + instance_tenancy                 = "default"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      + main_route_table_id              = (known after apply)
      + owner_id                         = (known after apply)
      + tags                             = {
          + "CostGroup" = "prj01"
          + "Name"      = "prj01VPC"
        }
    }

Plan: 7 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

実行(apply)

$ ../terraform.exe apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # aws_instance.prj01NAT will be created
  + resource "aws_instance" "prj01NAT" {
      + ami                          = "ami-0744cc369a48918e2"
      + arn                          = (known after apply)
      + associate_public_ip_address  = true
      + availability_zone            = (known after apply)
      + cpu_core_count               = (known after apply)
      + cpu_threads_per_core         = (known after apply)
      + get_password_data            = false
      + host_id                      = (known after apply)
      + id                           = (known after apply)
      + instance_state               = (known after apply)
      + instance_type                = "t2.micro"
      + ipv6_address_count           = (known after apply)
      + ipv6_addresses               = (known after apply)
      + key_name                     = "prj01keypair"
      + network_interface_id         = (known after apply)
      + outpost_arn                  = (known after apply)
      + password_data                = (known after apply)
      + placement_group              = (known after apply)
      + primary_network_interface_id = (known after apply)
      + private_dns                  = (known after apply)
      + private_ip                   = (known after apply)
      + public_dns                   = (known after apply)
      + public_ip                    = (known after apply)
      + security_groups              = (known after apply)
      + source_dest_check            = true
      + subnet_id                    = (known after apply)
      + tags                         = {
          + "CostGroup" = "prj01"
          + "Name"      = "prj01NAT"
        }
      + tenancy                      = (known after apply)
      + volume_tags                  = (known after apply)
      + vpc_security_group_ids       = (known after apply)

      + ebs_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + snapshot_id           = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }

      + ephemeral_block_device {
          + device_name  = (known after apply)
          + no_device    = (known after apply)
          + virtual_name = (known after apply)
        }

      + metadata_options {
          + http_endpoint               = (known after apply)
          + http_put_response_hop_limit = (known after apply)
          + http_tokens                 = (known after apply)
        }

      + network_interface {
          + delete_on_termination = (known after apply)
          + device_index          = (known after apply)
          + network_interface_id  = (known after apply)
        }

      + root_block_device {
          + delete_on_termination = (known after apply)
          + device_name           = (known after apply)
          + encrypted             = (known after apply)
          + iops                  = (known after apply)
          + kms_key_id            = (known after apply)
          + volume_id             = (known after apply)
          + volume_size           = (known after apply)
          + volume_type           = (known after apply)
        }
    }

  # aws_internet_gateway.prj01IGW will be created
  + resource "aws_internet_gateway" "prj01IGW" {
      + arn      = (known after apply)
      + id       = (known after apply)
      + owner_id = (known after apply)
      + tags     = {
          + "CostGroup" = "prj01"
          + "Name"      = "prj01IGW"
        }
      + vpc_id   = (known after apply)
    }

  # aws_route_table.prj01PublicRoute will be created
  + resource "aws_route_table" "prj01PublicRoute" {
      + id               = (known after apply)
      + owner_id         = (known after apply)
      + propagating_vgws = (known after apply)
      + route            = [
          + {
              + cidr_block                = "0.0.0.0/0"
              + egress_only_gateway_id    = ""
              + gateway_id                = (known after apply)
              + instance_id               = ""
              + ipv6_cidr_block           = ""
              + nat_gateway_id            = ""
              + network_interface_id      = ""
              + transit_gateway_id        = ""
              + vpc_peering_connection_id = ""
            },
        ]
      + vpc_id           = (known after apply)
    }

  # aws_route_table_association.prj01public-a will be created
  + resource "aws_route_table_association" "prj01public-a" {
      + id             = (known after apply)
      + route_table_id = (known after apply)
      + subnet_id      = (known after apply)
    }

  # aws_security_group.prj01SGpublic will be created
  + resource "aws_security_group" "prj01SGpublic" {
      + arn                    = (known after apply)
      + description            = "Prj01 Public Subnet SG"
      + egress                 = [
          + {
              + cidr_blocks      = [
                  + "0.0.0.0/0",
                ]
              + description      = ""
              + from_port        = 0
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "-1"
              + security_groups  = []
              + self             = false
              + to_port          = 0
            },
        ]
      + id                     = (known after apply)
      + ingress                = [
          + {
              + cidr_blocks      = [
                  + "xxx.xxx.xxx.xxx/32",
                ]
              + description      = "TLS from home"
              + from_port        = 22
              + ipv6_cidr_blocks = []
              + prefix_list_ids  = []
              + protocol         = "tcp"
              + security_groups  = []
              + self             = false
              + to_port          = 22
            },
        ]
      + name                   = "prj01SGpublic"
      + owner_id               = (known after apply)
      + revoke_rules_on_delete = false
      + vpc_id                 = (known after apply)
    }

  # aws_subnet.prj01PublicSubnet1a will be created
  + resource "aws_subnet" "prj01PublicSubnet1a" {
      + arn                             = (known after apply)
      + assign_ipv6_address_on_creation = false
      + availability_zone               = "us-west-2a"
      + availability_zone_id            = (known after apply)
      + cidr_block                      = "10.10.1.0/24"
      + id                              = (known after apply)
      + ipv6_cidr_block                 = (known after apply)
      + ipv6_cidr_block_association_id  = (known after apply)
      + map_public_ip_on_launch         = false
      + owner_id                        = (known after apply)
      + tags                            = {
          + "CostGroup" = "prj01"
          + "Name"      = "prj01PublicSubnet1a"
        }
      + vpc_id                          = (known after apply)
    }

  # aws_vpc.prj01VPC will be created
  + resource "aws_vpc" "prj01VPC" {
      + arn                              = (known after apply)
      + assign_generated_ipv6_cidr_block = false
      + cidr_block                       = "10.10.0.0/16"
      + default_network_acl_id           = (known after apply)
      + default_route_table_id           = (known after apply)
      + default_security_group_id        = (known after apply)
      + dhcp_options_id                  = (known after apply)
      + enable_classiclink               = (known after apply)
      + enable_classiclink_dns_support   = (known after apply)
      + enable_dns_hostnames             = (known after apply)
      + enable_dns_support               = true
      + id                               = (known after apply)
      + instance_tenancy                 = "default"
      + ipv6_association_id              = (known after apply)
      + ipv6_cidr_block                  = (known after apply)
      + main_route_table_id              = (known after apply)
      + owner_id                         = (known after apply)
      + tags                             = {
          + "CostGroup" = "prj01"
          + "Name"      = "prj01VPC"
        }
    }

Plan: 7 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

aws_vpc.prj01VPC: Creating...
aws_vpc.prj01VPC: Creation complete after 9s [id=vpc-07f01f8953bec8054]
aws_internet_gateway.prj01IGW: Creating...
aws_subnet.prj01PublicSubnet1a: Creating...
aws_security_group.prj01SGpublic: Creating...
aws_subnet.prj01PublicSubnet1a: Creation complete after 4s [id=subnet-002a899cff16b1c1e]
aws_internet_gateway.prj01IGW: Creation complete after 4s [id=igw-0a3ed75c307556d56]
aws_route_table.prj01PublicRoute: Creating...
aws_security_group.prj01SGpublic: Creation complete after 6s [id=sg-011197b779f8fc187]
aws_instance.prj01NAT: Creating...
aws_route_table.prj01PublicRoute: Creation complete after 3s [id=rtb-0572913cf77f4d732]
aws_route_table_association.prj01public-a: Creating...
aws_route_table_association.prj01public-a: Creation complete after 0s [id=rtbassoc-00749569edc659d21]
aws_instance.prj01NAT: Still creating... [10s elapsed]
aws_instance.prj01NAT: Still creating... [20s elapsed]
aws_instance.prj01NAT: Creation complete after 28s [id=i-0ead2524f144ce254]

Apply complete! Resources: 7 added, 0 changed, 0 destroyed.

Outputs:

prj01NAT-EIP = 34.220.64.163

接続確認

$ ssh -i ../../key/prj01keypair.pem ec2-user@34.220.64.163
The authenticity of host '34.220.64.163 (34.220.64.163)' can't be established.
ECDSA key fingerprint is SHA256:fT4x/XtdVtiIkGYGJp4oy2F1w/lHhCYHfh0Czg1QP5c.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '34.220.64.163' (ECDSA) to the list of known hosts.

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/
12 package(s) needed for security, out of 27 available
Run "sudo yum update" to apply all updates.
[ec2-user@ip-10-10-1-59 ~]$

無事接続できた。

作成されたものの状態確認

$ aws ec2 describe-vpcs  --region=us-west-2
{
    "Vpcs": [
        {
            "CidrBlock": "10.10.0.0/16",
            "DhcpOptionsId": "dopt-0ebee8b328487036e",
            "State": "available",
            "VpcId": "vpc-07f01f8953bec8054",
            "OwnerId": "679788997248",
            "InstanceTenancy": "default",
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-0c9db0dd4f6e7f945",
                    "CidrBlock": "10.10.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": false,
            "Tags": [
                {
                    "Key": "CostGroup",
                    "Value": "prj01"
                },
                {
                    "Key": "Name",
                    "Value": "prj01VPC"
                }
            ]
        }
    ]
}
$ aws ec2 describe-subnets  --region=us-west-2
{
    "Subnets": [
        {
            "AvailabilityZone": "us-west-2a",
            "AvailabilityZoneId": "usw2-az2",
            "AvailableIpAddressCount": 250,
            "CidrBlock": "10.10.1.0/24",
            "DefaultForAz": false,
            "MapPublicIpOnLaunch": false,
            "State": "available",
            "SubnetId": "subnet-002a899cff16b1c1e",
            "VpcId": "vpc-07f01f8953bec8054",
            "OwnerId": "679788997248",
            "AssignIpv6AddressOnCreation": false,
            "Ipv6CidrBlockAssociationSet": [],
            "Tags": [
                {
                    "Key": "CostGroup",
                    "Value": "prj01"
                },
                {
                    "Key": "Name",
                    "Value": "prj01PublicSubnet1a"
                }
            ],
            "SubnetArn": "arn:aws:ec2:us-west-2:679788997248:subnet/subnet-002a899cff16b1c1e"
        }
    ]
}
$ aws ec2 describe-internet-gateways  --region=us-west-2
{
    "InternetGateways": [
        {
            "Attachments": [
                {
                    "State": "available",
                    "VpcId": "vpc-07f01f8953bec8054"
                }
            ],
            "InternetGatewayId": "igw-0a3ed75c307556d56",
            "OwnerId": "679788997248",
            "Tags": [
                {
                    "Key": "CostGroup",
                    "Value": "prj01"
                },
                {
                    "Key": "Name",
                    "Value": "prj01IGW"
                }
            ]
        }
    ]
}
$ aws ec2 describe-route-tables  --region=us-west-2
{
    "RouteTables": [
        {
            "Associations": [
                {
                    "Main": true,
                    "RouteTableAssociationId": "rtbassoc-0acb7861b8f970d0c",
                    "RouteTableId": "rtb-00e81b7edaf3874c0",
                    "AssociationState": {
                        "State": "associated"
                    }
                }
            ],
            "PropagatingVgws": [],
            "RouteTableId": "rtb-00e81b7edaf3874c0",
            "Routes": [
                {
                    "DestinationCidrBlock": "10.10.0.0/16",
                    "GatewayId": "local",
                    "Origin": "CreateRouteTable",
                    "State": "active"
                }
            ],
            "Tags": [],
            "VpcId": "vpc-07f01f8953bec8054",
            "OwnerId": "679788997248"
        },
        {
            "Associations": [
                {
                    "Main": false,
                    "RouteTableAssociationId": "rtbassoc-00749569edc659d21",
                    "RouteTableId": "rtb-0572913cf77f4d732",
                    "SubnetId": "subnet-002a899cff16b1c1e",
                    "AssociationState": {
                        "State": "associated"
                    }
                }
            ],
            "PropagatingVgws": [],
            "RouteTableId": "rtb-0572913cf77f4d732",
            "Routes": [
                {
                    "DestinationCidrBlock": "10.10.0.0/16",
                    "GatewayId": "local",
                    "Origin": "CreateRouteTable",
                    "State": "active"
                },
                {
                    "DestinationCidrBlock": "0.0.0.0/0",
                    "GatewayId": "igw-0a3ed75c307556d56",
                    "Origin": "CreateRoute",
                    "State": "active"
                }
            ],
            "Tags": [],
            "VpcId": "vpc-07f01f8953bec8054",
            "OwnerId": "679788997248"
        }
    ]
}
$ aws ec2 describe-security-groups  --region=us-west-2
{
    "SecurityGroups": [
        {
            "Description": "default group",
            "GroupName": "default",
            "IpPermissions": [
                {
                    "FromPort": 0,
                    "IpProtocol": "udp",
                    "IpRanges": [],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": 65535,
                    "UserIdGroupPairs": [
                        {
                            "GroupId": "sg-f26ffcc2",
                            "GroupName": "default",
                            "UserId": "679788997248"
                        }
                    ]
                },
                {
                    "FromPort": -1,
                    "IpProtocol": "icmp",
                    "IpRanges": [],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": -1,
                    "UserIdGroupPairs": [
                        {
                            "GroupId": "sg-f26ffcc2",
                            "GroupName": "default",
                            "UserId": "679788997248"
                        }
                    ]
                },
                {
                    "FromPort": 0,
                    "IpProtocol": "tcp",
                    "IpRanges": [],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": 65535,
                    "UserIdGroupPairs": [
                        {
                            "GroupId": "sg-f26ffcc2",
                            "GroupName": "default",
                            "UserId": "679788997248"
                        }
                    ]
                }
            ],
            "OwnerId": "679788997248",
            "GroupId": "sg-f26ffcc2",
            "IpPermissionsEgress": []
        },
        {
            "Description": "quick-start-1",
            "GroupName": "quick-start-1",
            "IpPermissions": [
                {
                    "FromPort": 22,
                    "IpProtocol": "tcp",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": 22,
                    "UserIdGroupPairs": []
                },
                {
                    "FromPort": -1,
                    "IpProtocol": "icmp",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": -1,
                    "UserIdGroupPairs": []
                }
            ],
            "OwnerId": "679788997248",
            "GroupId": "sg-486dfe78",
            "IpPermissionsEgress": []
        },
        {
            "Description": "Prj01 Public Subnet SG",
            "GroupName": "prj01SGpublic",
            "IpPermissions": [
                {
                    "FromPort": 22,
                    "IpProtocol": "tcp",
                    "IpRanges": [
                        {
                            "CidrIp": "xxx.xxx.xxx.xxx/32",
                            "Description": "TLS from home"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "ToPort": 22,
                    "UserIdGroupPairs": []
                }
            ],
            "OwnerId": "679788997248",
            "GroupId": "sg-011197b779f8fc187",
            "IpPermissionsEgress": [
                {
                    "IpProtocol": "-1",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": []
                }
            ],
            "VpcId": "vpc-07f01f8953bec8054"
        },
        {
            "Description": "default VPC security group",
            "GroupName": "default",
            "IpPermissions": [
                {
                    "IpProtocol": "-1",
                    "IpRanges": [],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": [
                        {
                            "GroupId": "sg-02a3b4125c3e94cd6",
                            "UserId": "679788997248"
                        }
                    ]
                }
            ],
            "OwnerId": "679788997248",
            "GroupId": "sg-02a3b4125c3e94cd6",
            "IpPermissionsEgress": [
                {
                    "IpProtocol": "-1",
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "Ipv6Ranges": [],
                    "PrefixListIds": [],
                    "UserIdGroupPairs": []
                }
            ],
            "VpcId": "vpc-07f01f8953bec8054"
        }
    ]
}
$ aws ec2 describe-instances  --region=us-west-2
{
    "Reservations": [
        {
            "Groups": [],
            "Instances": [
                {
                    "AmiLaunchIndex": 0,
                    "ImageId": "ami-0744cc369a48918e2",
                    "InstanceId": "i-0ead2524f144ce254",
                    "InstanceType": "t2.micro",
                    "KeyName": "prj01keypair",
                    "LaunchTime": "2020-07-14T20:21:15+00:00",
                    "Monitoring": {
                        "State": "disabled"
                    },
                    "Placement": {
                        "AvailabilityZone": "us-west-2a",
                        "GroupName": "",
                        "Tenancy": "default"
                    },
                    "PrivateDnsName": "ip-10-10-1-59.us-west-2.compute.internal",
                    "PrivateIpAddress": "10.10.1.59",
                    "ProductCodes": [],
                    "PublicDnsName": "",
                    "PublicIpAddress": "34.220.64.163",
                    "State": {
                        "Code": 16,
                        "Name": "running"
                    },
                    "StateTransitionReason": "",
                    "SubnetId": "subnet-002a899cff16b1c1e",
                    "VpcId": "vpc-07f01f8953bec8054",
                    "Architecture": "x86_64",
                    "BlockDeviceMappings": [
                        {
                            "DeviceName": "/dev/xvda",
                            "Ebs": {
                                "AttachTime": "2020-07-14T20:21:15+00:00",
                                "DeleteOnTermination": true,
                                "Status": "attached",
                                "VolumeId": "vol-01040fdbd785ac7b0"
                            }
                        }
                    ],
                    "ClientToken": "D9FFD1E8-29B1-4BC9-BF89-0DB594B3460D",
                    "EbsOptimized": false,
                    "EnaSupport": true,
                    "Hypervisor": "xen",
                    "NetworkInterfaces": [
                        {
                            "Association": {
                                "IpOwnerId": "amazon",
                                "PublicDnsName": "",
                                "PublicIp": "34.220.64.163"
                            },
                            "Attachment": {
                                "AttachTime": "2020-07-14T20:21:15+00:00",
                                "AttachmentId": "eni-attach-00044a8099cfdad43",
                                "DeleteOnTermination": true,
                                "DeviceIndex": 0,
                                "Status": "attached"
                            },
                            "Description": "",
                            "Groups": [
                                {
                                    "GroupName": "prj01SGpublic",
                                    "GroupId": "sg-011197b779f8fc187"
                                }
                            ],
                            "Ipv6Addresses": [],
                            "MacAddress": "06:0d:c9:db:e6:f2",
                            "NetworkInterfaceId": "eni-0d1d1ba4cb5fef465",
                            "OwnerId": "679788997248",
                            "PrivateIpAddress": "10.10.1.59",
                            "PrivateIpAddresses": [
                                {
                                    "Association": {
                                        "IpOwnerId": "amazon",
                                        "PublicDnsName": "",
                                        "PublicIp": "34.220.64.163"
                                    },
                                    "Primary": true,
                                    "PrivateIpAddress": "10.10.1.59"
                                }
                            ],
                            "SourceDestCheck": true,
                            "Status": "in-use",
                            "SubnetId": "subnet-002a899cff16b1c1e",
                            "VpcId": "vpc-07f01f8953bec8054",
                            "InterfaceType": "interface"
                        }
                    ],
                    "RootDeviceName": "/dev/xvda",
                    "RootDeviceType": "ebs",
                    "SecurityGroups": [
                        {
                            "GroupName": "prj01SGpublic",
                            "GroupId": "sg-011197b779f8fc187"
                        }
                    ],
                    "SourceDestCheck": true,
                    "Tags": [
                        {
                            "Key": "Name",
                            "Value": "prj01NAT"
                        },
                        {
                            "Key": "CostGroup",
                            "Value": "prj01"
                        }
                    ],
                    "VirtualizationType": "hvm",
                    "CpuOptions": {
                        "CoreCount": 1,
                        "ThreadsPerCore": 1
                    },
                    "CapacityReservationSpecification": {
                        "CapacityReservationPreference": "open"
                    },
                    "HibernationOptions": {
                        "Configured": false
                    },
                    "MetadataOptions": {
                        "State": "applied",
                        "HttpTokens": "optional",
                        "HttpPutResponseHopLimit": 1,
                        "HttpEndpoint": "enabled"
                    }
                }
            ],
            "OwnerId": "679788997248",
            "ReservationId": "r-0621a71842792f593"
        }
    ]
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AWSとか知識ゼロのクライアントエンジニアがAWSとかの勉強を始めてみる 2

...というわけで、前回に引き続き
本当に何から手をつけたらいいのか分からない自分は
とりあえずAWSの世界で使われるコトバや概念を頭に入れるところから始めてみることにした。

語学を学ぶ時と同じように、単語から覚える感覚で。

ただし、そのまま単語だけ覚えてもただの暗記になってしまうので
この段階は早めに突破したい気持ち。

昨日見つけたYoutuberのくろかわこうへいさんの「AWS講座VPC編」を見ているところ。
この内容はきっと基礎中の基礎レベルの解説だと思われるのだけど、
正直、現段階の自分には結構難しい。。

そのうち理解できるようになるだろう...と思いながら、ひとまず最後まで見てみます。
VPC編を見終わったらEC2編かな。


今日覚えたコトバと概念と理解感

・そもそも「VPC」って何だって話

・リージョン
 ---> 国、のイメージでひとまず覚える

・AZ
 ---> データセンター、のイメージでひとまず覚える

・サブネット(publicとprivate)
 ---> まるでプログラミングのpublic, privateのようだ。

・インバウンド、アウトバウンド
 ---> 発信できるかできないかの違い

・ルートテーブル
 ---> ルールね

・インスタンス
 ---> なんだ、そのことをそう呼んでいたのか。やっと知った。

・Internet gateway
 ---> ネットの接続担当みたいな

・NATゲートウェイ
 ---> privateサブネットとInternet gatewayの仲介役かな

・ENI
 ---> まだ概念のイメージがしづらい。
    でも、これがInternet gatewayと繋がることでインターネット接続が
    できるようになるんだな、ってのはなんとなく分かった。

・Public IP address
 ---> インスタンスに付ける(割り当てる)ものなのね、くらいしか現段階ではあまりよく分かってないです。

・Elastic IP address
 ---> Public IP addressと同じくらい良く分からないけど
   「変わらないIP address」なのが特徴なのね、って認識。


今は動画で視覚的に学べる時代で、ありがたい。
学習用の動画を提供してくださっている方々にも感謝。

今日はここまで。
明日、忘れてませんように。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む