Blog JSystems - uwalniamy wiedzę!
Blog JSystems - uwalniamy wiedzę!
DevOps to jedno z najczęściej wpisywanych w wyszukiwarkę pojęć w branży IT i jednocześnie jedno z najgorzej rozumianych. Dla jednych to nazwa stanowiska, dla innych zestaw modnych narzędzi, a dla jeszcze innych magiczne zaklęcie, które ma sprawić, że oprogramowanie zacznie wdrażać się samo. Żadna z tych odpowiedzi nie jest pełna. W tym przewodniku wyjaśnimy po kolei, czym DevOps naprawdę jest, z jakich narzędzi się składa i do czego każde z nich służy. Wszystkie przykłady w kodzie uruchomiliśmy naprawdę, więc widzisz autentyczne wyniki z terminala, a nie slajdy z prezentacji.
DevOps to sposób pracy nad oprogramowaniem, który łączy tworzenie aplikacji (ang. development, w skrócie „dev") z jej utrzymaniem na serwerach (ang. operations, „ops") w jeden, wspólny i mocno zautomatyzowany proces. To przede wszystkim kultura i zestaw praktyk, a narzędzia są dopiero konsekwencją. Sama nazwa jest zbitką słów „development" i „operations" i to nie przypadek, bo DevOps powstał dokładnie po to, żeby zburzyć mur między tymi dwoma światami.
Warto od razu rozwiać trzy nieporozumienia. DevOps nie jest jednym narzędziem: nie da się „kupić DevOps" ani zainstalować go z jednego pakietu. DevOps nie jest osobnym działem, który wrzuca się między programistów a administratorów (to zwykle tworzy trzeci mur zamiast burzyć istniejące). I choć na rynku pracy istnieje stanowisko „DevOps Engineer", samo DevOps nie jest stanowiskiem, tylko sposobem, w jaki cała organizacja dowozi i utrzymuje oprogramowanie.
Skąd w ogóle wziął się ten mur? Z konfliktu celów. Programiści są rozliczani z tego, jak szybko dostarczają nowe funkcje, więc chcą wdrażać zmiany często. Administratorzy odpowiadają za stabilność, więc każda zmiana jest dla nich ryzykiem, które chcą ograniczać. W efekcie wdrożenie (ang. deployment, czyli udostępnienie nowej wersji aplikacji użytkownikom) stawało się rzadkim, stresującym wydarzeniem, a po każdej awarii zaczynało się wzajemne obwinianie. DevOps rozwiązuje ten spór, ustawiając obie strony po tej samej stronie: wspólna odpowiedzialność, wspólny potok i automatyzacja tam, gdzie wcześniej były ręczne przekazania „przez płot".
Żeby ta zmiana kultury nie została pustym hasłem, w środowisku DevOps przyjęło się pięć filarów opisywanych skrótem CALMS: Culture (kultura wspólnej odpowiedzialności), Automation (automatyzacja powtarzalnych czynności), Lean (dostarczanie małymi porcjami, bez marnotrawstwa), Measurement (mierzenie wszystkiego, bo bez pomiaru nie wiadomo, czy jest lepiej) i Sharing (dzielenie się wiedzą zamiast trzymania jej w silosach). Zapamiętaj te pięć słów, bo do każdego z nich wrócimy przy konkretnych narzędziach.
DevOps najłatwiej zrozumieć jako ciągłą pętlę, a nie jednorazowy proces „od-do". Oprogramowanie się planuje, pisze, buduje, testuje, wydaje, wdraża, utrzymuje i monitoruje, a wnioski z monitoringu wracają do planowania kolejnej zmiany. Ta pętla nigdy się nie zatrzymuje. Klasycznie rysuje się ją jako znak nieskończoności, w którym lewa połowa należy do „dev", a prawa do „ops". Każdy etap ma swoje narzędzia i to właśnie ich zestaw nazywamy potocznie „narzędziami DevOps".
Przejdźmy teraz przez tę pętlę od strony narzędzi. To najważniejsza część przewodnika, bo odpowiada wprost na pytanie „jakich narzędzi używa DevOps i do czego".
Zanim wejdziemy w narzędzia, uporządkujmy pojęcia, które krążą razem z DevOps i bywają ze sobą mylone. Wszystkie należą do tej samej rodziny, ale każde odpowiada na trochę inne pytanie.
W praktyce nie musisz wybierać jednego z nich. Firma może stosować DevOps jako kulturę, budować potoki CI/CD, prowadzić utrzymanie w duchu SRE, dawać programistom platformę i wdrażać zmiany w modelu GitOps, a przy tym mieć skanery bezpieczeństwa w każdym potoku. To wszystko warstwy tej samej filozofii.
Nie istnieje jedno „narzędzie DevOps". Istnieje zestaw narzędzi, w którym każde obsługuje inny etap pętli. Poniższa mapa porządkuje je według zadania, a potem przejdziemy przez najważniejsze z nich z prawdziwymi wynikami z terminala.
Git to rozproszony system kontroli wersji, czyli narzędzie, które zapamiętuje kolejne stany projektu jako „commity" (zapisane migawki zmian). Cała reszta łańcucha DevOps zaczyna się właśnie tutaj: potok CI/CD uruchamia się po zapisaniu zmiany, a w podejściu GitOps to zawartość repozytorium decyduje o tym, co ma działać na produkcji. Poniżej realna historia małego repozytorium z gałęzią funkcji i połączeniem jej z główną linią rozwoju:
Zwróć uwagę, że w tej historii są nie tylko commity z kodem aplikacji, ale też z plikami Dockera, manifestami Kubernetes i konfiguracją potoku CI/CD. To typowe dla DevOps: infrastruktura mieszka w tym samym repozytorium co aplikacja i podlega tym samym regułom, czyli przeglądowi zmian, historii i możliwości cofnięcia. Nie ma już „konfiguracji, która żyje tylko w głowie administratora".
CI/CD to skrót od ciągłej integracji (ang. Continuous Integration, czyli częstego scalania zmian i automatycznego sprawdzania, czy nic się nie zepsuło) oraz ciągłego dostarczania (ang. Continuous Delivery, czyli automatycznego przygotowania i wdrożenia gotowej wersji). Najpopularniejsze narzędzia to GitHub Actions, GitLab CI oraz Jenkins. Potok opisuje się jako plik w repozytorium. Oto minimalny, ale realny przepływ w GitHub Actions, który po każdej zmianie na gałęzi main buduje obraz i go wdraża:
name: build-and-deploy
on:
push:
branches: [main]
jobs:
ship:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Testy
run: pytest -q
- name: Zbuduj i wypchnij obraz
run: |
docker build -t registry.example.com/sklep-api:${{ github.sha }} .
docker push registry.example.com/sklep-api:${{ github.sha }}
- name: Wdróż na klaster
run: kubectl set image deploy/sklep-api sklep-api=registry.example.com/sklep-api:${{ github.sha }}
Ten plik to nie teoria. Wypchnęliśmy go do prawdziwego repozytorium, a GitHub Actions wykonał cały potok od początku do końca, bez naszej ingerencji:
A tak wygląda log samego kroku wdrożenia z tego przebiegu. To już nie zapowiedź, tylko realny zapis publikacji gotowego obrazu:
Chcesz zbudować własny potok CI/CD od commita po wdrożenie? Szkolenie GitHub Actions ma terminy gwarantowane.
Tam, gdzie potok jest bardziej rozbudowany albo firma woli własny serwer, króluje Jenkins. Poniżej realny widok tak zwanego stage view, czyli podziału jednego uruchomienia potoku na etapy z czasami wykonania. To dokładnie ten obraz, na który patrzy inżynier, gdy chce sprawdzić, na którym kroku potok się zatrzymał:
Gdy któryś krok zawiedzie, przechodzi się do konsoli danego uruchomienia i czyta logi. To pierwszy odruch przy diagnozie nieudanego wdrożenia:
Wolisz Jenkinsa na własnym serwerze? Szkolenie CI/CD w Jenkins ma terminy gwarantowane.
Docker to najpopularniejsze narzędzie do konteneryzacji, czyli uruchamiania aplikacji w izolowanych, lekkich „pudełkach" zwanych kontenerami. Kontener różni się od maszyny wirtualnej tym, że nie nosi ze sobą całego systemu operacyjnego, tylko współdzieli jądro z hostem, dlatego startuje w ułamku sekundy. Aplikację opisuje się plikiem Dockerfile. Kluczowa dobra praktyka to budowanie wielostopniowe (ang. multi-stage build): w jednym etapie kompilujemy program z pełnym zestawem narzędzi, a do finalnego obrazu kopiujemy tylko gotowy plik wykonywalny.
# Etap budowania: pełne środowisko Go
FROM golang:1.23-alpine AS build
WORKDIR /src
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /app .
# Etap finalny: pusty obraz tylko z binarką
FROM scratch
COPY --from=build /app /app
EXPOSE 8080
ENTRYPOINT ["/app"]
Efekt jest wymierny. Zbudowaliśmy ten sam program na dwa sposoby: naiwnie (cały obraz z narzędziami Go zostaje w środku) oraz wielostopniowo (do finalnego obrazu trafia sama binarka). Oto realne rozmiary z naszego terminala:
Z 472 MB robi się 7,3 MB, czyli obraz kilkadziesiąt razy mniejszy. Mniejszy obraz to szybszy transfer, szybszy start i mniejsza powierzchnia ataku (o bezpieczeństwie za chwilę). Docker służy też do składania kilku usług w jedną całość poleceniem docker compose. Cały zestaw (serwer WWW, baza i pamięć podręczna) opisuje jeden plik compose.yaml, w którym deklarujemy też kontrolę kondycji usług (ang. healthcheck) i zależności między nimi:
services:
web:
image: nginx:alpine
ports: ["8080:80"]
depends_on:
redis:
condition: service_healthy # startuj web dopiero, gdy redis jest zdrowy
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
retries: 5
db:
image: postgres:16-alpine
environment:
POSTGRES_PASSWORD: sekret
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:
Po uruchomieniu poleceniem docker compose up -d Docker startuje wszystkie usługi we właściwej kolejności i pilnuje ich stanu:
Widać tu istotny szczegół: serwer WWW wystartował dopiero po tym, jak pamięć podręczna (redis) zgłosiła stan Healthy. To automatyzacja zależności między usługami, którą wcześniej trzeba było robić ręcznie. Jeśli interesuje Cię konteneryzacja konkretnej aplikacji krok po kroku, pokazaliśmy to na przykładzie osadzania aplikacji Flask na Dockerze.
Konteneryzację najłatwiej opanować, budując realne obrazy pod okiem praktyka. Szkolenie Docker i Kubernetes: od zera do bohatera ma terminy gwarantowane.
Gdy kontenerów jest kilka, wystarczy Docker. Gdy są ich setki na wielu maszynach, potrzebny jest dyrygent. Tym dyrygentem jest Kubernetes (często skracany do „K8s"), czyli system do orkiestracji kontenerów. W Kubernetesie nie mówisz „uruchom ten kontener", tylko opisujesz stan docelowy („chcę trzy działające kopie aplikacji"), a klaster sam do niego dąży i sam go pilnuje. Różnice między samym Dockerem a Kubernetesem rozłożyliśmy na części w osobnym tekście o tym, czym różni się Kubernetes od Dockera. Stan docelowy opisuje się deklaratywnie w pliku YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: sklep-api
spec:
replicas: 3 # utrzymuj 3 kopie aplikacji
selector:
matchLabels: { app: sklep-api }
template:
metadata: { labels: { app: sklep-api } }
spec:
containers:
- name: sklep-api
image: registry.example.com/sklep-api:1.4.2
resources:
requests: { cpu: "100m", memory: "128Mi" }
limits: { cpu: "500m", memory: "256Mi" }
Poniżej prawdziwa sesja na działającym klastrze. Do przykładów użyliśmy k3s, czyli lekkiej, w pełni zgodnej dystrybucji Kubernetesa (to nadal Kubernetes, tyle że w wersji idealnej do nauki na laptopie, co widać w kolumnie VERSION jako v1.34.1+k3s1). Uruchamiamy aplikację w trzech kopiach, klaster sam doprowadza ją do stanu gotowości, a na końcu skalujemy do pięciu kopii jednym poleceniem:
To jest właśnie „samonaprawa" i skalowanie w praktyce: gdyby któraś kopia padła, Kubernetes od razu uruchomiłby nową, żeby utrzymać zadaną liczbę. Konfiguracje dla całych aplikacji pakuje się dodatkowo w Helm, czyli menedżer pakietów dla Kubernetesa. Zamiast trzymać dziesiątki plików YAML, instalujesz aplikację jak paczkę: helm install sklep-api ./chart, a aktualizujesz przez helm upgrade. Helm pozwala też jednym poleceniem cofnąć się do poprzedniej wersji, gdy coś pójdzie nie tak.
Chcesz swobodnie zarządzać Kubernetesem na realnym klastrze? Szkolenie Kubernetes: orkiestracja kontenerów ma terminy gwarantowane.
Terraform to najpopularniejsze narzędzie do podejścia zwanego infrastrukturą jako kod (ang. Infrastructure as Code). Opisujesz w plikach, co ma istnieć, a Terraform sam wylicza, co dodać, zmienić lub usunąć, żeby rzeczywistość zgadzała się z opisem. Opis jest krótki i czytelny. Poniższy fragment mówi po prostu: „ma istnieć kontener z serwerem nginx dostępny na porcie 8095":
resource "docker_container" "web" {
name = "tf-web"
image = docker_image.nginx.image_id
ports {
internal = 80
external = 8095
}
}
Najważniejsze jest to, że Terraform najpierw pokazuje plan, a dopiero po akceptacji go wykonuje. Nigdy nie zmienia niczego bez uprzedzenia. Oto prawdziwe uruchomienie na naszym środowisku, gdzie Terraform tworzy obraz i kontener:
Zdanie Plan: 2 to add, 0 to change, 0 to destroy to serce Terraform. Zanim cokolwiek się wydarzy, widzisz dokładnie, co narzędzie zamierza zrobić. Uruchomione ponownie na niezmienionym opisie, Terraform nie zrobi nic (bo rzeczywistość już się zgadza). Ta właściwość, zwana idempotencją, oznacza, że to samo polecenie wykonane wielokrotnie daje ten sam stan końcowy.
Infrastrukturę jako kod najszybciej przyswoisz w praktyce, na własnym stanie i planach. Szkolenie Terraform i infrastruktura jako kod ma terminy gwarantowane.
Ansible uzupełnia Terraform. Gdzie Terraform odpowiada za powołanie infrastruktury, tam Ansible dba o to, co jest na serwerach: instaluje pakiety, kopiuje konfiguracje, zakłada użytkowników, uruchamia usługi. Opis zadań, zwany „playbookiem", jest czytelny nawet dla osoby, która nie zna narzędzia: to lista kroków z jasnymi nazwami. Poniżej fragment playbooka konfigurującego serwer aplikacji:
- name: Konfiguracja serwera aplikacji Sklep API
hosts: web
become: true
tasks:
- name: Zapewnij katalog konfiguracji
ansible.builtin.file:
path: /etc/sklep
state: directory
- name: Utworz systemowego uzytkownika aplikacji
ansible.builtin.user:
name: sklep
system: true
Playbook uruchamia się jednym poleceniem, a Ansible na końcu pokazuje raport (ang. PLAY RECAP) z liczbą zadań wykonanych i zmienionych. Oto prawdziwy wynik uruchomienia naszego playbooka:
Podobnie jak Terraform, Ansible jest idempotentny. Uruchomiony ponownie na już skonfigurowanym serwerze pokaże changed=0, bo nie będzie miał nic do zmiany. To dlatego DevOps tak ceni te narzędzia: opisujesz stan docelowy raz, a potem możesz go egzekwować w kółko, na jednym serwerze albo na tysiącu, zawsze z tym samym skutkiem.
Automatyzację konfiguracji serwerów warto przećwiczyć na realnych maszynach. Szkolenie Automatyzacja zadań z Ansible ma terminy gwarantowane.
Nie da się utrzymywać systemu, którego się nie widzi. Ta warstwa nazywa się obserwowalnością (ang. observability), czyli zdolnością odpowiedzenia na pytanie „dlaczego to się dzieje" na podstawie danych, które system o sobie udostępnia. Prometheus to baza czasowa, która co kilkanaście sekund odpytuje aplikacje i serwery o ich metryki. Najpierw sprawdza się, czy w ogóle ma z czego zbierać dane, na liście celów (ang. targets):
Metryki odpytuje się językiem PromQL. Poniższe zapytanie liczy udział odpowiedzi z błędem serwera (kody 5xx) w całym ruchu, czyli klasyczny wskaźnik „czy usługa się psuje":
# Udział odpowiedzi 5xx w całym ruchu usługi (ostatnie 5 minut)
sum(rate(http_requests_total{service="sklep-api",status=~"5.."}[5m]))
/
sum(rate(http_requests_total{service="sklep-api"}[5m]))
Prosty wykres w samym Prometheusie to już duży krok. Ten sam ruch przedstawiony graficznie od razu pokazuje, kiedy zaczął się problem i czy sam wrócił do normy:
Prometheus rysuje proste wykresy, ale prawdziwe, bogate pulpity buduje się w Grafanie, czyli narzędziu do wizualizacji danych. Pulpit Grafany widzisz już na samej górze tego artykułu: ten kolorowy zestaw wskaźników i wykresów kondycji serwera to właśnie ona. Grafanie poświęciliśmy osobny, szczegółowy przewodnik, więc jeśli chcesz nauczyć się budować takie pulpity od zera, przeczytaj czym jest Grafana w przewodniku dla początkujących.
Sam wykres to za mało. Metryki mają sens dopiero wtedy, gdy potrafią samodzielnie zawołać o pomoc. Do tego służą reguły alertów i menedżer alertów (ang. Alertmanager), który wysyła powiadomienie na wskazany kanał, gdy przekroczony zostanie próg. Poniżej realny, wyzwolony alert po polsku:
Do metryk dochodzą jeszcze logi (zapisy zdarzeń), które centralizuje Loki albo starszy, ale wciąż popularny stos ELK (Elasticsearch, Logstash, Kibana). Metryki mówią „coś jest nie tak", a logi odpowiadają „dlaczego". Razem tworzą obserwowalność, bez której DevOps byłby lataniem po ciemku.
Cały stos monitoringu w jednym miejscu, na żywej infrastrukturze: Szkolenie Monitoring i Observability: Grafana, Prometheus, Loki, Zabbix ma terminy gwarantowane.
DevSecOps to DevOps, w którym bezpieczeństwo „przesunięto w lewo", czyli jak najbliżej początku procesu. Zamiast sprawdzać aplikację dopiero przed wdrożeniem, skanuje się ją przy każdym budowaniu. Popularnym skanerem jest Trivy, który wyszukuje znane podatności (ang. CVE) w obrazie. Świetnie pokazuje też, dlaczego wybór podstawowego obrazu ma znaczenie. Przeskanowaliśmy dwa warianty tego samego serwera WWW, pełny (oparty na systemie Debian) i odchudzony (oparty na Alpine):
Ta sama aplikacja, a liczba poważnych podatności spada ze 126 do 35 tylko dlatego, że wybraliśmy chudszy obraz podstawowy. To jest DevSecOps w pigułce: bezpieczeństwo nie jest osobnym etapem na końcu, tylko konsekwencją dobrych decyzji podejmowanych po drodze i pilnowanych automatycznie. Skany takie jak ten wpina się wprost do potoku CI/CD, który potrafi zatrzymać wdrożenie, gdy pojawi się nowa krytyczna podatność.
Cała pętla DevOps musi na czymś działać. Najczęściej jest to chmura publiczna: Amazon Web Services (AWS), Microsoft Azure lub Google Cloud (GCP). Chmura daje infrastrukturę na żądanie, rozliczaną za faktyczne użycie, i to z nią najczęściej rozmawia Terraform. Alternatywą (albo uzupełnieniem) jest wirtualizacja na własnym sprzęcie, na przykład Proxmox, popularny do budowy domowego lub firmowego laboratorium. Dla nauki DevOps nie potrzebujesz jednak od razu chmury: pełny klaster Kubernetes postawisz lokalnie na laptopie, dokładnie tak, jak zrobiliśmy to na potrzeby przykładów w tym artykule.
Pojedyncze narzędzia to dopiero połowa historii. Prawdziwa siła DevOps bierze się z tego, że układają się one w jeden ciągły potok. Prześledźmy drogę zwykłej zmiany w kodzie, od momentu zapisania jej przez programistę do chwili, gdy działa na produkcji i jest obserwowana:
Zwróć uwagę, że w całym tym łańcuchu człowiek podejmuje jedną świadomą decyzję: zapisuje dobrą zmianę w kodzie. Resztę wykonują narzędzia. To jest właśnie automatyzacja, o której mówi „A" w skrócie CALMS, i to ona odróżnia dojrzały DevOps od ręcznego wdrażania „na piechotę".
Cała ta maszyneria nie jest sztuką dla sztuki. DevOps ma mierzalny cel: dostarczać oprogramowanie szybciej i stabilniej zarazem, co przez lata wydawało się sprzecznością. Najczęściej używa się do tego czterech wskaźników znanych jako metryki DORA (od nazwy zespołu badawczego, który je spopularyzował). Opisują one, jak sprawnie organizacja dowozi zmiany:
Przełóżmy to na konkret. Dla firmy DevOps oznacza szybsze dostarczanie nowych funkcji (a więc szybszą reakcję na rynek), mniej awarii i krótsze przestoje, powtarzalność (nowe środowisko stawia się z kodu w minuty, a nie w dni) oraz przewidywalne koszty infrastruktury. Dla inżyniera to mniej nudnej, ręcznej pracy i gaszenia pożarów, a więcej budowania rzeczy, które sprawiają, że pożary w ogóle nie wybuchają. To także jeden z najlepiej opłacanych i najbardziej poszukiwanych zestawów umiejętności w branży.
Skoro wiesz już, czym DevOps jest i z czego się składa, naturalne pytanie brzmi „od czego zacząć". Najważniejsza zasada to właściwa kolejność. Najczęstszy błąd to rzucanie się od razu na Kubernetes, bo wygląda najatrakcyjniej. Bez fundamentów jest jednak ścianą niezrozumiałych skrótów. Sensowna droga wygląda tak:
Najlepiej uczyć się na własnym laboratorium. Pełny klaster Kubernetes uruchomisz lokalnie w minutę, a jeśli masz stary komputer lub domowy serwer, postaw na nim Proxmoxa i kilka maszyn wirtualnych, żeby przećwiczyć wszystko od instalacji po symulowaną awarię. Kompletną, rozpisaną na fazy drogę wraz z realnymi widełkami zarobków opisaliśmy osobno, w przewodniku o tym, jak zostać inżynierem DevOps od zera.
DevOps to nie narzędzie ani stanowisko, tylko sposób pracy: kultura wspólnej odpowiedzialności za oprogramowanie, wsparta automatyzacją całej pętli od pomysłu po monitoring działającej usługi. Narzędzia, o których mowa, nie są celem samym w sobie, tylko odpowiedziami na konkretne pytania. Git pilnuje historii i jest źródłem prawdy. CI/CD automatyzuje build, testy i wdrożenie. Docker pakuje aplikację, a Kubernetes nią dyryguje na produkcji. Terraform i Ansible zamieniają infrastrukturę w kod. Prometheus i Grafana sprawiają, że widać, co się dzieje, a Trivy pilnuje bezpieczeństwa po drodze. Razem tworzą one potok, w którym zwykła zmiana w kodzie sama płynie przez budowanie, testy, skan, wdrożenie i monitoring, a człowiek podejmuje tylko decyzje, które naprawdę wymagają jego uwagi. To właśnie jest DevOps i do tego on służy.
Komentarze (0)
Brak komentarzy...