TerraformでSnowflake環境構築スターターキットを作る試み
はじめに
Snowflake Advent Calendar 2021 11日目の記事になります。はい、11日目です。これは11日目なんです。遅くなって本当にごめんなさい。心の中で焼き土下座しながら投稿しています。
Snowflake環境構築の初期段階で必要なものをササっと作れるスターターキットを、Terraformで作ってみる取り組みのお話です。
背景
私が所属する会社では、Snowflakeを利用した環境構築プロジェクトがいくつか進行しています。それぞれのプロジェクトの情報を共有する中で、初期段階でよく使うお決まりリソース構成がなんとなくわかってきました。
そこで、社内Snowflakeユーザの間で プロジェクト開始時にリソースを速攻で展開できるスターターキットを作ろう! という取り組みを行っています。
本記事では、作成しているスターターキットについて、簡単にご紹介したいと思います。
やったこと
リソースのコード化
まず何よりもやりたいことは、前述のとおり「Snowflake利用プロジェクトで、開始時に速攻でリソースを展開できるようにしたい」です。
TerraformのSnowflakeプロバイダを使ってTerraformingしていきます。よく使うリソース構成をコード化し、ソースをコピペしてパラメータを変更すればOKなサンプルコードセットにまとめておけば、パパっと構築できます。ついでに、構成の使いまわしができて嬉しい感じになるはず。
というわけで、やったことは次になります:
- よく使うリソース構成をTerraformでコード化
- コピペしてパラメータを変更すればOKなサンプルコードセットにまとめる
リソースの洗い出し
次に、よく使うリソースにどんなものがあるか、洗い出しを行いました。挙がったものは、
- リソースモニター
- ストレージ統合一式(Integrationと外部のストレージ)
- ユーザー個別環境一式(ユーザー、ウェアハウス、データベース)
...などです。これらのサンプルコードセットをスターターキットに詰め込むことにしました。
その他
さらに、 個人的な趣味で作りたかった 社内のSnowflakeユーザーみんなが使うなら、やったほうが良さそうだなーと思った要素を追加したりしました。
- チームで開発するなら使うであろうバックエンド等の設定
- 必要なツールが一式揃ったDockerイメージ
- ついでにCI/CD(Gitlab CI/CD)も組んじゃえ(本記事では省略)
構成
全体図
AWS部分が若干目立つ気がしますが、メインは右側じゃなくて下側にあるSnowflakeブロック群です。ブラウザバックしないで下さいお願いします。
このように、複数のリソース構成(+開発環境のイメージ)を配下に置く形にしています。
Terraformディレクトリ構成
複数のリソース構成と開発環境を配下に置くため、こんな形にしています。
terraform
├─environments # 開発環境
│ ├─dev # 作業場所
│ ├─image # イメージ。AWS ECRで保存
│ └─backend # バックエンド。S3バケットとDynamoDBテーブル(※管理対象外)
│
├─examples # サンプルソース置き場
│ ├─ex_1
│ ├─ex_2
│ :
└─module # モジュール置き場
├─snowflake
└─aws
terraform/environments/dev
が作業場所です。ここに、使用するproviderやbackend設定と、examples/
以下にあるサンプルコードを参照するモジュールを定義します。
また、backendとして使用するS3バケットとDynamoDBテーブルについては、「Terraformが利用するインフラリソースはTerraform管理外に置く」ことが推奨されているため、管理下に置かずソースのみ置いておきます。
Terraform is an administrative tool that manages your infrastructure, and so ideally the infrastructure that is used by Terraform should exist outside of the infrastructure that Terraform manages. This can be achieved by creating a separate administrative AWS account which contains the user accounts used by human operators and any infrastructure and tools used to manage the other accounts.
実際のプロジェクトでは、開発系とは別のリソース管理系(インフラ系など)の配下におくか、これらのみ単発コマンドで作成することを想定しています。
Terraformソース
作業場所
作業場所(terraform/environments/dev
)の内容をいくつか挙げてみます。
まず、provider設定はこんな感じです。AWS・Snowflakeの両方のプロバイダを使います。
provider.tf
terraform {
required_version = ">= 1.0.11"
required_providers {
snowflake = {
source = "chanzuckerberg/snowflake"
version = "0.25.29"
}
aws = {
version = "3.69.0"
}
}
}
#
# Snowflake
#
# ACCOUNTADMINを使うときはこちら
provider "snowflake" {
alias = "snowflake_accountadmin"
account = var.snowflake_account
username = var.snowflake_username
private_key_path = var.snowflake_private_key_path
role = "ACCOUNTADMIN"
}
# Snowflake SYSADMINで十分なときはこちら
provider "snowflake" {
alias = "snowflake_sysadmin"
account = var.snowflake_account
username = var.snowflake_username
private_key_path = var.snowflake_private_key_path
role = "SYSADMIN"
}
#
# AWS
#
provider "aws" {
region = "ap-northeast-1"
}
Terraformが使用するSnowflakeユーザーの権限設定については、こちらの記事を参考に、ACCOUNTADMIN
権限の行使は必要な場合だけにとどめられるようにしています。
次にbackendです。あらかじめ作成してあるS3バケットとDynamoDBテーブルを指定します。
backend.tf
# S3バケット、DynamoDBテーブル定義は terraform/environments/backend/resource.tf にある
terraform {
backend "s3" {
bucket = "snowflake-starterkit" # 作成済み
key = "dev/terraform.tfstate"
region = "ap-northeast-1"
dynamodb_table = "snowflake-starterkit-lock" # 作成済み
}
}
最後に main.tf
です。
main.tf
#
# ここからは各サンプルコードを呼び出すだけ
# パラメータの与え方は examples/ 以下の各サンプルで書く
#
module "example_storage_integration_s3" {
source = "../../examples/storage_integration_s3"
providers = {
snowflake = snowflake.snowflake_accountadmin
aws = aws
}
env = "dev"
}
これはストレージ統合を作るサンプルコードを参照しているものです。ストレージ統合を作成するにはACCOUNTADMIN
が必要なので、snowflake.snowflake_accountadmin
プロバイダを渡しています。さらに、サンプルコードではS3バケット・IAMロール・IAMポリシーも作成するので、awsプロバイダも渡します。
サンプルコード
terraform/examples/
以下に、リソース構成ごとのサンプルコードを置いています。ここでは、さきほど作業環境から参照していた「ストレージ統合」のサンプルコードを紹介します。
作成するリソースの構成は、こんな感じです。
では、ソースを見ていきます。
まず、外部ステージとして使用するバケット名など、いくつかの変数を定義しておきます。
locals.tf
locals {
snowflake_storage_integration = "${var.env}-s3_integration"
snowflake_external_stage_bucket = "${var.env}-snowflake-external-stage"
snowflake_external_stage_role = "${var.env}-snowflake-external-stage-role"
snowflake_external_stage_policy = "${var.env}-snowflake-external-stage-policy"
}
次に、定義した変数を渡して、S3バケット、IAMロール、IAMポリシーを定義します。一括作成するモジュールをあらかじめ作っておいています(中身は省略します)。
aws_main.tf
# 外部ステージ(S3)
module "snowflake_external_stage" {
# S3・IAMロール・IAMポリシーを作成するモジュール
# Snowflake側のExternal IDを設定できるようにしてある
source = "../../modules/aws/s3_snowflake_external_stage"
providers = {
aws = aws
}
bucket_name = local.snowflake_external_stage_bucket
role_name = local.snowflake_external_stage_role
policy_name = local.snowflake_external_stage_policy
snowflake_external_id = [snowflake_storage_integration.s3_integration.storage_aws_external_id]
snowflake_iam_user_arn = [snowflake_storage_integration.s3_integration.storage_aws_iam_user_arn]
env = var.env
depends_on = [
# storage integration が先に存在する必要がある
snowflake_storage_integration.s3_integration
]
}
最後に、Snowflakeのストレージ統合を定義します。
snowflake_main.tf
# 外部ステージを参照するためのintegration
resource "snowflake_storage_integration" "s3_integration" {
provider = snowflake
name = local.snowflake_storage_integration
type = "EXTERNAL_STAGE"
enabled = true
storage_provider = "S3"
storage_allowed_locations = [
# 名前がわかっているので変数で与える
"s3://${local.snowflake_external_stage_bucket}/"
]
# 名前がわかっているので変数で与える
storage_aws_role_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${local.snowflake_external_stage_role}"
}
これで、いつものTerraformコマンドを実行すれば、ストレージ統合をパパっと作成することができます。あとは、locals.tf
に定義したs3バケット名などをプロジェクトに合わせて設定したり、プロジェクトごとに必要なもの(例えば、ストレージ統合を使用するタスクなど)だけ作成していけば、環境構築でスタートダッシュを決められる、という感じです。
Dockerイメージ
最後に、スターターキット開発環境のDockerイメージをご紹介します。
Terraform + SnowSQL + AWS CLIという環境になっています。これは、local-exec
でSnowSQLやAWS CLIのコマンドを使用することがたまーにあるためと、スターターキット開発中にコンテナ内でコマンドを使えると何かと便利だからです。
Dockerfile
################################################################################
# Base Image
################################################################################
FROM ubuntu:20.04
################################################################################
# Environments
################################################################################
# timezone
# "questions will narrow this down by presenting a list of cities, representing the time zones in which they are located." への対策
ENV TZ=Asia/Tokyo
################################################################################
# Arguments
################################################################################
# terraform version
ARG terraform_version="1.0.11"
# Snowflake version
ARG snowflake_bootstrap_version="1.2"
ARG snowflake_version="1.2.20"
# Snowflake install option
ARG snowsql_dest="/root/.snowsql/${snowflake_version}"
ARG snowsql_login_shell="/root/.bashrc"
################################################################################
# Run commands
################################################################################
# Set timezone
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# Install tools
RUN apt update && \
apt-get install --yes sudo make curl unzip jq git && \
# Install awscliv2
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" && \
unzip awscliv2.zip && \
rm -rf awscliv2.zip && \
sudo ./aws/install && \
# Install terraform
curl -O "https://releases.hashicorp.com/terraform/${terraform_version}/terraform_${terraform_version}_linux_amd64.zip" && \
unzip ./terraform_${terraform_version}_linux_amd64.zip -d /usr/local/bin/ && \
rm -rf ./terraform_${terraform_version}_linux_amd64.zip && \
# Install SnowSQL
curl -O "https://sfc-repo.snowflakecomputing.com/snowsql/bootstrap/${snowflake_bootstrap_version}/linux_x86_64/snowsql-${snowflake_version}-linux_x86_64.bash" && \
sudo touch "${snowsql_login_shell}" && \
mkdir -p ${snowsql_dest} && \
export SNOWSQL_DEST=${snowsql_dest} && export SNOWSQL_LOGIN_SHELL=${snowsql_login_shell} && bash ./snowsql-${snowflake_version}-linux_x86_64.bash
おわりに
スターターキットは、今まさに開発中のものです。キット内容もまだまだ少ないです。
もっと内容を充実させたら、また記事で公開したいと思います。
それと、最後にもう一つ、、、
盛大に遅刻して本当にすみませんでした!!!!!!!!!!111
Discussion