cloud-initでインスタンスの初期設定あれこれ

この記事は [高知工科大学 AdvenCalendar 2019](https://adventar.org/calendars/4578) の7日目の記事です。

# はじめに
OSの"初回"起動時にやりたいことってありますか？
OSインストールしたら `yum update && yum upgrade -y`をしなくてもパッケージが最新状態になってたりしたいですよね？？

「他にもインストールしたいパッケージあるし手動でやるよ。」って言われたらこの話は閉幕です。
それを自動化してみませんか？っていうお話です！

# Auto Scaling
主題ではないのでサラッと概要だけ書きます。
AutoScalingはインスタンスやロードバランサに、CPUとかネットワークの負荷が掛かると、スケール(インスタンスの数)を自動で変化して全体負荷を軽減する機能です。

 - ECサイトとかで広告を打つ前にスケールアウト(インスタンスの数を増やすこと)しておいて大量アクセスに備える。
 - 外部要因でアクセスが増えたり減ったりしてユーザの傾向が読めないけど、余裕を持ちすぎて余計な費用を掛けたくない。必要なときに必要なリソースがほしい。

こんなときに活躍します。オンプレミスみたいに初期費用がかからず、使う分だけ費用のかかるクラウドならではの機能だと思います。AWSの`Auto Scaling`やAzureの`Virtual Machine Scale Set(VMSS)`とかがあります。

AutoScalingってインスタンスを建てたり、消したりするんです。
だから各インスタンスに永続的なデータは持たないように設計しないといけません。
そしてインスタンスは毎回新しい状態で建つので、毎回手動で設定なんて出来ません。
そこで登場するのが`cloud-init`です。

# cloud-init
本題です。OSインストール後の初回起動で実行されるスクリプトです。
EC2インスタンスを建てるときに「インスタンスの詳細設定」で "高度な詳細"って項目があります。ここにcloud-initを書きます。(ShellScriptでも書けます)。
とりあえず例をペタリ。

```
#cloud-config
timezone: Asia/Tokyo
locale: ja_JP.utf8
package_upgrade: true
packages:
 - httpd

write_files:
 - owner: root:root
   path: /etc/cron.d/submit_log
   content: 0 * * * * ec2-user sh /home/ec2-user/submit_log.sh 

runcmd:
 - service httpd enable
 - service httpd start
```

これでインスタンス起動時にApacheでWebサーバが立ち上がってます。

勉強したなりに解説していきます。
## #cloud-init
１行目の宣言です。これは必要です。書いときましょう。

```
#cloud-init
```

## timezone, locate, package
timezone, locateそれぞれ設定できます。

パッケージがアップグレードされます。読んで字の如しです。
yumともaptとも書いてませんがOSに応じて適切に変換してくれるっぽいです。

```
package_upgrade: true
```

インストールするパッケージを書きます。
複数個書く場合は下につなげていきます。

```
packages:
 - httpd
 - git
 - docker
```

## write_files
ファイルをしていした内容で生成してくれます。有り難いです。
１つのファイルにつき、１つハイフンを書きます。下記を設定できます。

```
 - path: [PATH]
   owner: [OWNER]
   permissions: [PERMISSION]
   content: [CONTENT]
```


 - [PATH]  
ファイルの置き場所です。
相対パスの場合どうなるんだろう...わかりません。
 - [OWNER]  
root:root みたいな感じで書きます。
`chown`コマンドみたいなノリです！
 - [PERMISSION]  
`chmod`コマンドで数字使うみたいなノリです！（説明下手なので外部リンク）  
https://www.atmarkit.co.jp/ait/articles/1605/23/news020.html
 - [CONTENT]
ファイルの中身です。改行して書く場合は`|`を使います。下の感じです。

```
content: |
     <HTML>
       <HEAD>
         <TITLE>たいとる</TITLE>
       <HEAD>
       <BODY>
         <H1>cloud-init</H1>
       </BODY>
     <HTML>
```

write_filesは、最低限PATHとCONTENTが必要だと思います。多分。

## runcmd
実行したいコマンドを書いていきます。root権限で実行されるようです。以下例です。

```
 - service docekr restart
 - mkdir /home/ec2-user/rails_app
 - git clone https://github.com/[username]/[repository].git /home/ec2-user/rails_app/.
```

# other
cloud-initについて設定の際役に立つことリストです。

- 実行順序について
上の中だと、次の順番で実行される。
 1. packages
 2. write_files
 3. runcmd
- ログの在り処  
`/var/log/cloud-init.log`
- 処理の時間  
インスタンスが起動完了しても、cloud-initの処理が完了しているとは限りません。statusがruuningでもcloud-initは処理中の場合があります。コーヒーでも飲んで待ちましょう。

# おわりに
cloud-initについて特段詳しいわけではないのでこう書けば動くよ！っていう記事です。
結局cloud-init内でシェルスクリプトをwrite_filesしたりしてます。
手探りだった頃は、インスタンスが起動したらcloud-initの処理も終わっていると勘違いしていて、何回インスタンスを消したかわかりません。数十回はやりました。

オートスケール、Webサービスがバズって「鯖落ちした〜」とかならないように設定したいですね。確証はないですが、オートスケールの設定にはお金かからないはずです。
ただ、オートスケールの考えではインスタンスは使い捨てなので、データベースとかは別に持つ必要があるので構成ちゃんとしなきゃだめですね。インスタンスは受け口のみ、データベースはAmazon RDSを使って、画像データはS3に入れとく、みたいな感じです（適当）。

下手な文章ですが、最後まで読んでいただきありがとうございました。
参考になれば幸いです。

## 参考
https://cloudinit.readthedocs.io/en/latest/
https://docs.aws.amazon.com/ja_jp/autoscaling/
https://qiita.com/toshihirock/items/81d6612511f0d1f5db77