Nasza strona używa cookies. Korzystając ze strony, wyrażasz zgodę na używanie cookies, zgodnie z aktualnymi ustawieniami przeglądarki. Rozumiem

Docker na Windowsie - problemy i ich rozwiązania

Sprawdź, jakie problemy mogą wynikać z połączenia Dockera i Windowsa oraz dowiedz się, jak je rozwiązywać.
Docker na Windowsie - problemy i ich rozwiązania

Docker to jedno z narzędzi, które w ostatnim czasie najszybciej zyskuje popularność. Dziś jest on podstawą dużej części rozwiązań po stronie programistów, jak i administratorów. W Internecie można znaleźć wiele artykułów oraz tutoriali pokazujących jak korzystać z Dockera i jego możliwości, jednak mało mówi się o problemach wynikających z połączenia Docker + Windows.


Najczęstsze problemy

Jeżeli jesteś jedną z osób które mimo, że podążają krok w krok za najprostszym przykładem nie są w stanie wykonać danego zadania to ten artykuł jest dla Ciebie. Docker for Windows boryka się z wieloma bolączkami, których rozwiązanie jest często poza zasięgiem normalnego śmiertelnika. Przykładami mogą być:

  • docker volumes nie działa bez podania żadnego błędu,
  • network i volumes nie działa w docker-compose,
  • mój VPN sprawia, że kontenery przestają działać,
  • docker-compose czasami chce listę jako argument, czasami string,
  • Docker zacina się, kiedy w custom context próbujesz przekopiować za duży folder


Najczęściej można spotkać się ze stwierdzeniem, że zmiana na Hyper-V naprawia większość tych problemów. Jednak z własnego doświadczenia wiem, że wersja ta wprowadza nowe ograniczenia i nadal nie rozwiązuje wielu problemów. Mimo że te błędy występują od wielu wersji i są dobrze udokumentowane, to żadne z podanych tam innych rozwiązań (np. downgrade Dockera) nie pomoże ze wszystkimi.

Żeby było jasne należy pamiętać o tym, że Docker jest przełomowym narzędziem w dzisiejszym rynku IT oraz dalej prężnie rozwijanym projektem open source. Liczę, że kiedyś uda się rozwiązać wszystkie problemy, które występują w wersji na Windowsa, ale trzeba sobie radzić już dziś.


Jak było - Korzystanie z VM

Wcześniej jedynym rozwiązaniem było korzystanie z maszyny wirtualnej z postawionym tam systemem linuxowym. Zdecydowanie utrudniało to jakąkolwiek pracę nie tylko z Dockerem, ale z samym programowaniem. Zabierało to również sporą część zasobów komputera, tak potrzebnych w pracy developera. Przy dużych projektach korzysta się z połączenia vagranta i Dockera, aby uzyskać spójne środowisko. Natomiast w projektach jednoosobowych i w mniejszych zespołach znacznie łatwiej używać Dockera z WSL 2.


Jak jest - Korzystanie z WSL 2

O samym Windows Subsystem for Linux 2 można przeczytać tutaj. Uogólniając, WSL 2 pozwala użytkownikom Windowsa na uruchomienie środowiska Linuxowego bez użycia wirtualnej maszyny. Druga wersja tego narzędzia wprowadziła wiele udogodnień i aktualności w tym pełną integracje z kontenerami Dockerowymi. Możliwości jakie daje to narzędzie można już zobaczyć przy najprostszym przykładzie aplikacji napisanej w JavaScript i Node.js


Przygotowanie środowiska pod WSL 2

Aby wyzbyć się większości problemów należy otworzyć projekt przy pomocy WSL 2. Żeby to zrobić potrzebne będzie:

  1. Zainstalowane WSL 2 oraz dowolna dystrybucja linuxa (np. Ubuntu), którą można ściągnąć z Microsoft Store.

  2. Ściągnąć i zainstalować Dockera z strony projektu. Wraz z najnowszą aktualizacją można to też zrobić na wersji Home systemu Windows. O samej kompatybilność wstecznej można przeczytać tutaj.

  3. W Docker Desktop w zakładce resources należy włączyć WSL integration i wybrać zainstalowaną wersję Linuxa.

  4. Do Visual Studio Code zainstalować dodatek Remote - WSL. Umożliwia on połączenie się z poziomu edytora do subsystemu linuxowego.


Jeden prosty trick - zobacz jak

Przejdźmy teraz do samego programu. Będzie to prosta aplikacja Node.js, która zostanie otwarta przy pomocy Dockera i WSL 2.

W nowo stworzonym folderze przy pomocy komendy npm init --y zostaje zainicjonowany projekt i następuje instalacja bibliotek Express.js oraz Nodemon:

npm init -y
npm i express
npm i -g nodemon


Następnie tworze plik app.js. Struktura projektu wygląda następująco:

image-20200826104206092

Jest to prosta aplikacja, która pokazuje wpisany tekst. Celem tutaj nie jest sam program ale pokazanie działania Dockera oraz WSL 2. Sam program prezentuje się tak:

const express = require("express");
const app = express();

app.get("/", (req, res) => res.send("Witam ;)"));
app.listen(8080, () => console.log("Server is up!"));


Żeby stworzyć kontener należy utworzyć plik Dockerfile, w którym zawarte są instrukcję, które mają się wykonać w czasie budowania obrazu oraz .dockerignore, gdzie znajdują się ignorowane pliki.

#Dockerfile
FROM node:latest as build-env
ADD . /app
WORKDIR /app
RUN npm install

FROM mhart/alpine-node:latest
COPY --from=build-env /app /app
RUN npm install -g nodemon
WORKDIR /app
EXPOSE 8080
CMD ["nodemon", "app.js"]
#.dockerignore
.git
node_modules
Dockerfile
.Dockerignore


W pierwszej linijce określony zostaje obraz, na podstawie którego zostaje zbudowany projekt. Następnie dodawany jest folder /app, gdzie będzie znajduje się aplikacja. Zaraz potem bieżący katalog ustawiany jest jako katalog roboczy. Komenda RUN npm install instaluje w kontenerze biblioteki potrzebne do uruchomienia programu.

Wszystkie te kroki zawarte są w części budowy nazwanej build-env. Żeby oszczędzić miejsce na dysku, używa się multi-stage builds, które pozwala na podział fazy budowania na poszczególne etapy.

Druga część Dockerfile opiera się na obrazie alpine. Obrazy te są tworzone w taki sposób, żeby miały jak najmniejszy rozmiar. Następnie kopiowana jest część build-env. Znowu ustawiany jest katalog roboczy i określone zostaje, na jakim porcie działa aplikacja (8080).

Aktualna struktura projektu:

image-20200826152603842

Normalnie, wraz z każdą zmianą w kodzie konieczne byłoby przebudowywanie kontenera do nowszej wersji. Staje się to bardzo kłopotliwe przy większych projektach lub kiedy korzysta się z wielu połączonych ze sobą kontenerów poprzez docker-compose, gdzie czas potrzebny na tę operację może sięgać nawet parunastu minut. Aby tego unikać od strony frontendu korzysta się z Nodemona, który automatycznie restartuje serwer z nowymi zmianami. Jednak kiedy wprowadzimy zmiany u siebie na komputerze to zmiany nie zostaną odzwierciedlone w kontenerze.


Korzystanie z WSL 2 + Docker

Aby mieć stuprocentową pewność, że Docker będzie działał poprawnie należy przełączyć się na WSL 2. Żeby to zrobić:

  1. Kliknąć F1, wpisać Remote - WSL reopen folder in distro
  2. Otworzyć folder w wybranej wersji Linuxa


Okno zostanie przeładowane i projekt zostanie otwarty w WSL 2.

image-20200826165018205

Żeby zbudować obraz należy użyć komendy:

docker build -t demo .


W podanym przykładzie demo to tag obrazu, a kropka oznacza bieżący katalog. W terminalu widać poszczególne kroki, które zostały zadeklarowane w pliku Dockerfile.

Aby uruchomić aplikacje należy użyć komendy:

docker run -p 8080:8080 -v /mnt/c/Users/folder_na_dysku_c:/app demo


Gdzie:

  • flaga -p wskazuje jaki port na komputerze ma odpowiadać portowi z kontenera.
  • flaga -v definiuje połączenie pomiędzy folderami na komputerze, oraz folderami z kontenera.

/mnt/c/Users/folder_na_dysku_c to ścieżka katalogu na moim komputerze natomiast /app to katalog znajdujący się w kontenerze.

image-20200827090332058

Sama aplikacja prezentuje się następująco:

image-20200827090439577

Teraz każda zmiana wykonana w kodzie spowoduje przeładowanie się projektu i zmiany zostaną od razu odwzorowane na serwerze jak i w kontenerze.

image-20200827090631000


Podsumowanie

Przedstawione powyżej rozwiązanie pokazuje sposób, w jaki można uniknąć wielu problemów związanych z używaniem Dockera i które osobiście preferuję niezależnie od tego, jaki projekt realizuję. Mam nadzieję, że opisany przykład pomoże, chociaż odrobinę w zrozumieniu działania Dockera + WSL 2 i używania ich w sposób bezproblemowy.

Rozpocznij dyskusję

Lubisz dzielić się wiedzą i chcesz zostać autorem?

Podziel się wiedzą z 160 tysiącami naszych czytelników

Dowiedz się więcej