我正在尝试通过ECS fargate部署由docker + RDS(mysql,port 3306)构建的ruby on rails应用程序(port 3000)。我在cloudwatch上检查了我的数据库和ruby on rails应用程序,它的构建成功。(请确保服务器正在运行,数据库已初始化)
然而,即使构建成功结束,也无法使用公共IP访问我的ruby on rails应用程序。浏览器上仅返回消息->无法访问此站点..或..响应时间过长。我下面的代码有什么问题?我正在等待AWSMaven的帮助。
main.tf
provider "aws" {
region = "ap-northeast-1"
default_tags {
tags = {
Owner = "[email protected]"
Terraform = "true"
}
}
}
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
instance_tenancy = "default"
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "app"
}
}
# Subnet
resource "aws_subnet" "public_1a" {
vpc_id = aws_vpc.main.id
availability_zone = "ap-northeast-1a"
cidr_block = "10.0.1.0/24"
map_public_ip_on_launch = true
tags = {
Name = "app-public-1a"
}
}
resource "aws_subnet" "public_1c" {
vpc_id = aws_vpc.main.id
availability_zone = "ap-northeast-1c"
cidr_block = "10.0.2.0/24"
map_public_ip_on_launch = true
tags = {
Name = "app-public-1c"
}
}
resource "aws_subnet" "public_1d" {
vpc_id = aws_vpc.main.id
availability_zone = "ap-northeast-1d"
cidr_block = "10.0.3.0/24"
map_public_ip_on_launch = true
tags = {
Name = "app-public-1d"
}
}
resource "aws_subnet" "private_1a" {
vpc_id = aws_vpc.main.id
availability_zone = "ap-northeast-1a"
cidr_block = "10.0.10.0/24"
tags = {
Name = "app-public-1a"
}
}
resource "aws_subnet" "private_1c" {
vpc_id = aws_vpc.main.id
availability_zone = "ap-northeast-1c"
cidr_block = "10.0.20.0/24"
tags = {
Name = "app-private-1c"
}
}
resource "aws_subnet" "private_1d" {
vpc_id = aws_vpc.main.id
availability_zone = "ap-northeast-1d"
cidr_block = "10.0.30.0/24"
tags = {
Name = "app-private-1d"
}
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "app"
}
}
# Elastic IP
resource "aws_eip" "nat_1a" {
vpc = true
tags = {
Name = "app-natgw-1a"
}
}
# NAT Gateway
resource "aws_nat_gateway" "nat_1a" {
subnet_id = aws_subnet.public_1a.id
allocation_id = aws_eip.nat_1a.id
tags = {
Name = "app-1a"
}
}
resource "aws_eip" "nat_1c" {
vpc = true
tags = {
Name = "app-natgw-1c"
}
}
resource "aws_nat_gateway" "nat_1c" {
subnet_id = aws_subnet.public_1c.id
allocation_id = aws_eip.nat_1c.id
tags = {
Name = "app-1c"
}
}
resource "aws_eip" "nat_1d" {
vpc = true
tags = {
Name = "app-natgw-1d"
}
}
resource "aws_nat_gateway" "nat_1d" {
subnet_id = aws_subnet.public_1d.id
allocation_id = aws_eip.nat_1d.id
tags = {
Name = "app-1d"
}
}
# Route Table
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
tags = {
Name = "app-public"
}
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
}
# Route
resource "aws_route" "public" {
destination_cidr_block = "0.0.0.0/0"
route_table_id = aws_route_table.public.id
gateway_id = aws_internet_gateway.main.id
}
# Association
resource "aws_route_table_association" "public_1a" {
subnet_id = aws_subnet.public_1a.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "public_1c" {
subnet_id = aws_subnet.public_1c.id
route_table_id = aws_route_table.public.id
}
resource "aws_route_table_association" "public_1d" {
subnet_id = aws_subnet.public_1d.id
route_table_id = aws_route_table.public.id
}
# Route Table (Private)
resource "aws_route_table" "private_1a" {
vpc_id = aws_vpc.main.id
tags = {
Name = "app-private-1a"
}
}
resource "aws_route_table" "private_1c" {
vpc_id = aws_vpc.main.id
tags = {
Name = "app-private-1c"
}
}
resource "aws_route_table" "private_1d" {
vpc_id = aws_vpc.main.id
tags = {
Name = "app-private-1d"
}
}
# Route (Private)
resource "aws_route" "private_1a" {
destination_cidr_block = "0.0.0.0/0"
route_table_id = aws_route_table.private_1a.id
nat_gateway_id = aws_nat_gateway.nat_1a.id
}
resource "aws_route" "private_1c" {
destination_cidr_block = "0.0.0.0/0"
route_table_id = aws_route_table.private_1c.id
nat_gateway_id = aws_nat_gateway.nat_1c.id
}
resource "aws_route" "private_1d" {
destination_cidr_block = "0.0.0.0/0"
route_table_id = aws_route_table.private_1d.id
nat_gateway_id = aws_nat_gateway.nat_1d.id
}
# Association (Private)
resource "aws_route_table_association" "private_1a" {
subnet_id = aws_subnet.private_1a.id
route_table_id = aws_route_table.private_1a.id
}
resource "aws_route_table_association" "private_1c" {
subnet_id = aws_subnet.private_1c.id
route_table_id = aws_route_table.private_1c.id
}
resource "aws_route_table_association" "private_1d" {
subnet_id = aws_subnet.private_1d.id
route_table_id = aws_route_table.private_1d.id
}
# SecurityGroup
resource "aws_security_group" "app-app" {
name = "app-app"
description = "app-app"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "app-app"
}
}
# SecurityGroup
resource "aws_security_group" "app-http" {
name = "app-http"
description = "aws_security_group"
vpc_id = aws_vpc.main.id
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "app-http"
}
}
# SecurityGroup
resource "aws_security_group" "app-db" {
name = "app-db"
description = "aws_security_group"
vpc_id = aws_vpc.main.id
ingress {
from_port = 3306
to_port = 3306
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "app-db"
}
}
resource "aws_ecs_task_definition" "main" {
family = "app"
network_mode = "awsvpc"
cpu = "1024"
memory = "2048"
requires_compatibilities = ["FARGATE"]
execution_role_arn = aws_iam_role.main.arn
runtime_platform {
operating_system_family = "LINUX"
cpu_architecture = "X86_64"
}
container_definitions = jsonencode([
{
"name": "web",
"image": RUBY ON RAILS IMGAE ECR URL IS HERE..,
"portMappings": [
{
"containerPort": 3000,
"hostPort": 3000,
"protocol": "tcp"
}
],
"essential": true,
"command": [
"bash",
"-c",
"rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
],
"network_configuration": {
"awsvpc_configuration": {
"subnets": [aws_subnet.public_1a.id, aws_subnet.public_1c.id, aws_subnet.public_1d.id],
"security_groups": [aws_security_group.app-app.id]
}
},
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "app",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "ecs"
}
},
"environment": [
{
"name": "RAILS_RELATIVE_URL_ROOT",
"value": "/"
}
]
}
])
}
# ECS Cluster
resource "aws_ecs_cluster" "main" {
name = "app"
}
# ECS Service
resource "aws_ecs_service" "main" {
name = "app"
launch_type = "FARGATE"
desired_count = 1
cluster = aws_ecs_cluster.main.name
task_definition = aws_ecs_task_definition.main.arn
health_check_grace_period_seconds = 90
deployment_controller {
type = "ECS"
}
deployment_maximum_percent = 200
deployment_minimum_healthy_percent = 100
lifecycle {
ignore_changes = [desired_count, task_definition]
}
load_balancer {
target_group_arn = aws_alb_target_group.main.arn
container_name = "app-app"
container_port = 3000
}
network_configuration {
subnets = [aws_subnet.public_1a.id, aws_subnet.public_1c.id, aws_subnet.public_1d.id]
security_groups = [
aws_security_group.app-app.id
]
assign_public_ip = true
}
}
# ECR
resource "aws_ecr_repository" "main" {
name = "app"
image_tag_mutability = "MUTABLE"
image_scanning_configuration {
scan_on_push = true
}
}
# IAM ROLE
resource "aws_iam_role" "main" {
name = "app"
assume_role_policy = data.aws_iam_policy_document.role_policy.json
inline_policy {
name = "EcsTaskExecutionPolicy"
policy = data.aws_iam_policy_document.ecs_task_policy.json
}
}
data "aws_iam_policy_document" "ecs_task_policy" {
statement {
sid = "EcsTaskPolicy"
actions = [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
]
resources = [
"*"
]
}
statement {
actions = [
"ecr:GetAuthorizationToken"
]
resources = [
"*"
]
}
statement {
actions = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
resources = [
"*"
]
}
}
data "aws_iam_policy_document" "role_policy" {
statement {
actions = ["sts:AssumeRole"]
principals {
type = "Service"
identifiers = ["ecs-tasks.amazonaws.com"]
}
}
}
# ALB
resource "aws_alb" "main" {
name = "app"
internal = false
enable_deletion_protection = false
security_groups = [
aws_security_group.app-http.id
]
subnets = [aws_subnet.public_1a.id, aws_subnet.public_1c.id, aws_subnet.public_1d.id]
}
# Listener
resource "aws_alb_listener" "main" {
port = "80"
protocol = "HTTP"
load_balancer_arn = aws_alb.main.arn
default_action {
type = "forward"
target_group_arn = aws_alb_target_group.main.arn
}
}
resource "aws_alb_target_group" "main" {
name = "app-targetgroup2"
port = 3000
protocol = "HTTP"
depends_on = [aws_alb.main]
target_type = "ip"
vpc_id = aws_vpc.main.id
deregistration_delay = 15
health_check {
interval = 120
path = "/"
port = "3000"
protocol = "HTTP"
timeout = 110
unhealthy_threshold = 2
matcher = 200
}
lifecycle {
create_before_destroy = true
}
}
# ALB Listener Rule
resource "aws_alb_listener_rule" "main" {
listener_arn = aws_alb_listener.main.arn
priority = 100
depends_on = [aws_alb_target_group.main]
condition {
path_pattern {
values = ["/"]
}
}
action {
type = "forward"
target_group_arn = aws_alb_target_group.main.arn
}
}
*/
resource "aws_cloudwatch_log_group" "name" {
name = "app"
retention_in_days = 7
}
resource "aws_db_subnet_group" "main" {
name = "app-db-subnet"
description = "app-db-subnet"
subnet_ids = [
aws_subnet.public_1a.id,
aws_subnet.public_1c.id,
aws_subnet.public_1d.id
]
tags = {
Name = "app-db-subnet-group"
}
}
resource "aws_db_instance" "main" {
identifier = "app-db"
engine = "mysql"
engine_version = "5.7"
instance_class = "db.t2.micro"
allocated_storage = 20
storage_type = "gp2"
db_name = "app_rails_production"
username = "root"
password = "password"
port = 3306
multi_az = true
skip_final_snapshot = true
vpc_security_group_ids = [aws_security_group.app-db.id]
db_subnet_group_name = aws_db_subnet_group.main.name
}
字符串
docker-compose.yml(在ruby on rails中)(在ecs中构建没有问题)
version: '3'
services:
web:
build: .
command: /bin/sh -c "rm -f tmp/pids/server.pid &&
bundle exec rails s -p 3000 -b '0.0.0.0'"
ports:
- "3000:3000"
platform: linux/x86_64
db:
image: mysql
ports:
- 3306:3306
platform: linux/x86_64
volumes:
- db-data:/var/lib/mysql
volumes:
db-data:
driver: local
型
database.yml(在ruby on rails中)(在ecs中构建没有问题)
default: &default
adapter: mysql2
encoding: utf8
username: root
password: password
port: 3306
pool: 5
development:
<<: *default
database: development
test:
<<: *default
database: test
production:
<<: *default
database: production
型
DOCKERFILE(在ruby on rails中)(在ecs中构建没有问题)
FROM --platform=linux/amd64 ubuntu
FROM ruby:2.6.6
ENV LANG="C.UTF-8"
WORKDIR /usr/src/app
RUN set -eu \
&& curl -sSL https://deb.nodesource.com/setup_16.x | bash - \
&& apt-get update \
&& apt-get install -y \
default-mysql-client \
ftp \
vim \
shared-mime-info \
libjemalloc-dev \
libjemalloc2 \
libappindicator1 \
fonts-liberation \
libappindicator3-1 \
libasound2 \
libnspr4 \
libnss3 \
libxss1 \
libu2f-udev \
libvulkan1 \
xdg-utils \
libgbm1 \
nodejs \
yarn \
cmake \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* \
&& ln -nfs /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so.2
COPY Gemfile Gemfile.lock ./
RUN gem update --system 3.2.3
RUN gem install bundler -v 2.4.22
RUN gem install nokogiri --platform=ruby
RUN gem update --system 3.2.3
RUN gem install mysql2
RUN bundle config set force_ruby_platform true
RUN bundle install
RUN bundle update
ARG BUNDLE_ENTERPRISE__CONTRIBSYS__COM
ARG BUNDLE_GITHUB__COM
COPY . .
RUN mkdir -p \
tmp/cache \
tmp/sockets \
tmp/pids \
tmp/cache/assets
CMD /bin/sh -c "rm -f tmp/pids/server.pid && rm -f tmp/mysql.sock && bundle exec rails s -p 3000 -b '0.0.0.0'"
型
我所尝试的:
删除health_check -> Task failed ELB health checks in (target-group .....) ERROR
删除load_balance设置-> Scaling activity initiated by ... ERROR
删除私有配置->没有更改
我觉得路由、安全组或端口设置有问题。欢迎任何建议。谢谢。
1条答案
按热度按时间ubbxdtey1#
名为
app-app
的安全组仅允许端口80
上的入站流量,但ECS任务侦听端口3000
。您需要更改该安全组以允许端口3000
而不是端口80
上的入站流量。修复后,负载均衡器应该能够到达ECS容器,并且希望健康检查将开始通过。如果健康检查开始通过,那么您应该能够在浏览器中转到负载均衡器的DNS名称以访问您的应用程序。
此外,在解决安全组问题后,您应该能够直接转到Web浏览器中的应用程序,位于Fargate任务的公共IP地址,如:
http://public-ip:3000
。请注意,如果您直接转到任务的IP地址,而不是通过负载均衡器,则需要将容器端口:3000
附加到地址。您还有一个额外的
aws_alb_listener_rule
资源,它执行与默认侦听器规则相同的操作,因此可以直接将其删除。您在
aws_route_table" "public"
资源的route
块中定义了一次到Internet Gateway的路由,然后在单独的"aws_route" "public"
资源中定义了一次。这不仅是冗余的,而且可能会导致在两个资源中定义相同内容的问题。由于Terraform正在管理aws_route_table
资源,因此您应该删除冗余的aws_route
资源。**注意:**如果删除冗余资源,我会连续运行
terraform apply
几次,直到它说没有发现任何更改。