TerraformをDocker環境上で動かし、基本操作(plan,apply,show,destroy)を習得する

![25.jpeg](https://qiita-image-store.s3.amazonaws.com/0/199531/7cfba305-57ad-2b28-9be3-e57b4917ec5f.jpeg)

# はじめに
仕事でTerafformを使うことになったので、Terraformの基本的な操作方法を習得します。
Terraformのblog投稿、参考訳 - Qiita
http://qiita.com/zembutsu/items/402e02950ce9d59fa0e6
Terraform入門 日本語訳 - Qiita
http://qiita.com/zembutsu/items/84f5478701c5391df537

# 環境
- Windows7
- VirtualBox V5.1.26
- Terraform v0.10.5
- docker 17.06.2-ce

# 1. ダウンロードと導入
MacOS版(AMD64)、Linux版(i386, AMD64)、Windows版(i386)等のバイナリが、[ダウンロードページ](https://www.terraform.io/downloads.html)から配付されています。Linux版があるということはDockerでも提供されているのかなと思い、DockerHubを確認。ありましたので、[こちら](https://hub.docker.com/r/hashicorp/terraform/)を使います。
![24.jpeg](https://qiita-image-store.s3.amazonaws.com/0/199531/6b349bf2-10a4-9975-a725-e56168259d43.jpeg)


仮想化層という余計なモジュールは必要なく、WindowsやLinuxにterraformを導入すれば良いのですが、Docker Swarm、Kubernetes等により以下が実現できるのでDocker版を選択します。もう少し平たくお伝えすると、どんなSWであれ、特に理由がなければ、Docker版を個人的に選択しています。
①コスト効率の向上
- 必要な時に、必要な分だけ、自動でサーバーを追加・削除する
- リソースが不要な期間はリソースを利用しないことにより、コスト効率が向上する
②可用性の向上
- 複数のAvailability Zone(=AZ)でAuto Scalingを設定し、１つのAZが利用できなくなっても別AZで新しいインスタンスを起動する
③耐障害性の向上
- サーバー異常時を検出し、サーバーを終了し、新しいサーバーを起動する

デスクトップにあるアイコンをダブルクリックし、Docker Quick Start Terminalを起動します。[こちら](https://hub.docker.com/r/hashicorp/terraform/)に従って、docker pullコマンドを実行します。

```
$ docker pull hashicorp/terraform:light
$ docker pull hashicorp/terraform:full
$ docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
hashicorp/terraform   full                57305292ab71        3 days ago          647.9 MB
hashicorp/terraform   latest              e84f3c7e4c2e        3 days ago          96.65 MB
$ docker run -it hashicorp/terraform:latest version
Terraform v0.10.5
$ docker run -it hashicorp/terraform:full version
Terraform v0.10.5
```

# 2. エディション選定
lightバージョン(=tagはlatest)とfullバージョンがありますが、コンテナーにログインしたいためfullバージョンを使います。647.9MBと96.65MBで、かなりの差があります。
[Dockerfile](https://hub.docker.com/r/hashicorp/terraform/)を確認したところ、ENTRYPOINT ["terraform"]が設定されていました。lightバージョンで、ENTRYPOINTを/bin/bashで上書きしてコンテナーを起動してみたのですが、no such file or directoryになりました。lightバージョンはterraformコマンドの実行だけに特化されているコンテナーのようです。

```
FROM golang:alpine 
MAINTAINER "HashiCorp Terraform Team <terraform@hashicorp.com>" 

ENV TERRAFORM_VERSION=0.10.0 

RUN apk add --update git bash openssh

ENV TF_DEV=true 
ENV TF_RELEASE=true

WORKDIR $GOPATH/src/github.com/hashicorp/terraform
RUN git clone https://github.com/hashicorp/terraform.git ./ && \
    git checkout v${TERRAFORM_VERSION} && \
    /bin/bash scripts/build.sh

WORKDIR $GOPATH
ENTRYPOINT ["terraform"]
```

```
$ docker-machine ls
NAME      ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
default   *        virtualbox   Running   tcp://192.168.99.100:2376           v17.06.2-ce
$ docker-machine ssh default
docker@default:~$ sudo -s
root@default:/home/docker# docker run -it  --entrypoint /bin/bash hashicorp/terraform:latest
docker: Error response from daemon: oci runtime error: container_linux.go:262: starting container process caused "exec: \"/bin/bash\": stat /bin/bash: no such file or directory".
```

# 3. 事前準備
Terraform社の[ホームページ](https://www.terraform.io/docs/providers/docker/index.html)に従って設定ファイル(=test01.tf)を作成し、仮想マシン(=HostOS/名前:default)上の/test01に保管します。

```
root@default:~# mkdir /test01
root@default:~# cat /test01/test01.tf
provider "docker" {
    host = "tcp://192.168.99.100:2376/"
}

resource "docker_container" "hoge" {
    image = "${docker_image.centos.latest}"
    name = "hoge"
    hostname = "hoge"
    command = ["/bin/sh", "-c", "while true ; do sleep 1; hostname -s ; done"]
}

resource "docker_image" "centos" {
    name = "centos:latest"
}
```

Unix環境の場合、host = "unix:///var/run/docker.sock"のようにunixドメインソケットを設定できます。今回、Terraformが稼働するコンテナー(=GuestOS)から仮想マシン(=HostOS/名前:default)上にunixドメインソケットを設定するので、下記でも対応できそうなのですが、tcp://xxxにしました。。
[docker のコンテナ内から ホストの docker を操作する　- Qiita](http://qiita.com/hikoma/items/8fbfb4f783d31e66b228)

その他、[こちらの注意事項](https://www.terraform.io/docs/providers/docker/index.html)に対応するために、鍵関連のファイル(=C:\Users\shimizu\.docker\machine\machines\default)を仮想マシン(=HostOS/名前:default)上の/test01に保管します。ここ、曲者です。
**docker-machine environment, the Docker daemon uses an encrypted TCP socket (TLS) and requires cert_path for a successful connection. As an alternative, if using docker-machine, run eval $(docker-machine env <machine-name>) prior to running Terraform, and the host and certificate path will be extracted from the environment.**
考慮しないと、malformed HTTP response "\x15\x03\x01\x00\x02\x02"のメッセージが出力されます。

```
$ docker-machine ls
NAME      ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
default   *        virtualbox   Running   tcp://192.168.99.100:2376           v17.06.2-ce

$ docker-machine env default
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="C:\Users\shimizu\.docker\machine\machines\default"
export DOCKER_MACHINE_NAME="default"
# Run this command to configure your shell:
# eval $("C:\Program Files\Docker Toolbox\docker-machine.exe" env default)

root@default:/test01# ls -la /test01
total 36
drwxr-xr-x    3 root     root           240 Sep 18 15:34 .
drwxr-xr-x   18 tc       staff          440 Sep 18 14:32 ..
-rw-r--r--    1 root     root          1039 Sep 18 14:45 ca.pem
-rw-r--r--    1 root     root          1079 Sep 18 14:45 cert.pem
-rw-r--r--    1 root     root          1676 Sep 18 14:45 key.pem
-rw-r--r--    1 root     root          1680 Sep 18 14:46 server-key.pem
-rw-r--r--    1 root     root          1112 Sep 18 14:46 server.pem
-rw-r--r--    1 root     root           330 Sep 18 14:34 test01.tf
```

これらのファイルを仮想マシン(=HostOS/名前:default)上の/test01に保管するのは、Terraformが稼働するコンテナー(=GuestOS)からデータを読めるようにするためです。もっとスマートなやり方があるかと思いますが、理屈を理解するのが目的ですので、これで良しとしました。

# 4. 基本操作
docker runコマンドを実行し、Terraformが導入されているコンテナーを起動します。HostOS:/test01を、GuestOS:/mnt/test01にマウントしています。ENTRYPOINTを/bin/bashに上書きし、コンテナーにログインします。

- コンテナーの起動

```
$ docker run -v /test01:/mnt/test01 -it  --entrypoint /bin/bash hashicorp/terraform:full
```


- 環境変数の設定

```
bash-4.3# export DOCKER_TLS_VERIFY="1"
bash-4.3# export DOCKER_HOST="tcp://192.168.99.100:2376"
bash-4.3# export DOCKER_CERT_PATH="/mnt/test01"
bash-4.3# export DOCKER_MACHINE_NAME="default"
```

- 初期化:terraform init

```
bash-4.3# terraform init /mnt/test01

Initializing provider plugins...
- Checking for available provider plugins on https://releases.hashicorp.com...
- Downloading plugin for provider "docker" (0.1.0)...
(省略)
```

- 計画:terraform plan

```
bash-4.3# terraform plan /mnt/test01/
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.
(省略)
```

- 実行:terraform apply

```
bash-4.3# terraform apply /mnt/test01
docker_image.centos: Creating...
  latest: "" => "<computed>"
  name:   "" => "centos:latest"
```

- 確認:terraform state

```
bash-4.3# terraform state list
docker_image.centos
bash-4.3# terraform state show
id     = sha256:196e0ce0c9fbb31da595b893dd39bc9fd4aa78a474bbdc21459a3ebe855b7768centos:latest
latest = sha256:196e0ce0c9fbb31da595b893dd39bc9fd4aa78a474bbdc21459a3ebe855b7768
name   = centos:latest
```

- 確認:docker ps

```
root@default:/test01# docker ps
CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS               NAMES
58a7abd9022a        hashicorp/terraform:full   "/bin/bash"              24 minutes ago      Up 24 minutes                           priceless_shaw
8ea9cb51ff36        196e0ce0c9fb               "/bin/sh -c 'while..."   2 hours ago         Up 2 hours                              hoge
```

- 計画(破棄):terraform plan -destroy

```
bash-4.3# terraform plan -destroy
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.

docker_image.centos: Refreshing state... (ID: sha256:196e0ce0c9fbb31da595b893dd39bc9f...474bbdc21459a3ebe855b7768centos:latest)
```

- コンテナー停止: docker stop / docker rm 

```
root@default:/test01# docker stop 8ea9cb51ff36
root@default:/test01# docker rm 8ea9cb51ff36
```

- 破棄:terraform destroy

```
bash-4.3# terraform destroy
docker_image.centos: Refreshing state... (ID: sha256:196e0ce0c9fbb31da595b893dd39bc9f...474bbdc21459a3ebe855b7768centos:latest)

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:
(省略)
```

# 5. ログの出力
ログ(詳細)を出力する場合は、export TF_LOG=1を実行します。出力したくない場合は、export TF_LOG=''を実行します。その他、ログファイルに書き込むこともできました。
https://www.terraform.io/docs/configuration/environment-variables.html


```
bash-4.3# export TF_LOG=1
bash-4.3# export TF_LOG_PATH='./terraform.log'
bash-4.3# terraform version
2017/09/18 17:30:54 [WARN] Invalid log level: "1". Defaulting to level: TRACE. Valid levels are: [TRACE DEBUG INFO WARN ERROR]
Terraform v0.10.5
```

# まとめ
Terraform@Docker版の選択したら、マウント(run -v)、環境変数(DOCKER_CERT_PATH)、ENTRYPOINT(/bin/bash)等を使う必要があり、Dockerを復習できてしまいました。そして、少し苦労しました...
Terraformに関しては、マルチクラウド環境ではないので、今回の検証だけでは価値を享受できなかったです。しかし、一応、理解しているつもりです。サーバーセットアップ部分もコード化でき、GithubやJenkinsと連携することで、インフラCI環境を実現できるのが本当の価値であることを...
またTerraformのResouceとして、docker_container、docker_image、docker_network、docker_volumeが提供されているので、docker_network、docker_volumeを試してみます。昔は、docker_containerとdocker_imageだけだったのかな...
その他、Terraform経由でプロビジョニングした仮想マシンの起動/停止はできないのかな。terraformコマンドにはstart/stopがなく、destroy(=停止ではなく破棄)しかありませんでした。