社内PCでホスティングされていたRedashをFargateに移行してみた

f:id:vasilyjp:20181025102627p:plain

こんにちは、最近気になっている哺乳類はオリンギートな、開発部の塩崎です。

私の所属しているMarketingAutomationチームではRealtimeMarketingシステムの開発運用を行っております。 このシステムはZOZOTOWNのユーザーに対してメールやLINEなどのコミュニケーションチャンネルを使い情報の配信を行うものです。 メルマガの配信数や開封数などの数値は自動的に集計され、BIツールであるRedashによってモニタリングされています。 このRedashは社内PCによってホスティングされていましたが、運用面で辛い部分が多々あったためパブリッククラウドに移行しました。 移行先のクラウドはawsを選択し、RedashをホスティングするためのサービスはECS/Fargateを選択しました。 この記事ではawsに構築した環境や、移行作業などを紹介します。

移行前のRedash

移行前のRedashがどのような環境でホスティングされていたのかを説明します。

f:id:vasilyjp:20181025103011p:plain

Windowsの物理マシンがあり、そのなかにコンテナ管理ツールのPortainerが入っています。 RedashのwebサーバーやRedashを動作させるためのミドルウェアなどはすべてDockerコンテナの中で動いており、その管理をPortainerに任せています。

Redashのデータソースは主に2種類あり、それはBigQueryとPuredataです。 BigQueryはGoogle製の完全にマネージドなDWHです。 BigQueryとの接続にはRedashがデフォルトで用意しているインテグレーション機能を使用しています。

一方、PuredataはIBM製のDWHアプライアンスです。 こちらはRedashとのインテグレーションが用意されていません。 そのため、我々のチームでPuredataと接続するためのゲートウェイを作成し、Redashとの接続に使用しています。 Puredataに接続するためのインタフェースとしてIBMからJDBCドライバーが提供されています。 しかし、RedashはPythonで実装されており、JDBCドライバーを直接読み込むことができません。 何らかの仕組みでPythonとJavaの変換が必要です。 ですので、Py4Jを使いPythonからJavaのメソッドを呼ぶことでRedashからPuredataに接続しています。

参考: RedashとBigQueryのインテグレーション機能

移行前のRedashの問題点

上記で説明したような構成のRedashにはいくつかの問題がありました。 主には物理マシンの管理が面倒という内容です。

物理マシンが落ちた時の復旧が手動

何らかの原因で物理マシンが落ちたときには、物理マシンが置いてある場所まで行き、手動で起動をするという運用が必要になってしまっていました。

ビルの法定停電の時にマシンの電源を落とす必要がある

ビルの法定停電の時には停電前に電源を落とし、停電から復旧した後に正常に動作するかどうかを確認する運用が必要になっていました。

現行のRedashの紹介

次にawsに構築したRedashの紹介をします。

f:id:vasilyjp:20181025103131p:plain

Redashの動作に必要なミドルウェアはPostgerSQLとRedisです。 これらはawsのフルマネージドサービスであるRelational Database Service(RDS)とElastiCacheでホスティングすることによって、管理コストを下げました。 RedashとPuredataGatewayのDockerイメージはElastic Container Registry(ECR)で管理されており、ECSはここからdocker pullを行います。 DockerhubでホスティングされているRedash公式のDockerイメージを使わない理由は、Puredataに対応させるために独自のパッチを当てているためです。

次にコンテナ間通信について説明します。 RedashとPuredataGatewayの間はPy4JによるTCP通信をさせる必要があります。 しかしPuredataGatewayコンテナのIPアドレスは起動するごとに変化するので、Redashのコンテナの中にIPアドレスをハードコーディングすることはできません。 Redash workerコンテナがPuredataGatewayコンテナのIPアドレスを取得する仕組みが必要です。 NLBを使うことはその1つの解決策ですが、今回は最近東京リージョンでも使えるようになったRoute53 Auto Namingで実現しました。 これはコンテナの起動・終了のタイミングでDNSのAレコードの値が自動的に増減する仕組みです。 Redash側からPuredataGatewayに接続するときにはこのAレコードを参照すれば、現在アクティブなPuredataGatewayのIPアドレスを得ることができます。 NLBではなくAutoNamingを利用した主な理由はロードバランサーを入れないほうが全体の構成がシンプルになるためです。

これらのインフラ構成はすべてCloudFormationで管理されています。 これ以降でインフラ構成を説明するときにはCloudFormationのテンプレートファイルを使って説明します。

また、これらのサービスの監視はDatadogで行っています。 RDSとElastiCacheはaws integrationでメトリクスの収集を行っています。 そして、コンテナのメトリクスの収集はDatadog Agentのサイドカーコンテナを配置することで行っています。 FargateとDatadogの連携に関する設定はこの下で詳しく説明します。

技術選定

今回のRedashホスティングにFargateを選定した理由を説明します。

まず、Redashを動作させるためのDockerイメージが公式から提供されているので、それを使うことを考えました。 その場合、何かしらの仕組みでコンテナのオーケストレーションを行う必要があります。 マーケティングオートメーションチームは既にawsでのサービスの開発・運用実績がありましたので、パブリッククラウドとしてawsを選択しました。 awsでDockerのオーケストレーションをしてくれるマネージドサービスはECSとEKSがあります。 EKSはDocker界隈で最近ますます存在感を増しているkubernatesのマネージドサービスです。 ですが、コントロールプレーンのみがマネージドであり、ワーカーノードの管理は自分たちで行う必要があります。 今回は可能な限りインフラの管理をawsに任せたいという思いがあったため、ECS/Fargateを選択しました。

構成を詳細に説明

ここからは今回構築したRedash環境の詳細をCloudFormationのテンプレートファイルを交えながら説明していきます。

RDS(PostgreSQL) + ElastiCache(Redis)

まずは、Redashが必要としているミドルウェアであるPostgreSQLとRedisを構築します。 どちらともawsによるマネージドサービスが提供されているので、RDSとElastiCacheを使うことにしました。 今回は高いSLOが求められないので、値段を優先して一番安いインスタンスを使ったシングルAZ構成で構築しました。

たとえシングルAZ構成でも、 OSのセキュリティパッチやデータベースのバックアップはaws側がやってくれます。 そのため、Redash公式のdocker-composeを使った構成よりも管理コストが低いです。 また、可用性を高めるためこれをマルチAZ構成にすることも比較的容易です。

Resources:
  RDSDBParameterGroupForRedash:
    Type: 'AWS::RDS::DBParameterGroup'
    Properties:
      Description: 'rds parameter group for redash'
      Family: 'postgres9.6'
  RDSDBSubnetGroupNameForRedash:
    Type: "AWS::RDS::DBSubnetGroup"
    Properties:
      DBSubnetGroupDescription: 'rds subnet group for redash'
      DBSubnetGroupName: redash-rds-subnet-group
      SubnetIds:
        - !Ref EC2SubnetApplicationPrivateAZ1 # 予め定義しておく
        - !Ref EC2SubnetApplicationPrivateAZ2 # 予め定義しておく
  EC2SecurityGroupRDSRedash:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: redash-rds-security-group
      GroupDescription: 'rds security group for redash'
      VpcId: !Ref EC2VPC
      SecurityGroupIngress:
        - SourceSecurityGroupId: !Ref EC2SecurityGroupECSRedash # ECSタスクに紐づけているセキュリティグループ
          FromPort: 5432
          ToPort: 5432
          IpProtocol: 'tcp'
  RDSForRedash:
    Type: 'AWS::RDS::DBInstance'
    Properties:
      Engine: 'postgres'
      EngineVersion: '9.6'
      AutoMinorVersionUpgrade: true
      DBInstanceClass: 'db.t2.micro'
      DBParameterGroupName: !Ref RDSDBParameterGroupForRedash
      BackupRetentionPeriod: 7   # バックアップの保持期間
      StorageType: 'gp2'
      AllocatedStorage: 20       # ストレージのサイズ(GB)
      MultiAZ: false
      AvailabilityZone: !Ref AZ1 # 予め定義しておく
      PubliclyAccessible: false
      DBSubnetGroupName: !Ref RDSDBSubnetGroupNameForRedash
      VPCSecurityGroups:
        - !Ref EC2SecurityGroupRDSRedash
      MasterUsername: !Ref RDSForRedashMasterUsername
      MasterUserPassword: !Ref RDSForRedashMasterUserPassword
      PreferredBackupWindow: '15:00-17:00' # UTC
      PreferredMaintenanceWindow: 'Sat:17:00-Sat:19:00' # UTC

  ## Redis
  ElastiCacheSubnetGroupForRedash:
    Type: 'AWS::ElastiCache::SubnetGroup'
    Properties:
      CacheSubnetGroupName: redash-redis-subnet-group
      Description: 'redis subnet group for redash'
      SubnetIds:
        - !Ref EC2SubnetApplicationPrivateAZ1
        - !Ref EC2SubnetApplicationPrivateAZ2
  EC2SecurityGroupRedashRedis:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: redash-redis-security-group
      GroupDescription: 'redis security group for redash'
      VpcId: !Ref EC2VPC
      SecurityGroupIngress:
        - SourceSecurityGroupId: !Ref EC2SecurityGroupECSRedash
          FromPort: 6379
          ToPort: 6379
          IpProtocol: 'tcp'
  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

ECS/Fargate

次にECS /FargateでRedashをホスティングする部分を説明します。 今回は以下のようにサービスを分割しました。

  • Redash Server
  • Redash Worker
  • Puredata Gateway

PuredataGatewayはPy4JでPythonとJavaの間を取り持つコンテナなので、分離されているのは自然です。 一方でRedash系の2つが分離されているのは違和感ある人がいるかもしてないので、理由を説明します。 Redash ServerはRedashのweb UIをホスティングしているwebサーバーで、このサービスが直接クエリの実行することはありません。 クエリの実行を担うのはRedash Workerの方です。 これらの間は非同期ジョブライブラリのCeleryによってクエリのやり取りが行われています。 なお、Celeryによって作られるジョブキューの実体はRedisのリストです。 そしてクエリの実行結果はPostgreSQLに書き込まれ、それがRedash Serverによって読み出されグラフィカルに表示されます。

クエリの個数が多くなった時にRedash Workerのスケーリングが必要です。 ですので、Redash ServerとRedash Workerの分離を行いました。

今回はECSサービスを3つ作りましたが、まずはRedash ServerとRedash Workerのサービス定義を説明します。 特に注意が必要なポイントは以下の2点で、それぞれ後で説明します。

  • Redashの待受ポートが5100なこと
  • puredata-gateway.redash.internalというドメインでRedash WorkerがPuredata Gatewayに接続すること
Resources:
  ## ECS
  ECSClusterForRedash:
    Type: 'AWS::ECS::Cluster'
    Properties:
      ClusterName: redash
  IAMRoleForRedashTaskExecution:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Effect: 'Allow'
          Principal:
            Service: 'ecs-tasks.amazonaws.com'
          Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'
  IAMServiceLinkedRoleForECSRedashService:
    Type: 'AWS::IAM::ServiceLinkedRole'
    Properties:
      AWSServiceName: 'ecs.amazonaws.com'
      Description: 'Role to enable Amazon ECS to manage your cluster.'
  ECSTaskDefinitionApplication:
    Type: 'AWS::ECS::TaskDefinition'
    Properties:
      Family: redash-server
      RequiresCompatibilities:
        - 'FARGATE'
      Cpu: 1024
      Memory: 2048
      NetworkMode: 'awsvpc'
      ExecutionRoleArn: !GetAtt IAMRoleForRedashTaskExecution.Arn
      ContainerDefinitions:
        - Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepositoryForRedash}:last
          Name: 'redash-server'
          Command:
            - 'server'
          Environment:
            - Name: 'PYTHONUNBUFFERED'
              Value: '0'
            - Name: 'REDASH_LOG_LEVEL'
              Value: 'INFO'
            - Name: 'REDASH_REDIS_URL'
              Value: !Sub redis://${ElasticacheClusterForRedash.RedisEndpoint.Address}:${ElasticacheClusterForRedash.RedisEndpoint.Port}/0
            - Name: 'REDASH_DATABASE_URL'
              Value: !Sub postgresql://redash:${RDSRedashUserPassword}@${RDSForRedash.Endpoint.Address}/redash # Redash用のRDSユーザーを予め作っておく
            - Name: 'REDASH_HOST'
              Value: ''
            - Name: 'REDASH_WEB_PORT' # Redashが待ち受けているポート番号 デフォルトの5000ではない理由は後述
              Value: '5100'
            - Name: 'REDASH_ALLOW_SCRIPTS_IN_USER_INPUT'
              Value: 'true'
            - Name: 'REDASH_DATE_FORMAT'
              Value: 'YY/MM/DD'
            - Name: 'REDASH_ADDITIONAL_QUERY_RUNNERS' # Redashに新しいquery runnerを登録するため
              Value: 'redash.query_runner.puredata'
            - Name: 'REDASH_JAVA_GATEWAY_HOST'        # このドメインはRoute53のAutoNamingで提供される
              Value: puredata-gateway.redash.internal
          Cpu: 1024
          Memory: 2048
          PortMappings:
            - ContainerPort: 5100
              HostPort: 5100
              Protocol: 'tcp'
          LogConfiguration:
            LogDriver: 'awslogs'
            Options:
              awslogs-group: !Ref LogsLogGroupForRedash # 予め作っておく
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: 'server'
  ECSTaskDefinitionWorker:
    Type: 'AWS::ECS::TaskDefinition'
    Properties:
      Family: redash-worker
      RequiresCompatibilities:
        - 'FARGATE'
      Cpu: 1024
      Memory: 2048
      NetworkMode: 'awsvpc'
      ExecutionRoleArn: !GetAtt IAMRoleForRedashTaskExecution.Arn
      ContainerDefinitions:
        - Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepositoryForRedash}:${RedashGithubSHA1}
          Name: 'redash-worker'
          Command:
            - 'scheduler'
          Environment:
            - Name: 'PYTHONUNBUFFERED'
              Value: '0'
            - Name: 'REDASH_LOG_LEVEL'
              Value: 'DEBUG'
            - Name: 'REDASH_REDIS_URL'
              Value: !Sub redis://${ElasticacheClusterForRedash.RedisEndpoint.Address}:${ElasticacheClusterForRedash.RedisEndpoint.Port}/0
            - Name: 'REDASH_DATABASE_URL'
              Value: !Sub postgresql://redash:${RDSRedashUserPassword}@${RDSForRedash.Endpoint.Address}/redash
            - Name: 'QUEUES'
              Value: 'queries,scheduled_queries,celery'
            - Name: 'WORKERS_COUNT'
              Value: '10'
            - Name: 'REDASH_ADDITIONAL_QUERY_RUNNERS'
              Value: 'redash.query_runner.puredata'
            - Name: 'REDASH_JAVA_GATEWAY_HOST'
              Value: !Sub puredata-gateway.redash.internal
          Cpu: 1024
          Memory: 2048
          LogConfiguration:
            LogDriver: 'awslogs'
            Options:
              awslogs-group: !Ref LogsLogGroupForRedash
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: 'worker'
  EC2SecurityGroupECSRedash:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: redash-ecs-security-group
      GroupDescription: 'ecs security group for redash'
      VpcId: !Ref EC2VPC
      SecurityGroupIngress:
        - SourceSecurityGroupId: !Ref EC2SecurityGroupALBExternalRedash
          FromPort: 5100
          ToPort: 5100
          IpProtocol: 'tcp'
  ECSServiceRedash:
    Type: 'AWS::ECS::Service'
    DependsOn:
      - IAMServiceLinkedRoleForECSRedashService
      - ECSServicePuredataGateway
    Properties:
      Cluster: !Ref ECSClusterForRedash
      DesiredCount: 1
      LaunchType: 'FARGATE'
      LoadBalancers:
        - ContainerName: 'redash-server'
          ContainerPort: 5100
          TargetGroupArn: !Ref ElasticLoadBalancingV2TargetGroupExternalRedash
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: 'DISABLED'
          SecurityGroups:
            - !Ref EC2SecurityGroupECSRedash
          Subnets:
            - !Ref EC2SubnetApplicationPrivateAZ1
            - !Ref EC2SubnetApplicationPrivateAZ2
      PlatformVersion: '1.2.0'
      TaskDefinition: !Ref ECSTaskDefinitionApplication
  ECSServiceRedashWorker:
    Type: 'AWS::ECS::Service'
    DependsOn:
      - IAMServiceLinkedRoleForECSRedashService
      - ECSServicePuredataGateway
    Properties:
      Cluster: !Ref ECSClusterForRedash
      DesiredCount: 3
      LaunchType: 'FARGATE'
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: 'DISABLED'
          SecurityGroups:
            - !Ref EC2SecurityGroupECSRedash
          Subnets:
            - !Ref EC2SubnetApplicationPrivateAZ1
            - !Ref EC2SubnetApplicationPrivateAZ2
      PlatformVersion: '1.2.0'
      TaskDefinition: !Ref ECSTaskDefinitionWorker

次にPuredata Gatewayのサービス定義を示します。 基本的にはRedashのサービス定義と変わりませんが、ServiceDiscoveryに関する設定が追加されているのが特徴です。 前述したRedashサービスから接続をしていたpuredata-gatewat.redash.internalのサービスディスカバリはここで行われます。 まず AWS::ServiceDiscovery::PrivateDnsNamespace リソースによって、 puredata-gateway.redash.internal というHostedZoneがRoute53に作られます。 そして AWS::ServiceDiscovery::Service リソースによって、そのHostedZoneの中にマルチバリューAレコードが作られます。 最後に AWS::ECS::Service リソースのServiceRegistriesプロパティを指定します。 これによって、タスクの起動終了のタイミングでAレコードの値が自動的に書き換わります。

https://docs.aws.amazon.com/Route53/latest/APIReference/overview-service-discovery.html

Resources:
  ServiceDiscoveryPrivateDnsNamespaceForPuredataGateway:
    Type: "AWS::ServiceDiscovery::PrivateDnsNamespace"
    Properties:
      Vpc: !Ref EC2VPC
      Name: puredata-gateway.redash.internal
  ServiceDiscoveryServiceForPuredataGateway:
    Type: "AWS::ServiceDiscovery::Service"
    Properties:
      Name: puredata-gateway
      DnsConfig:
        NamespaceId: !Ref ServiceDiscoveryPrivateDnsNamespaceForPuredataGateway
        DnsRecords:
          - Type: 'A'
            TTL: 60
      HealthCheckCustomConfig:
        FailureThreshold: 2
  ECSTaskDefinitionPuredataGateway:
    Type: 'AWS::ECS::TaskDefinition'
    Properties:
      Family: puredata-gateway
      RequiresCompatibilities:
        - 'FARGATE'
      Cpu: 1024
      Memory: 2048
      NetworkMode: 'awsvpc'
      ExecutionRoleArn: !GetAtt IAMRoleForRedashTaskExecution.Arn
      ContainerDefinitions:
        - Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepositoryForPuredataGateway}
          Name: 'puredata-gateway'
          Command: ['command', 'to', 'start', 'puregata gateway']
          Cpu: 1024
          Memory: 2048
          PortMappings:
            - ContainerPort: 25333
              HostPort: 25333
              Protocol: 'tcp'
          LogConfiguration:
            LogDriver: 'awslogs'
            Options:
              awslogs-group: !Ref LogsLogGroupForRedash
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: 'puredata-gateway'
  EC2SecurityGroupECSPuredataGateway:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupName: puredata-gateway-ecs-security-group
      GroupDescription: 'ecs security group for puredata gateway'
      VpcId: !Ref EC2VPC
      SecurityGroupIngress:
        - CidrIp: !Ref ApplicationPrivateCidrBlockAZ1
          FromPort: 25333
          ToPort: 25333
          IpProtocol: 'tcp'
        - CidrIp: !Ref ApplicationPrivateCidrBlockAZ2
          FromPort: 25333
          ToPort: 25333
          IpProtocol: 'tcp'
  ECSServicePuredataGateway:
    Type: 'AWS::ECS::Service'
    DependsOn: IAMServiceLinkedRoleForECSRedashService
    Properties:
      Cluster: !Ref ECSClusterForRedash
      DesiredCount: 1
      LaunchType: 'FARGATE'
      ServiceRegistries:
        - RegistryArn: !GetAtt ServiceDiscoveryServiceForPuredataGateway.Arn
      NetworkConfiguration:
        AwsvpcConfiguration:
          AssignPublicIp: 'DISABLED'
          SecurityGroups:
            - !Ref EC2SecurityGroupECSPuredataGateway
          Subnets:
            - !Ref EC2SubnetApplicationPrivateAZ1
            - !Ref EC2SubnetApplicationPrivateAZ2
      PlatformVersion: '1.2.0'
      TaskDefinition: !Ref ECSTaskDefinitionPuredataGateway

Redashをawsに構築するにあたって、これ以外にもALB、ACM、Route53なども使用しましたが、記事が長くなってしまうのでここでは割愛します。

Datadog

これらのコンテナのメトリクスの監視にはDatadogを使用しています。 Datadogでメトリクスを収集するためには、Datadogのコンテナをサイドカーコンテナとして配置します。 例えば、Redash Serverのコンテナのメトリクスを収集したい場合は、以下のようにコンテナを配置します。 環境変数ECS_FARGATEを true に指定するだけで、そのタスクに含まれるすべてのコンテナのメトリクスを収集してくれます。

Resources:
  ECSTaskDefinitionApplication:
    Type: 'AWS::ECS::TaskDefinition'
    Properties:
      ContainerDefinitions:
        - Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${ECRRepositoryForRedash}:last
          Name: 'redash-server'
        - Image: datadog/agent:latest
          Name: 'datadog'
          Cpu: 256
          Memory: 512
          Environment:
            - Name: 'DD_API_KEY'
              Value: !Ref DatadogAPIKey
            - Name: 'ECS_FARGATE'
              Value: 'true'
          LogConfiguration:
            LogDriver: 'awslogs'
            Options:
              awslogs-group: !Ref LogsLogGroupForRedash
              awslogs-region: !Ref AWS::Region
              awslogs-stream-prefix: 'redash-datadog'

https://www.datadoghq.com/blog/monitor-aws-fargate/

Fargateの利点欠点

FargateでRedashを構築する最中に感じた、Fargateの利点欠点などをまとめます。

利点

EC2の管理から解放される

まず真っ先に思い浮かぶFargateの利点はEC2を管理する必要がないということです。 ECS on EC2の構築をするためにはEC2インスタンスを立ち上げ、そこにECSエージェントをインストールする必要があります。 Fargateを使うことによって、そのような一手間をかけること無く、いきなりコンテナをデプロイできるので便利です。

コンテナ数のスケーリングをするときにEC2のスケーリングをしなくても良い

前述したことと少し関連しますが、EC2の管理から解放されたことでスケーリングの時に考慮する必要のある要素が減りました。 スケーリングをするときにはサービスのタスク数だけを変化させればよく、従来のECS on EC2で考える必要のあったEC2インスタンスのスケーリングは不要です。

AutoNamingによるサービスディスカバリは便利

システムをマイクロサービス化するときにサービスディスカバリの仕組みはほぼ必須です。 Route53のAutoNamingを使うとDNSレベルでのサービスディスカバリができます。 なお、これはFargateの利点というよりもECSの利点です。

欠点

コンテナのデプロイが遅い

Fargateを使った時にはコンテナのデプロイには数分かかってしまいます。 そのため、高速なスケールアウトを期待してFargateを使うとがっかりすることがあるかと思います。

同一タスク中の複数のプロセスが同じポートで待受できない

Fargateでは同一タスクに属するコンテナ間の通信をさせるためにはlocalhostを使います。 そのため、同一タスクに属する複数のプロセスが同じポート番号で待受しようとすると、後から待受した方が失敗します。 実際にRedashとDatadog Agentの両方が5000番ポートでlistenをしていたため、Redashのサーバーが立ち上がらないという現象が発生しました。 そのためRedashにパッチを当て、5100番ポートで待ち受けるように修正しました。 CloudFormationのテンプレートで5100番ポートを使ってRedashの待受をしている理由はこれです(伏線回収)

ログドライバーがawslogsしか選択できない

ECS on EC2の場合はjournaldやfluentdにログを流すことができますが、ECS/Fargateではawslogsドライバーのみがサポートされます。 awslogsドライバーが収集したログはCloudWatchLogsに送られます。 そのため、すでに別のログ収集基盤がある場合には、CloudWatchLogsからそちらにログを流す必要があります。

https://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_LogConfiguration.html

previllegeモードでDockerを動かすことができない

これは些細な欠点ですが、privilegeモードでDockerを動かすことができないので、例えばDocker in Dockerのような構成にすることはできません。

まとめ

社内PCでホスティングされていたRedashをawsに移すことによって、Redashの動作に必要なコンポーネントをすべてフルマネージドにできました。 さらにこの設定はすべてCloudFormationで管理されています。 そのため、他のチームがawsにRedashを構築したくなったときにはテンプレートファイルを渡すことで同等の構成を簡単に作ることが出来るようになりました。

弊社ではZOZOTOWNの売上を支えているMarketingAutomationに興味がある方、クラウドを活用して既存の運用を改善することに興味がある方を募集しています。 興味のある方はぜひ以下のリンクからご応募ください。

www.wantedly.com

カテゴリー