AWS에 Fargate, RDS 및 Terraform을 사용하여 Hasura 배포

Hasura는 Postgres의 훌륭한 GraphQL 게이트웨이입니다.간단하게 Heroku에서 시작할 수 있지만, 전자동 배치를 통해 AWS에 배치하고 싶다면, 이 글은 가능한 방법을 찾도록 안내할 것입니다.
AWS를 배포할 때 여러 가용성 영역(AZ)에 걸쳐 배포하는 것을 강력히 권장합니다. 이 경우 AZ 하나에 장애가 발생하면 AZ가 복구될 때까지 서비스가 잠시 중단되지 않고 중단될 수 있습니다.
이 배포에 사용되는 구성 요소는 다음과 같습니다.
  • Multi AZ
  • 에 Postgres RDS 데이터베이스 배포
  • Hasura가 Fargate에 배포한 여러 AZ
  • Hasura 작업 간의 ALB 로드 밸런싱
  • ACM에서 발급한 ALB 교통 안전을 보장하는 인증서.
  • RDS, ECS 및 ALB를 Cloudwatch 로그에 기록합니다.
  • 이것은 우리가 구축할 구조이다.

    Cloudformation을 사용하여 구축할 수 있지만, Terraform을 선택한 것은 여러 가지 이유, 특히 그 능력terraform plan 때문입니다.
    참고로, 만약에 Fargate를 사용하기 시작했다면, 웹 관리 컨트롤러에서 실험을 시작할 때, 서비스 역할 만들기, IAM 권한, 로그 그룹 등 다음과 같은 많은 복잡한 문제를 처리할 것입니다. 자동화를 원할 때, 돌아가서 다음 세부 사항을 깊이 이해하십시오.
    AWS 계정에서 ECS 리소스를 구성하기 전에 이 계정에 IAM 역할AWSServiceRoleForECS을 생성해야 합니다.웹 컨트롤러에 그룹을 수동으로 만들었다면, 이것은 당신을 위해 만들 것입니다.Terraform을 사용하여 관리하려면 Terraform 구성으로 가져올 수 있습니다.
    주의해야 할 것은 AWSServiceRoleForECS 모든 계정은 한 번만 존재할 수 있기 때문에 (서비스 역할 접미사를 지원하지 않습니다.) 따라서 AWS 계정에 여러 개의 Hasura 창고를 배치하면 서비스 역할의 지형은 메인 창고와 독립되어야 합니다.
    이런 역할 만들기
    # Service role allowing AWS to manage resources required for ECS
    resource "aws_iam_service_linked_role" "ecs_service" {
      aws_service_name = "ecs.amazonaws.com"
    }
    
    인프라 시설의 구성 요소를 깊이 연구하기 전에 약간의 변수가 필요하다
    # Which region to deploy to
    variable "region" { }
    # Which domain to use. Service will be deployed at hasura.domain
    variable "domain" { }
    # The access key to secure hasura with. For admin access
    variable "hasura_access_key" { }
    # The secret shared HMAC key for JWT authentication
    variable "hasura_jwt_hmac_key" { }
    # User name for RDS
    variable "rds_username" { }
    # Password for RDS
    variable "rds_password" { }
    # The DB name in the RDS instance. Note that this cannot contain -'s
    variable "rds_db_name" { }
    # The size of RDS instance, eg db.t2.micro
    variable "rds_instance" { }
    # How many AZ's to create in the VPC
    variable "az_count" { default = 2 }
    # Whether to deploy RDS and ECS in multi AZ mode or not
    variable "multi_az" { default = true }
    
    이제 ALB에 대한 인증서를 작성합니다.개발 환경과 같은 창고를 정기적으로 삭제하고 다시 만들려면 단독 지형 창고에 인증서를 만드는 것이 좋습니다. 매번 인증서를 파괴하고 다시 만들지 않습니다.새 AWS 계정의 기본 제한은 연간 20개의 인증서이기 때문에 이 제한을 의외로 소모하기 쉽다.요구에 따라 늘릴 수 있지만 내 경험에 따르면 하루 이틀이 걸려야 완성할 수 있다.
    Route 53을 사용하는 경우 ACM 인증서를 자동으로 검증할 수 있습니다. 이것은 전체 자동 워크플로우를 실현하는 가장 간단한 방법입니다.또는 Terraform이 DNS 공급자를 지원하는 경우 DNS 레코드를 추가할 수 있습니다.
    인증서를 만들려면 다음과 같이 하십시오.
    resource "aws_acm_certificate" "hasura" {
      domain_name       = "hasura.${var.domain}"
      validation_method = "DNS"
    
      lifecycle {
        create_before_destroy = true
      }
    }
    
    인증서 확인
    data "aws_route53_zone" "hasura" {
      name         = "${var.domain}."
    }
    
    resource "aws_route53_record" "hasura_validation" {
        depends_on = ["aws_acm_certificate.hasura"]
        name = "${lookup(aws_acm_certificate.hasura.domain_validation_options[0], "resource_record_name")}"
        type = "${lookup(aws_acm_certificate.hasura.domain_validation_options[0], "resource_record_type")}"
        zone_id = "${data.aws_route53_zone.hasura.zone_id}"
        records = ["${lookup(aws_acm_certificate.hasura.domain_validation_options[0], "resource_record_value")}"]
        ttl = 300
    }
    
    resource "aws_acm_certificate_validation" "hasura" {
        certificate_arn = "${aws_acm_certificate.hasura.arn}"
        validation_record_fqdns = ["${aws_route53_record.hasura_validation.*.fqdn}" ]
    }
    
    
    자, 이제 인프라 시설의 주체를 처리할 수 있습니다.
    우선, 우리는 이 인프라 시설을 설치하기 위해 전문 네트워크가 필요하다.우리는 RDS를 위한 개인 서브넷을 만들고 ECS를 위한 공공 서브넷을 만들 것입니다.ECS 작업이 공공 서브넷에 설치되어 있기 때문에 docker hub에서 Hasura 이미지를 가져올 수 있습니다.개인 서브넷에 배치할 경우 이미지를 추출할 수 있도록 NAT 게이트웨이를 추가해야 합니다.
    
    ### VPC
    
    # Fetch AZs in the current region
    data "aws_availability_zones" "available" {}
    
    resource "aws_vpc" "hasura" {
      cidr_block = "172.17.0.0/16"
    }
    
    # Create var.az_count private subnets for RDS, each in a different AZ
    resource "aws_subnet" "hasura_rds" {
      count             = "${var.az_count}"
      cidr_block        = "${cidrsubnet(aws_vpc.hasura.cidr_block, 8, count.index)}"
      availability_zone = "${data.aws_availability_zones.available.names[count.index]}"
      vpc_id            = "${aws_vpc.hasura.id}"
    }
    
    # Create var.az_count public subnets for Hasura, each in a different AZ
    resource "aws_subnet" "hasura_ecs" {
      count                   = "${var.az_count}"
      cidr_block              = "${cidrsubnet(aws_vpc.hasura.cidr_block, 8, var.az_count + count.index)}"
      availability_zone       = "${data.aws_availability_zones.available.names[count.index]}"
      vpc_id                  = "${aws_vpc.hasura.id}"
      map_public_ip_on_launch = true
    }
    
    # IGW for the public subnet
    resource "aws_internet_gateway" "hasura" {
      vpc_id = "${aws_vpc.hasura.id}"
    }
    
    # Route the public subnet traffic through the IGW
    resource "aws_route" "internet_access" {
      route_table_id         = "${aws_vpc.hasura.main_route_table_id}"
      destination_cidr_block = "0.0.0.0/0"
      gateway_id             = "${aws_internet_gateway.hasura.id}"
    }
    
    
    
    이제 ALB는 ECS, ECS 작업은 RDS와 대화할 수 있는 보안 그룹을 만듭니다.
    # Security Groups
    
    # Internet to ALB
    resource "aws_security_group" "hasura_alb" {
      name        = "hasura-alb"
      description = "Allow access on port 443 only to ALB"
      vpc_id      = "${aws_vpc.hasura.id}"
    
      ingress {
        protocol    = "tcp"
        from_port   = 443
        to_port     = 443
        cidr_blocks = ["0.0.0.0/0"]
      }
    
      egress {
        from_port = 0
        to_port   = 0
        protocol  = "-1"
        cidr_blocks = ["0.0.0.0/0"]
      }
    }
    # ALB TO ECS
    
    resource "aws_security_group" "hasura_ecs" {
      name        = "hasura-tasks"
      description = "allow inbound access from the ALB only"
      vpc_id      = "${aws_vpc.hasura.id}"
    
      ingress {
        protocol        = "tcp"
        from_port       = "8080"
        to_port         = "8080"
        security_groups = ["${aws_security_group.hasura_alb.id}"]
      }
    
      egress {
        protocol    = "-1"
        from_port   = 0
        to_port     = 0
        cidr_blocks = ["0.0.0.0/0"]
      }
    }
    
    # ECS to RDS
    resource "aws_security_group" "hasura_rds" {
      name        = "hasura-rds"
      description = "allow inbound access from the hasura tasks only"
      vpc_id      = "${aws_vpc.hasura.id}"
    
      ingress {
        protocol        = "tcp"
        from_port       = "5432"
        to_port         = "5432"
        security_groups = ["${aws_security_group.hasura_ecs.id}"]
      }
    
      egress {
        protocol    = "-1"
        from_port   = 0
        to_port     = 0
        cidr_blocks = ["0.0.0.0/0"]
      }
    }
    
    
    이제 RDS 인스턴스를 만들 수 있습니다.인스턴스를 배치하려면 서브넷 그룹이 필요합니다. 위에 생성된 hasura_rds 서브넷을 사용합니다.
    resource "aws_db_subnet_group" "hasura" {
      name       = "hasura"
      subnet_ids = ["${aws_subnet.hasura_rds.*.id}"]
    }
    
    그런 다음 RDS 인스턴스 자체를 작성합니다.
    resource "aws_db_instance" "hasura" {
        name                        = "${var.rds_db_name}"
        identifier                  = "hasura"
        username                    = "${var.rds_username}"
        password                    = "${var.rds_password}"
        port                        = "5432"
        engine                      = "postgres"
        engine_version              = "10.5"
        instance_class              = "${var.rds_instance}"
        allocated_storage           = "10"
        storage_encrypted           = false
        vpc_security_group_ids      = ["${aws_security_group.hasura_rds.id}"]
        db_subnet_group_name        = "${aws_db_subnet_group.hasura.name}"
        parameter_group_name        = "default.postgres10"
        multi_az                    = "${var.multi_az}"
        storage_type                = "gp2"
        publicly_accessible         = false
        # snapshot_identifier       = "hasura"
        allow_major_version_upgrade = false
        auto_minor_version_upgrade  = false
        apply_immediately           = true
        maintenance_window          = "sun:02:00-sun:04:00"
        skip_final_snapshot         = false
        copy_tags_to_snapshot       = true
        backup_retention_period     = 7
        backup_window               = "04:00-06:00"
        final_snapshot_identifier   = "hasura"
    }
    
    위의 구성에서는 hasura 라는 새 RDS 인스턴스가 구축됩니다.terraform을 기존 스냅샷에서 RDS 인스턴스를 복구할 수 있습니다.# snapshot_identifier 줄의 주석을 취소함으로써 이 점을 실현할 수 있다.그러나 스냅샷에서 실례를 만들기 전에 this issue 를 읽는 것을 권장합니다.간단히 말해 스냅샷에서 인스턴스를 만들려면 템플릿의 향후 실행에 항상 포함 snapshot_identifier 해야 합니다. 그렇지 않으면 인스턴스를 삭제하고 새 인스턴스로 다시 생성합니다.
    ECS/Fargate로 계속...
    ECS 클러스터 생성
    resource "aws_ecs_cluster" "hasura" {
      name = "hasura-cluster"
    }
    
    Hasura 서비스를 만들기 전에 로그인할 수 있는 곳을 만듭시다
    resource "aws_cloudwatch_log_group" "hasura" {
      name = "/ecs/hasura"
    }
    
    로그 그룹을 만드는 것은 매우 간단합니다. ECS 작업이 그것에 로그인할 수 있도록 합니다. 대부분의 IAM처럼 좀 복잡합니다!
    data "aws_iam_policy_document" "hasura_log_publishing" {
      statement {
        actions = [
          "logs:CreateLogStream",
          "logs:PutLogEvents",
          "logs:PutLogEventsBatch",
        ]
        resources = ["arn:aws:logs:${var.region}:*:log-group:/ecs/hasura:*"]
      }
    }
    
    resource "aws_iam_policy" "hasura_log_publishing" {
      name        = "hasura-log-pub"
      path        = "/"
      description = "Allow publishing to cloudwach"
    
      policy = "${data.aws_iam_policy_document.hasura_log_publishing.json}"
    }
    
    data "aws_iam_policy_document" "hasura_assume_role_policy" {
      statement {
        actions = ["sts:AssumeRole"]
    
        principals {
          type        = "Service"
          identifiers = ["ecs-tasks.amazonaws.com"]
        }
      }
    }
    
    resource "aws_iam_role" "hasura_role" {
      name               = "hasura-role"
      path               = "/system/"
      assume_role_policy = "${data.aws_iam_policy_document.hasura_assume_role_policy.json}"
    }
    
    
    resource "aws_iam_role_policy_attachment" "hasura_role_log_publishing" {
      role = "${aws_iam_role.hasura_role.name}"
      policy_arn = "${aws_iam_policy.hasura_log_publishing.arn}"
    }
    
    
    그런 다음 작업 정의를 작성합니다.이것은 실례의 크기를 조정하는 곳이며,docker 용기에 전달되는 환경 속성을 설정하는 곳이다.여기서 JWT 인증을 위한 구성 실례를 살펴보겠습니다.image를 실행할 버전으로 업데이트합니다.응용 프로그램 이름의 CORS 설정을 업데이트하거나 완전히 삭제해야 합니다.
    
    resource "aws_ecs_task_definition" "hasura" {
      family                   = "hasura"
      network_mode             = "awsvpc"
      requires_compatibilities = ["FARGATE"]
      cpu                      = "256"
      memory                   = "512"
      execution_role_arn       = "${aws_iam_role.hasura_role.arn}"
    
      container_definitions = <<DEFINITION
        [
          {
            "image": "hasura/graphql-engine:v1.0.0-alpha34",
            "name": "hasura",
            "networkMode": "awsvpc",
            "portMappings": [
              {
                "containerPort": 8080,
                "hostPort": 8080
              }
            ],
            "logConfiguration": {
              "logDriver": "awslogs",
              "options": {
                "awslogs-group": "/ecs/hasura",
                "awslogs-region": "${var.region}",
                "awslogs-stream-prefix": "ecs"
              }
            },
            "environment": [
              {
                "name": "HASURA_GRAPHQL_ACCESS_KEY",
                "value": "${var.hasura_access_key}"
              },
              {
                "name": "HASURA_GRAPHQL_DATABASE_URL",
                "value": "postgres://${var.rds_username}:${var.rds_password}@${aws_db_instance.hasura.endpoint}/${var.rds_db_name}"
              },
              {
                "name": "HASURA_GRAPHQL_ENABLE_CONSOLE",
                "value": "true"
              },
              {
                "name": "HASURA_GRAPHQL_CORS_DOMAIN",
                "value": "https://app.${var.domain}:443"
              },
    
              {
                "name": "HASURA_GRAPHQL_PG_CONNECTIONS",
                "value": "100"
              },
              {
                "name": "HASURA_GRAPHQL_JWT_SECRET",
                "value": "{\"type\":\"HS256\", \"key\": \"${var.hasura_jwt_hmac_key}\"}"
              }
            ]
          }
        ]
    DEFINITION
    
    }
    
    
    이제 ECS 서비스를 만듭니다.multi_az 속성을true로 설정하면 두 작업이 시작됩니다.그것은 자동으로 임무를 서비스에 설정된 서브넷, 즉 두 개의 서브넷에 고르게 분포할 것이다.
    resource "aws_ecs_service" "hasura" {
      depends_on      = ["aws_ecs_task_definition.hasura", "aws_cloudwatch_log_group.hasura"]
      name            = "hasura-service"
      cluster         = "${aws_ecs_cluster.hasura.id}"
      task_definition = "${aws_ecs_task_definition.hasura.arn}"
      desired_count   = "${var.multi_az == true ? "2" : "1"}"
      launch_type     = "FARGATE"
    
      network_configuration {
        assign_public_ip  = true
        security_groups   = ["${aws_security_group.hasura_ecs.id}"]
        subnets           = ["${aws_subnet.hasura_ecs.*.id}"]
      }
    
      load_balancer {
        target_group_arn = "${aws_alb_target_group.hasura.id}"
        container_name   = "hasura"
        container_port   = "8080"
      }
    
      depends_on = [
        "aws_alb_listener.hasura",
      ]
    }
    
    현재 우리는 ECS 서비스와 RDS 데이터베이스를 가지고 있으며, 우리는 단지 약간의 공공 접근만 필요로 하며, 이것은 ALB에서 제공할 것이다.
    먼저 ALB가 기록할 곳을 만듭니다.S3 삽질부터.원하는 생명주기 정책을 추가할 수 있고, 버킷 이름은 전역적으로 유일하다는 것을 기억할 수 있습니다.
    resource "aws_s3_bucket" "hasura" {
      bucket = "hasura-${var.region}"
      acl    = "private"
    }
    
    ALB 로그인을 허용하기 위해 IAM 정책을 추가합니다.버킷 이름을 업데이트하는 것을 기억하십시오.
    data "aws_elb_service_account" "main" {}
    
    resource "aws_s3_bucket_policy" "hasura" {
      bucket = "${aws_s3_bucket.hasura.id}"
    
      policy = <<POLICY
    {
      "Id": "hasuraALBWrite",
      "Version": "2012-10-17",
      "Statement": [
        {
          "Sid": "hasuraALBWrite",
          "Action": [
            "s3:PutObject"
          ],
          "Effect": "Allow",
          "Resource": "arn:aws:s3:::hasura-${var.region}/alb/*",
          "Principal": {
            "AWS": [
              "${data.aws_elb_service_account.main.arn}"
            ]
          }
        }
      ]
    }
    POLICY
    }
    
    
    ACM 인증서를 별도의 지형 스택에 둔 경우 가져와야 합니다.
    data "aws_acm_certificate" "hasura" {
      domain   = "hasura.${var.domain}"
      types       = ["AMAZON_ISSUED"] 
      most_recent = true
      statuses = ["ISSUED"]
    }
    
    ALB 자체를 만듭니다.
    resource "aws_alb" "hasura" {
      name            = "hasura-alb"
      subnets         = ["${aws_subnet.hasura_ecs.*.id}"]
      security_groups = ["${aws_security_group.hasura_alb.id}"]
    
      access_logs {
        bucket = "${aws_s3_bucket.hasura.id}"
        prefix = "alb"
        enabled = true
      }
    }
    
    그런 다음 대상 그룹을 생성합니다.작업을 중지/시작할 때 ECS가 대상 그룹에 작업을 등록합니다.
    resource "aws_alb_target_group" "hasura" {
      name        = "hasura-alb"
      port        = 8080
      protocol    = "HTTP"
      vpc_id      = "${aws_vpc.hasura.id}"
      target_type = "ip"
      health_check {
        path = "/"
        matcher = "302"
      }
    }
    
    그리고 탐지기를 만듭니다.가져온 경우 certificate_arn"${data.aws_acm_certificate.hasura.arn}" 로 설정합니다.
    resource "aws_alb_listener" "hasura" {
      load_balancer_arn = "${aws_alb.hasura.id}"
      port              = "443"
      protocol          = "HTTPS"
      certificate_arn   = "${aws_acm_certificate.hasura.arn}"
    
      default_action {
        target_group_arn = "${aws_alb_target_group.hasura.id}"
        type             = "forward"
      }
    }
    
    마지막으로 ALB를 가리키는 Route 53 레코드를 만듭니다.
    resource "aws_route53_record" "hasura" {
      zone_id = "${data.aws_route53_zone.hasura.zone_id}"
      name    = "hasura.${var.domain}"
      type    = "A"
    
      alias {
        name                   = "${aws_alb.hasura.dns_name}"
        zone_id                = "${aws_alb.hasura.zone_id}"
        evaluate_target_health = true
      }
    }
    
    
    지형 배치가 완성되었습니다!잘 해봐야지!
    스택은 빈 모드와 탐색 중인 Hasura 실례https://hasura.domain 상태에서 시작해야 합니다
    행운을 빕니다. 언제든지 메시지를 남겨주세요. 아니면
    하술라의 불화 속에.
    이 게시물을 적당한 지형 모듈로 바꾸었다.만약 당신이 그것을 배치하고 싶다면, 당신은 이곳에서 보아야 한다. https://github.com/Rayraegah/terraform-aws-hasura

    좋은 웹페이지 즐겨찾기