Add AWS CDK deployment scripts by @kazuki306 (#1193)
This commit is contained in:
commit
ca576c1bd9
33 changed files with 5626 additions and 7 deletions
|
|
@ -1 +1,2 @@
|
||||||
.venv/
|
.venv/
|
||||||
|
**/aws
|
||||||
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
|
@ -40,5 +40,5 @@ jobs:
|
||||||
run: docker compose up --exit-code-from tests tests result_backend broker celeryworker db --build
|
run: docker compose up --exit-code-from tests tests result_backend broker celeryworker db --build
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Stop services
|
# - name: Stop services
|
||||||
run: docker compose down
|
# run: docker compose down
|
||||||
|
|
|
||||||
20
cdk.Dockerfile
Normal file
20
cdk.Dockerfile
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
FROM --platform=linux/amd64 python:3.10-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Install Poetry
|
||||||
|
RUN apt-get update && apt-get install gcc g++ curl build-essential postgresql-server-dev-all -y
|
||||||
|
RUN curl -sSL https://install.python-poetry.org | python3 -
|
||||||
|
# # Add Poetry to PATH
|
||||||
|
ENV PATH="${PATH}:/root/.local/bin"
|
||||||
|
# # Copy the pyproject.toml and poetry.lock files
|
||||||
|
COPY poetry.lock pyproject.toml ./
|
||||||
|
# Copy the rest of the application codes
|
||||||
|
COPY ./ ./
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
RUN poetry config virtualenvs.create false && poetry install --no-interaction --no-ansi
|
||||||
|
|
||||||
|
RUN poetry add pymysql==1.0.2
|
||||||
|
|
||||||
|
CMD ["sh", "./container-cmd-cdk.sh"]
|
||||||
3
container-cmd-cdk.sh
Normal file
3
container-cmd-cdk.sh
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export LANGFLOW_DATABASE_URL="mysql+pymysql://${username}:${password}@${host}:3306/${dbname}"
|
||||||
|
# echo $LANGFLOW_DATABASE_URL
|
||||||
|
uvicorn --factory src.backend.langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --log-level debug
|
||||||
|
|
@ -1,28 +1,35 @@
|
||||||
version: "3"
|
version: "3"
|
||||||
|
networks:
|
||||||
|
langflow:
|
||||||
|
|
||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
build:
|
build:
|
||||||
context: ./
|
context: ./
|
||||||
dockerfile: ./dev.Dockerfile
|
dockerfile: ./dev.Dockerfile
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
ports:
|
ports:
|
||||||
- "7860:7860"
|
- "7860:7860"
|
||||||
volumes:
|
volumes:
|
||||||
- ./:/app
|
- ./:/app
|
||||||
command: bash -c "uvicorn --factory src.backend.langflow.main:create_app --host 0.0.0.0 --port 7860 --reload"
|
command: bash -c "uvicorn --factory src.backend.langflow.main:create_app --host 0.0.0.0 --port 7860 --reload"
|
||||||
|
networks:
|
||||||
|
- langflow
|
||||||
frontend:
|
frontend:
|
||||||
build:
|
build:
|
||||||
context: ./src/frontend
|
context: ./src/frontend
|
||||||
dockerfile: ./dev.Dockerfile
|
dockerfile: ./cdk.Dockerfile
|
||||||
args:
|
args:
|
||||||
- BACKEND_URL=http://backend:7860
|
- BACKEND_URL=http://backend:7860
|
||||||
environment:
|
environment:
|
||||||
- VITE_PROXY_TARGET=http://backend:7860
|
- VITE_PROXY_TARGET=http://backend:7860
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "8080:3000"
|
||||||
volumes:
|
volumes:
|
||||||
- ./src/frontend/public:/home/node/app/public
|
- ./src/frontend/public:/home/node/app/public
|
||||||
- ./src/frontend/src:/home/node/app/src
|
- ./src/frontend/src:/home/node/app/src
|
||||||
- ./src/frontend/package.json:/home/node/app/package.json
|
- ./src/frontend/package.json:/home/node/app/package.json
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
networks:
|
||||||
|
- langflow
|
||||||
|
|
|
||||||
11
scripts/aws/.env.example
Normal file
11
scripts/aws/.env.example
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Description: Example of .env file
|
||||||
|
# Usage: Copy this file to .env and change the values
|
||||||
|
# according to your needs
|
||||||
|
# Do not commit .env file to git
|
||||||
|
# Do not change .env.example file
|
||||||
|
# You can set up a superuser's username and password
|
||||||
|
# If there is no need for user management, set LANGFLOW_AUTO_LOGIN=true and delete LANGFLOW_SUPERUSER and LANGFLOW_SUPERUSER_PASSWORD.
|
||||||
|
|
||||||
|
LANGFLOW_AUTO_LOGIN=false
|
||||||
|
LANGFLOW_SUPERUSER=admin
|
||||||
|
LANGFLOW_SUPERUSER_PASSWORD=123456
|
||||||
9
scripts/aws/.gitignore
vendored
Normal file
9
scripts/aws/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
*.js
|
||||||
|
!jest.config.js
|
||||||
|
*.d.ts
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# CDK asset staging directory
|
||||||
|
.cdk.staging
|
||||||
|
cdk.out
|
||||||
|
!/lib
|
||||||
6
scripts/aws/.npmignore
Normal file
6
scripts/aws/.npmignore
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
*.ts
|
||||||
|
!*.d.ts
|
||||||
|
|
||||||
|
# CDK asset staging directory
|
||||||
|
.cdk.staging
|
||||||
|
cdk.out
|
||||||
54
scripts/aws/README.ja.md
Normal file
54
scripts/aws/README.ja.md
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Langflow on AWS
|
||||||
|
|
||||||
|
**想定時間**: 30 分
|
||||||
|
|
||||||
|
## 説明
|
||||||
|
Langflow on AWS では、 [AWS Cloud Development Kit](https://aws.amazon.com/cdk/?nc2=type_a) (CDK) を用いて Langflow を AWS 上にデプロイする方法を学べます。
|
||||||
|
このチュートリアルは、AWS アカウントと AWS に関する基本的な知識を有していることを前提としています。
|
||||||
|
|
||||||
|
作成するアプリケーションのアーキテクチャです。
|
||||||
|

|
||||||
|
AWS CDK によって [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/?nc1=h_ls)、[AWS Fargate](https://aws.amazon.com/fargate/?nc2=type_a)、[Amazon Aurora](https://aws.amazon.com/rds/aurora/?nc2=type_a) を作成します。
|
||||||
|
Auroraのシークレットは [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/?nc2=type_a) によって管理されます。
|
||||||
|
Fargate のタスクはフロントエンドとバックエンドに分かれており、サービス検出によって通信します。
|
||||||
|
リソースをデプロイするだけであれば、上記の各サービスについて深い知識は必要ありません。
|
||||||
|
|
||||||
|
# 環境構築とデプロイ方法
|
||||||
|
1. [AWS CloudShell](https://us-east-1.console.aws.amazon.com/cloudshell/home?region=us-east-1)を開きます。
|
||||||
|
|
||||||
|
1. 以下のコマンドを実行します。
|
||||||
|
```shell
|
||||||
|
git clone https://github.com/aws-samples/cloud9-setup-for-prototyping
|
||||||
|
cd cloud9-setup-for-prototyping
|
||||||
|
./bin/bootstrap
|
||||||
|
```
|
||||||
|
|
||||||
|
1. `Done!` と表示されたら [AWS Cloud9](https://us-east-1.console.aws.amazon.com/cloud9control/home?region=us-east-1#/) から `cloud9-for-prototyping` を開きます。
|
||||||
|

|
||||||
|
|
||||||
|
1. 以下のコマンドを実行します。
|
||||||
|
```shell
|
||||||
|
git clone -b aws-cdk https://github.com/logspace-ai/langflow.git
|
||||||
|
cd langflow/scripts/aws
|
||||||
|
cp .env.example .env # 環境設定を変える場合はこのファイル(.env)を編集してください。
|
||||||
|
npm ci
|
||||||
|
cdk bootstrap
|
||||||
|
cdk deploy
|
||||||
|
```
|
||||||
|
1. 表示される URL にアクセスします。
|
||||||
|
```shell
|
||||||
|
Outputs:
|
||||||
|
LangflowAppStack.NetworkURLXXXXXX = http://alb-XXXXXXXXXXX.elb.amazonaws.com
|
||||||
|
```
|
||||||
|
1. サインイン画面でユーザー名とパスワードを入力します。`.env`ファイルでユーザー名とパスワードを設定していない場合、ユーザー名は`admin`、パスワードは`123456`で設定されます。
|
||||||
|

|
||||||
|
|
||||||
|
# 環境の削除
|
||||||
|
1. `Cloud9` で以下のコマンドを実行します。
|
||||||
|
```shell
|
||||||
|
bash delete-resources.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
1. [AWS CloudFormation](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/getting-started)を開き、`aws-cloud9-cloud9-for-prototyping-XXXX` を選択して削除します。
|
||||||
|

|
||||||
53
scripts/aws/README.md
Normal file
53
scripts/aws/README.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Deploy Langflow on AWS
|
||||||
|
|
||||||
|
**Duration**: 30 minutes
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
In this tutorial, you will learn how to deploy langflow on AWS using [AWS Cloud Development Kit](https://aws.amazon.com/cdk/?nc2=type_a) (CDK).
|
||||||
|
This tutorial assumes you have an AWS account and basic knowledge of AWS.
|
||||||
|
|
||||||
|
The architecture of the application to be created:
|
||||||
|

|
||||||
|
|
||||||
|
[Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/?nc1=h_ls), [AWS Fargate](https://aws.amazon.com/fargate/?nc2=type_a) and [Amazon Aurora](https://aws.amazon.com/rds/aurora/?nc2=type_a) are created by AWS CDK.
|
||||||
|
The aurora's secrets are managed by [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/?nc2=type_a).
|
||||||
|
The Fargate task is divided into a frontend and a backend, which communicate through service discovery.
|
||||||
|
If you just want to deploy resources, you do not need in-depth knowledge of each of the above services.
|
||||||
|
|
||||||
|
# How to set up your environment and deploy langflow
|
||||||
|
|
||||||
|
1. Open [AWS CloudShell](https://us-east-1.console.aws.amazon.com/cloudshell/home?region=us-east-1).
|
||||||
|
1. Run the following commands in Cloudshell:
|
||||||
|
```shell
|
||||||
|
git clone https://github.com/aws-samples/cloud9-setup-for-prototyping
|
||||||
|
cd cloud9-setup-for-prototyping
|
||||||
|
./bin/bootstrap
|
||||||
|
```
|
||||||
|
1. When you see `Done!` in Cloudshell, open `cloud9-for-prototyping` from [AWS Cloud9](https://us-east-1.console.aws.amazon.com/cloud9control/home?region=us-east-1#/).
|
||||||
|

|
||||||
|
1. Run the following command in the Cloud9 terminal.
|
||||||
|
```shell
|
||||||
|
git clone -b aws-cdk https://github.com/logspace-ai/langflow.git
|
||||||
|
cd langflow/scripts/aws
|
||||||
|
cp .env.example .env # Edit this file if you need environment settings
|
||||||
|
npm ci
|
||||||
|
cdk bootstrap
|
||||||
|
cdk deploy
|
||||||
|
```
|
||||||
|
1. Access the URL displayed.
|
||||||
|
```shell
|
||||||
|
Outputs:
|
||||||
|
LangflowAppStack.NetworkURLXXXXXX = http://alb-XXXXXXXXXXX.elb.amazonaws.com
|
||||||
|
```
|
||||||
|
1. Enter your user name and password to sign in. If you have not set a user name and password in your `.env` file, the user name will be set to `admin` and the password to `123456`.
|
||||||
|

|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
|
||||||
|
1. Run the following command in the Cloud9 terminal.
|
||||||
|
```shell
|
||||||
|
bash delete-resources.sh
|
||||||
|
```
|
||||||
|
1. Open [AWS CloudFormation](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/getting-started), select `aws-cloud9-cloud9-for-prototyping-XXXX` and delete it.
|
||||||
|

|
||||||
21
scripts/aws/bin/cdk.ts
Normal file
21
scripts/aws/bin/cdk.ts
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
import 'source-map-support/register';
|
||||||
|
import * as cdk from 'aws-cdk-lib';
|
||||||
|
import { LangflowAppStack } from '../lib/cdk-stack';
|
||||||
|
|
||||||
|
const app = new cdk.App();
|
||||||
|
new LangflowAppStack(app, 'LangflowAppStack', {
|
||||||
|
/* If you don't specify 'env', this stack will be environment-agnostic.
|
||||||
|
* Account/Region-dependent features and context lookups will not work,
|
||||||
|
* but a single synthesized template can be deployed anywhere. */
|
||||||
|
|
||||||
|
/* Uncomment the next line to specialize this stack for the AWS Account
|
||||||
|
* and Region that are implied by the current CLI configuration. */
|
||||||
|
// env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
|
||||||
|
|
||||||
|
/* Uncomment the next line if you know exactly what Account and Region you
|
||||||
|
* want to deploy the stack to. */
|
||||||
|
// env: { account: '123456789012', region: 'us-east-1' },
|
||||||
|
|
||||||
|
/* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */
|
||||||
|
});
|
||||||
55
scripts/aws/cdk.json
Normal file
55
scripts/aws/cdk.json
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
{
|
||||||
|
"app": "npx ts-node --prefer-ts-exts bin/cdk.ts",
|
||||||
|
"watch": {
|
||||||
|
"include": [
|
||||||
|
"**"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"README.md",
|
||||||
|
"cdk*.json",
|
||||||
|
"**/*.d.ts",
|
||||||
|
"**/*.js",
|
||||||
|
"tsconfig.json",
|
||||||
|
"package*.json",
|
||||||
|
"yarn.lock",
|
||||||
|
"node_modules",
|
||||||
|
"test"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
|
||||||
|
"@aws-cdk/core:checkSecretUsage": true,
|
||||||
|
"@aws-cdk/core:target-partitions": [
|
||||||
|
"aws",
|
||||||
|
"aws-cn"
|
||||||
|
],
|
||||||
|
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
|
||||||
|
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
|
||||||
|
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
|
||||||
|
"@aws-cdk/aws-iam:minimizePolicies": true,
|
||||||
|
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
|
||||||
|
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
|
||||||
|
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
|
||||||
|
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
|
||||||
|
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
|
||||||
|
"@aws-cdk/core:enablePartitionLiterals": true,
|
||||||
|
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
|
||||||
|
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
|
||||||
|
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
|
||||||
|
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
|
||||||
|
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
|
||||||
|
"@aws-cdk/aws-route53-patters:useCertificate": true,
|
||||||
|
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
|
||||||
|
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
|
||||||
|
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
|
||||||
|
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
|
||||||
|
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
|
||||||
|
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
|
||||||
|
"@aws-cdk/aws-redshift:columnId": true,
|
||||||
|
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
|
||||||
|
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
|
||||||
|
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
|
||||||
|
"@aws-cdk/aws-kms:aliasNameRef": true,
|
||||||
|
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true
|
||||||
|
}
|
||||||
|
}
|
||||||
3
scripts/aws/delete-docker-images.sh
Normal file
3
scripts/aws/delete-docker-images.sh
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
docker stop $(docker ps -aq)
|
||||||
|
docker rm $(docker ps -aq)
|
||||||
|
docker rmi -f $(docker images -aq)
|
||||||
4
scripts/aws/delete-resources.sh
Normal file
4
scripts/aws/delete-resources.sh
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
aws cloudformation delete-stack --stack-name LangflowAppStack
|
||||||
|
aws ecr delete-repository --repository-name langflow-backend-repository --force
|
||||||
|
aws ecr delete-repository --repository-name langflow-frontend-repository --force
|
||||||
|
# aws ecr describe-repositories --output json | jq -re ".repositories[].repositoryName"
|
||||||
BIN
scripts/aws/img/langflow-archi.png
Normal file
BIN
scripts/aws/img/langflow-archi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 291 KiB |
BIN
scripts/aws/img/langflow-cfn.png
Normal file
BIN
scripts/aws/img/langflow-cfn.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
BIN
scripts/aws/img/langflow-cloud9-en.png
Normal file
BIN
scripts/aws/img/langflow-cloud9-en.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
BIN
scripts/aws/img/langflow-signin.png
Normal file
BIN
scripts/aws/img/langflow-signin.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 166 KiB |
8
scripts/aws/jest.config.js
Normal file
8
scripts/aws/jest.config.js
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
module.exports = {
|
||||||
|
testEnvironment: 'node',
|
||||||
|
roots: ['<rootDir>/test'],
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.tsx?$': 'ts-jest'
|
||||||
|
}
|
||||||
|
};
|
||||||
64
scripts/aws/lib/cdk-stack.ts
Normal file
64
scripts/aws/lib/cdk-stack.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
import * as cdk from 'aws-cdk-lib';
|
||||||
|
import { Construct } from 'constructs';
|
||||||
|
import * as ecs from 'aws-cdk-lib/aws-ecs'
|
||||||
|
|
||||||
|
import { Network, EcrRepository, FrontEndCluster, BackEndCluster, Rds, EcsIAM } from './construct';
|
||||||
|
// import * as sqs from 'aws-cdk-lib/aws-sqs';
|
||||||
|
|
||||||
|
export class LangflowAppStack extends cdk.Stack {
|
||||||
|
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
|
||||||
|
super(scope, id, props);
|
||||||
|
// Arch
|
||||||
|
const arch = ecs.CpuArchitecture.X86_64
|
||||||
|
|
||||||
|
// VPC
|
||||||
|
const { vpc, cluster, alb, targetGroup, cloudmapNamespace, ecsFrontSG, ecsBackSG, dbSG, albSG, backendLogGroup, frontendLogGroup} = new Network(this, 'Network')
|
||||||
|
|
||||||
|
// ECR
|
||||||
|
const { ecrFrontEndRepository,ecrBackEndRepository} = new EcrRepository(this, 'Ecr', {
|
||||||
|
cloudmapNamespace:cloudmapNamespace,
|
||||||
|
arch:arch
|
||||||
|
})
|
||||||
|
|
||||||
|
// RDS
|
||||||
|
// VPCとSGのリソース情報をPropsとして引き渡す
|
||||||
|
const { rdsCluster } = new Rds(this, 'Rds', { vpc, dbSG })
|
||||||
|
|
||||||
|
// IAM
|
||||||
|
const { frontendTaskRole, frontendTaskExecutionRole, backendTaskRole, backendTaskExecutionRole } = new EcsIAM(this, 'EcsIAM',{
|
||||||
|
rdsCluster:rdsCluster
|
||||||
|
})
|
||||||
|
|
||||||
|
const backendService = new BackEndCluster(this, 'backend', {
|
||||||
|
cluster:cluster,
|
||||||
|
ecsBackSG:ecsBackSG,
|
||||||
|
ecrBackEndRepository:ecrBackEndRepository,
|
||||||
|
backendTaskRole:backendTaskRole,
|
||||||
|
backendTaskExecutionRole:backendTaskExecutionRole,
|
||||||
|
backendLogGroup:backendLogGroup,
|
||||||
|
cloudmapNamespace:cloudmapNamespace,
|
||||||
|
rdsCluster:rdsCluster,
|
||||||
|
alb:alb,
|
||||||
|
arch:arch
|
||||||
|
})
|
||||||
|
backendService.node.addDependency(rdsCluster);
|
||||||
|
|
||||||
|
const frontendService = new FrontEndCluster(this, 'frontend',{
|
||||||
|
cluster:cluster,
|
||||||
|
ecsFrontSG:ecsFrontSG,
|
||||||
|
ecrFrontEndRepository:ecrFrontEndRepository,
|
||||||
|
targetGroup: targetGroup,
|
||||||
|
backendServiceName: backendService.backendServiceName,
|
||||||
|
frontendTaskRole: frontendTaskRole,
|
||||||
|
frontendTaskExecutionRole: frontendTaskExecutionRole,
|
||||||
|
frontendLogGroup: frontendLogGroup,
|
||||||
|
cloudmapNamespace: cloudmapNamespace,
|
||||||
|
arch:arch
|
||||||
|
})
|
||||||
|
frontendService.node.addDependency(backendService);
|
||||||
|
|
||||||
|
|
||||||
|
// S3+CloudFront
|
||||||
|
// new Web(this,'Cloudfront-S3')
|
||||||
|
}
|
||||||
|
}
|
||||||
105
scripts/aws/lib/construct/backend.ts
Normal file
105
scripts/aws/lib/construct/backend.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
import { Duration } from 'aws-cdk-lib'
|
||||||
|
import { Construct } from 'constructs';
|
||||||
|
import {
|
||||||
|
aws_ec2 as ec2,
|
||||||
|
aws_ecs as ecs,
|
||||||
|
aws_ecr as ecr,
|
||||||
|
aws_rds as rds,
|
||||||
|
aws_servicediscovery as servicediscovery,
|
||||||
|
aws_iam as iam,
|
||||||
|
aws_logs as logs,
|
||||||
|
aws_elasticloadbalancingv2 as elb,
|
||||||
|
} from 'aws-cdk-lib';
|
||||||
|
import * as dotenv from 'dotenv';
|
||||||
|
const path = require('path');
|
||||||
|
dotenv.config({path: path.join(__dirname, "../../.env")});
|
||||||
|
|
||||||
|
interface BackEndProps {
|
||||||
|
cluster: ecs.Cluster
|
||||||
|
ecsBackSG:ec2.SecurityGroup
|
||||||
|
ecrBackEndRepository:ecr.Repository
|
||||||
|
backendTaskRole: iam.Role;
|
||||||
|
backendTaskExecutionRole: iam.Role;
|
||||||
|
backendLogGroup: logs.LogGroup;
|
||||||
|
cloudmapNamespace: servicediscovery.PrivateDnsNamespace;
|
||||||
|
rdsCluster:rds.DatabaseCluster
|
||||||
|
alb:elb.IApplicationLoadBalancer
|
||||||
|
arch:ecs.CpuArchitecture
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BackEndCluster extends Construct {
|
||||||
|
readonly backendServiceName: string
|
||||||
|
|
||||||
|
constructor(scope: Construct, id: string, props:BackEndProps) {
|
||||||
|
super(scope, id)
|
||||||
|
const containerPort = 7860
|
||||||
|
// Secrets ManagerからDB認証情報を取ってくる
|
||||||
|
const secretsDB = props.rdsCluster.secret!;
|
||||||
|
|
||||||
|
// Create Backend Fargate Service
|
||||||
|
const backendTaskDefinition = new ecs.FargateTaskDefinition(
|
||||||
|
this,
|
||||||
|
'BackEndTaskDef',
|
||||||
|
{
|
||||||
|
memoryLimitMiB: 3072,
|
||||||
|
cpu: 1024,
|
||||||
|
executionRole: props.backendTaskExecutionRole,
|
||||||
|
runtimePlatform:{
|
||||||
|
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
|
||||||
|
cpuArchitecture: props.arch,
|
||||||
|
},
|
||||||
|
taskRole: props.backendTaskRole,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
backendTaskDefinition.addContainer('backendContainer', {
|
||||||
|
image: ecs.ContainerImage.fromEcrRepository(props.ecrBackEndRepository, "latest"),
|
||||||
|
containerName:'langflow-back-container',
|
||||||
|
logging: ecs.LogDriver.awsLogs({
|
||||||
|
streamPrefix: 'my-stream',
|
||||||
|
logGroup: props.backendLogGroup,
|
||||||
|
}),
|
||||||
|
environment:{
|
||||||
|
// user:pass@endpoint:port/dbname
|
||||||
|
// "LANGFLOW_DATABASE_URL" : `mysql+pymysql://${username}:${password}@${host}:3306/${dbname}`,
|
||||||
|
// "LANGFLOW_DATABASE_URL" : "sqlite:///./langflow.db",
|
||||||
|
// "LANGFLOW_LANGCHAIN_CACHE" : "SQLiteCache",
|
||||||
|
// "LANGFLOW_AUTO_LOGIN" : "false",
|
||||||
|
// "LANGFLOW_SUPERUSER" : "admin",
|
||||||
|
// "LANGFLOW_SUPERUSER_PASSWORD" : "1234567"
|
||||||
|
"LANGFLOW_AUTO_LOGIN" : process.env.LANGFLOW_AUTO_LOGIN ?? 'false',
|
||||||
|
"LANGFLOW_SUPERUSER" : process.env.LANGFLOW_SUPERUSER ?? "admin",
|
||||||
|
"LANGFLOW_SUPERUSER_PASSWORD" : process.env.LANGFLOW_SUPERUSER_PASSWORD ?? "123456"
|
||||||
|
},
|
||||||
|
portMappings: [
|
||||||
|
{
|
||||||
|
containerPort: containerPort,
|
||||||
|
protocol: ecs.Protocol.TCP,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// Secretの設定
|
||||||
|
secrets: {
|
||||||
|
"dbname": ecs.Secret.fromSecretsManager(secretsDB, 'dbname'),
|
||||||
|
"username": ecs.Secret.fromSecretsManager(secretsDB, 'username'),
|
||||||
|
"host": ecs.Secret.fromSecretsManager(secretsDB, 'host'),
|
||||||
|
"password": ecs.Secret.fromSecretsManager(secretsDB, 'password'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.backendServiceName = 'backend'
|
||||||
|
const backendService = new ecs.FargateService(this, 'BackEndService', {
|
||||||
|
cluster: props.cluster,
|
||||||
|
serviceName: this.backendServiceName,
|
||||||
|
taskDefinition: backendTaskDefinition,
|
||||||
|
enableExecuteCommand: true,
|
||||||
|
securityGroups: [props.ecsBackSG],
|
||||||
|
cloudMapOptions: {
|
||||||
|
cloudMapNamespace: props.cloudmapNamespace,
|
||||||
|
containerPort: containerPort,
|
||||||
|
dnsRecordType: servicediscovery.DnsRecordType.A,
|
||||||
|
dnsTtl: Duration.seconds(10),
|
||||||
|
name: this.backendServiceName
|
||||||
|
},
|
||||||
|
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
65
scripts/aws/lib/construct/db.ts
Normal file
65
scripts/aws/lib/construct/db.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { Construct } from 'constructs';
|
||||||
|
import * as ec2 from 'aws-cdk-lib/aws-ec2'
|
||||||
|
import * as rds from "aws-cdk-lib/aws-rds";
|
||||||
|
import * as cdk from 'aws-cdk-lib';
|
||||||
|
|
||||||
|
interface RdsProps {
|
||||||
|
vpc: ec2.Vpc
|
||||||
|
dbSG:ec2.SecurityGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Rds extends Construct{
|
||||||
|
readonly rdsCluster: rds.DatabaseCluster
|
||||||
|
|
||||||
|
constructor(scope: Construct, id:string, props: RdsProps){
|
||||||
|
super(scope, id);
|
||||||
|
|
||||||
|
const {vpc, dbSG} = props
|
||||||
|
const instanceType = ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE4_GRAVITON, ec2.InstanceSize.MEDIUM)
|
||||||
|
|
||||||
|
// RDSのパスワードを自動生成してSecrets Managerに格納
|
||||||
|
const rdsCredentials = rds.Credentials.fromGeneratedSecret('db_user',{
|
||||||
|
secretName: 'langflow-DbSecret',
|
||||||
|
})
|
||||||
|
|
||||||
|
// DB クラスターのパラメータグループ作成
|
||||||
|
const clusterParameterGroup = new rds.ParameterGroup(scope, 'ClusterParameterGroup',{
|
||||||
|
engine: rds.DatabaseClusterEngine.auroraMysql({
|
||||||
|
version: rds.AuroraMysqlEngineVersion.VER_3_02_0
|
||||||
|
}),
|
||||||
|
description: 'for-langflow',
|
||||||
|
})
|
||||||
|
clusterParameterGroup.bindToCluster({})
|
||||||
|
|
||||||
|
// DB インスタンスのパラメタグループ作成
|
||||||
|
const instanceParameterGroup = new rds.ParameterGroup(scope, 'InstanceParameterGroup',{
|
||||||
|
engine: rds.DatabaseClusterEngine.auroraMysql({
|
||||||
|
version: rds.AuroraMysqlEngineVersion.VER_3_02_0,
|
||||||
|
}),
|
||||||
|
description: 'for-langflow',
|
||||||
|
})
|
||||||
|
instanceParameterGroup.bindToInstance({})
|
||||||
|
|
||||||
|
this.rdsCluster = new rds.DatabaseCluster(scope, 'LangflowDbCluster', {
|
||||||
|
engine: rds.DatabaseClusterEngine.auroraMysql({
|
||||||
|
version: rds.AuroraMysqlEngineVersion.VER_3_02_0,
|
||||||
|
}),
|
||||||
|
storageEncrypted: true,
|
||||||
|
credentials: rdsCredentials,
|
||||||
|
instanceIdentifierBase: 'langflow-instance',
|
||||||
|
vpc:vpc,
|
||||||
|
vpcSubnets:vpc.selectSubnets({
|
||||||
|
subnetGroupName: 'langflow-Isolated',
|
||||||
|
}),
|
||||||
|
securityGroups:[dbSG],
|
||||||
|
writer: rds.ClusterInstance.provisioned("WriterInstance", {
|
||||||
|
instanceType: instanceType,
|
||||||
|
enablePerformanceInsights: true,
|
||||||
|
parameterGroup:instanceParameterGroup,
|
||||||
|
}),
|
||||||
|
// 2台目以降はreaders:で設定
|
||||||
|
parameterGroup: clusterParameterGroup,
|
||||||
|
defaultDatabaseName: 'langflow',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
78
scripts/aws/lib/construct/ecr.ts
Normal file
78
scripts/aws/lib/construct/ecr.ts
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { RemovalPolicy } from 'aws-cdk-lib'
|
||||||
|
import * as ecr from 'aws-cdk-lib/aws-ecr'
|
||||||
|
import * as ecrdeploy from 'cdk-ecr-deployment'
|
||||||
|
import * as ecs from 'aws-cdk-lib/aws-ecs'
|
||||||
|
import * as servicediscovery from 'aws-cdk-lib/aws-servicediscovery'
|
||||||
|
import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets'
|
||||||
|
import * as path from "path";
|
||||||
|
import { Construct } from 'constructs'
|
||||||
|
|
||||||
|
|
||||||
|
interface ECRProps {
|
||||||
|
cloudmapNamespace: servicediscovery.PrivateDnsNamespace;
|
||||||
|
arch:ecs.CpuArchitecture;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EcrRepository extends Construct {
|
||||||
|
readonly ecrFrontEndRepository: ecr.Repository
|
||||||
|
readonly ecrBackEndRepository: ecr.Repository
|
||||||
|
|
||||||
|
constructor(scope: Construct, id: string, props: ECRProps) {
|
||||||
|
super(scope, id)
|
||||||
|
|
||||||
|
const imagePlatform = props.arch == ecs.CpuArchitecture.ARM64 ? Platform.LINUX_ARM64 : Platform.LINUX_AMD64
|
||||||
|
const backendPath = path.join(__dirname, "../../../../../", "langflow")
|
||||||
|
const frontendPath = path.join(__dirname, "../../../../src/", "frontend")
|
||||||
|
const excludeDir = ['node_modules','.git', 'cdk.out']
|
||||||
|
const LifecycleRule = {
|
||||||
|
tagStatus: ecr.TagStatus.ANY,
|
||||||
|
description: 'Delete more than 30 image',
|
||||||
|
maxImageCount: 30,
|
||||||
|
}
|
||||||
|
|
||||||
|
// リポジトリ作成
|
||||||
|
this.ecrFrontEndRepository = new ecr.Repository(scope, 'LangflowFrontEndRepository', {
|
||||||
|
repositoryName: 'langflow-frontend-repository',
|
||||||
|
removalPolicy: RemovalPolicy.RETAIN,
|
||||||
|
imageScanOnPush: true,
|
||||||
|
})
|
||||||
|
this.ecrBackEndRepository = new ecr.Repository(scope, 'LangflowBackEndRepository', {
|
||||||
|
repositoryName: 'langflow-backend-repository',
|
||||||
|
removalPolicy: RemovalPolicy.RETAIN,
|
||||||
|
imageScanOnPush: true,
|
||||||
|
})
|
||||||
|
// LifecycleRule作成
|
||||||
|
this.ecrFrontEndRepository.addLifecycleRule(LifecycleRule)
|
||||||
|
this.ecrBackEndRepository.addLifecycleRule(LifecycleRule)
|
||||||
|
|
||||||
|
// Create Docker Image Asset
|
||||||
|
const dockerFrontEndImageAsset = new DockerImageAsset(this, "DockerFrontEndImageAsset", {
|
||||||
|
directory: frontendPath,
|
||||||
|
file:"cdk.Dockerfile",
|
||||||
|
buildArgs:{
|
||||||
|
"BACKEND_URL":`http://backend.${props.cloudmapNamespace.namespaceName}:7860`
|
||||||
|
},
|
||||||
|
exclude: excludeDir,
|
||||||
|
platform: imagePlatform,
|
||||||
|
});
|
||||||
|
const dockerBackEndImageAsset = new DockerImageAsset(this, "DockerBackEndImageAsset", {
|
||||||
|
directory: backendPath,
|
||||||
|
file:"cdk.Dockerfile",
|
||||||
|
exclude: excludeDir,
|
||||||
|
platform: imagePlatform,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deploy Docker Image to ECR Repository
|
||||||
|
new ecrdeploy.ECRDeployment(this, "DeployFrontEndImage", {
|
||||||
|
src: new ecrdeploy.DockerImageName(dockerFrontEndImageAsset.imageUri),
|
||||||
|
dest: new ecrdeploy.DockerImageName(this.ecrFrontEndRepository.repositoryUri)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deploy Docker Image to ECR Repository
|
||||||
|
new ecrdeploy.ECRDeployment(this, "DeployBackEndImage", {
|
||||||
|
src: new ecrdeploy.DockerImageName(dockerBackEndImageAsset.imageUri),
|
||||||
|
dest: new ecrdeploy.DockerImageName(this.ecrBackEndRepository.repositoryUri)
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
117
scripts/aws/lib/construct/frontend.ts
Normal file
117
scripts/aws/lib/construct/frontend.ts
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
import { Duration } from 'aws-cdk-lib'
|
||||||
|
import { Construct } from 'constructs'
|
||||||
|
import {
|
||||||
|
aws_ec2 as ec2,
|
||||||
|
aws_ecs as ecs,
|
||||||
|
aws_ecr as ecr,
|
||||||
|
aws_servicediscovery as servicediscovery,
|
||||||
|
aws_iam as iam,
|
||||||
|
aws_logs as logs,
|
||||||
|
aws_elasticloadbalancingv2 as elb,
|
||||||
|
} from 'aws-cdk-lib';
|
||||||
|
import { CpuArchitecture } from 'aws-cdk-lib/aws-ecs';
|
||||||
|
|
||||||
|
interface FrontEndProps {
|
||||||
|
cluster:ecs.Cluster
|
||||||
|
ecsFrontSG:ec2.SecurityGroup
|
||||||
|
ecrFrontEndRepository:ecr.Repository
|
||||||
|
targetGroup: elb.ApplicationTargetGroup;
|
||||||
|
backendServiceName: string;
|
||||||
|
frontendTaskRole: iam.Role;
|
||||||
|
frontendTaskExecutionRole: iam.Role;
|
||||||
|
frontendLogGroup: logs.LogGroup;
|
||||||
|
cloudmapNamespace: servicediscovery.PrivateDnsNamespace;
|
||||||
|
arch:ecs.CpuArchitecture;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class FrontEndCluster extends Construct {
|
||||||
|
constructor(scope: Construct, id: string, props:FrontEndProps) {
|
||||||
|
super(scope, id)
|
||||||
|
|
||||||
|
const containerPort = 3000
|
||||||
|
const frontendTaskDefinition = new ecs.FargateTaskDefinition(
|
||||||
|
this,
|
||||||
|
'FrontendTaskDef',
|
||||||
|
{
|
||||||
|
memoryLimitMiB: 3072,
|
||||||
|
cpu: 1024,
|
||||||
|
executionRole: props.frontendTaskExecutionRole,
|
||||||
|
runtimePlatform:{
|
||||||
|
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
|
||||||
|
cpuArchitecture: props.arch,
|
||||||
|
},
|
||||||
|
taskRole: props.frontendTaskRole,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const frontendServiceName = 'frontend'
|
||||||
|
frontendTaskDefinition.addContainer('frontendContainer', {
|
||||||
|
image: ecs.ContainerImage.fromEcrRepository(props.ecrFrontEndRepository, "latest"),
|
||||||
|
containerName:'langflow-front-container',
|
||||||
|
environment: {
|
||||||
|
BACKEND_SERVICE_NAME: props.backendServiceName,
|
||||||
|
BACKEND_URL: `http://${props.backendServiceName}.${props.cloudmapNamespace.namespaceName}:7860/`,
|
||||||
|
VITE_PROXY_TARGET: `http://${props.backendServiceName}.${props.cloudmapNamespace.namespaceName}:7860/`,
|
||||||
|
},
|
||||||
|
logging: ecs.LogDriver.awsLogs({
|
||||||
|
streamPrefix: 'my-stream',
|
||||||
|
logGroup: props.frontendLogGroup,
|
||||||
|
}),
|
||||||
|
portMappings: [
|
||||||
|
{
|
||||||
|
name:frontendServiceName,
|
||||||
|
containerPort: containerPort,
|
||||||
|
protocol: ecs.Protocol.TCP,
|
||||||
|
appProtocol:ecs.AppProtocol.http,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const frontendService = new ecs.FargateService(
|
||||||
|
this,
|
||||||
|
'FrontendService',
|
||||||
|
{
|
||||||
|
serviceName: frontendServiceName,
|
||||||
|
cluster: props.cluster,
|
||||||
|
desiredCount: 1,
|
||||||
|
assignPublicIp: false,
|
||||||
|
taskDefinition: frontendTaskDefinition,
|
||||||
|
enableExecuteCommand: true,
|
||||||
|
securityGroups: [props.ecsFrontSG],
|
||||||
|
cloudMapOptions: {
|
||||||
|
cloudMapNamespace: props.cloudmapNamespace,
|
||||||
|
containerPort: containerPort,
|
||||||
|
dnsRecordType: servicediscovery.DnsRecordType.A,
|
||||||
|
dnsTtl: Duration.seconds(10),
|
||||||
|
name: frontendServiceName
|
||||||
|
},
|
||||||
|
healthCheckGracePeriod: Duration.seconds(1000),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
props.targetGroup.addTarget(frontendService);
|
||||||
|
|
||||||
|
// // Create ALB and ECS Fargate Service
|
||||||
|
// const frontService = new ecs_patterns.ApplicationLoadBalancedFargateService(
|
||||||
|
// this,
|
||||||
|
// "FrontEndService",
|
||||||
|
// {
|
||||||
|
// cluster: cluster,
|
||||||
|
// serviceName: 'langflow-frontend-service',
|
||||||
|
// cpu: 256,
|
||||||
|
// memoryLimitMiB: 512,
|
||||||
|
// listenerPort: 80,
|
||||||
|
// assignPublicIp: true, // Public facing - ALB
|
||||||
|
// taskSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS },
|
||||||
|
// securityGroups:[ecsFrontSG],
|
||||||
|
// taskImageOptions: {
|
||||||
|
// family: 'langflow-taskdef',
|
||||||
|
// containerName: 'langflow-front-container',
|
||||||
|
// image: ecs.ContainerImage.fromEcrRepository(ecrFrontEndRepository, "latest"),
|
||||||
|
// containerPort: 3000, // L2なので、TargetGroupのportが3000で設定されるはず
|
||||||
|
// },
|
||||||
|
// loadBalancer:alb,
|
||||||
|
// openListener:false,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
101
scripts/aws/lib/construct/iam.ts
Normal file
101
scripts/aws/lib/construct/iam.ts
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
import { RemovalPolicy, Duration } from 'aws-cdk-lib'
|
||||||
|
import { Construct } from 'constructs'
|
||||||
|
import {
|
||||||
|
aws_rds as rds,
|
||||||
|
aws_iam as iam,
|
||||||
|
} from 'aws-cdk-lib';
|
||||||
|
|
||||||
|
interface IAMProps {
|
||||||
|
rdsCluster:rds.DatabaseCluster
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EcsIAM extends Construct {
|
||||||
|
readonly frontendTaskRole: iam.Role;
|
||||||
|
readonly frontendTaskExecutionRole: iam.Role;
|
||||||
|
readonly backendTaskRole: iam.Role;
|
||||||
|
readonly backendTaskExecutionRole: iam.Role;
|
||||||
|
|
||||||
|
constructor(scope: Construct, id: string, props:IAMProps) {
|
||||||
|
super(scope, id)
|
||||||
|
|
||||||
|
// Policy Statements
|
||||||
|
// ECS Policy State
|
||||||
|
const ECSExecPolicyStatement = new iam.PolicyStatement({
|
||||||
|
sid: 'allowECSExec',
|
||||||
|
resources: ['*'],
|
||||||
|
actions: [
|
||||||
|
'ecr:GetAuthorizationToken',
|
||||||
|
'ecr:BatchCheckLayerAvailability',
|
||||||
|
'ecr:GetDownloadUrlForLayer',
|
||||||
|
'ecr:BatchGetImage',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
// Bedrock Policy State
|
||||||
|
const BedrockPolicyStatement = new iam.PolicyStatement({
|
||||||
|
sid: 'allowBedrockAccess',
|
||||||
|
resources: ['*'],
|
||||||
|
actions: [
|
||||||
|
'bedrock:*',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
// Kendra Policy State
|
||||||
|
const KendraPolicyStatement = new iam.PolicyStatement({
|
||||||
|
sid: 'allowKendraAccess',
|
||||||
|
resources: ['*'],
|
||||||
|
actions: [
|
||||||
|
'kendra:*'
|
||||||
|
],
|
||||||
|
});
|
||||||
|
// Create Rag Policy
|
||||||
|
const RagAccessPolicy = new iam.Policy(this, 'RAGFullAccess', {
|
||||||
|
statements: [KendraPolicyStatement,BedrockPolicyStatement],
|
||||||
|
})
|
||||||
|
// Secrets ManagerからDB認証情報を取ってくるためのPolicy
|
||||||
|
const SecretsManagerPolicy = new iam.Policy(this, 'SMGetPolicy', {
|
||||||
|
statements: [new iam.PolicyStatement({
|
||||||
|
actions: ['secretsmanager:GetSecretValue'],
|
||||||
|
resources: [props.rdsCluster.secret!.secretArn],
|
||||||
|
})],
|
||||||
|
})
|
||||||
|
|
||||||
|
// FrontEnd Task Role
|
||||||
|
this.frontendTaskRole = new iam.Role(this, 'FrontendTaskRole', {
|
||||||
|
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
|
||||||
|
});
|
||||||
|
this.frontendTaskRole.addToPolicy(ECSExecPolicyStatement);
|
||||||
|
|
||||||
|
// BackEnd Task Role
|
||||||
|
this.backendTaskRole = new iam.Role(this, 'BackendTaskRole', {
|
||||||
|
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
|
||||||
|
});
|
||||||
|
// ECS Exec Policyの付与
|
||||||
|
this.backendTaskRole.addToPolicy(ECSExecPolicyStatement);
|
||||||
|
// KendraとBedrockのアクセス権付与
|
||||||
|
this.backendTaskRole.attachInlinePolicy(RagAccessPolicy);
|
||||||
|
|
||||||
|
// FrontEnd Task ExecutionRole
|
||||||
|
this.frontendTaskExecutionRole = new iam.Role(this, 'frontendTaskExecutionRole', {
|
||||||
|
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
|
||||||
|
managedPolicies: [
|
||||||
|
{
|
||||||
|
managedPolicyArn:
|
||||||
|
'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// BackEnd Task ExecutionRole
|
||||||
|
this.backendTaskExecutionRole = new iam.Role(this, 'backendTaskExecutionRole', {
|
||||||
|
assumedBy: new iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
|
||||||
|
managedPolicies: [
|
||||||
|
{
|
||||||
|
managedPolicyArn:
|
||||||
|
'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
this.backendTaskExecutionRole.attachInlinePolicy(SecretsManagerPolicy);
|
||||||
|
this.backendTaskExecutionRole.attachInlinePolicy(RagAccessPolicy);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
scripts/aws/lib/construct/index.ts
Normal file
6
scripts/aws/lib/construct/index.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
export * from './db';
|
||||||
|
export * from './ecr';
|
||||||
|
export * from './iam';
|
||||||
|
export * from './frontend';
|
||||||
|
export * from './backend';
|
||||||
|
export * from './network';
|
||||||
143
scripts/aws/lib/construct/network.ts
Normal file
143
scripts/aws/lib/construct/network.ts
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
import { RemovalPolicy, Duration, CfnOutput } from 'aws-cdk-lib'
|
||||||
|
import { Construct } from 'constructs'
|
||||||
|
import {
|
||||||
|
aws_ec2 as ec2,
|
||||||
|
aws_ecs as ecs,
|
||||||
|
aws_logs as logs,
|
||||||
|
aws_servicediscovery as servicediscovery,
|
||||||
|
aws_elasticloadbalancingv2 as elb,
|
||||||
|
} from 'aws-cdk-lib';
|
||||||
|
|
||||||
|
export class Network extends Construct {
|
||||||
|
readonly vpc: ec2.Vpc;
|
||||||
|
readonly cluster: ecs.Cluster;
|
||||||
|
readonly alb: elb.IApplicationLoadBalancer;
|
||||||
|
readonly targetGroup: elb.ApplicationTargetGroup;
|
||||||
|
readonly cloudmapNamespace: servicediscovery.PrivateDnsNamespace;
|
||||||
|
readonly ecsFrontSG: ec2.SecurityGroup;
|
||||||
|
readonly ecsBackSG: ec2.SecurityGroup;
|
||||||
|
readonly dbSG: ec2.SecurityGroup;
|
||||||
|
readonly albSG: ec2.SecurityGroup;
|
||||||
|
readonly backendLogGroup: logs.LogGroup;
|
||||||
|
readonly frontendLogGroup: logs.LogGroup;
|
||||||
|
|
||||||
|
constructor(scope: Construct, id: string) {
|
||||||
|
super(scope, id)
|
||||||
|
const alb_listen_port=80
|
||||||
|
const front_service_port=3000
|
||||||
|
const back_service_port=7860
|
||||||
|
|
||||||
|
// VPC等リソースの作成
|
||||||
|
this.vpc = new ec2.Vpc(scope, 'VPC', {
|
||||||
|
vpcName: 'langflow-vpc',
|
||||||
|
ipAddresses: ec2.IpAddresses.cidr('10.0.0.0/16'),
|
||||||
|
maxAzs: 3,
|
||||||
|
subnetConfiguration: [
|
||||||
|
{
|
||||||
|
cidrMask: 24,
|
||||||
|
name: 'langflow-Isolated',
|
||||||
|
subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cidrMask: 24,
|
||||||
|
name: 'langflow-Public',
|
||||||
|
subnetType: ec2.SubnetType.PUBLIC,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cidrMask: 24,
|
||||||
|
name: 'langflow-Private',
|
||||||
|
subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS
|
||||||
|
},
|
||||||
|
],
|
||||||
|
natGateways: 1,
|
||||||
|
})
|
||||||
|
// Cluster
|
||||||
|
this.cluster = new ecs.Cluster(this, 'EcsCluster', {
|
||||||
|
clusterName: 'langflow-cluster',
|
||||||
|
vpc: this.vpc,
|
||||||
|
enableFargateCapacityProviders: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Private DNS
|
||||||
|
this.cloudmapNamespace = new servicediscovery.PrivateDnsNamespace(
|
||||||
|
this,
|
||||||
|
'Namespace',
|
||||||
|
{
|
||||||
|
name: 'ecs-deploy.com',
|
||||||
|
vpc: this.vpc,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// ALBに設定するセキュリティグループ
|
||||||
|
this.albSG = new ec2.SecurityGroup(scope, 'ALBSecurityGroup', {
|
||||||
|
securityGroupName: 'alb-sg',
|
||||||
|
description: 'for alb',
|
||||||
|
vpc: this.vpc,
|
||||||
|
})
|
||||||
|
this.albSG.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(alb_listen_port))
|
||||||
|
|
||||||
|
this.alb = new elb.ApplicationLoadBalancer(this,'langflow-alb',{
|
||||||
|
internetFacing: true, //インターネットからのアクセスを許可するかどうか指定
|
||||||
|
loadBalancerName: 'langflow-alb',
|
||||||
|
securityGroup: this.albSG, //作成したセキュリティグループを割り当てる
|
||||||
|
vpc:this.vpc,
|
||||||
|
})
|
||||||
|
|
||||||
|
const listener = this.alb.addListener('Listener', { port: alb_listen_port });
|
||||||
|
|
||||||
|
this.targetGroup = listener.addTargets('targetGroup', {
|
||||||
|
port: front_service_port,
|
||||||
|
protocol: elb.ApplicationProtocol.HTTP,
|
||||||
|
healthCheck: {
|
||||||
|
enabled: true,
|
||||||
|
path: '/health',
|
||||||
|
healthyThresholdCount: 2,
|
||||||
|
unhealthyThresholdCount: 4,
|
||||||
|
interval: Duration.seconds(100),
|
||||||
|
timeout: Duration.seconds(30),
|
||||||
|
healthyHttpCodes: '200',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// ECS FrontEndに設定するセキュリティグループ
|
||||||
|
this.ecsFrontSG = new ec2.SecurityGroup(scope, 'ECSFrontEndSecurityGroup', {
|
||||||
|
securityGroupName: 'langflow-ecs-front-sg',
|
||||||
|
description: 'for langflow-front-ecs',
|
||||||
|
vpc: this.vpc,
|
||||||
|
})
|
||||||
|
this.ecsFrontSG.addIngressRule(this.albSG, ec2.Port.allTcp())
|
||||||
|
|
||||||
|
// ECS BackEndに設定するセキュリティグループ
|
||||||
|
this.ecsBackSG = new ec2.SecurityGroup(scope, 'ECSBackEndSecurityGroup', {
|
||||||
|
securityGroupName: 'langflow-ecs-back-sg',
|
||||||
|
description: 'for langflow-back-ecs',
|
||||||
|
vpc: this.vpc,
|
||||||
|
})
|
||||||
|
this.ecsBackSG.addIngressRule(this.ecsFrontSG, ec2.Port.tcp(back_service_port))
|
||||||
|
|
||||||
|
// RDSに設定するセキュリティグループ
|
||||||
|
this.dbSG = new ec2.SecurityGroup(scope, 'DBSecurityGroup', {
|
||||||
|
allowAllOutbound: true,
|
||||||
|
securityGroupName: 'langflow-db',
|
||||||
|
description: 'for langflow-db',
|
||||||
|
vpc: this.vpc,
|
||||||
|
})
|
||||||
|
// AppRunnerSecurityGroupからのポート3306:mysql(5432:postgres)のインバウンドを許可
|
||||||
|
this.dbSG.addIngressRule(this.ecsBackSG, ec2.Port.tcp(3306))
|
||||||
|
|
||||||
|
// Create CloudWatch Log Group
|
||||||
|
this.backendLogGroup = new logs.LogGroup(this, 'backendLogGroup', {
|
||||||
|
logGroupName: 'langflow-backend-logs',
|
||||||
|
removalPolicy: RemovalPolicy.DESTROY,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.frontendLogGroup = new logs.LogGroup(this, 'frontendLogGroup', {
|
||||||
|
logGroupName: 'langflow-frontend-logs',
|
||||||
|
removalPolicy: RemovalPolicy.DESTROY,
|
||||||
|
});
|
||||||
|
|
||||||
|
new CfnOutput(this, 'URL', {
|
||||||
|
value: `http://${this.alb.loadBalancerDnsName}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
4613
scripts/aws/package-lock.json
generated
Normal file
4613
scripts/aws/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
29
scripts/aws/package.json
Normal file
29
scripts/aws/package.json
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"name": "cdk",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"bin": {
|
||||||
|
"cdk": "bin/cdk.js"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"watch": "tsc -w",
|
||||||
|
"test": "jest",
|
||||||
|
"cdk": "cdk"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jest": "^29.5.1",
|
||||||
|
"@types/node": "20.1.7",
|
||||||
|
"aws-cdk": "2.86.0",
|
||||||
|
"jest": "^29.5.0",
|
||||||
|
"ts-jest": "^29.1.0",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"typescript": "~5.1.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"aws-cdk-lib": "^2.86.0",
|
||||||
|
"cdk-ecr-deployment": "^2.5.30",
|
||||||
|
"constructs": "^10.0.0",
|
||||||
|
"dotenv": "^16.3.1",
|
||||||
|
"source-map-support": "^0.5.21"
|
||||||
|
}
|
||||||
|
}
|
||||||
17
scripts/aws/test/cdk.test.ts
Normal file
17
scripts/aws/test/cdk.test.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
// import * as cdk from 'aws-cdk-lib';
|
||||||
|
// import { Template } from 'aws-cdk-lib/assertions';
|
||||||
|
// import * as Cdk from '../lib/cdk-stack';
|
||||||
|
|
||||||
|
// example test. To run these tests, uncomment this file along with the
|
||||||
|
// example resource in lib/cdk-stack.ts
|
||||||
|
test('SQS Queue Created', () => {
|
||||||
|
// const app = new cdk.App();
|
||||||
|
// // WHEN
|
||||||
|
// const stack = new Cdk.CdkStack(app, 'MyTestStack');
|
||||||
|
// // THEN
|
||||||
|
// const template = Template.fromStack(stack);
|
||||||
|
|
||||||
|
// template.hasResourceProperties('AWS::SQS::Queue', {
|
||||||
|
// VisibilityTimeout: 300
|
||||||
|
// });
|
||||||
|
});
|
||||||
|
|
@ -47,7 +47,6 @@ class Settings(BaseSettings):
|
||||||
else:
|
else:
|
||||||
logger.debug("No DATABASE_URL env variable, using sqlite database")
|
logger.debug("No DATABASE_URL env variable, using sqlite database")
|
||||||
value = "sqlite:///./langflow.db"
|
value = "sqlite:///./langflow.db"
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@validator("COMPONENTS_PATH", pre=True)
|
@validator("COMPONENTS_PATH", pre=True)
|
||||||
|
|
|
||||||
26
src/frontend/cdk.Dockerfile
Normal file
26
src/frontend/cdk.Dockerfile
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#baseline
|
||||||
|
FROM --platform=linux/amd64 node:19-bullseye-slim AS base
|
||||||
|
RUN mkdir -p /home/node/app
|
||||||
|
RUN chown -R node:node /home/node && chmod -R 770 /home/node
|
||||||
|
RUN apt-get update && apt-get install -y jq curl
|
||||||
|
WORKDIR /home/node/app
|
||||||
|
|
||||||
|
# client build
|
||||||
|
FROM base AS builder-client
|
||||||
|
ARG BACKEND_URL
|
||||||
|
ENV BACKEND_URL $BACKEND_URL
|
||||||
|
RUN echo "BACKEND_URL: $BACKEND_URL"
|
||||||
|
|
||||||
|
WORKDIR /home/node/app
|
||||||
|
COPY --chown=node:node . ./
|
||||||
|
|
||||||
|
COPY ./set_proxy.sh .
|
||||||
|
RUN chmod +x set_proxy.sh && \
|
||||||
|
cat set_proxy.sh | tr -d '\r' > set_proxy_unix.sh && \
|
||||||
|
chmod +x set_proxy_unix.sh && \
|
||||||
|
./set_proxy_unix.sh
|
||||||
|
|
||||||
|
USER node
|
||||||
|
|
||||||
|
RUN npm install --loglevel warn
|
||||||
|
CMD ["npm", "run", "dev:docker"]
|
||||||
|
|
@ -198,6 +198,7 @@ export default function FormModal({
|
||||||
window.location.protocol === "https:" || window.location.port === "443";
|
window.location.protocol === "https:" || window.location.port === "443";
|
||||||
const webSocketProtocol = isSecureProtocol ? "wss" : "ws";
|
const webSocketProtocol = isSecureProtocol ? "wss" : "ws";
|
||||||
const host = isDevelopment ? "localhost:7860" : window.location.host;
|
const host = isDevelopment ? "localhost:7860" : window.location.host;
|
||||||
|
|
||||||
const chatEndpoint = `/api/v1/chat/${chatId}`;
|
const chatEndpoint = `/api/v1/chat/${chatId}`;
|
||||||
|
|
||||||
return `${
|
return `${
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue