Po co to?Nr 1 W grudniu 2020 roku zrobiłem webinar na temat Kubernetes i Dockera. Postanowiłem zrobić podobny poradnik dla początkującego tylko w formie tekstowej. Poza tym bądźmy szczerzy kopiowanie komend z filmu nie jest ciekawym doświadczeniem.

Technika idzie bardzo szybko. Już w Visual Studio 2019 mając zainstalowany .NET 5 możesz jawnie tworzyć projekty ASP.NET CORE, które domyślnie łączą się z Dockerem i debugować przy jego użyciu aplikację.

Wszystko fajnie pięknie tylko : Co to jest Docker i Co jest Kubernetes ? Do czego to służy  ?

W praktyce jako programista któregoś dnia przychodzi do Ciebie admin i mówi, że od tej pory robimy aplikacje w Kubernetes i każe Ci zrobić konfigurację w taki sposób, aby Kubernetes to widział.

Admin i programista nie wytłumaczy Ci

A powiesz mi jak?

Admin i programista

No cóż, bądźmy szczery firma oczekuje od Ciebie, że ty to już wszystko wiesz i nie ma czasu na szkolenia i kursy dla Ciebie.

Potem oglądasz jakiś tutorial na YouTube i myślisz sobie, że jeśli jeszcze raz będziesz musiał słuchać coś na temat historii Kubernetes, k8s i dlaczego coś tam o Borgach i kotwicach to Ciebie krew zaleje.

Nic nie gadaj o borgach

Jak więc nauczyć się jak najszybciej podstaw, tak abyś mógł z dumą powiedzieć, że rozumiesz Kubernetes i Dockera na tyle aby się tym chwalić albo wpisać to w CV

Od kiedy ty umiesz dockera

Idealny programista w końcu musisz rozumieć proces wdrażania aplikacji.

idealny programista

Wielki skrót

Na początku masz swoją aplikację. Może ona być napisana w C#, Javie, Go, w Pythonie. Nie ma to znaczenia. 

Jak wiesz każda technologia, język programowania wymaga swojego środowiska uruchomieniowego. Dla C# jest to .NET. Dla Javy jest to JDK. 

Docker kontekst czym steruje

Warto także pamiętać, że aplikacja potrzebuje też kontekstu, w jakim systemie operacyjnym się uruchamia. Może to być Linux, dlaczego nie.

Ostatecznie te wszystkie informacje, które są potrzebne do uruchomienia twojego kodu są pakowane do obrazu. Docker takie obrazy potrafi tworzyć, chociaż warto zaznaczyć, że istnieje wiele innych programów, które  potrafią także tworzyć obrazy.

Mając obraz swojej aplikacji możesz go później go uruchamiać na każdym komputerze, nawet jeśli system operacyjny jest inny i nawet gdy nie ma na nim odpowiedniego środowiska uruchomieniowego.

Jak sobie uświadamiasz jest to spełnienie marzeń dla administratorów, którzy muszą konfigurować każde środowiska na komputerach. 

Kubernetes nie koniecznie steruje dockerem

A gdzie w tym wszystkim jest Kubernetes? Masz więc obrazy swoich aplikacji. Przydałby Ci się jakiś bardzo zaawansowany orkiestrator, który by tymi obrazami wszystkim sterował.

Co potrafi na przykład Kubernetes? Może tworzyć repliki twoich aplikacji. Może zarządzać połączeniami sieciowymi do twoich aplikacji.  Gdy jakaś aplikacja będzie chora to ją uśmierci i stworzy automatycznie nową instancję twojej aplikacji. 

Dodatkowo Kubernetes ma gotowy system sekretów i obsługi plików konfiguracyjnych. 

Kubernetes steruje wszystkim

W przyszłości Kubernetes nie będzie korzystał już Dockera. Na szczęście filozofia się nie zmienia więc Kubernetes będzie zarządzał każdym obrazem niezależnie jak on został utworzony.

Jak więc praca wygląda, jeśli mówimy tylko o Docker ?

  • Docker utworzy obraz twojej aplikacji na podstawie pliku "dockerfile"
  • Docker potem uruchamia obraz i teraz "to" nazywamy kontenerem

Jak więc praca wygląda, jeśli mówimy Kubernetes ?

  • Korzystasz z Dockera lub czegoś innego, aby utworzyć obraz
  • Ten obraz potem jest umieszczony w abstrakcyjnych jednostkach jak :
    • Pod który może zawierać 1 lub więcej obrazów / kontenerów
    • Deployment, który może zawierać repliki Pod-ów
    • Serwisy, który komunikują się z wybranymi Deploymentami
    • To przykładowy schemat tego, co może zawierać się w czym. Ty możesz zrobić to innaczej
  • Sterujesz tym obrazem/kontenerem. O czym napiszę później 

Podsumowując.  Docker tworzy obraz. A uruchomionym obraz nazywamy kontenerem. 

Chociaż czytając wiele tutorial więc, że obie definicję są używane zamienię. 

Warto też zaznaczyć, że Kubernetes potrafi także uruchamiać wirtualne maszyny. Nie wszystko musi być kontenerem.

Po co to ?

Jeśli chodzi o Kubernetes to jego zalety najbardziej widać w chmurze. Możesz w końcu zainstalować w chmurze system operacyjny jak Windows czy Linus i na tym systemie operacyjnym wyodrębniać procesy i zasoby.

Możesz też w chmurze Amazon, Azure, Google zainstalować Kubernetes i w taki sposób wyodrębniać zasoby do mikroserwisów i natywnych aplikacji chmurowych.

Zmienia to także podejście do tworzenia infrastruktury. Infrastruktura zawsze była problemem programisty, ale wcześniej mogłeś to zwalić na adminów i architektów.

O samej infrastrukturze można zrobić oddzielny wpis. O czym teraz warto pamiętać. O tym, że jedna aplikacja powinna robić jedną rzecz. Stworzyć tak aplikację, aby można było ją skalować horyzontalnie w zależności od odpowiedzialności. 

A jakie mamy zalety kontenerów / obrazów, bo już trochę poruszyłem tą kwestie.

Zalety kontenerów

Skoro obrazy twojej aplikacji można przenosić do każdego komputera bez problemu to zalety pojawia się same. 

Przyspiesza to pracę między zespołami. Eliminuje to konflikty na środowiskach. Wszystkie środowiska dla danej aplikacji są spójne skoro są zawarte one w obrazie. Czyli już nikt Ci nie powie, że działa coś tylko na jego maszynie.

Dzięki temu szybciej dostarczysz aplikację i zrobisz wdrożenie.

Inne zalety kontenerów są następujące :

  • Przenośność
    • Łatwo jest przekazać obraz dalej
  • Wyizolowany
    • Inne aplikacje nie mają wpływu na twoją
  • Stały
    • Raz utworzony obraz magicznie się nie zmieni
  • Lekki
    • Kontener waży mniej niż maszyna wirtualna

Podsumowując to wszystko w dwóch zdaniach :

"Zbuduj raz. Zrób deploy wszędzie"

Kontener kontra maszyna wirtualna

Maszyny wirtualne szybko nie zniknął. Maszyna wirtualna automatycznie nie jest gorsza też od kontenera. Jak to mówią to zależy.

Jeśli masz aplikację legacy to prawdopobnie najlepiej jest ją umieścić w maszynie wirtualnej. Jeśli twoja aplikacja ma skomplikowaną konfigurację lub musi być bardzo dobrze zabezpieczona to lepiej jest ją umieścić w maszynie wirtualnej.

Pamiętaj do maszyny wirtualnej możesz się zalogować jak do normalnego systemu operacyjnego. 

Jak to jednak możliwe, że kontener zajmuje mniej miejsca.

https://blog.netapp.com/blogs/containers-vs-vms

https://blog.netapp.com/blogs/containers-vs-vms

W kontenerze informację o systemie operacyjnym, jak i o środowisku uruchomieniowym są pobierane z jedno źródła. 

Maszyna wirtualna może mieć wiele systemów operacyjnych i środowisk w sobie.

Najlepiej użyć takiej metafory aby to wyjaśnić.

Maszyna wirtualna jest jak dom gdzie prąd, woda, ogrzewanie dochodzi z jednego źródła. Każdy dom ma swoje 1 źródło tych zasobów.

Kontener jest jak mieszkanie w bloku gdzie prąd,woda, ogrzewanie jest współdzielone pomiędzy innymi mieszkaniami w bloku.

Esencji to sprawia, że kontener waży mniej niż maszyna wirtualna. 

Przypadki użycia

Wróćmy do kwesti po co to wszystko raz jeszcze. Jakie przypadki użycia będzie mógł zrobić korzystając z Dockera i Kubernetesa :

  • Emulowanie produkcji
  • Stworzenie środowiska na Testy end-to-end
  • Sekrety/Konfiguracje gotowe mechanizmy
  • Pewność, że aplikacja skaluje się poprawnie
  • Tworzenie testów wydajnościowych
  • Pomoc dla adminów, aby programiści stali się DEV-OPS
  • Ciekawe strategie wdrażania aplikacji
  • Migracja do chmury
  • Scenariusze CI/CD

Docker i Kubernetes, czyli polityka

Obie firmy się nie lubią. Była bitwa pomiędzy Docker Swarm, a Kubernetes w 2016-2017 roku.

Bitwę o orkiestrator obrazów wygrał Kubernetes.

Kubernetes w grudniu 2020 roku stwierdził, że chce się pozbyć Dockershizm.

Dockershizm

Powodem tego jest nie tylko fakt, że obie firmy się nie lubią, ale także fakt, że Kubernetes słusznie stwierdził, że Docker jest za ciężki, aby był on częścią Kubernetes.

W czerwonym kwadracie widzisz funkcje, z który korzysta Kubernetes w Docker.

Docker też nie został stworzony z myślą, aby działał wewnątrz Kubernetes. Po prostu obie popularne technologie przypadkiem zostały łączone ze sobą. 

Dobra wiadomość jest taka, że obrazy utworzone w Docker nadal będą działać w Kubernetes.

Jednakże warto poszukać czegoś co jest kompatybilne z Container Runtime Interface. Zwłaszcza jeśli mówimy o środowiskach produkcyjnych, a nie o aplikacjach "hello world" do nauki Dockera i Kubernetes.

Jakie mamy alternatywy do Dockera, jeśli chodzi o obsługę obrazów:

  • ContainerD
  • Cri-o
  • crun

Jakie mamy alternatywy do Dockera, jeśli chodzi o budowanie obrazów:

  • Buildah

Jako początkujący nie musisz się tym przejmować. Filozofia tworzenia obrazów i uruchamiania się nie zmieni. Ta wiedza nigdy nie będzie utracona.

W chwili pisania tego wpisu nie jestem w stanie przewidzieć jaka technologia zastąpi Dockera. Docker był przez lata kojarzony z kontenerami więc jego dominacja nie zniknie z dnia na dzień.

Co trzeba mieć, aby zainstalować Dockera z Kubernetes na swojej maszynie

W przypadku Windows musiałem rozszerzyć swoją wersję Windows z Home do Professional. Zakupiłem klucz z Allegro, żeby nie wydać za dużo pieniędzy na ten awans.

Mając Windows Professional mogłem zainstalować Hyper-V. Możesz grzebać w Aplikację i funkcje -> Pogramy i Funkcje -> Włącz lub wyłącz funkcje systemu Windows -> Hyper-V.

Możesz też wpisać komendę w PowerShell

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All

Mając Hyper-V w końcu możesz zainstalować Dockera.

W samym Docker w ustawieniach zaznacz opcję : Enable Kubernetes i tak możesz zacząć swoją przygodę z tymi technologiami.

Zainstalowanie Kubernetes z Docker

Na koniec w wierszu poleceń wpisz :

kubectl version
Client Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.0", GitCommit:"af46c47ce925f4c4ad5cc8d1fca46c7b77d13b38", GitTreeState:"clean", BuildDate:"2020-12-08T17:59:43Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"windows/amd64"} Server Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.3", GitCommit:"1e11e4a2108024935ecfcb2912226cedeafd99df", GitTreeState:"clean", BuildDate:"2020-10-14T12:41:49Z", GoVersion:"go1.15.2", Compiler:"gc", Platform:"linux/amd64"}

Aby sprawdzić czy wszystko zainstalowało się poprawnie.

Co warto zainstalować do Visual Studio Code?

Dodatek : Kubernetes, który daje podpowiedzi przy tworzeniu plików YAML

Dodatek : YAML, który doda obsługę tych plików

Na koniec tego wpisu stworzymy statyczną stronę na serwerze APACHE 

Chce stworzyć obraz / kontener z moją statyczną stroną internetową :

Moja statyczna strona

Mój genialny projekt składa się z 3 plików.

Pliki w folderze bez docker file

Aby utworzyć obraz Docker będę potrzebował pliku dockerfile tutaj

Pliki w folderze

Jak Visual Studio rozpoznaje ten plik i dodał do niego ikonkę wieloryba. Do pliku dockerfile dodałem następujący kod.

FROM httpd:2.4

COPY ./index.html /usr/local/apache2/htdocs/index.html
COPY ./bakcground.jpg /usr/local/apache2/htdocs/bakcground.jpg
COPY ./wordart.png /usr/local/apache2/htdocs/wordart.png

Pierwsze wyrażenie FROM odnosi do bazowego obrazu z repozytorium Dockera. Ten obraz bazowy zawiera w sobie środowisko serwera HTTP Apache.

Jak się domyślasz mógłbym na nim uruchomić stronę napisaną w PHP lub w Javie. Ja tutaj robię prosty projekt ze statycznymi plikami.

httpd docker offical images

Wyrażenia COPY kopiują pliki z jednego miejsca na drugie. 

Warto zaznaczyć, że każde polecenie w Docker dzieli potem budowanie obrazu na poszczególne fazy, które są potem zapamiętywane, aby następne budowanie obrazu było szybsze.

Nie ma sensu kopiować każdy plik oddzielnie. Dlatego ten kod dockerfile można poprawić tak :

FROM httpd:2.4

COPY ./* /usr/local/apache2/htdocs/

Pozostało nam skorzystać z wiersza poleceń i utworzyć ten obraz. Wpisując "docker" do wiersza poleceń możesz zobaczyć ile poleceń ono ma :

PanNiebieski@CEZMSI                                                                                   [10:47]
docker

Usage:  docker [OPTIONS] COMMAND

A self-sufficient runtime for containers

Options:
      --config string      Location of client config files (default
                           "C:\\Users\\PanNiebieski\\.docker")
  -c, --context string     Name of the context to use to connect to the
                           daemon (overrides DOCKER_HOST env var and
                           default context set with "docker context use")
  -D, --debug              Enable debug mode
  -H, --host list          Daemon socket(s) to connect to
  -l, --log-level string   Set the logging level
                           ("debug"|"info"|"warn"|"error"|"fatal")
                           (default "info")
      --tls                Use TLS; implied by --tlsverify
      --tlscacert string   Trust certs signed only by this CA (default
                           "C:\\Users\\PanNiebieski\\.docker\\ca.pem")
      --tlscert string     Path to TLS certificate file (default
                           "C:\\Users\\PanNiebieski\\.docker\\cert.pem")
      --tlskey string      Path to TLS key file (default
                           "C:\\Users\\PanNiebieski\\.docker\\key.pem")
      --tlsverify          Use TLS and verify the remote
  -v, --version            Print version information and quit

Management Commands:
  app*        Docker App (Docker Inc., v0.9.1-beta3)
  builder     Manage builds
  buildx*     Build with BuildKit (Docker Inc., v0.4.2-docker)
  config      Manage Docker configs
  container   Manage containers
  context     Manage contexts
  image       Manage images
  manifest    Manage Docker image manifests and manifest lists
  network     Manage networks
  node        Manage Swarm nodes
  plugin      Manage plugins
  scan*       Docker Scan (Docker Inc., v0.5.0)
  secret      Manage Docker secrets
  service     Manage services
  stack       Manage Docker stacks
  swarm       Manage Swarm
  system      Manage Docker
  trust       Manage trust on Docker images
  volume      Manage volumes

Commands:
  attach      Attach local standard input, output, and error streams to a running container
  build       Build an image from a Dockerfile
  commit      Create a new image from a container's changes
  cp          Copy files/folders between a container and the local filesystem
  create      Create a new container
  diff        Inspect changes to files or directories on a container's filesystem
  events      Get real time events from the server
  exec        Run a command in a running container
  export      Export a container's filesystem as a tar archive
  history     Show the history of an image
  images      List images
  import      Import the contents from a tarball to create a filesystem image
  info        Display system-wide information
  inspect     Return low-level information on Docker objects
  kill        Kill one or more running containers
  load        Load an image from a tar archive or STDIN
  login       Log in to a Docker registry
  logout      Log out from a Docker registry
  logs        Fetch the logs of a container
  pause       Pause all processes within one or more containers
  port        List port mappings or a specific mapping for the container
  ps          List containers
  pull        Pull an image or a repository from a registry
  push        Push an image or a repository to a registry
  rename      Rename a container
  restart     Restart one or more containers
  rm          Remove one or more containers
  rmi         Remove one or more images
  run         Run a command in a new container
  save        Save one or more images to a tar archive (streamed to STDOUT by default)
  search      Search the Docker Hub for images
  start       Start one or more stopped containers
  stats       Display a live stream of container(s) resource usage statistics
  stop        Stop one or more running containers
  tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE
  top         Display the running processes of a container
  unpause     Unpause all processes within one or more containers
  update      Update configuration of one or more containers
  version     Show the Docker version information
  wait        Block until one or more containers stop, then print their exit codes

Run 'docker COMMAND --help' for more information on a command.
To get more help with docker, check out guides at https://docs.docker.com/go/guides/

Wpisując "docker images" możesz zobaczyć ile obrazów obecnie masz w swoim dockerze.

Powinieneś zobaczyć wszystkie obrazy pomocnicze, które zostały utworzone przez Dockera albo Kubernetes.

Jak więc zbudować obraz. W wierszu poleceń wchodzisz na ścieżkę swojego projektu i wpisujesz :

docker build -t mojblog:mojtag .

W taki sposób właśnie utworzyłeś swój pierwszy obraz Docker. Parametrem -t określiłeś nazwę swojego obrazu oraz jego tag. Kropka na końcu polecenia określa ścieżkę, w której dockerfile się znajduje. Jeśli docker file jest w tym samym katalogu to wpisujesz kropkę.

Teraz jak ten obraz uruchomić 

docker run --name my-running-app -p 8080:80 mojblog:mojtag

--name określa nazwę naszego kontenera , w którym będzie działa nasza aplikacja. Nazwa nie jest wymagana Docker może dodać swoją losową nazwę. 

-p : określa przejście portów. Domyślnie aplikacja serwera APACHE uruchomi się na porcie 80 więc robię przekierowanie na port 8080.

Na koniec polecenia muszę określić jaki obraz chce uruchomić. 

my-runing-app uruchomiona

W UI Dockera możesz zobaczyć, że faktycznie taki kontener. Samą stronę możesz uruchomić pod adresem localhost:8080

Jak zatrzymać tą aplikację?

docker container kill my-running-app

my-runing-app zatrzymana

Kontener można uruchomić, ale my tego nie zrobimy.

Jak usunąć kontener?

docker container rm my-running-app

rm : jest trochę nie intuicyjnie i nie powiem wołałbym polecenie "delete"

Podsumowanie

Swoją drogą używanie Dockera/Kubernetes do plików statycznych to idiotyzm.

Co warto konteryzować?

  • Pliki ".exe" do zadań
  • API
  • Aplikacje Webowe

Co nie konteryzować?

  • Mobilne aplikacje
  • Aplikacje desktopowe (na razie)
  • Małe projekty

Czy miałem problem z budowanie obrazu w Dockerze, gdy szykowałem webinar i ten wpis?

Tak, w trakcie robienia właśnie tego przykładu dostawałem następujący błąd.

docker: Error response from daemon : driver failed programming external connectivity on endpoint xenodochail-snyder

Znalazłem w internecie, że podobno w Docker istnieje taki BUG przez wiele lat i nikt z nim nic nie zrobił. Zastosowałem zalecenia z wątku o tym błędzie i zrestartowałem po prostu Dockera.

Restart Docker

To było tyle, jeżeli chodzi o ten wpis. 

Przykład pierwszy jest pobrania z GitHub-a : https://github.com/PanNiebieski/DemoExamples-For-KubernetesDockerWebinar

Jeśli zainteresowała Cię tematyk do polecam swój webinar na temat Kubernetes i Dockera.

Możesz go obejrzeć go tutaj : https://www.youtube.com/watch?v=7g00wOg9Jto

;