データ活用基盤を作ってみたその６(構築・デプロイ構成)

# はじめに
こんにちは、NTTデータ先端技術の[白木](@shiraki_ils)です。
本記事はデータ活用基盤を作ってみた連載記事の６(構築・デプロイ構成)です。
本シリーズの取り組みの内容については[その1(構成シナリオ)](https://qiita.com/kamein/private/81df29ef9d32bf0a0810)をご覧ください。
前の記事は、[その５(試験)](https://qiita.com/S-takahashi1129/items/f5fba7eca86cc1d30940)からご覧ください。
本記事では構築編の最初ということで、AWSにデータ活用基盤を構築する際のデプロイ構成について紹介します。

# 目次
* 本記事について
* 前提
* 構築時の構成案
* リモートバックエンドの構築
* セキュアなアクセスキーの作成
* ローカル環境の構築
* 困った点
* 最後に

# 本記事について
* 本記事で触れていること
    * Terraformを用いたAWSへの構築手順
    * リモートバックエンドの構築手順
    * AWSアクセスキーのIP制限について
    * ローカルにTerraformの実行環境を構築する手順
* 本記事で記載していないこと
    * AWS、Terraformの用語や詳しい説明

# 前提
* TechlearningはSmall Startで進行
* メンバーは７名

# 構築時の構成案
IaCに知見があるメンバーがいなかったこともあり、勉強もかねてTerraformを用いてAWSに構築することになりました。
また、Small Startということもあって複数環境を用意せず1つのみとしました。
その際の構築時のデプロイ構成やコード管理について検討した4つの案をご紹介します。

### 1. デプロイ担当者1人のローカル管理
デプロイ担当者を1人立て、その人がデプロイの実施とローカルでのState管理を実施します。
* 簡易ですぐ始められ、アクセスキーが1つでよい点はメリット。
* ただ、デプロイ担当者の負担が大きく効率的ではない。

| 考慮内容 | 実施内容 |
|:-:|:-:|
| コードの管理場所  | 共有フォルダ  |
|  Stateの管理場所（Backend） | 担当者のローカル  |
|  デプロイ方法 | 担当者が手動で実施する  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3361542/e4e5833f-59fe-6fb2-da52-fb9179a8221a.png)

### 2. Remote Backendの採用
Remote Backendを採用し、各自の環境からデプロイを実施します。
* Remote Backendには、S3とDynamoDBを採用。
* 各自がデプロイ可能になる点はメリット。
* 各作業者にIAMアクセスキーが必要になる。

| 考慮内容 | 実施内容 |
|:-:|:-:|
| コードの管理場所  | 共有フォルダ  |
|  Stateの管理場所（Backend） | Remote Backend  |
|  デプロイ方法 | 各作業者が手動で実施する  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3361542/cc308ad8-542b-4683-0b35-1ffb30f3c408.png)


### 3. git管理、remote backendの採用
TerraformのコードをGitで管理し、Terraformの差分をRemote Backendで管理します。
* Gitによるコード管理により、競合解決やデグレの防止ができる点はメリット。
* Gitサービスの構築が必要。各利用者にユーザー払い出しが必須。
    * ※今回のPJでGitサービスを利用する場合は、社内で追加の使用許可等をとる必要がある。。

| 考慮内容 | 実施内容 |
|:-:|:-:|
| コードの管理場所  | Git  |
|  Stateの管理場所（Backend） | Remote Backend  |
|  デプロイ方法 | 担当者が手動で実施する  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3361542/940aaeba-4b3c-d4c2-6056-c93bb8eb5055.png)


### 4. git管理、remote backendの採用かつCI/CDの実装
コードをGitで管理し、Terraformの差分をRemote Backendで管理した上で、CI/CDの実装を行いデプロイの自動化をします。
* デプロイが安全かつ容易に実施できる。
* CI/CDの構築に工数がとられる。

| 考慮内容 | 実施内容 |
|:-:|:-:|
| コードの管理場所  | Git  |
|  Stateの管理場所（Backend） | Remote Backend  |
|  デプロイ方法 | CI/CD  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3361542/73ee20d9-e116-0bf2-a8c3-7a6aec5be6d4.png)

### 選択した構成案
上記案を整理した表が下記になります。

| 構築案 | 1.ローカル管理  | 2. Remote Backend | 3. Git管理 | 4. CI/CD |
|:-:|:-:|:-:|:-:|:-:|
| コード管理  |  ファイルサーバー |  ファイルサーバー | Git管理  |  Git管理 |
| State管理 | ローカル管理  | Remote Backend | Remote Backend  |  Remote Backend |
| デプロイ方法  | 担当者の手動実行  | 各自が実行 |  各自が実行 | 担当者の手動実行  |
| IAMアクセスキー  |  1つ | 担当者分必要  | 担当者分必要  | 1つ  |

本PTはSmall Startで実施しているので、構築の構成に工数をできるだけかけたくないことが前提としてありました。そのため、案4のCI/CD構築は工数がかかりすぎるためNGに。
また、案3のGit管理はGitのアカウント払い出しや使用申請に期間が要するためNGとなり、案1のローカル管理は担当者の負担が大きく効率的に開発が実施できないことから**案2のRemote Backendを利用した環境**の構築を選択することになりました。

# リモートバックエンドの構築
構築時の構成でRemote Backendを採用することになったため、Remote Backendの構築手順についてご紹介します。
今回はチームメンバーそれぞれでstate fileにアクセスするために、S3とDynamoDBをRemote Backendとして構築しました。
S3はstateファイルを管理する役割でDynamoDBは同期制御を行う目的で採用してます。

また、せっかくTerraformを採用しているので、作成するリソースと同じくRemote BackendもTerraformで管理をしたいと思ったのですが一緒に管理をするのはよくないそうです。

>「Terraformはインフラストラクチャを管理する管理ツールです。
したがって、Terraformで使用されるインフラストラクチャは、Terraformが管理するインフラストラクチャの外部に存在する必要があります。 」

[参考記事](https://nekopunch.hatenablog.com/entry/2018/03/28/100042)によると、Terraformで使用されるインフラストラクチャは外部で管理するべきだそうです。

>人間のオペレータが使用するユーザアカウントと、
他のアカウントを管理するために使用されるインフラストラクチャとツールを含む別の管理 AWSアカウントを作成することで実現できます。

TerraformでRemote Backendの管理をしたい場合は、別のAWSアカウントを作成しそのアカウントで管理をする必要があります。AWS Organizationsと組み合わせれば実現可能なのですが、アカウントの払い出し等時間を要するため、手動でBackendの構築を行うことにいたしました。
（Terrafomと手動構築のリソースの2重管理面倒ですね。。）

下記が構築手順になります。
### S3構築手順
1. S3バケット一覧から「バケットを作成」を押下
1. 下記パラメータを入力し、「バケットを作成」を押下する。
    * バケット名：Remote-Backend-Bucket
    * AWS リージョン：ap-northeast-1
    * オブジェクト所有者：ACL無効
    * このバケットのブロックパブリックアクセス設定：パブリックアクセスをすべて ブロック
    * バケットのバージョニング：有効にする
    * デフォルトの暗号化：Amazon S3 マネージドキーを使用したサーバー側の暗号化 (SSE-S3)
    * バケットキー：無効にする 

### DynamoDB構築手順
1. テーブル一覧から「テーブルの作成」を押下
1. 下記パラメータを入力し、「テーブルの作成」を押下する。
    * テーブル名：Remote-Backend-Table
    * パーティションキー：LockID、文字列
    * ソートキー：なし
    * テーブル設定：デフォルト


Remote BackendのTerraformへの設定は後のローカル環境構築で記載いたします。

# セキュアなアクセスキーの作成
今回の場合、各作業者ごとにデプロイ環境を作成するため、それぞれにアクセスキーの発行が必要になります。何もセキュリティの対策をしていないアクセスキーが漏洩すると不正利用される可能性が高いため、スイッチロールの権限のみをアクセスキーに付与し、IP制限をかけ特定のIPからでしかスイッチできない仕組みとしました。

### スイッチロール
スイッチロールの構成を以下としました。
デプロイ用のユーザーから特定のIP制限化でスイッチを可能にし、管理者権限を付与する形式です。
ロールの作成は下記を参考に作成しました。
参考）https://dev.classmethod.jp/articles/iam-switch-role-in-same-account/

### IP制限手順
* スイッチ先のロールに特定IPのみの制限をかけるために、信頼関係にIPを記載します。
    ```json:信頼されたエンティティ
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Principal": {
                    "AWS": "arn:aws:iam::{account_id}:root"
                },
                "Action": "sts:AssumeRole",
                "Condition": {
                    "IpAddress": {
                        "aws:SourceIp": [
                            "{IPアドレス}",
                            "{IPアドレス}"
                        ]
                    }
                }
            }
        ]
    }
    ```
* Condition句でIpAddressを指定することで特定のIP以外のアドレスからアクセスできないようになります。


# ローカル環境構築
リモートバックエンドの作成とデプロイ用のアクセスキーが作成できたので各自のローカル環境の構築を行います。

* version
    * Terraform : 5.5.0
    * provider : 1.5.2

下記、今回の構築手順になります。
### 手順
1. プロキシ設定
    * 各環境下において、プロキシ設定を行う
        * 弊社はプロキシ制限があるため、プロキシ突破用に環境変数を設定する。
    ```bash:プロキシ設定
    $ env:http_proxy="http://{use_name}:{password}@{プロキシサーバーアドレス}:{ポート番号}"
    $ env:https_proxy="http://{use_name}:{password}@{プロキシサーバーアドレス}:{ポート番号}" 
    ```
2. Terraform
    1. Terraformのインストール  
    下記リンクより、WindowsのAMD64をダウンロードする。  
    https://developer.hashicorp.com/terraform/downloads
    
    2. 解凍し、任意のフォルダにterraform_1.2.2_windows_amd64を保存する  
    （私は、/c/ 直下にterraformとrenameして保存しました。）
    
    3. Pathを通す  
        1. Windowsの設定のシステムの［詳細情報］より【システムの詳細設定】から「環境変数」を選択。  
        2. 「Path」を選択し、編集を押下する。
        3. 「新規」から2.で配置したフォルダを選択する。
        4. terraformコマンドが実行できたらOK
        ```
        $ terraform -v
        Terraform v1.5.1
        on windows_amd64
        + provider registry.terraform.io/hashicorp/aws v5.5.0
        
        Your version of Terraform is out of date! The latest version
        is 1.5.2. You can update by downloading from https://www.terraform.io/downloads.html
        ```
    4. 参考  
    https://dev.classmethod.jp/articles/kidapan-terraform-install/

3. AWS CLI
    1. AWS CLIのインストール  
        * 下記公式リンクより、インストーラーのダウンロードを実施する。
            * https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html 
        * 下記コマンドが実施できたらインストール完了
        ```
        $ aws --version
        aws-cli/2.12.3 Python/3.11.4 Windows/10 exe/AMD64 prompt/off
        ```
    
    2. クレデンシャルの設定
        * 対話形式で入力を行う
        ```
        $ aws configure
        
        AWS Access Key ID [None]: アクセスキーを入力する
        AWS Secret Access Key [None]: シークレットアクセスキーを入力する
        Default region name [None]: ap-northeast-1
        Default output format [None]: json
        ```
        * 設定ファイルを確認し、正しく反映されていればOK
        ```
        $ cat ~/.aws/config 
        [default]
        region = ap-northeast-1
        output = json
        
        $ cat ~/.aws/credentials 
        [default]
        aws_access_key_id = ~~~
        aws_secret_access_key = ~~~
        ```
    
    3. AWS CLIのassume roleの設定
        * 現状のユーザーでは権限がなく、何もできないのでスイッチロール先のロールをconfigに記載する必要がある。
        ```
        $ aws s3 ls
        An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied
        ```
        * ~/aws/configに情報を追記する(profile名はなんでもOK)
        ```
        [default]
        region = ap-northeast-1
        output = json
        
        [profile deploy]
        role_arn = arn:aws:iam::{acount_id}:role/{role_name}
        source_profile = default
        region = ap-northeast-1
        output = json
        ```
        
        * 設定したprofileを環境変数に設定する  
        ※ターミナル再起動行った際は、再度設定してください。
        ```
        $ export AWS_DEFAULT_PROFILE=deploy
        ```
        
        * 下記コマンドを実施し、結果が返ってきたらOK
        ```
        $ aws s3 ls
        2023-07-03 16:10:40 Remote-Backend-Bucket
        ```
    
    4. 参考
        * https://dev.classmethod.jp/articles/installed_aws-cli_on_my_local_windows/
        * https://dev.classmethod.jp/articles/aws-cli-switch-role-script/

4. Terraformのassume roleの設定、Remote Backendの設定
    * スイッチロール先のARNを記載する必要があるので、下記のように記載する。
    * また、Backend用に作成したS3とDynamoDBをbackend.tfに記載する。
    ```terraform:provider.tf
    provider "aws" {
      region                   = "ap-northeast-1"
      shared_credentials_files = ["$HOME/.aws/credentials"]
      profile                  = "default"
    
      # assume_roleブロックを追加
      assume_role {
        role_arn = "arn:aws:iam::{acount_id}:role/{role_name}"
      }
    }
    
    terraform {
      required_providers {
        aws = {
          source  = "hashicorp/aws"
          version = "~> 5.5.0"
        }
      }
      required_version = ">= 1.5.2"
    }
    
    data "aws_region" "current" {}
    
    data "aws_caller_identity" "current" {}
    ```
    ```terraform:backend.tf
    terraform {
      backend "s3" {
        bucket                  = "Remote-Backend-Bucket"
        key                     = "terraform.tfstate"
        region                  = "ap-northeast-1"
        dynamodb_table          = "Remote-Backend-Table"
        shared_credentials_file = "$HOME/.aws/credentials"
    
        # role_arn オプションを追加
        role_arn = "arn:aws:iam::{acount_id}:role/{role_name}"
      }
    }
    ```

    下記コマンドを実行すれば、BackendにS3とDynamoDBを設定できる。
    ```
    $ terraform init
    $ terraform plan
    $ terraform apply
    ```



# 困った点
1. コードのファイルサーバー管理
今回は小規模な開発だったた問題なかったのですが、大規模な開発になった際はコードをファイルサーバーで管理するのは大変だなと感じました。毎回修正分を共有サーバーに反映させ、古いバージョンをリネーム、他のチームメンバーが修正した分を手動で取り込みをする対応が必要となります。
バグ修正等を繰り返す中で、毎回手動で最新版と古いもののリネームを行うことが手間なので、雑になってしまうことがあり、作業者の判断によって最新版を反映しないということもあり得うるためです。
そのため、開発を進めていく中でファイルサーバー管理は効率を下げ工数が上がってしまっているように感じるので、Git等ある程度強制力のある中で開発をする方管理が楽になり、効率も上がるのではと思いました。

2. 開発環境がなかった点
開発環境と本番環境が同じで１環境のみだったので、Glue jobスクリプトの検証をした際に他サービスに影響を与えてしまった。Small Startととはいえ、環境は複数あった方が安全で構築を進めやすいと感じました。

# 最後に
最後までご覧くださりありがとうございました！
今回は、Terraformを用いたAWSのインフラ構築の手順をご紹介しました。
少しでも、本記事が参考になれば幸いです。
間違い等ございましたら、気軽にコメントください～～
次章は、[その７(構築・IAM)](https://qiita.com/fujitak-intellilink/items/4c4d5b62dac2d7b4d39f)についてです！