CLI で学ぶAWSでのネットーワーク＆サーバー構築

## 概要

### 背景と目的

AWSの入門としてよく挙げられる[「Amazon Web Services 基礎からのネットワーク＆サーバー構築」](https://www.nikkeibp.co.jp/atclpubmkt/book/17/261530/)を読んで、AWSマネージメントコンソール（GUI）から操作をして概要を理解しました。しかし、自分の頭の中にしっかりと定着したかというと、操作した経験が不足しており理屈を理解した程度だったので、今度はCLIから操作をしてより発展的なアークテクチャを設計することにしました。

ここでは、AWS CLI を使ってコマンドライン上からAWSを操作して、最終的にはNginxでS3にプロキシし、静的コンテンツを表示します。

個人的にはAWSマネージメントコンソールの画面を見ながらの説明だと、GUIのボタンをとりあえず押せば実現できるので、初学者には適切だと思います。

一方で、概要を知った上でCLIから操作すると、明示的にAWSサービスとの関係をコマンド引数に渡す必要があり、それぞれのAWSサービス間の関係や状態を決める属性などがより明らかになりました。また、コマンドを覚えることでGUI上の操作よりもはやく目的の操作が可能になったり、コマンドを組み合わせてスクリプトを作ったりできる利点もあります。

そのため以下のような方を対象に記事を書いています。

- AWSのVPCやサブネット、S3などの概要はなんとなくわかる
- 概要は理解したが、実際にAWSサービスを組み合わせてアーキテクチャを設計できるほどではない
- 実際に手を動かしてAWSについて理解したい
- AWS CLI を使ってAWSサービスを操作したい

### 目標となるアーキテクチャ

最終的にはEC2上のNginxが外部からのリクエストを受け付けて、S3にプロキシし、S3に配置した静的コンテンツを取得できるようにします。
そのため、以下のようなアークテクチャーをCLIからAWS上に作ることになります。

![スクリーンショット 2019-02-17 20.40.02.png](https://qiita-image-store.s3.amazonaws.com/0/68018/d6a0f228-c8a4-f5bf-5328-7506c938326d.png)


これらを実現するためには、以下のような操作をAWS CLI から行うことになります。

- VPCの作成
- サブネットの作成
- インターネットゲートウェイとルートテーブルの作成
- EC2インスタンスの作成と外部ネットワークへの公開
- EC２インスタンスのセキュリティグループへの所属
- S3バケットの作成とポリシーの付与
- EC2インスタンスからS3へアクセスできるようにVPCエンドポイントの作成

## AWS CLI の接続設定

AWS CLI を利用するには、[こちら](https://docs.aws.amazon.com/ja_jp/streams/latest/dev/kinesis-tutorial-cli-installation.html)からインストールが必要です。

次にAWS CLI が対象のAWSサービスに通信できるように接続設定をする必要があります。
これには、アクセスキーを作成し、接続時に参照するようにします。
アクセスキーは、アクセスキーIDとシークレットキーから成り、まだCLIの設定をしていないので、AWSマネジメントコンソールから作成することにします。

まず、[ここ](https://console.aws.amazon.com/iam/home#/users)からIAMユーザーを作成します。

<img width="1182" alt="スクリーンショット 2019-02-17 17.56.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/68018/4e3efe6a-25c4-eeef-c7cd-07175c80ba16.png">

作成する際に、そのIAMユーザーに権限（IAMポリシー）を付与(アタッチ)します。
ここでは学習を目的とするので、AWSアカウントのルートユーザーと同等の管理者権限を付与します。
これで、接続設定ができれば、AWSマネージメントコンソールでできていたことがCUIからでも実行可能になります。

<img width="1154" alt="スクリーンショット 2019-02-17 17.59.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/68018/226e32ff-9f80-e203-b022-86f3b60f1aa1.png">

作成するとアクセスキーIDとシークレットキーが生成されます。これを控えておきます。

では、先ほど生成したアクセスキーIDとシークレットキーを使って接続設定をしましょう。
c.f https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-configure-files.html

`~/.aws/credentials`がデフォルトのCLIが読み込む認証情報のファイルなので、ここに先ほどのアクセスキーを記述します。

```shell
[default]
aws_access_key_id=AAaAAAAAAAAAAAAAAAAAAAAAAAAAA
aws_secret_access_key=AAAAAAAAAaAAAAAaaBBBBbCCCC
```

また、`~/.aws/config`に接続先のリージョンや出力形式を指定しておきます。

```shell
[default]
region = ap-northeast-1
output = json
```

CLIの認証情報の設定については[この記事](https://dev.classmethod.jp/cloud/aws/how-to-configure-aws-cli/)が分かりやすかったです。
公式Documentだと[こちら](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-configure.html)で説明されています。

以下のコマンドで、接続に利用されているアカウント情報（IAM identity）が取得できれば接続できています。

```shell
$ aws sts get-caller-identity
{
    "UserId": "ABCDFGHIJKLMN",
    "Account": "1111111111111",
    "Arn": "arn:aws:iam::12345678:user/user1"
}
```


## VPCとサブネットの作成

### VPCの作成

![スクリーンショット 2019-02-17 20.15.13.png](https://qiita-image-store.s3.amazonaws.com/0/68018/40fd1c06-53bc-2296-b76c-43d55b0b9e4b.png)

まずはVPC（Virtual Private Cloud）を作成します。VPCとは、以下の説明がわかりやすいです。
c.f https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/what-is-amazon-vpc.html

> Virtual Private Cloud (VPC) は、AWS アカウント専用の仮想ネットワークです。VPC は、AWS クラウドの他の仮想ネットワークから論理的に切り離されており、AWS のリソース（例えば Amazon EC2 インスタンス）を VPC 内に起動できます。VPC の IP アドレス範囲を指定して、サブネットを追加し、セキュリティグループを関連付けて、ルートテーブルを設定できます。

VPCは上述の通り、IPアドレスの範囲を指定して、その範囲でネットワークを更に区切り、サーバーを立ち上げたりすることができます。
IPアドレスの範囲はCIDRブロックを指定する必要があります。ここでは`10.0.0.0/16`と指定します。
これによってネットワーク部は16ビットなので、`10.0.0.0`~`10.0.255.255`の範囲がVPC内に割り振られました。

```shell
$ aws ec2 create-vpc --cidr-block 10.0.0.0/16
```

c.f https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-subnets-commands-example.html

実際にVPCが作成されたか確認してみましょう。

```shell
$ aws ec2 describe-vpcs --filters Name=cidr,Values=10.0.0.0/16
{
    "Vpcs": [
        {
            "CidrBlock": "10.0.0.0/16",
            "DhcpOptionsId": "dopt-41256826",
            "State": "available",
            "VpcId": "vpc-03b2f9f9f97a38c41",
            "InstanceTenancy": "default",
            "CidrBlockAssociationSet": [
                {
                    "AssociationId": "vpc-cidr-assoc-05b4f8516eccd0c8c",
                    "CidrBlock": "10.0.0.0/16",
                    "CidrBlockState": {
                        "State": "associated"
                    }
                }
            ],
            "IsDefault": false
        }
    ]
}
```

ここで作ったVPCのIDは今後のコマンド引数に利用されることが多いので環境変数に格納します。

```shell
$ VPCID="vpc-03b2f9f9f97a38c41"
```

### パブリックサブネットの作成

![スクリーンショット 2019-02-17 20.17.24.png](https://qiita-image-store.s3.amazonaws.com/0/68018/0a2f4ccd-2c84-536b-5796-ad8299600751.png)

次にサブネットを作成します。サブネットとは、以下の説明がわかりやすいです。

c.f https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/what-is-amazon-vpc.html
> サブネットは、VPC の IP アドレスの範囲です。AWS リソースは、指定したサブネット内に起動できます。インターネットに接続する必要があるリソースにはパブリックサブネットを、インターネットに接続しないリソースにはプライベートサブネットを使用してください。

要はVPCのIPアドレスの範囲を分割したものがサブネットです。分割することでそれぞれのサブネットに用途に応じたルーティングをすることができます。
ここでは、VPCのCIDRブロックを`10.0.0.0/16`としましたが、それを更に区切って`10.0.1.0/24`とします。
これによってこのサブネットでは`10.0.1.0` ~ `10.0.1.255`の範囲でIPアドレスを利用することが可能になります。

```shell
$ aws ec2 create-subnet --vpc-id $VPCID --cidr-block 10.0.1.0/24
{
    "Subnet": {
        "AvailabilityZone": "ap-northeast-1a",
        "AvailableIpAddressCount": 251,
        "CidrBlock": "10.0.1.0/24",
        "DefaultForAz": false,
        "MapPublicIpOnLaunch": false,
        "State": "pending",
        "SubnetId": "subnet-04dbde66b2861c4bb",
        "VpcId": "vpc-03b2f9f9f97a38c41",
        "AssignIpv6AddressOnCreation": false,
        "Ipv6CidrBlockAssociationSet": []
    }
}
```

ここで取得したSubnetIdはこれからのコマンド引数に必要になるため、環境変数に格納しておきます。

```shell
$ SUBNETID="subnet-04dbde66b2861c4bb"
```

## インスタンスへSSHする

サブネットが作成されたのでその中にインスタンスを立ち上げます。
インスタンスとは仮想サーバーのことです。

ここの説明は[公式Document](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-chap-tutorial.html)でもされているので参考になります。
そこにもあるように、作成するには以下の前提条件が必要になります。

1. セキュリティグループ
2. キーペア

セキュリティグループとは、仮想ファイアウォールでインスタンスへの通信を送信元や送信先、ポートなどで制限することができるサービスです。
c.f https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-network-security.html

キーペアとは、インスタンスへアクセスするための公開鍵と秘密鍵のセットのことで、接続元に秘密鍵を配置し、インスタンスに公開鍵を登録します。
c.f https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-key-pairs.html

![スクリーンショット 2019-02-17 20.23.00.png](https://qiita-image-store.s3.amazonaws.com/0/68018/436034e8-e1b5-c45d-783c-395da80a3b36.png)

### セキュリティグループを作る

まずはセキュリティグループを作成します。
セキュリティグループは、インスタンス「へ」の通信許可（ingress）とインスタンス「から」の通信許可（engress）をそれぞれ設定できます。
それらの設定では、プロトコル（TCPやUDPなど）、ポート範囲、ingressの場合は送信元、engressの場合は送信先などを指定します。

ここでは、ingressの設定のみ行い、SSHでの22ポートとHTTPによる80ポートへの通信を送信元に関わらず許可します。

```shell
$ aws ec2 create-security-group --group-name SSHAccess --description "Security group for SSH access" --vpc-id $VPCID
{
    "GroupId": "sg-0dc191eb2b56d2ffc"
}
$ aws ec2 authorize-security-group-ingress --group-id sg-0dc191eb2b56d2ffc --protocol tcp --port 22 --cidr 0.0.0.0/0
$ aws ec2 authorize-security-group-ingress --group-id sg-0dc191eb2b56d2ffc --protocol tcp --port 80 --cidr 0.0.0.0/0
```

上記のコマンドではVPCのIDを引数に渡す必要があり、セキュリティグループはVPCごとに存在することがわかります。
そしてこの後見ていくように、そのセキュリティグループをインスタンスが参照するような関係になります。

ちなみに、後述するようにルートテーブルもVPCごとに存在し、それをサブネットが参照するような関係になり、引数の渡し方からAWSサービスの関係性が読み取れます。

では、実際にセキュリティグループが作成されたか確認してみましょう。
この後でも利用するため、セキュリティグループのIDを環境変数に格納しておきます。

```shell
$ SGID="sg-0dc191eb2b56d2ffc"
```

以下のように二つのルールが作成されたことが分かります。

```shell
$ aws ec2 describe-security-groups --group-ids $SGID --query 'SecurityGroups[*].IpPermissions'
[
    [
        {
            "FromPort": 80,
            "IpProtocol": "tcp",
            "IpRanges": [
                {
                    "CidrIp": "0.0.0.0/0"
                }
            ],
            "Ipv6Ranges": [],
            "PrefixListIds": [],
            "ToPort": 80,
            "UserIdGroupPairs": []
        },
        {
            "FromPort": 22,
            "IpProtocol": "tcp",
            "IpRanges": [
                {
                    "CidrIp": "0.0.0.0/0"
                }
            ],
            "Ipv6Ranges": [],
            "PrefixListIds": [],
            "ToPort": 22,
            "UserIdGroupPairs": []
        }
    ]
]
```

### キーペアを作る

次に、以下のコマンドでキーペアを作成し、PrivateKeyを標準出力させたものを、ローカルの`MyKeyPair.pem`ファイルに保存します。
そして、管理者に読み取り権限だけ付与します。

```shell
$ aws ec2 create-key-pair --key-name MyKeyPair --query 'KeyMaterial' --output text > MyKeyPair.pem
$ chmod 400 MyKeyPair.pem
```

### インスタンスの作成

セキュリティグループとキーペアを作成したので、実際にインスタンスを作成します。
インスタンスとして利用するEC2 AIMを指定する必要があるのですが、これはAWSマネジメントコンソールから確認した方が分かりやすいです。
https://console.aws.amazon.com/ec2/ から「インスタンスの作成」を押すとAMIの一覧が確認でき、それぞれにIDがあることが確認できます。

<img width="1190" alt="スクリーンショット 2019-02-17 18.42.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/68018/5f9905e7-e82d-f757-0729-3e526c2f7991.png">

ここでは、`ami-0d7ed3ddb85b521a6`のIDである無償利用枠のAmazon Linuxを利用します。
以下のコマンドで、先ほど作成したキーペアの名前、セキュリティグループ、サブネットを指定してインスタンスを作成します。

```shell
$ aws ec2 run-instances --image-id ami-0d7ed3ddb85b521a6 --count 1 --instance-type t2.micro --key-name MyKeyPair --security-group-ids $SGID --subnet-id $SUBNETID
```

作成したインスタンスのパブリックIDアドレスを確認します。

```shell
$ aws ec2 describe-instances --filters Name=subnet-id,Values=$SUBNETID --query 'Reservations[*].Instances[*].PublicIpAddress'
[
    [
        "13.231.142.47"
    ]
]
```

これを利用して作成したインスタンスへローカルにあるPrivateKeyを利用し、SSHで入ります。
これは既にセキュリティグループの設定でSSHによる22ポートへのアクセスを許可しているため可能になっています。

```shell
$ ssh -i MyKeyPair.pem ec2-user@13.231.142.47
Warning: Permanently added '13.231.142.47' (ECDSA) to the list of known hosts.

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

https://aws.amazon.com/amazon-linux-2/
3 package(s) needed for security, out of 3 available
Run "sudo yum update" to apply all updates.
```

### DNSの設定

先ほどのセキュリティグループの設定でHTTPによる80番ポートへの通信も受け付けるようにしたので、パブリックIPアドレスを指定してHTTPで通信することが可能になりました。（80番ポートで待ち受けるアプリケーションはまだインストールしていません。）

しかし数字の羅列であるIPアドレスは視認性が低いのでドメイン名を用いてアクセスできるようにしておきます。
これをインスタンスに持たせるには、以下のVPCの属性をtrueにする必要があります。

c.f https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-dns.html#vpc-dns-viewing

- enableDnsSupport: VPCのDNSサーバーを有効にするかどうか
- enableDnsHostnames: VPC内に起動されるインスタンスがDNSホスト名を取得するかどうか

実際に以下のコマンドでtrueに更新すると、ドメイン名が付与されていることが分かります。

```shell
$ aws ec2 modify-vpc-attribute --vpc-id $VPCID --enable-dns-support "{\"Value\":true}"
$ aws ec2 modify-vpc-attribute --vpc-id $VPCID --enable-dns-hostname "{\"Value\":true}"
$ aws ec2 describe-instances --query 'Reservations[0].Instances[*].PublicDnsName'
[
    "ec2-13-231-142-47.ap-northeast-1.compute.amazonaws.com"
]
$ curl http://ec2-13-231-142-47.ap-northeast-1.compute.amazonaws.com
```

## インスタンスからインターネットの接続する

インスタンスへSSHやHTTPで通信することが可能になりましたが、インスタンスからインターネットにアクセスすることはできません。
理由はVPC外部のネットワークへのパケットの送受信をするインターネットゲートウェイの作成と、そことサブネット内のインスタンスを繋ぐためのルートテーブルの作成がされていないからです。

### ルーティングをする

そのため、やるべきことは以下の三つです。

- VPC内にインターネットゲートウェイを作る
- サブネット内にルートテーブルを作成する
- ルートテーブルとインターネットゲートウェイがパケットを送受信できるようにする

まず、インターネットゲートウェイを作ります。

```shell
$ aws ec2 create-internet-gateway
{
    "InternetGateway": {
        "Attachments": [],
        "InternetGatewayId": "igw-0f8e69039f7ffbe33",
        "Tags": []
    }
}
```

そのインターネットゲートウェイをVPCに関連付けます。

```shell
$ aws ec2 attach-internet-gateway --vpc-id $VPCID --internet-gateway-id igw-0f8e69039f7ffbe33
```

![スクリーンショット 2019-02-17 20.24.26.png](https://qiita-image-store.s3.amazonaws.com/0/68018/a7dc2d61-21bb-d239-0478-e67ae4c32de5.png)

次にルートテーブルを作成します。

```shell
$ aws ec2 create-route-table --vpc-id $VPCID
{
    "RouteTable": {
        "Associations": [],
        "PropagatingVgws": [],
        "RouteTableId": "rtb-0484c8a5f9983fabe",
        "Routes": [
            {
                "DestinationCidrBlock": "10.0.0.0/16",
                "GatewayId": "local",
                "Origin": "CreateRouteTable",
                "State": "active"
            }
        ],
        "Tags": [],
        "VpcId": "vpc-03b2f9f9f97a38c41"
    }
}
```

上記のようにデフォルトでは`10.0.0.0/16`というVPC内のネットワークの範囲の宛先の場合は、VPC内のルーター（`local`）に転送されるようになっています。
つまり、現在のルートテーブルは以下のようになっています。

|宛先のCIDRブロック|送られるルーター|
|:-----|:-----|
|10.0.0.0/16|local|


このままでは、VPC内以外の宛先の指定をすると、そのパケットは全て破棄されてしまいます。
そのため、上記以外の宛先（`0.0.0.0/0`）は全てンターネットゲートウェイへを向けるルートをルートテーブルに追加します。

```shell
$ aws ec2 create-route --route-table-id rtb-0484c8a5f9983fabe --destination-cidr-block 0.0.0.0/0 --gateway-id igw-0f8e69039f7ffbe33
{
    "Return": true
}
```

|宛先のCIDRブロック|送られるルーター|
|:-----|:-----|
|10.0.0.0/16|local|
|0.0.0.0/0|igw-0f8e69039f7ffbe33|


```shell
$ aws ec2 describe-route-tables --route-table-id rtb-0484c8a5f9983fabe --query 'RouteTables[*].Routes'
[
    [
        {
            "DestinationCidrBlock": "10.0.0.0/16",
            "GatewayId": "local",
            "Origin": "CreateRouteTable",
            "State": "active"
        },
        {
            "DestinationCidrBlock": "0.0.0.0/0",
            "GatewayId": "igw-0f8e69039f7ffbe33",
            "Origin": "CreateRoute",
            "State": "active"
        }
    ]
]
```


しかし、このままでは、サブネットと作成したルートテーブルが関連付けられていないので、サブネットからのトラフィックはこのルートテーブルで処理されません。
そのため、サブネットとルートテーブルを関連付けます。これによって、このサブネットからのトラフィックは関連付けられたルートテーブルを利用するようになります。

```shell
$ aws ec2 associate-route-table --subnet-id $SUBNETID --route-table-id rtb-0484c8a5f9983fabe
{
    "AssociationId": "rtbassoc-024fbd16933a7171e"
}
```

![スクリーンショット 2019-02-17 20.29.29.png](https://qiita-image-store.s3.amazonaws.com/0/68018/02d3963a-93d9-dc3d-32a0-902aa89d0462.png)

更に、サブネット内で起動されたインスタンスがパブリックIPアドレスに自動的で更新されるようにします。

```shell
$ aws ec2 modify-subnet-attribute --subnet-id $SUBNETID --map-public-ip-on-launch
```

これで、インスタンスからVPC外部のネットワークへ通信することができるようになりました。
実際に、インスタンスへSSHで入って、外部のネットワークへ通信してみます。

```shell
$ ssh -i MyKeyPair.pem ec2-user@13.231.142.47
$ curl https://aws.amazon.com/jp/
```

## NginxでS3にプロキシする

これで、インスタンスからインターネットに接続できるようになったのでNginxをインストールすることができます。
そのあと、以下の手順で操作していきます。

- Nginxのインストール
- S3のバケットの作成
- S3のポリシーの付与
- VPCエンドポインtの作成
- サブネットのルートテーブルへ追加
- Nginxのプロキシの設定


### Nginxのインストールと起動

SSHしてインタンスのコンソールに入り、Nginxをインストールします。

```shell
$ ssh -i MyKeyPair.pem ec2-user@13.231.142.47
$ amazon-linux-extras list | grep nginx
  4  nginx1.12=latest         enabled      [ =1.12.2 ]
$ sudo amazon-linux-extras install nginx1.12
```

そして、Nginxを起動します。

```shell
$ sudo systemctl start nginx.service
```

インスタンスからリクエストを送るとNgixのデフォルトのページが表示されることが確認できます。

```shell
$ curl http://localhost
...
```

### S3の作成

![スクリーンショット 2019-02-17 20.31.05.png](https://qiita-image-store.s3.amazonaws.com/0/68018/eba609fd-5b04-1e8e-3a5a-4a0f42fb2d86.png)

S3とは、AWSのストレージサービスです。他には静的ウェブサイトのホスティング機能も有します。
そしてS3に先ほど作成したインスタンスからアクセスし、S3にあるアセットを取得するようにします。

S3とインスタンスの通信にはVPCエンドポイントを用います。

c.f https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/vpc-endpoints.html
c.f https://dev.classmethod.jp/cloud/vpc-endpoint-for-s3/

> VPC エンドポイントでは、PrivateLink を使用する AWS サービスや VPC エンドポイントサービスに VPC をプライベートに接続できます。インターネットゲートウェイ、NAT デバイス、VPN 接続、または AWS Direct Connect 接続は必要ありません。VPC のインスタンスは、サービスのリソースと通信するためにパブリック IP アドレスを必要としません。VPC と他のサービス間のトラフィックは、Amazon ネットワークを離れません。

これによって、VPC外部のあるAWSサービスへAWSのネットワークを介してアクセスすることが可能になります。
もちろんインターネットゲートウェイ経由でVPC外のAWSサービスと通信することもできますが、その場合よりAWS内のネットワークで通信が閉じるのでセキュアだったり、Privateなサブネットから外部ネットワークに通信するためにNATサーバーを構築する必要がなくなるなどの利点があります。

まず、S3にバケットを作成しましょう。リージョンは同じにしておく必要があります。

```shell
$ aws s3api create-bucket --bucket my-nginx-bucket --region ap-northeast-1 --create-bucket-configuration LocationConstraint=ap-northeast-1
{
    "Location": "http://my-nginx-bucket.s3.amazonaws.com/"
}
```

更に、そのバケットにポリシーを付与して指定のVPCからのみ読み取り専用にします。
c.f https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-bucket-policies-vpc-endpoint.html

```shell
$ cat << EOF > policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "GetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-nginx-bucket/*",
      "Condition": {
        "StringEquals": {
          "aws:sourceVpc": $VPCID 
        }
      }
    }
  ]
}
EOF

$ aws s3api put-bucket-policy --bucket my-nginx-bucket --policy file://policy.json
```

そして、表示するためのHTMLを配置します。

```shell
$ cat << EOF > sample.html
<html>
  <head>
    <title>タイトル</title>
  </head>
  <body>
    aws cli の練習
  </body>
</html>
EOF
$ aws s3 cp sample.html s3://my-nginx-bucket/
upload: ./sample.html to s3://my-nginx-bucket/sample.html
```

ポリシーを指定のVPCからのみ読み取り可能にしたので、ローカルから通信できないことが確認できます。

```shell
$ curl https://s3-ap-northeast-1.amazonaws.com/my-nginx-bucket/sample.html
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>0FFEB150E2721710</RequestId><HostId>fjGav1kPhVwtD6DZxBqiMW6YWwQkYz3xSN7YyVsbEANd7YC+io+u+1DXfKOaWf/jKw8ZbSGr+lE=</HostId></Error>%
```

### インスタンスからS3に接続する

![スクリーンショット 2019-02-17 20.34.12.png](https://qiita-image-store.s3.amazonaws.com/0/68018/408be3b1-a64a-5ae5-1f93-5f683a49fe69.png)

次に、作成したS3バケットにVPCから接続できるようにエンドポイントを作成しましょう。
そして、サブネットが利用するルートテーブルからそのエンドポイントへトラフィックを向けます。

以下のコマンドで、対象のサブネットに関連付けられたルートテーブルのIDとVPCのIDを利用して、エンドポイントを作成します。

```shell
$ aws ec2 describe-route-tables --query 'RouteTables[*].RouteTableId' --filter Name=association.subnet-id,Values=$SUBNETID
[
    "rtb-0484c8a5f9983fabe"
]
$ aws ec2 create-vpc-endpoint --vpc-id $VPCID --service-name com.amazonaws.ap-northeast-1.s3 --route-table-ids rtb-0484c8a5f9983fabe
{
    "VpcEndpoint": {
        "VpcEndpointId": "vpce-0663e4cf6645ddfd0",
        "VpcEndpointType": "Gateway",
        "VpcId": "vpc-03b2f9f9f97a38c41",
        "ServiceName": "com.amazonaws.ap-northeast-1.s3",
        "State": "available",
        "PolicyDocument": "{\"Version\":\"2008-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":\"*\",\"Action\":\"*\",\"Resource\":\"*\"}]}",
        "RouteTableIds": [
            "rtb-0484c8a5f9983fabe"
        ],
        "SubnetIds": [],
        "Groups": [],
        "PrivateDnsEnabled": false,
        "NetworkInterfaceIds": [],
        "DnsEntries": [],
        "CreationTimestamp": "2019-02-16T15:50:54.000Z"
    }
}
```

サブネットのルートテーブルが以下のようになったことが確認できます。

```shell
$ aws ec2 describe-route-tables --filter Name=association.subnet-id,Values=$SUBNETID --query 'RouteTables[*].Routes'
[
    [
        {
            "DestinationCidrBlock": "10.0.0.0/16",
            "GatewayId": "local",
            "Origin": "CreateRouteTable",
            "State": "active"
        },
        {
            "DestinationCidrBlock": "0.0.0.0/0",
            "GatewayId": "igw-0f8e69039f7ffbe33",
            "Origin": "CreateRoute",
            "State": "active"
        },
        {
            "DestinationPrefixListId": "pl-61a54008",
            "GatewayId": "vpce-0663e4cf6645ddfd0",
            "Origin": "CreateRoute",
            "State": "active"
        }
    ]
]
```

つまり、以下のようなルーティングになりました。

|宛先のCIDRブロック|送られるルーター|
|:-----|:-----|
|10.0.0.0/16|local|
|0.0.0.0/0|igw-0f8e69039f7ffbe33|
|pl-61a54008（S3のバケット）|vpce-0663e4cf6645ddfd0（VPCエンドポイント）|


インスタンスからS3へエンドポイント経由で通信できるか確認してみましょう。

```shell
$ ssh -i MyKeyPair.pem ec2-user@52.199.184.55
$ curl https://s3-ap-northeast-1.amazonaws.com/my-nginx-bucket/sample.html
<html>
  <head>
    <title>タイトル</title>
  </head>
  <body>
    aws cli の練習
  </body>
</html>
```

### NginxからS3へプロキシする

最後に、VPC外部からインスタンス、インスタンスからS3へとそれぞれ通信ができるようになったのでインスタンス上のNginxからS3へプロキシするように設定します。

SSHしてインスタンスに入って、Nginxの設定ファイルを書き換えます。設定ファイルは`/etc/nginx/nginx.conf`になります。

```shell
$ ssh -i MyKeyPair.pem ec2-user@13.231.142.47
$ sudo vim /etc/nginx/nginx.conf
```

以下のlocationの箇所にS3のバケットへプロキシするように設定します。

```nginx

# /etc/nginx/nginx.conf
...

http {

    ...

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;
        
        # ここを以下のように編集し、対象バケットのHTMLへプロキシされるようにする
        location / {
                proxy_pass      https://s3-ap-northeast-1.amazonaws.com/my-nginx-bucket/sample.html;
                proxy_intercept_errors on;
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }

    ...
    
}
```

```shell
$ sudo systemctl restart nginx.service
$ curl http://localhost
<html>
  <head>
    <title>タイトル</title>
  </head>
  <body>
    aws cli の練習
  </body>
</html>
$ ログアウト
$ curl http://ec2-13-231-142-47.ap-northeast-1.compute.amazonaws.com/
<html>
  <head>
    <title>タイトル</title>
  </head>
  <body>
    aws cli の練習
  </body>
</html>
```

これで以下のようなアークテクチャーが実現されました！

![スクリーンショット 2019-02-17 20.40.02.png](https://qiita-image-store.s3.amazonaws.com/0/68018/51f2c61c-7c4c-db03-f726-f12b0b7011db.png)

## 最後に

料金が発生する可能性があるのでインスタンスの停止やVPCの削除など後片付けを忘れないようにしましょう。