L'ultima cosa di cui parleremo è il processo di distribuzione di ciascuno dei componenti su AWS. La pipeline di dati, il backend e il frontend sono contenuti ciascuno nei propri stack CloudFormation (raccolte di risorse AWS). Consentire la distribuzione isolata in questo modo garantisce che l'intera app non venga ridistribuita inutilmente durante lo sviluppo. Utilizzo AWS SAM (Serverless Application Model) per distribuire l'infrastruttura per ciascun componente come codice, sfruttando le specifiche del modello SAM e la CLI:
- La specifica del modello SAM: una sintassi abbreviata che funge da estensione di AWS CloudFormation per definire e configurare raccolte di risorse AWS, il modo in cui dovrebbero interagire e le eventuali autorizzazioni richieste.
- La CLI SAM: uno strumento da riga di comando utilizzato, tra le altre cose, per creare e distribuire risorse come definito in un modello SAM. Gestisce il confezionamento del codice dell'applicazione e delle dipendenze, convertendo il modello SAM nella sintassi di CloudFormation e distribuendo i modelli come stack individuali su CloudFormation.
Invece di includere i modelli completi (definizioni delle risorse) di ciascun componente, evidenzierò aree di interesse specifiche per ciascun servizio di cui abbiamo discusso nel post.
Passaggio di variabili di ambiente sensibili alle risorse AWS:
Si fa molto affidamento su componenti esterni come l'API Youtube Data, l'API OpenAI e l'API Pinecone in tutta l'applicazione. Sebbene sia possibile codificare questi valori nei modelli CloudFormation e trasmetterli come “parametri”, un metodo più sicuro consiste nel creare segreti per ciascuno in AWS SecretsManager e fare riferimento a questi segreti nel modello in questo modo:
Parameters:
YoutubeDataAPIKey:
Type: String
Default: '{{resolve:secretsmanager:youtube-data-api-key:SecretString:youtube-data-api-key}}'
PineconeAPIKey:
Type: String
Default: '{{resolve:secretsmanager:pinecone-api-key:SecretString:pinecone-api-key}}'
OpenaiAPIKey:
Type: String
Default: '{{resolve:secretsmanager:openai-api-key:SecretString:openai-api-key}}'
Definizione di una funzione Lambda:
Queste unità di codice serverless costituiscono la spina dorsale della pipeline di dati e fungono da punto di ingresso al backend per l'applicazione web. Per distribuirli utilizzando SAM, è semplice come definire il percorso del codice che la funzione dovrebbe eseguire quando richiamata, insieme a eventuali autorizzazioni e variabili di ambiente richieste. Ecco un esempio di una delle funzioni utilizzate nella pipeline di dati:
FetchLatestVideoIDsFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: ../code_uri/.
Handler: chatytt.youtube_data.lambda_handlers.fetch_latest_video_ids.lambda_handler
Policies:
- AmazonS3FullAccess
Environment:
Variables:
PLAYLIST_NAME:
Ref: PlaylistName
YOUTUBE_DATA_API_KEY:
Ref: YoutubeDataAPIKey
Recupero della definizione della pipeline di dati in Amazon States Language:
Per utilizzare Step Functions come orchestratore per le singole funzioni Lambda nella pipeline di dati, dobbiamo definire l'ordine in cui ciascuna deve essere eseguita, nonché configurazioni come il numero massimo di tentativi in Amazon States Language. Un modo semplice per farlo è utilizzare il file Studio del flusso di lavoro nella console Step Functions per creare schematicamente il flusso di lavoro, quindi prendere la definizione ASL generata automaticamente del flusso di lavoro come punto di partenza che può essere modificato in modo appropriato. Questo può quindi essere collegato nel modello CloudFormation anziché essere definito sul posto:
EmbeddingRetrieverStateMachine:
Type: AWS::Serverless::StateMachine
Properties:
DefinitionUri: statemachine/embedding_retriever.asl.json
DefinitionSubstitutions:
FetchLatestVideoIDsFunctionArn: !GetAtt FetchLatestVideoIDsFunction.Arn
FetchLatestVideoTranscriptsArn: !GetAtt FetchLatestVideoTranscripts.Arn
FetchLatestTranscriptEmbeddingsArn: !GetAtt FetchLatestTranscriptEmbeddings.Arn
Events:
WeeklySchedule:
Type: Schedule
Properties:
Description: Schedule to run the workflow once per week on a Monday.
Enabled: true
Schedule: cron(0 3 ? * 1 *)
Policies:
- LambdaInvokePolicy:
FunctionName: !Ref FetchLatestVideoIDsFunction
- LambdaInvokePolicy:
FunctionName: !Ref FetchLatestVideoTranscripts
- LambdaInvokePolicy:
FunctionName: !Ref FetchLatestTranscriptEmbeddings
Vedere Qui per la definizione ASL utilizzata per la pipeline di dati discussa in questo post.
Definizione della risorsa API:
Poiché l'API per l'app Web sarà ospitata separatamente dal front-end, dobbiamo abilitare il supporto CORS (condivisione di risorse multiorigine) quando definiamo la risorsa API:
ChatYTTApi:
Type: AWS::Serverless::Api
Properties:
StageName: Prod
Cors:
AllowMethods: "'*'"
AllowHeaders: "'*'"
AllowOrigin: "'*'"
Ciò consentirà alle due risorse di comunicare liberamente tra loro. I vari endpoint resi accessibili tramite una funzione Lambda possono essere definiti in questo modo:
ChatResponseFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.9
Timeout: 120
CodeUri: ../code_uri/.
Handler: server.lambda_handler.lambda_handler
Policies:
- AmazonDynamoDBFullAccess
MemorySize: 512
Architectures:
- x86_64
Environment:
Variables:
PINECONE_API_KEY:
Ref: PineconeAPIKey
OPENAI_API_KEY:
Ref: OpenaiAPIKey
Events:
GetQueryResponse:
Type: Api
Properties:
RestApiId: !Ref ChatYTTApi
Path: /get-query-response/
Method: post
GetChatHistory:
Type: Api
Properties:
RestApiId: !Ref ChatYTTApi
Path: /get-chat-history/
Method: get
UpdateChatHistory:
Type: Api
Properties:
RestApiId: !Ref ChatYTTApi
Path: /save-chat-history/
Method: put
Definizione della risorsa dell'app React:
AWS Amplify può creare e distribuire applicazioni utilizzando un riferimento al repository Github pertinente e un token di accesso appropriato:
AmplifyApp:
Type: AWS::Amplify::App
Properties:
Name: amplify-chatytt-client
Repository: <https://github.com/suresha97/ChatYTT>
AccessToken: '{{resolve:secretsmanager:github-token:SecretString:github-token}}'
IAMServiceRole: !GetAtt AmplifyRole.Arn
EnvironmentVariables:
- Name: ENDPOINT
Value: !ImportValue 'chatytt-api-ChatYTTAPIURL'
Una volta accessibile il repository stesso, Ampify cercherà un file di configurazione con le istruzioni su come creare e distribuire l'app:
version: 1
frontend:
phases:
preBuild:
commands:
- cd client
- npm ci
build:
commands:
- echo "VITE_ENDPOINT=$ENDPOINT" >> .env
- npm run build
artifacts:
baseDirectory: ./client/dist
files:
- "**/*"
cache:
paths:
- node_modules/**/*
Come bonus, è anche possibile automatizzare il processo di distribuzione continua definendo una risorsa del ramo che verrà monitorata e utilizzata per ridistribuire automaticamente l'app dopo ulteriori commit:
AmplifyBranch:
Type: AWS::Amplify::Branch
Properties:
BranchName: main
AppId: !GetAtt AmplifyApp.AppId
EnableAutoBuild: true
Una volta finalizzata la distribuzione in questo modo, è accessibile a chiunque tramite il collegamento reso disponibile dalla console AWS Amplify. È possibile trovare una demo registrata dell'app a cui si accede in questo modo Qui:
Fonte: towardsdatascience.com