Hosted Redash와 작별하여 ECS(Fargate)에서 Redash(v10) 구축

개시하다


이 글은 FOLIO Advent Calendar 2021 16일째 되는 글이다.
올해 HostedRedash 서비스 종료가 방송되었다.Hosted Redash를 영입하고 싶은 분들은 대책 옮기느라 바쁘시죠?
FOLIO 역시 호스테드 레즈를 활용해 9월 AWS ECS(Fargate)의 자비 운용으로 변경했다.
호스티드 레지스의 서비스가 끝난 뒤 보낸 편지가 커뮤니티에 제대로 작용하지 못해 마음이 아팠지만, 하나의 사례로 한 일의 기록을 전달했으면 좋겠다는 생각에 이 기사를 썼다.
화두로 참고할 사례가 많지만, 사례의 내용을 테라포름으로 낮추기가 힘들다는 점에 초점을 맞췄다.참고가 됐으면 좋겠어요.

해본 일

  • 레다는 이전 당시(2021년 9월)에 당초 최신 v10(beta) 버전을 활용했다가 이후 v10 정식 버전이 발매되면서 v10으로 이전했다.
  • 자동 운용 시 ECS를 사용하는 운용으로 전환하고, 별도로 각종 중간부품을 포함한 구축에 필요한 AWS 자원은 IaC(terraform)로 변경한다.
  • 중간부품의 사이즈는 대부분 최소한에 가까운 규격으로 정의되지만 그래도 움직인다.소속 조직의 규모 등에 따라 조정하십시오
  • .
  • Hosted Redash 내부 데이터에 대해 마이그레이션 스크립트를 작성하고 데이터를 마이그레이션했습니다.
  • 기타 각종 주변 자원과 설정도 모두 준비되었다.
  • 이외에 자기운용 환경을 조성할 때 관련자들에게 이전을 호소하는 등 업무 차원의 이전 작업도 진행했다.
  • 다음은 각 항목에 대한 상세한 설명입니다.

    중간부재 구축


    테라form의 구조 샘플을 함께 게재했습니다.

    RDS


    다음은 모두 최소한의 규격이고 매개 변수도 최소한의 것만 넣기 때문에 여러분의 상황에 따라 변경해 달라고 말씀드릴 수 있습니다.
    rds.tf
    module "db" {
      # see: https://github.com/terraform-aws-modules/terraform-aws-rds
      source  = "terraform-aws-modules/rds/aws"
      version = "~> 3.0"
    
      identifier = "<your-redash-rds>"
    
      engine = "postgres"
      # redash公式は9系を利用しているが、さすがに古すぎるので無視して一旦12で構築する
      engine_version              = "12.6"
      family                      = "postgres12"
      major_engine_version        = "12"
      instance_class              = "db.t3.micro"
      allocated_storage           = 5
      storage_encrypted           = true
      allow_major_version_upgrade = true
      auto_minor_version_upgrade  = true
      apply_immediately           = true
    
      username = "root"
      password = random_password.rds_root_password.result
      port     = 5432
    
      vpc_security_group_ids = ["<your-security-group_id>"]
    
      maintenance_window = "sat:19:00-sat:19:30"
      backup_window      = "18:30-19:00"
      backup_retention_period = 7
    
      # Enhanced Monitoring - see example for details on how to create the role
      # by yourself, in case you don't want to create it automatically
      monitoring_interval    = "30"
      monitoring_role_name   = "your-redash-rds-MonitoringRole"
      create_monitoring_role = true
    
      kms_key_id = aws_kms_key.db_encryption_key.arn
    
    
      enabled_cloudwatch_logs_exports = ["postgresql"]
    
      # DB subnet group
      subnet_ids = ["<your-subnet-a>", "<your-subnet-c>"]
    
      # Database Deletion Protection
      deletion_protection = true
    
      parameters = [
        {
          name  = "autovacuum"
          value = 1
        },
        {
          name  = "client_encoding"
          value = "utf8"
        }
      ]
    }
    
    resource "random_password" "rds_root_password" {
      length           = 30
      override_special = "!$%&*()-_=+[]{}<>:"
    }
    
    resource "aws_kms_key" "db_encryption_key" {
      description         = "kms key for database encryption"
      enable_key_rotation = true
    }
    
    resource "aws_kms_alias" "db_encryption_key" {
      name          = "alias/kms-redash-db-encryption-key"
      target_key_id = aws_kms_key.db_encryption_key.key_id
    }
    
    실례가 만들어지면 임의의 페달 등에서 만들어진 루트 서버로 RDS에 연결하고, 레드 캐시를 만드는 사용자를 사용한다.
    login_rds.sh
    psql -h <your-host>.<your-region>.rds.amazonaws.com -U root -p 5432
    
    물론 정말 노력한다면 이 과정도 IaC화할 수 있지만 이번에는 포기하고 수동으로 집행했다.
    create_redash_role.sql
    CREATE USER redash WITH PASSWORD '<your-awesome-strong-password>' CREATEDB;
    CREATE DATABASE redash;
    GRANT ALL ON ALL TABLES IN SCHEMA public TO redash;
    

    redis


    별다른 일을 하지 않고 최소한의 규격으로 구축하다.
    redis.tf
    resource "aws_elasticache_cluster" "redis" {
      cluster_id      = "redash-redis"
      engine          = "redis"
      node_type       = "cache.t2.micro"
      num_cache_nodes = 1
      # 公式がredis3系なのでそれに合わせる
      # see: https://github.com/getredash/redash/blob/0f41f2572060600301a31ac3654dff09395f5ab4/docker-compose.yml#L48
      parameter_group_name = "default.redis3.2"
      engine_version       = "3.2.10"
      port                 = 6379
      maintenance_window = "sat:19:00-sat:20:00"
      subnet_group_name  = aws_elasticache_subnet_group.redis.name
      security_group_ids = ["<your-security-group-id>"]
    }
    
    
    resource "aws_elasticache_subnet_group" "redis" {
      name        = "redash-redis-subnet"
      description = "redis subnet group for redash redis"
      subnet_ids  = ["<your-subnet-a>", "<your-subnet-c>"]
    }
    
    rds와 Redis를 방문할 때의 URL은 Redash 주체를 구축할 때 사용하기 때문에 파라미터 저장소에 등록하고 일어나세요.

    ECS


    전체 구성 모방참고 문장 구축.
    집단은 하나만 만들었고 서비스 정의 정의는 두 개를 정의했다. 작업 정의 정의 정의 정의는 최초로 수동으로 한 번 이동하는 create_db 명령의 세 가지를 포함한다.

    클러스터 정의 샘플


    ecs_cluster.tf
    resource "aws_ecs_cluster" "redash_ecs_cluster" {
      name = "redash-ecs-cluster"
      setting {
        name  = "containerInsights"
        value = "enabled"
      }
      capacity_providers = [
        "FARGATE",
        "FARGATE_SPOT"
      ]
    }
    

    서비스 정의 샘플


    Redash 서버를 위한 서비스와 Redash worker를 위한 서비스는 부하에 따라 worker를 규모화하려는 상황에 따라 서비스를 분할합니다.그러나 워크맨이 사용하는 서비스는 뒤에 기술한 ALB 접근 설정을 제외하고는 내용이 거의 같다. 여기에는 서버가 사용하는 서비스만 예시한다.
    또 레다스는 웹 서버와worker 사이에는 용기 간의 통신이 필요 없습니다로, 서비스 분할 시 서비스 할인 등을 설정할 필요가 없다.
    또 레드쉬의 컨테이너가 일어서는 데 시간이 걸리는 경우가 있어 건강검진 시간을 600초로 늘렸다는 내용도 댓글에 실렸다.
    ecs_service.tf
    # ECSサービス
    resource "aws_ecs_service" "redash_server_service" {
      name            = "redash-server"
      cluster         = aws_ecs_cluster.redash_ecs_cluster.id
      # 後述
      task_definition = aws_ecs_task_definition.redash_server.arn
    
      desired_count = 1
      deployment_maximum_percent = 200
      deployment_minimum_healthy_percent = 100
      # デフォルトの設定のままだと立ち上がる前にヘルスチェックが走ってしまうので、ヘルスチェックを始めるまで10分待つ
      # see: https://discuss.redash.io/t/aws-fargate-redash-server-alb/6152
      health_check_grace_period_seconds = 600
    
      deployment_circuit_breaker {
        enable   = true
        rollback = true
      }
    
      network_configuration {
        subnets          = ["<your-subnet-a>", "<your-subnet-c>"]
        security_groups  = ["<your-security-group-id>"]
        assign_public_ip = false
      }
      capacity_provider_strategy {
        capacity_provider = "FARGATE"
        base = 2
        weight = 1
      }
    
      capacity_provider_strategy {
        capacity_provider = "FARGATE_SPOT"
        base = 0
        weight = 0
      }
    
      enable_execute_command = true
    
      # ALBからアクセスさせるための設定
      load_balancer {
        target_group_arn = "<your-alb-target-group-arn>"
        container_name   = "redash_server"
        container_port   = 5000
      }
    

    작업 정의 샘플


    이것은 서비스 정의와 관련된 작업 정의의 견본입니다.CPU와 메모리는 최소한이지만 환경에 따라 축소하는 것이 좋다고 생각합니다.
    container_definition.tf
    # 初回のみ手動で一度だけ実行するコマンドなのでサービスには紐付け不要
    resource "aws_ecs_task_definition" "create_db" {
      family                   = "redash_create_db"
      network_mode             = "awsvpc"
      requires_compatibilities = ["FARGATE"]
      task_role_arn            = "<your-task-role-arn>"
      execution_role_arn       = "<your-task-execusion-role-arn>"
      cpu                      = 1024
      memory                   = 2048
      container_definitions    = file("container_definition/create_db.json")
    }
    
    # redash server用のサービスに紐付けるコンテナ定義
    resource "aws_ecs_task_definition" "redash_server" {
      family                   = "redash_server"
      network_mode             = "awsvpc"
      requires_compatibilities = ["FARGATE"]
      task_role_arn            = "<your-task-role-arn>"
      execution_role_arn       = "<your-task-execusion-role-arn>"
      cpu                      = 1024
      memory                   = 2048
      container_definitions    = file("container_definition/redash_server.json")
    }
    
    # redash worker用のサービスに紐付けるコンテナ定義
    resource "aws_ecs_task_definition" "redash_worker" {
      family                   = "redash_worker"
      network_mode             = "awsvpc"
      requires_compatibilities = ["FARGATE"]
      task_role_arn            = "<your-task-role-arn>"
      execution_role_arn       = "<your-task-execusion-role-arn>"
      cpu                      = 1024
      memory                   = 2048
      container_definitions    = file("container_definition/redash_worker.json")
    }
    
    다음 작업 정의container데피니션 샘플도 거기다 놔둬.
    적절하게 namecommand를 교체하십시오.워크맨worker,schedullerscheduler,createdb 명령을 실행하려면 create_db로 바꾸십시오.
    그리고 다른 사람이 만든 조회참고 문장를 편집할 수 있습니다. 필요없는query런너참고 문장를 삭감하고 있습니다.
    container_definition/redash_worker.json
    [
        {
            "name": "redash_server",
            "image": "redash/redash:10.0.0.b50363",
            "cpu": 0,
            "stopTimeout": 60,
            "memoryReservation": null,
            "environment": [
                {
                    "name": "REDASH_HOST",
                    "value": "<your-host>"
                },
    	    {
                    "name": "PYTHONUNBUFFERED",
                    "value": "0"
                },
    	    {
                    "name": "REDASH_LOG_LEVEL",
                    "value": "INFO"
                },
    	    {
                    "name": "REDASH_FEATURE_SHOW_PERMISSIONS_CONTROL",
                    "value": "true"
                },
    	    {   
                    "name": "REDASH_DISABLED_QUERY_RUNNERS",
                    "value": "redash.query_runner.graphite,redash.query_runner.mongodb,redash.query_runner.couchbase,redash.query_runner.url"
                }
            ],
            "secrets": [
                {
                    "name": "REDASH_DATABASE_URL",
                    "valueFrom": "<your-redah-rds-url>"
                },
                {
                    "name": "REDASH_REDIS_URL",
                    "valueFrom": "<your-redah-redis-url>"
                }
            ],
            "command": [
                "server"
            ],
            "logConfiguration": {
                "logDriver": "awslogs",
                "secretOptions": null,
                "options": {
                    "awslogs-group": "<your-log-group>",
                    "awslogs-region": "<your-region>",
                    "awslogs-stream-prefix": "<your-prefix>"
                }
            },
            "entryPoint": null,
            "portMappings": [
                {
                    "hostPort": 5000,
                    "protocol": "tcp",
                    "containerPort": 5000
                }
            ],
            "dockerLabels": null
        }
    ]
    

    ALB+Route53+ACM


    https를 통해 제작된 ECS에서 실행되는 Redash 환경에 접근할 수 있도록 ALB+Route 53+ACM의 스택을 준비합니다.
    루트 53에서 도메인을 취득하고 ACM으로 인증서를 만들어 거기에 ALB를 추가한 청취자, ECS 항목에 추가된 타겟팀을 추가했다.
    이렇게 하면 건강검진을 통해 브라우저에서redash에 안전하게 접근할 수 있다.
    건강검진은 /ping에서 시행하죠.
    alb.tf
    resource "aws_lb" "redash" {
      name               = "redash-lb"
      internal           = false
      load_balancer_type = "application"
      security_groups    = [<"your-alb-sg-id">]
      subnets            = var.subnet_ids
    }
    
    resource "aws_lb_target_group" "redash" {
      name        = "redash-target"
      port        = 5000
      protocol    = "HTTP"
      target_type = "ip"
      vpc_id      = <your-vpc-id>
      health_check {
        # health check用の /ping でhealth checkを行う
        # see: https://discuss.redash.io/t/aws-fargate-redash-server-alb/6152/6
        path     = "/ping"
        interval = 300
        timeout  = 120
      }
    }
    
    resource "aws_lb_listener" "forward" {
      load_balancer_arn = aws_lb.redash.arn
      port              = "443"
      protocol          = "HTTPS"
      certificate_arn   = aws_acm_certificate.redash.arn
    
      default_action {
        type             = "forward"
        target_group_arn = aws_lb_target_group.redash.arn
      }
    }
    
    resource "aws_acm_certificate" "redash" {
      domain_name = "redash.exapmle.com"
      subject_alternative_names = [
        "*.redash.exapmle.com"
      ]
      validation_method = "DNS"
      tags = {
        Name = "<your-redash-dns-name>"
      }
    }
    

    Redash 내부 데이터 전송


    이전 호스트 버전의 Redash에서 이전한 경우 dump 이전 환경의 RDS 데이터를 고려하여 INSERT를 새 환경의 DB로 옮길 수 있지만 Hosted Redash는 RDS에 직접 접근할 수 없기 때문에 변환 스크립트를 써야 한다.
    왕도는 redash-migrate 방법을 사용했지만 이번에는 Hosted Redash가 이전에 사용했던 자전Redash(참고로 그때는 EC2를 사용했는데...) 데이터를 Hosted Redash로 옮기는 스크립트가 있어서 회수하고 사용했습니다.하지만 0을 바탕으로 하면 레드스-migrate로 둥글다고 생각해요.

    기타, 상세한 주변 자원 제작 및 준비

  • Redashbot
  • 이쪽도 공식 물건을 사용했지만 갈아타게 해 주세요yamitzky의bot.구축 방법 참고여기 기사..
  • 당사는 AWS Apprunner에서 디버깅과 운용을 진행하였다.저는 개인적으로 이때 처음으로 AWS Apprunner를 사용했지만 매우 간단한 조작으로 간단한 컨테이너 응용 프로그램의 디자인에 적합하다고 생각합니다.
  • AzureaD 기반 SAML 인증 설정
  • FOLIO에서 IDaS로서AzureaD를 사용했기 때문에AzureaD의SAML인증을 통해 안전을 보장합니다.구체적인 설정 방법은 생략했지만 FOLIO 원격 작업도 당연한 만큼 VPN 없이 방문할 수 있어 기쁘다.
  • 데이터dog을 이용하여 감시
  • 오류 로그, 워크맨, scheduller 등 각 용기의 CPU/메모리 사용량이 너무 높은지 최소한의 감시를 설정했다.
  • 업무 수준의 이동


    그러면 필요한 자원 준비가 끝난 후 자신의 환경의redash를 이용하여 업무 수준의 이전을 시작할 수 있다.Redash는 사내 각 팀에 의해 다양한 용도로 사용되고 있으며, 이전할 때 다음과 같은 절차를 취하여 각 단계에서 발생하는 문의에 대응하는 동시에 이전하였다.
  • 이전 환경(Hosted Redash), 새 환경(ECS의 사전 운용)에 액세스할 수 있는 병행 작업 기간을 설정하는 동시에 새 환경으로의 이전을 추진합니다.구체적으로 아래와 같다.
  • confluence에서 옛 환경과 새로운 환경에 대한 RedashURL 대응 표를 작성합니다.
  • 로그인 이력에서 옛 환경을 방문한 사람을 확인하고 적절히 듣는다.
  • 슬랙과 Confluence에 옛 환경의 URL을 붙일 곳을 찾아 새로운 환경의 URL로 대체한다.
  • 상기 완성과 어느 정도 이전을 완성하는 단계에서SAML 로그인 권한(기업 응용의 분배)을 일반 사용자로부터 벗기고 SAML에 로그인할 수 있는 구성원을 관리자에게 한정한다.
  • 이렇게 하면 일반 사용자는 무의식중에 낡은 환경을 이용하는 상황에서도 새로운 환경의 이동을 추진할 수 있고 상담이 오는 상황에서 관리자는 필요에 따라 낡은 환경을 방문하여 상담을 할 수 있다.
  • 총결산


    무사히 Hosted Redash에 Redash(v10)를 구축해 Hosted Redash와 작별했다.
    FOLIO가 2020년부터 이전운용(EC2) 레드ash에서 Hosted Redash로 옮긴 뒤 올해 9월 Hosted Redash가 철수해 이전운용(ECS)으로 이동하기 시작했는데, 이 짧은 시간 안에 지난번을 포함해 두 번의 레드ash 이전이 이뤄질 줄은 몰랐다는 게 실감이지만 다양한 지식을 정리하는 좋은 경험이 됐다.Hosted Redash의 서비스가 끝났기 때문에 이 글을 직접 쓰는 사람은 드물겠지만, 앞으로 Redash를 새롭게 시작하는 분들에게 참고가 될 수 있다면 좋겠다.

    좋은 웹페이지 즐겨찾기