Aplikacja Spring Boot w kontenerze Dockera

  1. Instalacja Dockera w maszynie wirtualnej
  2. Konfiguracja IntelijIDEA
  3. Aplikacja SpringBoot w kontenerze Dockera
  4. Własne repozytorium Dockery Registry
  5. Publikacja obrazu aplikacji w rejestrze

W swoim rozwiązaniu wykorzystuję maszynę wirtualną Ubuntu, na której zainstalowałem Dockera. Takie rozwiązanie przyjąłem ze względu na problemy z kompatybilnością wirtualizacji WIndows 10 opartej na Hyper-V z VMWare Workstation czy VirtualBox.

Poniżej opiszę jak skonfigurować środowisko IntelijIDEA do pracy z Dockerem w takiej konfiguracji.

Instalacja Dockera w maszynie wirtualnej

Po zainstalowaniu Dockera na wirtualnym linuksie (używam Ubuntu 20.04 LTS) modyfikuję plik sterujący usługą Dockera /lib/systemd/system/docker.service

EnvironmentFile=/etc/default/docker
ExecStart=/usr/bin/dockerd -H fd:// $DOCKER_OPTS

Jak widzisz, do podania parametrów konfiguracyjnych usługi użyłem zewnętrznego pliku. Poniżej zawartość /etc/default/docker

DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4 -H tcp://192.168.1.199:2375 -H unix:///var/run/docker.sock"

Zwróć uwagę na adres IP – w moim przypadku to adres maszyny wirtualnej 192.168.1.199. Docker będzie dostępny na porcie 2375.

Następnie wczytaj zmiany i zrestartuj Dockera:

sudo systemctl daemon-reload 
sudo systemctl restart docker

Jeżeli używasz firewalla, to otwórz port 2375:

sudo sudo ufw allow 2375/tcp

i upewnij się, że port ma status open.

nmap -p 2375 192.168.1.199

Konfiguracja IntelijIDEA

W menu Settings skonfiguruj połączenie z Dockerem zainstalowanym na maszynie wirtualnej (Ctrl+Alt+S).

Jeśli wszystko poszło OK, to możesz połączyć się z Dockerem za pomocą przycisku Connect.

Aplikacja SpringBoot w kontenerze Dockera

Skoro mamy już skonfigurowanego Dockera, to spróbujemy uruchomić w nim Spring Bootową aplikację.

Testowana aplikacja to prosty @RestController

package pl.javascratcher.hellodocker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class HelloDockerApplication {

    @GetMapping
    public String index() {
        return "It Works!";
    }

    public static void main(String[] args) {
        SpringApplication.run(HelloDockerApplication.class, args);
    }

}

Jeżeli nie zmieniłeś domyślnego portu aplikacji, to pod adresem: http://localhost:8080 zobaczysz tekst “It Works!”.

Dodaj do projektu plik Dockerfile, w którym będą znajdować się instrukcje niezbędne do zbudowania obrazu.

FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

Teraz pozostaje tylko zbudować obraz i uruchomić kontener.

Jeżeli proces nie powiedzie się i otrzymasz błąd Failed to deploy ‘ Dockerfile: Dockerfile’: Can’t retrieve image ID from build stream

to zbuduj plik JAR za pomocą Mavena.

mvn package

Skonfigurujmy jeszcze proces tworzenia i uruchamiania obrazu:

podając nazwę kontenera oraz tag, mapujemy również port aplikacji 8080.

Po uruchomieniu kontenera, możesz połączyć się z aplikacją podając adres wirtualnego hosta: http://192.168.1.199:8080

Po stronie linuksa maszyny wirtualnej możesz sprawdzić, że obraz został zainstalowany:

sudo docker images

Możesz również uruchomić kontener:

sudo docker run -d -p 8080:8080 hello-docker

Własne repozytorium Dockery Registry

Opublikowanie obrazu w rejestrze umożliwia łatwe wdrożenie aplikacji. Do pobrania i uruchomienia aplikacji wystarczy jedna komenda docker run. Swój obraz możesz opublikować w jednym z ogólnodostępnych rejestrów, możesz też utworzyć własny rejestr. W poniższym przykładzie skorzystam z tej drugiej opcji.

Najprostszą opcją na uruchomienie rejestru jest po prostu uruchomienie kontenera:

docker run -d -p 5000:5000 --name registry registry:2

Zatrzymanie rejestru:

docker container stop registry

Konfiguracja

W związku z tym, iż umieszczam rejestr na serwerze publicznym, skonfiguruję certyfikat SSL, który pozwoli na użycie protokołu HTTPS zamiast HTTP.

W pliku docker-compose.yml zdefiniuję parametry rejestru:

registry:
  restart: always
  image: registry:2
  container_name: registry
  ports:
    - 8443:443
  environment:
    REGISTRY_HTTP_ADDR: 0.0.0.0:443 
    REGISTRY_AUTH: htpasswd
    REGISTRY_HTTP_TLS_CERTIFICATE: /certs/server.crt
    REGISTRY_HTTP_TLS_KEY: /certs/server.key
    REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
    REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
  volumes:
    - /registry/data:/var/lib/registry
    - /registry/certs:/certs
    - /registry/auth:/auth

W powyższym przykładzie mapuje port na 8443 (pod tym adresem rejestr będzie dostępny) oraz wolumeny:

  • /registry/data – miejsce na dane
  • /registry/auth -lokalizacja pliku htpasswd z danymi do autoryzacji
  • /registry/certs – katalog, w którym umieszczę certyfikat server.crt oraz klucz prywatny server.key

Certyfikat SSL

Jeżeli nie dysponujesz certyfikatem SSL, to możesz wygenerować do tego celu certyfikat typu self-signed .

Uwaga! Przed wygenerowaniem certyfikatu zmodyfikuj konfigurację OpenSSL – plik /usr/lib/ssl/openssl.cnf dodając w sekcji v3_ca klucz subjectAltName, wskazujący na twój adres IP.

[ v3_ca ]
subjectAltName = IP:192.168.1.199


Jeżeli tego nie zrobisz, podczas łączenia się z rejestrem otrzymasz komunikat o błędzie:

x509: cannot validate certificate for because it doesn’t contain any IP SANs

W celu wygenerowania certyfikatu wykonaj:

sudo openssl req -nodes -new -x509 -keyout server.key -out server.crt

Utworzone pliku server.key i server.crt umieść w /registry/certs

Zainstaluj również certyfikat w systemie:

sudo cp server.crt /usr/local/share/ca-certificates/server.crt
sudo update-ca-certificates

Brak zainstalowanego certyfikatu skutkuje podczas łączenia się z rejestrem błędem:

x509: certificate signed by unknown authority

Autoryzacja

W konfiguracji rejestru używam pliku htaccess, w którym znajdują się dane niezbędne do zalogowania. Poniższe polecenie dodaje użytkownika javascratches z hasłem topsecret

sudo docker run \
  --entrypoint htpasswd \
  httpd:2 -Bbn javascratches topsecret > /registry/auth/htpasswd

Możemy teraz uruchomić rejestr:

sudo docker-compose up -d

Logowanie do rejestru:

sudo docker login localhost:8443

Po wprowadzeniu użytkownika i hasła powinieneś otrzymać komunikat o udanym zalogowaniu.

Publikacja obrazu aplikacji w rejestrze

Przed przesłaniem obrazu do rejestru musisz nadać mu odpowiedni tag

sudo docker image tag hello-docker localhost:8443/hello-docker

Teraz możesz opublikować obraz:

sudo docker push localhost:8443/hello-docker

Swój obraz możesz publikować w rejestrze bezpośrednio z IntelijIDEA, najpierw dodaje rejestr:

Aby przesłać obraz do rejestru wywołaj polecenie Push Image..

a następnie uzupełnij nazwę repozytorium i tag:

Po przesłaniu obrazu możesz uruchamiać kontener podając adres rejestru oraz nazwę repozytorium.

sudo docker run 192.168.1.199:8443/javascratches/hello-docker:v1

Zamiast lokalnego adresu IP możesz rzecz jasna użyć adresu IP swojego serwera VPS.