テクノロジー戦略本部の村上です。弊社ではRedashを使用しているのですが、バージョン4だったため、Dockerfileは提供されていたものの現在のように公式でDockerによるデプロイ手順はサポートされていませんでした。今回管理しやすいように新たにRedashを建ててそちらに利用者を移すことにしたので、その建てた際の手順等をご紹介したいと思います。
デプロイの方針
今回は以下の方針で建てました。
- AWSのECS FargateにECS CLIでデプロイする
- RedashのコマンドをECSのタスクとして叩くことが出来るようにする
- terraformでECS以外のリソースを作成する
1については、ECSへのデプロイは基本的にはtask definitionとDockerイメージがあれば良いので色々な方法があります。 今回は、Redashが公式のdocker-composeファイルを公開していることもあり、ecs-cliを使うことにしました。 それによって何か変更があった場合に追従しやすいかなと考えました。
2については、初回のデータベースセットアップや、バージョンアップ時にmigrateコマンドを叩くことなどあるので、コマンドを叩けるようにしておく必要があるからです。 それに加え、今回はデータサイエンスグループから権限変更のコマンドを叩きたいという要望もありました。
3は文字通りですが、次にポイントだけ紹介させて頂きます。
terraformでのリソース作成
- ECSクラスターとCloudwatch logsのロググループ、Systems Manager Parameter Storeのパラメータを作成する
- RedisとAurora postgresを構築する
- VPC Peeringを使うために、VPC Peeringをする予定のVPCのIP rangeと被らないようにしておく
- private subnetにECSを配置
- ECSで必要なロールを作成する
1については、クラスターもロググループもecs-cliのコマンドやオプションで作成出来るのですが、terraformで管理出来るものは管理したいので作成しておきます。
2は本番稼働ということでマネージドサービスを使います。
3と4は私達の都合です。VPC Peeringを使うのは、データソースとして接続したいDBが別AWSプロジェクトのprivate subnetにあるのでそれに接続するためです。 重複したIPアドレスの範囲にしてしまうと、VPCから作成し直しになるので気をつけましょう。 また、データソースとするDBにIP制限を掛けているので、NATのIPアドレスを指定して出来るようにするためにprivate subnetにECSを配置しました。
5についてはECSに詳しくないとわかりづらいので、少し詳しく説明します。
ECSのロールについて
ECSで関連するロールには、task execution roleとtask roleがあります。
task execution roleは、その名の通りタスクを実行するにあたって必要な権限を割り当てるロールです。ですので例えばECRからイメージを取ってきて使用する、Parameter Storeのパラメータを復号して環境変数として設定する等は、タスク実行時の処理なのでそれらの権限が必要となります。最低限必要な権限はAmazonECSTaskExecutionRolePolicy
として定義されているので、ECRのための権限等必要なものを追加すれば良いです。
task roleはタスクとして動かすプログラム内でAWSのライブラリを使う場合に設定が必要ですが、今回はRedashをそのまま動かすだけなのでtask roleは設定しなくて大丈夫です。
task execution roleについては、今回は公式のDockerイメージを使うのでParameter Storeにさえアクセスして復号出来るように割り振っておけば良いです。 その際に定義するpolicy statementは、公式ドキュメントのRequired IAM permissions for private registry authenticationの項に書かれているのでこれを採用すれば良いです。
必要なリソースをterraformで定義すると以下のようになります。
data "aws_kms_alias" "redash" { name = "alias/redash" } resource "aws_ssm_parameter" "cookie_secret" { name = "/redash/cookie_secret" key_id = data.aws_kms_alias.redash.arn type = "SecureString" value = data.aws_kms_secrets.redash.plaintext["cookie_secret"] } data "aws_iam_policy_document" "ecs_tasks" { statement { effect = "Allow" actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["ecs-tasks.amazonaws.com"] } } } data "aws_iam_policy_document" "ecs_tasks_ssm" { statement { effect = "Allow" actions = [ "ssm:GetParameters", "kms:Decrypt", ] resources = [ aws_ssm_parameter.cookie_secret.arn, // 他のparameter群は省略 data.aws_kms_alias.redash.target_key_arn, ] } } resource "aws_iam_policy" "ecs_tasks_ssm" { name = "ecs_tasks_ssm" path = "/" policy = data.aws_iam_policy_document.ecs_tasks_ssm.json } resource "aws_iam_role" "ecs_task_execution" { name = "ecs-task-execution-role" assume_role_policy = data.aws_iam_policy_document.ecs_tasks.json } resource "aws_iam_role_policy_attachment" "ecs_task_execution" { role = aws_iam_role.ecs_task_execution.name policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" } resource "aws_iam_role_policy_attachment" "ecs_tasks_ssm" { role = aws_iam_role.ecs_task_execution.name policy_arn = aws_iam_policy.ecs_tasks_ssm.arn }
docker-composeの準備
Dockerでのインストール方法は、以下の公式ドキュメントに説明があります。
https://redash.io/help/open-source/setup#-Docker
そこで紹介されているように、以下の公式docker-composeファイルが用意されています。
https://github.com/getredash/setup/blob/master/data/docker-compose.yml
同じリポジトリのsetup.shのcreate_config
関数を見るとわかりますが、いくつかの環境変数をenvファイルに書き込んでいます。
ですので秘匿情報以外の変数は同様にenvに書き込んでおきましょう。
PYTHONUNBUFFERED=0 REDASH_LOG_LEVEL=INFO REDASH_REDIS_URL=<REDIS_URL>
ここで言う秘匿情報は、pwgenコマンドを使うことで生成している変数のことです。
これらは自身で値を作成して、terraformでParameter Storeに格納しておきましょう。例えばREDASH_COOKIE_SECRET
は、terraformのコードで出てきたaws_ssm_parameter.cookie_secret
に該当します。
docker-composeの修正
修正点は、
- Redash自体で使用されるPostgresとRedisはマネージドサービスを使うので、docker-composeから削除
- 弊社の利用負荷がそこまで高くないのもあり、ワーカーのコンテナをまとめる
- こちらはそのままでも構わないです
- ログの出力先をCloudwatch logsに設定
この3つで、docker-compose.ymlは以下となります。
version: "2" x-redash-service: &redash-service image: redash/redash:<TAGを指定> env_file: env restart: always services: server: <<: *redash-service command: server ports: - "5000:5000" environment: REDASH_WEB_WORKERS: 4 logging: driver: awslogs options: awslogs-group: /ecs/redash awslogs-region: ap-northeast-1 awslogs-stream-prefix: server scheduler: <<: *redash-service command: scheduler environment: QUEUES: "celery,scheduled_queries,schemas,queries" WORKERS_COUNT: 2 logging: driver: awslogs options: awslogs-group: /ecs/redash awslogs-region: ap-northeast-1 awslogs-stream-prefix: scheduler
commandがworkerのものが無くても良いかという疑問があるかもしれません。
以下のv8.0.0のスクリプトを見てみますと、--beat
オプションが付いているかが基本的な違いとなっています。
https://github.com/getredash/redash/blob/v8.0.0/bin/docker-entrypoint
このオプションはcelery beatをcelery workerに同居させるものなので、最低限schedulerを起動すればworkerが無くても大丈夫です。ただし、QUEUESにworkerで指定されていたキューの種類も全て指定する必要があります。名前からおおよそキューの役割がわかるのですが、celeryだけはわからないです。これは、以下のIssueを見るとschemaをrefreshするためのキューのようです。schemaのリフレッシュを定期的に行うためにcelery beatが使われているようです。
https://github.com/getredash/redash/pull/3534
ただ、ひとつ注意点としましては、バージョン9からはceleryではなくRQ(Redis Queue)が使われるので変更があるかもしれません。
ログについては、Dockerの公式のAmazon CloudWatch Logs logging driverに設定できる内容が書かれているので、terraformで作成したロググループをこちらで指定します。
ECSへのデプロイ
ECS CLIの準備は、
この2つに従えば大丈夫です。そしてデプロイは
Tutorial: Creating a cluster with a Fargate task using the Amazon ECS CLI
に基本的に従います。docker-compose.ymlとは別に、ECS独自の設定を記載したecs-params.yml
が必要となります。
version: 1 task_definition: task_execution_role: terraformで作成したtask execution role名を指定 ecs_network_mode: awsvpc task_size: mem_limit: 2.0GB cpu_limit: 1024 services: server: secrets: - value_from: /redash/cookie_secret name: REDASH_COOKIE_SECRET # 他のsecretsも同様に設定 scheduler: secrets: - # ここにはserverと同じ値を指定 run_params: network_configuration: awsvpc_configuration: subnets: - private subnetsを指定 security_groups: - 内部からしかアクセスしないので、VPCのデフォルトセキュリティグループを指定 assign_public_ip: DISABLED # private subnetなので
CPUとメモリの値の組み合わせは、Invalid CPU or memory value specifiedを見るとわかるように既定値が決まっているのでその中から選びます。
デプロイコマンドは以下となります。 ただ、先にデータベース作成が必要となるのでそこはコマンド実行について次に話すのでそこで説明します。
ecs-cli compose --project-name <ECSクラスタ名> \ --file docker-compose.yml \ --ecs-params ecs-params.yml \ service up \ --target-groups "targetGroupArn=<terraformで作成したALBのターゲットグループのARN>,containerName=server,containerPort=5000"
Redashコマンド実行環境作成
Redashのコマンドを実行する環境として、task definitionまでは事前に作成しておいて、コマンドを叩く際にaws ecs run-task
で実行するという方法をとりました。
task definitionを作成するために、docker-compose.ymlは1コンテナだけ走らせるためにRedash本体のものと別に作成して、ecs-params.ymlには追記しています。
docker-compose.ymlは以下となります。ほぼRedash本体と同じですが、commandにはcreate_dbを指定しています。
version: "2" x-redash-service: &redash-service image: redash/redash:<TAGを指定> env_file: env services: redash-task-runner: <<: *redash-service command: create_db ports: - "5000:5000" environment: REDASH_WEB_WORKERS: 1 logging: driver: awslogs options: awslogs-group: redash awslogs-region: ap-northeast-1 awslogs-stream-prefix: task-runner
そしてecs-params.ymlには以下を追記しています。
services: redash-task-runner: secrets: - value_from: /redash/cookie_secret name: REDASH_COOKIE_SECRET - 他のsecretsを設定
上の2ファイルを元に、タスク実行用のtask definitionを作成します。
ecs-cli compose --project-name redash-task-runner \ --file docker-compose-redash-task-runner.yml \ --ecs-params ecs-params.yml \ create
よって初回のデータベースセットアップ時には、
aws ecs run-task --cluster <クラスタ名> \ --task-definition redash-task-runner \ --count 1 \ --launch-type FARGATE \ --network-configuration 'awsvpcConfiguration={subnets=[private subnetを指定],securityGroups=[デフォルトセキュリティグループを指定],assignPublicIp=DISABLED}'
のようにcount 1でタスクを実行すれば良いです。
また、他のコマンドを叩きたいときは、containerOverrides
で設定を上書きする必要があります。基本的にmanageコマンドを使うはずなので、そのためにrun-taskをラップした以下のようなシェルスクリプトを用意しました。
CMD=$* echo "command: manage $(IFS=' '; echo $*)" task_arn=$(aws ecs run-task --cluster redash --task-definition redash-task-runner --count 1 --launch-type FARGATE --overrides "{\"containerOverrides\": [{\"name\":\"redash-task-runner\",\"command\": [\"manage\", \"$CMD\"]}]}" --network-configuration 'awsvpcConfiguration={subnets=[省略],securityGroups=[省略],assignPublicIp=DISABLED}' --output json --query 'tasks[0].taskArn' | sed 's/"//g') task_id=$(echo $task_arn | cut -f 3 -d/) echo "Task information:" echo $task_arn echo $task_id echo "Task output:" aws ecs wait tasks-stopped --cluster redash --tasks $task_arn ecs-cli logs --task-id $task_id --region ap-northeast-1 --cluster redash
少し実行に時間がかかりますが、シェルスクリプトの引数として例えばgroups list
を与えると、ユーザーグループの一覧が確認できます。
メール送信設定
最後にメール送信の設定です。パスワードリセットやアカウント作成時の通知のために、メール設定は必要となります。 そのために、公式のMail Configurationを参考に、 envに以下を追加します。私達はsendgridを使用しているのでその例となっていますが、そうではない方は適宜変更してください。
REDASH_MAIL_SERVER=smtp.sendgrid.net REDASH_MAIL_PORT=587 REDASH_MAIL_USE_TLS=true REDASH_MAIL_USERNAME=apikey REDASH_MAIL_DEFAULT_SENDER=<SENDER_EMAIL_ADDRESS> REDASH_HOST=<REDASH_HOST>
まとめ
試行錯誤する際に、tjinjinさんのRedashをFargateで立ち上げるが大変に参考となりました。 私の勉強不足で間違っているところもあるかもしれませんが、本記事もまた同様にRedashを建てる際の参考になれば幸いです。