Время подключения к кластеру AWS ECS истекло - PullRequest
0 голосов
/ 21 октября 2019

У меня кластер ECS, работающий поверх EC2 в частных подсетях. У меня есть запись CNAME в моем регистраторе, которая перенаправляет мой домен API на имя DNS моего открытого доступа NLB. У меня есть nginx контейнер для боковой машины, действующий в качестве обратного прокси для пользовательского Go API. Порты хоста / контейнера прослушивают 443, а затем контейнер nginx направляет мой API через 8080 через мостовую сеть. Слушатель балансировки нагрузки прослушивает 443 / TCP, так как я хочу, чтобы завершение TLS было выполнено с контейнером nginx. Таким образом, у меня есть tcp проход на NLB. Я также прикрепил целевую группу, принимающую трафик через 443 / tcp. Кроме того, я подключил группу безопасности к кластеру, позволяющему весь трафик свыше 443 из балансировщика сетевой нагрузки. Несмотря на все это, когда я делаю запросы к API, я получаю тайм-аут подключения к балансировщику нагрузки. У меня такое чувство, что у меня здесь мои утки подряд, поэтому я не знаю, почему это происходит. Все проверки здоровья моей целевой группы также проходят. Ниже приведены моя конфигурация, nginx conf, определение задачи и вывод ошибок.

Configuration:

resource "aws_lb" "myapi_api_lb" {
  name               = "my-api-lb"
  internal           = false
  load_balancer_type = "network"
  subnets            = ["${module.vpc.public_subnets}"]

  enable_deletion_protection = false

  tags = {
     Environment = "dev/demo"
   }
 }

resource "aws_security_group" "ecs_tasks" {
  name        = "ecs-tasks"
  description = "allow inbound access from the LB only"
  vpc_id      = "${module.vpc.vpc_id}"

  ingress {
    protocol        = "TCP"
    from_port       = 443
    to_port         = 443
    cidr_blocks     = ["10.10.1.0/24", "10.10.2.0/24", "10.10.3.0/24"]
  }

  egress {
    protocol    = "-1"
    from_port   = 0
    to_port     = 0
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_lb_target_group" "myapi_api_tg" {
  name     = "myapi-api-tg"
  port     = 443
  protocol = "TCP"
  target_type = "instance"
  vpc_id   = "${module.vpc.vpc_id}"

  stickiness{
    enabled = false
    type = "lb_cookie"
  }
  health_check{
    interval = 10
    port     = 443
    protocol = "TCP"
    healthy_threshold = 2
    unhealthy_threshold = 2
  }
}

resource "aws_lb_listener" "myapi_listener" {
  load_balancer_arn = "${aws_lb.myapi_api_lb.id}"
  port              = "443"
  protocol          = "TCP"

  default_action {
    target_group_arn = "${aws_lb_target_group.myapi_tg.id}"
    type             = "forward"
  }
}

resource "aws_iam_role" "ecs-instance-role" {
    name                = "ecs-instance-role"
    path                = "/"
    assume_role_policy  = "${data.aws_iam_policy_document.ecs-instance-policy.json}"
}

data "aws_iam_policy_document" "ecs-instance-policy" {
    statement {
        actions = ["sts:AssumeRole"]

        principals {
            type        = "Service"
            identifiers = ["ec2.amazonaws.com"]
        }
    }
}

resource "aws_iam_role_policy_attachment" "ecs-instance-role-attachment" {
    role       = "${aws_iam_role.ecs-instance-role.name}"
    policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
}

resource "aws_iam_instance_profile" "ecs-instance-profile" {
    name = "ecs-instance-profile"
    path = "/"
    role = "${aws_iam_role.ecs-instance-role.id}"
    provisioner "local-exec" {
      command = "sleep 10"
    }
}

resource "aws_launch_configuration" "ecs-launch-configuration" {
    image_id                    = "ami-123456"
    instance_type               = "t2.medium"
    iam_instance_profile        = "${aws_iam_instance_profile.ecs-instance-profile.id}"

    root_block_device {
      volume_type = "standard"
      volume_size = 25
      delete_on_termination = false
    }

    lifecycle {
      create_before_destroy = true
    }

    security_groups             = ["${aws_security_group.ecs_tasks.id}", "${module.ssh_security_group.this_security_group_id}"]
    associate_public_ip_address = "false"
    key_name                    = "myapiadmin"
    user_data                   = <<EOF
                                  #!/bin/bash
                                  sudo yum update -y ecs-init
                                  echo ECS_CLUSTER=${aws_ecs_cluster.myapi.name} >> /etc/ecs/ecs.config
                                  EOF
}

resource "aws_autoscaling_group" "ecs-autoscaling-group" {
    name                        = "ecs-autoscaling-group"
    max_size                    = 4
    min_size                    = 2
    desired_capacity            = 2
    vpc_zone_identifier         = ["${module.vpc.private_subnets}"]
    launch_configuration        = "${aws_launch_configuration.ecs-launch-configuration.name}"
    health_check_type           = "ELB"
  }

resource "aws_ecs_cluster" "myapi" {
  name = "mygoapis"
}

data "aws_iam_role" "ecs_task_execution_role" {
  name = "ecsTaskExecutionRole"
}

resource "aws_ecs_task_definition" "myapi" {
  family                   = "myapi"
  requires_compatibilities = ["EC2"]
  network_mode = "bridge"
  cpu = 512
  memory = 1024
  execution_role_arn = "${data.aws_iam_role.ecs_task_execution_role.arn}"

  container_definitions = "${file("myapi-service.json")}"
}

resource "aws_ecs_service" "myapiservice" {
  name            = "myapi_service"
  cluster         = "${aws_ecs_cluster.myapi.id}"
  task_definition = "${aws_ecs_task_definition.myapi.arn}"
  launch_type     = "EC2"
  desired_count   = 2

  load_balancer {
    target_group_arn = "${aws_lb_target_group.myapi_tg.id}"
    container_name   = "nginx"
    container_port   = 443
  }

    depends_on = [
    "aws_lb_listener.myapi_listener",
  ]
}

nginx.conf:

events { }
http {
    server {
        listen 80;
        listen [::]:80;     
        server_name www.myapi.com myapi.com;
        return 301 https://$host$request_uri;
    } 

    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;  
        server_name www.myapi.com myapi.com;

        ssl_certificate      /etc/certbot/live/myapi.com/fullchain.pem;
        ssl_certificate_key  /etc/certbot/live/myapi.com/privkey.pem;

        ssl_dhparam /etc/ssl/ffdhe2048.pem;

        ssl_protocols TLSv1.3 TLSv1.2 TLSv1.1;
        ssl_prefer_server_ciphers on;
        ssl_ciphers EECDH+ECDSA+AESGCM:EECDH+aRSA+AESGCM:EECDH+ECDSA+SHA512:EECDH+ECDSA+SHA384:EECDH+ECDSA+SHA256:ECDH+AESGCM:ECDH+AES256:DH+AESGCM:DH+AES256:RSA+AESGCM:!aNULL:!eNULL:!LOW:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS;

        ssl_session_cache shared:TLS:2m;
        ssl_buffer_size 4k;

        ssl_stapling on;
        ssl_stapling_verify on;
        resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001]; # Cloudflare

        add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload' always;

        location /api/ {
            proxy_pass http://myapi:8080/;

            # Reject requests with unsupported HTTP method
            if ($request_method !~ ^(GET|POST)$) {
                return 405;
            }
        }    
    }
}

Task Definition:

[  
  {
    "name": "nginx",
    "image": "12345678910.dkr.ecr.us-east-1.amazonaws.com/myapi-reverse-proxy:1.0",
    "memory": 256,
    "cpu": 256,
    "essential": true,
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
          "awslogs-group": "reverse_proxy_nginx",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "nginx"
      }
    },
    "portMappings": [
      {
        "hostPort": 443,
        "containerPort": 443
      }
    ],
    "links": [
      "myapi"
    ]
  }, 
  {
    "name": "myapi",
    "image": "12345678910.dkr.ecr.us-east-1.amazonaws.com/myapi:1.0",
    "cpu": 256,
    "memory": 256,
    "essential": true,
    "portMappings": [
      {
        "containerPort": 8080
      }
    ],
    "logConfiguration": {
      "logDriver": "awslogs",
      "options": {
          "awslogs-group": "myapi",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "api"
      }
    }
  }
]

Error:

curl -v -X POST https://myapi.com/api/register
*   Trying 1.2.3.4...
* TCP_NODELAY set
* Connection failed
* connect to 1.2.3.4 port 443 failed: Operation timed out
* Closing connection 0
curl: (7) Failed to connect to myapi.com port 443: Operation timed out

1 Ответ

0 голосов
/ 22 октября 2019

Я нашел идеально решение этой проблемы. Nginx "proxy_pass" не может использовать информацию "etc / hosts". Это делает возможность Docker link отключить в ECS.

Я хочу предложить вам использовать обратный прокси-сервер HA-Proxy в ECS. Я пробовал обратный прокси nginx, но не получилось. И успех с HA-Proxy. Это проще, чем конфигурация nginx.

Во-первых, используйте опцию «ссылки» Docker и задайте «переменные среды» (например, LINK_APP, LINK_PORT).

Во-вторых, заполните это «переменные среды"into haproxy.cfg.

Также я рекомендую вам использовать" динамическое сопоставление портов "для ALB. это делает работу более гибкой.

taskdef.json:

# taskdef.json

{
    "executionRoleArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/<APP_NAME>_ecsTaskExecutionRole",
    "containerDefinitions": [
      {
        "name": "<APP_NAME>-rp",
        "image": "gnokoheat/ecs-reverse-proxy:latest",
        "essential": true,
        "memoryReservation": <MEMORY_RESV>,
        "portMappings": [
          {
            "hostPort": 0,
            "containerPort": 80,
            "protocol": "tcp"
          }
        ],
        "links": [
          "<APP_NAME>"
        ],
        "environment": [
          {
            "name": "LINK_PORT",
            "value": "<SERVICE_PORT>"
          },
          {
            "name": "LINK_APP",
            "value": "<APP_NAME>"
          }
        ]
      },
      {
        "name": "<APP_NAME>",
        "image": "<IMAGE_NAME>",
        "essential": true,
        "memoryReservation": <MEMORY_RESV>,
        "portMappings": [
          {
            "protocol": "tcp",
            "containerPort": <SERVICE_PORT>
          }
        ],
        "environment": [
          {
            "name": "PORT",
            "value": "<SERVICE_PORT>"
          },
          {
            "name": "APP_NAME",
            "value": "<APP_NAME>"
          }
        ]
      }
    ],
    "requiresCompatibilities": [
      "EC2"
    ],
    "networkMode": "bridge",
    "family": "<APP_NAME>"
  }

haproxy.cfg:

# haproxy.cfg

global
    daemon
    pidfile /var/run/haproxy.pid

defaults
    log global
    mode http
    retries 3
    timeout connect 5000
    timeout client 50000
    timeout server 50000

frontend http
    bind *:80

    http-request set-header X-Forwarded-Host %[req.hdr(Host)]

    compression algo gzip
    compression type text/css text/javascript text/plain application/json application/xml

    default_backend app

backend app
    server static "${LINK_APP}":"${LINK_PORT}"

Dockerfile (haproxy):

FROM haproxy:1.7
USER root
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

См .:

Github: https://github.com/gnokoheat/ecs-reverse-proxy

Изображение докера: gnokoheat / ecs-reverse-proxy: последний

...