新人プログラマの為のAWS入門 ～実践編～

これは[Tech Fun Advent Calendar 2017](https://qiita.com/advent-calendar/2017/techfun)の一記事です。

[～導入編～](https://qiita.com/tf-oikawa/items/6c29fbb4131a72f7adbb)のつづきになります。

AWS入門の実践編として、AWS EC2、CLI、およびサーバーレスサービスの代表であるLambda + API Gatewayをハンズオン形式で触ってみます。


-----------------------------------------------------
# 実践 #1 ～EC2編～
-----------------------------------------------------
目標：

* 仮想マシンを気軽に立ち上げたり、破棄できるようになる

---
## ①EC2インスタンス作成

---
### ステップ 0: インスタンス作成ウィザードを開始
1. サービス一覧から「EC2」を選択
1. EC2ダッシュボード＞
    * Topページ「インスタンスの作成」ボタンからウィザードを開始
    * または左側のメニュー「インスタンス」を選択＞インスタンス一覧画面左上の「インスタンスの作成」ボタンから開始

---
### ステップ 1: Amazon マシンイメージ(AMI)
> Amazon マシンイメージ (AMI) は、ソフトウェア構成 (オペレーティングシステム、アプリケーションサーバー、アプリケーションなど) を記録したテンプレートです。
(引用：http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instances-and-amis.html)

⇒ひとまず**[無料利用枠](https://aws.amazon.com/jp/free/)の「Amazon Linux AMI」**を選択
![2017-11-05_22h28_15.png](https://qiita-image-store.s3.amazonaws.com/0/104276/1cda6676-23ee-1ed3-e192-1b421abe7c86.png)

---
#### AMI サイドメニューの補足

* マイ AMI ・・・
  * 自分でカスタマイズしたマシンイメージを登録できる
* AWS Marketplace / コミュニティ AMI ・・・
  * Microsoftなどベンダー各社がソフトウェア製品を予めセットアップしたAMIを購入して使用できる
  * OSSのコミュニティなどが作成した無料利用対象のAMIもあり

---
### ステップ 2: インスタンスタイプの選択
仮想マシーンに割り振られるハードウェアスペック。
汎用向け・CPU性能が高いもの・メモリ容量が多いもの などなど、要求性能別にカテゴリ分けされている。
![2017-11-05_22h36_03.png](https://qiita-image-store.s3.amazonaws.com/0/104276/d354551b-375d-a405-768e-80d524b5b7ca.png)
詳細：https://aws.amazon.com/jp/ec2/instance-types/

⇒**[無料利用枠](https://aws.amazon.com/jp/free/)の「t2.micro」**を選択 (後からスケールアップ可能)
⇒「次の手順」へ
![2017-11-05_22h36_55.png](https://qiita-image-store.s3.amazonaws.com/0/104276/b8d6379a-da67-f717-3239-61c2e93d31cb.png)
※以降の設定が全てデフォルトで良ければ、この時点で「確認と作成」ボタンを押せばインスタンス作成を完了出来る

---
### ステップ 3: インスタンスの詳細の設定
このページの設定はかなり重要な項目が多い。

![2017-11-05_22h42_02.png](https://qiita-image-store.s3.amazonaws.com/0/104276/f3a1f95d-a0b3-1598-276d-cc3194e7abb5.png)

* インスタンス数・・・
  * 同時作成するインスタンス数。サーバのスケーリング設定時などに活用する
  * 下手に大きな数で作成してしまうと・・・
* ネットワーク＆サブネット・・・
  * VPC(バーチャルプライベートクラウド)を使用する場合などは設定必須
  * この設定を間違うと作成した仮想マシンが他のインスタンスやサービスと通信できない
* IAMロール・・・
  * インスタンスに適用させるIAM権限。
  * インスタンスで稼働させるアプリ等が他のAWSサービスと連携する場合は適切なロールの設定が必要

⇒ひとまず今回はデフォルト設定のまま進めてOK。

---
### ステップ 4: ストレージの追加
インスタンスで使用するストレージのアタッチ。
デフォルトではインスタンス削除とともにデータも削除される8GBのルートボリュームしか設定されていない。
容量の変更や暗号化ストレージ・ネットワーク共有ストレージ(EFS)などが必要な場合は別途追加する必要がある。

⇒ひとまずデフォルト設定で進む。
![2017-11-05_23h00_55.png](https://qiita-image-store.s3.amazonaws.com/0/104276/974a0645-74b2-baf3-2edf-506f16671fc7.png)

---
### ステップ 5: タグの追加
インスタンス(とボリューム)の判別の為にユーザ独自のタグを設定できる。
例えば「env」というキーのタグを設定し、値に

  * 開発環境 ⇒「develop」
  * 検証環境 ⇒「staging」
  * 本番環境 ⇒「product」

などを設定して環境ごとにタグで識別出来るようにする。

好きなように設定して次に進む。
![2017-11-05_23h32_44.png](https://qiita-image-store.s3.amazonaws.com/0/104276/2b1c4555-9317-2341-1fc0-3d2254ebd074.png)

※タグはEC2インスタンスだけでなく他の様々なAWSリソースに設定できる
※`Name`タグを設定すると、インスタンス一覧画面などでデフォルトで識別名として使用される

---
### ステップ 6: セキュリティグループの設定
セキュリティグループ(SG)・・・AWSにおけるファイアウォール設定ルールのグループ

この画面で新規に作成したものを使用するか、既存のグループを一覧から選択し割り当てる。
デフォルトでは[SSH接続](https://ja.wikipedia.org/wiki/Secure_Shell)のみ許可されたSGが新規作成される。
ネットワーク要件により適切なIP・ポートの通信を許可する必要がある。

⇒後で削除する際分かりやすいようセキュリティグループ名を適当な名前に変更
⇒SSHポートのアクセス許可のソースに「マイIP」を選択(＜現在アクセス中のIPからのみアクセスが許可される)
⇒「確認と作成」から次へ
![2017-11-06_00h01_04.png](https://qiita-image-store.s3.amazonaws.com/0/104276/9e94163c-6d52-722b-efb8-14da34f82bfa.png)

---
### ステップ 7: インスタンス作成の確認
これまで設定した内容が一覧で表示される。
確認後、問題なければ「作成」ボタンを押し、キーペアの設定に進む。
![2017-11-06_00h06_22.png](https://qiita-image-store.s3.amazonaws.com/0/104276/3e656927-24be-5ed0-efcb-4ee1267e3170.png)

---
#### SSH接続用キーペアの設定
確認画面で「作成」ボタンを押すと、下記のようにSSHログイン時に使用する鍵ファイルの設定画面が表示される。

* 鍵ファイルを新規に作成する場合
  1. プルダウンから「新しいキーペアの作成」を選び、適当なキーペア名を入れて「キーペアのダウンロード」**(※一度しかDL出来ないので注意！！)**
  2. 作成された鍵ファイルを任意の場所に保存。(ユーザホームの`.ssh`フォルダが一般的)
![2017-11-06_00h10_49.png](https://qiita-image-store.s3.amazonaws.com/0/104276/52168a2f-e5ae-5a99-8d27-62af1eeaa370.png)

* 既存の鍵ファイルを使用する場合
  * 上記1.の設定で「既存のキーペアの選択」を選び、2番目のプルダウンから使用するキーペアを選択する
  * 下部の注意書きの内容を理解した後、チェックボックスにチェックをつけて「インスタンスの作成」
![2017-11-06_00h57_55.png](https://qiita-image-store.s3.amazonaws.com/0/104276/a5289392-2d60-6623-3a4a-89ee5a46f7a6.png)

**※鍵ファイルの管理は自己責任。無くすとインスタンスにログイン出来なくなる。**

---
### インスタンス起動
キーペアの設定まで完了すると、下記のような画面が表示されインスタンス作成が始まる。
![2017-11-06_00h37_46.png](https://qiita-image-store.s3.amazonaws.com/0/104276/3fd4873f-5796-4ddc-5df4-6bc4c450d27e.png)

⇒右下の「インスタンスの表示」を押下

インスタンス一覧に作成中のインスタンスが追加されている
![2017-11-06_00h40_37.png](https://qiita-image-store.s3.amazonaws.com/0/104276/58f7c264-0ed6-d895-cd68-fd965c96d9bf.png)
作成直後はステータスチェックが「初期化中」
⇒ステータスがグリーンになればインスタンス起動完了
![2017-11-06_01h07_34.png](https://qiita-image-store.s3.amazonaws.com/0/104276/bcbc7929-4dc6-2da1-b5e0-6381b948a245.png)

---
## ②インスタンスへログイン
---

インスタンス一覧画面で対象を選択し、「接続」ボタンを押すとSSH接続方法の説明が表示される。
![2017-11-06_01h13_07.png](https://qiita-image-store.s3.amazonaws.com/0/104276/9b0f5469-1b68-3356-12cf-093a8065d652.png)
⇒お好きなクライアントから接続確認

・・・特にこだわりが無い場合は

* 適当なターミナルでSSHコマンドを使えるようにする
* インスタンスのキーペアに設定したpemファイルが存在するフォルダに移動
* 「例：～」のSSHコマンドサンプルをそのまま実行

⇒初回接続時はSSH接続先としてサーバを信頼するか確認される＞`yes`と入力
⇒無事ログイン出来ればAmazon Linuxのロゴが表示される
![2017-11-06_01h34_21.png](https://qiita-image-store.s3.amazonaws.com/0/104276/b25c6e77-6ec6-bcb2-4c2e-9a446df5a34c.png)

`ec2-user`はAmazon Linuxにデフォルトで用意されている管理用ユーザ(sudoコマンド使用可能)


※[Tips]
EC2のデフォルトではクライアントから一定時間操作がないとSSH接続が切断されるようになっている。
クライアントのssh configに下記を設定しておくと切断を防止できる。

```bash:~/.ssh/config
Host *
  ServerAliveInterval 60    #60秒ごとに通信して接続を維持
```

---
## ③インスタンスの停止・再起動
---

EC2インスタンスの停止や再起動などは下記操作で行う。

1. インスタンス一覧から対象を選択
1. 「アクション」
1. 「インスタンスの状態」
1. 実行する操作を選択
![2017-11-06_02h27_34.png](https://qiita-image-store.s3.amazonaws.com/0/104276/05ec8d5e-8831-c522-a459-5702d9fc7df3.png)

※インスタンス停止中はインスタンス使用料は発生しないが、EBS(ストレージ)やEIP(固定IPアドレス)の保持料金は課金される

---
### EC2のパブリックIPについて
通常、EC2インスタンスのパブリックIPアドレスはインスタンスを停止する度に変更される。(再起動の場合はそのまま)
SSH接続のサンプルで使用したDNSも変更されるので注意。

* インスタンス停止前
![2017-11-06_03h06_53.png](https://qiita-image-store.s3.amazonaws.com/0/104276/e026168e-a244-2ce9-538e-4e1f7c68d2ae.png)

* インスタンス停止(⇒パブリックIPの割り当てが解除される)
![2017-11-06_03h15_58.png](https://qiita-image-store.s3.amazonaws.com/0/104276/b4a5ba6a-3a24-1026-40d4-6dc78ba1d609.png)

* インスタンス起動(⇒新しいパブリックIPが割り当てられる)
![2017-11-06_03h19_46.png](https://qiita-image-store.s3.amazonaws.com/0/104276/b48ef6b7-b8d9-1329-bc6b-abaefe33a4fe.png)

EIP(Elastic IP、固定IPアドレス)を取得して割り当てれば固定化出来るが、アカウント毎に2つ目以降はお金が掛かる(and 発行可能なIP数に上限がある)ので、稼働させるEC2インスタンス全てにEIPを割り当てたりは(普通は)しない。

⇒大抵の場合、管理用のネットワークとEIPを割り当てた"踏み台サーバ"を1つ用意し、他のインスタンスへは常にこのサーバを経由してプライベートIP経由でアクセスするようにして運用する。(※気が向いたら、後日詳細追記)

---
## ④インスタンスの削除
---

EC2インスタンスの削除は、停止や再起動と同じく、「アクション」＞「インスタンスの状態」メニューから行う。
削除を選択すると、確認ダイアログが表示される。
![2017-11-06_03h23_32.png](https://qiita-image-store.s3.amazonaws.com/0/104276/18ce09f5-d9f8-5b15-cec4-30048d4a99e4.png)
インスタンスが起動中だった場合はシャットダウンが実行される。
![2017-11-06_03h24_35.png](https://qiita-image-store.s3.amazonaws.com/0/104276/4d265e1d-2012-32f2-bb66-c2d334cee680.png)
削除が完了すると状態が「terminated」になる。
![2017-11-06_03h47_23.png](https://qiita-image-store.s3.amazonaws.com/0/104276/aeb097e4-4bf1-075d-aa40-8856d7fa9572.png)

---
### インスタンスにアタッチされていたリソースについて
* ストレージ・・・
  * 「合わせて削除」にチェックして作成したものは一緒に削除される。
  * それ以外は残る。
* セキュリティグループ・・・
  * そのまま残る。不要な場合は手動削除要。
* キーペア・・・
  * そのまま残る。不要な場合は手動削除要。



-----------------------------------------------------
# 実践 #2 ～CLI編～
-----------------------------------------------------
目標：

* コマンドラインでAWSを操作できるようになる
* AWSクレデンシャル情報の使用方法を理解する

---
## [AWS Command Line Interface (CLI)](https://aws.amazon.com/jp/cli/)

* コマンドでAWSにアクセスする公式ツール
* サーバ設定の自動化やスクリプトでよく使う
* OSSやサードパーティ製のツールでもこのAWS CLIがセットアップされている前提のものがあったりする
* EC2の`Amazon Linux AMI`にはデフォルトでインストールされている
* リファレンスは[こちら](http://docs.aws.amazon.com/cli/latest/reference/index.html#cli-aws)
* 結局のところAWSのWEB APIを読んでいるに過ぎないので、迷ったら各サービスのAPIリファレンス(e.g. [EC2](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/APIReference/Welcome.html))が参考になる事も多い

---
### 認証情報の設定
AWSのAPIを使用するには基本的にAWS認証(クレデンシャル)情報※が必要となる。(※≒アクセスキーIDとシークレットアクセスキーのこと)

AWS CLIはコマンド実行時、下記の順に認証情報を読み込む。

1. コマンドラインオプション
  * コマンド実行時にオプションでプロファイルを指定できる
  * ※プロファイル＝後述の`.aws/credentials`ファイルに記述される認証情報単位
2. 環境変数
  * `AWS_ACCESS_KEY_ID`、`AWS_SECRET_ACCESS_KEY` という環境変数が設定されていた場合はそれを使用
  * 設定できる環境変数：Ref. [Environment Variables](http://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-environment.html)
3. AWS 認証情報ファイル
  * ユーザのホームフォルダ以下に `.aws/credentials` というファイルがあればそれを読み込む
  * プロファイルという単位で名前を付けて複数記述しておける
  * 通常は`[default]`プロファイルが使用される

---
#### aws configure
AWS CLIの `aws configure` コマンドで認証情報ファイルを設定できる。

```bash
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: ap-northeast-1
Default output format [None]: json
```
※`Default output format` ＝コマンド出力結果の表示形式。`json`／`table`／`text`のいずれかで指定できる。(省略可)

`--profile`オプションでプロファイル名を指定可能。(無指定の場合は`default`扱い)

```bash
$ aws configure --profile user2
AWS Access Key ID [None]: AKIAI44QH8DHBEXAMPLE
AWS Secret Access Key [None]: je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
Default region name [None]: us-east-1
Default output format [None]: text
```

以下のような設定ファイルが生成されているはず＞

```properties:~/.aws/credentials
[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
[user2]
aws_access_key_id = AKIAI44QH8DHBEXAMPLE
aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
```

```properties:~/.aws/config
[default]
output = json
region = ap-northeast-1
[profile user2]
output = text
region = us-west-1
```

---
### AWSコマンドヘルプ
とりあえず困ったらヘルプ

```bash
$ aws help
```
＞最上位コマンド一覧が出る(≒サービス毎のコマンド)

---
試しにEC2コマンドのヘルプ

```bash
$ aws ec2 help
```
＞サブコマンド一覧が出る(≒EC2で操作可能なコマンド)

サブコマンド毎にもヘルプ

```bash
$ aws ec2 describe-instances help
```
＞やっとコマンドの使い方の詳細が出る(コマンドもオプションも多すぎ；；；)

---
#### 覚えておきたいコマンドオプション

* `--dry-run`
  * 変更系のリクエストで活躍
  * 実際のリソースは変更されず、リクエストの正当性だけを確認できる
  * aws以外でも良く見かけるオプション
* `--cli-input-json`
  * コマンドパラメータを記述したJSONを読み込む
  * AWS CLIのコマンドパラメータはだいたいJSONで渡せるようになっている
  * ファイルとして読み込むにはファイル名に`file://`を付ける
* `--generate-cli-skeleton`
  * そのコマンドに渡せるパラメータのJSONファイルの雛形を作成してくれる
  * 初めて使うコマンド＞
      1. helpでパラメータ確認
      1. `--generate-cli-skeleton`で雛形作成
      1. `--cli-input-json`で読込
      1. `--dry-run`しながら編集
      1. `Request would have succeeded, ～`と表示されたら`--dry-run`外す
      2. 実行＞`(*｀д´)b OK!`

---
#### [おまけ]

* [コマンド補完](http://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-command-completion.html)を設定可能
  * AWS CLIをよく使う場合は設定しておくと幸せになれるかも
* 現在[aws-shell](https://github.com/awslabs/aws-shell)なるものも開発中の模様

---
### AWSコマンドお試し
試しにコマンドでEC2インスタンスを立ち上げてみる

インスタンス作成は`aws ec2 run-instances`コマンドで行う
＞とりあえず`--dry-run`してみる

```bash
$ aws ec2 run-instances --dry-run
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help
aws.exe: error: argument --image-id is required
```
＞`--image-id`(AMIの指定)は必須

イメージIDは「EC2ダッシュボード」＞「イメージ」＞「AMI」画面などで検索
![2017-11-07_21h11_22.png](https://qiita-image-store.s3.amazonaws.com/0/104276/78f7bd2e-f072-5fbb-166d-649bc23a56f0.png)

・・・一応`describe-images`コマンドでも検索できるが、レスポンスがめちゃくちゃ遅い

```bash
$ aws ec2 describe-images --owners amazon --filters "Name=name,Values=amzn-ami-hvm-2017.09.1.20171103-x86_64-gp2" "Name=root-device-type,Values=ebs"
{
    "Images": [
        {
            "VirtualizationType": "hvm",
            "Name": "amzn-ami-hvm-2017.09.1.20171103-x86_64-gp2",
            "Hypervisor": "xen",
            "ImageOwnerAlias": "amazon",
            "EnaSupport": true,
            "SriovNetSupport": "simple",
            "ImageId": "ami-2803ac4e",
            "State": "available",
            "BlockDeviceMappings": [
                {
                    "DeviceName": "/dev/xvda",
                    "Ebs": {
                        "DeleteOnTermination": true,
                        "SnapshotId": "snap-00f63af2b938a9ed8",
                        "VolumeSize": 8,
                        "VolumeType": "gp2",
                        "Encrypted": false
                    }
                }
            ],
            "Architecture": "x86_64",
            "ImageLocation": "amazon/amzn-ami-hvm-2017.09.1.20171103-x86_64-gp2",
            "RootDeviceType": "ebs",
            "OwnerId": "137112412989",
            "RootDeviceName": "/dev/xvda",
            "CreationDate": "2017-11-03T23:21:03.000Z",
            "Public": true,
            "ImageType": "machine",
            "Description": "Amazon Linux AMI 2017.09.1.20171103 x86_64 HVM GP2"
        }
    ]
}
```

`--image-id`を追加して再度実行

```bash
$ aws ec2 run-instances --dry-run --image-id ami-2803ac4e

An error occurred (InvalidParameterCombination) when calling the RunInstances operation: Non-Windows instances with a virtualization type of 'hvm' are currently not supported for this instance type.
```
＞指定したAMI(の仮想化タイプ)とinstance typeの組み合わせが不正らしい
＞instance typeはデフォルトでは"m1.small"

"t2.micro"を指定して再実行

```bash
$ aws ec2 run-instances --dry-run --image-id ami-2803ac4e --instance-type t2.micro

An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
```
＞最低限のリクエストは設定できたようなので`--dry-run`を外して実行

```bash
$ aws ec2 run-instances --image-id ami-2803ac4e --instance-type t2.micro
{
    "OwnerId": "XXXXXXXXXXXX",
    "ReservationId": "r-XXXXXXXXXXXXXXXXX",
    "Groups": [],
    "Instances": [
        {
            "Monitoring": {
                "State": "disabled"
            },
            "PublicDnsName": "",
            "RootDeviceType": "ebs",
            "State": {
                "Code": 0,
                "Name": "pending"
            },
            "EbsOptimized": false,
            "LaunchTime": "2017-11-07T12:51:26.000Z",
            "PrivateIpAddress": "172.31.0.154",
            "ProductCodes": [],
            "VpcId": "vpc-XXXXXXXX",
            "StateTransitionReason": "",
            "InstanceId": "i-0eeef5f5fb27bad22",
            "ImageId": "ami-2803ac4e",
            "PrivateDnsName": "ip-172-31-0-154.ap-northeast-1.compute.internal",
            "SecurityGroups": [
                {
                    "GroupName": "default",
                    "GroupId": "sg-XXXXXXXX"
                }
            ],
            "ClientToken": "",
            "SubnetId": "subnet-XXXXXXXX",
            "InstanceType": "t2.micro",
            "NetworkInterfaces": [
                ...(省略)...
            ],
            "SourceDestCheck": true,
            "Placement": {
                "Tenancy": "default",
                "GroupName": "",
                "AvailabilityZone": "ap-northeast-1a"
            },
            "Hypervisor": "xen",
            "BlockDeviceMappings": [],
            "Architecture": "x86_64",
            "StateReason": {
                "Message": "pending",
                "Code": "pending"
            },
            "RootDeviceName": "/dev/xvda",
            "VirtualizationType": "hvm",
            "AmiLaunchIndex": 0
        }
    ]
}
```
＞無事起動

コンソール画面から確認してみる
![2017-11-07_22h12_46.png](https://qiita-image-store.s3.amazonaws.com/0/104276/afc6da90-d10b-561c-0d2e-3ed0119fa3c5.png)

---
インスタンスは作成できたが、このままでは最低限の設定しかされていない(キーペアも設定されてないのでSSHログインも出来ない)

再度必要な設定を追加して作り直してみる

先ほど作成したインスタンスを一旦削除

```bash
$ aws ec2 terminate-instances --instance-ids i-0eeef5f5fb27bad22
{
    "TerminatingInstances": [
        {
            "InstanceId": "i-0eeef5f5fb27bad22",
            "CurrentState": {
                "Code": 32,
                "Name": "shutting-down"
            },
            "PreviousState": {
                "Code": 16,
                "Name": "running"
            }
        }
    ]
}
```

`run-instances --generate-cli-skeleton`で設定できる項目を洗い出し

```bash
$ aws ec2 run-instances --generate-cli-skeleton >run-instances.json
```
```json:run-instances.json
{
    "DryRun": true, 
    "ImageId": "", 
    "MinCount": 0, 
    "MaxCount": 0, 
    "KeyName": "", 
    "SecurityGroups": [
        ""
    ], 
    "SecurityGroupIds": [
        ""
    ], 
    "UserData": "", 
    "InstanceType": "", 
    "Placement": {
        "AvailabilityZone": "", 
        "GroupName": "", 
        "Tenancy": "", 
        "HostId": "", 
        "Affinity": ""
    }, 
    "KernelId": "", 
    "RamdiskId": "", 
    "BlockDeviceMappings": [
        {
            "VirtualName": "", 
            "DeviceName": "", 
            "Ebs": {
                "SnapshotId": "", 
                "VolumeSize": 0, 
                "DeleteOnTermination": true, 
                "VolumeType": "", 
                "Iops": 0, 
                "Encrypted": true
            }, 
            "NoDevice": ""
        }
    ], 
    "Monitoring": {
        "Enabled": true
    }, 
    "SubnetId": "", 
    "DisableApiTermination": true, 
    "InstanceInitiatedShutdownBehavior": "", 
    "PrivateIpAddress": "", 
    "Ipv6Addresses": [
        {
            "Ipv6Address": ""
        }
    ], 
    "Ipv6AddressCount": 0, 
    "ClientToken": "", 
    "AdditionalInfo": "", 
    "NetworkInterfaces": [
        {
            "NetworkInterfaceId": "", 
            "DeviceIndex": 0, 
            "SubnetId": "", 
            "Description": "", 
            "PrivateIpAddress": "", 
            "Groups": [
                ""
            ], 
            "DeleteOnTermination": true, 
            "PrivateIpAddresses": [
                {
                    "PrivateIpAddress": "", 
                    "Primary": true
                }
            ], 
            "SecondaryPrivateIpAddressCount": 0, 
            "AssociatePublicIpAddress": true, 
            "Ipv6Addresses": [
                {
                    "Ipv6Address": ""
                }
            ], 
            "Ipv6AddressCount": 0
        }
    ], 
    "IamInstanceProfile": {
        "Arn": "", 
        "Name": ""
    }, 
    "EbsOptimized": true
}
```
＞必要な設定を残して削除
＞"KeyName"や"SecurityGroups"には『実践 #1』で作成したものなどを使用

```json:run-instances.json
{
    "DryRun": true, 
    "ImageId": "ami-2803ac4e", 
    "MinCount": 1, 
    "MaxCount": 1, 
    "KeyName": "temp-key-pair", 
    "SecurityGroups": [
        "launch-wizard-1"
    ], 
    "SecurityGroupIds": [
        "sg-XXXXXXXX"
    ], 
    "InstanceType": "t2.micro",
    "IamInstanceProfile": {
        "Arn": "arn:aws:iam::XXXXXXXXXXXX:instance-profile/aws-handson-ec2-ssm"
    }
}
```

```bash
$ aws ec2 run-instances --cli-input-json 'file://run-instances.json'

An error occurred (DryRunOperation) when calling the RunInstances operation: Request would have succeeded, but DryRun flag is set.
```
＞リクエストが通ることを確認出来たらJSON先頭の`"DryRun": true,`を削除して実行

![2017-11-07_23h10_02.png](https://qiita-image-store.s3.amazonaws.com/0/104276/e00085a3-e51b-1fad-da31-ff87601d01a3.png)

『実践 #1』同様SSHログイン出来るか確認

```bash
$ ssh -i "temp-key-pair.pem" ec2-user@ec2-XX-XXX-XXX-XXX.ap-northeast-1.compute.amazonaws.com
```
＞ついでにAmazon Linux内でAWS CLIが使用できることを試してみるのもあり

-----------------------------------------------------
# 実践 #3 ～サーバレス編～
-----------------------------------------------------
目標：

* サーバレスサービスに触れる
* Lambda + API Gateway + DynamoDB で REST API を立ててみる

---
## サーバレスアーキテクチャとは
* サーバを一切保有せず、フルマネージドなサービスのみを使用してシステムを構築すること
* マネージドサービス＝運用に必要なサーバ・OS・ミドルウェアなどの管理を全てクラウド事業者側が担うサービス

---
### AWS Lambda
* AWSのFaaS（Function as a Service）型のサーバレスサービス
* PaaSのようにアプリケーション単位ではなく、ファンクション(Lambda関数と呼ぶ)のみを登録して実行できる
* 実行環境のサーバやミドルウェアは意識しなくていい(というか利用者側は分からない)
* 関数実行のリクエストが来るとAWSが自動でサーバを起動＞関数コードをデプロイ＞実行する
  * 利用可能なインスタンスが既に起動されていた場合はそのままそれを使用する
* メリット
  * 料金はコードの実行に掛かった時間のみ(秒課金)
  * リクエストに合わせて自動でサーバのスケーリングが行われる
* デメリット
  * レスポンスが遅い
      * インスタンスが再利用されればそうでもないが、こちらからは制御できない
  * プログラミング言語や開発ツール・テスト方法などがLambdaに依存してしまう
  * 色々と面倒な[制限](http://docs.aws.amazon.com/ja_jp/lambda/latest/dg/limits.html)がある

---
### API Gateway
* APIエンドポイントの作成・管理サービス
* 認証機能もAWSがだいたい管理してくれる
  * IAMロール(AWSクレデンシャル)による認証が可能
  * APIキーの管理やリクエスト制限もAWS上で管理
* Swagger形式に対応
* メリット
  * バックエンドにAWSのサービスを指定可能
      * Lambdaなどのサーバレスサービスと容易に連携出来る
  * 自前でWEB&HTTPサーバを管理するよりも大分管理コストを減らせる
  * 料金はリクエスト数とデータ転送量による重量課金
* デメリット
  * こちらも色々と[制限](http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/limits.html)が多い

---
### DynamoDB
* AWSのNoSQLデータベースサービス
* フルマネージド
* 従量課金制


---
## サーバレスAPI作成
コンソールトップページに最低限の入力で Lambda + API Gateway + DynamoDB 構成のシンプルなCRUD操作を行うAPIを作成してくれるウィザードが用意されている

![2017-11-08_00h48_23.png](https://qiita-image-store.s3.amazonaws.com/0/104276/4b213800-c2a0-55f6-2551-bb04044f4b39.png)

![2017-11-08_00h54_24.png](https://qiita-image-store.s3.amazonaws.com/0/104276/fd9595d6-76b8-5c9a-e074-7245d3ce0e36.png)

---
・・・さすがにこれだけだと簡単すぎるので
---
同じ構成のものを各サービスのコンソールから手動で作成してみる

---
### DynamoDB テーブル作成
最初にLambda関数からアクセスするDynamoDBテーブルを作成する

サービス＞「DynamoDB」
![2017-11-08_03h27_50.png](https://qiita-image-store.s3.amazonaws.com/0/104276/c7ac8eb9-7774-4be2-ff25-6dbe86090c81.png)

「DynamoDB テーブルの作成」＞テーブル名とプライマリキー(文字列)を入力＞「デフォルト設定の使用」＞「作成」
![2017-11-08_03h31_45.png](https://qiita-image-store.s3.amazonaws.com/0/104276/52909880-d1db-8dfc-24c7-ffffbf837309.png)

「作成中」の表示が消えるまで待つ
![2017-11-08_03h32_28.png](https://qiita-image-store.s3.amazonaws.com/0/104276/7f19ded6-1938-cd87-a19d-1d831bc3a707.png)


---
### Lambda Function 作成
サービス＞「Lambda」
![2017-11-08_01h54_44.png](https://qiita-image-store.s3.amazonaws.com/0/104276/482e873e-671b-c00f-1ddb-fe25a01a9668.png)

「関数の作成」画面＞
今回は「一から作成」するが、画面下部に色々なサンプル(blueprint)が用意されている
（今回作成する関数は`microservice-http-endpoint`というblueprintとほぼ同様）
![2017-11-08_02h00_26.png](https://qiita-image-store.s3.amazonaws.com/0/104276/b6fe6c56-71aa-863a-85e9-af13a33757c6.png)

![2017-11-08_02h04_37.png](https://qiita-image-store.s3.amazonaws.com/0/104276/57278859-b25a-775f-5f6e-75f0922891e7.png)

* 名前：作成するFunction名を入力
* ロール：「テンプレートから新しいロールを作成」
* ロール名：作成するロール名を入力
* ポリシーテンプレート：プルダウンから「シンプルなマイクロハーネスのアクセス権限」を選択

「関数の作成」＞デフォルトコードのLambda関数が生成される
![2017-11-08_02h15_23.png](https://qiita-image-store.s3.amazonaws.com/0/104276/7346d0cf-9016-1799-198b-fbb50cd1c143.png)


インラインコードに下記を入力して保存

```js:index.js
'use strict';
console.log('Loading function');
const doc = require('dynamodb-doc');
const dynamo = new doc.DynamoDB();

exports.handler = (event, context, callback) => {
    console.log('Received event:', JSON.stringify(event, null, 2));

    const done = (err, res) => callback(null, {
        statusCode: err ? '400' : '200',
        body: err ? err.message : JSON.stringify(res),
        headers: {
            'Content-Type': 'application/json',
        },
    });

    switch (event.httpMethod) {
        case 'DELETE':
            dynamo.deleteItem(JSON.parse(event.body), done);
            break;
        case 'GET':
            dynamo.scan({ TableName: event.queryStringParameters.TableName }, done);
            break;
        case 'POST':
            dynamo.putItem(JSON.parse(event.body), done);
            break;
        case 'PUT':
            dynamo.updateItem(JSON.parse(event.body), done);
            break;
        default:
            done(new Error(`Unsupported method "${event.httpMethod}"`));
    }
};
```

---
#### 関数のテスト実行

「テストイベントの設定」
![2017-11-08_03h06_24.png](https://qiita-image-store.s3.amazonaws.com/0/104276/698dbe25-4c53-01cf-b766-3a926e210e23.png)

![2017-11-08_03h50_52.png](https://qiita-image-store.s3.amazonaws.com/0/104276/d7a2c745-8caf-e382-1ff4-bd454b2708a9.png)

* 「新しいテストイベントの作成」を選択
* イベントテンプレート：「API Gateway AWS Proxy」
* イベント名：任意

＞CRUD操作ごとにそれぞれ下記のようにJSONを書き換えて保存＞「テスト」実行

```json:CREATE
{
  "body": "{\"TableName\":\"ServerlessHandson\",\"Item\":{\"Test\":\"HandsonTest\"}}",
  "httpMethod": "POST"
}
```

```json:READ
{
  "queryStringParameters": {
    "TableName": "ServerlessHandson"
  },
  "httpMethod": "GET"
}
```

```json:UPDATE
{
  "body": "{\"TableName\":\"ServerlessHandson\",\"Item\":{\"Test\":\"HandsonTest\",\"Update\":1}}",
  "httpMethod": "PUT"
}
```

```json:DELETE
{
  "body": "{\"TableName\":\"ServerlessHandson\",\"Key\":{\"Test\":\"HandsonTest\"},\"ReturnValues\":\"ALL_OLD\"}",
  "httpMethod": "DELETE"
}
```

＞DynamoDBのコンソール画面から各操作の結果を確認
![2017-11-08_04h23_50.png](https://qiita-image-store.s3.amazonaws.com/0/104276/fbabeedf-2f56-a618-0e02-5e5283e0eb35.png)


---
### API Gateway 登録
API GatewayではSwagger定義ファイルが読み込めるので少々手抜きする

サービス＞「API Gateway」
![2017-11-08_01h32_47.png](https://qiita-image-store.s3.amazonaws.com/0/104276/cc7f9513-dc98-77e1-3be6-aa090f990a0e.png)

「新しい API の作成」画面に進み、「Swagger からインポート」＞下記のYAMLを入力して「インポート」
![2017-11-08_01h40_06.png](https://qiita-image-store.s3.amazonaws.com/0/104276/b2291039-45fb-ea3f-59c1-eb6f596b0b5a.png)
![2017-11-08_01h38_21.png](https://qiita-image-store.s3.amazonaws.com/0/104276/7ada9e2f-cdf4-a9f6-0f6c-a18324020654.png)

```yaml:serverlessHandsonAPI-swagger-apigateway.yaml
swagger: "2.0"
info:
  title: "serverlessHandsonAPI"
schemes:
- "https"
paths:
  /serverlessHandsonFunction:
    x-amazon-apigateway-any-method:
      responses:
        200:
          description: "200 response"
      security:
      - api_key: []
      x-amazon-apigateway-integration:
        responses:
          .*:
            statusCode: "200"
        uri: "arn:aws:apigateway:ap-northeast-1:lambda:path/2015-03-31/functions/arn:aws:lambda:ap-northeast-1:{アカウントID}:function:serverlessHandsonFunction/invocations"
        passthroughBehavior: "when_no_match"
        httpMethod: "POST"
        type: "aws_proxy"
securityDefinitions:
  api_key:
    type: "apiKey"
    name: "x-api-key"
    in: "header"
```
※アカウントIDなどは適宜変更

---
#### Lambda関数実行権限付与
API Gatewayのリソースを作成しただけでは呼び出し先のLambdaを実行する権限が与えられていないので付与する。

API「リソース」＞「ANY」メソッド選択＞「統合リクエスト」
![2017-11-08_02h36_32.png](https://qiita-image-store.s3.amazonaws.com/0/104276/583ce3e9-919d-5df0-53e6-362373232425.png)

「Lambda 関数」欄の横の編集アイコンをクリック
![2017-11-08_02h41_20.png](https://qiita-image-store.s3.amazonaws.com/0/104276/3d1fbdd7-3e3b-184c-633b-b1766bdae354.png)
⇒そのまま値は変更せず「:heavy_check_mark:」をクリック
![2017-11-08_02h40_04.png](https://qiita-image-store.s3.amazonaws.com/0/104276/d352f79a-9157-9497-195d-ec07d56600b6.png)

下記のようなダイアログが出る＞「OK」
![2017-11-08_02h43_33.png](https://qiita-image-store.s3.amazonaws.com/0/104276/b3663056-5ca1-0e1d-7629-3bb470274488.png)

＞LambdaFunctionにAPI Gatewayからのトリガーとポリシーが追加される
![2017-11-08_02h30_40.png](https://qiita-image-store.s3.amazonaws.com/0/104276/227114e2-e244-a208-f7d2-ce1fc9ac4257.png)

---
#### APIデプロイ
「API」＞「リソース」＞「ANY」メソッドを選択＞「アクション」＞「APIのデプロイ」
![2017-11-08_04h35_24.png](https://qiita-image-store.s3.amazonaws.com/0/104276/922572bb-617a-4319-9e65-ead26f0cb100.png)

[新しいステージ]＞任意のステージ名を入力＞「デプロイ」
![2017-11-08_04h37_58.png](https://qiita-image-store.s3.amazonaws.com/0/104276/0eb1cb5e-2eb0-2ad4-4fb8-560065ef0591.png)

APIがデプロイされ、青枠の欄のURLから実際にアクセスが可能となる
![2017-11-08_04h38_54.png](https://qiita-image-store.s3.amazonaws.com/0/104276/5def6706-a81e-2344-4248-c6b613bf03f9.png)
＞が、そのままではAPIキーが設定されていないので正当なリクエストが出来ない（＜リソース作成時のSwaggerでAPIキー認証を必須にした為）

---
#### APIキーの設定
最初にAPIキーと紐づける使用量プランを作成する
![2017-11-08_04h56_44.png](https://qiita-image-store.s3.amazonaws.com/0/104276/2286cabe-a4e1-6e6c-34ef-c2aa8b491b80.png)

* スロットリング：[トークンバケット](https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%BC%E3%82%AF%E3%83%B3%E3%83%90%E3%82%B1%E3%83%83%E3%83%88)アルゴリズムによるリクエスト制限
  * レート：1秒間に実行できるリクエスト数（平均）
  * バースト：トークンバケットの上限数
* クォータ：指定期間(日／週／月)に実行できるリクエスト数 

⇒テスト用なので適当な値で「次へ」

使用量プランと先ほどデプロイした「APIステージ」を紐づける
![2017-11-08_05h02_21.png](https://qiita-image-store.s3.amazonaws.com/0/104276/3b6804d1-f5b1-b0d9-061a-86c2e122d8c5.png)

次にAPIキーを作成する
「APIキーを作成して使用量プランに追加」をクリック
![2017-11-08_05h04_35.png](https://qiita-image-store.s3.amazonaws.com/0/104276/07410324-99a2-0660-2ac8-72b5d53aab59.png)
＞APIキー作成ダイアログが表示される
![2017-11-08_05h06_00.png](https://qiita-image-store.s3.amazonaws.com/0/104276/619d1c75-c2ca-c184-d368-eda5467962b5.png)

* 名前：任意の識別名を入力
* APIキー：
  * 自動生成＝ランダム生成された文字列がキーに設定される
  * カスタム＝任意の値を入力できる
* 説明：任意

作成したAPIキーが使用量プランに追加される
![2017-11-08_05h06_50.png](https://qiita-image-store.s3.amazonaws.com/0/104276/36ef3611-6c0f-6afd-9a94-f99070854e73.png)

作成した「APIキー」は下記ページから確認できる
![2017-11-08_05h08_13.png](https://qiita-image-store.s3.amazonaws.com/0/104276/3db2911f-4c28-24f4-6ca4-449e6ec52830.png)
「APIキー」＞「表示」リンクをクリックすると、実際のAPIリクエストで使用する値が表示される


---
### API呼び出し
APIキーも設定できたので、今度こそ実際にAPIを実行できる

＞CRUD操作ごとに下記curlコマンドを実行
※`{APIキー}`、`{アカウントID}`、`{リージョン}` は適宜適切な値に修正

```bash:CREATE
$ curl -i -X POST \
   -H "x-api-key:{APIキー}" \
   -H "Content-Type:application/json" \
   -d \
'{"TableName":"ServerlessHandson","Item":{"Test":"HandsonTest"}}' \
 'https://{アカウントID}.execute-api.{リージョン}.amazonaws.com/prod/serverlessHandsonFunction'
```

```bash:READ
$ curl -i -X GET \
   -H "x-api-key:{APIキー}" \
 'https://{アカウントID}.execute-api.{リージョン}.amazonaws.com/prod/serverlessHandsonFunction?TableName=ServerlessHandson'
```

```bash:UPDATE
$ curl -i -X PUT \
   -H "x-api-key:{APIキー}" \
   -H "Content-Type:application/json" \
   -d \
'{"TableName":"ServerlessHandson","Item":{"Test":"HandsonTest","Update":1}}' \
 'https://{アカウントID}.execute-api.{リージョン}.amazonaws.com/prod/serverlessHandsonFunction'
```

```bash:DELETE
$ curl -i -X DELETE \
   -H "x-api-key:{APIキー}" \
   -H "Content-Type:application/json" \
   -d \
'{"TableName":"ServerlessHandson","Key":{"Test":"HandsonTest"},"ReturnValues":"ALL_OLD"}' \
 'https://{アカウントID}.execute-api.{リージョン}.amazonaws.com/prod/serverlessHandsonFunction'
```

＞DynamoDBコンソールから結果確認

以上


---
### [Trivial Hands-on]
余裕がある人は下記課題にチャレンジ！

#### [#1] 日次AWS料金サーバレス通知
* AWSアカウントの課金料を毎日定時にSlackなどに通知
  * 日毎に把握できるとなんとなく安心感が増す
* Lambda活用のサンプル事例として〇
  * 他サービスとの連携
  * スケジューリング起動
* 参考記事：
  * [Lambdaのサンプルを使用](https://dev.classmethod.jp/cloud/aws/lambda-blueprint-slack-cloudwatch/)
  * [サービス毎に明細表示](https://qiita.com/ishikun/items/90b766e5555421970e9f)
  * [CLIで簡単？導入(Python)](https://qiita.com/tomohiko_isobe/items/88e8e0dcb0ee224a31e4)

#### [#2] [Serverless Framework](https://serverless.com) (slsコマンド)
* Lambdaや連携する周辺サービスを纏めてコード管理し、コマンド一発でデプロイ出来るツール
  * GCFなどAWS以外のサーバレスサービスにも対応
  * 適当なサンプルを実際にデプロイして動かしてみると良い＞https://github.com/serverless/examples
* 他にも似たようなツールある(ApexとかLamveryとか)ので、お好みでお試しを
* 参考記事：
  * https://qiita.com/bohebohechan/items/fdf6815b1203f67fd2b7



-----------------------------------------------------
# Special Thanks
当資料からリンクを貼らせて頂いた記事＞

  * https://dev.classmethod.jp/cloud/aws/lambda-blueprint-slack-cloudwatch/
  * https://qiita.com/ishikun/items/90b766e5555421970e9f
  * https://qiita.com/tomohiko_isobe/items/88e8e0dcb0ee224a31e4
  * https://qiita.com/bohebohechan/items/fdf6815b1203f67fd2b7