- 投稿日:2019-06-27T23:35:49+09:00
EC2の削除保護を無視してterminatedなる状況を発生させてサービスダウンさせてしまった
ECS on EC2での話です。
結論
- ECSのスケールインはマルチAZを優先にスケールインさせる
- ECSのスケールインは条件によってはEC2の削除保護を無視してスケールインされる
- インスタンスタイプ選択時は立てれるアベイラビリティゾーンは確認しよう
再現方法
簡略化させて発生させる再現手順を記載します。
- アベイラビリティゾーンに偏りがある状態でECSのEC2インスタンスを管理する
- 片側のアベイラビリティゾーンだけを対象にEC2の削除保護を設定する
- ECS(CloudformationもしくはAuto Scalingで)の最大台数を減らす
- 削除保護されたEC2がterminatedになる
手順はわかったところで、スクショを交えて手順の説明の実施と再現させます。
ECSインスタンスの状況
まずは前提となる状況を作り出します。
ECSのアベイラビリティゾーンの設定
ECSのアベイラビリティゾーンを2つとします。今回はcとd。
ECSインスタンス達
アベイラビリティゾーンを2ゾーンしか設定していなければ、奇数台数のインスタンスを立てれば偏りが出ます。
このインスタンス達の 削除保護 の状態はこんな感じに片側だけ保護するようにする。スケールインの実施
ここはCloudformationを変更してもいいし、Auto Scalingグループの変更でもどっちでもいいですが、最大台数を落とします。
今回は3台→2台への変更とします。すると、あら不思議!削除保護したインスタンスがterminatedになる。
実業務で発生した状況
グダグダ書きましたが、私自身が実際に起きた状況は以下です。
前提条件
- 該当のECSの アベイラビリティゾーンはaおよびc しか設定されていない
状況
- EC2インスタンスタイプを変更するために最大台数(2→4)とEC2インスタンスタイプを R5a に変更する
- R5aは現状アベイラビリティゾーンaとdしか対応していない ため今回はaに偏って立つ
→この時点で状況を理解していたが、 気にせず次のオペレーションを実施した が最大のミス- 作成された新インスタンスの削除保護を実行
- 旧インスタンスに乗っているコンテナのdrainingを開始
- 旧インスタンスに乗っているコンテナがないことを確認後、Cloudformationで最大台数を戻す(4→2)
- 旧インスタンスの1台はdrainingにも関わらず、アベイラビリティゾーンcであるがゆえに残されて、新インスタンス1台がterminatedになる
- 晴れてterminatedとなった新インスタンスで動いていたコンテナ達は無情にも落とされて、サービスダウンとなる?
正直ここからサービス復旧には30分もかかってないけど(長いと見るか短いと見るかは人それぞれだが、私に長い)、まぁ落ちたのは凹んだ。
最悪なのはECSのCloudformationのアベイラビリティゾーンの設定にdを足しても復旧しなかった。理由はアベイラビリティゾーンdに立ったインスンタンスでコンテナが立ち上がってもALBからのヘルスチェックが通らなかったため。
なぜならECSだけでなく、ALBのアベイラビリティゾーンもdの設定がなかったためです。
急遽ALBにもアベイラビリティゾーンdを足してコンテナを立ち上げたけど、時すでに遅し。最後に
ECSのすべての動作を理解できてなかった検証不足と今後このような悲劇を生み出さないようにと自分への戒めとしてこの記事を書きました。
- 投稿日:2019-06-27T22:36:32+09:00
Amazon Linux2にyumでMeCabが入らなかった
はじめに
https://github.com/neologd/mecab-ipadic-neologd
の手順に従って、 Amazon Linux2にMeCabをインストールしようとしたところ、以下のエラーが発生しました。実行したコマンド$ sudo rpm -ivh http://packages.groonga.org/centos/groonga-release-1.1.0-1.noarch.rpm $ sudo yum install mecab mecab-devel mecab-ipadic git make curl xz patchエラーメッセージhttp://packages.groonga.org/centos/2/x86_64/repodata/repomd.xml: [Errno 14] HTTP Error 404 - Not Found解決策
エラーメッセージ中のURLにAmazon Linuxのバージョン「2」が入っているっぽいのでCentOSの最新バージョンである「7」に変えてみます。
具体的には、
/etc/yum.repos.d/groonga.repo
の以下の部分を/etc/yum.repos.d/groonga.repobaseurl=http://packages.groonga.org/centos/$releasever/$basearch/以下のように変えました。
/etc/yum.repos.d/groonga.repo(更新後)baseurl=http://packages.groonga.org/centos/7/$basearch/その後以下をもう一度実行するとエラーは出ず
$ sudo yum install mecab mecab-devel mecab-ipadic git make curl xz patchMeCabも実行できました。
$ mecab アデリーペンギンかわいい アデリーペンギン 名詞,一般,*,*,*,*,* かわいい 形容詞,自立,*,*,形容詞・イ段,基本形,かわいい,カワイイ,カワイイ EOSおわりに
これが正しい方法なのかはわかりません。
ソースからビルドするのが無難だと思います。
- 投稿日:2019-06-27T17:47:34+09:00
Amazon Linux2とLet's EncryptでSSL対応サーバを0から爆速構築
どんな話?
以前Amazon Linux2でLet's Encrypt使おうとしたらコケた話という記事で
certbotを書き換えて認証なんてことをしちゃてたわけなのですが、これをする必要はもうありません。EPEL7を有効化して
certbot
と一緒にpython2-certbot-apache
もしくはpython2-certbot-nginx
をインストール
あとはcertbotコマンドを実行するだけです。
すっごい楽ですね!
実際、新規環境で試した際は5分もかかりませんでした。また、python-certbot-○○は他のディストリビューションでも利用可能ですので参考にしていただけると思います。
ということでご紹介します。
実際にやってみる
Amazon linux2インスタンスの立ち上げとドメイン割当までは完了しているものとします。
また、Apacheでの設定方法についてはAWSのドキュメントに記載がありますので、今回はnginxにします。nginxのインストール
今回はサクッとextrasレポジトリからインストールします。
コマンドを実行してnginxのバージョンを確認
今回は1.12.1。$ amazon-linux-extras list 0 ansible2 available [ =2.4.2 =2.4.6 ] 2 httpd_modules available [ =1.0 ] 3 memcached1.5 available [ =1.5.1 =1.5.16 ] 4 nginx1.12 available [ =1.12.2 ] 5 postgresql9.6 available [ =9.6.6 =9.6.8 ]nginx1.12をインストール。
$ sudo amazon-linux-extras install nginx1.12nginxを起動して終了。
$ sudo systemctl enable nginx$ sudo systemctl start nginxEPEL7の有効化
EPEL7をダウンロードして、
$ sudo wget -r --no-parent -A 'epel-release-*.rpm' http://dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/リポジトリパッケージをインストール。
$ sudo rpm -Uvh dl.fedoraproject.org/pub/epel/7/x86_64/Packages/e/epel-release-*.rpm最後にEPEL7を有効化。
$ sudo yum-config-manager --enable epel*Let's Encryptの証明書を取得と設定
以下のコマンドで証明書取得を開始。
$ sudo certbot --nginxアドレスを聞かれるので入力。
このアドレスに重要な連絡が来るようになります。Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator nginx, Installer nginx Enter email address (used for urgent renewal and security notices) (Enter 'c' to cancel): example@example.comライセンスへの同意を求められるので従うことができる場合はaを入力。
当然ですが同意しないと使えません。- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Please read the Terms of Service at https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must agree in order to register with the ACME server at https://acme-v02.api.letsencrypt.org/directory - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (A)gree/(C)ancel: aキャンペーンメール等を送るためEFFとアドレスを共有してもいいか聞かれる。
同意できればy、同意できなければn。
これはどちらでもオッケーです。- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Would you be willing to share your email address with the Electronic Frontier Foundation, a founding partner of the Let's Encrypt project and the non-profit organization that develops Certbot? We'd like to send you email about our work encrypting the web, EFF news, campaigns, and ways to support digital freedom. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - (Y)es/(N)o: nnginxをインストールしたてのまっさらな状態だとドメイン名は設定されていないのでここで聞かれる。
利用するドメイン名を入力。No names were found in your configuration files. Please enter in your domain name(s) (comma and/or space separated) (Enter 'c' to cancel): example.comドメイン認証、鍵取得、nginxへ鍵設定までが実施されるので待機。
設定はnginx.confに書き込まれる。Obtaining a new certificate Performing the following challenges: http-01 challenge for example.com Waiting for verification... Cleaning up challenges Deploying Certificate to VirtualHost /etc/nginx/nginx.confHTTPアクセスをHTTPSにリダイレクト設定をするか聞かれる。
設定しない場合は1、設定しない場合2を入力。
今回は2を選択してみましたが、ドメイン直下のリダイレクトがうまくいかなかったので
個人的には1を選んで自分で設定する方法がおすすめです。Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1: No redirect - Make no further changes to the webserver configuration. 2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for new sites, or if you're confident your site works on HTTPS. You can undo this change by editing your web server's configuration. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2 Redirecting all traffic on port 80 to ssl in /etc/nginx/nginx.conf以下のように成功メッセージが出れば完了。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations! You have successfully enabled https://example.com You should test your configuration at: https://www.ssllabs.com/ssltest/analyze.html?d=example.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/example.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/example.com/privkey.pem Your cert will expire on 2019-09-25. To obtain a new or tweaked version of this certificate in the future, simply run certbot again with the "certonly" option. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal. - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le自動更新設定
メッセージにもあるように
certbot renew
コマンドで証明書更新ができます。
AWSのドキュメントでは--no-self-upgradeをつけるようになっているので安定性を考えるとそちらがベストでしょう。これをCronにでも突っこんでおけば何もしなくても更新されるのでやっておきましょう。
crontabコマンドを実行して
$ sudo crontab -u root -e以下のように書き加えればOKです。この例では毎日0:00と12:00にroot権限で実行されます。
(AWSでは1:39と13:39でした。golangの"2009-11-10 23:00:00 UTC"のようなおもしろい理由があるんでしょうか…?)0 0,12 * * * root certbot renew --no-self-upgrade最後に設定されているか確認すれば完了です。
$ sudo crontab -u root -l 0 0,12 * * * root certbot renew --no-self-upgrade最後に
簡単、爆速ですね。
extpectコマンドも併せて使ってやれば、ほとんどすることなくなっちゃうんじゃないでしょうか?
これが無料でできるなんていい時代だなー!
- 投稿日:2019-06-27T16:18:16+09:00
CircleCIでAWS CLIを利用する時に環境変数(or context)が効かない
概要
CircleCIで環境変数やContextsに値を設定しても、AWS CLIが以下のようなエラーで落ちることがあります。
#!/bin/bash -eo pipefail aws configure set aws_access_key_id \ $AWS_ACCESS_KEY_ID \ --profile default usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters] To see help text, you can run: aws help aws <command> help aws <command> <subcommand> help aws: error: the following arguments are required: value Exited with code 2原因
- GitHubのフォークしたリポジトリからPRを出している
- そしてCircleCIの
Advanced Settings
のPass secrets to builds from forked pull requests
がOff
になっている対策
上記の設定を
On
にすれば解決するが、脆弱性の懸念が大きいためおすすめできません。(詳しくは設定画面の説明を参照)Pass secrets to builds from forked pull requests
Run builds for fork pull request changes with this project's configuration, environment variables, and secrets.There are serious security concerns with this setting (see the documentation for details.) If you have SSH keys, sensitive env vars or AWS credentials stored in your project settings and untrusted forks can make pull requests against your repo, then this option isn't for you!
各自でビルドを行わないのであればフォークしない運用とするのが良いと思われます。
- 投稿日:2019-06-27T14:04:55+09:00
EC2にgit、docker、docker-compose、pip、pythonコマンドをインストールする方法
EC2を立ち上げた際にやることを忘れがち&チームに共有としてメモしておきます。
準備まではQiitaの「(下準備編)世界一丁寧なAWS解説。EC2を利用して、RailsアプリをAWSにあげるまで」という記事がわかりやすかった。
コマンドのインストール
git
$ sudo yum install gitgitの連携方法は以下
# gotconfigを作成、編集 $ vi .gitconfig [user] name = your_name email = hoge@hoge.com # githubに公開鍵を登録するために公開鍵作成 $ chmod 700 ~/.ssh $ cd ~/.ssh $ ssh-keygen -t rsaあとは、Github/Gitlabに公開鍵を登録してcloneするだけ。
docker
# yum の更新 $ sudo yum update -y # yum から docker をインストール $ sudo yum install -y docker # docker サービスの起動 $ sudo service docker start # ec2-user を docker グループに追加する $ sudo usermod -a -G docker ec2-user # ログインしなおして以下を実行しインストールされていることを確認 $ docker infodocekrコマンドをsudo無しで実行する場合は以下を実行
# dockerグループがなければ作る $ sudo groupadd docker # 現行ユーザをdockerグループに所属させる $ sudo gpasswd -a $USER docker # dockerデーモンを再起動する (CentOS7の場合) $ sudo systemctl restart docker # exitして再ログインすると反映される。 $ exitdocker-compose
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose $ sudo chmod +x /usr/local/bin/docker-compose $ docker-compose --version他のリンクにしたい場合以下から探す
https://github.com/docker/compose/releases/byobu
$ sudo yum update -y $ wget https://launchpad.net/byobu/trunk/5.119/+download/byobu_5.119.orig.tar.gz $ tar xzf byobu*.tar.gz $ cd byobu-* && ./configure $ sudo make && sudo make installpip
$ curl -O https://bootstrap.pypa.io/get-pip.py $ python get-pip.py --user # or python3python
# 依存関係インストール $ sudo yum install gcc zlib-devel bzip2 bzip2-devel readline readline-devel sqlite sqlite-devel openssl openssl-devel -y # 本体インストール $ pyenv install 3.6.5 # このOSで使用するPythonのバージョンを宣言 $ pyenv global 3.6.5 $ pyenv rehash $ python --version
- 投稿日:2019-06-27T13:44:35+09:00
awscli を pip でインストールするときは Python 3 を使ってください
Python 2 は 2020-01-01 に EOL を迎えます。
しかし、いまだに PyPI からのダウンロードの50%強は Python 2 からのものです。
from PyPI Statsこの膨大なPython 2からのダウンロード数は、 Python のライブラリをメンテナンスしている人たちに Python 2 のサポートを続けさせるプレッシャーになっています。 Python 2 の EOL までにこのダウンロード数をなるべく下げたいところです。
だれがこんなに Python 2 で
pip install
をしているのでしょうか。OS別ダウンロード数統計にヒントがあります。過半数の Python ユーザーが macOS か Windows を使っているのに対して、ダウンロードの90%くらいが Linux からのものです。 CI, CD, サーバーのプロビジョニングで実行される
pip install
が多いのでしょう。ではどのパッケージが多くダウンロードされているのでしょうか。 PyPI Stats のランキング を見てみましょう。
トップ20のうち、pip とその依存ライブラリである setuptools を除いた 18 パッケージは、 awscli かその依存パッケージです!
そして awscli パッケージの 80% 以上が Python 2 から
pip install
されています!!もし多くの人に読まれているドキュメントや Dockerfile のサンプルなどで awscli を Python 2 から pip install しているのを見かけたら、この記事か 英語版 のURLを紹介して Python 3 に移行するように提案してみてください。
- 投稿日:2019-06-27T13:41:30+09:00
WSL 上に AWS CLI をインストールする
概要
WSL上に AWS CLI をインストールする方法 with pip3
環境
Windows10 Pro
バージョン 1803
WSL ... Ubuntu 18.04 LTSWSLのインストール方法
WSL への pip3 インストール方法インストール
--upgrade オプションは pip 自体のアップデート
--upgrade オプションについて--user オプションは linux のユーザーディレクトリにインストトールするためのオプション。
AWS CLI の場合は /home/username/.local/bin/aws ディレクトリにインストールされる。# --upgrade $ pip3 install awscli --upgrade --user Collecting awscli Downloading https://files.pythonhosted.org/packages/1c/fc/6de0d0e616db20fbb0bffbed73b4b575b6c5d6632503720760d21b5ec354/awscli-1.16.188-py2.py3-none-any.whl (1.7MB) 100% |████████████████████████████████| 1.7MB 577kB/s Collecting colorama<=0.3.9,>=0.2.5 (from awscli) Downloading https://files.pythonhosted.org/packages/db/c8/7dcf9dbcb22429512708fe3a547f8b6101c0d02137acbd892505aee57adf/colorama-0.3.9-py2.py3-none-any.whl Collecting PyYAML<=5.1,>=3.10; python_version != "2.6" (from awscli) Collecting rsa<=3.5.0,>=3.1.2 (from awscli) Downloading https://files.pythonhosted.org/packages/e1/ae/baedc9cb175552e95f3395c43055a6a5e125ae4d48a1d7a924baca83e92e/rsa-3.4.2-py2.py3-none-any.whl (46kB) 100% |████████████████████████████████| 51kB 1.4MB/s Collecting s3transfer<0.3.0,>=0.2.0 (from awscli) Downloading https://files.pythonhosted.org/packages/16/8a/1fc3dba0c4923c2a76e1ff0d52b305c44606da63f718d14d3231e21c51b0/s3transfer-0.2.1-py2.py3-none-any.whl (70kB) 100% |████████████████████████████████| 71kB 1.5MB/s Collecting docutils>=0.10 (from awscli) Downloading https://files.pythonhosted.org/packages/36/fa/08e9e6e0e3cbd1d362c3bbee8d01d0aedb2155c4ac112b19ef3cae8eed8d/docutils-0.14-py3-none-any.whl (543kB) 100% |████████████████████████████████| 552kB 1.0MB/s Collecting botocore==1.12.178 (from awscli) Downloading https://files.pythonhosted.org/packages/98/8f/ca7e67d37570e0dc0c5a495096a9a7428fb01a97fa908f57dcd0739f956b/botocore-1.12.178-py2.py3-none-any.whl (5.6MB) 100% |████████████████████████████████| 5.6MB 261kB/s Collecting pyasn1>=0.1.3 (from rsa<=3.5.0,>=3.1.2->awscli) Using cached https://files.pythonhosted.org/packages/7b/7c/c9386b82a25115cccf1903441bba3cbadcfae7b678a20167347fa8ded34c/pyasn1-0.4.5-py2.py3-none-any.whl Collecting python-dateutil<3.0.0,>=2.1; python_version >= "2.7" (from botocore==1.12.178->awscli) Downloading https://files.pythonhosted.org/packages/41/17/c62faccbfbd163c7f57f3844689e3a78bae1f403648a6afb1d0866d87fbb/python_dateutil-2.8.0-py2.py3-none-any.whl (226kB) 100% |████████████████████████████████| 235kB 1.2MB/s Collecting urllib3<1.26,>=1.20; python_version >= "3.4" (from botocore==1.12.178->awscli) Downloading https://files.pythonhosted.org/packages/e6/60/247f23a7121ae632d62811ba7f273d0e58972d75e58a94d329d51550a47d/urllib3-1.25.3-py2.py3-none-any.whl (150kB) 100% |████████████████████████████████| 153kB 1.1MB/s Collecting jmespath<1.0.0,>=0.7.1 (from botocore==1.12.178->awscli) Downloading https://files.pythonhosted.org/packages/83/94/7179c3832a6d45b266ddb2aac329e101367fbdb11f425f13771d27f225bb/jmespath-0.9.4-py2.py3-none-any.whl Collecting six>=1.5 (from python-dateutil<3.0.0,>=2.1; python_version >= "2.7"->botocore==1.12.178->awscli) Using cached https://files.pythonhosted.org/packages/73/fb/00a976f728d0d1fecfe898238ce23f502a721c0ac0ecfedb80e0d88c64e9/six-1.12.0-py2.py3-none-any.whl Installing collected packages: colorama, PyYAML, pyasn1, rsa, six, python-dateutil, urllib3, jmespath, docutils, botocore, s3transfer, awscli Successfully installed PyYAML-5.1 awscli-1.16.188 botocore-1.12.178 colorama-0.3.9 docutils-0.14 jmespath-0.9.4 pyasn1-0.4.5 python-dateutil-2.8.0 rsa-3.4.2 s3transfer-0.2.1 six-1.12.0 urllib3-1.25.3インストール確認
PATH は通ってました。
もし aws なんてコマンドねーよって言われたら which コマンドでインストールされたディレクトリを確認後 PATH を通してみましょう。$ aws --version aws-cli/1.16.188 Python/3.6.7 Linux/4.4.0-17763-Microsoft botocore/1.12.178 $ which aws /home/unix_user/.local/bin/aws認証情報設定
インストール後そのまま何かコマンド実行すると認証情報がないよって怒られます。
# iam ユーザーのリスト取得 $ aws iam list-users Unable to locate credentials. You can configure credentials by running "aws configure".設定方法
$ aws configure # 以下は対話式で入力していく AWS Access Key ID [None]: accesskey1 AWS Secret Access Key [None]: secret1 Default region name [None]: ap-northeast-1 Default output format [None]: json設定ファイル確認
~/.aws
ディレクトリが作成されています。
~/.aws/credentials
は認証情報、~/.aws/config
は認証情報以外の情報(デフォルトのリージョンとかデフォルトの出力形式とか)が格納されています。ls -la ~/.aws/ total 0 drwxrwxrwx 1 unix_user unix_user 4096 Jun 27 13:27 . drwxr-xr-x 1 unix_user unix_user 4096 Jun 27 13:27 .. -rw------- 1 unix_user unix_user 48 Jun 27 13:27 config -rw------- 1 unix_user unix_user 116 Jun 27 13:27 credentials $ cat ~/.aws/config [default] region = ap-northeast-1 output = json $ cat ~/.aws/credentials [default] aws_access_key_id = accesskey1 aws_secret_access_key = secret1環境変数とかでも設定できます。
ほかの設定方法と優先順位設定されたか確認
設定したアクセスキーで取得できるはずの AWS のリソースへアクセスしてみます。
# 無事取れてます $ aws iam list-users { "Users": [ { "Path": "/", "UserName": "some.one", "UserId": "user1", "Arn": "arn:aws:iam::123456789:user/some.one", "CreateDate": "2019-06-25T11:43:32Z", "PasswordLastUsed": "2019-06-26T00:38:36Z" }, { "Path": "/", "UserName": "some.two", "UserId": "user2", "Arn": "arn:aws:iam::123456789:user/some.two", "CreateDate": "2019-06-25T11:45:32Z", "PasswordLastUsed": "2019-06-26T00:40:36Z" } ] }
- 投稿日:2019-06-27T11:56:28+09:00
RDS Notification Message について
What is RDS Notification Message?
Amazon RDSでは, Amazon RDSのイベントが発生した時に, Amazon Simple Notification Service (Amazon SNS) を使用して通知を送る.
通知にはAmazon SNSでサポートされている全ての通知形式が使用可能.
e.g. Email, Text message, Calling HTTP endpoint.サブスクライブ可能なカテゴリにイベントが分類されており, サブスクラブしたカテゴリのイベントの通知を受け取ることができる.
イベント通知は, サブスクリプションを作成する時に指定したアドレスに送信されるが, 複数のサブスクリプションを作成することで 必要なイベントを必要なアドレスに対して通知することができる.
例えば, 全ての通知を受信するサブスクリプション (all-infra@hirrot.com) と, 本番か同様のDBインスタンスに関する重要なイベントのみを含むサブスクリプション (production-infra@hirrot.com) など分類して作成することができる.Event Categories and Event Message of Amazon RDS
各カテゴリとイベントIDは公式サイトから参照できる.
例えば, 以下の形式で通知が送信される.Event Source : db-instance Identifier Link: https://console.aws.amazon.com/rds/home?region=ap-asia-9999#dbinstance:id=source_id SourceId: source_id Notification time : 2019-06-26 16:12:05.604 Message : Finished DB Instance backup Event ID : http://docs.amazonwebservices.com/AmazonRDS/latest/UserGuide/USER_Events.html#RDS-EVENT-0002
Event Source
: イベントの発生元Identifier Link
: RDS DB Instance IDSourceId
: 発生元IDNotification time
: 通知時刻Message
: 通知の内容Event ID
: Amazon RDS イベント IDつまり, 上記の通知内容は
2019-06-26 16:12:05.604
にdb-instance:source_id
でDBインスタンスをバックアップする(EventID: http://docs.amazonwebservices.com/AmazonRDS/latest/UserGuide/USER_Events.html#RDS-EVENT-0002) ことを表している.Subscribe to Amazon RDS Events Notification
Amazon RDSイベント通知サブスクリプションを作成して特定の通知を受け取る.
最も簡単な方法はRDS Consoleを使用する方法. CLIまたはAPIを使用してイベント通知をサブスクリプションすることも可能.How to
- AWS マネジメントコンソールにサインインし, Amazon RDS コンソール (https://console.aws.amazon.com/rds/) を開く.
- ナビゲーションペインで, [イベントサブスクリプション] を選択する.
- [イベントサブスクリプション] ページで, [イベントサブスクリプションの作成] を選択する.
- [イベントサブスクリプションの作成] ダイアログボックスで, 次の操作を行う.
- [名前] に, イベント通知サブスクリプションの名前を入力する.
- [通知の送信先] で既存の Amazon SNS トピックの Amazon SNS ARN を選択するか, [トピックを作成] を選択してトピックの名前と受取人のリストを入力する.
- [ソースタイプ] で、ソースタイプを選択する.
- [はい] を選択して, サブスクリプションを有効にする. サブスクリプションは作成するが, 通知はまだ送信しない場合は, [いいえ] を選択する.
- 選択したソースタイプに応じて, イベント通知を受け取る対象のイベントカテゴリとソースを選択する.
- [Create] を選択する.
Amazon RDS コンソールでは、サブスクリプションが作成されることが示される.
hirrot.
- 投稿日:2019-06-27T10:51:12+09:00
S3 へのデータアップロードをトリガーにデータを推論し、推論結果を S3 に保存する AWS Lambda 関数の作成
はじめに
今回は S3 へのデータアップロードをトリガーに、アップロードされたデータを推論し、結果を S3 に保存する関数を作成しました。
その内容を記載します。前提
- SageMakerでデプロイできるモデルがある。
※今回作成した Lambda 関数を使う際には、事前にモデルをデプロイしておく必要があります。
※私は画像検出のモデルを使用します。関数の作成/設定
選択したら、更に下にスクロールし各種設定を行います。
今回私が作成したプログラムは以下の通りです。
import json import boto3 def lambda_handler(event, context): s3 = boto3.client("s3") sagemaker = boto3.client("sagemaker-runtime", region_name="ap-northeast-1") #S3にアップロードされた画像データ情報を取得 bucket = event['Records'][0]['s3']['bucket']['name'] key = event['Records'][0]['s3']['object']['key'] input_data = s3.get_object(Bucket=bucket, Key=key) #画像データをバイナリデータに変換 body = input_data['Body'].read() b = bytearray(body) #推論 endpoint_response = sagemaker.invoke_endpoint( #エンドポイント名を設定 EndpointName='auto-endpoint', Body=b, ContentType='image/jpeg' ) results = endpoint_response['Body'].read() print(endpoint_response) print(results) split_s3_path = key.split('/') for i in split_s3_path: if 'jpg' in i: full_file_name = i file_name = i.split('.')[0] #output_keyに推論結果の保存パスを設定 output_key = 'dog-face/lambda/推論結果/'+file_name+'.json' #S3に推論結果をjsonファイルで保存 s3.put_object(Body=results,Bucket=bucket,Key=output_key) #copy_keyに推論後画像を格納するパスを設定 copy_key = 'dog-face/lambda/推論済み画像/'+full_file_name #S3のアップロードされたデータを別フォルダにコピーして元データは削除 s3.copy_object(Bucket=bucket,Key=copy_key,CopySource={'Bucket': bucket, 'Key': key}) s3.delete_object(Bucket=bucket,Key=key)プログラムは下記の流れになっています。
1. S3にアップロードされたデータを推論。
2. 推論結果を保存。
3. アップロードされたデータを、別フォルダにコピーし、アップロードされたデータを削除。実行ロール の設定
さきほど何もいじらずに作成した、実行ロールに、S3とSageMakerのフルアクセスをアタッチします。
※今回はテストのため、フルアクセスをアタッチしますが、本番で利用する際は適切なIAMロールを作成してください。実際にS3にデータをアップロードし、Lambda 関数を起動してみる。
この手順に入る前に使用するモデルをデプロイしてください。
アップロードしたら、作成したLambda関数を開いて、モニタリングタブを選択し、CloudWatch のログ表示を選択します。
するとこのような形で、Lambda 関数の起動ごとにログが記録されています。
ログを開くと Lambda 関数が正常に動作しているか確認できます。
最後に
今回は、S3へのデータアップロードをトリガーに、推論を行いました。
トリガーを変更することで、他にもいろいろな処理ができますので、ぜひ試してみてください。
[undefined]()
- 投稿日:2019-06-27T08:30:02+09:00
Baby Step Terraform for AWS [EC2]
お題
英語的におかしいであろう表題のことは置いといて、ある(小さな)アプリをAWS上で動かそうとした時にTerraformを使って少しずつ目的のものに近づけていくことを試みる。
Terraformのテンプレートファイルの記述も、最初は愚直に、問題に直面しつつ徐々に改修していく想定。
世の中の記事や書籍では最初からベストプラクティスを踏まえた設計をすることが多い。
でも、理解のためには、まず目的とする最低限の書き方で書いて、そこから「今、こうなってるからこうした方がいい」、「次にこれらを追加する想定だから、こうしておいた方がいい」といった改善をしてベストプラクティスに近づいた方がいいと思う。以下、構築予定。ただし、今回は1の
EC2
上でWebアプリを動かすとこのみ。
- アプリ(※)を
EC2
上で動かしてみる。- マネージドなDBに接続しにいく。
- デフォルトのネットワークを使うのではなく
vpc
、subnet
を定義する。- マルチAZ(アベイラビリティゾーン)化とロードバランサーを導入する。 (5. 画像ファイルを
S3
に置いてCloudFront
経由でアクセスさせる。) ※- アプリ(※)をDocker化して
ECS
で動かすようにする。- 最後はCI/CD。GitHubにアプリのソースをプッシュしたら自動でビルド・デプロイが走るようにする。
Route 53
使って独自ドメインでアクセスできるようにする。※Goで適当に作ったアプリ(
http://【デプロイ先ホスト】/hello
にアクセスすると「hello
」と吐くだけ)
https://github.com/sky0621/go-experiment/tree/af129be808476b56e06073775a92fb5d4cec3980Terraformのテンプレートファイル(
*.tf
)の書き方やModule化など、最初は愚直なやり方で行い、ちょっとずつベストプラクティス要素を取り入れていく。極力、無料枠内でやりくりしたいところだけど、立てるリソース如何ではなんとも。。。
https://aws.amazon.com/jp/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=categories%2312monthsfree%7Ccategories%23alwaysfreeお断り
- AWS自体の説明は薄いので「AWS触ったこともない」人に理解してもらえる内容にはなっていないです。
- 上記に関連して、AWSのアカウントは保持済み、AWSのCredentialはローカルにある前提で
terraform
コマンド叩いています。- この記事の内容で実際のサービスを本番運用しているわけではないので、このまま真似してプロダクションレディになる保証はないです。
開発環境
# OS - Linux(Ubuntu)
$ cat /etc/os-release NAME="Ubuntu" VERSION="18.04.2 LTS (Bionic Beaver)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 18.04.2 LTS" VERSION_ID="18.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=bionic UBUNTU_CODENAME=bionic# IDE - Visual Studio Code
Version: 1.35.1 Commit: c7d83e57cd18f18026a8162d042843bda1bcf21f Date: 2019-06-12T14:27:31.086Z Electron: 3.1.8 Chrome: 66.0.3359.181 Node.js: 10.2.0 V8: 6.6.346.32 OS: Linux x64 4.15.0-47-genericvscode-terraform Plugin
Name: Terraform Id: mauve.terraform Description: Syntax highlighting, linting, formatting, and validation for Hashicorp's Terraform Version: 1.3.12 Publisher: Mikael Olenfalk VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=mauve.terraform# Terraform
$ terraform version Terraform v0.12.2# tfenv
$ tfenv tfenv 0.6.0実践
1. ミニマムアプリのEC2デプロイ
アプリはGolang製でLinux(Ubuntu)環境でビルドしたバイナリ(
go-experiment
)がGitHub(※)に上がっている前提。
※ https://github.com/sky0621/go-experiment/tree/af129be808476b56e06073775a92fb5d4cec3980Projectのディレクトリ構成
EC2インスタンス1つ立てるだけということもあり、Projectルート直下に1ファイルのみ。
$ tree . └── main.tf 0 directories, 1 fileテンプレートファイルの中身チェック
main.tfresource "aws_instance" "go-app-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF }aws_instance
EC2のインスタンス生成を行う。
https://www.terraform.io/docs/providers/aws/r/instance.htmlami
Amazonマシンイメージは、Amazon Linux2を採用。
instance_type
インスタンスタイプの種類はさまざま。
https://aws.amazon.com/jp/ec2/instance-types/
今回は無料枠の対象であるt2.micro
を採用。
https://aws.amazon.com/jp/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=categories%23featureduser_data
ここでGitHub経由で
go-experiment
アプリをEC2インスタンス内に格納して起動。ただ、ここでエラーが起きた場合、どうやって検知できるんだろう・・・。
AWS環境へ反映
フォーマッターにかけて、
$ terraform fmt main.tfバリデーションチェックして、
$ terraform validate Success! The configuration is valid.プランチェックして、
$ terraform 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.name will be created + resource "aws_instance" "name" { + ami = "ami-0f9ae750e8274075b" + arn = (known after apply) 〜〜 省略 〜〜 + instance_state = (known after apply) + instance_type = "t2.micro" + ipv6_address_count = (known after apply) 〜〜 省略 〜〜 + tenancy = (known after apply) + user_data = "3a280d56e8e7e3aec19bad9a8f5b696867b8dd39" + volume_tags = (known after apply) + vpc_security_group_ids = (known after apply) 〜〜 省略 〜〜いざ、実行。
$ terraform apply An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create 〜〜 省略 〜〜 Enter a value: yes aws_instance.name: Creating... aws_instance.name: Still creating... [10s elapsed] aws_instance.name: Still creating... [20s elapsed] aws_instance.name: Still creating... [30s elapsed] aws_instance.name: Creation complete after 32s [id=i-0018f388ca8a7774f] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.EC2インスタンスは?
出来てる。
じゃあ、もうアプリにつながるかというと、つながらない。
セキュリティグループの定義
EC2インスタンス立てただけでは
80
番ポートが開いてないので、穴あけ作業が必要。テンプレートファイルに追記
main.tfresource "aws_instance" "go-app-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = [aws_security_group.go-app-security-group.id] 〜〜 省略 〜〜 } resource "aws_security_group" "go-app-security-group" { name = "go-app-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } output "go-app-public-dns" { value = aws_instance.go-app-server.public_dns }vpc_security_group_ids
作成したセキュリティグループとこのEC2インスタンスを紐付ける。
https://www.terraform.io/docs/providers/aws/r/instance.html#vpc_security_group_idsaws_security_group
紐付けたインスタンスへの”入り”と”出”を制御する。
https://www.terraform.io/docs/providers/aws/r/security_group.htmlingress
紐付けるサーバへの”IN"を制御。今回は
tcp:80
番ポートを開ける。特にアクセス元は絞らない。egress
紐付けるサーバの”OUT"を制御。
protocol
を-1
ないしall
にすると全ポート開放になるらしい。
https://www.terraform.io/docs/providers/aws/r/security_group_rule.html再度、AWS環境へ反映
Terraformコマンド使うくだりは同じなので省略。
applyが終わり、再度、ブラウザで見てみると、セキュリティグループの設定でポート「
80
」番を開けたので、今度は表示された。この段階でのTerraform関連のソース
1の後のStepupフェーズ
おさらい
現在のTerraformテンプレートファイルは下記。
main.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = [aws_security_group.web-server-security-group.id] user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF } resource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } output "web-server-public-dns" { value = aws_instance.web-server.public_dns }プラクティス
この分量のテンプレートファイルなら一読して理解不可能な複雑さはない。
ただ、少なくともこの後、ネットワークを構成したり、マネージドなDB、S3、ECSというようにAWSのリソースをふんだんに使い出すことになる。
そうなると、main.tf
だけでは読み解きづらくなるので、現時点でファイルを分けておく。outputs
本家HashiCorpのGitHub上の事例でも、
output
やvariable
(当記事ではまだ使ってない)は専用のファイルを設けている。
※Subnetの事例上記にならって以下のようにファイル分けしておく。
以下の通り。main.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF } resource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }outputs.tfoutput "web-server-public-dns" { value = "${aws_instance.web-server.public_dns}" }※ちなみに、これまで単に「
aws_instance.web-server.public_dns
」と書いていた部分を今回「"${aws_instance.web-server.public_dns}"
」というように修正した。
修正前の時点でも動作はしていたものの、Visual Studio CodeのTerraformプラグイン上では警告が出ていた。
今回の修正により警告がなくなった。AWSリソース別のファイル化
これも本家HashiCorpの事例や他の記事でもよく見るように、AWSリソース別にファイル化することが多い様子。
あわせて、何度も使われる(であろう)リソースはModule化も行うらしいけど、今回はまだそこまではしない。今回やるのは、EC2インスタンスリソース分とセキュリティグループ分の別ファイル化。
以下の通り。ec2.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF }security_group.tfresource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }user_dataの別ファイル化
EC2インスタンス起動時に実行するコマンドを
user_data
に指定しているが、これはいわゆるセットアップスクリプトなので別ファイルとして管理したい。
Terraformでは、ファイル操作や文字列操作ができる(テンプレートファイル内に書ける)組み込み関数が用意されているので、それを利用。web-server-setup.sh#!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experimentec2.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = file("./web-server-setup.sh") }ファイル分割後の構成
$ tree . ├── ec2.tf ├── outputs.tf ├── security_group.tf ├── terraform.tfstate └── web-server-setup.sh※
terraform.tfstate
はAWS適用後の状態を保持するファイル(terraform apply
実行により自動作成される)Providerの設定
リージョン指定の謎
これまでのテンプレートファイルで作られるのはAWSの各種リソース。
ただ、Terraformが扱うのはAWSに限ったことではない。GCPだってAzureだって扱える。
じゃあ、何をもってAWSのリソースを使うと判断できるのか。
テンプレートファイルで「aws_instance
」といった、AWSを示すリソースを指定しているのだから、当然と言えば当然なんだけど。
ここで1つ疑問。
今のところ、テンプレートファイルでリージョンの指定はしていない。
なのに、EC2インスタンスは下記のように東京リージョンに作られている。
これは、実はAWSのクライアントツールをセットアップした時に各種クレデンシャル情報に加え、デフォルトのリージョンを下記のように環境変数にセットしたためと思われる。$ env | grep AWS AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=ap-northeast-1 AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxx試しに、以下のようにリージョンを「
us-east-2
(=オハイオ)」に変えてterraform apply
を実行すると、$ env | grep AWS AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=us-east-2 AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxxとなる。
リージョンの明示的な定義
上記のようにTerraformコマンドの実行環境に依存するリージョンを明示的に指定するには「
provider
」を定義する。
https://www.terraform.io/docs/configuration/providers.html
これも、専用のテンプレートファイルとする。
ちなみに、前述の「outputs.tf
」もそうだったけど、「variables.tf
」や今回のprovider
が「providers.tf
」になるようにファイル名には複数形を用いられる(ことが多い?)。
ただし、「vpc.tf
」や「security_group.tf
」のように各サービスの場合は単数形を用いている様子。providers.tfprovider "aws" { region = "ap-northeast-1" }上記のようにしておくと、環境変数として下記のように「
us-east-2
」がセットされていても、$ env | grep AWS AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=us-east-2 AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxx下のように「
ap-northeast-1
」リージョンでインスタンスが作られる。その他プロバイダー設定
プロバイダーバージョン
プロバイダーとしてはAWSを用いる。そのプロバイダーにはバージョンがある。
今どんなバージョンが存在するかは下記参照。
https://github.com/terraform-providers/terraform-provider-aws/blob/master/CHANGELOG.mdセマンティックバージョニングを採用しているので、現時点の安定版バージョンの中でバグフィックス起因のアップデートのみ許容する指定にする。
https://www.terraform.io/docs/configuration/providers.html#version-provider-versionsprovider "aws" { version = "~> 2.16.0" region = "ap-northeast-1" }これ以外に指定可能な設定については下記参照。
https://www.terraform.io/docs/providers/aws/index.html#argument-referenceTerraform自体の設定
プロバイダーのバージョンも大事だけど、そもそもTerraform自体のバージョンも明示的に固定すべき。
これは以下のように、また別ファイル化しておく。terraform.tfterraform { required_version = "~> 0.12.0" }ファイル分割後の構成
$ tree . ├── ec2.tf ├── outputs.tf ├── providers.tf ├── security_group.tf ├── terraform.tf └── web-server-setup.sh※あえて
*.tfstate
ファイルは表示上、外した。上記以外の設定については下記参照。
https://www.terraform.io/docs/configuration/terraform.htmlこの段階でのTerraform関連のソース
今回は、まだ6ファイルだけなので、中身を全て再掲しておく。
以下は
EC2
インスタンスを立てて外部からアクセスするのに必要な定義。ec2.tf# https://www.terraform.io/docs/providers/aws/r/instance.html resource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = file("./web-server-setup.sh") }web-server-setup.sh#!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experimentsecurity_group.tf# https://www.terraform.io/docs/providers/aws/r/security_group.html resource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }↓は、単に
terraform apply
した時にコンソールに出力してほしい内容。outputs.tfoutput "web-server-public-dns" { value = "${aws_instance.web-server.public_dns}" }以降は、Terraform自体やAWSプロバイダーの定義。
terraform.tfterraform { required_version = "~> 0.12.0" }providers.tfprovider "aws" { version = "~> 2.16.0" region = "ap-northeast-1" }
- 投稿日:2019-06-27T08:30:02+09:00
Baby Step Terraform for AWS [No.01: EC2]
お題
英語的におかしいであろう表題のことは置いといて、ある(小さな)アプリをAWS上で動かそうとした時にTerraform使って少しずつ目的のものに近づけていくことを試みる。
(Terraformのテンプレートファイルの記述も、最初は愚直に、問題に直面しつつ徐々に改修していく想定)以下、構築予定。ただし、今回は1の
EC2
上でWebアプリを動かすとこのみ。
- アプリ(※)を
EC2
上で動かしてみる。- マネージドなDBに接続しにいく。
- デフォルトのネットワークを使うのではなく
vpc
、subnet
を定義する。- マルチAZ(アベイラビリティゾーン)化とロードバランサーを導入する。 (5. 画像ファイルを
S3
に置いてCloudFront
経由でアクセスさせる。) ※- アプリ(※)をDocker化して
ECS
で動かすようにする。- 最後はCI/CD。GitHubにアプリのソースをプッシュしたら自動でビルド・デプロイが走るようにする。
Route 53
使って独自ドメインでアクセスできるようにする。※Goで適当に作ったアプリ(
http://【デプロイ先ホスト】/hello
にアクセスすると「hello
」と吐くだけ)
https://github.com/sky0621/go-experiment/tree/af129be808476b56e06073775a92fb5d4cec3980Terraformのテンプレートファイル(
*.tf
)の書き方やModule化など、最初は愚直なやり方で行い、ちょっとずつベストプラクティス要素を取り入れていく。極力、無料枠内でやりくりしたいところだけど、立てるリソース如何ではなんとも。。。
https://aws.amazon.com/jp/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=categories%2312monthsfree%7Ccategories%23alwaysfreeお断り
- AWS自体の説明は薄いので「AWS触ったこともない」人に理解してもらえる内容にはなっていないです。
- 上記に関連して、AWSのアカウントは保持済み、AWSのCredentialはローカルにある前提で
terraform
コマンド叩いています。- この記事の内容で実際のサービスを本番運用しているわけではないので、このまま真似してプロダクションレディになる保証はないです。
開発環境
# OS - Linux(Ubuntu)
$ cat /etc/os-release NAME="Ubuntu" VERSION="18.04.2 LTS (Bionic Beaver)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 18.04.2 LTS" VERSION_ID="18.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=bionic UBUNTU_CODENAME=bionic# IDE - Visual Studio Code
Version: 1.35.1 Commit: c7d83e57cd18f18026a8162d042843bda1bcf21f Date: 2019-06-12T14:27:31.086Z Electron: 3.1.8 Chrome: 66.0.3359.181 Node.js: 10.2.0 V8: 6.6.346.32 OS: Linux x64 4.15.0-47-genericvscode-terraform Plugin
Name: Terraform Id: mauve.terraform Description: Syntax highlighting, linting, formatting, and validation for Hashicorp's Terraform Version: 1.3.12 Publisher: Mikael Olenfalk VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=mauve.terraform# Terraform
$ terraform version Terraform v0.12.2# tfenv
$ tfenv tfenv 0.6.0実践
1. ミニマムアプリのEC2デプロイ
アプリはGolang製でLinux(Ubuntu)環境でビルドしたバイナリ(
go-experiment
)がGitHub(※)に上がっている前提。
※ https://github.com/sky0621/go-experiment/tree/af129be808476b56e06073775a92fb5d4cec3980Projectのディレクトリ構成
EC2インスタンス1つ立てるだけということもあり、Projectルート直下に1ファイルのみ。
$ tree . └── main.tf 0 directories, 1 fileテンプレートファイルの中身チェック
main.tfresource "aws_instance" "go-app-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF }aws_instance
EC2のインスタンス生成を行う。
https://www.terraform.io/docs/providers/aws/r/instance.htmlami
Amazonマシンイメージは、Amazon Linux2を採用。
instance_type
インスタンスタイプの種類はさまざま。
https://aws.amazon.com/jp/ec2/instance-types/
今回は無料枠の対象であるt2.micro
を採用。
https://aws.amazon.com/jp/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=categories%23featureduser_data
ここでGitHub経由で
go-experiment
アプリをEC2インスタンス内に格納して起動。ただ、ここでエラーが起きた場合、どうやって検知できるんだろう・・・。
AWS環境へ反映
フォーマッターにかけて、
$ terraform fmt main.tfバリデーションチェックして、
$ terraform validate Success! The configuration is valid.プランチェックして、
$ terraform 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.name will be created + resource "aws_instance" "name" { + ami = "ami-0f9ae750e8274075b" + arn = (known after apply) + associate_public_ip_address = (known after apply) + 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 = (known after apply) + network_interface_id = (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) + tenancy = (known after apply) + user_data = "3a280d56e8e7e3aec19bad9a8f5b696867b8dd39" + volume_tags = (known after apply) + vpc_security_group_ids = (known after apply) 〜〜 省略 〜〜いざ、実行。
$ terraform apply An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create 〜〜 省略 〜〜 Enter a value: yes aws_instance.name: Creating... aws_instance.name: Still creating... [10s elapsed] aws_instance.name: Still creating... [20s elapsed] aws_instance.name: Still creating... [30s elapsed] aws_instance.name: Creation complete after 32s [id=i-0018f388ca8a7774f] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.EC2インスタンスは?
出来てる。
じゃあ、もうアプリにつながるかというと、つながらない。
セキュリティグループの定義
EC2インスタンス立てただけでは
80
番ポートが開いてないので、穴あけ作業が必要。テンプレートファイルに追記
main.tfresource "aws_instance" "go-app-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = [aws_security_group.go-app-security-group.id] 〜〜 省略 〜〜 } resource "aws_security_group" "go-app-security-group" { name = "go-app-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } output "go-app-public-dns" { value = aws_instance.go-app-server.public_dns }vpc_security_group_ids
作成したセキュリティグループとこのEC2インスタンスを紐付ける。
https://www.terraform.io/docs/providers/aws/r/instance.html#vpc_security_group_idsaws_security_group
紐付けたインスタンスへの”入り”と”出”を制御する。
https://www.terraform.io/docs/providers/aws/r/security_group.htmlingress
紐付けるサーバへの”IN"を制御。今回は
tcp:80
番ポートを開ける。特にアクセス元は絞らない。egress
紐付けるサーバの”OUT"を制御。
protocol
を-1
ないしall
にすると全ポート開放になるらしい。
https://www.terraform.io/docs/providers/aws/r/security_group_rule.html再度、AWS環境へ反映
Terraformコマンド使うくだりは同じなので省略。
applyが終わり、再度、ブラウザで見てみると、セキュリティグループの設定でポート「
80
」番を開けたので、今度は表示された。この段階でのTerraform関連のソース
1の後のStepupフェーズ
おさらい
現在のTerraformテンプレートファイルは下記。
main.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = [aws_security_group.web-server-security-group.id] user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF } resource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } output "web-server-public-dns" { value = aws_instance.web-server.public_dns }プラクティス
この分量のテンプレートファイルなら一読して理解不可能な複雑さはない。
ただ、少なくともこの後、ネットワークを構成したり、マネージドなDB、S3、ECSというようにAWSのリソースをふんだんに使い出すことになる。
そうなると、main.tf
だけでは読み解きづらくなるので、現時点でファイルを分けておく。outputs
本家HashiCorpのGitHub上の事例でも、
output
やvariable
(当記事ではまだ使ってない)は専用のファイルを設けている。
※Subnetの事例上記にならって以下のようにファイル分けしておく。
以下の通り。main.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF } resource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }outputs.tfoutput "web-server-public-dns" { value = "${aws_instance.web-server.public_dns}" }※ちなみに、これまで単に「
aws_instance.web-server.public_dns
」と書いていた部分を今回「"${aws_instance.web-server.public_dns}"
」というように修正した。
修正前の時点でも動作はしていたものの、Visual Studio CodeのTerraformプラグイン上では警告が出ていた。
今回の修正により警告がなくなった。AWSリソース別のファイル化
これも本家HashiCorpの事例や他の記事でもよく見るように、AWSリソース別にファイル化することが多い様子。
あわせて、何度も使われる(であろう)リソースはModule化も行うらしいけど、今回はまだそこまではしない。今回やるのは、EC2インスタンスリソース分とセキュリティグループ分の別ファイル化。
以下の通り。ec2.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF }security_group.tfresource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }user_dataの別ファイル化
EC2インスタンス起動時に実行するコマンドを
user_data
に指定しているが、これはいわゆるセットアップスクリプトなので別ファイルとして管理したい。
Terraformでは、ファイル操作や文字列操作ができる(テンプレートファイル内に書ける)組み込み関数が用意されているので、それを利用。web-server-setup.sh#!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experimentec2.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = file("./web-server-setup.sh") }ファイル分割後の構成
$ tree . ├── ec2.tf ├── outputs.tf ├── security_group.tf ├── terraform.tfstate └── web-server-setup.sh※
terraform.tfstate
はAWS適用後の状態を保持するファイル(terraform apply
実行により自動作成される)Providerの設定
リージョン指定の謎
これまでのテンプレートファイルで作られるのはAWSの各種リソース。
ただ、Terraformが扱うのはAWSに限ったことではない。GCPだってAzureだって扱える。
じゃあ、何をもってAWSのリソースを使うと判断できるのか。
テンプレートファイルで「aws_instance
」といった、AWSを示すリソースを指定しているのだから、当然と言えば当然なんだけど。
ここで1つ疑問。
今のところ、テンプレートファイルでリージョンの指定はしていない。
なのに、EC2インスタンスは下記のように東京リージョンに作られている。
これは、実はAWSのクライアントツールをセットアップした時に各種クレデンシャル情報に加え、デフォルトのリージョンを下記のように環境変数にセットしたためと思われる。$ env | grep AWS AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=ap-northeast-1 AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxx試しに、以下のようにリージョンを「
us-east-2
(=オハイオ)」に変えてterraform apply
を実行すると、$ env | grep AWS AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=us-east-2 AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxxとなる。
リージョンの明示的な定義
上記のようにTerraformコマンドの実行環境に依存するリージョンを明示的に指定するには「
provider
」を定義する。
https://www.terraform.io/docs/configuration/providers.html
これも、専用のテンプレートファイルとする。
ちなみに、前述の「outputs.tf
」もそうだったけど、「variables.tf
」や今回のprovider
が「providers.tf
」になるようにファイル名には複数形を用いられる(ことが多い?)。
ただし、「vpc.tf
」や「security_group.tf
」のように各サービスの場合は単数形を用いている様子。providers.tfprovider "aws" { region = "ap-northeast-1" }上記のようにしておくと、環境変数として下記のように「
us-east-2
」がセットされていても、$ env | grep AWS AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=us-east-2 AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxx下のように「
ap-northeast-1
」リージョンでインスタンスが作られる。その他プロバイダー設定
プロバイダーバージョン
プロバイダーとしてはAWSを用いる。そのプロバイダーにはバージョンがある。
今どんなバージョンが存在するかは下記参照。
https://github.com/terraform-providers/terraform-provider-aws/blob/master/CHANGELOG.mdセマンティックバージョニングを採用しているので、現時点の安定版バージョンの中でバグフィックス起因のアップデートのみ許容する指定にする。
https://www.terraform.io/docs/configuration/providers.html#version-provider-versionsprovider "aws" { version = "~> 2.16.0" region = "ap-northeast-1" }これ以外に指定可能な設定については下記参照。
https://www.terraform.io/docs/providers/aws/index.html#argument-referenceTerraform自体の設定
プロバイダーのバージョンも大事だけど、そもそもTerraform自体のバージョンも明示的に固定すべき。
これは以下のように、また別ファイル化しておく。terraform.tfterraform { required_version = "~> 0.12.0" }ファイル分割後の構成
$ tree . ├── ec2.tf ├── outputs.tf ├── providers.tf ├── security_group.tf ├── terraform.tf └── web-server-setup.sh※あえて
*.tfstate
ファイルは表示上、外した。上記以外の設定については下記参照。
https://www.terraform.io/docs/configuration/terraform.htmlこの段階でのTerraform関連のソース
今回は、まだ6ファイルだけなので、中身を全て公開しておく。
以下は
EC2
インスタンスを立てて外部からアクセスするのに必要な定義。ec2.tf# https://www.terraform.io/docs/providers/aws/r/instance.html resource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = file("./web-server-setup.sh") }web-server-setup.sh#!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experimentsecurity_group.tf# https://www.terraform.io/docs/providers/aws/r/security_group.html resource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }↓は、単に
terraform apply
した時にコンソールに出力してほしい内容。outputs.tfoutput "web-server-public-dns" { value = "${aws_instance.web-server.public_dns}" }以降は、Terraform自体やAWSプロバイダーの定義。
terraform.tfterraform { required_version = "~> 0.12.0" }providers.tfprovider "aws" { version = "~> 2.16.0" region = "ap-northeast-1" }
- 投稿日:2019-06-27T08:30:02+09:00
第1回「EC2」@Baby Step Terraform for AWS
お題
英語的におかしいであろう表題のことは置いといて、ある(小さな)アプリをAWS上で動かそうとした時にTerraformを使って少しずつ目的のものに近づけていくことを試みる。
Terraformのテンプレートファイルの記述も、最初は愚直に、問題に直面しつつ徐々に改修していく想定。
世の中の記事や書籍では最初からベストプラクティスを踏まえた設計をすることが多い。
でも、理解のためには、まず目的とする最低限の書き方で書いて、そこから「今、こうなってるからこうした方がいい」、「次にこれらを追加する想定だから、こうしておいた方がいい」といった改善をしてベストプラクティスに近づいた方がいいと思う。以下、構築予定。ただし、今回は1の
EC2
上でWebアプリを動かすとこのみ。
- アプリ(※)を
EC2
上で動かしてみる。- マネージドなDBに接続しにいく。
- デフォルトのネットワークを使うのではなく
vpc
、subnet
を定義する。- マルチAZ(アベイラビリティゾーン)化とロードバランサーを導入する。 (5. 画像ファイルを
S3
に置いてCloudFront
経由でアクセスさせる。) ※- アプリ(※)をDocker化して
ECS
で動かすようにする。- 最後はCI/CD。GitHubにアプリのソースをプッシュしたら自動でビルド・デプロイが走るようにする。
Route 53
使って独自ドメインでアクセスできるようにする。※Goで適当に作ったアプリ(
http://【デプロイ先ホスト】/hello
にアクセスすると「hello
」と吐くだけ)
https://github.com/sky0621/go-experiment/tree/af129be808476b56e06073775a92fb5d4cec3980Terraformのテンプレートファイル(
*.tf
)の書き方やModule化など、最初は愚直なやり方で行い、ちょっとずつベストプラクティス要素を取り入れていく。極力、無料枠内でやりくりしたいところだけど、立てるリソース如何ではなんとも。。。
https://aws.amazon.com/jp/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=categories%2312monthsfree%7Ccategories%23alwaysfreeお断り
- AWS自体の説明は薄いので「AWS触ったこともない」人に理解してもらえる内容にはなっていないです。
- 上記に関連して、AWSのアカウントは保持済み、AWSのCredentialはローカルにある前提で
terraform
コマンド叩いています。- この記事の内容で実際のサービスを本番運用しているわけではないので、このまま真似してプロダクションレディになる保証はないです。
開発環境
# OS - Linux(Ubuntu)
$ cat /etc/os-release NAME="Ubuntu" VERSION="18.04.2 LTS (Bionic Beaver)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 18.04.2 LTS" VERSION_ID="18.04" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=bionic UBUNTU_CODENAME=bionic# IDE - Visual Studio Code
Version: 1.35.1 Commit: c7d83e57cd18f18026a8162d042843bda1bcf21f Date: 2019-06-12T14:27:31.086Z Electron: 3.1.8 Chrome: 66.0.3359.181 Node.js: 10.2.0 V8: 6.6.346.32 OS: Linux x64 4.15.0-47-genericvscode-terraform Plugin
Name: Terraform Id: mauve.terraform Description: Syntax highlighting, linting, formatting, and validation for Hashicorp's Terraform Version: 1.3.12 Publisher: Mikael Olenfalk VS Marketplace Link: https://marketplace.visualstudio.com/items?itemName=mauve.terraform# Terraform
$ terraform version Terraform v0.12.2# tfenv
$ tfenv tfenv 0.6.0実践
1. ミニマムアプリのEC2デプロイ
アプリはGolang製でLinux(Ubuntu)環境でビルドしたバイナリ(
go-experiment
)がGitHub(※)に上がっている前提。
※ https://github.com/sky0621/go-experiment/tree/af129be808476b56e06073775a92fb5d4cec3980Projectのディレクトリ構成
EC2インスタンス1つ立てるだけということもあり、Projectルート直下に1ファイルのみ。
$ tree . └── main.tf 0 directories, 1 fileテンプレートファイルの中身チェック
main.tfresource "aws_instance" "go-app-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF }aws_instance
EC2のインスタンス生成を行う。
https://www.terraform.io/docs/providers/aws/r/instance.htmlami
Amazonマシンイメージは、Amazon Linux2を採用。
instance_type
インスタンスタイプの種類はさまざま。
https://aws.amazon.com/jp/ec2/instance-types/
今回は無料枠の対象であるt2.micro
を採用。
https://aws.amazon.com/jp/free/?all-free-tier.sort-by=item.additionalFields.SortRank&all-free-tier.sort-order=asc&awsf.Free%20Tier%20Types=categories%23featureduser_data
ここでGitHub経由で
go-experiment
アプリをEC2インスタンス内に格納して起動。ただ、ここでエラーが起きた場合、どうやって検知できるんだろう・・・。
AWS環境へ反映
フォーマッターにかけて、
$ terraform fmt main.tfバリデーションチェックして、
$ terraform validate Success! The configuration is valid.プランチェックして、
$ terraform 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.name will be created + resource "aws_instance" "name" { + ami = "ami-0f9ae750e8274075b" + arn = (known after apply) 〜〜 省略 〜〜 + instance_state = (known after apply) + instance_type = "t2.micro" + ipv6_address_count = (known after apply) 〜〜 省略 〜〜 + tenancy = (known after apply) + user_data = "3a280d56e8e7e3aec19bad9a8f5b696867b8dd39" + volume_tags = (known after apply) + vpc_security_group_ids = (known after apply) 〜〜 省略 〜〜いざ、実行。
$ terraform apply An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create 〜〜 省略 〜〜 Enter a value: yes aws_instance.name: Creating... aws_instance.name: Still creating... [10s elapsed] aws_instance.name: Still creating... [20s elapsed] aws_instance.name: Still creating... [30s elapsed] aws_instance.name: Creation complete after 32s [id=i-0018f388ca8a7774f] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.EC2インスタンスは?
出来てる。
じゃあ、もうアプリにつながるかというと、つながらない。
セキュリティグループの定義
EC2インスタンス立てただけでは
80
番ポートが開いてないので、穴あけ作業が必要。テンプレートファイルに追記
main.tfresource "aws_instance" "go-app-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = [aws_security_group.go-app-security-group.id] 〜〜 省略 〜〜 } resource "aws_security_group" "go-app-security-group" { name = "go-app-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } output "go-app-public-dns" { value = aws_instance.go-app-server.public_dns }vpc_security_group_ids
作成したセキュリティグループとこのEC2インスタンスを紐付ける。
https://www.terraform.io/docs/providers/aws/r/instance.html#vpc_security_group_idsaws_security_group
紐付けたインスタンスへの”入り”と”出”を制御する。
https://www.terraform.io/docs/providers/aws/r/security_group.htmlingress
紐付けるサーバへの”IN"を制御。今回は
tcp:80
番ポートを開ける。特にアクセス元は絞らない。egress
紐付けるサーバの”OUT"を制御。
protocol
を-1
ないしall
にすると全ポート開放になるらしい。
https://www.terraform.io/docs/providers/aws/r/security_group_rule.html再度、AWS環境へ反映
Terraformコマンド使うくだりは同じなので省略。
applyが終わり、再度、ブラウザで見てみると、セキュリティグループの設定でポート「
80
」番を開けたので、今度は表示された。この段階でのTerraform関連のソース
1の後のStepupフェーズ
おさらい
現在のTerraformテンプレートファイルは下記。
main.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = [aws_security_group.web-server-security-group.id] user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF } resource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } } output "web-server-public-dns" { value = aws_instance.web-server.public_dns }プラクティス
この分量のテンプレートファイルなら一読して理解不可能な複雑さはない。
ただ、少なくともこの後、ネットワークを構成したり、マネージドなDB、S3、ECSというようにAWSのリソースをふんだんに使い出すことになる。
そうなると、main.tf
だけでは読み解きづらくなるので、現時点でファイルを分けておく。outputs
本家HashiCorpのGitHub上の事例でも、
output
やvariable
(当記事ではまだ使ってない)は専用のファイルを設けている。
※Subnetの事例上記にならって以下のようにファイル分けしておく。
以下の通り。main.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF } resource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }outputs.tfoutput "web-server-public-dns" { value = "${aws_instance.web-server.public_dns}" }※ちなみに、これまで単に「
aws_instance.web-server.public_dns
」と書いていた部分を今回「"${aws_instance.web-server.public_dns}"
」というように修正した。
修正前の時点でも動作はしていたものの、Visual Studio CodeのTerraformプラグイン上では警告が出ていた。
今回の修正により警告がなくなった。AWSリソース別のファイル化
これも本家HashiCorpの事例や他の記事でもよく見るように、AWSリソース別にファイル化することが多い様子。
あわせて、何度も使われる(であろう)リソースはModule化も行うらしいけど、今回はまだそこまではしない。今回やるのは、EC2インスタンスリソース分とセキュリティグループ分の別ファイル化。
以下の通り。ec2.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = <<EOF #!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experiment EOF }security_group.tfresource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }user_dataの別ファイル化
EC2インスタンス起動時に実行するコマンドを
user_data
に指定しているが、これはいわゆるセットアップスクリプトなので別ファイルとして管理したい。
Terraformでは、ファイル操作や文字列操作ができる(テンプレートファイル内に書ける)組み込み関数が用意されているので、それを利用。web-server-setup.sh#!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experimentec2.tfresource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = file("./web-server-setup.sh") }ファイル分割後の構成
$ tree . ├── ec2.tf ├── outputs.tf ├── security_group.tf ├── terraform.tfstate └── web-server-setup.sh※
terraform.tfstate
はAWS適用後の状態を保持するファイル(terraform apply
実行により自動作成される)Providerの設定
リージョン指定の謎
これまでのテンプレートファイルで作られるのはAWSの各種リソース。
ただ、Terraformが扱うのはAWSに限ったことではない。GCPだってAzureだって扱える。
じゃあ、何をもってAWSのリソースを使うと判断できるのか。
テンプレートファイルで「aws_instance
」といった、AWSを示すリソースを指定しているのだから、当然と言えば当然なんだけど。
ここで1つ疑問。
今のところ、テンプレートファイルでリージョンの指定はしていない。
なのに、EC2インスタンスは下記のように東京リージョンに作られている。
これは、実はAWSのクライアントツールをセットアップした時に各種クレデンシャル情報に加え、デフォルトのリージョンを下記のように環境変数にセットしたためと思われる。$ env | grep AWS AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=ap-northeast-1 AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxx試しに、以下のようにリージョンを「
us-east-2
(=オハイオ)」に変えてterraform apply
を実行すると、$ env | grep AWS AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=us-east-2 AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxxとなる。
リージョンの明示的な定義
上記のようにTerraformコマンドの実行環境に依存するリージョンを明示的に指定するには「
provider
」を定義する。
https://www.terraform.io/docs/configuration/providers.html
これも、専用のテンプレートファイルとする。
ちなみに、前述の「outputs.tf
」もそうだったけど、「variables.tf
」や今回のprovider
が「providers.tf
」になるようにファイル名には複数形を用いられる(ことが多い?)。
ただし、「vpc.tf
」や「security_group.tf
」のように各サービスの場合は単数形を用いている様子。providers.tfprovider "aws" { region = "ap-northeast-1" }上記のようにしておくと、環境変数として下記のように「
us-east-2
」がセットされていても、$ env | grep AWS AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx AWS_DEFAULT_REGION=us-east-2 AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxxxxxxx下のように「
ap-northeast-1
」リージョンでインスタンスが作られる。その他プロバイダー設定
プロバイダーバージョン
プロバイダーとしてはAWSを用いる。そのプロバイダーにはバージョンがある。
今どんなバージョンが存在するかは下記参照。
https://github.com/terraform-providers/terraform-provider-aws/blob/master/CHANGELOG.mdセマンティックバージョニングを採用しているので、現時点の安定版バージョンの中でバグフィックス起因のアップデートのみ許容する指定にする。
https://www.terraform.io/docs/configuration/providers.html#version-provider-versionsprovider "aws" { version = "~> 2.16.0" region = "ap-northeast-1" }これ以外に指定可能な設定については下記参照。
https://www.terraform.io/docs/providers/aws/index.html#argument-referenceTerraform自体の設定
プロバイダーのバージョンも大事だけど、そもそもTerraform自体のバージョンも明示的に固定すべき。
これは以下のように、また別ファイル化しておく。terraform.tfterraform { required_version = "~> 0.12.0" }ファイル分割後の構成
$ tree . ├── ec2.tf ├── outputs.tf ├── providers.tf ├── security_group.tf ├── terraform.tf └── web-server-setup.sh※あえて
*.tfstate
ファイルは表示上、外した。上記以外の設定については下記参照。
https://www.terraform.io/docs/configuration/terraform.htmlこの段階でのTerraform関連のソース
今回は、まだ6ファイルだけなので、中身を全て再掲しておく。
以下は
EC2
インスタンスを立てて外部からアクセスするのに必要な定義。ec2.tf# https://www.terraform.io/docs/providers/aws/r/instance.html resource "aws_instance" "web-server" { ami = "ami-0f9ae750e8274075b" # https://aws.amazon.com/jp/amazon-linux-2/ instance_type = "t2.micro" # https://aws.amazon.com/jp/ec2/instance-types/ vpc_security_group_ids = ["${aws_security_group.web-server-security-group.id}"] user_data = file("./web-server-setup.sh") }web-server-setup.sh#!/bin/bash sudo yum -y install git git clone https://github.com/sky0621/go-experiment.git cd go-experiment ./go-experimentsecurity_group.tf# https://www.terraform.io/docs/providers/aws/r/security_group.html resource "aws_security_group" "web-server-security-group" { name = "web-server-security-group" ingress { from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = ["0.0.0.0/0"] } }↓は、単に
terraform apply
した時にコンソールに出力してほしい内容。outputs.tfoutput "web-server-public-dns" { value = "${aws_instance.web-server.public_dns}" }以降は、Terraform自体やAWSプロバイダーの定義。
terraform.tfterraform { required_version = "~> 0.12.0" }providers.tfprovider "aws" { version = "~> 2.16.0" region = "ap-northeast-1" }
- 投稿日:2019-06-27T06:23:51+09:00
JAWSUG HPC: ParallelCluster Workshop
JAWSUG HPC支部 第16回勉強会 ParallelCluster Workshop
https://jawsug-hpc.connpass.com/event/132565/
https://github.com/porcaro33/aws-parallelcluster-workshopParallelCluster Workshop Environment
1. What is ParallelCluster?
- AWS ParallelCluster は、AWS がサポートするオープンソースのクラスター管理ツールです。このツールは、AWS クラウドでハイパフォーマンスコンピューティング (HPC) クラスターを簡単にデプロイおよび管理するのに役立ちます。オープンソース CfnCluster プロジェクト上に構築されている AWS ParallelCluster を使用すると、AWS にすばやく HPC コンピューティング環境を構築できます。必要なコンピューティングリソースと共有ファイルシステムが自動的に設定されます。AWS ParallelCluster は、さまざまなバッチスケジューラ (AWS Batch、SGE、Torque、Slurm) で使用できます。AWS ParallelCluster では、クイックスタート PoC (概念実証) および本番稼働用デプロイのいずれも可能です。また、AWS ParallelCluster には、DNA シーケンスワークフロー全体を自動化するゲノミクスポータルなど、高度なワークフローを構築することもできます。
- https://docs.aws.amazon.com/ja_jp/parallelcluster/latest/ug/what-is-aws-parallelcluster.html
2. Infrastructure as Code x GitOps
- シンプルな設定ファイルとスクリプトでクラスターを記述できるのでIaCにもってこい。
- Gitでバージョン管理して、誰がいつ何を変更したかもバッチリ
CentOS7 AMI Subscribe: https://aws.amazon.com/marketplace/pp/B00O7WM7QW?qid=1561718024960&sr=0-1&ref_=srh_res_product_title
3. Build Infrastructure
Prerequisites
- まずはAWSにログイン https://console.aws.amazon.com/
- Oregonリージョンに移動、ログインしたユーザがAdmin権限であることを確認してください。
Create KeyPair
- Bastion Serverにアクセスするのに使用するKeyPairを作成します。
- 後ほどCloudFormationを実行するときにKeyNameを入力するのでメモしといてください。
AWS Console -> EC2 -> Key Pairs -> Create Key Pair -> Enter key name -> Create![]()
Run CloudFormation
ParallelClusterで構築するクラスターの管理ノード兼踏み台ノードとして、CentOS7をPublic Subnetに構築します。このワークショップではCloudFormationで必要な環境を構築してしまいます。
- VPC
- Subnets
- SecurityGroup
- Bastion EC2 (CentOS7)
- Bastion Instance Role
- S3 Bucket
- CodeCommit repo
- ...etc
CloudFormationテンプレートをGithubからダウンロード
https://raw.githubusercontent.com/porcaro33/aws-parallelcluster-workshop/master/pcluster_infrastructure.ymlNext -> checkin at "I acknowledge that AWS CloudFormation..." -> Create
CloudFormaitonが収束したら、Outputsを見てください。ここの情報をあとでスクリプト編集をするときに使います。このBrowser Tabは残しておいてください。
4. Build Pipeline for ParallelCluster
Login to the bastion server
- CloudFormationで作成したCentOS7サーバにログインします。お好きなssh clientでログインしてください。ユーザ名は"centos"です。
ssh -i <path_to_keypair> centos@<IP_from_CF_OUTPUT>
- parallelclusterはインストール済みです。確認してみましょう。またhomeに".parallelcluster"というフォルダがあるのを確認しましょう。
pcluster version ls -la ~/Create SSH key
- CodeCommitにアクセスするためのsshkeyを作成します。public keyを表示して内容をコピーしましょう。
ssh-keygen -f ~/.ssh/id_rsa -q -N "" cat ~/.ssh/id_rsa.pub
- AWS Console -> IAM -> Users -> pcluster-admin -> Upload SSH public key -> public key貼り付け -> Upload SSH public key -> copy "SSH key ID"
![]()
SSH Config
- CodeCommitを使うための設定をしていきます。configファイルに先ほどコピーしたSSH key IDをセットします。
cd git clone https://github.com/porcaro33/aws-parallelcluster-workshop.git cd aws-parallelcluster-workshop/ vi config
- SSH key IDをセットしたconfigファイルを.sshフォルダにおいて、パーミッションを変更します。最後にCodeCommitへのsshを確認します。
cp ~/aws-parallelcluster-workshop/config ~/.ssh/config chmod 600 ~/.ssh/config ssh git-codecommit.us-west-2.amazonaws.comWorking on CodeCommit
- CodeCommitからgit cloneして空のローカルリポジトリを作っておきます。
cd git clone ssh://git-codecommit.us-west-2.amazonaws.com/v1/repos/pcluster-git
- いくつかのファイルとフォルダをgithubからコピーして、CodeCommitにpushします。
cp -r ~/aws-parallelcluster-workshop/configs ~/pcluster-git cp -r ~/aws-parallelcluster-workshop/projects ~/pcluster-git cp -r ~/aws-parallelcluster-workshop/appspec.yml ~/pcluster-git/appspec.yml cd ~/pcluster-git git add . git commit -m "initial commit" git push origin master
ここまでで、CodeCommitの準備はできました。ここからはCodePileline, CodeDeployを設定して、Pipelineを完成させます。
Create Pipeline to S3
AWS Cosole -> CodePipeline -> Create pipeline
- Pipeline name : pcluster-pipeline
- Service role : Existing service role
- Role name : -CodePipelineServiceRole-XXXXXX
Source provider : AWS CodeCommit
Repository name: pcluster-git
Branch name : master
click "Next"
click "Skip build stage"
Deploy providor : Amazon S3
Region : US West - (Oregon)
Bucket :
Check-in : Extract file before deploy
click "create pipeline"
これでParallelclusterで実行するpost_installスクリプトをCodeCommitからS3に送るPipelineができました。Pipelineを作成した時点で一回実行されるのでSucceededになっていることを確認してください。
Create Pipeline to EC2
- CodeDeploy -> Applications -> Create application
![]()
- Application name : deploy2ec2
- Compute platform : EC2/On-premises
Click "Create application"
Enter a deployment group name : pcluster-mgr
Choose a service role : -CodeDeployServiceRole-XXXXXXXX
Deploy type : In-plane
Environment configuration : Amazon EC2 Instances
Key : Name
Value : CentOS7 Bastion
Uncheck : Enable load balancing
Add pipeline
pcluster-pipeline -> Edit
Edit deploy stage -> Add action
Action name : deploy2ec2
Action provider : AWS CodeDeploy
Input artifact : SourceArtifact
Application name : deploy2ec2
Deployment group : pcluster-mgr
Click "Save"
Click "Release change"
これでParallelClusterのクラスターConfigファイルを、クラスター管理ノードに送るPipelineができました。2つのデプロイがともにSucceededになっていることを確認してください。
5. Launch ParallelCluster
- pipelineが完成したので、cluster configファイルにbucket nameやsubnet idをセットしてクラスターを起動します。/home/pcluster-gitに移動して下記ファイルを編集してください。
- パラメータはCloudFormationのOutputから取ってきてください。
Edit cluster config and post_install scripts
projects/cpu/scripts/00-cluster-init.sh
- S3BUCKET
projects/cpu/scripts/00-cluster-init.sh
- S3BUCKET
config/cpu
- master_subnet_id
- compute_subnet_id
- vpc_id
- additional_sg
- key_name
- ec2_iam_role
- s3_read_resource
- s3_read_write_resource
- tags
- post_install
ファイルの修正が完了したら、CodeCommitのPush
git add . git commit -m "first cluster" git push origin masterLaunch a cluster via ParallelCluster
- Pipelineが流れるのを確認したら、実際の起動コマンドを打つ
cd ~/.parallelcluster pcluster list -r us-west-2 pcluster create cpu -c cpu
- 10分くらい待つと完了します。コンソールにIPが表示される。
- pem keyを踏み台ノードに転送して、踏み台ノードからCluster Masterノードにssh
- あとはご自由にアプリケーションをインストールして、ジョブスケジューラ使ってあそんでください。
- configファイルを修正して、git push, pcluster updateってやるとクラスターの設定が更新されます。
6. Terminate workshop environment
delete cluster
cd ~/.parallelcluster pcluster delete cpu -c cpuclean-up environment
- delete s3 objects
- remove policies from roles of codepipeline, codedeploy and cluster
- delete pipeline
- deleter deployment group
- delete deployment application
- delete cloudformation stack
fin!
注意点
- ParallelClusterは様々なサービスを組み合わせて実行されます。このワークショップのIAM Roleまわりはいいかげんな設定なので、公式ドキュメントをみて適切に設定してください。
- ParallelClusterは追加機能絶賛開発中で、今後バージョンがどんどん上がっていくので、venvとかPythonの仮想環境を準備して、複数バージョン動かせるようにしておいた方がいいと思います。
- Bugとか不可思議な挙動があったら、AWS Supportに問い合わせるよりもGitHub Issueをあげる方がレスポンスも早いし、問題も早く解決できると思います。(要英語)
- 使いこなすならGitHub Issueは全部読む。公式ドキュメントは基本的なところだけしか記述がない。
- 現在計算インスタンスタイプは1種類のみだけど、複数インスタンスタイプの機能は開発中だそうです。
- クラスター起動のログは~/.parallelcluster/pcluster-cli.logにも出てくるが、これだと何もわからないので、クラスターノード内の/var/log/cfn-init.logを見るのがオススメ。Chefのログが見える。これでどこでコケたかがよくわかる。
- post_installスクリプトでは/etc/parallelcluster/cfnconfig内のパラメータを使ってあげると色々と楽に組める。
参考資料
- https://aws-parallelcluster.readthedocs.io/en/latest/getting_started.html
- https://github.com/aws/aws-parallelcluster
- https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/tutorials-s3deploy.html#tutorials-s3deploy-acc
- https://qiita.com/yukofeb/items/e077fc8755416c904032
- https://docs.aws.amazon.com/ja_jp/parallelcluster/latest/ug/processes.html
- 投稿日:2019-06-27T01:06:03+09:00
AWS Route53 DNS設定メモ
EC2にDNSを割り当ててみる
EC2インスタンスを作成する
AWSチュートリアルWordPress ウェブサイトの起動をやっておくと簡単に確認できるEC2ダッシュボードからElastic IPを作成する
IPをメモしておくRoute53の画面にアクセスする
メニューから「ホストゾーン」を選ぶ
「ホストゾーンの作成」ボタンをクリックする
ドメイン名の入力欄にドメインを入力する
作成ボタンを押す
レコードが作成される
NSレコードとSOAレコードが自動で作成される作成したレコードを選択する
「レコードセットの作成」ボタンをクリックする
Nameのテキストボックスに「www」、Type:「A」、Value:2で設定したIPを入力
作成ボタンを押す
ブラウザで「www.ドメイン名」にアクセスする
WordPress ウェブサイトの起動をやっていると、ウェブサイトが表示される設定完了
その他DNS知識メモ
FQDN(完全修飾ドメイン)
Fully Qualified Domain Nameの略。
ホスト名などを省略せずに指定した形式のこと。例えばgoogleのドメインは「www.google.co.jp.」
www:第4レベルドメイン
google:第3レベルドメイン
co:第2レベルドメイン
jp:第1レベルドメイン
.:トップドメイン
となる。レコード
A レコード
AはAdressの略。
ドメインに対応するIPアドレス。CNAME レコード
CはCanonicalの略。
ドメイン名につけられた別名(FQDN)。
あだ名のようなもの。MX レコード
MXはMailExchangerの略。
ドメインに対するメールのサーバー(FQDN)。TXT レコード
TXTはTEXTの略。
ドメインに対するコメント。NS レコード
NSはNameServerの略。
ドメインに対するゾーンファイル中身で、DNSサーバー名。SOA レコード
SOAはStart Of Authorityの略。
ドメインに対するゾーンファイルの中身で、ゾーンの管理のための情報。参考
- 投稿日:2019-06-27T00:08:54+09:00
Nuxt.jsとAWSで招待状webページを作ったまとめ
概要
2018年に結婚しました!ので!
ここはエンジニアらしくパーティ招待状Webページをつくってみることにしたのが始まりです。とりあえず無事にパーティも終わったので、開発時の記憶をさかのぼりながら残す備忘録ですが
申請から開発まで一通り殴り書くので、何かしら参考になれば幸いです。┗(^o^)┛
…ちょっと前の記憶を掘り起こしながらやるので、間違って書いてそうなところもある気がしますが温かい目で御覧ください![]()
開発環境はMacなので、Winの方は適宜読み替えていただけると幸いです
![]()
AWS上で日本語表示できている部分は、日本語の画面で説明してる…はずです成果物
webページ
ざっくり3つの画面構成です。(3つ目はフリー素材やOSSの情報なので割愛)
GoogleMapや開催日を記載したホーム画面と、実際に参加者の情報を登録してもらう登録画面の2つです。
ホーム画面 登録画面 システム構成
Route53でドメインをとったうえで、「必要なときに必要な程度稼働してくれる」実運用を考えながら、今回そこまでやる必要はないシステムを作ってます。先人たちの知恵借りまくりです。
Github
書いたコードは、以下の2つ。
システム リンク webページ https://github.com/tyabata/web-invite api(lambda) https://github.com/tyabata/lambda-api-invite 個人情報とかcommitに入れちゃったのでgitのcommitログだけ消し去ってます
![]()
採用した技術
関連キーワードの羅列。詳細は次項から説明します。
Server Side
AWS
Lambda
DynamoDB
API Gateway
CloudFront
Route53
S3
理由
ちょっと前に自作IoTでGCP使ったので今回はAWS。
業務でスマートスピーカー開発してたときに触ったけど雰囲気でやってたので
復習でもしようかなという気持ちで選択しました。Client Side
Nuxt.js
Vue.js
Vuex
Vuetify
TypeScript
axios
PWA
PostCSS
直近の業務で、React + Reduxを使っていたので今回はVue。
上記にいろいろ陳列してますがnuxt-ts
でほぼ全て用意してるので、PostCSS以外はだいたいコマンド一発。それが今年の1月末の頃…
nuxt-tsは2019/4/5ぐらいにnuxtに統合され 導入方法が変わっています
https://github.com/nuxt/nuxt.js/releases/tag/v2.6.0Nuxt v2.4.0 is finally here ?
— Nuxt.js (@nuxt_js) 2019年1月28日
Official TypeScript support, smart prefetching and many more features & bug fixes.https://t.co/Ljf29xYvXi「nuxtのconfigにtsの設定いれるのつら」
と思いながら格闘して環境構築完了した後日(2019/1末頃)、Nuxt公式が上をツイートしててnuxt-ts
を知り、結果的にはほぼ何もせず 「TypeScript」で「Vue+Vuex」を作る環境ができました。型はいいぞぉ
型により構造の把握が楽になるし、ちゃんと書けてれば静的に問題に気付けるし以下略開発手順概要
とりあえずAWSで登録をすませます
https://aws.amazon.com/jp/register-flow/複数人で開発するならIAMとかで管理アカウントと分けましょう。と言いたいとこですが
今回は一人で かつ お仕事ではないので端折ります。Server Side
まずは「参加者情報を登録するAPI」「Webサーバの代わりにs3を使う」という流れ
Route53
でドメイン取得 ->Certificate Manager
でSSL証明書取得s3
準備 (linkのみ紹介)aws-cli
の導入Lambda
準備からDynamoDB
にデータ登録までAPI Gateway
とLambda
を接続CloudFront
でS3
とAPI Gateway
のマルチオリジンにバックポストする際の設定必要なとき以外、見る必要も見られることもない招待ページなので
コンピューティング時間を減らして省エネ運用の構成をとって…いるように見せかけてやたら色々準備したのは勉強がてら実際に使うことを考えた構成を目指してみたという具合です。Client Side
Nuxt.jsを利用してページを開発 -> index.htmlを出力してS3にアップロードするまで
を以下の手順で説明していきます。
nuxt-ts
で開発できる下準備- ページを作る
- index.htmlを出力する
- S3にアップロードする
開発詳細 : Server Side
ここからが本題です
ドメイン取得と証明書作成まで
Route53でドメイン取得
まずはドメインを取得します。
新しいドメインの登録 - Amazon Route 53
- https://console.aws.amazon.com/route53/ でRoute53を開く
- 登録方法
- 初めて : [Domain Registration] の [Get Started Now]を選択
- 二回目以降 : [Registered Domains]を選択
画像は省きますが、基本は同じ。
リンク先の説明にならって作業をすれば、自分のドメインがつくれます。
トップドメインによってお値段が異なるので、今回は安価でよくみる.net
を選択
- トップドメイン以降の自分でほしい名前を入力
- チェックの結果がOKであれば、それをカートに入れる (
Add to cart
)- 連絡先などを入力して進むと登録完了
- 登録したドメインが [Domain registration in progress]の状態からしばらく待つと[All Contacts]になれば完了。SOAレコードとかも同時に作成済みの状態になります。
ドメイン作成はここまでですが、これだけは登録したタイミングで課金が発生します。
Certificate Managerで証明書作成
さすがにwebエンジニアとしてhttpのリンクで友人だけでなく、嫁の知人含めて招待ページ登録してねー。と公開するのは社会の窓全開でご挨拶してる気がするのでサボらずちゃんと作成します。
初回は
[Provision certificates(証明書のプロビジョニング)] => [Get Started(今すぐ始める)] => [Request a Certificated(署名書のリクエスト)]
と選択していくと以下のような画面になると思います。ドメイン名の入力欄に先程登録したドメインを入れましょう。
ここではワイルドカード証明書のリクエストもできるので、私は*.hoge.net
のような名前で証明書を作成しました。
この後、進めていくとドメイン所有者(つまり自分)に下記のようなメールが飛びます。
DomainやAccountIDや取得したRegionなどに問題がなければ、メールに記載されている
To approve this request, go to~
と書いてあるあたりのリンクから遷移して承認完了させます。
下記のような画面まで行けば、証明書の作成まで完了です。
実際に証明書を設定したりするのはCloudFrontあたりを扱う項へ。
S3の準備
ほか項目含めて全部書くと、やたら長いドキュメントになるので備忘録としてリンクだけ。
S3 バケットを作成する方法 - Amazon Simple Storage Serviceバケット作成後に追加で設定したものは
- バージョニングの有効化 + ライフサイクルから旧バージョンに対する削除の設定
- 本番リリース後に問題発覚して戻すことがある場合、バージョン指定で戻せる
- しかし一度動いてしまえば1日以上たっても変わらない
- cliコマンドからのデプロイするために対象アカウントのみ書き込みを有効化
- それ以外の全ユーザーに対してはReadのみ有効
ライフサイクルの設定については、作成したバケットの上部にある「管理」タブから
[ライフサイクルルールの追加]という項目からできます。
上記通り、一時的なロールバックを考慮してバージョニングの有効化をしたので
一日以上たった過去バージョンは削除する。というライフサイクルを設定しました。
(結果的に不要でした)アクセス権限については、CloudFrontからアクセスが前提なので
本当は全ユーザー有効設定ではなくCloudFrontからのアクセスに対してReadを与えるような設定が良いと思います。
…色々調べながらやってたので、全ユーザーがreadできる方が都合がよかったのです…w
(といってもさすがにURLは公開してないです)とりあえずこのタイミングでは、表示確認のための
index.html
に適当になんか書いたものをバケットのrootにおいといてください。設定についてはこちらを参考にしてみてください。
CloudFront ディストリビューションからのみ S3 バケットへのアクセスを許可するアクセス権限は適切にね!!
aws-cliの準備
Lambdaやs3にデプロイをするために、AWS用のコマンドラインツールである
aws-cli
からデプロイする準備をします。IAMでユーザーの作成
aws-cliでアクセスする際に使うユーザー設定を行います。
admin使ってもできるんですが、お勉強とお作法的に分けます。下記リンク等を参考に必要な情報を作成します。
最初の IAM 管理者のユーザーおよびグループの作成 - AWS Identity and Access ManagementIAMから、左カラムのナビゲーションメニューから
ユーザー
を選択し、上部にあるユーザーを追加
を選択します。
すると下の画像のように、ユーザーを追加するための設定画面が出てくるので、下の表のように設定していってください。
項目 設定 補足 ユーザー名 cli ※なんでもOK アクセスの種類 プログラムによるアクセス グループの作成 あとで説明 既存のポリシーを直接アタッチすることもできます タグ なし IAMの管理用です。個人開発で特にいらないので今回は省略 ここまで入力すると、確認の表示が出てくるので問題なければ次に進むと以下のようにアクセスキーとシークレットアクセスキーが取得できます。
どこかにメモっておきましょう。あとのcli設定に使いますIAMの概念(雑まとめ
グループ作成について説明をしていきます。
AWSに初めて触るとロール
ユーザー
グループ
ポリシー
といろんな言葉がでてきて
チンプンカンプンになる(な気もしてる)ので、私の雑まとめです。
- ポリシーは 「AというポリシーはDynamoへのRead権限をもつ」といったルール的なもの
- ユーザー、グループ、ロールはそれぞれポリシーをアタッチして使う
- ユーザーやグループは人の管理に使う
- ロールはシステムで使う
という雰囲気理解です。とりあえず関係性が雰囲気でもわかればOKかと。
上の画像で線を足し忘れましたが、ユーザーにポリシーをアタッチできます。
が、複数で開発する場合は「開発」というグループをつくってユーザーをそこに紐づけていくことで、わざわざ個別にポリシーをアタッチする手間を一つにまとめられる利点等があると思います。
- Lambdaにデプロイするには
AWSLambdaFullAccess
ポリシー- S3にデプロイするには
AmazonS3FullAccess
ポリシーといった具合で今回は
項目 設定値 ユーザー名 cli グループ名 deployment ポリシー(グループに対して) AWSLambdaFullAccess,AmazonS3FullAccess を設定しています。
「勉強だしとりあえず全権限ふっておけ」といった場合は
AdministratorAccess
を設定すればOKです。
仕事でやるなら用法用量はまもりまs(ryaws-cli導入
brewで入れました。brew update行ってinstallから始めていきます。
brew install awscli [~] aws --version 13:29:19 aws-cli/1.16.80 Python/3.7.1 Darwin/18.0.0 botocore/1.12.70次にアクセスキーの設定をします。
[~] aws configure 14:52:40 AWS Access Key ID [****************XXXX]: AWS Secret Access Key [****************XXXX]: Default region name [us-east-1]: Default output format [None]:
Access Key ID
やAWS Secret Access Key
は先程取得した値を設定してください。
regionはとりあえずTokyo(ap-northeast-1)とかでもいいと思います。
Outputはjson
やText
とかありますが、これはご随意に(説明略)[~] aws s3 ls 14:52:50 2018-12-24 20:36:54 hoge.xxxx.netといったように確認コマンドで結果が返ってくればOKです。
LambdaへのdeployとdynamoDBの設定
とうとうコードがでてきますが、API側はだいぶシンプルに書いたつもりなので概要だけ。
https://github.com/tyabata/lambda-api-inviteAPIのロジック
- Lambdaに対してリクエスト
/invitees/user
へのGET と/invitees
へのPUTを処理する- 実行例外は拾って、500を返すようにしている
/invitees/user
: GET
- ページを初めて開いたときにアクセスされる。サーバ側でもつパスワードでcryptoした文字列をUserIDとして返す。UUIDは
crypto-js
を利用して時間から生成してる- ページ側がlocalstorageで保存しているので、消さない限りは再リクエストは発生しない(が、消されると新しいユーザーとして再度UUIDを発行します)
- webページ用にサーバを建てないようにした結果。こうなった
![]()
/invitees
: PUT
- ページに登録した情報とUserIDをセットにして登録リクエスト
- UserIDが正しく複合できるか確認
- 問題なければ、登録情報が正しい値かチェックする
- すべてOKであればDynamoDBに登録
という流れ。
特に嫁側の方に登録時のハードルを上げないようにしたかったので、下を意識してざっくり作りました。
- ログインせずに登録できる。
- 一度画面閉じたあとに登録情報を更新できるようにidをもたせておきたい
- 私の友達が 絶対
いたずらデバッグするので簡単に登録APIを叩かれないようにしたlambdaへのデプロイ
シンプルなAPIで aws-cliもいれたので、package.jsonに以下のように記述
"zip": "zip lambda.zip -r node_modules src", "first": "aws lambda create-function --function-name <lambdaに登録するfunction名> --zip-file fileb://lambda.zip --region <登録するlambdaのregion> --handler src/index.handler --runtime nodejs8.10 --role <登録するfunctionにアタッチするrole>"これは新規登録用。nodeとかは古いので適当に置き換えてください
![]()
アタッチしているroleについては後で説明します。コードができたら
zip化
->first
でアップロード&function作成
をしています。"deploy": "npm run zip && npm run upload", "upload": "aws lambda update-function-code --function-name <lambdaに登録したfunction名> --zip-file fileb://lambda.zip --profile cli --publish",こちらは更新用
一つ上のzip化するコマンドにあわせて、awsにアップロードしてfunctionを更新するupload
コマンド。
そして、それらを一発で行うためのdeploy
コマンドです。ここは特別に複雑なことはしてないです。
前項のaws-cli導入
でAWSLambdaFullAccess
がポリシーとしてアタッチされている状態であれば、これで新規登録や更新が完了します。
Lambdaで登録したregionで開くと、上の通りに登録されていることが確認できると思います。そしてLambdaにアタッチしているroleですが、
CloudWatchにログを流す
DynamoにアクセスしてR/Wする
といったポリシーをふったroleをアタッチしています。詳細説明は省きます
![]()
ただ、Lambdaのログを流すために設定しておくことで、問題があったときにも気づけるので登録しておきましょう。
Amazon CloudWatch Logs とは - Amazon CloudWatch LogsDynamoDBへ登録
DynamoDBにデータを登録していく準備をします。
上記のリンクからテーブルの作成
を選び、テーブル名やプライマリキーを設定して作成を押します。
今回は、前項あたりで説明した UserIDをプライマリキーに設定するため、uid:文字列
として設定しました。
…うろ覚えですが、コレ以上はDynamoDBでの設定はなかったはずです。コード上ではここらへん
https://github.com/tyabata/lambda-api-invite/blob/master/src/dynamo.js#L30普通のDBみたいにテーブル定義がどうとか行わなくても、データをputすればそのobjectの要素通りにカラムを自動で作ります。そこらへんはお手軽。
ただ、PUTするときに空文字とかを入れようとするとエラーになるので、チェックして弾いてあげるか
設定されていないときのデフォルト値が必要であれば、登録前に足してあげましょう。const AWS = require('aws-sdk'); // 登録先のregionを設定する AWS.config.update({ region: 'us-east-1' }); const docClient = new AWS.DynamoDB.DocumentClient(); docClient.put( { // テーブル名 TableName: "table名をここに", Item: item }, (error, data) => { // 割愛コード概要はこんな感じです。登録などに必要な権限的なものは、実行しているLambdaにアタッチされているロールのポリシーに依存しています。
前項でIAMでユーザーを作ったときと同様に
- IAMの左カラムの
ロール
からロールを作成
- ロールを使用するサービスとして Lambdaを選択
- ロールにアタッチするポリシーとして
AmazonDynamoDBFullAccess
をセット- タグは任意(管理用です)
- 最後にロール名とロールの説明を書いて完了。
ちなみにFullAccessはいらないぜ。といった場合は、他にもロールがあるのですが適切なものがない場合は独自でポリシーを作成することもできます。が、これもここでは省略します。
動作確認
とりあえずここまでちゃんと設定できているか確認のためにテストしてみましょう。
直接リクエスト送るには、この先のAPI Gatewayへの登録などが必要になります。
しかし、Lambdaのテストを使って、試しに処理を実行させることは可能です。
Lambdaの関数の画面から、右上のテストボタンの左にあるプルダウンを選択しテストイベントの設定
を押します。
そこで、画像のようにhandlerが受け付けたときに処理するbodyを用意してあげることで 疎通確認とDynamoへの登録の確認ができると思います。API GatewayとLambdaを接続
API Gatewayを使い、そのバックポスト先として先程作成したLambdaのfunctionのARN(Amazon Resource Name)を指定することで、外からhttpリクエストでLambdaを実行できるようになります。
API Gatewayを選択すると、下のように新規作成時の画面が表示されます。
ここではREST APIと 新しいAPIを選択し、API名を入力して作成を開始します。
APIのパスとメソッドの設定
API作成後に上記のような画面になるのでアクション
からリソースの作成
を選択します。
- リソース名 : 適切な名前
- リソースパス : 今回は
/invitees
をすることで、リソース一覧にパスが追加されます。
/invitees
以下にパスを設定する場合は選択した状態で、上のようにアクション
から作成を同じ手順で行います。リクエストメソッドを足す場合も
アクション
からメソッドの作成
を選ぶと、一覧に新しいプルダウンが表示されるのでメソッドを選ぶと、下記のような画面が表示されるので
今回は 総合タイプにLambda関数
。Lambda関数に 先程作成したLambda functionのARNを設定してください。ARNは、Lambdaのfunctionの画面右上にある文字列で
arn:aws:lambda:us-east-1:111111111111:function:invitees_prod
みたいな形式のものです。
その他、regionなどはLambdaにあわせて設定して保存を押すと作成完了です。リクエスト/レスポンスの詳細設定
ここから、API Gatewayの詳細を設定していきます。
今回は作ったもののうち、PUT : /invitees
について説明していきます
項目 概要 メソッドリクエスト 作成したタイミングの値が入ってる。今回はこのまま 総合リクエスト リクエスト情報の制御的なところ。マッピングテンプレートをいじります(後ほど) Lambda 設定されているLambda functionの名前が表示されていればOK 総合レスポンス 返すレスポンスを制御するところ。ここに400や500のパターンを追加する(後ほど) メソッドレスポンス こちらも200, 400, 500の3つを追加しておく リクエストのマッピングについて
コレはパッと身よくわからなくてつらかったので、ここでは概要だけ。
詳細はリファレンスや先人の知恵をおかりするのをオススメします。
API Gateway マッピングテンプレートとアクセスのログ記録の変数リファレンス - Amazon API Gateway上記で設定した項目のうち 総合リクエストのを選択し、下部にあるマッピングテンプレートの項目を選びます。
「テンプレートが定義されていない場合」を選択し、
Content-Type
にapplication/json
を選びます。追加すると、さらに下にテンプレートを入力する欄が表示されるので、今回は下のように入力しました。
{ "method": "$context.httpMethod", "body" : $input.json('$'), "path" : "$context.resourcePath", "headers": { #foreach($param in $input.params().header.keySet()) "$param": "$util.escapeJavaScript($input.params().header.get($param))" #if($foreach.hasNext),#end #end } }とりあえずわからなくてもいいです。私もドキュメントにならった説明しかできなないです。
拡張したくなったらリファレンスを読みましょう![]()
雑にいうとAPI Gatewayで受けたリクエストを展開してLambdaのコードで受けやすい形に変換してるといったところになると思います。上のようにテンプレートにはめ込んだものを handlerの第一引数で受け取って処理を実行しています。
https://github.com/tyabata/lambda-api-invite/blob/master/src/index.js#L22レスポンスのマッピングについて
Lambdaがいくらエラーメッセージを返しても、API Gatewayは200を返してしまいます。
そこで、特定の文字列が含まれるときは400
や500
で返せるように設定します。ココらへんはシンプルに
.*"status" *: *400.*
という文字列がレスポンスに含まれていれば 400を返す
といった設定です。500も同様。次に一つページをもどって、全体の画面からメソッドレスポンスを選択し、HTTPのステータスという項目に400や500などを追加します。
これで、リクエストに対して適切なステータスコードを返せるようになります。APIのデプロイ
忘れがちですが、これをやらないと反映されないので、メソッド作成等と同様にアクションから APIのデプロイを押しましょう。
デプロイ対象のステージが表示されるので、選択してデプロイ
を押すことで初めて反映されます。…ステージの説明を書き忘れていましたが、コンソールの左にAPIごとにステージという項目があり
ソレを選択して、beta
とかprod
とかステージを分けておくと、開発環境と本番環境を分けておくことができます。例えば
beta
ステージの場合は、下のようなパスになります。
https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/beta
動作確認
最後に動作確認として、メソッドの実行画面の左側にある 雷マークがついた
テスト
を選択してテストを行います。
メソッドテストと表示されている画面に遷移したら、一番したのリクエスト本文
の項目に
リクエストするbodyの情報をセットします。右側に実行時のログが表示されます。ただしく設定ができていれば、成功時のレスポンス内容がログに表示されると思います。
CloudFrontでS3とAPI Gatewayにバックポストする
やっとServer Sideの説明の最後に来ました…! 総集編?です。
取得したドメインを設定したCloud Frontでリクエストを受け取って、s3からhtmlを返すか、APIにリクエストするかをパスによってバックポストする設定を追加していきます。
2つのオリジンの追加
- Cloud Frontを開いて、
Create Distribution
から作成Web
のGet Started
を選択Origin Domain Name
にAPI Gatewayの設定を追加していきます。https://xxxxxxxx.execute-api.us-east-1.amazonaws.com/beta
というURLなので下の通りに設定する
- Origin Domain Name
xxxxxxxx.execute-api.us-east-1.amazonaws.com
- Origin Path
/beta
- Origin IDは勝手に入力されるやつのまま
- Origin Protocol Policyは
HTTPS Only
- Behavior Settingsは以下の通り
- Viewer Protocol Policy
Redirect HTTP to HTTPS
- Allowed HTTP Methods GETとPUTが選択できる一番下の項目
- Distribution Settings
- Default Root Object :
http:web.xxx.net/
にアクセスしたときに/index.html
にアクセスするように設定する何か漏らしてる気がするけど、これで一旦作成を完了させる。
これでhomeに新しく作成されたDistributionが表示される。
作成完了には少し時間がかかりますが、作成が完了したら次にCreate Origin
でs3の設定を足していきます
Create Origin
を選択- Origin Domain Name でs3のオリジンを選択する(サジェストで表示されます)
- Origin Access Identityで
Create a New Identiy
を選択する。(理想 : こうすることで、s3へのアクセスをcloud frontのみにできる。が、今回はやってないです)これでs3のオリジンも追加しました。
アクセスしたパスでバックポストするオリジンを分ける
次に
Origins and Origin Groups
の隣のBehaviors
タブを選択して
Create behavior
を追加します。今回は、jsやcssなどをs3に置くので API以外のパスはすべてs3に流れるように設定します
で設定したのが以下の2つのパス
Default(*)
: S3にバックポストする
- Viewer Protocol Policy は
Redirect HTTP to HTTPS
- Allowed HTTP Methodsは
GET HEAD
- Cache Based on Selected Request Headersは whitelist
- Originとリクエスト情報だけforwardする(しなくてもいいけど)
invitees/*
: API Gateway にバックポストする
- GETとPUTは受け付けられるように
- Cache Based on Selected Request Headersは whitelist
- Originと一応Authorizationだけforwardしてる
- キャッシュがじゃまになるAPIしかないので、cacheの設定は問題ないように修正。
CNAMEや証明書の設定
最初に作った証明書をやっと使うときが来ました。
DistributionのGeneral
タブから、Editボタンを押し設定を編集していきます。
- CNAME(Alternate Domain Names) :
web.xxxx.net
といった証明書にあわせて自分がつけたホスト名を設定- SSL Certificate :
Custom SSL Certificate
と書いてるほうを選択して先程作成した証明書を選択する。ソレ以外はだいたいデフォの設定のままでOK
おまけ Error Pageを設定
最後におまけで、Error Pageの設定をします。
ほぼ全部のパスがs3に流れるが、適当なパスを入れるとs3のエラー画面が表示されてしまいs3使ってる感がモロバレなので、設定をします。Create Custom Error Responseを選択して
- HTTP Error Code :
403
- Error Caching Minimum TTL : よしなに
- Customize Error Response :
YES
- Response Page Path :
/error.html
- HTTP Response Code :
404
s3がなにもないパスにアクセスしたときに403を返すので、これで代わりにerror用のhtmlを表示させることができます。
動作確認
おつかれさまでした!備忘録的に書いてるので何か漏らしてる気がしますが、
これで問題がなければ 設定したドメインでアクセスしたら s3作成時においたindex.html
が表示でき、APIのパスにアクセスすればAPIの結果が返ってくるようになるはずです。…もし動かなかったらごめんなさい。
![]()
ググれば多分解決できる程度にn番煎じネタではあると思います…たぶん開発詳細 : Client Side
とりあえず一息ついたら次に表示系の開発をしていきましょう。
Serverと比べるとだいぶ楽かもです。一応補足
https://github.com/nuxt/nuxt.js/releases/tag/v2.6.0
この記事は、2019/04/05にv2.6がでるより前に作ったものの備忘録です。一応 nuxt-tsを削除してnuxtに置き換えてみたので、気になったら下の変更箇所を見てみてください
https://github.com/tyabata/web-invite/commit/6d99fbbdde76b84b6c8baa4d1c7b73f3a5683afaNuxtとTypeScriptの開発環境を構築する
ここは、僕が2019/2頃にやった
nuxt-ts
もすでに古いので、公式や他の皆々様が書いている記事をご覧になる方が良いと思います
https://ja.nuxtjs.org/guide/typescript/
create-nuxt-app
で作るところは一緒で下の設定になります。
(対話式で導入するモジュールを選んでいきます)> Generating Nuxt.js project in /Users/xxxxx/Documents/workspace/vscode/sample ? Project name sample ? Project description My sensational Nuxt.js project ? Use a custom server framework none ? Choose features to install Progressive Web App (PWA) Support, Linter / Formatter, Prettier, Axios ? Use a custom UI framework vuetify ? Use a custom test framework jest ? Choose rendering mode Single Page App ? Author name xxxxx ? Choose a package manager yarnTypeScript対応はここから下を入れてjsをtsに入れ替えていく作業になると思います。
yarn add ts-node @nuxt/typescript簡単なページはこれで完結できるのですが
webpackがラップされてる感じなのでアレコレ拡張するのが煩わしく感じることもあると思うので、プロダクトにあわせて使う使わないは判断すれば良いかなと思います。ページを作る
といっても、基本はVueとVuexです。
- Nuxtにのっとって書く
- axiosで用意したAPIと通信をする
- Vuetifyで基本的なデザイン作成
- ロジックの分離を意識する
といったぐらいのことしか…やってないかも
![]()
とりあえず書きます!create-nuxt-appから少しだけ設定を変える
create-nuxt-app
をつくると、ルートにstore
やpage
といったフォルダができます。
ちょっぱやで作る分にはいいのですが、気になる人(自分含む)は
例えばsrc
というフォルダ以下に移したい場合はsrcDir
というフィールドに設定します。nuxt.config.tsconst config: NuxtConfiguration = { srcDir: 'src/', mode: 'spa', ...また、cssを書く用にPostCSSを入れたかったので
ここに、ページで使うcssを設定
https://github.com/tyabata/web-invite/blob/master/nuxt.config.ts#L47そして
nuxt.config.js
のbuild以下に下のように設定を入れました。nuxt.config.tsbuild: { extend(config: any, context: any) { }, cssSourceMap: true, postcss: { plugins: { 'postcss-import': {}, 'postcss-mixins': {}, 'postcss-preset-env': {}, 'postcss-nested': {}, // css minify csswring: {} } } }これで設定は完了です。このアプリでは
src/assets/postcsss
以下にPostCSSを使ったcss郡がおいてあります。
…post cssをassetは違う気がするな![]()
ちなみに テンプレのvueファイルでPostCSSをつかった記述をする場合は下の通りにstyleタグを足します
index.vue<template> </template> <script lang="ts"> </script> <style lang="postcss" scoped> @import '@/assets/postcss/invite.css'; </style>Vuetifyをつかってレイアウトを作る
Vue.js Material Component Framework — Vuetify.js
今回作ったページはほぼほぼVuetifyの力をつかって調整していて、cssを使ったのは微調整ぐらいでした。
Vueでマテリアルデザインに沿ったレイアウトを作るためのいろんな機能を提供してくれるので
基本的にはタグを埋め込んで終わり。という感じです。一応元Android開発をしていたので、ToolbarとかSnackbarとかきいて何かわかりますが、聞き馴染みのない方には最初苦労するかもしれません(主に検索で)
比較的公式のドキュメントもまとまっている……と思います。ページのコンポーネント構造について
そもそも今回作ったコンポーネントの構造を雑に説明するとこんな感じです。
layouts/default.vue<template> <v-app> <nuxt/> </v-app> </template>上はおまじない的なやつです。vuetifyに欠かせない部分になります。
このテンプレートをベースに、index.vueはつくっていますindex.vue(概略)<template> <v-container> <!-- 下タブの選択によってアニメーションしながら切り替えるためのコンポーネント --> <v-window /> <v-snackbar /> <v-bottom-nav /> </v-container> </template>レスポンシブなページの対応はだいたいVuetifyがやってくれるのでcssはほぼ何もがんばりません。
また、v-bottom-nav
などは、ページに下部に固定できたり、カードや検索窓なども提供されているので、アプリっぽいwebページを作るにはとても強力なツールかなと思いました(雑感)コンポーネント紹介
v-window
Windows — Vuetify.js
ページ遷移のトランジションが簡単にできちゃうコンポーネント
<v-window v-model="activePage" :touchless="true"> <v-window-item :value="'home'"> <home/> </v-window-item> <v-window-item :value="'register'"> <register/> </v-window-item> <v-window-item :value="'other'"> <other/> </v-window-item> </v-window>デフォルトは、スワイプによる画面遷移が備わっていますが
調整する暇もないし、タブによるアクションのみにするため
touchless = true
としています。あとは上の通りですが、
activePage
に入ってる値によって出す表示を切り替えているだけです。
ページによって要素の高さが違うと思うので、遷移時に気になる場合はscroll位置の調整などもしてみてください。ロジックの分離について
今回の要件だけならVueだけでもよいのですが、NuxtでVuexが簡単に入れられるので
意識して実装してみました。公式の図からVuexにおけるフロー図です。
このwebページでは、ducksパターンでstoreを作成しています。
React + Reduxでもそうなのですが、action type
やaction
やreducer(Redux)
mutation(Vuex)
をそれぞれ分けると管理がつらいので、どうせそれぞれが密なものであれば一つのファイルでまとめよう。っていうデザインパターンです。あとは
- Vueでイベントがあれば、Actionを呼ぶ
- Actionごとの処理をする
- 処理後に状態の変更をcommitをしてstateに反映する
- Vue側で新しい状態を反映する
という流れにそって書きました。
それぞれが、役割以上のロジックを持たないようにしましたが
今回はそれとあわせて一つ意識していることについて書いておきます。「templateは状態の変え方をしらない」ようにする
Smart UIにならないようにしましょうというやつです。
Atomicデザインを目指したり目指さなかったりしても、可能な限りViewで表示以外のロジックを持つべきではないと思います。例えば Vueファイルのクラス内で、ボタンが押されたときに次のページに遷移するという処理のために
this.$store.dispatch('goToNextPage', current + 1)という書き方をしたこともあると思いますが、一度ここで立ち止まって考えてみましょう。
本当に次のページは「currentに1を足した値」でいいのか
「そりゃ1ページの次は2ページでしょ?」といえば普通なのですが、それはあくまで現実の話で
コードに落とし込んだときに、この書き方は「Viewが表示の変え方を+1することと知ってしまっている」状態になります。
もしかしたら、文字列で管理されているかもしれません。例えばですが、表示系からは「次のページへいく」という振る舞いだけ呼び出し
this.$store.dispatch('goToNextPage')そして、Actionで以下のように「次のページは、今のページ + 1である」という振る舞いの詳細を定義する。
goToNextPage(context: ActionContext<IState, any>, payload: any) { const nextPage = context.state.current + 1; ...という書き方で、可能な限りViewからロジックを剥がすことで
そのコンポーネントの再利用性が高まっていくと思います![]()
余談ですが、ReduxでContainer Componentで書いたとき、connectの第三引数の
mergeProps
を使うことで、テンプレート側に状態を渡さずに dispatchイベントで現在の状態を知る。といった書き方もできるので、「テンプレート側に値を渡してたぜ!」という方はぜひぜひ。import { connect } from 'react-redux' // 色々省略 export default connect( mapStateToProps, mapDispatchToProps, mergeProps // これ }(component)redux connect mergePropsとか調べれば色々出てくるかと思います!
react-redux/connect.md at master · reduxjs/react-reduxデプロイをする
最後の工程です。
index.htmlの出力 と s3のアップロードについて書いていきます。htmlの出力
今回はサーバまわりを書いてないですが
create-nuxt-app
で作っている場合、/pages
以下のvueファイルの名前にならってエントリポイントが作成されます。そして、
nuxt generate
コマンドはそのエントリでアクセスしたときに表示されるhtmlをファイルとして出力します。例/pages - index.vue - error.vue↓このようなファイルをおいている場合、上のコマンドを実行すると
dist/index.html dist/error.htmlというファイルと関連するcssやjsが出力されていると思います。
こちらをs3にアップロードしていきます。generateの結果をs3へアップロードする
とうとう最後の作業です。以下のようなコマンドをpackage.jsonのscriptに足しています。
package.json"upload": "aws s3 sync ./dist s3://${npm_package_config_bucket} --include \"*\" --acl public-read --cache-control \"max-age=900\" --profile=cli",ビルドする内容が複雑になりすぎて、scriptに書きたくない量になりそうなら
適宜いい感じにgulpとか使ってあげてください。
npm configの変数については後述しています。まず
aws s3 sync
コマンドで、指定のフォルダをまるっとs3の特定バケットにアップロードしています
aws s3 sync ./dist s3://${npm_package_config_bucket}
ソレ以外のオプションについては↓のとおりです。
option 説明 include excludeとあわせて使うオプションです。今回は不要で実はミスしていました acl s3においたコンテンツに対するACL設定です。 public-read
で全Userが参照可能になりますcache-control s3においたコンテンツにCache-Controlを設定します profile aws-cliにdefault以外のユーザーを設定してるとき、コマンドを実行するユーザー設定を指定します 不要なファイルを上げないようにexcludeといったオプションも細かく設定していくべきなときもありますが、今回は特に気にせず
nuxt generate
した結果を全部アップロードしています。これで、全てが完了です(たぶん…)
おまけ: npm configで公開したくない値をscript上で変数化する
ついでですが
npm config
を使ってgit上にバケット名を公開しないようにしています。
privateな場所であれば気にしなくてもいいですが、今回は最終的にpublicなリポジトリに公開をするため、npm config
でscript上で使われる値をセットできます。例として
package.json
に定義したname
がappname
の場合npm config set appname:bucket <値>とsetするとscriptで
"hoge": "echo ${npm_package_config_bucket}"のように
npm_package_config_<変数名>
形でscript上で展開することもできます。動作確認
ここまでやったすべての確認をしていきましょう。
- 作成したドメインでアクセスができた
- 作成したドメインのルートにアクセスしたら index.htmlにアクセスするようになっているので、s3にデプロイしたindex.htmlが表示された
- 初回アクセスで、userIdを取得するために /inviteesのパスにリクエストして、レスポンスが返ってきた
- 入力フォームに値を入力してOKを押したら登録成功した
- DynamoDBに登録した情報が表示されている
備忘録的に順にかいてますが、3月前の記憶がベースなのでもしかしたら足りない情報があるかもしれません…
もし失敗してCloudFrontにキャッシュ設定をしたせいでs3を更新しても反映されない。
といった場合、CloudFrontの画面からInvalidation
のタブを選択して、Create Invalidation
のボタンを押すとパスを入力する画面が表示されると思います。こちらの入力欄に
/*
と入れてInvalidate
を実行すると まるっとキャッシュを消してくれます(実行完了に少しだけ時間がかかります)
注意 やりすぎると課金にかかわるので、やりすぎないかお財布と相談してください![]()
まとめ
言い訳になりますが、2019/2末ぐらいには開発完了していてそれを出してから放置をしていたため、この備忘録も当時の記憶を頼りに書いてるのでだいぶヌケモレあるんじゃないかな…と思います。
フロントにいたっては、あまり特殊実装もしてないのであとは「コード見て」状態になってますね…/(^o^)\多分おかしいところあるきがするのでアレば教えてください
とりあえず整理もせずひたすら書いたのでクソ長いですが、
もしここまで読んでくださった方がいたら本当にありがとうございました!会社だと整った環境があるので、なかなか1から作ることはないからこそ
お勉強としてやってみましたが…それでもAWSで完結するんだから楽ですよね![]()
パーティも終わってこれも書き終わったのでAWSの解約をして
趣味のゲームづくり(Unity)に力を注いでいきたいなという そんな今日このごろ