Skip to content

Commit d6ef026

Browse files
committed
Gestione annotazioni versione AWS onprem e su EC2
1 parent 491b05f commit d6ef026

File tree

16 files changed

+692
-56
lines changed

16 files changed

+692
-56
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
annotazioni-key.pem
2+
13
.vscode
24
.vscode/settings.json
35

README.md

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Un sistema di gestione annotazioni multi-modulo basato su Spring Boot che implem
1616
- [🐳 Deploy e utilizzo con DockerHub](#-deploy-e-utilizzo-con-dockerhub)
1717
- [🐳 Deploy completo con Docker Compose](#-deploy-completo-con-docker-compose)
1818
- [☸️ Deploy su Minikube (Kubernetes locale)](#️-deploy-su-minikube-kubernetes-locale)
19+
- [🐳 Deploy AWS-onprem (MySQL e DynamoDB Local)](#-Deploy-AWS-onprem-MySQL-e-DynamoDB-Local)
20+
- [🚀 Deploy su AWS EC2](#-Deploy-su-AWS-EC2)
1921
- [📝 Roadmap / TODO](#-todo--roadmap)
2022
- [🚧 Coming Soon](#-coming-soon)
2123
- [🔒 Sicurezza](#-sicurezza)
@@ -24,7 +26,6 @@ Un sistema di gestione annotazioni multi-modulo basato su Spring Boot che implem
2426
- [Deployment AWS ECS/Fargate](#deployment-aws-ecsfargate)
2527

2628

27-
2829
## 🛠️ Struttura progetto:
2930
Il progetto segue i principi dell'*Hexagonal Architecture* (Ports and Adapters) e si basa su un'architettura a microservizi modulare:
3031
```
@@ -243,6 +244,10 @@ L'immagine ufficiale dell'applicazione è pubblicata su [DockerHub](https://hub.
243244
docker build -t alnao/annotazioni:latest .
244245
docker push alnao/annotazioni:latest
245246
```
247+
oppure lanciare lo script
248+
```bash
249+
./script/push-image-docker-hub.sh
250+
```
246251
- **Pull dell'immagine**:
247252
```bash
248253
docker pull alnao/annotazioni:latest
@@ -399,20 +404,98 @@ L’applicazione e i database posso essere eseguiti anche su Minikube, l’ambie
399404
- Viene usata l'immagine `alnao/annotazioni:latest` su dockerHub e non una immagine creata in sistema locale.
400405
401406
407+
## 🐳 Deploy AWS-onprem (MySQL e DynamoDB Local)
408+
409+
Per simulare l'ambiente AWS in locale (MySQL come RDS, DynamoDB Local, Adminer, DynamoDB Admin UI, Spring Boot profilo AWS):
410+
- Comando per la creazione dello stack docker
411+
```bash
412+
docker-compose -f script/aws-onprem/docker-compose.yml up
413+
```
414+
- lo stack crea anche tabelle su Dynamo e tabella MySql
415+
- presenta anche uno script `start-all.sh` ma non è necessario usarlo
416+
- Servizi disponibili:
417+
- **Frontend**: [http://localhost:8085](http://localhost:8085)
418+
- **Backend API**: [http://localhost:8085/api/annotazioni](http://localhost:8085/api/annotazioni)
419+
- **Adminer (MySQL)**: [http://localhost:8086](http://localhost:8086)
420+
- Server: `mysql`
421+
- User: `annotazioni_user`
422+
- Password: `annotazioni_pass`
423+
- Database: `annotazioni`
424+
- **DynamoDB Admin**: [http://localhost:8087](http://localhost:8087)
425+
426+
- Per prova è stato scritto e poi commentato anche un Nginx/Proxy
427+
- Il frontend statico è servito da Nginx su `localhost:8080`.
428+
- Le chiamate API `/api/annotazioni` sono automaticamente proxyate verso il backend Spring Boot su `localhost:8085`.
429+
- Non è necessario modificare `app.js` o la logica frontend: tutte le chiamate API funzionano come in produzione.
430+
- Se modifichi i file statici, riavvia solo il servizio Nginx:
431+
```bash
432+
docker-compose restart nginx
433+
```
434+
- Se la porta 8080 è occupata, puoi cambiare la porta esposta nel servizio Nginx nel `docker-compose.yml`.
435+
- Per vedere i log di un servizio:
436+
```bash
437+
docker-compose logs -f <nome-servizio>
438+
```
439+
- Per fermare tutto e rimuovere i componenti:
440+
```bash
441+
docker-compose -f script/aws-onprem/docker-compose.yml down
442+
```
443+
- presente anche uno script `stop-all.sh`
444+
445+
446+
## 🚀 Deploy su AWS EC2
447+
Questa modalità consente di eseguire l'intero stack annotazioni su AWS EC2, con provisioning completamente automatizzato di tutte le risorse cloud necessarie (Aurora MySQL, DynamoDB, EC2, Security Group, IAM Role, KeyPair, ecc.) tramite script Bash e AWS CLI.
448+
- Prerequisiti:
449+
- AWS CLI installata e configurata (`aws configure`)
450+
- Credenziali AWS con permessi minimi per EC2, RDS, DynamoDB, IAM, VPC, KeyPair
451+
- Chiave SSH per accesso sicuro all'istanza EC2 (verrà generata se non presente)
452+
- Lo script usa la VPC di default di un account e crea il security group necessario
453+
- Provisioning e deploy automatico:
454+
- **Avvia provisioning e deploy**:
455+
```bash
456+
./script/aws-ec2/start-all.sh
457+
```
458+
Lo script esegue in sequenza:
459+
- Creazione VPC, Security Group, KeyPair, IAM Role
460+
- Provisioning Aurora MySQL (RDS) e DynamoDB
461+
- Upload e lancio script di inizializzazione SQL su Aurora (init-mysql.sql)
462+
- Creazione e configurazione istanza EC2 (Amazon Linux 2)
463+
- Deploy automatico del jar Spring Boot e avvio con profilo `aws`
464+
- Configurazione variabili d'ambiente e sicurezza SSH
465+
- **Accesso all'applicazione**:
466+
- L'output finale dello script mostra l'IP pubblico EC2 e la porta applicativa (default 8080)
467+
- Accedi da browser: `http://<EC2_PUBLIC_IP>:8080`
468+
- Accesso SSH:
469+
```bash
470+
ssh -i annotazioni-key.pem ec2-user@<EC2_PUBLIC_IP>
471+
```
472+
- **Teardown e cleanup**:
473+
Per rimuovere tutte le risorse create (EC2, RDS, DynamoDB, Security Group, KeyPair, ecc):
474+
```bash
475+
./script/aws-ec2/stop-all.sh
476+
```
477+
- Note
478+
- Lo script supporta anche il caricamento dello script SQL da S3 (opzionale, vedi variabile `INIT_SQL_S3_PATH`)
479+
- Il provisioning è idempotente: puoi rilanciare lo script senza duplicare risorse
480+
- Tutte le risorse sono taggate per facile identificazione e cleanup
481+
- Potrebbe avere qualche problema in fase di avvio perchè il database non viene *agganciato* dal microservizio, non so il perchè, ho *ignorato* il problema visto che conviene usare ECS e EKS.
482+
483+
402484
## 📝 TODO / Roadmap
403485
- ✅ Creazione progetto e primo test con pagina web di esempio
404486
- ✅ Introduzione modifica nota e documento con vecchie versioni delle note
405487
- ✅ Configurazione di OpenApi-Swagger e Quality-SonarQube
406-
- ✅ Build e deploy su DockerHub, configurazione di docker-compose
407-
- ✅ Run on Minikube
408-
- 🚧 Test con Mysql e DynamoDB
409-
- 🚧 Deploy su AWS su EC2 / Lightsail
488+
- ✅ Build e deploy su DockerHub, configurazione di docker-compose con MongoDb e Postgresql
489+
- ✅ Esecuzione su Minikube con yaml dedicati
490+
- ✅ Esecuzione con docker-compose della versione AWS su sistema locale con Mysql e DynamoDB
491+
- ✅ Deploy su AWS su EC2 con script scritto in AWS-CLI per il provisioning delle risorse necessarie
492+
- 🚧 Storico annotazioni su Dynamo
410493
- 🚧 Deploy su AWS su Fargate/ECS
411494
- 🚧 Deploy su AWS su EKS
412495
- 🚧 Sistema di caching e ottimizzazione
413496
- 🚧 Sistem di Deploy con Kubernetes Helm charts
414-
- 🚧 Autenticazione e autorizzazione (Spring Security) e token Jwt
415497
- 🚧 Gestione multiutente e versioning annotazioni
498+
- 🚧 Autenticazione e autorizzazione (Spring Security) e token Jwt
416499
- 🚧 Export/Import annotazioni (JSON, CSV)
417500
- 🚧 Export/Import annotazioni (Kafka)
418501
- 🚧 Notifiche real-time (WebSocket)

adapter-aws/src/main/java/it/alnao/annotazioni/aws/config/AwsConfiguration.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package it.alnao.annotazioni.aws.config;
22

3+
import org.springframework.beans.factory.annotation.Value;
34
import org.springframework.boot.autoconfigure.domain.EntityScan;
45
import org.springframework.context.annotation.Bean;
56
import org.springframework.context.annotation.Configuration;
67
import org.springframework.context.annotation.Profile;
78
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
9+
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
10+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
811
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
12+
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
913
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
1014
import software.amazon.awssdk.regions.Region;
1115
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
@@ -16,12 +20,36 @@
1620
@EntityScan(basePackages = "it.alnao.annotazioni.aws.entity")
1721
public class AwsConfiguration {
1822

23+
@Value("${AWS_REGION:eu-central-1}")
24+
private String awsRegion;
25+
26+
@Value("${AWS_ACCESS_KEY_ID:dummy}")
27+
private String awsAccessKeyId;
28+
29+
@Value("${AWS_SECRET_ACCESS_KEY:dummy}")
30+
private String awsSecretAccessKey;
31+
32+
@Value("${DYNAMODB_ENDPOINT:}")
33+
private String dynamodbEndpoint;
34+
1935
@Bean
2036
public DynamoDbClient dynamoDbClient() {
37+
AwsCredentialsProvider credentialsProvider;
38+
if (awsAccessKeyId != null && !awsAccessKeyId.equals("") && awsSecretAccessKey != null && !awsSecretAccessKey.equals("")) {
39+
credentialsProvider = StaticCredentialsProvider.create(AwsBasicCredentials.create(awsAccessKeyId, awsSecretAccessKey));
40+
} else {
41+
credentialsProvider = DefaultCredentialsProvider.create();
42+
}
43+
// Build the client directly, chaining methods, without declaring a Builder variable
2144
return DynamoDbClient.builder()
22-
.region(Region.US_EAST_1) // Configurabile via application.yml
23-
.credentialsProvider(DefaultCredentialsProvider.create())
24-
.build();
45+
.region(Region.of(awsRegion))
46+
.credentialsProvider(credentialsProvider)
47+
.applyMutation(b -> {
48+
if (dynamodbEndpoint != null && !dynamodbEndpoint.isEmpty()) {
49+
b.endpointOverride(java.net.URI.create(dynamodbEndpoint));
50+
}
51+
})
52+
.build();
2553
}
2654

2755
@Bean

adapter-aws/src/main/java/it/alnao/annotazioni/aws/entity/AnnotazioneStoricoDynamoEntity.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package it.alnao.annotazioni.aws.entity;
22

3+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
4+
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
35
import java.time.LocalDateTime;
46

7+
@DynamoDbBean
58
public class AnnotazioneStoricoDynamoEntity {
69
private String id;
710
private String idOriginale;
@@ -15,7 +18,7 @@ public class AnnotazioneStoricoDynamoEntity {
1518
private Integer priorita;
1619
private LocalDateTime dataModifica;
1720

18-
// Getters and Setters
21+
@DynamoDbPartitionKey
1922
public String getId() { return id; }
2023
public void setId(String id) { this.id = id; }
2124
public String getIdOriginale() { return idOriginale; }
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package it.alnao.annotazioni.aws.repository;
2+
3+
import it.alnao.annotazioni.aws.entity.AnnotazioneStoricoDynamoEntity;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.context.annotation.Profile;
6+
import org.springframework.stereotype.Repository;
7+
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
8+
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
9+
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
10+
import software.amazon.awssdk.enhanced.dynamodb.model.PageIterable;
11+
import software.amazon.awssdk.enhanced.dynamodb.model.ScanEnhancedRequest;
12+
13+
import java.util.List;
14+
import java.util.stream.Collectors;
15+
16+
@Repository
17+
@Profile("aws")
18+
public class AnnotazioneStoricoDynamoRepositoryDdbImpl implements AnnotazioneStoricoDynamoRepository {
19+
private final DynamoDbTable<AnnotazioneStoricoDynamoEntity> table;
20+
21+
@Autowired
22+
public AnnotazioneStoricoDynamoRepositoryDdbImpl(DynamoDbEnhancedClient dynamoDbEnhancedClient) {
23+
this.table = dynamoDbEnhancedClient.table("annotazioni_storico", TableSchema.fromBean(AnnotazioneStoricoDynamoEntity.class));
24+
}
25+
26+
@Override
27+
public AnnotazioneStoricoDynamoEntity save(AnnotazioneStoricoDynamoEntity entity) {
28+
if (entity.getId() == null || entity.getId().isEmpty()) {
29+
entity.setId(java.util.UUID.randomUUID().toString());
30+
}
31+
table.putItem(entity);
32+
return entity;
33+
}
34+
35+
@Override
36+
public List<AnnotazioneStoricoDynamoEntity> findByIdOriginale(String idOriginale) {
37+
// DynamoDB non supporta query su attributi non chiave senza GSI, quindi si fa scan e filtro in memoria
38+
PageIterable<AnnotazioneStoricoDynamoEntity> results = table.scan(ScanEnhancedRequest.builder().build());
39+
return results.items().stream()
40+
.filter(e -> idOriginale.equals(e.getIdOriginale()))
41+
.collect(Collectors.toList());
42+
}
43+
}

adapter-aws/src/main/java/it/alnao/annotazioni/aws/repository/AnnotazioneStoricoDynamoRepositoryImpl.java

Lines changed: 0 additions & 22 deletions
This file was deleted.

adapter-aws/src/test/java/it/alnao/annotazioni/aws/repository/AnnotazioneStoricoDynamoRepositoryImplTest.java

Lines changed: 0 additions & 12 deletions
This file was deleted.

0 commit comments

Comments
 (0)