Skip to content

Commit

Permalink
Fix bug in deployment procedure in AWS CDK and support HTTPS (#1491)
Browse files Browse the repository at this point in the history
* add cloudfront

* modify: healthcheck disable

* modify: health check option

* modify nodejsbuild

* add nodejs version

* endpoint configuration

* set axios url

* remote console.log VITE_PROXY_TARGET

* create alb stack

* add alb to origin

* alb build success

* remote baseURL setting

* add redirect

* change responseHttpStatus to 200

* modify output and readme

* modify architecture

* add health check path route to cloudfront routing

* modified: c9 env name in deploy langflow section

* modified : package dependency for pymysql

* modified: deploy procedure

* modified: deploy procedure (ja)

---------

Co-authored-by: nsxshota <[email protected]>
Co-authored-by: ymkazuki <[email protected]>
Co-authored-by: Shota Nakamoto <[email protected]>
  • Loading branch information
4 people committed Mar 4, 2024
1 parent 7363df6 commit 3eeda84
Show file tree
Hide file tree
Showing 18 changed files with 677 additions and 270 deletions.
3 changes: 2 additions & 1 deletion cdk.Dockerfile
Expand Up @@ -15,6 +15,7 @@ COPY ./ ./
# Install dependencies
RUN poetry config virtualenvs.create false && poetry install --no-interaction --no-ansi

RUN poetry add pymysql==1.0.2
RUN poetry add botocore
RUN poetry add pymysql

CMD ["sh", "./container-cmd-cdk.sh"]
4 changes: 3 additions & 1 deletion container-cmd-cdk.sh
@@ -1,3 +1,5 @@
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
uvicorn --factory src.backend.langflow.main:create_app --host 0.0.0.0 --port 7860 --reload --log-level debug

# python -m langflow run --host 0.0.0.0 --port 7860
9 changes: 4 additions & 5 deletions scripts/aws/README.ja.md
Expand Up @@ -8,10 +8,9 @@ Langflow on AWS では、 [AWS Cloud Development Kit](https://aws.amazon.com/cdk

作成するアプリケーションのアーキテクチャです。
![langflow-archi](./img/langflow-archi.png)
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) を作成します。
AWS CDK によって Langflow のアプリケーションをデプロイします。アプリケーションは [Amazon CloudFront](https://aws.amazon.com/cloudfront/?nc1=h_ls) を介して配信されます。CloudFront は 2 つのオリジンを有しています。1 つ目は静的な Web サイトを配信するための [Amazon Simple Storage Service](https://aws.amazon.com/s3/?nc1=h_ls) (S3)、2 つ目は バックエンドと通信するための [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/?nc1=h_ls) (ALB) です。ALB の背後には FastAPI が動作する [AWS Fargate](https://aws.amazon.com/fargate/?nc2=type_a) 、データベースの [Amazon Aurora](https://aws.amazon.com/rds/aurora/?nc2=type_a) が作成されます。
Fargate は [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/?nc1=h_ls) (ECR) に保存された Docker イメージを使用します。
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)を開きます。
Expand All @@ -28,7 +27,7 @@ Fargate のタスクはフロントエンドとバックエンドに分かれて

1. 以下のコマンドを実行します。
```shell
git clone -b aws-cdk https://github.com/logspace-ai/langflow.git
git clone https://github.com/logspace-ai/langflow.git
cd langflow/scripts/aws
cp .env.example .env # 環境設定を変える場合はこのファイル(.env)を編集してください。
npm ci
Expand All @@ -38,7 +37,7 @@ Fargate のタスクはフロントエンドとバックエンドに分かれて
1. 表示される URL にアクセスします。
```shell
Outputs:
LangflowAppStack.NetworkURLXXXXXX = http://alb-XXXXXXXXXXX.elb.amazonaws.com
LangflowAppStack.frontendURLXXXXXX = https://XXXXXXXXXXX.cloudfront.net
```
1. サインイン画面でユーザー名とパスワードを入力します。`.env`ファイルでユーザー名とパスワードを設定していない場合、ユーザー名は`admin`、パスワードは`123456`で設定されます。
![make-cloud9](./img/langflow-signin.png)
Expand Down
18 changes: 9 additions & 9 deletions scripts/aws/README.md
Expand Up @@ -9,11 +9,9 @@ This tutorial assumes you have an AWS account and basic knowledge of AWS.

The architecture of the application to be created:
![langflow-archi](./img/langflow-archi.png)

[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.
Langflow is deployed using AWS CDK. The application is distributed via [Amazon CloudFront](https://aws.amazon.com/cloudfront/?nc1=h_ls), which has two origins: the first is [Amazon Simple Storage Service](https://aws.amazon.com/s3/?nc1=h_ls) (S3) for serving a static website, and the second is an [Application Load Balancer](https://aws.amazon.com/elasticloadbalancing/application-load-balancer/?nc1=h_ls) (ALB) for communicating with the backend. [AWS Fargate](https://aws.amazon.com/fargate/?nc2=type_a), where FastAPI runs and [Amazon Aurora](https://aws.amazon.com/rds/aurora/?nc2=type_a), the database, are created behind the ALB.
Fargate uses a Docker image stored in [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/?nc1=h_ls) (ECR).
Aurora's secret is managed by [AWS Secrets Manager](https://aws.amazon.com/secrets-manager/?nc2=type_a).

# How to set up your environment and deploy langflow

Expand All @@ -22,13 +20,14 @@ If you just want to deploy resources, you do not need in-depth knowledge of each
```shell
git clone https://github.com/aws-samples/cloud9-setup-for-prototyping
cd cloud9-setup-for-prototyping
cat params.json | jq '.name |= "c9-for-langflow"'
./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. When you see `Done!` in Cloudshell, open `c9-for-langflow` from [AWS Cloud9](https://us-east-1.console.aws.amazon.com/cloud9control/home?region=us-east-1#/).
![make-cloud9](./img/langflow-cloud9-en.png)
1. Run the following command in the Cloud9 terminal.
```shell
git clone -b aws-cdk https://github.com/logspace-ai/langflow.git
git clone 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
Expand All @@ -38,7 +37,7 @@ If you just want to deploy resources, you do not need in-depth knowledge of each
1. Access the URL displayed.
```shell
Outputs:
LangflowAppStack.NetworkURLXXXXXX = http://alb-XXXXXXXXXXX.elb.amazonaws.com
LangflowAppStack.frontendURLXXXXXX = https://XXXXXXXXXXX.cloudfront.net
```
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`.
![signin-langflow](./img/langflow-signin.png)
Expand All @@ -49,5 +48,6 @@ If you just want to deploy resources, you do not need in-depth knowledge of each
```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.
1. Open [AWS CloudFormation](https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/getting-started), select `aws-cloud9-c9-for-langflow-XXXX` and delete it.
![delete-cfn](./img/langflow-cfn.png)
s
1 change: 1 addition & 0 deletions scripts/aws/bin/cdk.ts
Expand Up @@ -4,6 +4,7 @@ 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,
Expand Down
2 changes: 2 additions & 0 deletions scripts/aws/cdk.json
Expand Up @@ -17,6 +17,8 @@
]
},
"context": {
"ragEnabled": false,
"kendraIndexArn": null,
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
Expand Down
4 changes: 2 additions & 2 deletions scripts/aws/delete-resources.sh
@@ -1,4 +1,4 @@
aws cloudformation delete-stack --stack-name LangflowAppStack
# 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 delete-repository --repository-name langflow-frontend-repository --force
# aws ecr describe-repositories --output json | jq -re ".repositories[].repositoryName"
Binary file modified scripts/aws/img/langflow-archi.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 27 additions & 21 deletions scripts/aws/lib/cdk-stack.ts
Expand Up @@ -2,21 +2,38 @@ 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 { Network, EcrRepository, Web, BackEndCluster, Rds, EcsIAM, Rag} from './construct';
// import * as sqs from 'aws-cdk-lib/aws-sqs';

const errorMessageForBooleanContext = (key: string) => {
return `There was an error setting $ {key}. Possible causes are as follows.
- Trying to set it with the -c option instead of changing cdk.json
- cdk.json is set to a value that is not a boolean (e.g. “true” double quotes are not required)
- no items in cdk.json (unset) `;
};


export class LangflowAppStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Kendra Enable
const ragEnabled: boolean = this.node.tryGetContext('ragEnabled')!;
if (typeof ragEnabled !== 'boolean') {
throw new Error(errorMessageForBooleanContext('ragEnabled'));
}
if (ragEnabled) {
new Rag(this, 'Rag', {
});
}

// Arch
const arch = ecs.CpuArchitecture.X86_64

// VPC
const { vpc, cluster, alb, targetGroup, cloudmapNamespace, ecsFrontSG, ecsBackSG, dbSG, albSG, backendLogGroup, frontendLogGroup} = new Network(this, 'Network')
const { vpc, cluster, ecsBackSG, dbSG, backendLogGroup, alb, albTG, albSG} = new Network(this, 'Network')

// ECR
const { ecrFrontEndRepository,ecrBackEndRepository} = new EcrRepository(this, 'Ecr', {
cloudmapNamespace:cloudmapNamespace,
const { ecrBackEndRepository } = new EcrRepository(this, 'Ecr', {
arch:arch
})

Expand All @@ -25,7 +42,7 @@ export class LangflowAppStack extends cdk.Stack {
const { rdsCluster } = new Rds(this, 'Rds', { vpc, dbSG })

// IAM
const { frontendTaskRole, frontendTaskExecutionRole, backendTaskRole, backendTaskExecutionRole } = new EcsIAM(this, 'EcsIAM',{
const { backendTaskRole, backendTaskExecutionRole } = new EcsIAM(this, 'EcsIAM',{
rdsCluster:rdsCluster
})

Expand All @@ -36,29 +53,18 @@ export class LangflowAppStack extends cdk.Stack {
backendTaskRole:backendTaskRole,
backendTaskExecutionRole:backendTaskExecutionRole,
backendLogGroup:backendLogGroup,
cloudmapNamespace:cloudmapNamespace,
rdsCluster:rdsCluster,
alb:alb,
arch:arch
arch:arch,
albTG:albTG
})
backendService.node.addDependency(rdsCluster);

const frontendService = new FrontEndCluster(this, 'frontend',{
const frontendService = new Web(this, 'frontend',{
cluster:cluster,
ecsFrontSG:ecsFrontSG,
ecrFrontEndRepository:ecrFrontEndRepository,
targetGroup: targetGroup,
backendServiceName: backendService.backendServiceName,
frontendTaskRole: frontendTaskRole,
frontendTaskExecutionRole: frontendTaskExecutionRole,
frontendLogGroup: frontendLogGroup,
cloudmapNamespace: cloudmapNamespace,
arch:arch
alb:alb,
albSG:albSG
})
frontendService.node.addDependency(backendService);


// S3+CloudFront
// new Web(this,'Cloudfront-S3')
}
}
29 changes: 7 additions & 22 deletions scripts/aws/lib/construct/backend.ts
Expand Up @@ -21,18 +21,17 @@ interface BackEndProps {
backendTaskRole: iam.Role;
backendTaskExecutionRole: iam.Role;
backendLogGroup: logs.LogGroup;
cloudmapNamespace: servicediscovery.PrivateDnsNamespace;
rdsCluster:rds.DatabaseCluster
alb:elb.IApplicationLoadBalancer
arch:ecs.CpuArchitecture
albTG: elb.ApplicationTargetGroup;
}

export class BackEndCluster extends Construct {
readonly backendServiceName: string

constructor(scope: Construct, id: string, props:BackEndProps) {
super(scope, id)
const containerPort = 7860
const backendServiceName = 'backend'
const backendServicePort = 7860
// Secrets ManagerからDB認証情報を取ってくる
const secretsDB = props.rdsCluster.secret!;

Expand All @@ -59,20 +58,13 @@ export class BackEndCluster extends Construct {
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,
containerPort: backendServicePort,
protocol: ecs.Protocol.TCP,
},
],
Expand All @@ -84,22 +76,15 @@ export class BackEndCluster extends Construct {
"password": ecs.Secret.fromSecretsManager(secretsDB, 'password'),
},
});
this.backendServiceName = 'backend'

const backendService = new ecs.FargateService(this, 'BackEndService', {
cluster: props.cluster,
serviceName: this.backendServiceName,
serviceName: 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 },
});

props.albTG.addTarget(backendService);
}
}
26 changes: 1 addition & 25 deletions scripts/aws/lib/construct/ecr.ts
Expand Up @@ -9,65 +9,41 @@ 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,
})
// Backend ECR リポジトリ作成
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),
Expand Down

0 comments on commit 3eeda84

Please sign in to comment.