Skip to content

Instantly share code, notes, and snippets.

@MrHertal
Last active July 18, 2024 09:00
Show Gist options
  • Save MrHertal/549b31a18e350b69c7200ae8d26ed691 to your computer and use it in GitHub Desktop.
Save MrHertal/549b31a18e350b69c7200ae8d26ed691 to your computer and use it in GitHub Desktop.
AWS Fargate cluster hosting Flowise exposed through ELB.
AWSTemplateFormatVersion: "2010-09-09"
Description: This template creates resources for Flowise application
Parameters:
Stage:
Description: Prefix of resource names
Type: String
Default: flowise
Mappings:
SubnetConfig:
VPC:
CIDR: "10.0.0.0/16"
PublicOne:
CIDR: "10.0.0.0/24"
PublicTwo:
CIDR: "10.0.1.0/24"
PrivateOne:
CIDR: "10.0.2.0/24"
PrivateTwo:
CIDR: "10.0.3.0/24"
Resources:
####
# VPC related resources
####
VPC:
Type: AWS::EC2::VPC
Properties:
EnableDnsSupport: true
EnableDnsHostnames: true
CidrBlock: !FindInMap ["SubnetConfig", "VPC", "CIDR"]
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, vpc]]
PublicSubnetOne:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref "AWS::Region"
VpcId: !Ref "VPC"
CidrBlock: !FindInMap ["SubnetConfig", "PublicOne", "CIDR"]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, public-subnet-one]]
PublicSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 1
- Fn::GetAZs: !Ref "AWS::Region"
VpcId: !Ref "VPC"
CidrBlock: !FindInMap ["SubnetConfig", "PublicTwo", "CIDR"]
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, public-subnet-two]]
PrivateSubnetOne:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref "AWS::Region"
VpcId: !Ref "VPC"
CidrBlock: !FindInMap ["SubnetConfig", "PrivateOne", "CIDR"]
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, private-subnet-one]]
PrivateSubnetTwo:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: !Select
- 1
- Fn::GetAZs: !Ref "AWS::Region"
VpcId: !Ref "VPC"
CidrBlock: !FindInMap ["SubnetConfig", "PrivateTwo", "CIDR"]
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, private-subnet-two]]
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, igw]]
GatewayAttachement:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref "VPC"
InternetGatewayId: !Ref "InternetGateway"
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref "VPC"
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, public-rt]]
PublicRoute:
Type: AWS::EC2::Route
DependsOn: GatewayAttachement
Properties:
RouteTableId: !Ref "PublicRouteTable"
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref "InternetGateway"
PublicSubnetOneRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetOne
RouteTableId: !Ref PublicRouteTable
PublicSubnetTwoRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnetTwo
RouteTableId: !Ref PublicRouteTable
NatGatewayOneAttachment:
Type: AWS::EC2::EIP
DependsOn: GatewayAttachement
Properties:
Domain: vpc
NatGatewayTwoAttachment:
Type: AWS::EC2::EIP
DependsOn: GatewayAttachement
Properties:
Domain: vpc
NatGatewayOne:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayOneAttachment.AllocationId
SubnetId: !Ref PublicSubnetOne
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, ngw-one]]
NatGatewayTwo:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatGatewayTwoAttachment.AllocationId
SubnetId: !Ref PublicSubnetTwo
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, ngw-two]]
PrivateRouteTableOne:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref "VPC"
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, private-rt-one]]
PrivateRouteOne:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTableOne
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGatewayOne
PrivateRouteTableOneAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTableOne
SubnetId: !Ref PrivateSubnetOne
PrivateRouteTableTwo:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref "VPC"
Tags:
- Key: Name
Value: !Join ["-", [!Ref Stage, private-rt-two]]
PrivateRouteTwo:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTableTwo
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGatewayTwo
PrivateRouteTableTwoAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PrivateRouteTableTwo
SubnetId: !Ref PrivateSubnetTwo
####
# ALB related resources
####
PublicLoadBalancerSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Join ["-", [!Ref Stage, public-lb-sg]]
GroupDescription: Access to the public facing load balancer
VpcId: !Ref "VPC"
SecurityGroupIngress:
- CidrIp: 0.0.0.0/0
IpProtocol: -1
PublicLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Join ["-", [!Ref Stage, public-lb]]
Scheme: internet-facing
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: "30"
Subnets:
- !Ref "PublicSubnetOne"
- !Ref "PublicSubnetTwo"
SecurityGroups: [!Ref "PublicLoadBalancerSG"]
# A dummy target group is used to setup the ALB to just drop traffic
# initially, before any real service target groups have been added.
DummyTargetGroupPublic:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Join ["-", [!Ref Stage, dummy-tg]]
HealthCheckIntervalSeconds: 6
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 2
VpcId: !Ref "VPC"
PublicLoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn:
- PublicLoadBalancer
Properties:
DefaultActions:
- TargetGroupArn: !Ref "DummyTargetGroupPublic"
Type: "forward"
LoadBalancerArn: !Ref "PublicLoadBalancer"
Port: 80
Protocol: HTTP
####
# ECS cluster related resources
####
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Join ["-", [!Ref Stage, ecs-cluster]]
ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Join ["-", [!Ref Stage, container-sg]]
GroupDescription: Access to the containers
VpcId: !Ref "VPC"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
SourceSecurityGroupId: !Ref "PublicLoadBalancerSG"
####
# EFS related resources
####
EFSFileSystemSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Join ["-", [!Ref Stage, efs-sg]]
GroupDescription: Security group for EFS file system
VpcId: !Ref "VPC"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 2049
ToPort: 2049
SourceSecurityGroupId: !Ref ContainerSecurityGroup
EFSFileSystem:
Type: AWS::EFS::FileSystem
Properties:
Encrypted: true
PerformanceMode: generalPurpose
ThroughputMode: bursting
EFSMountTargetOne:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSFileSystem
SubnetId: !Ref PrivateSubnetOne
SecurityGroups:
- !Ref EFSFileSystemSecurityGroup
EFSMountTargetTwo:
Type: AWS::EFS::MountTarget
Properties:
FileSystemId: !Ref EFSFileSystem
SubnetId: !Ref PrivateSubnetTwo
SecurityGroups:
- !Ref EFSFileSystemSecurityGroup
####
# IAM roles related resources
####
AutoScalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: "sts:AssumeRole"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceAutoscaleRole"
ECSRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs.amazonaws.com]
Action: ["sts:AssumeRole"]
Path: /
Policies:
- PolicyName: ecs-service
PolicyDocument:
Statement:
- Effect: Allow
Action:
- "ec2:AttachNetworkInterface"
- "ec2:CreateNetworkInterface"
- "ec2:CreateNetworkInterfacePermission"
- "ec2:DeleteNetworkInterface"
- "ec2:DeleteNetworkInterfacePermission"
- "ec2:Describe*"
- "ec2:DetachNetworkInterface"
- "elasticloadbalancing:DeregisterInstancesFromLoadBalancer"
- "elasticloadbalancing:DeregisterTargets"
- "elasticloadbalancing:Describe*"
- "elasticloadbalancing:RegisterInstancesWithLoadBalancer"
- "elasticloadbalancing:RegisterTargets"
Resource: "*"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
ECSTaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ecs-tasks.amazonaws.com]
Action: ["sts:AssumeRole"]
Path: /
Policies:
- PolicyName: AmazonECSTaskExecutionRolePolicy
PolicyDocument:
Statement:
- Effect: Allow
Action:
# Allow the ECS Tasks to download images from ECR
- "ecr:GetAuthorizationToken"
- "ecr:BatchCheckLayerAvailability"
- "ecr:GetDownloadUrlForLayer"
- "ecr:BatchGetImage"
# Allow the ECS tasks to upload logs to CloudWatch
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: "*"
- Effect: Allow
Action:
- elasticfilesystem:ClientMount
- elasticfilesystem:ClientWrite
- elasticfilesystem:DescribeMountTargets
- elasticfilesystem:DescribeFileSystems
Resource: !GetAtt EFSFileSystem.Arn
####
# Flowise ECS service related resources
####
ServiceFlowise:
Type: AWS::ECS::Service
DependsOn:
- PrivateRouteOne
- PrivateRouteTwo
- ListenerRuleFlowise
Properties:
Cluster: !Ref ECSCluster
TaskDefinition: !Ref TaskDefinitionFlowise
LaunchType: FARGATE
DesiredCount: 2
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
Subnets:
- !Ref PrivateSubnetOne
- !Ref PrivateSubnetTwo
SecurityGroups:
- !Ref ContainerSecurityGroup
LoadBalancers:
- ContainerName: "flowise-service"
ContainerPort: 3000
TargetGroupArn: !Ref TargetGroupFlowise
TaskDefinitionFlowise:
Type: "AWS::ECS::TaskDefinition"
Properties:
ExecutionRoleArn: !GetAtt ECSTaskExecutionRole.Arn
RequiresCompatibilities:
- FARGATE
ContainerDefinitions:
- Name: "flowise-service"
Image: "flowiseai/flowise:latest"
Essential: true
PortMappings:
- ContainerPort: 3000
Protocol: tcp
Environment:
- Name: Port
Value: "3000"
- Name: CORS_ORIGINS
Value: ""
- Name: IFRAME_ORIGINS
Value: ""
- Name: FLOWISE_USERNAME
Value: ""
- Name: FLOWISE_PASSWORD
Value: ""
- Name: FLOWISE_FILE_SIZE_LIMIT
Value: ""
- Name: DEBUG
Value: ""
- Name: DATABASE_PATH
Value: "/root/.flowise"
- Name: DATABASE_TYPE
Value: ""
- Name: DATABASE_PORT
Value: ""
- Name: DATABASE_HOST
Value: ""
- Name: DATABASE_NAME
Value: ""
- Name: DATABASE_USER
Value: ""
- Name: DATABASE_PASSWORD
Value: ""
- Name: DATABASE_SSL
Value: ""
- Name: DATABASE_SSL_KEY_BASE64
Value: ""
- Name: APIKEY_PATH
Value: "/root/.flowise"
- Name: SECRETKEY_PATH
Value: "/root/.flowise"
- Name: FLOWISE_SECRETKEY_OVERWRITE
Value: ""
- Name: LOG_LEVEL
Value: ""
- Name: LOG_PATH
Value: "/root/.flowise/logs"
- Name: BLOB_STORAGE_PATH
Value: "/root/.flowise/storage"
- Name: DISABLE_FLOWISE_TELEMETRY
Value: ""
EntryPoint:
- /bin/sh
- -c
- "sleep 3; flowise start"
MountPoints:
- SourceVolume: efs-volume
ContainerPath: "/root/.flowise"
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref CloudWatchLogsGroupFlowise
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: !Ref Stage
NetworkMode: awsvpc
Memory: "512"
Cpu: "256"
Volumes:
- Name: efs-volume
EFSVolumeConfiguration:
FilesystemId: !Ref EFSFileSystem
RootDirectory: /
TransitEncryption: ENABLED
CloudWatchLogsGroupFlowise:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Ref AWS::StackName
RetentionInDays: 7
TargetGroupFlowise:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId: !Ref VPC
Port: 80
Protocol: HTTP
Matcher:
HttpCode: 200-299
HealthCheckIntervalSeconds: 10
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
TargetType: ip
ListenerRuleFlowise:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref PublicLoadBalancerListener
Priority: 1
Conditions:
- Field: path-pattern
Values:
- /*
Actions:
- TargetGroupArn: !Ref TargetGroupFlowise
Type: forward
Outputs:
ExternalUrl:
Description: The url of Flowise application
Value: !Sub http://${PublicLoadBalancer.DNSName}
@ciscogeek
Copy link

I could not deploy the stack and encountered below error in ECS task in AWS us-west-2 region.

05 July 2024 at 14:53 (UTC+8:00) › Error: Unexpected arguments: /bin/sh, -c, sleep 3; flowise start

@carrgust
Copy link

I had the same error. Any clue?

@MrHertal
Copy link
Author

@ciscogeek @carrgust Thanks for sharing, I've pushed a new revision with a fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment