Serverlös AI-drivad tjänst för innehållsmoderering

Questo file e stato tradotto automaticamente dall'IA, potrebbero verificarsi errori
För ungefär ett år sedan skapade jag ett blogginlägg om skapandet av en Filhanterartjänst. I detta inlägg kommer vi att använda denna tjänst som vår grund och utöka den med innehållsmoderering. Vi kommer att använda GuardDuty och Rekognition för att hjälpa till med denna uppgift. Som vanligt kommer allt att vara serverlöst och händelsedrivet.
Sammanfattning
För att uppfrisha allas minne, låt oss börja med en kort sammanfattning.
Tjänsten kommer att lagra filer i S3 och hålla en registrering av alla filer i en DynamoDB-tabell. Systemöversikten ser ut så här, med ett API som exponerar funktionalitet till användaren och sedan utför arbetet på ett serverlöst och händelsedrivet sätt.

Uppladdningsflödet initieras av en klient som anropar API:et där en Lambda-funktion skapar en försignerad S3-URL som klienten kan använda för att ladda upp filen. Vi laddar inte upp filer direkt över API:et, eftersom Amazon API Gateway har en maximal payload-storlek på 10 MB, och för att stödja alla typer av filer kommer detta att bli en begränsning.
När klienten sedan laddar upp filen till S3 kommer detta att generera en händelse, delen under den streckade linjen i bilden, vilket kommer att anropa en StepFunction som kommer att uppdatera filinventeringen.

Utökad arkitektur
I den utökade arkitekturen lägger vi till funktionalitet för att använda GuardDuty S3 malwareskanning och Rekognition för bildmoderering. GuardDuty kommer att skanna nya filer som kommer in i S3-bucketen, som jag kallar staging, en tagg kommer att läggas till på objektet och skanningsresultatet kommer att skickas till standardhändelsebussen. Skanningsresultatet, om det är OK, kommer att anropa en StepFunction som använder Rekognition för bildmoderering. Jag har implementerat samma logik i denna StepFunction och lägger till en tagg på objektet och skickar en händelse till en händelsebuss. Slutligen flyttas filer till antingen en karantän- eller lagringsobjektbuckets.
Varje del av lösningen är avkopplad och kan köra oberoende och ett saga-mönster används för att flytta logiken till nästa fas.

Nu låt oss dyka djupare in i varje del av denna lösning.
Malwareskanning
GuardDuty Malwareskanning kräver inte mycket setup. Detta är en helt hanterad funktion i GuardDuty och det enda som krävs är att konfigurera den. GuardDuty kommer då att plocka upp nya objekt automatiskt.

För att uppnå detta flöde är det enda vi behöver göra att skapa en S3MalwareProtectionPlan och tilldela den lämpliga behörigheter. En viktig sak att komma ihåg är att om du krypterar dina objekt i S3 med en Customer Managed Key, glöm inte att ge GuardDuty behörighet att dekryptera med denna nyckel.
S3MalwareProtectionPlan:
Type: AWS::GuardDuty::MalwareProtectionPlan
Properties:
Actions:
Tagging:
Status: ENABLED
ProtectedResource:
S3Bucket:
BucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:staging-bucket-name
Role: !GetAtt S3MalwareProtectionPlanRole.ArnBildmoderering
Modereringsdelen med Rekognition innebär ett par fler steg. En StepFunction anropas av resultatet från malwareskanningen och anropar Rekognition för att moderera bilden. Denna StepFunction kommer sedan att tagga objektet och skicka skanningsresultatet till EventBridge anpassad servicebuss. En viktig sak att komma ihåg är att om du krypterar dina objekt i S3 med en Customer Managed Key, glöm inte att ge behörigheter att dekryptera med denna nyckel. Rekognition ger dig ett konstigt fel Unsupported och inte ett tydligt fel på varför det misslyckades i detta fall.

För att uppnå detta flöde är det enda vi behöver göra att skapa StateMachine och konfigurera de händelser den bör anropas på.
ModerateImageStateMachineStandard:
Type: AWS::Serverless::StateMachine
Properties:
DefinitionUri: StateMachine/moderation.asl.yaml
DefinitionSubstitutions:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
Tracing:
Enabled: true
Policies:
- Statement:
- Effect: Allow
Action:
- logs:*
Resource: "*"
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:staging-bucket-name
- EventBridgePutEventsPolicy:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
- RekognitionDetectOnlyPolicy: {}
Events:
GuardDutyMalwareScanResult:
Type: EventBridgeRule
Properties:
InputPath: $.detail
Pattern:
source:
- aws.guardduty
detail-type:
- GuardDuty Malware Protection Object Scan Result
detail:
scanResultDetails:
scanResultStatus:
- NO_THREATS_FOUNDStateMachine-definitionen är ganska stor, och det finns behov av lite magi. Eftersom du inte kan lägga till taggar på ett S3-objekt, måste vi först hämta alla befintliga taggar, lägg till vår nya tagg och placera hela arrayen av taggar på objektet. Detta skulle förmodligen vara enklare att göra i en Lambda-funktion, men var är det roliga med det. Inbyggda funktioner för vinnaren....
Comment: Modera bilder med Rekognition
StartAt: Debug
States:
Debug:
Type: Pass
Next: Get Object Metadata
Get Object Metadata:
Type: Task
Parameters:
Bucket.$: $.s3ObjectDetails.bucketName
Key.$: $.s3ObjectDetails.objectKey
Resource: arn:aws:states:::aws-sdk:s3:headObject
Next: Get Object Tags
ResultPath: $.S3MetaData
Get Object Tags:
Type: Task
Parameters:
Bucket.$: $.s3ObjectDetails.bucketName
Key.$: $.s3ObjectDetails.objectKey
Resource: arn:aws:states:::aws-sdk:s3:getObjectTagging
Next: Is File Supported?
ResultPath: $.s3Tags
Is File Supported?:
Type: Choice
Choices:
- Or:
- Variable: $.S3MetaData.ContentType
StringMatches: image/png
- Variable: $.S3MetaData.ContentType
StringMatches: image/jpeg
Next: Moderate Image
Default: File Not Supported
File Not Supported:
Type: Pass
Next: Add FILE_NOT_SUPPORTED to Object Tags Array
Parameters:
ContentTypes: []
ModerationLabels: []
ModerationModelVersion: "7.0"
ThreatsFound: "-1"
ResultPath: $.RekognitionModeration
Add FILE_NOT_SUPPORTED to Object Tags Array:
Type: Pass
ResultPath: $.scanResult
Parameters:
status: FILE_NOT_SUPPORTED
newTagSet.$: >-
States.StringToJson(States.Format('[{},{}]',
States.ArrayGetItem(States.StringSplit(States.JsonToString($.s3Tags.TagSet),
'[]'),0), '{"Key":"ImageModerationStatus","Value":"FILE_NOT_SUPPORTED"}'))
Next: Tag S3 Object
Moderate Image:
Type: Task
Parameters:
Image:
S3Object:
Bucket.$: $.s3ObjectDetails.bucketName
Name.$: $.s3ObjectDetails.objectKey
Resource: arn:aws:states:::aws-sdk:rekognition:detectModerationLabels
Next: File Supported
ResultPath: $.RekognitionModeration
File Supported:
Type: Pass
Parameters:
ThreatsFound.$: States.ArrayLength($.RekognitionModeration.ModerationLabels)
ContentTypes.$: $.RekognitionModeration.ContentTypes
ModerationLabels.$: $.RekognitionModeration.ModerationLabels
ModerationModelVersion.$: $.RekognitionModeration.ModerationModelVersion
ResultPath: $.RekognitionModeration
Next: Was Threats Found?
Was Threats Found?:
Type: Choice
Choices:
- Variable: $.RekognitionModeration.ThreatsFound
NumericGreaterThan: 0
Next: Add THREATS_DETECTED to Object Tags Array
Default: Add NO_THREATS to Object Tags Array
Add THREATS_DETECTED to Object Tags Array:
Type: Pass
Parameters:
status: THREATS_FOUND
newTagSet.$: >-
States.StringToJson(States.Format('[{},{}]',
States.ArrayGetItem(States.StringSplit(States.JsonToString($.s3Tags.TagSet),
'[]'),0), '{"Key":"ImageModerationStatus","Value":"THREATS_FOUND"}'))
ResultPath: $.scanResult
Next: Tag S3 Object
Add NO_THREATS to Object Tags Array:
Type: Pass
Next: Tag S3 Object
Parameters:
status: NO_THREATS_FOUND
newTagSet.$: >-
States.StringToJson(States.Format('[{},{}]',
States.ArrayGetItem(States.StringSplit(States.JsonToString($.s3Tags.TagSet),
'[]'),0), '{"Key":"ImageModerationStatus","Value":"NO_THREATS_FOUND"}'))
ResultPath: $.scanResult
Tag S3 Object:
Type: Task
Parameters:
Bucket.$: $.s3ObjectDetails.bucketName
Key.$: $.s3ObjectDetails.objectKey
Tagging:
TagSet.$: $.scanResult.newTagSet
Resource: arn:aws:states:::aws-sdk:s3:putObjectTagging
ResultPath: null
Next: Post Scan Result Event
Post Scan Result Event:
Type: Task
Resource: arn:aws:states:::events:putEvents
Parameters:
Entries:
- Detail:
metadata: {}
data:
id.$: $.s3ObjectDetails.objectKey
status.$: $.scanResult.status
scanData.$: $.RekognitionModeration
DetailType: Moderation Scan Completed
EventBusName: ${EventBusName}
Source: ImageModeration
End: true
ResultPath: null
Avsluta uppladdningen
Den sista delen av denna lösning är att reagera på modereringen och placera innehållet antingen i karantänbuckets eller i lagringsbuckets på lång sikt. För detta använder jag två olika StepFunction med vissa skillnader i händelsen som anropar dem.

För att uppnå detta flöde skapas en ny StateMachine.
MoveFilesToPermanentStorageStateMachineStandard:
Type: AWS::Serverless::StateMachine
Properties:
DefinitionUri: StateMachine/move-to-permanent-storage.asl.yaml
DefinitionSubstitutions:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
StorageBucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:storage-bucket-name
StagingBucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:staging-bucket-name
Tracing:
Enabled: true
Policies:
- Statement:
- Effect: Allow
Action:
- logs:*
Resource: "*"
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:staging-bucket-name
- S3FullAccessPolicy:
BucketName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:storage-bucket-name
- EventBridgePutEventsPolicy:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
Events:
NoModerationThreatsFoundEvent:
Type: EventBridgeRule
Properties:
EventBusName:
Fn::ImportValue: !Sub ${CommonInfraStackName}:event-bus-name
InputPath: $.detail
Pattern:
source:
- ImageModeration
detail-type:
- Moderation Scan Completed
detail:
data:
status:
- NO_THREATS_FOUND Med en StateMachine-definition som är lite enklare att följa än bildmoderationsdelen.
Comment: Hantera resultat och kopiera filer till lagringsbuckets
StartAt: Debug
States:
Debug:
Type: Pass
Next: Get Object Metadata
Get Object Metadata:
Type: Task
Parameters:
Bucket: ${StagingBucketName}
Key.$: $.data.id
Resource: arn:aws:states:::aws-sdk:s3:headObject
Next: CopyObject
ResultPath: $.S3MetaData
CopyObject:
Type: Task
Parameters:
Bucket: ${StorageBucketName}
CopySource.$: >-
States.Format('${StagingBucketName}/{}',$.data.id)
Key.$: $.data.id
Resource: arn:aws:states:::aws-sdk:s3:copyObject
ResultPath: null
Next: DeleteObject
DeleteObject:
Type: Task
Parameters:
Bucket: ${StagingBucketName}
Key.$: $.data.id
Resource: arn:aws:states:::aws-sdk:s3:deleteObject
ResultPath: null
Next: Post Event File Moved
Post Event File Moved:
Type: Task
Resource: arn:aws:states:::events:putEvents
Parameters:
Entries:
- Detail:
metadata: {}
data:
id.$: $.data.id
status: STORED
contentType.$: $.S3MetaData.ContentType
fileSize.$: $.S3MetaData.ContentLength
DetailType: Completed
EventBusName: ${EventBusName}
Source: ImageModeration
End: true
ResultPath: nullSlutsats
Detta var ett kort inlägg om hur jag utökade min tidigare byggda filhanterare med malwareskanning och bildmoderering. Genom att endast använda hanterade tjänster gjorde detta ett ganska enkelt uppdrag.
För att få hela källkoden och distribuera den själv, besök Serverless-Handbook Image Moderation
Avslutande ord
Glöm inte att följa mig på LinkedIn och X för mer innehåll, och läs resten av mina Bloggar
Som Werner säger! Nu gå och bygg!