Cloud FormationでAWSリソースを使ったRedash環境を構築してみました。Redashの開発環境の際必要となったことを備忘録として残せたらと思います。
Redashとは
役割
Redashはデータ分析を行うためのダッシュボードです。BigQueryなどのデータソースを追加し、クエリの実行結果に基づいたダッシュボードを作ることできます。また、クエリ実行結果に基づいたアラート通知を行うことが可能です。
redash.io
構成要素
Redashの構成要素は次の通りです。
- Nginx: Webアプリのリバースプロキシ
- Gunicorn: WSGI対応のサーバ。RedashのWebアプリを動かす
Nginx+gunicorn構成でFlaskを使う[ローカル環境編] - Qiita
- Celery: 分散タスクキュー。クエリの非同期実行を行う
django+Celeryによる非同期処理について - Qiita
- Redis: Cerelyのメッセージブローカー。
https://aws.amazon.com/jp/redis/Redis_Streams/
- PostgreSQL: データベース。各種情報の保持
- Supervisor:プロセス管理ツール
クエリ実行フロー
Redashは RedisとPostgresを使っています。クエリ処理の流れとしては次の図がわかりやすいです。
一歩踏み込む Redash - Speaker Deck
作りたいもの
次の構成でRedash環境を作ってみます。データベースはAWSのAurora PostgreSQLとElasticache Redisを使います。ドメインはAWSの別アカウント(本番環境)で使っているドメインのサブドメインを使って開発環境を作ります。
Redash AMI
Setting up a Redash Instance
BigQuery接続方法
BigQuery
Redashの注意点
Redash環境作るにあたり注意点をまとめました。
環境変数の変更
Redashが参照するデータベースなど環境変数ファイルを書き換える必要があります。環境変数ファイルを変更した場合、再度コンテナを起動させる必要がありますが、次に記載されている通りコンテナの再起動では不十分です。
「 Once you updated the configuration, restart all services (docker-compose up -d, running docker-compose restart won’t be enough as it won’t read changes to env file).
To test email configuration, docker-compose run --rm server manage send_test_mail」
Setting up a Redash Instance
試しに「docker-compose down 」から「docker-compose up -d --build 」を行ってみたところ、Redash UIから投げたクエリが返りませんでした。
UserDataの初期化は成功しており、ワーカーコンテナも起動してたのでなかなか謎でした。
対応として初回に再起動「sudo shutdown -r now」を行ったところうまく行きました。EC2 Image Builderを使って環境変数ファイル変更後に動的にAMIを作るでも良いかもしれません。
EC2 Image Builderで作ったAMI IDを動的に取得して、AutoScalingグループの起動設定を更新する - Qiita
{ echo PYTHONUNBUFFERED=0 echo REDASH_LOG_LEVEL=INFO echo POSTGRES_PASSWORD=${!RedashPassword} echo REDASH_COOKIE_SECRET=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-cookie-secret | jq -r .SecretString) echo REDASH_SECRET_KEY=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-secret-key | jq -r .SecretString) echo REDASH_REDIS_URL=redis://${ElasticacheClusterForRedash.RedisEndpoint.Address}:${ElasticacheClusterForRedash.RedisEndpoint.Port}/0 echo REDASH_DATABASE_URL=postgresql://${!RedashUsername}:${!RedashPassword}@${RDSDBClusterForRedash.Endpoint.Address}:${RDSDBClusterForRedash.Endpoint.Port}/${!RedashUsername} echo REDASH_FEATURE_EXTENDED_ALERT_OPTIONS=true } >> /opt/redash/env sudo shutdown -r now
Redashのバージョンアップ
Redashのバージョンアップは段階的に行う必要があります。次のように参照するAMIを変更して段階的にマイグレーションを行います。
今回バージョン4.0.1からバージョン8.00にあげた際は次の手順で段階的にマイグレーションを行いました。
1. 最新(バージョン8.00)のAMIを起動
Setting up a Redash Instance
2. バージョンを変更してビルド
sudo vi /opt/redash/docker-compose.yml redash/redash:8.0.0.b32245 → redash/redash:4.0.1.b4038 sudo docker-compose -f /opt/redash/docker-compose.yml down sudo docker-compose -f /opt/redash/docker-compose.yml up -d --build
2. マイグレーション対象のデータをロード(redash/redash:4.0.1.b4038)
3. マイグレーション
sudo docker-compose stop server scheduler scheduled_worker adhoc_worker sudo docker-compose run --rm server manage db upgrade sudo docker-compose up -d sudo docker ps -a
4. バージョンを変更してビルド、マイグレーション処理を繰り返す
sudo vi /opt/redash/docker-compose.yml redash/redash:4.0.1.b4038 → redash/redash:5.0.0.b4754 sudo docker-compose -f /opt/redash/docker-compose.yml down sudo docker-compose -f /opt/redash/docker-compose.yml up -d --build sudo docker-compose stop server scheduler scheduled_worker adhoc_worker sudo docker-compose run --rm server manage db upgrade sudo docker-compose up -d sudo docker ps -a
Redashの秘密情報
Redash AMIの環境変数ファイルにある「REDASH_COOKIE_SECRET」や「 REDASH_SECRET_KEY」がないとデータソースの追加や参照ができなくなります。Redashはバージョンアップも頻繁にあるので注意が必要です。
旧Redash(V2) から 新Redash(V8) にアップデートを行いました
Cloud Formation
ここからはCloud Formationを使ってAWS上にRedash環境を作ります。
ネットワーク
まずはネットワーク環境を作ります。
パラメータ
Cloud Formationで扱うパラメータを定義します。
AWSTemplateFormatVersion: 2010-09-09 Parameters: GlobalPrefix: Type: 'String' Default: '<GlobalPrefix>' GlobalEnvironment: Type: 'String' Default: '<GlobalEnvironmen>' VPCCidrBlock: Type: 'String' Default: '10.0.0.0/16' IPCidrBlockPublicAZ1: Type: 'String' Default: '10.0.1.0/24' IPCidrBlockPublicAZ2: Type: 'String' Default: '10.0.2.0/24' IPCidrBlockPrivateAZ1: Type: 'String' Default: '10.0.3.0/24' IPCidrBlockPrivateAZ2: Type: 'String' Default: '10.0.4.0/24' IPCidrBlockAllow1 Type: 'String' Default: '<Ip Address>' IPCidrBlockAllow2 Type: 'String' Default: '<Ip Address>' AZ1: Type: 'AWS::EC2::AvailabilityZone::Name' Default: '' AZ2: Type: 'AWS::EC2::AvailabilityZone::Name' Default: '' SSHKeyName : Type: 'String' Default: '' NativeDomain: Type: 'String' Default: '<Domain Name>'
VPC
ネットワーク領域を定義します。IPアドレスの範囲を示すときは「CIDR表記」もしくは「サブネットマスク表記」のいずれかを用います。VPCのCIDRは「10.0.0.0/16」とします。
## VPC Definition --------------------------------------------------------------------------------
EC2VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VPCCidrBlock
Tags:
- Key: Name
Value: VPC
DHCP
DHCP (Dynamic Host Configuration Protocol) は、 IPv4ネットワークにおいて通信用の基本的な設定を自動的に行うためのプロトコルです。
DHCP オプションセット - Amazon Virtual Private Cloud
## DHCP Definition -------------------------------------------------------------------------------- EC2DHCPOptions: Type: 'AWS::EC2::DHCPOptions' Properties: DomainName: !Sub '${AWS::Region}.compute.internal' DomainNameServers: - 'AmazonProvidedDNS' Tags: - Key: 'Name' Value: !Sub ${AWS::StackName}-dhcp-options - Key: 'Scope' Value: !Ref GlobalEnvironment EC2VPCDHCPOptionsAssociation: Type: 'AWS::EC2::VPCDHCPOptionsAssociation' Properties: DhcpOptionsId: !Ref EC2DHCPOptions VpcId: !Ref EC2VPC
サブネット
サブネットはVPCをさらに分割したネットワークです。インターネットからアクセスできるパブリックサブネットとアクセスできないプライベートサブネットを作ります。可用性を高めるためマルチAZ構成(パブリックサブネット×2, プライベートサブネット×2, )で作ります。AZ(アベイラビリティゾーン)は各地域にあるリージョン(データセンター群)をさらに分割したものです。
- パブリックサブネット
## Subnet Definition -------------------------------------------------------------------------------- EC2SubnetPublicAZ1: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: !Ref AZ1 CidrBlock: !Ref IPCidrBlockPublicAZ1 MapPublicIpOnLaunch: true VpcId: !Ref EC2VPC Tags: - Key: Name Value: EC2SubnetPublicAZ1 EC2SubnetPublicAZ2: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: !Ref AZ2 CidrBlock: !Ref IPCidrBlockPublicAZ2 MapPublicIpOnLaunch: true VpcId: !Ref EC2VPC Tags: - Key: Name Value: EC2SubnetPublicAZ2
- プライベートサブネット
EC2SubnetPrivateAZ1: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: !Ref AZ1 CidrBlock: !Ref IPCidrBlockPrivateAZ1 MapPublicIpOnLaunch: false VpcId: !Ref EC2VPC Tags: - Key: Name Value: EC2SubnetPrivateAZ1 EC2SubnetPrivateAZ2: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: !Ref AZ2 CidrBlock: !Ref IPCidrBlockPrivateAZ2 MapPublicIpOnLaunch: false VpcId: !Ref EC2VPC Tags: - Key: Name Value: EC2SubnetPrivateAZ2
ルートテーブル
ネットワークにデータを流すために「ルーティング情報」の設定が必要です。ルートテーブルはVPCとサブネットの紐付けを行います。ルートテーブルはサブネットごとに設定することができます。
## RouteTable Definition --------------------------------------------------------------------------------
EC2RouteTablePublicAZ1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref EC2VPC
Tags:
- Key: Name
Value: EC2RouteTablePublicAZ1
EC2RouteTablePublicAZ2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref EC2VPC
Tags:
- Key: Name
Value: EC2RouteTablePublicAZ2
EC2RouteTablePrivateAZ1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref EC2VPC
Tags:
- Key: Name
Value: EC2RouteTablePrivateAZ1
EC2RouteTablePrivateAZ2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref EC2VPC
Tags:
- Key: Name
Value: EC2RouteTablePrivateAZ2
EC2SubnetRouteTableAssociationPublicAZ1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref EC2RouteTablePublicAZ1
SubnetId: !Ref EC2SubnetPublicAZ1
EC2SubnetRouteTableAssociationPublicAZ2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref EC2RouteTablePublicAZ2
SubnetId: !Ref EC2SubnetPublicAZ2
EC2SubnetRouteTableAssociationPrivateAZ1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref EC2RouteTablePrivateAZ1
SubnetId: !Ref EC2SubnetPrivateAZ1
EC2SubnetRouteTableAssociationPrivateAZ2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref EC2RouteTablePrivateAZ2
SubnetId: !Ref EC2SubnetPrivateAZ2
インターネットゲートウェイ
パブリックサブネットをインタネットに接続するためにはインターネットゲートウェイが必要です。インターネットゲートウェイは自分のネットワークにインタネット回線を引き込む役割があります。
## Internet Gateway Definition -------------------------------------------------------------------------------- EC2InternetGateWay: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: EC2InternetGateWay EC2GatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref EC2InternetGateWay VpcId: !Ref EC2VPC EC2InternetGatewayRouteAZ1: Type: "AWS::EC2::Route" DependsOn: - EC2GatewayAttachment Properties: RouteTableId: !Ref EC2RouteTablePublicAZ1 GatewayId: !Ref EC2InternetGateWay DestinationCidrBlock: 0.0.0.0/0 EC2InternetGatewayRouteAZ2: Type: "AWS::EC2::Route" DependsOn: - EC2GatewayAttachment Properties: RouteTableId: !Ref EC2RouteTablePublicAZ2 GatewayId: !Ref EC2InternetGateWay DestinationCidrBlock: 0.0.0.0/0
NATゲートウェイ
DBサーバ(片方向)からの接続だけを許すためにNATゲートウェイを作ります。NATゲートウェイはインターネットにアクセスするために、パブリックサブネットに配置します。それぞれEIPが必要となります。
## Nat Gateway Definition -------------------------------------------------------------------------------- EC2EIPForNatGatewayAZ1: Type: 'AWS::EC2::EIP' Properties: Domain: 'vpc' Tags: - Key: Name Value: EC2EIPForNatGatewayAZ1 EC2NatGatewayAZ1: Type: 'AWS::EC2::NatGateway' Properties: AllocationId: !GetAtt EC2EIPForNatGatewayAZ1.AllocationId SubnetId: !Ref EC2SubnetPublicAZ1 Tags: - Key: Name Value: EC2NatGatewayAZ1 EC2EIPForNatGatewayAZ2: Type: 'AWS::EC2::EIP' Properties: Domain: 'vpc' Tags: - Key: Name Value: EC2EIPForNatGatewayAZ2 EC2NatGatewayAZ2: Type: 'AWS::EC2::NatGateway' Properties: AllocationId: !GetAtt EC2EIPForNatGatewayAZ2.AllocationId SubnetId: !Ref EC2SubnetPublicAZ2 Tags: - Key: Name Value: EC2NatGatewayAZ2 EC2InternetNatGatewayRouteAZ1: Type: 'AWS::EC2::Route' Properties: DestinationCidrBlock: '0.0.0.0/0' NatGatewayId: !Ref EC2NatGatewayAZ1 RouteTableId: !Ref EC2RouteTablePrivateAZ1 EC2InternetNatGatewayRouteAZ2: Type: 'AWS::EC2::Route' Properties: DestinationCidrBlock: '0.0.0.0/0' NatGatewayId: !Ref EC2NatGatewayAZ2 RouteTableId: !Ref EC2RouteTablePrivateAZ2
開発環境に本番環境のサブドメインを適用する
AWSの本番環境(別アカウント)で取得して使っているドメイン(親)のサブドメイン(子)を開発環境のRedashに割り当てます。
Route53
Route53はAWSのドメインネームシステムでドメインの取得やゾーンを作ることができます。Route53を使って開発環境にゾーンを作ります。作られたゾーンはCloud Formation間で共有できるようにExportします。
AWSTemplateFormatVersion: 2010-09-09 Parameters: NativeDomain: Type: 'String' Default: '<~.com.>' Resources: Route53HostedZoneNativeDomain: Type: "AWS::Route53::HostedZone" DeletionPolicy: 'Retain' Properties: Name: !Sub dev.${NativeDomain} Outputs: OutputNativeDomainHostedZoneID: Value: !Ref Route53HostedZoneNativeDomain Export: Name: !Join [':', [!Ref 'AWS::StackName', 'native-domain', 'hosted-zone-id'] ] OutputNativeDomainNameServers: Value: !Join [',', !GetAtt Route53HostedZoneNativeDomain.NameServers]
AWS Certificate Manager(ACM)
AWS Certificate Manager (ACM) はAWS ベースのウェブサイトとアプリケーション用のパブリック SSL/TLS 証明書の複雑な作成と管理を処理します。RedashをSSL対応させるためにはACM証明書が必要です。
ACM DNS検証
次のようにしてACMのDNS認証リクエストを投げます。リクエストを投げると検証中となり、CNAMEレコードが表示されます。
ACM証明書発行をDNS検証で行う - サーバーワークスエンジニアブログ
AWSTemplateFormatVersion: 2010-09-09 Parameters: NativeDomain: Type: 'String' Default: '<Domain Name>' Resources: CertificateManagerCertificateRedash: Type: 'AWS::CertificateManager::Certificate' Properties: DomainName: !Sub redash.dev.${NativeDomain} ValidationMethod: 'DNS' Outputs: OutputCertificateManagerRedashDomainArn: Value: !Ref CertificateManagerCertificateRedash Export: Name: !Join [':', [!Ref 'AWS::StackName', 'redash-domain', 'arn'] ]
CNAMEレコードを追加
CNAMEをCloud Formationに入力して適用します。認証が完了するとAWSコンソールのゾーンにCNAMEレコードが追加され、ACM証明書が発行されます。
【ドメイン】DNSレコード設定の各レコードの意味を教えてください。|ヘルプサポート | ドメイン取るならお名前.com
AWSTemplateFormatVersion: 2010-09-09 Parameters: DNSRecordPairRedashDevDomain: Type: 'CommaDelimitedList' Default: '<Cname Name >,_<Cname Value>' Resources: Route53RecordSetCNAMEACMRedashDevDomain: Type: 'AWS::Route53::RecordSet' Properties: HostedZoneId: !ImportValue hosted-zone:native-domain:hosted-zone-id Name: !Select [0, !Ref DNSRecordPairRedashDevDomain] ResourceRecords: - !Select [1, !Ref DNSRecordPairRedashDevDomain] TTL: '60' Type: 'CNAME'
NSレコードの追加
NSレコードはゾーン情報を管理するネームサーバーのサーバー名を定義するレコードです。NSレコードはドメインの委任に関する情報を管理しています。NSレコードはゾーンカットの親側と子側の双方に設定する必要があります。例えばjpがexample.jpを委任している時、jpとexample.jpゾーンの双方で設定します。今回本番環境で取得したドメイン(親)のサブドメインを開発環境(子)に割り当てたいので、開発環境のNSレコードを本番環境のゾーンにも追加します。NSレコードは開発環境にゾーン作成後コンソールから取得します。NSレコードをCloud Formationに入力して本番環境に適用します。
AWSTemplateFormatVersion: 2010-09-09 Parameters: NativeDomain: Type: 'String' Default: '<Domain Name>.' RedashDevNSRecords: Type: 'CommaDelimitedList' Default: 'ns-.net., ns-.com., ns-.org., ns-.co.uk.' Resources: Route53RecordSetAliasRedashElb: Type: 'AWS::Route53::RecordSet' Properties: HostedZoneId: !ImportValue hosted-zone:native-domain:hosted-zone-id # <Domain Name>. Name: !Sub dev.${NativeDomain} Type: 'NS' TTL: '900' ResourceRecords: - !Select [0, !Ref RedashDevNSRecords] - !Select [1, !Ref RedashDevNSRecords] - !Select [2, !Ref RedashDevNSRecords] - !Select [3, !Ref RedashDevNSRecords]
AWSリソースを使ったRedash
AWSリソースを使ったRedash環境を構築します。秘密情報の管理はSecrets Managerを使います。
CloudFormationテンプレートに秘密情報を渡す方法 - ZOZO Technologies TECH BLOG
AutoScaling
サーバーが死んだら自動復旧(オートヒーリング)させたいので、AWSの オートスケーリング機能を利用します。次の記事の「スケーリングはせずインスタンス数の維持に利用する」に該当する処理を行います。
AWS再入門2018 Amazon EC2 Auto Scaling編 | Developers.IO
## AutoScaling Definition -------------------------------------------------------------------------------- AutoScalingGroupForRedash: Type: 'AWS::AutoScaling::AutoScalingGroup' Properties: VPCZoneIdentifier: - !Ref EC2SubnetPrivateAZ1 - !Ref EC2SubnetPrivateAZ2 LaunchConfigurationName: !Ref LaunchConfigurationForRedash MinSize: '1' MaxSize: '3' TargetGroupARNs: - !Ref ElasticLoadBalancingV2TargetGroupExternalRedash DesiredCapacity: '1' Tags: - Key: Name Value: EC2RedashAutoScalingServerPrivateAZ1AZ2 PropagateAtLaunch: true UpdatePolicy: AutoScalingReplacingUpdate: WillReplace: true EC2RedashInstanceIAMRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' Path: / ManagedPolicyArns: - 'arn:aws:iam::aws:policy/SecretsManagerReadWrite' EC2RedashInstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: Path: "/" Roles: - !Ref EC2RedashInstanceIAMRole LaunchConfigurationForRedash: Type: AWS::AutoScaling::LaunchConfiguration Properties: ImageId: "ami-060741a96307668be" IamInstanceProfile: !Ref EC2RedashInstanceProfile SecurityGroups: - !Ref EC2SecurityGroupAutoScalingServerRedash InstanceType: 'm5.xlarge' KeyName: !Ref SSHKeyName UserData: Fn::Base64: !Sub | #!/bin/bash -xe rm /opt/redash/env curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" sudo rm /var/lib/dpkg/lock* sudo dpkg --configure -a sudo apt update sudo apt install python -y sudo python get-pip.py sudo pip install awscli sudo apt install jq -y RedashUsername=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-user | jq -r .SecretString) RedashPassword=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-password | jq -r .SecretString) { echo PYTHONUNBUFFERED=0 echo REDASH_LOG_LEVEL=INFO echo POSTGRES_PASSWORD=${!RedashPassword} echo REDASH_COOKIE_SECRET=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-cookie-secret | jq -r .SecretString) echo REDASH_SECRET_KEY=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-secret-key | jq -r .SecretString) echo REDASH_REDIS_URL=redis://${ElasticacheClusterForRedash.RedisEndpoint.Address}:${ElasticacheClusterForRedash.RedisEndpoint.Port}/0 echo REDASH_DATABASE_URL=postgresql://${!RedashUsername}:${!RedashPassword}@${RDSDBClusterForRedash.Endpoint.Address}:${RDSDBClusterForRedash.Endpoint.Port}/${!RedashUsername} echo REDASH_FEATURE_EXTENDED_ALERT_OPTIONS=true } >> /opt/redash/env sudo shutdown -r now EC2SecurityGroupAutoScalingServerRedash: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Activity security group VpcId: !Ref EC2VPC SecurityGroupIngress: - SourceSecurityGroupId: !Ref EC2SecurityGroupStepServerRedash IpProtocol: 'tcp' FromPort: 22 ToPort: 22 - SourceSecurityGroupId: !Ref EC2SecurityGroupALBExternalRedash IpProtocol: 'tcp' FromPort: 80 ToPort: 80 Tags: - Key: Name Value: EC2SecurityGroupAutoScalingServerRedash
Application Load Balance(ALB)
ALB(Application Load Balancer)とは、Amazon.comが提供するAWS(Amazon Web Services)と呼ばれるシステムの一部で、Webサービスに発生する負荷を分散するロードバランシングサービスです。
- サブネット
ALBにはパブリックサブネットを紐付けます。
## ALB Definition -------------------------------------------------------------------------------- ElasticLoadBalancingV2LoadBalancerExternalRedash: Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' Properties: Name: !Sub external-${GlobalPrefix}-redash Scheme: 'internet-facing' SecurityGroups: - !Ref EC2SecurityGroupALBExternalRedash Subnets: - !Ref EC2SubnetPublicAZ1 - !Ref EC2SubnetPublicAZ2 Tags: - Key: 'Scope' Value: !Ref GlobalEnvironment
- ターゲットグループ
ターゲットグループは次のようにして定義します。ALBはターゲットグループのインスタンスに対して負荷を分散します。
ElasticLoadBalancingV2TargetGroupExternalRedash: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' Properties: Port: 80 Protocol: 'HTTP' VpcId: !Ref EC2VPC HealthCheckPath: '/login' HealthCheckPort: 'traffic-port' HealthCheckProtocol: 'HTTP' HealthCheckTimeoutSeconds: 2 HealthyThresholdCount: 2 TargetGroupAttributes: [ { "Key" : "stickiness.enabled", "Value" : "true" }, { "Key" : "stickiness.type", "Value" : "lb_cookie" }, { "Key" : "stickiness.lb_cookie.duration_seconds", "Value" : "86400" } ] Matcher: HttpCode: '200-399' UnhealthyThresholdCount: 2 TargetType: 'instance' Tags: - Key: 'Scope' Value: !Ref GlobalEnvironment
HealthCheckPath: '/login'
Redash EC2 instance behind ELB or CloudFront distribution - #3 by hernangarcia - Self Hosted Redash Support - Redash Discourse
【基礎から学ぶ】ELBのスティッキーセッションについてまとめてみた - サーバーワークスエンジニアブログ
- リスナー
リスナーとは設定したプロトコルとポートを使用して接続リクエストをチェックするプロセスです。リスナーは次のように定義します。ACM証明書を使いSSL化させます。開発環境のドメインのゾーンにAレコードを追加します。
AレコードとはIPアドレスの関連づけを定義するレコードです。Aレコードのドメインから名前解決を行うことができます。もし「www.example.com」でWEBサイトからアクセスしたいならAレコードは「www.example.com」とします。
【ドメイン】DNSレコード設定の各レコードの意味を教えてください。|ヘルプサポート | ドメイン取るならお名前.com
ElasticLoadBalancingV2ListenerExternalRedashHTTPS: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: Certificates: - CertificateArn: !ImportValue acm:redash-domain:arn DefaultActions: - TargetGroupArn: !Ref ElasticLoadBalancingV2TargetGroupExternalRedash Type: 'forward' LoadBalancerArn: !Ref ElasticLoadBalancingV2LoadBalancerExternalRedash Port: 443 Protocol: 'HTTPS' Route53RecordSetAliasRedashElb: Type: 'AWS::Route53::RecordSet' Properties: AliasTarget: DNSName: !GetAtt ElasticLoadBalancingV2LoadBalancerExternalRedash.DNSName HostedZoneId: !GetAtt ElasticLoadBalancingV2LoadBalancerExternalRedash.CanonicalHostedZoneID HostedZoneId: !ImportValue hosted-zone:native-domain:hosted-zone-id Name: !Sub redash.dev.${NativeDomain}. Type: 'A' EC2SecurityGroupALBExternalRedash: Type: 'AWS::EC2::SecurityGroup' Properties: GroupName: !Sub ${GlobalPrefix}-${GlobalEnvironment}-redash-alb-security-group GroupDescription: 'alb security group for redash' VpcId: !Ref EC2VPC SecurityGroupIngress: - CidrIp: !Ref IPCidrBlockAllow1 FromPort: 443 ToPort: 443 IpProtocol: 'tcp' - CidrIp: !Ref IPCidrBlockAllow2 FromPort: 443 ToPort: 443 IpProtocol: 'tcp'
Aurora PostgreSQL
AWSのAuroraを使います。セキュリティグループの範囲としては、踏み台サーバとALBで作られたインスタンスのみからとし、プライベートサブネットに配置します。
## Aurora PostgreSQL Definition -------------------------------------------------------------------------------- RDSDBClusterParameterGroupForRedash: Type: 'AWS::RDS::DBClusterParameterGroup' Properties: Description: 'rds cluster parameter group for digdag' Family: 'aurora-postgresql10' Parameters: timezone: 'Asia/Tokyo' Tags: - Key: 'Name' Value: !Sub ${GlobalPrefix}-${GlobalEnvironment}-rds-cluster-parameter-group-digdag - Key: 'Scope' Value: !Ref GlobalEnvironment RDSDBParameterGroupForRedash: Type: 'AWS::RDS::DBParameterGroup' Properties: Description: 'rds parameter group for redash' Family: 'aurora-postgresql10' Tags: - Key: 'Name' Value: !Sub ${GlobalPrefix}-${GlobalEnvironment}-rds-parameter-group-redash - Key: 'Scope' Value: !Ref GlobalEnvironment RDSDBSubnetGroupNameForRedash: Type: "AWS::RDS::DBSubnetGroup" Properties: DBSubnetGroupDescription: 'rds subnet group for redash' DBSubnetGroupName: !Sub ${GlobalPrefix}-${GlobalEnvironment}-redash-rds-subnet-group SubnetIds: - !Ref EC2SubnetPrivateAZ1 - !Ref EC2SubnetPrivateAZ2 RDSDBClusterForRedash: Type: 'AWS::RDS::DBCluster' DeletionPolicy: 'Snapshot' Properties: AvailabilityZones: - !Ref AZ1 - !Ref AZ2 BackupRetentionPeriod: 35 DBClusterParameterGroupName: !Ref RDSDBClusterParameterGroupForRedash DBSubnetGroupName: !Ref RDSDBSubnetGroupNameForRedash Engine: 'aurora-postgresql' EngineVersion: '10.7' MasterUsername: '{{resolve:secretsmanager:redash-master-user:SecretString}}' MasterUserPassword: '{{resolve:secretsmanager:redash-master-password:SecretString}}' Port: 5432 PreferredBackupWindow: '21:00-21:30' # JST 05:00-05:30 PreferredMaintenanceWindow: 'Thu:20:00-Thu:20:30' # JST FRI 04:00-04:30 StorageEncrypted: true Tags: - Key: 'Scope' Value: !Ref GlobalEnvironment VpcSecurityGroupIds: - !Ref EC2SecurityGroupRDSRedash IAMRoleRDSEnhancedMonitoring: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: 'Allow' Principal: Service: 'monitoring.rds.amazonaws.com' Action: 'sts:AssumeRole' ManagedPolicyArns: - 'arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole' RDSForRedash: Type: 'AWS::RDS::DBInstance' DeletionPolicy: 'Delete' Properties: AllowMajorVersionUpgrade: false AutoMinorVersionUpgrade: false CopyTagsToSnapshot: true DBClusterIdentifier: !Ref RDSDBClusterForRedash DBInstanceClass: 'db.t3.medium' Engine: 'aurora-postgresql' MonitoringInterval: 15 MonitoringRoleArn: !GetAtt IAMRoleRDSEnhancedMonitoring.Arn PreferredMaintenanceWindow: 'Thu:20:00-Thu:20:30' # JST FRI 04:00-04:30 PubliclyAccessible: false Tags: - Key: 'Scope' Value: !Ref GlobalEnvironment DBParameterGroupName: !Ref RDSDBParameterGroupForRedash EC2SecurityGroupRDSRedash: Type: 'AWS::EC2::SecurityGroup' Properties: GroupName: !Sub ${GlobalPrefix}-${GlobalEnvironment}-redash-rds-security-group GroupDescription: 'rds security group for redash' VpcId: !Ref EC2VPC SecurityGroupIngress: - SourceSecurityGroupId: !Ref EC2SecurityGroupStepServerRedash FromPort: 5432 ToPort: 5432 IpProtocol: 'tcp' - SourceSecurityGroupId: !Ref EC2SecurityGroupAutoScalingServerRedash FromPort: 5432 ToPort: 5432 IpProtocol: 'tcp'
ElastiCache Redis
AWSの ElastiCacheを使います。セキュリティグループの範囲としては、踏み台サーバとALBで作られたインスタンスのみからとし、プライベートサブネットに配置します。
## ElastiCache Redis Definition -------------------------------------------------------------------------------- ElastiCacheSubnetGroupForRedash: Type: 'AWS::ElastiCache::SubnetGroup' Properties: CacheSubnetGroupName: !Sub ${GlobalPrefix}-${GlobalEnvironment}-redash-redis-subnet-group Description: 'redis subnet group for redash' SubnetIds: - !Ref EC2SubnetPrivateAZ1 - !Ref EC2SubnetPrivateAZ2 ElastiCacheParameterGroupForRedash: Type: 'AWS::ElastiCache::ParameterGroup' Properties: CacheParameterGroupFamily: 'redis4.0' Description: 'Redash Redis' ElasticacheClusterForRedash: Type: 'AWS::ElastiCache::CacheCluster' Properties: ClusterName: 'redash-redis' Engine: 'redis' EngineVersion: '4.0.10' AutoMinorVersionUpgrade: true CacheNodeType: 'cache.t2.micro' NumCacheNodes: 1 CacheParameterGroupName: !Ref ElastiCacheParameterGroupForRedash PreferredAvailabilityZone: !Ref AZ1 CacheSubnetGroupName: !Ref ElastiCacheSubnetGroupForRedash VpcSecurityGroupIds: - !GetAtt EC2SecurityGroupRedashRedis.GroupId Port: 6379 Tags: - Key: 'Name' Value: !Sub ${GlobalPrefix}-${GlobalEnvironment}-redash - Key: 'Scope' Value: !Ref GlobalEnvironment EC2SecurityGroupRedashRedis: Type: 'AWS::EC2::SecurityGroup' Properties: GroupName: !Sub ${GlobalPrefix}-${GlobalEnvironment}-redash-redis-security-group GroupDescription: 'redis security group for redash' VpcId: !Ref EC2VPC SecurityGroupIngress: - SourceSecurityGroupId: !Ref EC2SecurityGroupStepServerRedash FromPort: 6379 ToPort: 6379 IpProtocol: 'tcp' - SourceSecurityGroupId: !Ref EC2SecurityGroupAutoScalingServerRedash FromPort: 6379 ToPort: 6379 IpProtocol: 'tcp'
踏み台サーバ
踏み台サーバを作ります。UserDataではRedashユーザをつくります。
## Step Server Definition -------------------------------------------------------------------------------- EC2RedashStepServerPublicAZ1: Type: AWS::EC2::Instance DependsOn: - EC2InternetGatewayRouteAZ1 - EC2SubnetRouteTableAssociationPublicAZ1 - RDSForRedash Properties: InstanceType: t3.small SubnetId: !Ref EC2SubnetPublicAZ1 ImageId: ami-07f4cb4629342979c IamInstanceProfile: !Ref EC2RedashInstanceProfile KeyName: !Ref SSHKeyName SecurityGroupIds: - !Ref EC2SecurityGroupStepServerRedash Tags: - Key: Name Value: EC2RedashStepServerPublicAZ1 UserData: Fn::Base64: !Sub | #!/bin/bash -xe sudo apt update sudo apt install postgresql-client-common sudo apt install postgresql-client -y sudo apt install python -y sudo curl "https://bootstrap.pypa.io/get-pip.py" -o "get-pip.py" sudo python get-pip.py sudo pip install awscli sudo apt install jq -y RedashMasterUsername=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-master-user | jq -r .SecretString) RedashMasterPassword=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-master-password | jq -r .SecretString) RedashUsername=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-user | jq -r .SecretString) RedashPassword=$(aws secretsmanager get-secret-value --region ${AWS::Region} --secret-id redash-password | jq -r .SecretString) echo ${RDSDBClusterForRedash.Endpoint.Address}:${RDSDBClusterForRedash.Endpoint.Port}:*:${!RedashMasterUsername}:${!RedashMasterPassword} >> ~/.pgpass chmod 600 ~/.pgpass psql -U ${!RedashMasterUsername} -h ${RDSDBClusterForRedash.Endpoint.Address} -d postgres -c "CREATE ROLE ${!RedashUsername} WITH LOGIN PASSWORD '${!RedashPassword}'" psql -U ${!RedashMasterUsername} -h ${RDSDBClusterForRedash.Endpoint.Address} -d postgres -c "GRANT ${!RedashUsername} TO ${!RedashMasterUsername}" psql -U ${!RedashMasterUsername} -h ${RDSDBClusterForRedash.Endpoint.Address} -d postgres -c "CREATE DATABASE ${!RedashUsername} OWNER ${!RedashUsername}" EC2EIPForStepServer: Type: 'AWS::EC2::EIP' Properties: Domain: 'vpc' InstanceId: !Ref EC2RedashStepServerPublicAZ1 EC2SecurityGroupStepServerRedash: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Activity security group VpcId: !Ref EC2VPC SecurityGroupIngress: - CidrIp: !Ref IPCidrBlockAllow1 IpProtocol: 'tcp' FromPort: 22 ToPort: 22 - CidrIp: !Ref IPCidrBlockAllow2 FromPort: 22 ToPort: 22 IpProtocol: 'tcp' Tags: - Key: Name Value: EC2SecurityGroupStepServerRedash
なかなか長くなってしまいました。以上となります。