mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
Merge fd64159011 into 53fb175855
This commit is contained in:
commit
b6d455c6fb
2 changed files with 635 additions and 0 deletions
89
deploy/aws/README.md
Normal file
89
deploy/aws/README.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# Open Design AWS Deployment
|
||||
|
||||
This directory contains an AWS CloudFormation template (`template.yaml`) to deploy Open Design into your AWS environment using Amazon Elastic Container Service (ECS) with AWS Fargate.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
The template provisions a robust, fault-tolerant, and secure architecture for Open Design:
|
||||
|
||||
* **Networking:** A new Virtual Private Cloud (VPC) spanning two Availability Zones, with both Public and Private subnets. Two independent NAT Gateways (one in each AZ) provide highly available outbound internet access.
|
||||
* **Load Balancing:** An internet-facing Application Load Balancer (ALB) routes incoming traffic. It optionally supports HTTPS if a custom domain and ACM certificate are provided.
|
||||
* **Compute:** AWS ECS running on serverless Fargate instances in the private subnets. To protect the file-based SQLite database from concurrent network write corruption, the service hard-codes a single-instance baseline (DesiredCount: 1). However, it leverages the multi-AZ networking primitives for Active-Passive fault tolerance: if a task or zone fails, ECS automatically reschedules the container in the healthy AZ. The task definition includes:
|
||||
* The **Open Design** app container.
|
||||
* An **Nginx Auth Proxy** sidecar container that securely attaches the Open Design API Token to incoming `/api/` requests.
|
||||
* **Storage:** Amazon Elastic File System (EFS) is mounted to the Fargate containers to durably store the Open Design `.od` SQLite database and file artifacts. It is configured with deletion protection (`Retain`) to prevent accidental data loss.
|
||||
* **Security:**
|
||||
* **Secrets Manager:** Securely stores the Open Design API Token, preventing it from being exposed in plain text.
|
||||
* **Security Groups:** Restrict traffic flow. The ALB requires an explicitly configured CIDR — ensure this is your VPN or corporate range to avoid unintended public exposure. Fargate only accepts traffic from the ALB; EFS only accepts traffic from Fargate.
|
||||
* **Logging:** Amazon CloudWatch Log Group captures container logs for easy debugging.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* An AWS Account.
|
||||
* [AWS CLI](https://aws.amazon.com/cli/) installed and configured with appropriate permissions.
|
||||
* (Optional) An ACM Certificate ARN if you want to use a custom domain with HTTPS.
|
||||
|
||||
## Parameters
|
||||
|
||||
When deploying the CloudFormation stack, you can customize the following parameters:
|
||||
|
||||
| Parameter | Description | Default |
|
||||
| :--- | :--- | :--- |
|
||||
| `AllowedSourceIp` | **(Required)** The specific IPv4 CIDR block allowlisted to access the Load Balancer. The ALB requires an explicitly configured CIDR — ensure this is your VPN or corporate range to avoid unintended public exposure. Accepts any valid IPv4 range with a subnet mask between /16 and /32. | *None* |
|
||||
| `ApiToken` | **(Required)** The secure API token used to authenticate requests to the Open Design backend. It is stored securely in AWS Secrets Manager. | |
|
||||
| `DockerImage` | **(Required)** The full repository URI and tag for the Open Design Docker image. You must provide an explicit image as the public Docker Hub baseline is currently unmaintained. | *None* |
|
||||
| `VpcCidr` | The CIDR block for the VPC. | `10.42.0.0/16` |
|
||||
| `PublicSubnet1Cidr` | The CIDR block for Public Subnet 1 (AZ1). | `10.42.1.0/24` |
|
||||
| `PublicSubnet2Cidr` | The CIDR block for Public Subnet 2 (AZ2). | `10.42.3.0/24` |
|
||||
| `PrivateSubnet1Cidr` | The CIDR block for Private Subnet 1 (AZ1). | `10.42.2.0/24` |
|
||||
| `PrivateSubnet2Cidr` | The CIDR block for Private Subnet 2 (AZ2). | `10.42.4.0/24` |
|
||||
| `TaskSize` | The compute size for the Open Design application. Allowed values: `small` (256 CPU, 1024 MiB), `medium` (512 CPU, 2048 MiB), `large` (1024 CPU, 4096 MiB). | `small` |
|
||||
| `TaskCpuArchitecture` | The CPU architecture for the ECS task. Must match the architecture of your Docker image. Allowed values (available as a dropdown): `X86_64`, `ARM64`. | `X86_64` |
|
||||
| `CustomDomainName` | *(Optional)* Your custom domain name (e.g., `design.yourcompany.com`). If provided, you must manually create a DNS CNAME/Alias record pointing to the ALB after deployment. If blank, the default ALB DNS name is used over HTTP. | *None* |
|
||||
| `AcmCertificateArn` | *(Optional)* The ARN of your AWS Certificate Manager (ACM) certificate. **Required** if `CustomDomainName` is provided. | *None* |
|
||||
| `ProxyPort` | The dynamic port used by the Nginx proxy and exposed to the Load Balancer. Must be >= 1024 (unprivileged container). | `8080` |
|
||||
| `AppStoragePath` | The container path where the `.od` SQLite directory is mounted via EFS. | `/app/.od` |
|
||||
|
||||
## Deployment
|
||||
|
||||
You can deploy this stack via the AWS Management Console or the AWS CLI.
|
||||
|
||||
### Using AWS Management Console
|
||||
|
||||
1. Log in to the AWS Management Console and navigate to the **CloudFormation** service.
|
||||
2. Click **Create stack** and select **With new resources (standard)**.
|
||||
3. Under **Prerequisite - Prepare template**, select **Template is ready**.
|
||||
4. Under **Specify template**, select **Upload a template file**, click **Choose file**, and select the `template.yaml` file from this directory.
|
||||
5. Click **Next**.
|
||||
6. Enter a **Stack name** (e.g., `open-design-stack`).
|
||||
7. Fill in the **Parameters** according to your requirements. Note that `ApiToken`, `AllowedSourceIp`, and `DockerImage` are required.
|
||||
8. Click **Next**. Configure any stack options if desired, then click **Next** again.
|
||||
9. Scroll to the bottom of the review page, check the box that says **I acknowledge that AWS CloudFormation might create IAM resources**, and click **Submit**.
|
||||
|
||||
### Using AWS CLI
|
||||
|
||||
1. Open your terminal and navigate to this directory.
|
||||
2. Run the `aws cloudformation deploy` command, passing in the required parameters (`ApiToken`, `AllowedSourceIp`, and `DockerImage`):
|
||||
|
||||
```bash
|
||||
aws cloudformation deploy \
|
||||
--template-file template.yaml \
|
||||
--stack-name open-design-stack \
|
||||
--capabilities CAPABILITY_IAM \
|
||||
--parameter-overrides \
|
||||
ApiToken="YOUR_SECURE_API_TOKEN" \
|
||||
AllowedSourceIp="YOUR_IP_ADDRESS/32" \
|
||||
DockerImage="your-registry/open-design:latest"
|
||||
```
|
||||
|
||||
*Note: If you want to use a custom domain with HTTPS, include the `CustomDomainName` and `AcmCertificateArn` parameters in the `--parameter-overrides` list.*
|
||||
|
||||
## Accessing the Application
|
||||
|
||||
Once the CloudFormation stack creation is complete, go to the **Outputs** tab of the stack in the AWS CloudFormation Console to find the `AlbDnsName` and `AppUrl`.
|
||||
|
||||
**If you did NOT use a custom domain:**
|
||||
Access Open Design directly using the HTTP URL provided in `AppUrl`.
|
||||
|
||||
**If you used a Custom Domain (HTTPS):**
|
||||
You must create a DNS record to route traffic to your new load balancer. Go to your DNS provider (e.g., AWS Route53, Cloudflare) and create a CNAME or Alias (A) record that points your `CustomDomainName` to the `AlbDnsName` output value. Once DNS propagates, you can access Open Design securely via your custom HTTPS domain.
|
||||
546
deploy/aws/template.yaml
Normal file
546
deploy/aws/template.yaml
Normal file
|
|
@ -0,0 +1,546 @@
|
|||
AWSTemplateFormatVersion: '2010-09-09'
|
||||
Description: 'Open Design ECS/Fargate Deployment (VPC, ALB, EFS, Private Access)'
|
||||
|
||||
Parameters:
|
||||
CustomDomainName:
|
||||
Type: String
|
||||
Description: '(Optional) Your custom domain name (e.g., design.yourcompany.com). Leave blank to use the default ALB URL.'
|
||||
Default: ''
|
||||
AcmCertificateArn:
|
||||
Type: String
|
||||
Description: '(Optional) The ARN of your AWS Certificate Manager (ACM) certificate. Required if CustomDomainName is provided.'
|
||||
Default: ''
|
||||
DockerImage:
|
||||
Type: String
|
||||
MinLength: 1
|
||||
Description: 'REQUIRED: The full repository URI and tag for the Open Design Docker image (e.g., your-registry/open-design:latest). You must provide an explicit image as the public Docker Hub baseline is currently unmaintained.'
|
||||
AllowedSourceIp:
|
||||
Type: String
|
||||
AllowedPattern: '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/(1[6-9]|2[0-9]|3[0-2])$'
|
||||
ConstraintDescription: 'Must be a valid IPv4 CIDR range with a subnet mask between /16 and /32 (e.g., 192.168.1.0/24 or 10.0.0.1/32).'
|
||||
MinLength: 1
|
||||
Description: 'REQUIRED: The specific IPv4 CIDR block allowlisted to access the ALB. Ensure this is your VPN or corporate range to avoid unintended public exposure. Accepts any valid IPv4 range with a subnet mask between /16 and /32.'
|
||||
ProxyPort:
|
||||
Type: Number
|
||||
Default: 8080
|
||||
MinValue: 1024
|
||||
MaxValue: 65535
|
||||
Description: 'The dynamic port used by the Nginx proxy (must be >= 1024 for unprivileged container).'
|
||||
AppStoragePath:
|
||||
Type: String
|
||||
Description: 'Container path where the .od SQLite directory is stored.'
|
||||
Default: '/app/.od'
|
||||
ApiToken:
|
||||
Type: String
|
||||
NoEcho: true
|
||||
MinLength: 1
|
||||
Description: 'REQUIRED: The secure API token used to authenticate requests to the Open Design backend. It is stored securely in AWS Secrets Manager.'
|
||||
VpcCidr:
|
||||
Type: String
|
||||
Default: '10.42.0.0/16'
|
||||
Description: 'The CIDR block for the VPC'
|
||||
PublicSubnet1Cidr:
|
||||
Type: String
|
||||
Default: '10.42.1.0/24'
|
||||
Description: 'The CIDR block for Public Subnet 1 (AZ1)'
|
||||
PublicSubnet2Cidr:
|
||||
Type: String
|
||||
Default: '10.42.3.0/24'
|
||||
Description: 'The CIDR block for Public Subnet 2 (AZ2)'
|
||||
PrivateSubnet1Cidr:
|
||||
Type: String
|
||||
Default: '10.42.2.0/24'
|
||||
Description: 'The CIDR block for Private Subnet 1 (AZ1)'
|
||||
PrivateSubnet2Cidr:
|
||||
Type: String
|
||||
Default: '10.42.4.0/24'
|
||||
Description: 'The CIDR block for Private Subnet 2 (AZ2)'
|
||||
TaskSize:
|
||||
Type: String
|
||||
Default: small
|
||||
AllowedValues: [small, medium, large]
|
||||
Description: 'The compute size for the Open Design application.'
|
||||
TaskCpuArchitecture:
|
||||
Type: String
|
||||
Default: X86_64
|
||||
AllowedValues: [ARM64, X86_64]
|
||||
Description: 'The CPU architecture for the Fargate task. Must match how your DockerImage was built (e.g., use X86_64 for standard linux/amd64 images).'
|
||||
|
||||
Mappings:
|
||||
TaskSizes:
|
||||
small:
|
||||
Cpu: '256'
|
||||
Memory: '1024'
|
||||
medium:
|
||||
Cpu: '512'
|
||||
Memory: '2048'
|
||||
large:
|
||||
Cpu: '1024'
|
||||
Memory: '4096'
|
||||
|
||||
Conditions:
|
||||
UseCustomDomain: !Not [!Equals [!Ref CustomDomainName, '']]
|
||||
|
||||
Rules:
|
||||
RequireCertificateWithDomain:
|
||||
RuleCondition: !Not [!Equals [!Ref CustomDomainName, '']]
|
||||
Assertions:
|
||||
- Assert: !Not [!Equals [!Ref AcmCertificateArn, '']]
|
||||
AssertDescription: 'You must provide an AcmCertificateArn when specifying a CustomDomainName.'
|
||||
|
||||
|
||||
Resources:
|
||||
# NETWORKING (VPC, Subnets, NAT)
|
||||
VPC:
|
||||
Type: AWS::EC2::VPC
|
||||
Properties:
|
||||
CidrBlock: !Ref VpcCidr
|
||||
EnableDnsSupport: true
|
||||
EnableDnsHostnames: true
|
||||
|
||||
InternetGateway:
|
||||
Type: AWS::EC2::InternetGateway
|
||||
AttachGateway:
|
||||
Type: AWS::EC2::VPCGatewayAttachment
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
InternetGatewayId: !Ref InternetGateway
|
||||
|
||||
PublicSubnet:
|
||||
Type: AWS::EC2::Subnet
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
CidrBlock: !Ref PublicSubnet1Cidr
|
||||
MapPublicIpOnLaunch: true
|
||||
AvailabilityZone: !Select [ 0, !GetAZs '' ]
|
||||
|
||||
PublicSubnet2:
|
||||
Type: AWS::EC2::Subnet
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
CidrBlock: !Ref PublicSubnet2Cidr
|
||||
MapPublicIpOnLaunch: true
|
||||
AvailabilityZone: !Select [ 1, !GetAZs '' ]
|
||||
|
||||
PublicSubnet2RouteTableAssociation:
|
||||
Type: AWS::EC2::SubnetRouteTableAssociation
|
||||
Properties:
|
||||
SubnetId: !Ref PublicSubnet2
|
||||
RouteTableId: !Ref PublicRouteTable
|
||||
|
||||
PrivateSubnet1:
|
||||
Type: AWS::EC2::Subnet
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
CidrBlock: !Ref PrivateSubnet1Cidr
|
||||
AvailabilityZone: !Select [ 0, !GetAZs '' ]
|
||||
|
||||
PrivateSubnet2:
|
||||
Type: AWS::EC2::Subnet
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
CidrBlock: !Ref PrivateSubnet2Cidr
|
||||
AvailabilityZone: !Select [ 1, !GetAZs '' ]
|
||||
|
||||
NatGatewayEIP:
|
||||
Type: AWS::EC2::EIP
|
||||
Properties:
|
||||
Domain: vpc
|
||||
NatGateway:
|
||||
Type: AWS::EC2::NatGateway
|
||||
Properties:
|
||||
AllocationId: !GetAtt NatGatewayEIP.AllocationId
|
||||
SubnetId: !Ref PublicSubnet
|
||||
|
||||
NatGateway2EIP:
|
||||
Type: AWS::EC2::EIP
|
||||
Properties:
|
||||
Domain: vpc
|
||||
NatGateway2:
|
||||
Type: AWS::EC2::NatGateway
|
||||
Properties:
|
||||
AllocationId: !GetAtt NatGateway2EIP.AllocationId
|
||||
SubnetId: !Ref PublicSubnet2
|
||||
|
||||
PublicRouteTable:
|
||||
Type: AWS::EC2::RouteTable
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
PublicRoute:
|
||||
Type: AWS::EC2::Route
|
||||
DependsOn: AttachGateway
|
||||
Properties:
|
||||
RouteTableId: !Ref PublicRouteTable
|
||||
DestinationCidrBlock: 0.0.0.0/0
|
||||
GatewayId: !Ref InternetGateway
|
||||
PublicSubnetRouteTableAssociation:
|
||||
Type: AWS::EC2::SubnetRouteTableAssociation
|
||||
Properties:
|
||||
SubnetId: !Ref PublicSubnet
|
||||
RouteTableId: !Ref PublicRouteTable
|
||||
|
||||
PrivateRouteTable:
|
||||
Type: AWS::EC2::RouteTable
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
PrivateRoute:
|
||||
Type: AWS::EC2::Route
|
||||
Properties:
|
||||
RouteTableId: !Ref PrivateRouteTable
|
||||
DestinationCidrBlock: 0.0.0.0/0
|
||||
NatGatewayId: !Ref NatGateway
|
||||
PrivateSubnetRouteTableAssociation:
|
||||
Type: AWS::EC2::SubnetRouteTableAssociation
|
||||
Properties:
|
||||
SubnetId: !Ref PrivateSubnet1
|
||||
RouteTableId: !Ref PrivateRouteTable
|
||||
|
||||
PrivateRouteTable2:
|
||||
Type: AWS::EC2::RouteTable
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
PrivateRoute2:
|
||||
Type: AWS::EC2::Route
|
||||
Properties:
|
||||
RouteTableId: !Ref PrivateRouteTable2
|
||||
DestinationCidrBlock: 0.0.0.0/0
|
||||
NatGatewayId: !Ref NatGateway2
|
||||
|
||||
PrivateSubnet2RouteTableAssociation:
|
||||
Type: AWS::EC2::SubnetRouteTableAssociation
|
||||
Properties:
|
||||
SubnetId: !Ref PrivateSubnet2
|
||||
RouteTableId: !Ref PrivateRouteTable2
|
||||
|
||||
# SECURITY GROUPS
|
||||
AlbSecurityGroup:
|
||||
Type: AWS::EC2::SecurityGroup
|
||||
Properties:
|
||||
GroupDescription: Allow restricted inbound traffic to ALB
|
||||
VpcId: !Ref VPC
|
||||
SecurityGroupIngress:
|
||||
- IpProtocol: tcp
|
||||
FromPort: 80
|
||||
ToPort: 80
|
||||
CidrIp: !Ref AllowedSourceIp
|
||||
- IpProtocol: tcp
|
||||
FromPort: 443
|
||||
ToPort: 443
|
||||
CidrIp: !Ref AllowedSourceIp
|
||||
|
||||
FargateSecurityGroup:
|
||||
Type: AWS::EC2::SecurityGroup
|
||||
Properties:
|
||||
GroupDescription: Allow traffic from ALB to Fargate
|
||||
VpcId: !Ref VPC
|
||||
SecurityGroupIngress:
|
||||
- IpProtocol: tcp
|
||||
FromPort: !Ref ProxyPort
|
||||
ToPort: !Ref ProxyPort
|
||||
SourceSecurityGroupId: !Ref AlbSecurityGroup
|
||||
EfsSecurityGroup:
|
||||
Type: AWS::EC2::SecurityGroup
|
||||
Properties:
|
||||
GroupDescription: Allow NFS traffic from Fargate to EFS
|
||||
VpcId: !Ref VPC
|
||||
SecurityGroupIngress:
|
||||
- IpProtocol: tcp
|
||||
FromPort: 2049
|
||||
ToPort: 2049
|
||||
SourceSecurityGroupId: !Ref FargateSecurityGroup
|
||||
|
||||
# 3. STORAGE (EFS for SQLite .od directory)
|
||||
FileSystem:
|
||||
Type: AWS::EFS::FileSystem
|
||||
DeletionPolicy: Retain
|
||||
UpdateReplacePolicy: Retain
|
||||
Properties:
|
||||
Encrypted: true
|
||||
PerformanceMode: generalPurpose
|
||||
|
||||
MountTarget:
|
||||
Type: AWS::EFS::MountTarget
|
||||
Properties:
|
||||
FileSystemId: !Ref FileSystem
|
||||
SubnetId: !Ref PrivateSubnet1
|
||||
SecurityGroups:
|
||||
- !Ref EfsSecurityGroup
|
||||
|
||||
MountTarget2:
|
||||
Type: AWS::EFS::MountTarget
|
||||
Properties:
|
||||
FileSystemId: !Ref FileSystem
|
||||
SubnetId: !Ref PrivateSubnet2
|
||||
SecurityGroups:
|
||||
- !Ref EfsSecurityGroup
|
||||
|
||||
EfsAccessPoint:
|
||||
Type: AWS::EFS::AccessPoint
|
||||
Properties:
|
||||
FileSystemId: !Ref FileSystem
|
||||
PosixUser:
|
||||
Uid: "1001"
|
||||
Gid: "1001"
|
||||
RootDirectory:
|
||||
Path: "/od-data"
|
||||
CreationInfo:
|
||||
OwnerUid: "1001"
|
||||
OwnerGid: "1001"
|
||||
Permissions: "0755"
|
||||
|
||||
# LOAD BALANCER
|
||||
LoadBalancer:
|
||||
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
|
||||
Properties:
|
||||
Subnets:
|
||||
- !Ref PublicSubnet
|
||||
- !Ref PublicSubnet2
|
||||
SecurityGroups:
|
||||
- !Ref AlbSecurityGroup
|
||||
Scheme: internet-facing
|
||||
|
||||
TargetGroup:
|
||||
Type: AWS::ElasticLoadBalancingV2::TargetGroup
|
||||
Properties:
|
||||
VpcId: !Ref VPC
|
||||
Port: !Ref ProxyPort
|
||||
Protocol: HTTP
|
||||
TargetType: ip
|
||||
HealthCheckPath: /api/health
|
||||
|
||||
Listener:
|
||||
Type: AWS::ElasticLoadBalancingV2::Listener
|
||||
Properties:
|
||||
LoadBalancerArn: !Ref LoadBalancer
|
||||
Port: 80
|
||||
Protocol: HTTP
|
||||
DefaultActions: !If
|
||||
- UseCustomDomain
|
||||
- - Type: redirect
|
||||
RedirectConfig:
|
||||
Protocol: HTTPS
|
||||
Port: "443"
|
||||
StatusCode: HTTP_301
|
||||
- - Type: forward
|
||||
TargetGroupArn: !Ref TargetGroup
|
||||
|
||||
HttpsListener:
|
||||
Type: AWS::ElasticLoadBalancingV2::Listener
|
||||
Condition: UseCustomDomain
|
||||
Properties:
|
||||
DefaultActions:
|
||||
- Type: forward
|
||||
TargetGroupArn: !Ref TargetGroup
|
||||
LoadBalancerArn: !Ref LoadBalancer
|
||||
Port: 443
|
||||
Protocol: HTTPS
|
||||
Certificates:
|
||||
- CertificateArn: !Ref AcmCertificateArn
|
||||
|
||||
# COMPUTE (ECS Fargate)
|
||||
EcsCluster:
|
||||
Type: AWS::ECS::Cluster
|
||||
|
||||
TaskExecutionRole:
|
||||
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/AmazonECSTaskExecutionRolePolicy
|
||||
Policies:
|
||||
- PolicyName: ReadSecrets
|
||||
PolicyDocument:
|
||||
Version: '2012-10-17'
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- secretsmanager:GetSecretValue
|
||||
Resource: !Ref ApiTokenSecret
|
||||
- PolicyName: CloudWatchLogs
|
||||
PolicyDocument:
|
||||
Version: '2012-10-17'
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- logs:CreateLogStream
|
||||
- logs:PutLogEvents
|
||||
Resource: !GetAtt LogGroup.Arn
|
||||
|
||||
TaskRole:
|
||||
Type: AWS::IAM::Role
|
||||
Properties:
|
||||
AssumeRolePolicyDocument:
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Principal:
|
||||
Service: ecs-tasks.amazonaws.com
|
||||
Action: sts:AssumeRole
|
||||
Policies:
|
||||
- PolicyName: EfsMountAccess
|
||||
PolicyDocument:
|
||||
Version: '2012-10-17'
|
||||
Statement:
|
||||
- Effect: Allow
|
||||
Action:
|
||||
- elasticfilesystem:ClientMount
|
||||
- elasticfilesystem:ClientWrite
|
||||
Resource: !GetAtt FileSystem.Arn
|
||||
Condition:
|
||||
StringEquals:
|
||||
elasticfilesystem:AccessPointArn: !GetAtt EfsAccessPoint.Arn
|
||||
|
||||
TaskDefinition:
|
||||
Type: AWS::ECS::TaskDefinition
|
||||
Properties:
|
||||
Family: opendesign-app
|
||||
RequiresCompatibilities:
|
||||
- FARGATE
|
||||
NetworkMode: awsvpc
|
||||
Cpu: !FindInMap [TaskSizes, !Ref TaskSize, Cpu]
|
||||
Memory: !FindInMap [TaskSizes, !Ref TaskSize, Memory]
|
||||
ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn
|
||||
TaskRoleArn: !GetAtt TaskRole.Arn
|
||||
RuntimePlatform:
|
||||
CpuArchitecture: !Ref TaskCpuArchitecture
|
||||
OperatingSystemFamily: LINUX
|
||||
Volumes:
|
||||
- Name: efs-storage
|
||||
EFSVolumeConfiguration:
|
||||
FilesystemId: !Ref FileSystem
|
||||
TransitEncryption: ENABLED
|
||||
AuthorizationConfig:
|
||||
AccessPointId: !Ref EfsAccessPoint
|
||||
IAM: ENABLED
|
||||
ContainerDefinitions:
|
||||
- Name: app
|
||||
Image: !Ref DockerImage
|
||||
Environment:
|
||||
- Name: OD_ALLOWED_ORIGINS
|
||||
Value: !If
|
||||
- UseCustomDomain
|
||||
- !Sub 'https://${CustomDomainName}'
|
||||
- !Sub 'http://${LoadBalancer.DNSName}'
|
||||
- Name: OD_BIND_HOST
|
||||
Value: "127.0.0.1"
|
||||
- Name: OD_PORT
|
||||
Value: "7456"
|
||||
Secrets:
|
||||
- Name: OD_API_TOKEN
|
||||
ValueFrom: !Ref ApiTokenSecret
|
||||
MountPoints:
|
||||
- SourceVolume: efs-storage
|
||||
ContainerPath: !Ref AppStoragePath
|
||||
LogConfiguration:
|
||||
LogDriver: awslogs
|
||||
Options:
|
||||
awslogs-group: !Ref LogGroup
|
||||
awslogs-region: !Ref AWS::Region
|
||||
awslogs-stream-prefix: ecs
|
||||
- Name: auth-proxy
|
||||
Image: nginxinc/nginx-unprivileged:1.25-alpine-slim
|
||||
PortMappings:
|
||||
- ContainerPort: !Ref ProxyPort
|
||||
Environment:
|
||||
- Name: PROXY_PORT
|
||||
Value: !Ref ProxyPort
|
||||
- Name: OD_BIND_HOST
|
||||
Value: "127.0.0.1"
|
||||
- Name: OD_WEB_PORT
|
||||
Value: "7456"
|
||||
Secrets:
|
||||
- Name: PROXY_API_TOKEN
|
||||
ValueFrom: !Ref ApiTokenSecret
|
||||
EntryPoint:
|
||||
- /bin/sh
|
||||
- -c
|
||||
Command:
|
||||
- |
|
||||
cat << 'EOF' > /tmp/default.conf.template
|
||||
server {
|
||||
listen ${PROXY_PORT};
|
||||
|
||||
location / {
|
||||
proxy_pass http://${OD_BIND_HOST}:${OD_WEB_PORT};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
|
||||
proxy_set_header X-Forwarded-Host $http_x_forwarded_host;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://${OD_BIND_HOST}:${OD_WEB_PORT};
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
|
||||
proxy_set_header X-Forwarded-Host $http_x_forwarded_host;
|
||||
|
||||
proxy_set_header Authorization "Bearer ${PROXY_API_TOKEN}";
|
||||
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 600s;
|
||||
proxy_send_timeout 600s;
|
||||
proxy_set_header Connection '';
|
||||
http2_push_preload on;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
envsubst '$PROXY_PORT $OD_BIND_HOST $OD_WEB_PORT $PROXY_API_TOKEN' < /tmp/default.conf.template > /etc/nginx/conf.d/default.conf
|
||||
exec nginx -g "daemon off;"
|
||||
LogConfiguration:
|
||||
LogDriver: awslogs
|
||||
Options:
|
||||
awslogs-group: !Ref LogGroup
|
||||
awslogs-region: !Ref AWS::Region
|
||||
awslogs-stream-prefix: ecs-proxy
|
||||
EcsService:
|
||||
Type: AWS::ECS::Service
|
||||
DependsOn: Listener
|
||||
Properties:
|
||||
Cluster: !Ref EcsCluster
|
||||
TaskDefinition: !Ref TaskDefinition
|
||||
LaunchType: FARGATE
|
||||
DesiredCount: 1
|
||||
HealthCheckGracePeriodSeconds: 60
|
||||
NetworkConfiguration:
|
||||
AwsvpcConfiguration:
|
||||
AssignPublicIp: DISABLED
|
||||
Subnets:
|
||||
- !Ref PrivateSubnet1
|
||||
- !Ref PrivateSubnet2
|
||||
SecurityGroups:
|
||||
- !Ref FargateSecurityGroup
|
||||
LoadBalancers:
|
||||
- ContainerName: auth-proxy
|
||||
ContainerPort: !Ref ProxyPort
|
||||
TargetGroupArn: !Ref TargetGroup
|
||||
|
||||
LogGroup:
|
||||
Type: AWS::Logs::LogGroup
|
||||
Properties:
|
||||
LogGroupName: !Sub '/ecs/${AWS::StackName}/opendesign-app'
|
||||
RetentionInDays: 7
|
||||
|
||||
# SECRETS (Secrets Manager)
|
||||
ApiTokenSecret:
|
||||
Type: AWS::SecretsManager::Secret
|
||||
Properties:
|
||||
Description: "API Token for Open Design"
|
||||
SecretString: !Ref ApiToken
|
||||
|
||||
Outputs:
|
||||
AppUrl:
|
||||
Description: 'URL of the Load Balancer/Custom Domain'
|
||||
Value: !If
|
||||
- UseCustomDomain
|
||||
- !Sub 'https://${CustomDomainName}'
|
||||
- !Sub 'http://${LoadBalancer.DNSName}'
|
||||
AlbDnsName:
|
||||
Description: 'The raw DNS name of the Application Load Balancer (use as the target for custom domain CNAME/Alias records).'
|
||||
Value: !GetAtt LoadBalancer.DNSName
|
||||
Loading…
Reference in a new issue