Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dify v1 #16

Draft
wants to merge 14 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,16 @@ npx cdk destroy --force
The following table provides a sample cost breakdown for deploying this system in the us-east-1 (N. Virginia) region for one month (when deployed using cheap configuration).


| AWS service | Dimensions | Cost [USD] |
| AWS service | Dimensions | Cost [USD/month] |
| --------------------| ----------------- | -------------------------------|
| RDS Aurora | Postgres Serverless v2 (0 ACU) | $0 |
| ElastiCache | Valkey t4g.micro | $9.2 |
| ECS (Fargate) | Dify-web 1 task running 24/7 (256CPU) | $2.7 |
| ECS (Fargate) | Dify-api/worker 1 task running 24/7 (1024CPU) | $10.7 |
| Application Load Balancer | ALB-hour per month | $17.5 |
| VPC | NAT Instances t4g.nano x1 | $3.0 |
| TOTAL | estimate per month | $43.1 |
| Secrets Manager | Secret x3 | $1.2 |
| TOTAL | estimate per month | $44.3 |

Note that you have to pay LLM cost (e.g. Amazon Bedrock ) in addition to the above, which totally depends on your specific use case.

Expand Down
103 changes: 84 additions & 19 deletions lib/constructs/dify-services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { CpuArchitecture, FargateTaskDefinition, ICluster } from 'aws-cdk-lib/aw
import { Construct } from 'constructs';
import { Duration, Stack, aws_ecs as ecs } from 'aws-cdk-lib';
import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
import { PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { AccessKey, ManagedPolicy, PolicyStatement, User } from 'aws-cdk-lib/aws-iam';
import { Postgres } from '../postgres';
import { Redis } from '../redis';
import { IBucket } from 'aws-cdk-lib/aws-s3';
import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
import { join } from 'path';
import { IAlb } from '../alb';
import { Repository } from 'aws-cdk-lib/aws-ecr';

export interface ApiServiceProps {
cluster: ICluster;
Expand Down Expand Up @@ -39,8 +40,7 @@ export class ApiService extends Construct {

const taskDefinition = new FargateTaskDefinition(this, 'Task', {
cpu: 1024,
// 512だとOOMが起きたので、増やした
memoryLimitMiB: 2048,
memoryLimitMiB: 2048, // We got OOM frequently when RAM=512MB
runtimePlatform: { cpuArchitecture: CpuArchitecture.X86_64 },
volumes: [
{
Expand Down Expand Up @@ -103,7 +103,12 @@ export class ApiService extends Construct {
PGVECTOR_DATABASE: postgres.pgVectorDatabaseName,

// The sandbox service endpoint.
CODE_EXECUTION_ENDPOINT: 'http://localhost:8194', // Fargate の task 内通信は localhost 宛,
CODE_EXECUTION_ENDPOINT: 'http://localhost:8194',

PLUGIN_DAEMON_URL: 'http://localhost:5002',

MARKETPLACE_API_URL: 'https://marketplace.dify.ai',
MARKETPLACE_URL: 'https://marketplace.dify.ai',
},
logging: ecs.LogDriver.awsLogs({
streamPrefix: 'log',
Expand All @@ -124,6 +129,8 @@ export class ApiService extends Construct {
CELERY_BROKER_URL: ecs.Secret.fromSsmParameter(redis.brokerUrl),
SECRET_KEY: ecs.Secret.fromSecretsManager(encryptionSecret),
CODE_EXECUTION_API_KEY: ecs.Secret.fromSecretsManager(encryptionSecret), // is it ok to reuse this?
INNER_API_KEY_FOR_PLUGIN: ecs.Secret.fromSecretsManager(encryptionSecret), // is it ok to reuse this?
PLUGIN_API_KEY: ecs.Secret.fromSecretsManager(encryptionSecret), // is it ok to reuse this?
},
healthCheck: {
command: ['CMD-SHELL', `curl -f http://localhost:${port}/health || exit 1`],
Expand Down Expand Up @@ -164,6 +171,11 @@ export class ApiService extends Construct {
// pgvector configurations
VECTOR_STORE: 'pgvector',
PGVECTOR_DATABASE: postgres.pgVectorDatabaseName,

PLUGIN_API_URL: 'http://localhost:5002',

MARKETPLACE_API_URL: 'https://marketplace.dify.ai',
MARKETPLACE_URL: 'https://marketplace.dify.ai',
},
logging: ecs.LogDriver.awsLogs({
streamPrefix: 'log',
Expand All @@ -180,6 +192,7 @@ export class ApiService extends Construct {
REDIS_PASSWORD: ecs.Secret.fromSecretsManager(redis.secret),
CELERY_BROKER_URL: ecs.Secret.fromSsmParameter(redis.brokerUrl),
SECRET_KEY: ecs.Secret.fromSecretsManager(encryptionSecret),
PLUGIN_API_KEY: ecs.Secret.fromSecretsManager(encryptionSecret),
},
});

Expand All @@ -204,21 +217,6 @@ export class ApiService extends Construct {
.join(','),
}
: {}),
PYTHON_LIB_PATH: [
// Originally from here:
// https://github.com/langgenius/dify-sandbox/blob/main/internal/static/config_default_amd64.go
'/usr/local/lib/python3.10',
'/usr/lib/python3.10',
'/usr/lib/python3',
// copy all the lib. **DO NOT** add a trailing slash!
'/usr/lib/x86_64-linux-gnu',
'/etc/ssl/certs/ca-certificates.crt',
'/etc/nsswitch.conf',
'/etc/hosts',
'/etc/resolv.conf',
'/run/systemd/resolve/stub-resolv.conf',
'/run/resolvconf/resolv.conf',
].join(','),
},
logging: ecs.LogDriver.awsLogs({
streamPrefix: 'log',
Expand All @@ -244,6 +242,73 @@ export class ApiService extends Construct {
condition: ecs.ContainerDependencyCondition.COMPLETE,
});

const user = new User(this, 'PluginUser', {});
const accessKey = new AccessKey(this, 'AccessKey', { user });
storageBucket.grantReadWrite(user);

taskDefinition.addContainer('PluginDaemon', {
image: ecs.ContainerImage.fromEcrRepository(Repository.fromRepositoryName(this, 'Repo', 'misc'), 'dp2'),
// image: ecs.ContainerImage.fromRegistry(`langgenius/dify-plugin-daemon:main-local`),
environment: {
GIN_MODE: 'release',

// The configurations of redis connection.
REDIS_HOST: redis.endpoint,
REDIS_PORT: redis.port.toString(),
REDIS_USE_SSL: 'true',

DB_DATABASE: 'dify_plugin',
DB_SSL_MODE: 'disable',

SERVER_PORT: '5002',

// AWS_ACCESS_KEY: accessKey.accessKeyId,
// AWS_SECRET_KEY: accessKey.secretAccessKey.unsafeUnwrap(),
AWS_REGION: Stack.of(this).region,

// TODO: set this to aws_s3 for persistence
PLUGIN_STORAGE_TYPE: 'aws_s3',
PLUGIN_STORAGE_OSS_BUCKET: storageBucket.bucketName,
PLUGIN_INSTALLED_PATH: 'plugins',
PLUGIN_MAX_EXECUTION_TIMEOUT: '600',
MAX_PLUGIN_PACKAGE_SIZE: '52428800',
MAX_BUNDLE_PACKAGE_SIZE: '52428800',
PLUGIN_REMOTE_INSTALLING_ENABLED: 'false',
// PLUGIN_REMOTE_INSTALLING_HOST: 'localhost',
// PLUGIN_REMOTE_INSTALLING_PORT: port.toString(),

ROUTINE_POOL_SIZE: '10000',
LIFETIME_COLLECTION_HEARTBEAT_INTERVAL: '5',
LIFETIME_COLLECTION_GC_INTERVAL: '60',
LIFETIME_STATE_GC_INTERVAL: '300',
DIFY_INVOCATION_CONNECTION_IDLE_TIMEOUT: '120',
PYTHON_ENV_INIT_TIMEOUT: '120',
DIFY_INNER_API_URL: 'http://localhost:5001',
PLUGIN_WORKING_PATH: '/app/storage/cwd',
FORCE_VERIFYING_SIGNATURE: 'true',
},
secrets: {
DB_USERNAME: ecs.Secret.fromSecretsManager(postgres.secret, 'username'),
DB_HOST: ecs.Secret.fromSecretsManager(postgres.secret, 'host'),
DB_PORT: ecs.Secret.fromSecretsManager(postgres.secret, 'port'),
DB_PASSWORD: ecs.Secret.fromSecretsManager(postgres.secret, 'password'),
PGVECTOR_USER: ecs.Secret.fromSecretsManager(postgres.secret, 'username'),
PGVECTOR_HOST: ecs.Secret.fromSecretsManager(postgres.secret, 'host'),
PGVECTOR_PORT: ecs.Secret.fromSecretsManager(postgres.secret, 'port'),
PGVECTOR_PASSWORD: ecs.Secret.fromSecretsManager(postgres.secret, 'password'),
REDIS_PASSWORD: ecs.Secret.fromSecretsManager(redis.secret),
CELERY_BROKER_URL: ecs.Secret.fromSsmParameter(redis.brokerUrl),
SECRET_KEY: ecs.Secret.fromSecretsManager(encryptionSecret),
CODE_EXECUTION_API_KEY: ecs.Secret.fromSecretsManager(encryptionSecret), // is it ok to reuse this?
DIFY_INNER_API_KEY: ecs.Secret.fromSecretsManager(encryptionSecret), // is it ok to reuse this?
SERVER_KEY: ecs.Secret.fromSecretsManager(encryptionSecret), // is it ok to reuse this?
},
logging: ecs.LogDriver.awsLogs({
streamPrefix: 'log',
}),
portMappings: [{ containerPort: 5002 }],
});

taskDefinition.addContainer('ExternalKnowledgeBaseAPI', {
image: ecs.ContainerImage.fromAsset(join(__dirname, 'docker', 'external-knowledge-api'), {
platform: Platform.LINUX_AMD64,
Expand Down
3 changes: 3 additions & 0 deletions lib/constructs/dify-services/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ export class WebService extends Construct {
// https://nextjs.org/docs/pages/api-reference/next-config-js/output
HOSTNAME: '0.0.0.0',
PORT: port.toString(),

MARKETPLACE_API_URL: 'https://marketplace.dify.ai',
MARKETPLACE_URL: 'https://marketplace.dify.ai',
},
logging: ecs.LogDriver.awsLogs({
streamPrefix: 'log',
Expand Down
4 changes: 2 additions & 2 deletions lib/dify-on-aws-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@ export class DifyOnAwsStack extends cdk.Stack {

if (!props.useCloudFront && props.domainName == null) {
cdk.Annotations.of(this).addWarningV2(
'ALBWithoutEncryption',
'You are exposing ALB to the Internet without TLS encryption. Recommended to set useCloudFront: true or domainName property.',
'Dify:albWithoutEncryption',
'You are exposing ALB to the Internet without TLS encryption. It is recommended to set useCloudFront: true or domainName property.',
);
}

Expand Down
Loading
Loading