From c6e77a3191f62531f1fa4eabd8efc485dc43c39c Mon Sep 17 00:00:00 2001 From: shaobolin Date: Fri, 8 Aug 2025 16:11:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=A7=A3=E5=86=B3=E6=96=B9=E6=A1=88fil?= =?UTF-8?q?e-processing-on-fc=20tf=E6=96=87=E4=BB=B6=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tech-solution/fc-drive-file/README.md | 67 ++++ solution/tech-solution/fc-drive-file/main.tf | 350 ++++++++++++++++++ .../tech-solution/fc-drive-file/outputs.tf | 9 + .../tech-solution/fc-drive-file/provider.tf | 3 + .../tech-solution/fc-drive-file/variables.tf | 51 +++ 5 files changed, 480 insertions(+) create mode 100644 solution/tech-solution/fc-drive-file/README.md create mode 100644 solution/tech-solution/fc-drive-file/main.tf create mode 100644 solution/tech-solution/fc-drive-file/outputs.tf create mode 100644 solution/tech-solution/fc-drive-file/provider.tf create mode 100644 solution/tech-solution/fc-drive-file/variables.tf diff --git a/solution/tech-solution/fc-drive-file/README.md b/solution/tech-solution/fc-drive-file/README.md new file mode 100644 index 0000000000..db16387b91 --- /dev/null +++ b/solution/tech-solution/fc-drive-file/README.md @@ -0,0 +1,67 @@ +## Introduction + +本示例用于实现解决方案[Serverless 事件驱动架构实践](https://www.aliyun.com/solution/tech-solution/fc-drive-file), 涉及到专有网络(VPC)、交换机(VSwitch)、云服务器(ECS)、云数据库(RDS)MySQL版、对象存储(OSS)等资源的部署。 + + + +This example is used to implement solution [Practice of Serverless Event Driven Architecture](https://www.aliyun.com/solution/tech-solution/fc-drive-file), which involves the creation and deployment of resources such as Virtual Private Cloud (VPC), VSwitch, Elastic Compute Service (ECS), ApsaraDB RDS, Object Storage Service (OSS). + + + +## Providers + +| Name | Version | +|------|---------| +| [alicloud](#provider\_alicloud) | n/a | +| [random](#provider\_random) | n/a | + +## Modules + +No modules. + +## Resources + +| Name | Type | +|------|------| +| [alicloud_db_database.rds_database](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/db_database) | resource | +| [alicloud_db_instance.rds_instance](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/db_instance) | resource | +| [alicloud_ecs_command.deploy_application_command](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ecs_command) | resource | +| [alicloud_ecs_invocation.deploy_application_invocation](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ecs_invocation) | resource | +| [alicloud_fc_function.fc_function](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/fc_function) | resource | +| [alicloud_fc_service.fc_service](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/fc_service) | resource | +| [alicloud_fc_trigger.fc_trigger](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/fc_trigger) | resource | +| [alicloud_instance.ecs_instance](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/instance) | resource | +| [alicloud_message_service_queue.queue](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/message_service_queue) | resource | +| [alicloud_oss_bucket.oss_bucket](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/oss_bucket) | resource | +| [alicloud_ram_policy.fc_demo_policy](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ram_policy) | resource | +| [alicloud_ram_role.fc_demo_role](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ram_role) | resource | +| [alicloud_ram_role.oss_event_notification_role](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ram_role) | resource | +| [alicloud_ram_role_policy_attachment.fc_demo_policy_attachment](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ram_role_policy_attachment) | resource | +| [alicloud_ram_role_policy_attachment.oss_event_notification_policy](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ram_role_policy_attachment) | resource | +| [alicloud_ram_role_policy_attachment.sts_assume_role_attachment](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/ram_role_policy_attachment) | resource | +| [alicloud_rds_account.rds_account](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/rds_account) | resource | +| [alicloud_security_group.security_group](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/security_group) | resource | +| [alicloud_security_group_rule.security_group_rule_80](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/security_group_rule) | resource | +| [alicloud_vpc.vpc](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/vpc) | resource | +| [alicloud_vswitch.vswitch1](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/vswitch) | resource | +| [alicloud_vswitch.vswitch2](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/resources/vswitch) | resource | +| [random_id.suffix](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id) | resource | +| [alicloud_caller_identity.current](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/data-sources/caller_identity) | data source | +| [alicloud_db_instance_classes.default](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/data-sources/db_instance_classes) | data source | +| [alicloud_db_zones.default](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/data-sources/db_zones) | data source | +| [alicloud_images.instance_image](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/data-sources/images) | data source | +| [alicloud_instance_types.default](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/data-sources/instance_types) | data source | +| [alicloud_ram_roles.default](https://registry.terraform.io/providers/aliyun/alicloud/latest/docs/data-sources/ram_roles) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [bucket\_name](#input\_bucket\_name) | bucket\_name,存储空间名称。长度为3~63个字符,必须以小写字母或数字开头和结尾,可以包含小写字母、数字和连字符(-);需要全网唯一性,已经存在的不能在创建。 | `string` | `"file-processing-example"` | no | +| [db\_password](#input\_db\_password) | db\_password,数据库账号密码,必须包含三种及以上类型:大写字母、小写字母、数字、特殊符号。长度为8~32位。特殊字符包括! @ # $ % ^ & * () \_ + - = | `string` | n/a | yes | +| [db\_user\_name](#input\_db\_user\_name) | db\_user\_name,RDS数据库账号。由 2 到 32 个小写字母组成,支持小写字母、数字和下划线,以小写字母开头。 | `string` | `"applets"` | no | +| [demo\_user\_name](#input\_demo\_user\_name) | demo\_user\_name,在浏览器中登录示例应用程序时的用户名。3 到 63 个字母组成。 | `string` | `"demo-user-example"` | no | +| [demo\_user\_password](#input\_demo\_user\_password) | demo\_user\_password,在浏览器中登录示例应用程序时的密码,长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)` | `string` | n/a | yes | +| [ecs\_instance\_password](#input\_ecs\_instance\_password) | ecs\_instance\_password,服务器登录密码,长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)` | `string` | n/a | yes | +| [region](#input\_region) | 地域 | `string` | `"cn-hangzhou"` | no | + \ No newline at end of file diff --git a/solution/tech-solution/fc-drive-file/main.tf b/solution/tech-solution/fc-drive-file/main.tf new file mode 100644 index 0000000000..abd284a85a --- /dev/null +++ b/solution/tech-solution/fc-drive-file/main.tf @@ -0,0 +1,350 @@ +data "alicloud_db_zones" "default" { + engine = "MySQL" + engine_version = "8.0" + instance_charge_type = "PostPaid" + category = "Basic" + db_instance_storage_type = "cloud_essd" +} + +data "alicloud_db_instance_classes" "default" { + zone_id = data.alicloud_db_zones.default.zones.0.id + engine = "MySQL" + engine_version = "8.0" + category = "Basic" + db_instance_storage_type = "cloud_essd" + instance_charge_type = "PostPaid" +} + +# Declare the data source +data "alicloud_instance_types" "default" { + availability_zone = data.alicloud_db_zones.default.zones.0.id + instance_type_family = "ecs.g7" +} + +# ECS Instance +data "alicloud_images" "instance_image" { + name_regex = "^aliyun_3_x64_20G_alibase_*" + most_recent = true + owners = "system" + instance_type = data.alicloud_instance_types.default.instance_types[0].id +} + +# Generate random integer for unique naming +resource "random_id" "suffix" { + byte_length = 4 +} +locals { + common_name = "file-processing-${random_id.suffix.hex}" +} + +# VPC and Networking +resource "alicloud_vpc" "vpc" { + vpc_name = "${local.common_name}-vpc" + cidr_block = "192.168.0.0/16" +} + +resource "alicloud_vswitch" "vswitch1" { + vpc_id = alicloud_vpc.vpc.id + cidr_block = "192.168.0.0/24" + zone_id = data.alicloud_db_zones.default.zones.0.id + vswitch_name = "${local.common_name}-vsw-web" +} + +resource "alicloud_vswitch" "vswitch2" { + vpc_id = alicloud_vpc.vpc.id + cidr_block = "192.168.1.0/24" + zone_id = data.alicloud_db_zones.default.zones.0.id + vswitch_name = "${local.common_name}-vsw-db" +} + +# Security Group +resource "alicloud_security_group" "security_group" { + #security_group_name = "${local.common_name}-sg" + vpc_id = alicloud_vpc.vpc.id +} + +resource "alicloud_security_group_rule" "security_group_rule_80" { + security_group_id = alicloud_security_group.security_group.id + type = "ingress" + ip_protocol = "tcp" + port_range = "80/80" + cidr_ip = "0.0.0.0/0" +} + +# RDS Instance +resource "alicloud_db_instance" "rds_instance" { + engine = "MySQL" + engine_version = "8.0" + instance_type = data.alicloud_db_instance_classes.default.instance_classes.0.instance_class + instance_storage = 40 + db_instance_storage_type = "cloud_essd" + vpc_id = alicloud_vpc.vpc.id + vswitch_id = alicloud_vswitch.vswitch2.id + security_group_ids = [alicloud_security_group.security_group.id] + security_ips = ["192.168.0.0/16"] + category = "Basic" + zone_id = data.alicloud_db_zones.default.zones.0.id +} + +resource "alicloud_rds_account" "rds_account" { + db_instance_id = alicloud_db_instance.rds_instance.id + account_name = var.db_user_name + account_password = var.db_password + account_type = "Super" +} + +resource "alicloud_db_database" "rds_database" { + instance_id = alicloud_db_instance.rds_instance.id + name = "applets" + character_set = "utf8mb4" +} + +# OSS Bucket +resource "alicloud_oss_bucket" "oss_bucket" { + bucket = "${var.bucket_name}-oss-bucket" + storage_class = "Standard" + force_destroy = true + + cors_rule { + allowed_methods = ["GET", "POST", "HEAD"] + allowed_headers = ["*"] + allowed_origins = ["*"] + } +} + +# RAM Role for Function Compute +resource "alicloud_ram_role" "fc_demo_role" { + name = "${local.common_name}-role" + description = "FcDemoRole" + force = true + + document = jsonencode({ + Version = "1" + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = ["ecs.aliyuncs.com", "fc.aliyuncs.com"] + } + }] + }) +} + +# RAM Policy for Function Compute +resource "alicloud_ram_policy" "fc_demo_policy" { + policy_name = "${local.common_name}-policy" + description = "FcDemoPolicy" + force = true + + policy_document = jsonencode({ + Version = "1" + Statement = [{ + Effect = "Allow" + Action = [ + "oss:PutObject", + "oss:GetObject", + "oss:GetObjectMeta", + "mns:ReceiveMessage", + "mns:BatchReceiveMessage", + "mns:DeleteMessage" + ] + Resource = ["*"] + }] + }) +} + +resource "alicloud_ram_role_policy_attachment" "fc_demo_policy_attachment" { + role_name = alicloud_ram_role.fc_demo_role.name + policy_name = alicloud_ram_policy.fc_demo_policy.policy_name + policy_type = "Custom" +} + +resource "alicloud_ram_role_policy_attachment" "sts_assume_role_attachment" { + role_name = alicloud_ram_role.fc_demo_role.name + policy_name = "AliyunSTSAssumeRoleAccess" + policy_type = "System" +} + + +resource "alicloud_instance" "ecs_instance" { + instance_name = "${local.common_name}-ecs" + image_id = data.alicloud_images.instance_image.images[0].id + instance_type = data.alicloud_instance_types.default.instance_types[0].id + security_groups = [alicloud_security_group.security_group.id] + vswitch_id = alicloud_vswitch.vswitch1.id + system_disk_category = "cloud_essd" + internet_max_bandwidth_out = 5 + password = var.ecs_instance_password + role_name = alicloud_ram_role.fc_demo_role.name +} + +# MNS Queue +resource "alicloud_message_service_queue" "queue" { + queue_name = "${var.bucket_name}-oss-queue" +} + +# ECS Run Command for Application Deployment +locals { + ecs_deploy_command = <<-SHELL +#!/bin/bash + +cat << EOF >> ~/.bashrc +export APPLETS_RDS_ENDPOINT="${alicloud_db_instance.rds_instance.connection_string}" +export APPLETS_RDS_USER="${var.db_user_name}" +export APPLETS_RDS_DB_NAME="applets" +export APPLETS_RDS_PASSWORD="${var.db_password}" +export ALIYUN_RAM_ROLE_NAME="${alicloud_ram_role.fc_demo_role.name}" +export ALIYUN_OSS_ENDPOINT="oss-cn-hangzhou.aliyuncs.com" +export ALIYUN_REGION_ID="cn-hangzhou" +export ALIYUN_OSS_BUCKET="${alicloud_oss_bucket.oss_bucket.bucket}" +export ECS_IP=`curl http://100.100.100.200/latest/meta-data/eipv4` +export ALIYUN_MNS_ENDPOINT="http://1234567890.mns.cn-hangzhou-internal.aliyuncs.com" +export ALIYUN_MNS_QUEUE="${var.bucket_name}-oss-queue" +export WANX_DEMO_USERNAME="${var.demo_user_name}" +export WANX_DEMO_PASSWORD="${var.demo_user_password}" + +export ROS_DEPLOY=true +EOF + +cat << EOF > ~/.ossutilconfig +[Credentials] +endpoint = oss-cn-hangzhou-internal.aliyuncs.com +mode = EcsRamRole +ecsRoleName = ${alicloud_ram_role.fc_demo_role.name} + +EOF + +function log_info() { + printf "%s [INFO] %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$1" +} + +function log_error() { + printf "%s [ERROR] %s\n" "$(date '+%Y-%m-%d %H:%M:%S')" "$1" +} + +function debug_exec(){ + local cmd="$@" + log_info "$cmd" + eval "$cmd" + ret=$? + echo "" + log_info "$cmd, exit code: $ret" + return $ret +} + +function pre_work(){ + source ~/.bashrc + yum upgrade & yum install -y java-1.8.0-openjdk-devel unzip zip + sudo -v ; curl https://gosspublic.alicdn.com/ossutil/install.sh | sudo bash + + wget -O fc-web-demo-job-jar-with-dependencies.jar https://help-static-aliyun-doc.aliyuncs.com/demos/fc-web-demo-job-jar-with-dependencies.jar + zip fc-web-demo-job-jar-with-dependencies.jar.zip fc-web-demo-job-jar-with-dependencies.jar + ossutil cp fc-web-demo-job-jar-with-dependencies.jar.zip oss://${alicloud_oss_bucket.oss_bucket.bucket}/fc-code/ -c ~/.ossutilconfig + +} + +debug_exec pre_work + +curl -fsSL https://help-static-aliyun-doc.aliyuncs.com/install-script/fc-drive-file/install.sh|bash +SHELL +} + +resource "alicloud_ecs_command" "deploy_application_command" { + name = "${local.common_name}-deploy-command" + description = "Deploy application on ECS" + type = "RunShellScript" + command_content = base64encode(local.ecs_deploy_command) + timeout = 300 +} + +resource "alicloud_ecs_invocation" "deploy_application_invocation" { + instance_id = [alicloud_instance.ecs_instance.id] + command_id = alicloud_ecs_command.deploy_application_command.id + + timeouts { + create = "10m" + } +} + +# Function Compute Service +resource "alicloud_fc_service" "fc_service" { + name = "${local.common_name}-service" + internet_access = true + role = alicloud_ram_role.fc_demo_role.arn +} + +# Function Compute Function +resource "alicloud_fc_function" "fc_function" { + service = alicloud_fc_service.fc_service.name + name = "${local.common_name}-function" + runtime = "java11" + handler = "com.aliyun.demo.Main::handleRequest" + memory_size = 1024 + timeout = 120 + + oss_bucket = alicloud_oss_bucket.oss_bucket.bucket + oss_key = "fc-code/fc-web-demo-job-jar-with-dependencies.jar.zip" + + depends_on = [alicloud_ecs_invocation.deploy_application_invocation] +} + +data "alicloud_ram_roles" "default" { + name_regex = "AliyunOSSEventNotificationRole" +} + +locals { + oss_role_exists = length(data.alicloud_ram_roles.default.names) > 0 +} + +# OSS Event Notification Role +resource "alicloud_ram_role" "oss_event_notification_role" { + count = local.oss_role_exists ? 0 : 1 + name = "AliyunOSSEventNotificationRole" + description = "OSS默认使用此角色来发送事件通知" + + document = jsonencode({ + Version = "1" + Statement = [{ + Action = "sts:AssumeRole" + Effect = "Allow" + Principal = { + Service = ["oss.aliyuncs.com"] + } + }] + }) +} + +resource "alicloud_ram_role_policy_attachment" "oss_event_notification_policy" { + count = local.oss_role_exists ? 0 : 1 + role_name = alicloud_ram_role.oss_event_notification_role[0].name + policy_name = "AliyunOSSEventNotificationRolePolicy" + policy_type = "System" +} + +# 获取当前账户ID +data "alicloud_caller_identity" "current" {} + +# Function Compute Trigger +resource "alicloud_fc_trigger" "fc_trigger" { + service = alicloud_fc_service.fc_service.name + function = alicloud_fc_function.fc_function.name + name = "oss-trigger" + type = "oss" + + config = jsonencode({ + events = [ + "oss:ObjectCreated:PutObject", + "oss:ObjectCreated:PostObject", + "oss:ObjectCreated:CompleteMultipartUpload" + ] + filter = { + key = { + prefix = "source" + suffix = "" + } + } + }) + + source_arn = "acs:oss:cn-hangzhou:${data.alicloud_caller_identity.current.account_id}:${alicloud_oss_bucket.oss_bucket.bucket}" + role = "acs:ram::${data.alicloud_caller_identity.current.account_id}:role/AliyunOSSEventNotificationRole" +} \ No newline at end of file diff --git a/solution/tech-solution/fc-drive-file/outputs.tf b/solution/tech-solution/fc-drive-file/outputs.tf new file mode 100644 index 0000000000..fc3d477796 --- /dev/null +++ b/solution/tech-solution/fc-drive-file/outputs.tf @@ -0,0 +1,9 @@ +output "demo_url" { + description = "Demo address." + value = "http://${alicloud_instance.ecs_instance.public_ip}" +} + +output "demo_user_name" { + description = "Login Username." + value = var.demo_user_name +} \ No newline at end of file diff --git a/solution/tech-solution/fc-drive-file/provider.tf b/solution/tech-solution/fc-drive-file/provider.tf new file mode 100644 index 0000000000..5e236864d5 --- /dev/null +++ b/solution/tech-solution/fc-drive-file/provider.tf @@ -0,0 +1,3 @@ +provider "alicloud" { + region = var.region +} \ No newline at end of file diff --git a/solution/tech-solution/fc-drive-file/variables.tf b/solution/tech-solution/fc-drive-file/variables.tf new file mode 100644 index 0000000000..862891cffa --- /dev/null +++ b/solution/tech-solution/fc-drive-file/variables.tf @@ -0,0 +1,51 @@ +variable "region" { + description = "地域" + type = string + default = "cn-hangzhou" +} + +variable "demo_user_name" { + type = string + description = "demo_user_name,在浏览器中登录示例应用程序时的用户名。3 到 63 个字母组成。" + validation { + condition = can(regex("^[a-zA-Z-]{3,63}$", var.demo_user_name)) + error_message = "3 到 63 个字母组成。" + } + default = "demo-user-example" +} + +variable "demo_user_password" { + type = string + description = "demo_user_password,在浏览器中登录示例应用程序时的密码,长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)" + sensitive = true + default = "Demo_user_password!" +} + +variable "bucket_name" { + type = string + description = "bucket_name,存储空间名称。长度为3~63个字符,必须以小写字母或数字开头和结尾,可以包含小写字母、数字和连字符(-);需要全网唯一性,已经存在的不能在创建。" + default = "file-processing-example" +} + + +variable "ecs_instance_password" { + type = string + description = "ecs_instance_password,服务器登录密码,长度8-30,必须包含三项(大写字母、小写字母、数字、 ()`~!@#$%^&*_-+=|{}[]:;'<>,.?/ 中的特殊符号)" + sensitive = true +} + +variable "db_user_name" { + type = string + description = "db_user_name,RDS数据库账号。由 2 到 32 个小写字母组成,支持小写字母、数字和下划线,以小写字母开头。" + validation { + condition = can(regex("^[a-z][a-z0-9_]{0,31}$", var.db_user_name)) + error_message = "由 2 到 32 个小写字母组成,支持小写字母、数字和下划线,以小写字母开头。" + } + default = "applets" +} + +variable "db_password" { + type = string + description = "db_password,数据库账号密码,必须包含三种及以上类型:大写字母、小写字母、数字、特殊符号。长度为8~32位。特殊字符包括! @ # $ % ^ & * () _ + - =" + sensitive = true +} \ No newline at end of file