Istnieje kilka różnych podejść do pakowania aplikacji Play w obraz Dockera. Możesz stworzyć plik Dockerfile ręcznie, dostosować istniejący plik lub po prostu użyć jakiegoś narzędzia do jego wygenerowania. Ponieważ Play posiada świetną aplikację do pakowania – sbt-native-packager – użyjemy tej ostatniej opcji.

Docker Image

W build.sbt musimy dodać:

import com.typesafe.sbt.packager.docker._ 
dockerChmodType := DockerChmodType.UserGroupWriteExecute 
dockerPermissionStrategy := DockerPermissionStrategy.CopyChown

To ustawi prawidłowe uprawnienia plików wewnątrz naszego Docker Image.

Następnym krokiem jest przejście do katalogu projektu, otwarcie powłoki sbt i uruchomienie

docker:publishLocal

To polecenie buduje nasz lokalny obraz Dockera.

Aby sprawdzić czy obraz został utworzony musimy otworzyć terminal i uruchomić docker images [APP_NAME] to nazwa Twojej aplikacji Play. Powinien pojawić się rekord reprezentujący nasz obraz z numerem TAG takim samym jak wersja aplikacji podana w pliku application.conf.

Po tym musimy uruchomić obraz za pomocą polecenia:

docker run --rm -p 9000:9000 APP_NAME:TAG
  • --rm usunie pojemnik po wyjściu
  • -p 9000:9000 mapuje port 9000 na port kontenerów 9000

Przy takim ustawieniu Twoja aplikacja Play będzie działać z tym samym plikiem konfiguracyjnym, co Twoja lokalna wersja. Przez większość czasu będziesz potrzebował oddzielnego configu dla swojego środowiska produkcyjnego i dla swojego lokalnego rozwoju. Aby to osiągnąć, będziemy:

  1. Skopiuj plik application.conf i zmień jego nazwę na prod.conf
  2. Usuń informację version o wersji z pliku konfiguracyjnego produkcji
  3. Dodaj include "application.conf" nad plikiem produkcyjnym

Dzięki temu będziemy mogli nadpisać niektóre ustawienia w środowisku produkcyjnym poprzez edycję prod.conf.

Aby uruchomić naszą aplikację Play z nowym plikiem konfiguracyjnym musimy przebudować obraz dockera i ponownie uruchomić kontener z nowym obrazem i jeszcze jednym parametrem: -Dconfig.file=/opt/docker/conf/prod.conf. Pełne polecenie powinno wyglądać podobnie do tego:

 docker run -p 9000:9000 APP_NAME:TAG -Dconfig.file=/opt/docker/conf/prod.conf 

Volumes

Aby wprowadzić zmiany w pliku konfiguracyjnym produkcji, będziesz musiał edytować go w swojej lokalnej wersji kodu źródłowego, przebudować obraz i ponownie uruchomić kontener. Chociaż działa, jest to dość uciążliwe. Lepszym sposobem byłoby skonfigurowanie woluminu Dockera. Aby to zrobić, musimy skopiować katalog conf z twojego projektu do produkcji i uruchomić swój obraz, ale z dodatkowym argumentem:

 docker run 
    -p 9000:9000 
    -v /full/path/to/conf/directory:/opt/docker/conf APP_NAME:TAG 
    -Dconfig.file=/opt/docker/conf/prod.conf

Opcja -v /full/path/to/conf/directory:/opt/docker/conf utworzy wolumin Dockera, który pozwala nam na współdzielenie plików pomiędzy systemem hosta a kontenerem Dockera. Teraz jeśli dokonamy zmian w plikach konfiguracyjnych produkcji wystarczy ponownie uruchomić kontener Docker.

Teraz, gdy możemy łatwo oddzielić konfigurację produkcyjną od naszej konfiguracji deweloperskiej, możemy uprościć docker run. Możemy stworzyć plik application.ini w naszym środowisku produkcyjnym i umieścić w nim -Dconfig.file=/opt/docker/conf/prod.conf i inne parametry uruchamiania. Po tym możemy uruchomić nasz obraz za pomocą tego polecenia:

 docker run 
    -p 9000:9000 
    -v /full/path/to/conf/directory:/opt/docker/conf 
    APP_NAME:TAG

Files

Większość aplikacji serwerowych musi obsługiwać wysyłanie plików. Aby umożliwić zapisywanie plików z aplikacji Play musimy utworzyć katalog. Aby to zrobić musimy dodać poniższy kod do build.sbt.

 dockerCommands ++= Seq(
    ExecCmd("RUN", "mkdir", 
        s"${(defaultLinuxInstallLocation in Docker).value}/APP_NAME_files"), 
)

Dzięki temu do wygenerowanego pliku Dockerfile zostanie dodane polecenie, które utworzy katalog APP_NAME_files w kontenerze.

Przy każdym restarcie kontener Docker zostaje przebudowany, co oznacza, że wszystkie pliki, które nie znajdują się w woluminach są wyrzucane, w tym pliki z wcześniej utworzonego katalogu. Aby temu zapobiec, musimy dodać kolejny wolumin do polecenia docker run. Podczas gdy jesteśmy przy tym, możemy dodać kolejny wolumin dla plików dziennika.

 docker run 
    -p 9000:9000 
    -v /full/path/to//conf:/opt/docker/conf 
    -v /full/path/to/files:/opt/docker/APP_NAME_files 
    -v /full/path/to/logs:/opt/docker/YOUR_LOG_DIR 
    APP_NAME:TAG

Tips and Tricks

Dodając dockerUpdateLatest := true do swojego build.sbt będziesz miał zawsze dostęp do najnowszej wersji swojego obrazu przez TAG latest

Jeśli hostujesz swoją bazę danych na tej samej maszynie co kontener Docker, możesz uzyskać do niej dostęp z wnętrza kontenera za pomocą host.docker.internal lub wskazując na publiczne IP maszyny.