Terraform for さくらのクラウド スタートガイド(第2回)

# 2017/05追記

当記事の情報はすでに古くなっています。
新しい記事を投稿していますのでこれからTerraform for さくらのクラウドを利用される方はこちらを参照ください。

[Terraform for さくらのクラウド スタートガイド(全5回)](http://knowledge.sakura.ad.jp/knowledge/7230/)

こちらの記事は一応残しておきます。

## 連載目次

#### [第1回：導入編](http://qiita.com/yamamoto-febc/items/ae92cd258cf040957487)
  - 概要
  - セットアップ
  - 実践 Step1：サーバー１台構成

#### **[第2回：実践編](http://qiita.com/yamamoto-febc/items/2480b11c9e6a8b64f78d)(当記事)**
  - 実践 Step2：構成/設定の変更
    - リソースの追加
    - リソースの変更
    - count構文
    - output機能

#### [第3回：実践編2](http://qiita.com/yamamoto-febc/items/fe954e2d4a92b864cfef)
  - 実践 Step3：プロビジョニング
    - プロビジョニング接続設定
    - fileプロビジョニング
    - remote-execプロビジョニング
    - さくらのクラウドDNSリソースの利用

#### [第4回：応用編](http://qiita.com/yamamoto-febc/items/a9795cb909bd9b69f729)
  - 実践 Step4：Web/DB 2-Tier構成
    - MySQLの利用 
    - スイッチによるプライベートネットワークの構築
    - パケットフィルタ/シンプル管理:Slack通知の利用

#### [第5回：応用編2](http://qiita.com/yamamoto-febc/items/4b774404e041fa05688a)
  - 実践 Step5：東京/石狩 マルチゾーン構成
    - 東京と石狩でマルチゾーン構成
    - GSLBによるゾーン間HA構成
    - MySQL レプリケーション + PHP(mysqlnd_ms)によるDBのクラスタリング
    - `null_resource`や`template_file`などの特殊なリソース
    - tfファイルのリファクタリングとモジュール化 
    
---   

連載第２回です。
[前回](http://qiita.com/yamamoto-febc/items/ae92cd258cf040957487)を読んでから当記事を読むことをお勧めいたします。

[第2回サンプルコード](https://github.com/yamamoto-febc/terraform-for-sakuracloud-start-guide/tree/no2) / [第1回との差分表示](https://github.com/yamamoto-febc/terraform-for-sakuracloud-start-guide/compare/no1...no2)

---

# 実践　step2: 構成/設定の変更

前回は以下のような構成でした。

#### 前回の構成
![servers.png](https://qiita-image-store.s3.amazonaws.com/0/20668/2cd9c6ea-1e3e-9024-ca04-0ae44ba77d11.png)

この構成に対し以下の変更を行ってみましょう。

  * SSH公開鍵をアップロードし、SSH認証で使う
  * SSHでは公開鍵認証のみ利用可能とする(パスワード、チャレンジ/レスポンス方式不可)
  * 同様の構成のサーバーを1台追加

#### 今回変更後の構成
![servers02.png](https://qiita-image-store.s3.amazonaws.com/0/20668/249570b2-655c-0544-107b-aa6885130ffa.png "servers02.png")

色が付いている部分が追加されるリソースです。

## 準備

SSH公開鍵を準備します。既にキーペアをお持ちの場合はそれを使っても良いですが、
今回は新たに生成したキーペアを利用します。

以下コマンドで、カレントディレクトリに`id_rsa`と`id_rsa.pub`が生成されます。
途中パスフレーズを聞かれますが、何も入力せずEnterでOKです。
**注：本番運用の際はパスフレーズの設定やキーの保管などに十分注意して運用ください。**

```SSH用キーペア作成
$ ssh-keygen -C "" -f ./id_rsa
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):  #何も入力せずEnter
Enter same passphrase again:                 #何も入力せずEnter

```

## 定義ファイルへの追記/編集

まずは定義ファイル(tfファイル)に追加/変更したい内容を記載していきましょう。
前回の定義ファイルは以下の通りでした。

```sakura.tf(前回の内容)
provider "sakuracloud" {
    token = "[ACCESS_TOKEN]"
    secret = "[ACCESS_TOKEN_SECRET]"
}

resource "sakuracloud_disk" "disk"{
    name = "disk01"
    source_archive_name = "CentOS 7.2 64bit"
    password = "YourPassword"
}

resource "sakuracloud_server" "server" {
    name = "server01"
    disks = ["${sakuracloud_disk.disk.id}"]    
}
```

ここにリソースの追加/変更を記載していきます。

#### SSH公開鍵の登録

SSH公開鍵は以下のように記載します。

```sakura.tf(SSH公開鍵の定義)
resource "sakuracloud_ssh_key" "mykey" {
    name = "mykey"
    public_key = "${file("./id_rsa.pub")}"
}
```

ポイントは`"${file("./id_rsa.pub")}"`の部分です。
tfファイルではterraform組み込みの関数を利用することができます。
`${}`の内側に呼び出したい関数を記載することで利用可能です。
今回はファイルから文字列を取得する`file`関数を利用しています。

利用可能な関数の一覧は[Terraformドキュメント](https://www.terraform.io/docs/configuration/interpolation.html)を参照ください。

#### SSHで公開鍵認証のみ利用する

ディスクの定義を以下のようにすることでSSHで公開鍵認証のみを利用するように変更することができます。

```sakura.tf(SSH公開鍵認証のみ許可する)
resource "sakuracloud_disk" "disk" {
    name = "disk01"
    source_archive_name = "CentOS 7.2 64bit"
    ssh_key_ids = ["${sakuracloud_ssh_key.mykey.id}"]
    disable_pw_auth = true
#    パスワード設定はコメントアウトまたは削除する
#    password = "YourPassword"
}
```

#### 同様の構成のサーバを追加する(count構文の利用)

単純に`server01`と`disk01`の定義をコピーして`server02`と`disk02`というのを追加しても良いのですが、terraformには`count`という、同じ構成のリソースを複数定義する機能があります。
今回はこれを使ってみましょう。[DRY](https://ja.wikipedia.org/wiki/Don%27t_repeat_yourself)だね！[DRY](https://ja.wikipedia.org/wiki/Don%27t_repeat_yourself)だよ！
(詳細はこちらの[Terraformドキュメント](https://www.terraform.io/docs/configuration/resources.html)に記載されています)

まずは単純にコピーした場合は以下のようになります。

```sakura.tf(悪い例：コピペで作ったもの)
resource "sakuracloud_disk" "disk01" {
    name = "disk01"
    source_archive_name = "CentOS 7.2 64bit"
    ssh_key_ids = ["${sakuracloud_ssh_key.mykey.id}"]
    disable_pw_auth = true
}
resource "sakuracloud_server" "server01" {
    name = "server01"
    disks = ["${sakuracloud_disk.disk01.id}"]    
}

# コピペで以下を作成
resource "sakuracloud_disk" "disk02" {
    name = "disk02"
    source_archive_name = "CentOS 7.2 64bit"
    ssh_key_ids = ["${sakuracloud_ssh_key.mykey.id}"]
    disable_pw_auth = true
}
resource "sakuracloud_server" "server02" {
    name = "server02"
    disks = ["${sakuracloud_disk.disk02.id}"]    
}

```

`count`を使うと以下のように書けます。

```sakura.tf(countを使う場合)
resource "sakuracloud_disk" "disk" {
    name = "${format("disk%02d" , count.index+1)}"
    source_archive_name = "CentOS 7.2 64bit"
    ssh_key_ids = ["${sakuracloud_ssh_key.mykey.id}"]
    disable_pw_auth = true
    count = 2
}
resource "sakuracloud_server" "server" {
    name = "${format("server%02d" , count.index+1)}"
    disks = ["${element(sakuracloud_disk.disk.*.id,count.index)}"]
    count = 2
}
```

`${}`構文と関数で実現します。以下の変数、関数を利用してます。
`count.index` : 0開始のインデックス
`format` : 書式設定した文字列を返す関数
`element` : 与えられた配列から指定の位置インデックスの要素を返す

すっきりした上に、今後台数を増やす場合もcountを変更すればいいですね！
なお、さくらのクラウドでは1ゾーンあたりサーバー20台まで作れます。
それ以上作る場合はゾーンを分けましょう。

### 定義ファイル(tfファイル)全体

tfファイル全体は以下のようになっているはずです。

```sakura.tf(変更後)
provider "sakuracloud" {
    token = "[ACCESS_TOKEN]"
    secret = "[ACCESS_TOKEN_SECRET]"
}

resource "sakuracloud_disk" "disk" {
    name = "${format("disk%02d" , count.index+1)}"
    source_archive_name = "CentOS 7.2 64bit"
    ssh_key_ids = ["${sakuracloud_ssh_key.mykey.id}"]
    disable_pw_auth = true
    count = 2
}

resource "sakuracloud_server" "server" {
    name = "${format("server%02d" , count.index+1)}"
    disks = ["${element(sakuracloud_disk.disk.*.id,count.index)}"]
    count = 2
}

resource "sakuracloud_ssh_key" "mykey" {
    name = "mykey"
    public_key = "${file("./id_rsa.pub")}"
}
```

## `terraform plan`で確認

では`terraform plan`を実行してみましょう。
以下のようになるはずです。

![qiita01.png](https://qiita-image-store.s3.amazonaws.com/0/20668/0b996182-f9f5-1e3d-279b-60a40c3bbdda.png)

前回作成したサーバとディスクについては変更、それ以外のリソースは追加という扱いになっています。
これは`terraform.tfstate`という状態ファイルと現在のサーバの状態を
terraformが確認、差分を判定してくれているからです。

ではこれでインフラ構築してみましょう。

## `terraform apply`で反映

`terraform apply`を実行しましょう。
上手くいきましたね？

## 確認

ではSSHでつないで確認してみましょう。
まずはサーバーのグローバルIPを確認します。
前回と同じく`terraform show`を使います。

```bash:グローバルIP確認
$ terraform show 

#関連部分だけ抜粋

sakuracloud_server.server.0: # 前回作ったサーバー
  base_nw_ipaddress = 153.120.91.9

sakuracloud_server.server.1: # 今回作ったサーバー
  base_nw_ipaddress = 153.120.92.251

```

では前回作ったサーバーへ向けてSSH接続してみましょう。

```bash:SSH接続(パスワード認証)
$ ssh root@[前回作ったサーバーのグローバルIP]
Permission denied (publickey). #公開鍵認証しかダメ
```

エラーになりました。
これはパスワード認証しようとしたからですね。今度は公開鍵認証するために秘密鍵を指定してみます。


```bash:SSH接続(公開鍵認証)
$ ssh root@[前回作ったサーバーのグローバルIP] -i ./id_rsa
# ログイン成功するはず！
```

つながりましたね？
同様に今回作ったサーバーにも繋がるはずです。

# 応用：Outputの追加

さて、グローバルIPの確認に毎回`terraform show`するのも面倒です。
TerraformのOutput機能を使って、この辺を楽にしてみましょう。

## Outputとは

その名の通り、Terraformからの出力を定義するものです。
主に`terraform show`で確認できる値を出力するのに利用します。

定義ファイル(tfファイル)に以下のように記載してみましょう。

```sakura.tf(Output定義)
output "global_ip" {
    value = "${join("\n" , formatlist("%s : %s" , sakuracloud_server.server.*.name , sakuracloud_server.server.*.base_nw_ipaddress))}"
}
```

outputを利用するには以下のコマンドを実行します。

```output実行
$ terraform output [定義したoutputの名前]
```

今回は`global_ip`という名前で定義しましたので`terraform output global_ip`を実行します。

```output実行
$ terraform output global_ip

server01 : 153.120.91.9
server02 : 153.120.92.251
```

これで確認が楽になりました。

# まとめ

今回は

  - リソースの追加(SSH公開鍵、サーバーとディスク)
  - リソースの変更(SSH公開鍵認証以外の無効化)
  - count構文の利用
  - output機能の利用

を行いました。実際の作業は前回と同じく

  - 定義ファイル(tfファイル)を作って
  - `terraform plan`で確認して
  - `terraform apply`でインフラ構築実施

という流れでしたね。Terraformでのインフラ管理はこのサイクルを回していくということです。
定義ファイルは単純なテキストファイルな上、キーペアなどの必要なファイルも
手元に置いておけますので、バージョン管理システムと組み合わせればPullRequest駆動の
インフラ調達なんていうのもできちゃいますね :bangbang:

さて、次回は作ったインフラ、特にサーバーに対してのプロビジョニングを行ってみます。
`chef`や`ansible`、`シェルコマンド`などで作ったサーバーをセットアップできる機能です。
お楽しみに :bangbang:

次回：[実践 Step3：プロビジョニング](http://qiita.com/yamamoto-febc/items/fe954e2d4a92b864cfef)