【CPUの脆弱性対応】古いAmazon Linuxのkernelを最新版にアップデートする

AWSで管理しているAmazon Linuxが古いバージョンのまま放置していることありませんか?

CPUの脆弱性対応におけるAWSの公式ドキュメント

Customers with existing Amazon Linux AMI instances should run the following command to ensure they receive the updated package: sudo yum update kernel.

カーネルをアップデートして最新のカーネルにするように書いてあるのですが、
Amazon Linuxのバージョンが古いインスタンスでは、
単にカーネルのアップデートだけでは、最新のカーネルにアップデートされません。

この記事ではその際の対応手順を説明します。

※ 説明のためにコミュニティAMIにある古いAMIを使って進めます。

対象のマシンにsshし、インフォメーションをチェック

しばらくOSをアップデートされていないインスタンスにsshします。
OSが更新されていない場合は、以下のように

$ ssh hogehost

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

https://aws.amazon.com/amazon-linux-ami/2016.03-release-notes/
Amazon Linux version 2017.09 is available.

Amazon Linux version 2017.09 is available.
Amazon Linux version 2017.09が利用できます

と表示されていますね。
あまり意識しないと見逃しがちですが、このような情報もしっかり確認しましょう。

さらにカーネルのバージョンを確認します。

$ uname -srv
Linux 4.4.5-15.26.amzn1.x86_64 #1 SMP Wed Mar 16 17:15:34 UTC 2016

カーネルも2016年に作られたバージョンのままになっていますね。

yum.confをチェックし、AMIを特定のバージョンに固定されていないか確認する

AMIを特定のバージョンに固定するにはどうすればよいですか?

上記の公式ドキュメントに書かれているようにAmazon Linuxのマシーンイメージでは、1つのバージョンから次のバージョンへ連続的な更新を提供するように設定されていますが、過去のバージョンでは/etc/yum.confreleasever変数がなかったり、リリースバージョンが固定されていることがあります。

その場合は単にyum updateだけでは最新のパッケージに更新されません。

今回対象のyum.confを見てみると

$ cat /etc/yum.conf
[main]
cachedir=/var/cache/yum/$basearch/$releasever
keepcache=0
debuglevel=2
logfile=/var/log/yum.log
distroverpkg=system-release
exactarch=1
obsoletes=1
gpgcheck=1
plugins=1
installonly_limit=3
deltarpm=0

# by default the yum configuration will point to the latest release
# of Amazon Linux AMI. If you prefer not to automatically move to
# new releases, comment out this line.
releasever=2016.03

#  This is the default, if you make this bigger yum won't see if the metadata
# is newer on the remote and so you'll "gain" the bandwidth of not having to
# download the new metadata and "pay" for it by yum not having correct
# information.
#  It is esp. important, to have correct metadata, for distributions like
# Fedora which don't keep old packages around. If you don't like this checking
# interupting your command line usage, it's much better to have something
# manually check the metadata once an hour (yum-updatesd will do this).
# metadata_expire=90m

# PUT YOUR REPOS HERE OR IN separate files named file.repo
# in /etc/yum.repos.d

releasever2016.03に固定されています。

この状態では、カーネルをアップデートしても変更されません。

$ sudo yum update kernel
Loaded plugins: priorities, update-motd, upgrade-helper
No packages marked for update

そこで更新させるためには以下のように変更する必要があります。

releasever=latest

releaseverを「latest」に変更して、カーネルをアップデート

先ほどのyum.confのリリースサーバーを最新に更新します。

$ cp -ip /etc/yum.conf  /tmp/
$ sudo vi /etc/yum.conf
$ diff /etc/yum.conf /tmp/yum.conf
17c17
< releasever=latest
---
> releasever=2016.03
#更新しました

# kernelをアップデートする
$ sudo yum update kernel
Loaded plugins: priorities, update-motd, upgrade-helper
amzn-main/latest                                                                                                                                                                                                                 | 2.1 kB     00:00
amzn-updates/latest                                                                                                                                                                                                              | 2.3 kB     00:00
Resolving Dependencies
--> Running transaction check
---> Package kernel.x86_64 0:4.9.51-10.52.amzn1 will be installed
--> Finished Dependency Resolution

Dependencies Resolved

========================================================================================================================================================================================================================================================
 Package                                                 Arch                                                    Version                                                               Repository                                                  Size
========================================================================================================================================================================================================================================================
Installing:
 kernel                                                  x86_64                                                  4.9.51-10.52.amzn1                                                    amzn-main                                                   17 M

Transaction Summary
========================================================================================================================================================================================================================================================
Install  1 Package

Total download size: 17 M
Installed size: 71 M
Is this ok [y/d/N]: y
Downloading packages:
kernel-4.9.51-10.52.amzn1.x86_64.rpm                                                                                                                                                                                             |  17 MB     00:01
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  Installing : kernel-4.9.51-10.52.amzn1.x86_64                                                                                                                                                                                                     1/1
  Verifying  : kernel-4.9.51-10.52.amzn1.x86_64                                                                                                                                                                                                     1/1

Installed:
  kernel.x86_64 0:4.9.51-10.52.amzn1

Complete!

先ほどは、アップデートするカーネルはありませんでしたが、今回はアップデートされました。

再起動後に再度インスタンスにアクセスして、カーネルがアップデートされたか確認します。

$ ssh hogehost
Last login: Sat Jan 20 02:10:39 2018 

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

https://aws.amazon.com/amazon-linux-ami/2016.03-release-notes/
No packages needed for security; 138 packages available
Run "sudo yum update" to apply all updates.
Amazon Linux version 2017.09 is available.

$ uname -srv
Linux 4.9.51-10.52.amzn1.x86_64 #1 SMP Fri Sep 29 01:16:19 UTC 2017

カーネルは4.4.5から4.9.51に上がったようですね。ところがまだ最新ではないです。

またsshを行ったら、早速情報が更新されています。

No packages needed for security; 138 packages available
Run “sudo yum update” to apply all updates.

カーネルのアップデートではなく、パッケージのアップデートを勧められています。

全てのパッケージアップデートする

そこでカーネルだけれはなく全てのパッケージをアップデートさせます。

パッケージを全てアップデートすると既存で動いているサービスに影響を与えることがあるので、バージョンの依存関係があるパッケージについては除外しましょう

今回はカーネルを最新にアップデートさせることが目標なので、上記を考慮せずに全てのパッケージをアップデートします。

$ sudo yum update
Loaded plugins: priorities, update-motd, upgrade-helper
Resolving Dependencies
--> Running transaction check
---> Package acpid.x86_64 0:1.0.10-2.1.6.amzn1 will be updated
---> Package acpid.x86_64 0:2.0.19-6.7.amzn1 will be an update
・・・(省略)

# インストールとアップデートをするパッケージが一覧されるのでチェック
========================================================================================================================================================================================================================================================
 Package                                                                Arch                                            Version                                                                Repository                                          Size
========================================================================================================================================================================================================================================================
Updating:
 acpid                                                                  x86_64                                          2.0.19-6.7.amzn1                                                       amzn-main                                           73 k
 at                                                                     x86_64                                          3.1.10-48.15.amzn1                                                     amzn-main                                           66 k
 audit                                                                  x86_64                                          2.6.5-3.28.amzn1                                                       amzn-main                                          272 k
 audit-libs                                                             x86_64                                          2.6.5-3.28.amzn1                                                       amzn-main                                           96 k
・・・(省略)
Installing for dependencies:
 libXcomposite                                                          x86_64                                          0.4.3-4.6.amzn1                                                        amzn-main                                           21 k
 libidn2                                                                x86_64                                          0.16-1.2.amzn1                                                         amzn-main                                          103 k
 libnghttp2                                                             x86_64                                          1.21.1-1.4.amzn1                                                       amzn-main                                           73 k
 libseccomp                                                             x86_64                                          2.3.1-2.4.amzn1                                                        amzn-main                                           79 k
 libunistring                                                           x86_64                                          0.9.3-6.1.amzn1                                                        amzn-main                                          419 k
 perl-Time-HiRes                                                        x86_64                                          4:1.9725-272.5.amzn1                                                   amzn-main                                           46 k
 python27-futures                                                       noarch                                          3.0.3-1.3.amzn1                                                        amzn-main                                           30 k

Transaction Summary
========================================================================================================================================================================================================================================================
Install               ( 7 Dependent packages)
Upgrade  138 Packages

Total download size: 192 M

yum updateでは、インストールを始める前に、インストールとアップデートをするパッケージが一覧されます。
その後にアップデートするか選べるので、パッケージを確認してから、動いているサービスに影響がないか確認するといいと思います。

このままアップデートを実行します。

Is this ok [y/d/N]: y

Downloading packages:
(1/145): acpid-2.0.19-6.7.amzn1.x86_64.rpm                                                                                                                                                                                       |  73 kB     00:00
(2/145): at-3.1.10-48.15.amzn1.x86_64.rpm                                                                                                                                                                                        |  66 kB     00:00
・・・(省略)
  xfsprogs.x86_64 0:4.5.0-9.21.amzn1                              yum.noarch 0:3.4.3-150.70.amzn1                            yum-metadata-parser.x86_64 0:1.1.4-10.20.amzn1                  yum-plugin-priorities.noarch 0:1.1.31-40.29.amzn1
  yum-plugin-upgrade-helper.noarch 0:1.1.31-40.29.amzn1           yum-utils.noarch 0:1.1.31-40.29.amzn1

Complete!

全てのパッケージをアップデートしました。再起動を行い、再度sshします。

$ ssh hogehost
Last login: Sat Jan 20 02:17:24 2018 

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

https://aws.amazon.com/amazon-linux-ami/2017.09-release-notes/
10 package(s) needed for security, out of 34 available
Run "sudo yum update" to apply all updates.

$ uname -srv
Linux 4.9.51-10.52.amzn1.x86_64 #1 SMP Fri Sep 29 01:16:19 UTC 2017

カーネルのバージョンはかわっていませんが、

10 package(s) needed for security, out of 34 available
Run “sudo yum update” to apply all updates.

バージョンアップ後、さらにセキュリティ問題のためアップデートするように警告されていますね。

すべでのパッケージをアップデートしたことで、最新のカーネルにアップデートできるようになりました。

パッケージ全てをアップデートする(2回目)

最新のカーネルにアップデートするべく、再度アップデートします。

$ sudo yum update
Loaded plugins: priorities, update-motd, upgrade-helper
Resolving Dependencies
・・・(省略)

Installed:
  kernel.x86_64 0:4.9.77-31.58.amzn1

Dependency Installed:
  libtool-ltdl.x86_64 0:2.4.2-20.4.8.5.32.amzn1                                                                                     nss-pem.x86_64 0:1.0.3-4.3.amzn1

Updated:
  aws-cfn-bootstrap.noarch 0:1.4-27.19.amzn1      aws-cli.noarch 0:1.14.9-1.48.amzn1         curl.x86_64 0:7.53.1-13.80.amzn1               docker.x86_64 0:17.09.1ce-1.111.amzn1            docker-storage-setup.noarch 0:0.6.0-1.18.giteb688d4.amzn1
  ec2-net-utils.noarch 0:0.5-1.34.amzn1           ec2-utils.noarch 0:0.5-1.34.amzn1          ecs-init.x86_64 0:1.16.2-1.amzn1               irqbalance.x86_64 2:1.3.0-1.26.amzn1             java-1.7.0-openjdk.x86_64 1:1.7.0.161-2.6.12.0.75.amzn1
  kernel-tools.x86_64 0:4.9.77-31.58.amzn1        krb5-libs.x86_64 0:1.15.1-8.43.amzn1       libcurl.x86_64 0:7.53.1-13.80.amzn1            nss.x86_64 0:3.28.4-12.80.amzn1                  nss-softokn.x86_64 0:3.28.3-8.41.amzn1
  nss-softokn-freebl.x86_64 0:3.28.3-8.41.amzn1   nss-sysinit.x86_64 0:3.28.4-12.80.amzn1    nss-tools.x86_64 0:3.28.4-12.80.amzn1          nss-util.x86_64 0:3.28.4-3.53.amzn1              openssl.x86_64 1:1.0.2k-8.106.amzn1
  python27.x86_64 0:2.7.12-2.121.amzn1            python27-boto.noarch 0:2.48.0-1.2.amzn1    python27-botocore.noarch 0:1.8.13-1.66.amzn1   python27-devel.x86_64 0:2.7.12-2.121.amzn1       python27-libs.x86_64 0:2.7.12-2.121.amzn1
  ruby20.x86_64 0:2.0.0.648-1.30.amzn1            ruby20-irb.noarch 0:2.0.0.648-1.30.amzn1   ruby20-libs.x86_64 0:2.0.0.648-1.30.amzn1      rubygem20-bigdecimal.x86_64 0:1.2.0-1.30.amzn1   rubygem20-psych.x86_64 0:2.0.0-1.30.amzn1
  rubygems20.noarch 0:2.0.14.1-1.30.amzn1         system-release.noarch 0:2017.09-0.1        wget.x86_64 0:1.18-3.28.amzn1

Complete!

再起動してアップデートを反映させます。


$ ssh hogehost
Last login: Sat Jan 20 02:21:06 2018

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

https://aws.amazon.com/amazon-linux-ami/2017.09-release-notes/

$ uname -srv
Linux 4.9.77-31.58.amzn1.x86_64 #1 SMP Thu Jan 18 22:15:23 UTC 2018

カーネルもCPU脆弱性の対応がされた最新のバージョンになりました。

まとめ

今回2016年のAmazon LinuxのKernelを最新版にアップデートにするには以下のような流れです。

  • yum.conf更新(リリースバージョンを最新にする)
  • yum update(1回目。2016 → 2017にコミットされたバージョンをあげる)
  • yum update(2回目。2017 → 最新の2018にコミットされたバージョンをあげる。)

何回 yum updateが必要なのかは、管理しているインスタンスのバージョンによりますが、
OSのバージョンアップは早いうちからやっていた方が明らかに楽ですし、
パッケージを全てアップデートする場合は既存のサービスに影響を与える可能性もあります。

改めてセキュリティ対策は、コツコツアップデートしていかなければという学びを得ました。

また、sshの情報にしっかり目を通すことで、どうアップデートすべきかわかるので最新のOSに保ちつつ管理していきましょう。

続きを読む

Amazon S3 に Git LFS サーバを超簡単に立てる

元ネタはこちら。
Serverless Git LFS for Game Development – Alan Edwardes
Amazon API Gateway + Amazon Lambda + Amazon S3 を使うんだそうです。

準備: AWS のアカウントをとる

AWSのアカウントがなければとります。
作成の流れは 公式のドキュメント でだいじょうぶです。

アカウントをとったら初期設定しておきます。以下のエントリが参考になりました。
AWSアカウントを取得したら速攻でやっておくべき初期設定まとめ – Qiita

サーバ作成手順

Screen_Shot_2018-01-21_at_22_29_22-fullpage.png

  • 「次へ」をクリックします → オプションの設定ページはスルーで「次へ」をクリックします。
  • 「AWS CloudFormation によって IAM リソースが作成される場合があることを承認します。」にチェックして「作成」をクリックします。

Screen_Shot_2018-01-21_at_23_05_08-fullpage.png

  • スタックのリストに新しいスタックができて(スタックがなければリロードして下さい)、「CREATE_COMPLETE」と表示されたら成功です。

Screen_Shot_2018-01-21_at_23_10_18-fullpage.png

  • スタックの詳細画面に入って、出力を確認すると、Git LFSのエンドポイントのURLが表示されているので、リンクをコピーします。

Screen_Shot_2018-01-21_at_22_34_53-fullpage.png

Git LFS に lfs.url を設定

Git LFS は導入してあることとします。

以下のコマンドで、 .lfsconfig を作成します。

git config -f .lfsconfig lfs.url <表示されたエンドポイントのURL>

git lfs env で確認して、Endpoint= の行が設定したURLになってたらOKです。

git push すると、S3の方にLFSオブジェクトがpushされるようになります。

問題点

URL を見ればわかると思いますが、この方法で作成するとリージョンが 欧州 (アイルランド) に作成されます。
東京リージョンにS3を立てたいと思って、 URL の region=eu-west-1 の部分を region=ap-northeast-1 に変えてみましたが、リージョンを変えると成功しないみたいです。

alanedwardes/Estranged.Lfs – Github

どなたか検証お願いします。 m(_ _)m

続きを読む

環境構築201801 slackbot

はじめに

環境構築時のメモです。
* Amazon Linux AMI release 2017.09
* nvm 0.33.8 (201712)
* node v9.3.0 (201712) 
* redis v2.4.10(201712)
* hubot-slack v4.4 (201712)
* nginx v1.10.3 (201708)
* pyton v3.6.0 (201712)

初期設定

command
$ cat /etc/system-release1
$ sudo yum update -y
$ sudo yum install -y git

タイムゾーンをJSTに変更する。

command
$ sudo ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
$ sudo vi /etc/sysconfig/clock
---
ZONE="Asiz/Tokyo"
UTC=false
---

OSユーザ(例:hoge)作成

command
$ useradd hoge
$ sudo su - hoge
$ cd /home/hoge
$ mkdir .ssh
$ cd .ssh
$ ssh-keygen -t rsa
$ mv id_rsa.pub authorized_keys
$ chmod 600 authorized_keys
$ cat id_rsa ※内容を接続元PCに保存 hoge.pem
※puttyの場合
・Run PuTTYgenを起動
・Load an existing private file を読み込む(load)
・Save public key hoge.ppk
・上記ファイルを"Private key file for authentication"に設定する。
・wheelユーザに追加およびパスワードなしでsudoを利用できるようにする。
$ vi /etc/group
----
wheel:x:10:ec2-user,hoge
----
$ sudo visudo
---
# %wheel        ALL=(ALL)       NOPASSWD: ALL
%hoge      ALL=(ALL)       NOPASSWD: ALL
---

Nginxインストール

$ sudo useradd www
$ sudo groupadd www
$ sudo yum install nginx
$ sudo cp /etc/nginx/nginx.conf.default /etc/nginx/nginx.conf
$ sudo vi /etc/nginx/nginx.conf
---
server{
   listen    80;
   server_name localhost;
   root ***設置場所***;
   charset utf-8;
   index index.html index.htm index.php;
   access_log /var/log/host.access.log main;
}
---
$ sudo service nginx start
$ sudo chkconfig nginx on

redisインストール

$ sudo yum --enablerepo=epel install redis
$ sudo service redis start
$ sudo chkconfig --level 35 redis on
$ sudo chkconfig --list | grep redis

nodeインストール

$ git clone git://github.com/creationix/nvm.git ~/.nvm
$ vi .bashrc
---
if [ -f ~/.nvm/nvm.sh ];then
        source ~/.nvm/nvm.sh > /dev/null 2>&1
fi
---
$ source ~/.bashrc
$ nvm ls-remote nodeバージョン一覧表示
$ nvm install v6.9.0
$ nvm alias default v6.9.0  #恒久的な切り替え
$ nvm ls-remote
$ node -v
nvm自身のバージョンアップ
text
$ cd ~/.nvm
$ git pull origin master
$ source ~/.bashrc

Python3インストール

text
$ yum install gcc gcc-c++ make git openssl-devel bzip2-devel zlib-devel readline-devel sqlite-devel
$ git clone https://github.com/yyuu/pyenv.git ~/.pyenv
$ vi ~/.bashrc
---
export PYENV_ROOT="${HOME}/.pyenv"
if [ -d "${PYENV_ROOT}" ]; then
export PATH=${PYENV_ROOT}/bin:$PATH
eval "$(pyenv init -)"
fi
---
$ sudo yum install patch
$ pyenv install 3.6.0
$ pyenv global 3.6.0
$ pip install --upgrade pip
$ pip install bigquery-python
$ pip install beautifulsoup4
$ pip install jupyter
$ jupyter notebook --generate-config
$ python -c "from notebook.auth import passwd;print(passwd())" ※設定
$ vi ~/.jupyter/ 内に jupyter_notebook_config.py
---
c.NotebookApp.ip = '*'
c.NotebookApp.open_browser = False
c.NotebookApp.password = 'sha1:*******' ※
---

Slackbot構築

Slackにhubotアプリを追加

Add apps| Slack

hubot01png.png

hubot2.png

Hubotインストール

$ cd ~/.nvm/versions/node/(バージョン)/lib/node_modules
$ npm install hubot yo generator-hubot coffee-script hubot-slack
$ cd ~
$ mkdir hello-hubot
$ cd hello-hubot
$ yo hubot
$ vi external-scripts.json
---
heroku packageを削除
---
動作確認
$ vi ./scripts/examples.coffee
---
module.exports = (robot) ->
#(コメントアウトを外す) robot.hear /badger/i, (res) ->
#(コメントアウトを外す) res.send "Badgers? BADGERS? WE DON'T NEED NO STINKIN BADGERS"
---
$ export HUBOT_SLACK_TOKEN=<API Token>
$ ./bin/hubot -a slack 

Slackでbadgerを入力しリプライを確認
hubot3.png

SlackBot設置

slack からgoogle検索ができるか試す。
下記のアンプルコードを scriptsディレクトリに保存し、hubotを起動する。

./bin/hubot -a slack

サンプルコード|SlackBot

slackから作成したアプリに「search is ***」を送信するとreplyが届く。

hubot5.png

続きを読む

AWS Auto Scaling勉強まとめ

・需要に応じて自動的にサーバーが増減し、コストカット

Auto Scaling Group
・設定した最小値~最大値に起動インスタンスを収める
・起動台数をAZ間でバランシング
・AZ障害時は他のAZでインスタンス起動

Launch Configuration
・AMIやインスタンスタイプ、IAMなどを設定して起動する

Scaling Plan
・どのようにインスタンスを起動するか
①Auto Scaling Planの維持
最小台数を維持する。
Auto Healing:インスタンスに障害発生時に自動的にサービスから切り離し、健全なインスタンスをサービスイン

②手動管理
インスタンスを手動で変更

③スケジュールベース
CLI/SDKで定義
スケーリング開始は最大2分遅れる場合があるので注意

④動的スケーリング
監視:CloudWatchに応じたインスタンスの増減

上記の複数のプランを組み合わせることも可能

ヘルスチェック
①EC2ヘルスチェック:インスタンスのステータスがrunning以外を以上と判断
②ELBヘルスチェック
・ヘルスチェックの猶予期間がある。インスタンス起動からヘルスチェック開始までの時間。アプリケーションデプロイを考慮
・異常と判断されたインスタンスは自動的に終了

クールダウン
スケーリングアクション実行後指定した時間は次にスケーリングアクションを実行しない仕組み
→インスタンス初期化中の無駄なスケーリングを回避するため
※シンプルスケーリングポリシーにのみ対応

ターミネーションポリシー
①OldestInstance/NewestInstance:起動時刻
②OldestLaunchConfiguration:最も古いLaunch Configuration
③ClosestToNextInstanceHour:課金のタイミングが近い
④Default:②③の順に適用。複数インスタンスが残ればランダム

インスタンス保護
任意のインスタンスを削除されないよう保護できる

・インスタンスのデタッチ、アタッチ、スタンバイ

ライフサイクルフック
Auto Scalingの起動/終了時に一定時間(デフォルトは1時間、最大48時間)待機させ、カスタムアクションを実行できる

・スケールアウト時の初期化処理
①設定済みのAMIを用いる
②user-dataで初期化スクリプトを実行(Bootstrap処理)
③ライフサイクルフックで初期化

・サーバーをステートレスにする
ステートレス:サーバーにセッション情報などがない。スケールアウトに向いている
ステートフル:サーバーにセッション情報あり。常にサーバー間で同期が必要なので、スケールアウトに向いていない

・突発的なスパイクには向いていない
→インスタンス作成~アプリ起動の時間がかかるため。
対応策としては、
①CloudFrontなど大きなキャパシティを持ったAWSサービスに処理をオフロードする
②スパイクを裁くのを諦め、スロットリング機能(処理性能の上限)を設ける
③一定以上の負荷を超えたら静的ページに切り替える

・ユースケース
①ELB配下のWebサーバーをAuto Scaling
→EC2は複数AZに分散し、高可用性
ELBのリクエスト数、EC2の平均CPU使用率などがトリガー

②SQSのジョブを処理するWorkerをAuto Scaling
キューのメッセージ数などがトリガー

③Blue/Green
Blue/Green
デプロイ時に、既存のインスタンスとは違うインスタンスを作成し、一気に/徐々に作成したインスタンスを使用するようにする。
移行方法は下記
・DNSのWeighted round robinを使用して、徐々にトラフィックを移行
→DNSのTTLを考慮する必要あり。
・ELBを利用して、移行する
→Elastic Container Service(ECS。Dockerコンテナを格納する場所)を利用して、新しいインスタンスを作成することも可能

In place
デプロイ時に既存のインスタンスを操作することで対応する

参考URL:http://aws.typepad.com/sajp/2015/12/what-is-blue-green-deployment.html

Elastic MapReduce(EMR)

<Hadoop(分散処理をしてくれるソフトウェア)を動かせる環境を提供してくれるサービス
参考URL:http://mgi.hatenablog.com/entry/2014/05/04/085148

参考URL:https://www.slideshare.net/AmazonWebServicesJapan/aws-black-belt-online-seminar-2017-auto-scaling

続きを読む

Amazon Translateを使ってみた&自動翻訳付きチャットを作ってみた

Amazon Translateのプレビュー申請が先日通ったので、使ってみました。us-east-1(バージニア北部)で利用しています。

Amazon Translateとは?

公式ページへどうぞ。

成果物

とにかく成果物が早く見たい方はこちらへ。

https://github.com/kojiisd/amazon-translate-chat-demo

コンソールから使ってみる

とりあえずAWSコンソールから使ってみます。100万文字で15ドルの料金のようです。

スクリーンショット 2018-01-13 16.05.42.png

選択できる言語は7つとまだ少なめですが、いずれすぐに日本語も追加されるでしょう。

image.png

入力した文字がリアルタイムに翻訳されます。(日本語が選択できないので、文章を入れても自然な文章になっているのかは判断できませんがw)

スクリーンショット 2018-01-13 16.08.49.png

APIを使ってみる

ただこれだけだとなんの面白みもないので、自動翻訳をつけたようなチャットを作ってみたいと思います。

画面の準備

以前作成したiot-demo-deviceをベースに開発します。

https://github.com/kojiisd/aws-iot-demo-device/

とりあえずこんな感じの画面を組みます。

スクリーンショット 2018-01-20 13.26.33.png

項目名 意味
Name 名前
Source Language 受信するメッセージの言語
Target Language 翻訳対象言語
Start Chat チャット開始のためのボタン
Message メッセージ
Send Message メッセージ送付用ボタン

最初は名前やメッセージ受信時の言語を選択しないとチャットを
スタートできないようにしています。
→メッセージをSubscribeする際に自身と同じ名前だったら翻訳しないようにするため。

画面下部の「Original」と「Translated」にメッセージのやりとりが記録されるように実装します。

WebSocketにAWS IoTを利用する

チャットのやりとりをするために今回WebSocketを利用しますが、一から環境を用意するのが面倒なので、AWS IoTで手を抜きます。カスタマイズなどは特に気にせずデフォルトで設定してしまってOKです。

今回はこちらの記事と同じ設定をしました。

Three.jsとAWSを連携させてIoTっぽいことしてみた

AWS IoTは受信後のアクションが必要だったので、DynamoDBに履歴として登録する体で設定をしています。

aws-sdkのフルビルド

この記事を書いている時の「v2.184.0」ではフルビルドをしないとAWS.Translateクラスが使えなかったため、公式ページを参考にビルドを実施します。

https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/building-sdk-for-browsers.html

$ git clone git://github.com/aws/aws-sdk-js
$ cd aws-sdk-js
$ npm install
$ node dist-tools/browser-builder.js all > aws-sdk-full.js

翻訳部分を開発する

肝心の翻訳ですが、こんな感じのコードにします。事前にCredentialやEndpointなど必要な情報を設定しておく必要があります。なぜPromiseが必要かは後述します。

AWS.config.credentials = new AWS.Credentials(cred.awsAccessKeyId, cred.awsSecretAccessKey);
  :
  :
function translate(message) {

  var params = {
    Text: message,
    SourceLanguageCode: srcLang,
    TargetLanguageCode: targetLang
  };

  var syncProc = new Promise(
    function (resolve, reject) {
      window.translator.translateText(params, function onIncomingMessageTranslate(err, data) {
        if (err) {
          reject("Error calling Translate. " + err.message + err.stack);
      }
        if (data) {
          resolve(data.TranslatedText);
        }
      });
    }
  );

  return syncProc;
}

翻訳語の文章の追加部分の開発

上述のtranslateメソッドを呼び出す部分ですが、メッセージ受信後のコールバックメソッドとなります。

function onMessage(message) {
  var msgJson = JSON.parse(message.payloadString);
  var addingHtml = "<tr><td>" + msgJson.name + ": </td><td>" + msgJson.message + "</td>";
  if (msgJson.name == body.name) {
    addingHtml += "<td></td><td></td></tr>"
    $("#chatArea").prepend(addingHtml);
  }
  else {
    translate(msgJson.message).then(function (result) {
      addingHtml += "<td>" + msgJson.name + ": </td><td>" + result + "</td></tr>"
      $("#chatArea").prepend(addingHtml);
    }).catch(function(error){
      alert(error);
    });
  }

メッセージを受信した際、自身の名前と同じであれば翻訳は実施しないように処理をしています。

自分ではない人からのメッセージの場合translateメソッドを呼び出しますが、Translate APIにアクセスするタイミングで非同期処理となってしまうため、前述のPromiseでこの辺の処理の順番を制御しました。

実際の動き

画面を二つ用意して、それぞれ「kojiisd1」と「kojiisd2」でチャットを開始するようにします。設定はそれぞれ以下の通りとします。

kojiisd1 kojiisd2
Source Language French English
Target Language English French

kojiisd1からは英語で話すようにし、kojiisd2からはフランス語で話をしてみます。

こんな感じで操作できました(ちょっとGIFのサイズ大きい)。

amazon-translate-chat-demo3.gif

まとめ

Amazonが提供する翻訳サービスなので、Pollyなど他のサービスとの連携も容易にできるようになると期待が持てます。これは電話会議などもAmazonが提供する翻訳サービスを使いながらリアルタイムに全て自動翻訳できる日が来そうな感じがして、期待大のサービスですね。

続きを読む

アカウント毎にAWSコンソールのヘッダ/フッタ色を変えるChrome拡張を作った

なにこれ

https://github.com/syunm1n/color-aws-frame
AWSマネジメントコンソールのヘッダ/フッタの色を変えるChrome拡張です。

こんな感じで、ログインしているIAMユーザまたはアカウントごとに色が変わります(フッタも変わります!)
御存知の通りAWSマネジメントコンソールのUIはどのアカウントでも変わらないし変更できないので、複数のアカウントにログインして作業する機会があると、「今どのアカウントの操作してるんだっけ?」と不安になったり、実際操作を間違えたりしたので予防のために作ってみました。

どうやったの

色を指定する部分は、@uiureo さんの chame を利用させていただきました。
https://github.com/uiureo/chame

上記を利用して、ユーザ/アカウント名→カラーコードの変換を行い、その色をヘッダ/フッタ部分に適用します。

(function() {
  'use strict';

  // ユーザ / アカウント名の取得
  var account = document.querySelector("#nav-usernameMenu .nav-elt-label").textContent;

  // ユーザ / アカウント名 → カラーコードに変換
  var background = account.toRGBCode();

  // ヘッダ・フッタ部分の色を変える
  var selectors = ["#nav-menubar", "#nav-menu-right", "#console-nav-footer"];
  selectors.forEach(function(s) {
    document.querySelector(s).style.background = background;
  })

  var e = document.querySelectorAll(".nav-menu");
  for (var i = 0; i < e.length; i++) {
    e[i].style.background = background;
  }
})();

使い方

  • git clone
$ git clone https://github.com/syunm1n/color-aws-frame.git
  • Chromeでchrome://extensionsを開く
  • 「デベロッパーモード」にチェック
  • 「パッケージ化されていない拡張機能を読み込む」
  • cloneしたディレクトリを選択

今後の課題

  • 一般的にはIAMユーザでログインするので、(自分の名前) @ (AWSアカウント名)がカラーコードに変換されてヘッダ/フッタ色が決まるが、(自分の名前)部分は環境問わず同じなので、結果として出てくる色が似通ってしまうことがあるのをなんとかしたい

    • 実用的には設定画面を付けて、色を自由に変えられるようにする……?
    • 文字列からランダムに色が出てくるところも楽しいので、別物として作るかも……

続きを読む

EC2 拡張ネットワーキング 有効化手順

EC2 拡張ネットワーキング 有効化手順

ちゃんとした情報は公式をみよう。

インスタンスタイプに応じて、ixgbevf ドライバ、もしくは amzn-drivers を利用出来る状態に設定し、AMIもしくはインスタンスに利用可能とフラグ付けを行う事によって拡張ネットワーキングが利用可能となる。

インスタンスタイプ ドライバ
c3/c4/d2/i2/r3/m4(16xlarge以外) ixgbevf
c5/f1/g3/h1/i3/m5/p2/p3/r4/x1/m4.16xlarge amzn-drivers

確認事項

Amazon Linuxの場合

通常、対応するためのカーネルモジュールがインストールされている為、作業は不要。

CentOS 6の場合

カーネルモジュールの更新が必要。

CentOS 7の場合

少なくとも 7.4.1708 以降ではカーネルモジュールがインストールされている為、作業は不要。

モジュールの確認

ixgbevfena のカーネルモジュールの状態を確認し、バージョン情報を確認する。
ixgbevf は特定のバージョン以下の場合はカーネルモジュールの更新が必要となる。

対応バージョン

ドライバ 必要バージョン
ixgbevf 2.14.2 以降
ena インストールされている事
$ modinfo ixgbevf
filename:       /lib/modules/3.10.0-693.11.6.el7.x86_64/kernel/drivers/net/ethernet/intel/ixgbevf/ixgbevf.ko.xz
version:        3.2.2-k-rh7.4
license:        GPL
description:    Intel(R) 10 Gigabit Virtual Function Network Driver
author:         Intel Corporation, <linux.nics@intel.com>
rhelversion:    7.4
srcversion:     45F26A06C9B8C3202EA1ADC
alias:          pci:v00008086d000015C5sv*sd*bc*sc*i*
alias:          pci:v00008086d000015A9sv*sd*bc*sc*i*
alias:          pci:v00008086d000015A8sv*sd*bc*sc*i*
alias:          pci:v00008086d00001564sv*sd*bc*sc*i*
alias:          pci:v00008086d00001565sv*sd*bc*sc*i*
alias:          pci:v00008086d00001530sv*sd*bc*sc*i*
alias:          pci:v00008086d00001515sv*sd*bc*sc*i*
alias:          pci:v00008086d0000152Esv*sd*bc*sc*i*
alias:          pci:v00008086d000010EDsv*sd*bc*sc*i*
depends:
intree:         Y
vermagic:       3.10.0-693.11.6.el7.x86_64 SMP mod_unload modversions
signer:         CentOS Linux kernel signing key
sig_key:        2C:BC:98:70:54:63:43:CA:3A:E1:20:C2:BC:EB:98:44:01:95:59:62
sig_hashalgo:   sha256
parm:           debug:Debug level (0=none,...,16=all) (int)
$ modinfo ena
filename:       /lib/modules/3.10.0-693.11.6.el7.x86_64/kernel/drivers/net/ethernet/amazon/ena/ena.ko.xz
version:        1.0.2
license:        GPL
description:    Elastic Network Adapter (ENA)
author:         Amazon.com, Inc. or its affiliates
rhelversion:    7.4
srcversion:     3A6B9F1766C9A0B5CBC7D01
alias:          pci:v00001D0Fd0000EC21sv*sd*bc*sc*i*
alias:          pci:v00001D0Fd0000EC20sv*sd*bc*sc*i*
alias:          pci:v00001D0Fd00001EC2sv*sd*bc*sc*i*
alias:          pci:v00001D0Fd00000EC2sv*sd*bc*sc*i*
depends:
intree:         Y
vermagic:       3.10.0-693.11.6.el7.x86_64 SMP mod_unload modversions
signer:         CentOS Linux kernel signing key
sig_key:        2C:BC:98:70:54:63:43:CA:3A:E1:20:C2:BC:EB:98:44:01:95:59:62
sig_hashalgo:   sha256
parm:           debug:Debug level (0=none,...,16=all) (int)

使用中のドライバ確認

利用可能なインスタンスタイプで起動している場合に、使用中のドライバを確認し、ixgbevf もしくは ena が使用されている事を確認する。
使用されていない場合(driverが vif となっている)は拡張ネットワーキングが有効になっていないので確認が必要。

無効時の例

$ ethtool -i eth0
driver: vif
version:
firmware-version:
bus-info: vif-0
supports-statistics: no
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no

ixgbevfが有効時の例

$ ethtool -i eth0
driver: ixgbevf
version: 3.2.2-k-rh7.4
firmware-version:
expansion-rom-version:
bus-info: 0000:00:03.0
supports-statistics: yes
supports-test: yes
supports-eeprom-access: no
supports-register-dump: yes
supports-priv-flags: no

カーネルモジュールのインストール

事前準備

kernel kernel-headers kernel-develのバージョンを合わせる必要がある。

最新のkernelにアップデートする事を推奨しますが、旧カーネルの場合は kernel-headerskernel-devel をバージョン指定してインストールを行う事により対応が可能となる。

CentOS 6の中でも最新マイナーバージョン以下を利用する際はyumリポジトリが既にミラーから削除されている為、vault.centos.orgのリポジトリを利用する様に修正が必要となる。

# cat /etc/centos-release
CentOS release 6.2 (Final)

# sed -i.bak -E 's/^mirrorlist=/#mirrorlist=/' /etc/yum.repos.d/CentOS-Base.repo
# sed -i -E 's@^#?baseurl=http://mirror.centos.org/centos/$releasever/@baseurl=http://vault.centos.org/6.2/@' /etc/yum.repos.d/CentOS-Base.repo

CentOSのバージョンに合わせてsedの 6.2 となっている部分を修正する。

また、dkms によるカーネルアップデート時の自動コンパイル設定を行う事を推奨するため、epel リポジトリを有効化する。

準備

epelが設定されていない場合

# yum install -y epel-release
# sed -i -E 's/^enabled=1$/enabled=0/' /etc/yum.repos.d/epel*

各種パッケージのインストール

先にkernel-headersとkernel-develをインストールしておく。
また、ビルド時にperlが要求される場合があるので合わせてインストールする。

# yum install -y kernel-headers-$(uname -r) kernel-devel-$(uname -r) perl

既にkernel-headersがインストールされており、かつkernelよりバージョンが新しいものとなっている場合はダウングレードを行う。

# yum downgrade kernel-headers-$(uname -r)

dkmsはEPELからインストールを行う。

# yum --enablerepo=epel install -y dkms

ixgbevf のインストール

VER_IXGBEVF=4.3.3

curl -L -o "ixgbevf-${VER_IXGBEVF}.tar.gz" "https://downloads.sourceforge.net/project/e1000/ixgbevf%20stable/${VER_IXGBEVF}/ixgbevf-${VER_IXGBEVF}.tar.gz?r=https%3A%2F%2Fsourceforge.net%2Fprojects%2Fe1000%2Ffiles%2Fixgbevf%2520stable%2F${VER_IXGBEVF}%2Fixgbevf-${VER_IXGBEVF}.tar.gz%2Fdownload%3Fuse_mirror%3Djaist&ts=`date +%s`"
tar -xzvf ixgbevf-${VER_IXGBEVF}.tar.gz
rm -f ixgbevf-${VER_IXGBEVF}.tar.gz
mv ixgbevf-${VER_IXGBEVF} /usr/src/

echo 'PACKAGE_NAME="ixgbevf"
PACKAGE_VERSION="'${VER_IXGBEVF}'"
CLEAN="cd src/; make clean"
MAKE="cd src/; make BUILD_KERNEL=${kernelver}"
BUILT_MODULE_LOCATION[0]="src/"
BUILT_MODULE_NAME[0]="ixgbevf"
DEST_MODULE_LOCATION[0]="/updates"
DEST_MODULE_NAME[0]="ixgbevf"
AUTOINSTALL="yes"' | tee /usr/src/ixgbevf-${VER_IXGBEVF}/dkms.conf

dkms add -m ixgbevf -v ${VER_IXGBEVF}
dkms build -m ixgbevf -v ${VER_IXGBEVF}
dkms install -m ixgbevf -v ${VER_IXGBEVF}

amzn-drivers (ena) のインストール

VER_AMZN_DRIVERS=1.5.0

curl -L -o "ena_linux_${VER_AMZN_DRIVERS}.tar.gz" "https://github.com/amzn/amzn-drivers/archive/ena_linux_${VER_AMZN_DRIVERS}.tar.gz"
tar -xzvf ena_linux_${VER_AMZN_DRIVERS}.tar.gz
rm -f ena_linux_${VER_AMZN_DRIVERS}.tar.gz
mv amzn-drivers-ena_linux_${VER_AMZN_DRIVERS} amzn-drivers-${VER_AMZN_DRIVERS}
mv amzn-drivers-${VER_AMZN_DRIVERS} /usr/src/

echo 'PACKAGE_NAME="ena"
PACKAGE_VERSION="'${VER_AMZN_DRIVERS}'"
CLEAN="make -C kernel/linux/ena clean"
MAKE="make -C kernel/linux/ena/ BUILD_KERNEL=${kernelver}"
BUILT_MODULE_LOCATION[0]="kernel/linux/ena"
BUILT_MODULE_NAME[0]="ena"
DEST_MODULE_LOCATION[0]="/updates"
DEST_MODULE_NAME[0]="ena"
AUTOINSTALL="yes"' | tee /usr/src/amzn-drivers-${VER_AMZN_DRIVERS}/dkms.conf

dkms add -m amzn-drivers -v ${VER_AMZN_DRIVERS}
dkms build -m amzn-drivers -v ${VER_AMZN_DRIVERS}
dkms install -m amzn-drivers -v ${VER_AMZN_DRIVERS}

カーネルモジュールの有効化

depmod
cp /boot/initramfs-$(uname -r).img /boot/initramfs-$(uname -r).img.bak
dracut -f

この後、インスタンスを停止する。

拡張ネットワーキングの有効化

属性確認

インスタンスに属性が付与されているかを確認する。
属性が付与されていない場合は無効化されている状態のため、設定を行う。

これらはコンソール上では現れない情報の為、CLIにて作業を行う。

SriovNetSupport の確認

# aws ec2 describe-instance-attribute --attribute sriovNetSupport --region ap-northeast-1 --instance-id <インスタンスID>
{
    "InstanceId": "<インスタンスID>",
    "SriovNetSupport": {}
}

SriovNetSupportが空の場合は無効な為、有効化する必要がある。

EnaSupport の確認

# aws ec2 describe-instances --query 'Reservations[].Instances[].EnaSupport' --region ap-northeast-1 --instance-ids <インスタンスID>
[
]

EnaSupportが空の場合は無効な為、有効化する必要がある。

SriovNetSupport の設定

# aws ec2 modify-instance-attribute --sriov-net-support simple --region ap-northeast-1 --instance-id <インスタンスID>

# aws ec2 describe-instance-attribute --attribute sriovNetSupport --region ap-northeast-1 --instance-id <インスタンスID>
{
    "InstanceId": "<インスタンスID>",
    "SriovNetSupport": {
        "Value": "simple"
    }
}

EnaSupport の有効化

# aws ec2 modify-instance-attribute --ena-support --region ap-northeast-1 --instance-id <インスタンスID>

# aws ec2 describe-instances --query 'Reservations[].Instances[].EnaSupport' --region ap-northeast-1 --instance-ids <インスタンスID>
[
    true
]

属性の設定後、インスタンスを対応タイプで起動し、ethtool -i eth0 で確認する。

続きを読む

[バッドノウハウ]webプログラミング初学者がCloud9にPython3×djangoを使う

バッドノウハウというタイトルをつけましたが内容自体は使えなくもないと思います。
筆者にとって不要な手順を省略して後日記事を書き直す予定です。

この記事を書こうと思ったきっかけ

ローカルに環境を用意しなくても開発が始められるという統合開発環境(IDE)Cloud9を始めてみようと思いましたが、ネットで調べてもAWS版のcloud9がリリースされる前の情報が多く、いろいろと戸惑ったこともありましたので記事を書くことにしました。

これまでのあらすじ

Cloud9でpython3を標準で使えるまでできました。
webプログラミング初学者がAWS Cloud9でPython3を使う
webプログラミング初学者がAWS Cloud9を使い始める

pipでdjangoをインストールする→エラー

ec2-user:~/environment $ sudo pip install django
Collecting django
  Downloading Django-2.0.tar.gz (8.0MB)
    100% |████████████████████████████████| 8.0MB 140kB/s 
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "/tmp/pip-build-IzDxWp/django/setup.py", line 32, in <module>
        version = __import__('django').get_version()
      File "django/__init__.py", line 1, in <module>
        from django.utils.version import get_version
      File "django/utils/version.py", line 61, in <module>
        @functools.lru_cache()
    AttributeError: 'module' object has no attribute 'lru_cache'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-IzDxWp/django/

前回python -m pip -v の結果がこうだったのでいけるかなと思ったのですが。

ec2-user:~/environment $ python -m pip -V
pip 9.0.1 from /usr/lib/python3.6/dist-packages (python 3.6)

pip3を探す

pip3のありかを確認

ec2-user:~/environment $ ls -l /usr/bin |grep pip
-rwxr-xr-x   1 root root       2804 Sep 10  2014 lesspipe.sh
lrwxrwxrwx   1 root root         21 Jan  4 02:19 pip -> /etc/alternatives/pip
-rwxr-xr-x   1 root root        370 Aug 25 18:59 pip-2.7
-rwxr-xr-x   1 root root        370 Aug 25 18:59 pip-3.6

pip3.6にリンクを張る

alternativesとかよくわからないのでとりあえず

ec2-user:~/environment $ sudo ln -s /usr/bin/pip-3.6 /usr/bin/pip3

preferenceにPYTHONPATHの項目を発見

よく見るとpreferenceのPYTHONPATHに3.6が入っていないようなので先頭に追加

PYTHONPATH
/usr/local/lib/python3.6/dist-packages:/usr/local/lib/python2.7/dist-packages:/usr/local/lib/python3.4/dist-packages:/usr/local/lib/python3.5/dist-packages

pip3でdjangoインストール

pip3でdjangoをインストール

ec2-user:~/environment $ sudo pip3 install django                                                            
Collecting django
  Downloading Django-2.0.1-py3-none-any.whl (7.1MB)
    100% |████████████████████████████████| 7.1MB 200kB/s 
Collecting pytz (from django)
  Downloading pytz-2017.3-py2.py3-none-any.whl (511kB)
    100% |████████████████████████████████| 512kB 2.5MB/s 
Installing collected packages: pytz, django
Successfully installed django-2.0.1 pytz-2017.3

インストールできた!

ここまできて

“pip3″でやるんだったら、”python3″でいいじゃん。
前回の記事、べつにいらなくね?

なのでもう一回下記の手順でやり直して記事を書き直します。

  • cloud9でenvironment作成
  • cloud9の設定でPythonバージョン、PYTHONPATH設定
  • pip3のリンク作成
  • djangoインストール

続きを読む

mapを使った読みやすいterraform variablesの書き方

スクリーンショット 2018-01-19 0.51.28.png

ディレクトリ構成

以下のように、terraform/provider/aws/env/stgとしました。
変数ファイルであるvariables.tfもメイン処理をするec2.tfも同じディレクトリに置きます。

環境ごとに完全にファイルを分断するイメージで、tfstateファイルも環境ごとに別にします。(保管場所はS3です)
なお、module化はしません。

 %tree 
.
└── provider
    └── aws
        └── env
            └── stg
                ├── backend.tf
                ├── ec2.tf
                └── variables.tf

変数の書き方

Before

これまでterraformの変数ファイルはこんな感じで書いてました。

before_variables.tf
variable "ami" {
  default = "ami-4af5022c"
}

variable "instance_type" {
  default = "t2.micro"
}

variable "instance_key" {
  default = "id_rsa"
}

実際のEC2のパラメータはこれだけではありません。もっとたくさんある上に、AWSのリソースは他にもあるとなるとかなり長い変数ファイルとなります。

特徴としては、variableが毎回並んでしまって行数が増える。読みにくい見にくい探しづらい。

これを解決するのにmapを使いました。

A map value is a lookup table from string keys to string values. This is useful for selecting a value based on some other provided value.
A common use of maps is to create a table of machine images per region, as follows:

sample.tf
variable "images" {
  type    = "map"
  default = {
    "us-east-1" = "image-1234"
    "us-west-2" = "image-4567"
  }
}

After

一つのvariableに対してリソースの値をまとめます。
map型の宣言は省略可能みたいです。

after_variables.tf
variable "ec2_config" {
  type = "map" #省略化
  default = {
    ami = "ami-4af5022c" 
    instance_type = "t2.micro" 
    instance_key = "id_rsa" 
  }
}

こうすることで、リソースごとに値がまとまるので読みやすくなりました。
ではメイン処理をするec2.tfを見ていきましょう。

map型でのec2.tfの書き方

Before

これまでのリソース定義の書き方はこちらです。

before_ec2.tf
resource "aws_instance" "vtryo-web01" {
  ami              = "${var.ami}"
  instance_type    = "${var.instance_type}"
  instance_key     = "${var.instance_key}"

    tags {
    Name = "vtryo-web01"
  }
}

そしてafter版に対するvariableに対応するリソース定義の仕方です。
map型で変数を格納したので、lookup関数を使って値を参照させます。

beforeのときよりやや長くなったように見えますが、ルールを覚えれば(後述)そこまで複雑ではない上、柔軟性は良くなっています。

after_ec2.tf
resource "aws_instance" "vtryo-web" {
  ami              = "${lookup(var.ec2_config, "ami")}" 
  instance_type    = "${lookup(var.ec2_config, "instance_type")}" 
  key_name         = "${lookup(var.ec2_config, "instance_key")}" 

  tags {
    Name = "vtryo-${format("web%02d", count.index + 1)}" 
  }

注意点

resource定義内のami, instance_type, key_nameはterraform側で名前が決まっています。variables.tf内の変数名は任意ですが、こちらは自由には決められないので注意しましょう。
aws_instance

lookup

上記の書き方は、lookup関数でKeyを直接指定する方法を取っています。
こちらを参考にしています。
Terraformのoutputでmapを利用する方法

たとえば以下であれば
ami = "${lookup(var.ec2_config, "ami")}"

variables.tf内のec2_configamiの値を参照する」という意味になります。

format

最終行の"vtryo-${format("web%02d", count.index + 1)}"についてですが、変数格納後の値はvtryo-web01になります。
ちなみに"web%02d"を、"web%01d"にするとvtryo-web1になってしまうので注意です。

terraform init

さて実行です。

% terraform init

Initializing provider plugins...
- Checking for available provider plugins on https://releases.hashicorp.com...
- Downloading plugin for provider "aws" (1.7.0)...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.aws: version = "~> 1.7"

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

terraform plan

 % 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.vtryo-web
      id:                           <computed>
      ami:                          "ami-4af5022c"
      associate_public_ip_address:  <computed>
      availability_zone:            <computed>
      ebs_block_device.#:           <computed>
      ephemeral_block_device.#:     <computed>
      instance_state:               <computed>
      instance_type:                "t2.micro"
      ipv6_address_count:           <computed>
      ipv6_addresses.#:             <computed>
      key_name:                     "id_rsa"
      network_interface.#:          <computed>
      network_interface_id:         <computed>
      placement_group:              <computed>
      primary_network_interface_id: <computed>
      private_dns:                  <computed>
      private_ip:                   <computed>
      public_dns:                   <computed>
      public_ip:                    <computed>
      root_block_device.#:          <computed>
      security_groups.#:            <computed>
      source_dest_check:            "true"
      subnet_id:                    <computed>
      tags.%:                       "1"
      tags.Name:                    "vtryo-web01"
      tenancy:                      <computed>
      volume_tags.%:                <computed>
      vpc_security_group_ids.#:     <computed>


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

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

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

変数が格納されました!

ちょっと応用

さきほどのec2.tfでNameには"vtryo-${format("web%02d", count.index + 1)}"を書きました。
この出力結果はvtryo-web01です。
これをstg-vtryo-web01のように、環境を先頭に入れる方法を書いておきます。

variable “env”

variables.tfにvariable "env { }"を書きます。

variables.tf
variable "env" { }

variable "ec2_config" {
  default = {
    ami           = "ami-4af5022c"
    instance_type = "t2.micro"
    instance_key  = "id_rsa"
  }
}

{ }の中にdefaultを入れてもよいです。その場合は以下のように書きます。

variables.tf
variable "env" { 
  default = "text message..."
 }

${var.env}

一方でec2.tfには以下を書きます。

resource "aws_instance" "vtryo-web" {
  ami                      = "${lookup(var.ec2_config, "ami")}"
  instance_type            = "${lookup(var.ec2_config, "instance_type")}"
  key_name                 = "${lookup(var.ec2_config, "instance_key")}"
  tags {
    Name = "${var.env}-vtryo-${format("web%02d", count.index + 1)}"
  }
}

${var.env}をつけることで、terraform planしたときに入力を求められます。
入力した内容が、${var.env}に格納されるという仕組みです。

terraform plan

Enter a valueを求められるので今回はstgにしました。
すると、stgという文字列が格納されます。

% terraform plan
var.env
  Enter a value: stg

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.vtryo-web
      id:                           <computed>
      ami:                          "ami-4af5022c"
      associate_public_ip_address:  <computed>
      availability_zone:            <computed>
      ebs_block_device.#:           <computed>
      ephemeral_block_device.#:     <computed>
      instance_state:               <computed>
      instance_type:                "t2.micro"
      ipv6_address_count:           <computed>
      ipv6_addresses.#:             <computed>
      key_name:                     "id_rsa"
      network_interface.#:          <computed>
      network_interface_id:         <computed>
      placement_group:              <computed>
      primary_network_interface_id: <computed>
      private_dns:                  <computed>
      private_ip:                   <computed>
      public_dns:                   <computed>
      public_ip:                    <computed>
      root_block_device.#:          <computed>
      security_groups.#:            <computed>
      source_dest_check:            "true"
      subnet_id:                    <computed>
      tags.%:                       "1"
      tags.Name:                    "stg-vtryo-web01"
      tenancy:                      <computed>
      volume_tags.%:                <computed>
      vpc_security_group_ids.#:     <computed>


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

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

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

ちなみにterraform plan -var "env=stg"とすることで入力なしで格納することが可能です。

今回の補足というか余談

「terraformのbest-practice、わかりづらくない?」

開発者が推奨している構成だとはわかっていますが、素直にそんな感想がありました。
terraform best-practice
(もちろんQiitaにかかれている記事も読んでいます
Terraform Best Practices in 2017

ファイルの中に何度も宣言をしないといけないようですし、どうにも読みづらい印象があります。
また、「Environmentで環境を分ける」構成になっていますが、公式曰く

Workspaces can be used to manage small differences between development, staging, and production, but they should not be treated as the only isolation mechanism.

とあります。我らがGoogle翻訳を使うとこんなことを言ってます。

ワークスペースは、開発、ステージング、およびプロダクションの小さな違いを管理するために使用できますが、唯一の分離メカニズムとして扱うべきではありません。

初見で解読するには骨がいる内容です。何度もterraformを使い込んだらわかるんでしょうか。
ま、単純に私の技術力が追いついていない可能性の方が高いですが(爆)

とはいえ誰でも彼でもterraformの技術力が高いわけではないので、より読みやすくメンテナンスしやすい書き方をしても良いと思っています。

terraformは一歩間違えるとインフラそのものが削除されることがあるので、それを踏まえて確実な方が良いだろうというきもちです。

今回はQiitaの記事にだいぶ助けられたこともあったので、Qiitaに書くことにしました。

シンプルに書きたい欲

terraformにはmodule化が出来る機能があって、それがやや難易度を上げているように思います。

変数をmodule化できることでメリットもありますが、利用ができなければ意味がない。terraformにはmoduleのレジストリがありますが、仕組みを理解しないとやっぱり使うのは大変です。

学習コストばかりかかるくらいなら、いっそ使わずにシンプルにコードを書いて行こうということでした。

best-practiceに固執しない

重要なのは可読性ととっつきやすさだと思ったので、あえて固執せずにディレクトリを構成し、terraformを見やすく書く方法を考えました。
誰かの参考になれば幸いです。

参考

Terraformのoutputでmapを利用する方法 – Qiita
Terraform Best Practices in 2017 – Qiita
TerraformでのAWS環境構築の設定を分ける – Qiita
terraform
さらに一歩楽しむterraform. moduleでIAM UserとPolicy管理を簡素化しよう
本記事はこちらのクローンです。

続きを読む

クラウドエンジニア(AWS・GCP・Azure)

クラウドエンジニア(AWS・GCP・Azure)◇設計構築◇. 仕事内容. □当社が持つ各顧客のクラウド案件の提案、要件定義、設計構築をお任せします□ 増々オンプレからクラウドへの移行が行われていますのでその波に一緒に乗れる仲間を募集してます。 <ここがポイント> クラウド化に伴いインフラエンジニアの守備範囲は … 続きを読む