AWS Cloud9 + GCEインスタンスで無料の開発環境を手に入れる

# はじめに
AWS Cloud9はそれ自体は無料で利用することができますが、バックエンドに利用するEC2インスタンスの利用料が別途発生します。
ただ、EC2ではなく他のLinuxサーバも利用可能ということだったので、GCE（Google Compute Engine）のAlways Free枠を使って本当に無料にできないか試してみました。

[AWS Cloud9](https://aws.amazon.com/jp/cloud9/)
[Google Compute Engine](https://cloud.google.com/compute/)
[Google Cloud Platform の無料枠](https://cloud.google.com/free/)

# 事前準備
* AWSのアカウント登録
* GCPのプロジェクト作成および請求情報登録
    * はじめてなら、https://console.cloud.google.com あたりから自然と誘導されると思います
    * AWS同様に、無料枠しか使わなくても最初に支払い設定が必要です

# 手順
Cloud9が「ブラウザのみでコードを記述、実行、デバッグできるクラウドベースの統合開発環境 (IDE) 」をうたっているので、環境構築もブラウザのみでやることを目標にします。

## GCEインスタンスの起動（GCE側作業）
* 以下URLより「インスタンスを作成」<br>
https://console.cloud.google.com/compute/instances
<br><br>
* 以降はポチポチしていくだけなので細かい点は割愛しますが、無料枠におさめるために以下の点がポイントになります

| 項目 | 設定値 |
|:-:|:-:|
|マシンタイプ|f1-micro|
|ゾーン|USリージョンを選択|
|OSイメージ|[プレミアムイメージ](https://cloud.google.com/compute/docs/images#os-compute-support)以外のLinuxOSを選択|
|ディスク|「標準の永続ディスク」で30GB以内|

* あと、固定IPを確保したほうが継続的にCloud9のバックエンドとして運用するうえで都合がいいと思います
    * [静的外部 IP アドレスの予約](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address)
<br><br>

* 参考までに、ここまでの内容をコマンド一発（IPアドレス予約だけ終わっている前提）でやる場合は以下の通り

```shell:gcloud
$ gcloud compute --project "YOUR_PROJECT_ID" instances create "sandbox" \
    --zone "us-west1-b" --machine-type "f1-micro" --network "default" \
    --address YOUR_EXTERNAL_IP --image-family "ubuntu-1604-lts" \
    --image-project "ubuntu-os-cloud" --boot-disk-size "30" \
    --boot-disk-type "pd-standard" --boot-disk-device-name "sandbox"
```

ここでは例として以下の設定としています。

インスタンス名：sandbox
ゾーン：us-west1-b
OSイメージ：ubuntu16.04

## node.jsのインストール（GCE側作業）
* GCPコンソールより作成したインスタンスにSSH接続する
![スクリーンショット 2018-03-24 6.47.41.png](https://qiita-image-store.s3.amazonaws.com/0/92629/482d35bf-d419-9d43-d0e0-f94a326d8cca.png)
<br><br>
* 以下のコマンドを実行する<br>※OSイメージがubuntu16.04の場合の手順です

```shell:SSHConsole
$ sudo apt-get update
$ sudo apt-get -y install nodejs
$ which nodejs
/usr/bin/nodejs
```
→ 表示されたnodejsコマンドのパスをメモっておく

## Cloud9環境作成（AWS作業、一部GCE作業）
* AWSマネジメントコンソールにログインし、Cloud9のページを開く
<br><br>
* 東京リージョンではまだ提供されていないので、どこかサポートされているリージョンに移動する
    * 今回はGCEインスタンスをus-west1（オレゴン）に作ったので、Cloud9もus-west-2（オレゴン）にします
<br><br>
* 「Create environment」を選択
![スクリーンショット 2018-03-24 8.25.28.png](https://qiita-image-store.s3.amazonaws.com/0/92629/1d956221-fa50-f6db-e379-54fc58bce80a.png)
<br><br>
* Nameをお好みで入力して「Next Step」を選択
![スクリーンショット 2018-03-24 8.26.07.png](https://qiita-image-store.s3.amazonaws.com/0/92629/885df808-2cbd-5832-1743-aeaf81894333.png)
<br><br>
* バックエンドとなるサーバ情報を入力

| 項目 | 設定値 |
|:-:|:---------|
|Environment type|Connect and run in remote server (SSH)|
|User|話を簡単にするためGCEのUbuntuイメージに最初から存在する「ubuntu」を使う|
|Host|GCEインスタンスの外部IP|
|Port|22|
|Node.js binary path|前の項で確認したnodejsコマンドのパス|

![FireShot Capture 3 - Create a new env_ - https___us-west-2.console.aws.amazon.com_cloud9_home_create.png](https://qiita-image-store.s3.amazonaws.com/0/92629/dc9fcb30-3b62-295f-f045-7928e2e76151.png)
※まだ「Next Step」は押さない

* 「Copy key to clipboard」を選択
    * Cloud9が使用するsshキーペアのパブリックキーがクリップボードにコピーされる
<br><br>
* GCEのSSHコンソールに戻り、ubuntuユーザのauthorized_keysファイルにパブリックキーを追記

```shell:SSHConsole
$ sudo vi ~ubuntu/.ssh/authorized_keys
→ コピーしたパブリックキーをペーストして保存
```

* Environment settingsページの「Next Step」を選択
<br><br>
* 確認画面が表示されるので、間違いなければ「Create environment」を選択
<br><br>
* Always Install everything にチェックして「Finish」を選択
![FireShot Capture 6 - sandbox - AWS Cloud9_ - https___us-west-2.console.aws.amazo.png](https://qiita-image-store.s3.amazonaws.com/0/92629/0dc0ba85-9852-9f07-5198-47978890ab4a.png)
<br><br>
* これで環境構築は完了です！
![FireShot Capture 8 - sandbox - AWS Cloud9_ - https___us-west-2.console.aws.amazo.png](https://qiita-image-store.s3.amazonaws.com/0/92629/b422ba5e-8576-1bd2-77ab-72982a3e9238.png)


## Lambda Functionのデプロイ、実行を試す
せっかくなので実際に何かやってみます。
調子こいてテーマを変更したり先に試したFunctionがある状態でスクリーンショットをとってしまったので、一部の絵で前の項とのつながりがありません。。

### AWSマネジメントコンソールでの作業
* Cloud9用のIAMユーザを作成し、以下のポリシーをアタッチする
    * 「AWS マネジメントコンソールへのアクセス」は必要なし

| AWS管理ポリシー |
|:------------- |
| AWSLambdaFullAccess |
| AmazonAPIGatewayInvokeFullAccess |
| AmazonAPIGatewayAdministrator |

* 追加で以下のインラインポリシーを作成してアタッチする

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "cloudformation:CreateChangeSet",
                "cloudformation:CreateStack",
                "cloudformation:DescribeChangeSet",
                "cloudformation:DescribeStackEvents",
                "cloudformation:DescribeStacks",
                "cloudformation:ExecuteChangeSet",
                "cloudformation:ListStackResources",
                "cloudformation:UpdateStack",
                "iam:AttachRolePolicy",
                "iam:DetachRolePolicy",
                "iam:GetRole",
                "iam:GetUser",
                "iam:PassRole"
            ],
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "iam:CreateRole",
                "iam:DeleteRole"
            ],
            "Resource": "arn:aws:iam::ACCOUNT_ID:role/cloud9-*",
            "Effect": "Allow"
        }
    ]
}
```
→`ACCOUNT_ID` は自分のアカウントIDに置き換え

※参考
https://docs.aws.amazon.com/ja_jp/cloud9/latest/user-guide/lambda-functions.html#lambda-functions-prepare-user

### Cloud9画面での作業
* awscliインストール
    * 画面下部のterminalタブより以下の通り実行（表示されていなかったら、メニューより「Window」-「New Terminal」）

```
$ sudo apt install python-pip
$ pip install --upgrade pip
$ pip install awscli
$ aws configure
AWS Access Key ID [None]: xxxxxxxxxxxxxxxxxxxx
AWS Secret Access Key [None]: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Default region name [None]: us-west-2
Default output format [None]:

→作成したCloud9用IAMユーザの情報を入力
```

* λ+ボタンを押す
![スクリーンショット 2018-03-25 17.44.09.png](https://qiita-image-store.s3.amazonaws.com/0/92629/a6f98cf9-f14e-278e-1478-f3202b215967.png)
<br><br>
* それぞれお好みで入力して「Next」
![スクリーンショット 2018-03-25 17.44.53.png](https://qiita-image-store.s3.amazonaws.com/0/92629/ad6a5923-dc35-9f3c-6062-cdd680f03ea4.png)
<br><br>
* blueprint一覧が表示されるのでひとまずnodejs6.10の「hello-world」を選択して「Next」
![スクリーンショット 2018-03-25 17.45.30.png](https://qiita-image-store.s3.amazonaws.com/0/92629/f46fa4ad-8e64-6360-e649-486467b1aaf8.png)
<br><br>
* ひとまずそのまま「Next」
![スクリーンショット 2018-03-25 17.45.42.png](https://qiita-image-store.s3.amazonaws.com/0/92629/eabe5025-53e8-5142-fe7e-e9a85d5f945d.png)
<br><br>
* ひとまずそのまま「Next」
![スクリーンショット 2018-03-25 17.45.53.png](https://qiita-image-store.s3.amazonaws.com/0/92629/3559759e-16c3-78a1-eff2-cf0285a044f9.png)
<br><br>
* 確認して「Finish」<br>
![スクリーンショット 2018-03-25 17.46.05.png](https://qiita-image-store.s3.amazonaws.com/0/92629/c1ea8ca8-e9b4-ea18-0e02-607d4f157566.png)
<br><br>
* できちゃった
![スクリーンショット 2018-03-25 17.47.19.png](https://qiita-image-store.s3.amazonaws.com/0/92629/34c5b760-bf0b-605d-c6c0-16a684e73ec7.png)
<br><br>
* マネジメントコンソールでLambdaのページを確認すると、確かに同じ名前で作成されている
![スクリーンショット 2018-03-25 17.48.35.png](https://qiita-image-store.s3.amazonaws.com/0/92629/0d93d054-58b6-ba7d-1802-177cf5726ef8.png)
<br><br>
* 以下の通り「Run Remote」する
![スクリーンショット 2018-03-25 17.49.56.png](https://qiita-image-store.s3.amazonaws.com/0/92629/857f1d9e-3132-c5ac-eeb4-1ce327128ea1.png)
<br><br>
* payloadを適当に入力して「Run」
![スクリーンショット 2018-03-25 17.51.55.png](https://qiita-image-store.s3.amazonaws.com/0/92629/3067372e-eb63-a969-4133-01c472daaf25.png)
<br><br>
* 実行結果が表示された
![スクリーンショット 2018-03-25 17.52.12.png](https://qiita-image-store.s3.amazonaws.com/0/92629/2a0e4c0a-7c96-73bd-a5f8-f807f6611fc3.png)

# まとめ
いかがでしたでしょうか。簡単にできるわりに、けっこうよさげです。
開発用の高いマシンやソフトを手に入れなくてもそれなりのことができそうですね。

# おまけ
Amazonつながりで、この前のPrime Dayで買ったFireタブレットでCloud9に接続してみました。動きそう！
![Cloud9.JPG](https://qiita-image-store.s3.amazonaws.com/0/92629/0b71f701-24a6-6df9-f095-c9c9ee74cc2e.jpeg)

# 補足
* 話を簡単にするために一部手抜きをしていますので、本気で使う際は以下の点を考慮したほうがいいかもしれません。
    * ubuntuユーザではなくcloud9用のユーザを作成する
        * その場合そのユーザでパスワード無しでsudoが実行できるようにする必要あり
    * sshのポートを22からランダムな値に変更する
    * IAMのインラインポリシーはAWSのドキュメントを参考にしていますが、けっこう強い気がするのでCredential管理は十分ご注意を
<br><br>
* GCEの無料枠、と書きましたが、下りネットワークについては以下の制限がありますので念のためご認識をお願いします

```
1 GB の北米から全リージョン宛て下りネットワーク（1 か月あたり、中国およびオーストラリアを除く）
```

* やっといてなんですが、FireタブレットはAmazonの販促ツールだと思っているので、念のため大切なデータは扱わないようにすることをお勧めします...