terraformを使ってGCRのDocker imageからGCEインスタンスを作成する

terraform使ってみたときの色々メモ

#terraformとは
[10分で理解するTerraform](https://qiita.com/Chanmoro/items/55bf0da3aaf37dc26f73)

要は、クラウドの環境を作成するときに手順書作ってマニュアルで作成するのではなく、
その辺も全部コードでやってしまおうというツールのようです。
（コードで管理できると環境自体をバージョン管理しやすかったり、属人性を排除できたり良いことあります）

#目的（私の場合）
スクレイピングのスクリプトをColaboratoryでテストしてて、
それを一晩中動かしたいときとかにサクッとサーバーを立ち上げる用とか

#環境
cloudshell
デフォルトのcloudshellのdockerイメージにterraformがインストールされているので、カスタムイメージを作る必要はなし

#前提
GCRにDocker imageがpushしてある

##（参考）pythonスクレイピング用のDockerfile
seleniumとbeautiful soup使って、pandasデータフレームにスクレイピング結果投入して
pandasからBigQueryにInsertしたいときに使えるDockerfile

GCPのJSONキーファイルとスクレイピング用のpythonファイルはDockerfileと同一ディレクトリにある前提

```docker:Dockerfile
FROM python:3

ARG project_dir=/selenium/
ARG credential_json=<JSONキーファイル名>

ADD requirements.txt $project_dir
ADD $credential_json $project_dir
ADD <スクレイピングpythonファイル> $project_dir
ENV GOOGLE_APPLICATION_CREDENTIALS ./$credential_json 

WORKDIR $project_dir

RUN apt-get update
RUN apt-get install -y vim less python3-selenium

RUN pip install -r requirements.txt

CMD ["/bin/bash"]
```

```requirements.txt
selenium==3.141.0
requests==2.23.0
pandas==1.0.4
bs4==0.0.1
google-cloud-bigquery==1.21.0
pandas-gbq==0.11.0
lxml==4.5.1
```

#terraform設定ファイル（tfファイル）を作る
適当なディレクトリにリソースを定義するファイル（tfファイル）を作成する

##main.tf
GCEに関する諸々を設定するメインのファイル

```terraform:main.tf
provider "google" {
    credentials = "${file("<JSONキーファイル>")}"
	project     = "${var.project_name}"
    region      = "${var.region}"
  }

data "google_compute_network" "default" {
  name = "default"
}
  
resource "google_compute_instance" "apps-gcp-terraform" {
  name         = "selenium-docker"
  machine_type = "n1-standard-1"
  zone         = "${var.zone}"
  
  boot_disk {
	auto_delete = true
    initialize_params {
      image = "${var.boot_image_name}"
      type  = "pd-standard"
    }
  }

  metadata = {
    gce-container-declaration = "${var.docker_declaration}"
  }
  
  network_interface {
	network       = "default"
	access_config {
    }
  }

  service_account {
    email = "<サービスアカウント>"
    scopes = ["cloud-platform"]
  }
}
```

##variables.tf
パラメータを扱うファイル

Docker imageからGCEインスタンス作る場合は、container-optimized imageというブートイメージを使用する
現在のcontainer-optimized imageのバージョンは以下コマンドで確認できる

```
gcloud compute images list --project cos-cloud --no-standard-images
```

```terraform:variables.tf
variable "project_name" {
  type    = string
  default = "<プロジェクトID>"
}

variable "region" {
  type    = string
  default = "us-central1"
}

variable "zone" {
  type    = string
  default = "us-central1-a"
}

variable "boot_image_name" {
  type    = string
  default = "projects/cos-cloud/global/images/cos-stable-81-12871-119-0"
}

variable "docker_declaration" {
  type    = string
  default = "spec:\n  containers:\n    - name: selenium-docker\n      image: '<dockerイメージ>'\n      stdin: false\n      tty: true\n  restartPolicy: Always\n"
}

```


#terraformでGCEインスタンスを作成する
tfファイルがあるディレクトリで以下コマンドを実行していく

##1. terraform init
terraformを初期化する
成功すると、実行したディレクトリに .terraformディレクトリが作成され必要なプラグインとかがインストールされるっぽい

こんなメッセージ出るときは
Terraform has initialized, but configuration upgrades may be needed.

terraform 0.12upgrade
↑する
いい感じにtfファイルを最新のバージョンに更新してくれるっぽい

##2. terraform plan
tfファイルが正しいかチェックする
エラー出たらtfファイル修正する

##3. terraform apply
gcp にアップする
途中で実行するか聞かれるのでyesを入力

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

google_compute_instance.apps-gcp-terraform: Creating...
google_compute_instance.apps-gcp-terraform: Still creating... [10s elapsed]
google_compute_instance.apps-gcp-terraform: Creation complete after 13s 

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

↑みたいな感じででたらOK

あとは作られたGCEインスタンスに入って

```
docker container ls 
```

するとコンテナが稼働しているので、

```
docker container exec -it コンテナID /bin/bash 
```

等のコマンドでコンテナの中に入り、

```
nohup python スクレイピングpythonファイル > log.txt 2>&1 & 
```

とかでプログラム実行させる

#インスタンスを削除する
##terrafrom destroy
Do you really want to destroy all resources?
 Terraform will destroy all your managed infrastructure, as shown above.
 There is no undo. Only 'yes' will be accepted to confirm.
  
Enter a value:yes

で削除される

#ハマりどころ
##サービスアカウントをアタッチする
Error: Cannot get auth token: Metadata server responded with status 404
が起きたとき、、
GCEのメタデータサーバーからトークンゲットできないということらしい、、

で、インスタンスの中で以下コマンドを実行すると、

```
curl "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \
-H "Metadata-Flavor: Google"
```

↓のメッセージが出る
Service account not enabled on this instance

ということで、サービスアカウントのアタッチをmain.tfに追加したらOKとなった↓

```terraform:main.tfのこの部分
service_account {
    email = "<サービスアカウント>"
    scopes = ["cloud-platform"]
  }
```

#参考
[terraformドキュメント](https://www.terraform.io/docs/providers/google/r/compute_instance.html)
[インスタンスの作成と構成](https://cloud.google.com/container-optimized-os/docs/how-to/create-configure-instance?hl=ja#gcloud_1)
[Terraform で変数を使う](https://qiita.com/ringo/items/3af1735cd833fb80da75)
[GCPインフラ構築自動化の道 part1 ～Terraform 導入編～](https://www.apps-gcp.com/infra-automation-01/)
[TerraformでGoogle Compute EngineにWebアプリをデプロイする。](http://www.kurigohan.com/article/20190715_gcp_terraform.html)
[Terraform 0.12がリリースされたのでアップグレードしてみた](https://dev.classmethod.jp/articles/terraform-upgrade-from-0-11-to-0-12/)
[TerraformでGCP環境を構築してみる](https://techblog.gmo-ap.jp/2017/11/16/terraform%E3%81%A7gcp%E7%92%B0%E5%A2%83%E3%82%92%E6%A7%8B%E7%AF%89%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B/)
[scopes param for service_account of google_compute_instance should be optional](https://github.com/hashicorp/terraform/issues/17004)