『詳解Terraform』を写経する

『詳解 Terraform 第3版 ―Infrastructure as Codeを実現する』
https://amzn.asia/d/7MswJxk

こちらを写経してTerraformについて軽く概念を知ってる状態を作りたい。
写経中に気になることがあったら、メモしていく

（参考）
公式ドキュメントはこちらにあるので、本でわからなければこちらを見ればいいし、 https://developer.hashicorp.com/terraform/tutorials/aws-get-started

この本のサンプルコードもこちらにある
https://github.com/brikis98/terraform-up-and-running-code/tree/master

# 2.1 サーバ1台のデプロイ
## amiって何？
本にも書いてあるけど、AmazonMachineImage。EC2のインスタンスのページで「インスタンスを起動」を押して、AmazonLinux選ぶページで、AMI IDがわかる
（Amazon Linuxって、AmazonMachineImage、イメージの1つだったんだ）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356023/6e44d8e2-4aba-b456-e0d9-d86e84565983.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356023/6c9e48f4-5cbe-b1ca-d2a7-076cf08cedcf.png)


# 2. Terraformをはじめよう > 2.4 Webサーバ1台のデプロイ
## busyboxが動かない
実はこちらのコードが動かない

https://github.com/brikis98/terraform-up-and-running-code/blob/master/code/terraform/02-intro-to-terraform-syntax/one-webserver/main.tf#L16-L32
```tf
resource "aws_instance" "example" {
  ami                    = "ami-0091f05e4b8ee6709"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.instance.id]

  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, World" > index.html
              nohup busybox httpd -f -p 8080 &
              EOF

  user_data_replace_on_change = true

  tags = {
    Name = "terraform-example"
  }
}
```

### どうすればいいか
user_dataで`busybox`を立ち上げているのをpython3でのwebサーバー実行に変えてあげる（2024年8月現在）

```tf
resource "aws_instance" "example" {
  ami                    = "ami-0091f05e4b8ee6709"
  instance_type          = "t2.micro"
  vpc_security_group_ids = [aws_security_group.instance.id]

  # こちらを修正
  user_data = <<-EOF
              #!/bin/bash
              echo "Hello, World" > index.html
              python3 -m http.server 8080 --directory . &
              EOF

  user_data_replace_on_change = true

  tags = {
    Name = "terraform-example"
  }
}
```

### なぜこんなことをするか
AmazonLinuxでbusyboxがデフォルトで入らなくなったから。
（詳しくは知らないですし、入ってるものもあるかもです）
user_dataの中で入れてもいいんですが、設定しないとyumでbusybox入れようとしたときにエラーになって、ヤクの毛刈りになるので、元々入ってるpython3を使います。

ホームディレクトリ見てもindex.htmlが入ってないから、焦りました
（index.htmlどこにあるんだろうと探したら、`/`(ルートディレクトリ)直下にありました。怖いですね。Apacheを使わないにしても`/var/www/`ディレクトリ作ったほうが安全かも、と思いました）



### デバッグの方法
EC2のインスタンス一覧で「接続」をクリック（その前にセキュリティグループで22ポートに入るのを手動で許可しておく）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356023/6e44d8e2-4aba-b456-e0d9-d86e84565983.png)
「EC2 Instance Connectを使用して接続する」をクリックするとブラウザでターミナルが開く
![スクリーンショット 2024-08-17 11.00.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356023/0eac6211-bded-5a61-eb6f-8cbda4142536.png)

次の場所に起動ログがあるから確認する
```sh
$ less /var/log/cloud-init-output.log
```

# 2. Terraformをはじめよう > 2.7 ロードバランサのデプロイ
あ、今後ちゃんと書くには変えなきゃいけないのね
https://docs.aws.amazon.com/autoscaling/ec2/userguide/launch-configurations.html?icmpid=docs_ec2as_help_panel
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/356023/74eb0c4e-2e29-f2cf-7b2d-137fdb4013bc.png)

# 3 Terraformステートを管理する > 3.3 Terraformバックエンドの制限
S3でstateを管理するのは、複数人管理には堅牢っていうのはその通りだと思うけど、手順ミスると環境が壊れるから、個人でやる場合などは無理にS3で管理しなくていい気がする。

踏むべき手順はこちら

- backendのS3に保存する記述を削除（orコメントアウト）
- `terraform init -migrate-state`実行
- state保存用S3のstateファイルをバージョン含め全て削除
- `terraform destroy`



以下が起こったエラー

## backendの設定を消すの忘れてdestroy
destroy前にs3に保存する設定を削除しなければならないのに忘れてしまっていた。
destroyが失敗したときにDynamoDBのテーブルが消えてしまったので、
その後s3に保存する設定を削除しようと`terraform init -migrate-state`しようとしたが、Dynamoのテーブルにputできないとエラーになった。
→自前でDynamoDBのテーブルを作成してなんとか読んでもらって解決
（Terraform管轄外で作ったからdestroy成功後も消してもらえず手動で削除）

```sh
$ terraform init -migrate-state
Initializing the backend...
Terraform has detected you are unconfiguring your previously set "s3" backend.
╷
│ Error: Error acquiring the state lock
│
│ Error message: operation error DynamoDB: PutItem, https response error StatusCode: 400, RequestID:
│ ~~, ResourceNotFoundException: Requested resource
│ not found
│ Unable to retrieve item from DynamoDB table "terraform-up-and-running-locks": operation error
│ DynamoDB: GetItem, https response error StatusCode: 400, RequestID:
│ ~~, ResourceNotFoundException: Requested resource
│ not found
│
│ Terraform acquires a state lock to protect the state from being written
│ by multiple users at the same time. Please resolve the issue above and try
│ again. For most commands, you can disable locking with the "-lock=false"
│ flag, but this is not recommended.
```

## s3が削除できない
中身があるから削除できないと言われる。消せばいいだけの話だけど、せっかくオートメーションしてるものを手で消すのが戸惑ったし、バージョンも含めて消さなければならないのがやっかいだった。
```
│ Error: deleting S3 Bucket (terraform-sandbox-20240817): operation error S3: DeleteBucket, https response error StatusCode: 409, RequestID: H1H0A1Z1DZRN86KM, HostID: CFHwOVdWcFNTSsNrC3ZI9ZVGudjv1SZJTdszEiA6lQRyZrEPukz3uTDFyEchnbruW46Kgkqkjf0=, api error BucketNotEmpty: The bucket you tried to delete is not empty. You must delete all versions in the bucket.
```

# 7. 複数のプロバイダを使う > 7.3 異なる複数のプロバイダを使う
例として`training/webapp`イメージが挙げられているが、こちら古いイメージすぎて起動しようとするとエラーが起きる
https://hub.docker.com/r/training/webapp/
```sh
$ docker run -p 5000:5000 training/webapp                                                        [main]
Unable to find image 'training/webapp:latest' locally
latest: Pulling from training/webapp
docker: [DEPRECATION NOTICE] Docker Image Format v1 and Docker Image manifest version 2, schema 1 support is disabled by default and will be removed in an upcoming release. Suggest the author of docker.io/training/webapp:latest to upgrade the image to the OCI Format or Docker Image manifest v2, schema 2. More information at https://docs.docker.com/go/deprecated-image-specs/.
See 'docker run --help'.
```

これがk8sでコンテナ起動しようとしたときに尾を引く。
https://github.com/brikis98/terraform-up-and-running-code/blob/master/code/terraform/07-working-with-multiple-providers/modules/services/k8s-app/main.tf#L35
```
      spec {
        container {
          name  = var.name
          image = var.image # ここ

          port {
            container_port = var.container_port
          }
```
main.tfで記載したk8sはリモートからイメージを取得することになる。そのため、取得先がない状態だと動かない。そしてそれはk8sのcliツールのkubectlを使ったステータスやログを見て初めてわかるので、詳しくないとハマってしまう。

そのためこのコードを動かすために必要になるのは、ローカルで同等の動きをするDockerイメージを作って、それをリモートにプッシュすることとなる。

```Dockerfile
# Dockerfile

FROM python:3

COPY test.html .
EXPOSE 5000
ENTRYPOINT ["python3", "-m", "http.server", "5000", "--directory", "."]
```

```sh
$ docker build . -t example-http:latest
$ docker tag example-http:latest アカウント名/example-http:latest

$ docker login -u アカウント名 -p パスワード
$ docker push アカウント名/example-http:latest
```