Per spiegare nel dettaglio questa procedura partiamo da un progetto Spring Boot già pronto del tutorial precedente. Per iniziare cloniamo il progetto tramite il comando git:

git clone -b integrazione-swagger2-springboot-rest https://git.saverioriotto.it/saverioriotto.it/spring-boot-rest-tutorial.git

Per iniziare la dockerizzazione dell'applicazione, creremo un Dockerfile con il seguente contenuto:

FROM maven:3.8.2-jdk-8
LABEL maintainer="Saverio Riotto [email protected]"
WORKDIR /spring-app-rest
COPY . .
RUN mvn clean install
CMD mvn spring-boot:run

Questo file contiene le seguenti informazioni:

FROM: immagine di base, utilizzeremo maven con jdk-8
LABEL: chi gestisce l'immagine.
COPY : Docker copia tutti i file del progetto nell'immagine.
RUN: comando maven da eseguire per compilare il progetto e portare giù le dipendenze necessarie
CMD: comando maven per avviare il progetto Spring Boot

Per creare un'immagine dal nostro Dockerfile, dobbiamo eseguire il comando 'docker build':

docker build --tag=spring-boot-rest-app:latest .

Infine, siamo in grado di eseguire il container dalla nostra immagine:

docker run -p8080:8080 spring-boot-rest-app:latest

Dopo aver lanciato quest'ultimo comando la nostra applicazione sara raggiungibile tramite chiamata HTTP sulla porta 8080. (es: http://localhost:8080/api/).

In questo modo abbiamo avviato un singolo container con la nostra applicazione e senza parametri di configurazione.

Dockerizzazione tramite docker compose 

I comandi Docker e i Dockerfile sono particolarmente adatti per la creazione di singoli container. Tuttavia, se vogliamo operare su una rete di applicazioni isolate, la gestione dei container diventa rapidamente complessa e disordinata.

Per risolvere questo problema, ricorriamo al Docker Compose. Questo strumento viene fornito con il proprio file di compilazione in formato YAML ed è più adatto per la gestione di più contenitori. Ad esempio, è in grado di avviare o arrestare un insieme di servizi in un comando o unire l'output dei logs di più servizi in un unico pseudo-tty e tanto altro ancora che puoi scoprire tramite un precedente articolo chiamato: Docker Compose: Che cos'è e come si usa.

Prendiamo sempre il nostro progetto e cerchiamo di dividerlo in più servizi creando il nostro docker-compose.yml :

version: '3.9'
services:
    rest-server:
        container_name: rest-server
        build:
            dockerfile: Dockerfile
        ports:
            - $SPRING_DOCKER_PORT:$SPRING_DOCKER_PORT
        networks:
            - spring-network
        depends_on:
            - mariadb
        restart: on-failure
        env_file: ./.env
        environment:
            SPRING_APPLICATION_JSON: '{
                "spring.datasource.url"  : "jdbc:mariadb://$DB_HOST:3306/$DB_NAME?useSSL=false",
                "spring.datasource.username" : "$DB_USER",
                "spring.datasource.password" : "$DB_PASSWORD",
                "spring.jpa.properties.hibernate.dialect" : "org.hibernate.dialect.MariaDB10Dialect",
                "spring.jpa.hibernate.ddl-auto" : "update",
                "saverioriotto.app.jwtSecret" : "sRSecretKey",
                "saverioriotto.app.jwtExpirationMs" : "86400000",
                "server.port" : "$SPRING_DOCKER_PORT"}'
    mariadb:
      image: mariadb:10.2
      container_name: mariadb
      env_file: ./.env
      environment:
        MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
        MYSQL_DATABASE: ${DB_NAME}
        MYSQL_USER: ${DB_USER}
        MYSQL_PASSWORD: ${DB_PASSWORD}
      volumes:
      - mariadb:/var/lib/mysql
      networks:
        - spring-network
      restart: on-failure
      
    phpmyadmin:
        image: phpmyadmin/phpmyadmin
        container_name: phpma
        links:
        - mariadb:mariadb
        ports:
        - 80:80
        environment:
          PMA_HOST: mariadb
          PMA_PORT: 3306
        networks:
          - spring-network 
        
networks:
    spring-network:
        driver: bridge

volumes:
    mariadb:
        driver: local

version: specifica quale versione del formato deve essere utilizzata. Questo è un campo obbligatorio. Qui utilizziamo la versione 3.9, mentre il formato legacy è "1".
services: ogni oggetto in questa chiave definisce un servizio, alias container. Questa sezione è obbligatoria.
build: se fornito, docker-compose è in grado di creare un'immagine da un Dockerfile.
image: dice a Docker quale nome dovrebbe dare all'immagine quando vengono utilizzate le funzioni di build. In caso contrario, sta cercando questa immagine nella libreria o nel repository remoto.
networks: questo è l'identificatore delle reti da utilizzare. Specificato con un dato nome-valore. In questo esempio, lasciamo che docker-compose crei per noi una rete denominata di tipo 'bridge'.
depends_on: utilizzato per impostare l'ordine in cui i servizi devono essere avviati e arrestati.Nel nostro caso prima di avviare il servizio rest abbiamo bisogno che il container “mariadb” sia già stato avviato. Stessa situazione per il phpmyadmin
environment: In questa sezione dichiariamo tutte le variabili d’ambiente che vengono richiamate, nel nostro caso, da un file .env:

DB_USER=tutorial_user
DB_PASSWORD=tut04i@lu53r
DB_NAME=tutorialdb
DB_ROOT_PASSWORD=tut04i@lroo1
DB_HOST=mariadb
SPRING_DOCKER_PORT=8080

La variabile d'ambiente SPRING_APPLICATION_JSON invece contiene la configurazione parametrizzata del file application.properties di Spring Boot.

Prima di continuare, controlliamo che il nostro docker-compose.yml non abbiamo errori di sintassi con il seguente comando:

docker-compose config

Se risulta corretto possiamo avviare la build che costruirà tutti i container configurati con il seguente comando:

docker-compose up --build

Questo avvierà tutti e tre i container dichiarati come servizi in con un unico comando.

Per fermare i container, rimuoverli da Docker e rimuovere da esso le reti connesse Possiamo usare il comando opposto:

docker-compose down

Conclusione

In questo articolo abbiamo appreso come creare immagini Docker personalizzate, eseguire un'applicazione Spring Boot come container Docker e creare più container con un singolo docker-compose.yaml.

Il codice sorgente di questo tutorial lo trovi su Github.