Quantcast
Channel: Bashタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 2863

複数環境に対応したTerraform運用環境構築

$
0
0
概要 実務でAWS & Terraformを使用する機会があったので その際得た運用環境の知見をまとめる。 また、2章以降で紹介するTerraform運用手法は、一個人の最終的な到達点であり Terraformのベストプラクティスというわけではない為、参考程度に考えてください。 (あえてterraform workspaceを使用していない等) 本記事で扱う環境一覧 開発環境 (ローカルPC) ステージング環境 (ステージング環境用AWSアカウント) 本番環境 (本番環境用AWSアカウント) 動作環境 Linux Debian [v10.8] Windows WSL の Ubuntu [v20.04] WSLの構築については以下を参照 0. AWSアカウント作成 以下を参考に、ステージング環境用と本番環境用のAWSアカウントを作成し、 Terraformで扱うリソースの権限を付与した IAMユーザのクレデンシャル情報をそれぞれ取得しておく。 初めてアカウントを作る場合 AWSアカウントの初期設定について IAMユーザ作成について マルチアカウント管理について 1. 環境構築 ⅰ. AWS CLI インストール 公式ガイドを参考に以下コマンドを順に実行する。 ①. AWS CLI インストールファイルDL curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "`echo ~`/awscliv2.zip" ②. インストールファイル展開 unzip ~/awscliv2.zip ③. AWS CLI インストール sudo ~/aws/install ④. AWS CLIへステージング環境の設定情報保存 aws configure --profile ******(アプリ名など)-stage 上記コマンド実行後、以下の質問に答える。 AWS Access Key ID [None]: -> ステージング環境用IAMユーザのクレデンシャル情報(Access Key ID)を入力 AWS Secret Access Key [None]: -> ステージング環境用IAMユーザのクレデンシャル情報(Secret Access Key)を入力 Default region name [None]: -> ap-northeast-1 Default output format [None]: -> json ⑤. AWS CLIへ本番環境の設定情報保存 aws configure --profile ******(アプリ名など)-prod 上記コマンド実行後、④と同じ質問に 本番環境用IAMユーザのクレデンシャル情報で答える。 ⅱ. Session Manager Plugin インストール ①. パッケージDL curl "https://s3.amazonaws.com/session-manager-downloads/plugin/latest/ubuntu_64bit/session-manager-plugin.deb" -o "session-manager-plugin.deb" ②. Session Manager Plugin インストール sudo dpkg -i session-manager-plugin.deb ③. Session Manager Plugin 確認 session-manager-plugin ⅲ. Terraform インストール ①. リポジトリclone git clone https://github.com/tfutils/tfenv.git ~/.tfenv ②. リポジトリへパスを通す cat << 'EOS' >> ~/.bashrc export PATH="$PATH:$HOME/.tfenv/bin" EOS source ~/.bashrc ③. tfenv インストール tfenv install ④. コマンドのTab保管を有効化 terraform -install-autocomplete ⅳ. git-secrets インストール ①. リポジトリclone git clone https://github.com/awslabs/git-secrets.git ~/.git-secrets ②. git-secrets インストール cd ~/.git-secrets/ && sudo make install ③. git-secrets をAWS用に設定 git secrets --register-aws --global ④. Gitのカスタム git secrets --install ~/.git-templates/git-secrets git config --global init.templatedir '~/.git-templates/git-secrets' 2. Terraformリポジトリ作成 ブランチmasterで、Terraformリポジトリを作成していく。 作成が完了したら、v1.0.0/stage と v1.0.0/prod のような リソース構築管理用のブランチを切る。 以後、masterブランチは全体に関わる変更を管理するために使用していく。 以下に例として、LaravelアプリケーションをAWS Fargateへデプロイするために 私が作成したTerraformリポジトリのディレクトリ構造を紹介する。 . ├── README.md ├── .terraform-version --> Terraform実行時のバージョン指定 ├── set_aws_profile.sh --> terraformコマンドを使用する事前準備 ├── modules --> Terraformで繰り返し使うモジュール │ ├── alb_target_group │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── ecs_service │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── host_client_security_group │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── iam_role │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── route53_domain │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── s3_log_bucket │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ └── security_group │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── operation --> アプリメンテ用リソース群 │ ├── .gitignore │ ├── main.tf │ ├── operation_setup.sh │ ├── outputs.tf │ ├── prod.tfbackend │ ├── prod.tfvars │ └── variables.tf ├── step0.sh --> 初期構築リソース群 ├── step1 --> 第一段階構築リソース群 │ ├── .gitignore │ ├── ecr_repository.tf │ ├── lambda_iam_role.tf │ ├── main.tf │ ├── outputs.tf │ ├── prod.tfbackend │ ├── prod.tfvars │ ├── rds_db.tf │ ├── s3_buckets.tf │ ├── ssm_parameters.tf │ ├── ssm_parameters_setup.sh --> センシティブな値を手動更新 │ ├── variables.tf │ └── vpc_endpoint.tf ├── step2 --> 第二段階構築リソース群 │ ├── .gitignore │ ├── ******(アプリ名など)_container.json │ ├── ******(アプリ名など)_container_admin.json │ ├── ******(アプリ名など)_container_camera.json │ ├── main.tf │ ├── outputs.tf │ ├── prod.tfbackend │ ├── prod.tfvars │ └── variables.tf ├── git_cherry-pick_from_master.sh --> masterブランチから各ブランチへのcherry-pick ├── terraform_apply.sh --> [terraform apply] コマンド実行用 └── terraform_destroy.sh --> [terraform destroy] コマンド実行用 上記ディレクトリ構造で運用に関わるものを紹介する。 ⅰ. .terraform-version 1.0.0 説明:このリポジトリで実行するterraformコマンドの、Terraformバージョン指定用ファイル ⅱ. set_aws_profile.sh set -eu echo 'デプロイ先:ステージング環境 or 本番環境を入力してください。' read -p 'ex) stage/prod:' ENV # ステージング環境 or 本番環境のプロファイルを設定 if [ ${ENV} = 'stage' ]; then export PS1="\[\e[1;36m\][fw-${ENV}]\[\e[0;39m\]${PS1}" export AWS_PROFILE=******(アプリ名など)-${ENV} elif [ ${ENV} = 'prod' ]; then export PS1="\[\e[1;31m\][%%% fw-${ENV} %%%]\[\e[0;39m\]${PS1}" export AWS_PROFILE=******(アプリ名など)-${ENV} else echo '[stage] or [prod] のどちらかを入力してください。' fi set +eu 説明:terraformコマンドを実行する際に使用する、AWSクレデンシャル情報の設定用sh ⅲ. step0.sh set -eu create_bucket () { # step1のTerraform状態管理S3バケット作成 echo 'step1のTerraform状態管理S3バケット作成中...' aws s3api create-bucket --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step1 --create-bucket-configuration LocationConstraint=ap-northeast-1 aws s3api put-bucket-versioning --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step1 --versioning-configuration Status=Enabled aws s3api put-bucket-encryption --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step1 --server-side-encryption-configuration '{"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]}' aws s3api put-public-access-block --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step1 --public-access-block-configuration '{"BlockPublicAcls": true, "IgnorePublicAcls": true, "BlockPublicPolicy": true, "RestrictPublicBuckets": true}' # operationのTerraform状態管理S3バケット作成 echo 'operationのTerraform状態管理S3バケット作成中...' aws s3api create-bucket --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-operation --create-bucket-configuration LocationConstraint=ap-northeast-1 aws s3api put-bucket-versioning --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-operation --versioning-configuration Status=Enabled aws s3api put-bucket-encryption --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-operation --server-side-encryption-configuration '{"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]}' aws s3api put-public-access-block --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-operation --public-access-block-configuration '{"BlockPublicAcls": true, "IgnorePublicAcls": true, "BlockPublicPolicy": true, "RestrictPublicBuckets": true}' # step2のTerraform状態管理S3バケット作成 echo 'step2のTerraform状態管理S3バケット作成中...' aws s3api create-bucket --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step2 --create-bucket-configuration LocationConstraint=ap-northeast-1 aws s3api put-bucket-versioning --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step2 --versioning-configuration Status=Enabled aws s3api put-bucket-encryption --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step2 --server-side-encryption-configuration '{"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}]}' aws s3api put-public-access-block --bucket ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step2 --public-access-block-configuration '{"BlockPublicAcls": true, "IgnorePublicAcls": true, "BlockPublicPolicy": true, "RestrictPublicBuckets": true}' } # ステージング環境 or 本番環境でS3バケットの名前を切り替える if [ ${AWS_PROFILE} = '******(アプリ名など)-stage' ]; then export S3_BUCKET_ENV=stage create_bucket echo '----- 全S3バケット作成完了 -----' elif [ ${AWS_PROFILE} = '******(アプリ名など)-prod' ]; then export S3_BUCKET_ENV=prod create_bucket echo '----- 全S3バケット作成完了 -----' else echo '[source set_aws_profile.sh] を実行し、デプロイ先を指定して下さい。' fi set +eu 説明:Terraform状態管理ファイルを格納するS3バケットの作成用sh ⅳ. git_cherry-pick_from_master.sh #!/bin/bash set -eu echo 'masterブランチでコミットをしていますか?' read -p 'ex) y/n:' YN # masterブランチでコミットをしているかを確認 if [ ${YN} = 'y' ]; then : elif [ ${YN} = 'n' ]; then echo 'masterブランチでコミットをしてください。' exit 1 else echo '[y] or [n] のどちらかを入力してください。' exit 1 fi # masterから、[v1.0.0/stage, v1.0.0/prod]それぞれへ、HEAD_commitをcherry-pick git checkout master GIT_MASTER_HEAD_COMMIT_HASH=`git rev-parse HEAD` git checkout v1.0.0/stage git cherry-pick ${GIT_MASTER_HEAD_COMMIT_HASH} git checkout v1.0.0/prod git cherry-pick ${GIT_MASTER_HEAD_COMMIT_HASH} git checkout master 説明:stage、prodブランチそれぞれへ、masterブランチのHEAD_commitをcherry-pickする用sh ⅴ. terraform_apply.sh #!/bin/bash set -eu # ステージング環境 and 本番環境でTerraform applyコマンドの定義 terraform_apply_stage () { cd ./${DEPLOY} terraform fmt terraform init -reconfigure terraform apply cd - } terraform_apply_prod () { cd ./${DEPLOY} terraform fmt terraform init -reconfigure -backend-config=prod.tfbackend terraform apply -var-file=prod.tfvars cd - } # [source set_aws_profile.sh] を実行し、デプロイ先を指定しているか確認 if [ ${AWS_PROFILE} = '******(アプリ名など)-stage' -o ${AWS_PROFILE} = '******(アプリ名など)-prod' ]; then : else echo '[source set_aws_profile.sh] を実行し、デプロイ先を指定して下さい。' exit 1 fi echo 'デプロイ先:ステージング環境 or 本番環境を入力してください。' read -p 'ex) stage/prod:' ENV # ステージング環境 or 本番環境でTerraform applyコマンドを切り替える if [ ${ENV} = 'stage' ]; then git checkout v1.0.0/${ENV} echo 'AWSリソース構築:step1 or operation or step2を入力してください。' read -p 'ex) step1/operation/step2:' DEPLOY # step1 をデプロイ if [ ${DEPLOY} = 'step1' ]; then terraform_apply_stage # operation をデプロイ elif [ ${DEPLOY} = 'operation' ]; then terraform_apply_stage # step2 をデプロイ elif [ ${DEPLOY} = 'step2' ]; then terraform_apply_stage else echo '[step1] or [operation] or [step2] のいずれかを入力してください。' fi elif [ ${ENV} = 'prod' ]; then git checkout v1.0.0/${ENV} echo 'AWSリソース構築:step1 or operation or step2を入力してください。' read -p 'ex) step1/operation/step2:' DEPLOY # step1 をデプロイ if [ ${DEPLOY} = 'step1' ]; then terraform_apply_prod # operation をデプロイ elif [ ${DEPLOY} = 'operation' ]; then terraform_apply_prod # step2 をデプロイ elif [ ${DEPLOY} = 'step2' ]; then terraform_apply_prod else echo '[step1] or [operation] or [step2] のいずれかを入力してください。' fi else echo '[stage] or [prod] のどちらかを入力してください。' fi 説明:[terraform apply] コマンドをステージング環境、本番環境それぞれで使い分ける用sh ⅵ. terraform_destroy.sh #!/bin/bash set -eu # ステージング環境 and 本番環境でTerraform destroyコマンドの定義 terraform_destroy_stage () { cd ./${DEPLOY} terraform fmt terraform init -reconfigure terraform destroy cd - } terraform_destroy_prod () { cd ./${DEPLOY} terraform fmt terraform init -reconfigure -backend-config=prod.tfbackend terraform destroy -var-file=prod.tfvars cd - } # [source set_aws_profile.sh] を実行し、デプロイ先を指定しているか確認 if [ ${AWS_PROFILE} = '******(アプリ名など)-stage' -o ${AWS_PROFILE} = '******(アプリ名など)-prod' ]; then : else echo '[source set_aws_profile.sh] を実行し、デプロイ先を指定して下さい。' exit 1 fi echo 'デプロイ先:ステージング環境 or 本番環境を入力してください。' read -p 'ex) stage/prod:' ENV # ステージング環境 or 本番環境でTerraform destroyコマンドを切り替える if [ ${ENV} = 'stage' ]; then git checkout v1.0.0/${ENV} echo 'AWSリソース構築:step1 or operation or step2を入力してください。' read -p 'ex) step1/operation/step2:' DEPLOY # step1 をデプロイ if [ ${DEPLOY} = 'step1' ]; then terraform_destroy_stage # operation をデプロイ elif [ ${DEPLOY} = 'operation' ]; then terraform_destroy_stage # step2 をデプロイ elif [ ${DEPLOY} = 'step2' ]; then terraform_destroy_stage else echo '[step1] or [operation] or [step2] のいずれかを入力してください。' fi elif [ ${ENV} = 'prod' ]; then git checkout v1.0.0/${ENV} echo 'AWSリソース構築:step1 or operation or step2を入力してください。' read -p 'ex) step1/operation/step2:' DEPLOY # step1 をデプロイ if [ ${DEPLOY} = 'step1' ]; then terraform_destroy_prod # operation をデプロイ elif [ ${DEPLOY} = 'operation' ]; then terraform_destroy_prod # step2 をデプロイ elif [ ${DEPLOY} = 'step2' ]; then terraform_destroy_prod else echo '[step1] or [operation] or [step2] のいずれかを入力してください。' fi else echo '[stage] or [prod] のどちらかを入力してください。' fi 説明:[terraform destroy] コマンドをステージング環境、本番環境それぞれで使い分ける用sh ⅶ. ssm_parameters_setup.sh #!/bin/bash set -eu echo 'LaravelのAPP_KEYを入力してください。' read -p 'APP_KEY:' APP_KEY echo 'RDSのDBマスターパスワードを入力してください。' read -p 'DB_PASSWORD:' DB_PASSWORD # AWSリソースに適用 aws ssm put-parameter --name '/******(アプリ名など)/app/key' --type SecureString --value "${APP_KEY}" --overwrite aws ssm put-parameter --name '/******(アプリ名など)/db/password' --type SecureString --value "${DB_PASSWORD}" --overwrite aws rds modify-db-instance --db-instance-identifier '******(アプリ名など)' --master-user-password "${DB_PASSWORD}" 説明:リソース上のセンシティブな値を手動で更新する用sh ⅷ. 構築リソース群のprod.tfbackend bucket = "******(アプリ名など)-prod-tfstate-step1" 説明:本番環境用のTerraform状態管理S3バケットを参照するためのファイル ⅸ. 構築リソース群のprod.tfvars s3_bucket_env = "prod" s3_force_destroy = false alb_enable_deletion_protection = true db_name = "******(アプリ名など)Prod" db_multi_az = true db_deletion_protection = true db_skip_final_snapshot = false domain_name = "<test_domain_prod>" domain_name_camera = "<test_domain_prod_camera>" domain_name_admin = "<test_domain_prod_admin>" ecr_mutability = "IMMUTABLE" 説明:本番環境用のリソース構築パラメータのファイル ⅹ. 構築リソース群の.gitignore # ディレクトリ /.terraform/ # ファイル .terraform.lock.hcl .terraform.tfstate.lock.info terraform.tfstate terraform.tfstate.backup 説明:terraformコマンドを実行する際に自動生成されるファイルを    リモートリポジトリに含めないようにするためのファイル 3. 運用オペレーションについて 以下に例として、LaravelアプリケーションをAWS Fargateへデプロイするために 私が作成したTerraform運用オペレーションを紹介する。 作業手順の簡略化、ヒューマンエラーの防止を最大限意識した構成にするため シェルスクリプトのごり押し状態となっている。 (いつかコマンドラインツールを自作したい) ⅰ. AWSリソース構築を始める前に 本リポジトリ直下で source set_aws_profile.sh を実行し これからデプロイする環境を指定する。 AWSリソースの構築状態を管理するために必要なS3バケットを作成しておく。 (上記S3バケットの構築は、本リポジトリ直下へ移動し、source step0.sh ) テストドメインとして無料独自ドメインを取得しておく。(https://my.freenom.com/clientarea.php) 以下コード内のパラメータを確認する。 /step1/variables.tf のテストドメイン3種 (同ディレクトリ内の prod.tfvars も要変更) /step2/prod.tfvars の ecs_docker_image_tag ⅱ. AWSリソース構築手順 ※以降、以下実行するコマンドを [Ctrl + c] で強制終了しないよう注意。 ①. 本リポジトリ直下で bash terraform_apply.sh ②. AWSリソース構築:step1 ③. 構築されるリソース一覧を確認し、yes ④. テストドメインのNameServer設定 以下の出力が繰り返されるようになったら設定を行う。 module.route53_domain.aws_acm_certificate_validation.default: Still creating... [2m30s elapsed] ちなみに、テストドメインのNameServer設定が問題ないか確認するには、 dig <test_domain> +trace や dig @8.8.8.8 <test_domain> NS といったdigコマンドで検証する。 (SSL証明書の検証には、5〜100分ほどかかる) ⑤. LaravelのAPP_KEYの値とRDSのDBマスターパスワードを以下コマンドで変更 bash ./step1/ssm_parameters_setup.sh ⑥. ECRのプライベートリポジトリにDockerイメージをPush ⑦. 上記 ①〜③ の手順で、AWSリソース構築:operation ⑧. オペレーションサーバへ以下コマンドでシェルアクセス aws ssm start-session --target <operation_instance_id> シェルアクセス後、以下の手順を実施する。 sudo su - bash docker_run.sh php artisan migrate php artisan db:seed --class=<使用するシーダー> Ctrl + D を2回行い、ログアウト。 ⑨. 上記 1〜3 の手順で、AWSリソース構築:step2 ⅲ. テストドメイン変更手順 移行先DNSサービスにCNAMEレコードを、移行するテストドメインの数だけ追加する。 (ex: domain CNAME ALBhost) digコマンドで設定が反映されているか確認する。 ALBのHTTPSリスナールールのホスト条件をそれぞれ変更。 東京リージョン(ap-northeast-1) でSSLワイルドカード証明書を発行する。 SSL証明書を発行した際に生成されるCNAMEレコードを、移行先DNSサービスに追加する。 SSL証明書の検証終了後に、ALBのHTTPSリスナールールのデフォルトSSL証明書を変更する。 ※テストドメイン変更後は、step1 の ※コメントアウト対象 と記載されているコードを  コメントアウトし、bash terraform_apply.sh (step1)。 ⅳ. AWSリソース破棄手順 Lambda関連のリソースを事前に破棄しておく。 bash terraform_destroy.sh -> step2 bash terraform_destroy.sh -> operation bash terraform_destroy.sh -> step1 ⅴ. 本番環境について ステージング環境と異なる点を以下に記載する。 S3バケットの名前 S3バケットの強制削除無効化 ALBの削除保護有効化 本番環境用のDBインスタンスのDB名を使用 DBインスタンスのマルチAZ有効化 DBインスタンスの削除保護有効化 DBインスタンスのスナップショットスキップ無効化 本番環境用のテストドメインを使用 ECRリポジトリのタグのイミュータビリティ有効化 本番環境用のECSタスク定義で使用するDockerイメージタグ ECSタスクで確保するvCPU、メモリ容量UP ECSタスクの実行維持数が1→2 ECSタスクの実行維持数の変化に伴う、ECSサービスの最小/最大ヘルス率の調整 ⅵ. デプロイ単位について 仕様として、AWSリソースのデプロイ単位を以下のように分割している。 step1 --> 各Appサーバの稼働に必要なベースとなるリソースを担当 VPC ALB Route 53 (テストドメイン用) RDS S3 パラメータストア VPC Endpoint operation --> AppサーバやDBのメンテナンスをするためのリソースを担当 オペレーションサーバ (EC2) オペレーションログ セッションマネージャ (上記リソースに必要な) VPC Endpoint step2 --> 各Appサーバのリソースを担当 Appサーバ (Fargate) Cameraサーバ (Fargate) Adminサーバ (Fargate) ⅶ. 留意事項 DBのメンテナンスなどが終了し、オペレーションサーバが必要無くなったら bash terraform_destroy.sh -> operation (本番環境の場合、ログ用S3バケットが削除できないエラーが出力されるが、無視でOK) 現在のAWSリソース構築状況を確認したい場合は 確認したいディレクトリ直下で terraform show コード編集後は、(編集したファイルのカレントディレクトリで) terraform fmt でコードフォーマット。 (エディタのプラグインで、ファイル保存時に自動フォーマットする設定を導入するのがベスト) AWSの各種リソースは、以下のS3バケットの terraform.tfstate で それぞれ状態管理されている。 これらのバケットはバージョニング、暗号化、ブロックパブリックアクセスが設定されている。 ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step1 ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-operation ******(アプリ名など)-${S3_BUCKET_ENV}-tfstate-step2 Fargateの各コンテナへ以下コマンドでアクセスが可能。 aws ecs execute-command --cluster ******(アプリ名など) --task <タスクNo> --container <コンテナ名> --interactive --command "/bin/bash" 4. 参考 Terraform by HashiCorp 実践Terraform AWSにおけるシステム設計とベストプラクティス Terraform職人入門: 日々の運用で学んだ知見を淡々とまとめる git-secretsはじめました

Viewing all articles
Browse latest Browse all 2863

Trending Articles