Go dla początkujących - Część 16: Wdrażanie i konteneryzacja

2/20/2025 Kurs Go

Mateusz Kędziora

image

Go na Produkcji: Od Kodu do Działającej Aplikacji w Dockerze i Kubernetes

Witaj! Zaczynasz swoją przygodę z Go i chcesz dowiedzieć się, jak przenieść swój kod z komputera na działający serwer? Świetnie trafiłeś! Ten artykuł to kompleksowy przewodnik, który pokaże Ci, jak wdrożyć aplikację Go do środowiska produkcyjnego, wykorzystując narzędzia takie jak Docker i Kubernetes.

Go: Krótki Przegląd dla Początkujących

Jeśli jesteś zupełnie nowy w Go, spójrzmy na podstawowy przykład i wyjaśnimy, co się dzieje.

package main

import "fmt"

func main() {
	message := "Witaj, świecie!"
	fmt.Println(message)
}

Co robi ten kod?

  1. package main: Deklaruje, że ten plik jest głównym punktem wejścia aplikacji.
  2. import "fmt": Importuje pakiet fmt, który zawiera funkcje do formatowania i wypisywania tekstu.
  3. func main() {}: Definiuje funkcję main, która jest wykonywana po uruchomieniu programu.
  4. message := "Witaj, świecie!": Deklaruje zmienną message typu string i przypisuje jej wartość “Witaj, świecie!”.
  5. fmt.Println(message): Wywołuje funkcję Println z pakietu fmt, aby wypisać wartość zmiennej message na konsoli.

To bardzo prosty przykład, ale pokazuje podstawową strukturę programu w Go. W kolejnych sekcjach będziemy używać bardziej złożonych przykładów, ale zawsze będziemy wyjaśniać każdy krok.

Docker: Konteneryzacja w Pigułce

Czym jest Docker?

Docker to platforma do konteneryzacji. Wyobraź sobie, że masz pudełko (kontener), w którym umieszczasz wszystko, czego potrzebuje Twoja aplikacja do działania: kod, biblioteki, zależności, konfigurację. Ten kontener jest izolowany od reszty systemu, co oznacza, że możesz go uruchomić na dowolnym serwerze, który ma zainstalowanego Dockera, a aplikacja będzie działać tak samo.

Po co używać Dockera?

  • Spójność środowiska: Aplikacja działa tak samo na Twoim komputerze, na serwerze testowym i na serwerze produkcyjnym.
  • Izolacja: Aplikacje są izolowane od siebie, co zwiększa bezpieczeństwo i stabilność.
  • Skalowalność: Łatwo uruchomić wiele kopii aplikacji w kontenerach.
  • Reprodukowalność: Definicja kontenera jest zawarta w pliku Dockerfile, dzięki czemu łatwo odtworzyć środowisko.

Tworzenie obrazu Docker dla aplikacji Go

Załóżmy, że masz prostą aplikację Go:

package main

import (
	"fmt"
	"net/http"
	"os"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		hostname, _ := os.Hostname()
		fmt.Fprintf(w, "Witaj ze świata Go! Jestem uruchomiony na: %s\n", hostname)
	})

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	fmt.Println("Serwer słucha na porcie: ", port)
	http.ListenAndServe(":"+port, nil)
}

Opis kodu:

  1. package main: Deklaruje, że ten plik jest głównym punktem wejścia aplikacji.
  2. import (...): Importuje pakiety fmt, net/http i os.
    • fmt: Do formatowania tekstu.
    • net/http: Do tworzenia serwera HTTP.
    • os: Do odczytywania zmiennych środowiskowych i pobierania nazwy hosta.
  3. func main() {}: Definiuje funkcję main, która jest wykonywana po uruchomieniu programu.
  4. http.HandleFunc("/", ...): Rejestruje funkcję obsługującą żądania HTTP na ścieżce ”/”.
    • hostname, _ := os.Hostname(): Pobiera nazwę hosta, na którym działa aplikacja.
    • fmt.Fprintf(w, ...): Wypisuje wiadomość na stronie internetowej, zawierającą nazwę hosta.
  5. port := os.Getenv("PORT"): Pobiera wartość zmiennej środowiskowej “PORT”. Jeśli zmienna nie jest ustawiona, ustawia domyślny port na “8080”.
  6. http.ListenAndServe(":"+port, nil): Uruchamia serwer HTTP na określonym porcie.

Teraz stwórzmy plik Dockerfile:

# Użyj oficjalnego obrazu Go jako podstawy
FROM golang:1.24-alpine AS builder

# Ustaw katalog roboczy wewnątrz kontenera
WORKDIR /app

# Skopiuj pliki Go do kontenera
COPY . .

# Pobierz zależności
RUN go mod download

# Zbuduj aplikację
RUN go build -o main .

# Stwórz nowy, mniejszy obraz bazujący na Alpine Linux
FROM alpine:latest

# Skopiuj zbudowany plik wykonywalny z etapu budowy
COPY --from=builder /app/main /app/main

# Ustaw katalog roboczy
WORKDIR /app

# Ustaw port, na którym nasza aplikacja będzie nasłuchiwać
EXPOSE 8080

# Uruchom aplikację
CMD ["/app/main"]

Co robi ten plik Dockerfile?

  1. FROM golang:1.24-alpine AS builder: Bazuje na obrazie golang:1.24-alpine, który zawiera środowisko Go. Używamy aliasu builder, aby później odwołać się do tego etapu. Obraz Alpine jest lekki, co pomoże nam zmniejszyć rozmiar obrazu wynikowego.
  2. WORKDIR /app: Ustawia katalog /app jako katalog roboczy wewnątrz kontenera.
  3. COPY . .: Kopiuje wszystkie pliki z bieżącego katalogu (gdzie znajduje się Dockerfile) do katalogu /app w kontenerze.
  4. RUN go mod download: Pobiera zależności projektu Go (zdefiniowane w go.mod).
  5. RUN go build -o main .: Kompiluje kod Go do pliku wykonywalnego o nazwie main.
  6. FROM alpine:latest: Bazuje na obrazie alpine:latest, który jest bardzo małym obrazem Linuxa.
  7. COPY --from=builder /app/main /app/main: Kopiuje skompilowany plik main z etapu builder do katalogu /app w nowym obrazie.
  8. WORKDIR /app: Ustawia katalog /app jako katalog roboczy.
  9. EXPOSE 8080: Informuje, że aplikacja nasłuchuje na porcie 8080.
  10. CMD ["/app/main"]: Uruchamia aplikację po uruchomieniu kontenera.

Budowanie obrazu Docker

Otwórz terminal w katalogu, gdzie masz plik Dockerfile i uruchom:

docker build -t go-app .

Co robi to polecenie?

  • docker build: Polecenie budujące obraz Dockera.
  • -t go-app: Nadaje obrazowi nazwę go-app.
  • .: Określa, że plik Dockerfile znajduje się w bieżącym katalogu.

Po zbudowaniu obrazu, możesz go uruchomić:

docker run -p 8080:8080 go-app

Co robi to polecenie?

  • docker run: Polecenie uruchamiające kontener z obrazu.
  • -p 8080:8080: Przekierowuje port 8080 z komputera na port 8080 w kontenerze.
  • go-app: Nazwa obrazu, z którego ma być uruchomiony kontener.

Otwórz przeglądarkę i wpisz http://localhost:8080. Powinieneś zobaczyć “Witaj ze świata Go!”.

Kubernetes: Orkestracja Kontenerów

Czym jest Kubernetes?

Kubernetes (często skracane do K8s) to system do orkiestracji kontenerów. Wyobraź sobie, że masz wiele kontenerów Docker, które tworzą Twoją aplikację. Kubernetes pomaga Ci nimi zarządzać: uruchamiać, skalować, aktualizować, monitorować i automatycznie naprawiać w przypadku awarii.

Dlaczego używać Kubernetesa?

  • Automatyzacja: Kubernetes automatyzuje wiele zadań związanych z wdrażaniem i zarządzaniem aplikacjami w kontenerach.
  • Skalowalność: Łatwo zwiększyć lub zmniejszyć liczbę działających kontenerów w zależności od obciążenia.
  • Samonaprawa: Kubernetes automatycznie restartuje kontenery, które uległy awarii.
  • Aktualizacje: Możesz aktualizować aplikacje bez przestojów.
  • Orkiestracja: Kubernetes zarządza siecią, storage i konfiguracją aplikacji.

Podstawowe Koncepcje Kubernetesa

  • Pod: Najmniejsza jednostka w Kubernetes. Może zawierać jeden lub więcej kontenerów. Kontenery w jednym Podzie współdzielą zasoby, takie jak sieć i storage.
  • Deployment: Definicja, jak powinna być wdrażana aplikacja. Określa, ile kopii Podów ma być uruchomionych i jak powinny być aktualizowane.
  • Service: Udostępnia aplikację na zewnątrz klastra Kubernetes. Działa jako proxy, kierując ruch do Podów.
  • Namespace: Wirtualna przestrzeń nazw, która pozwala na logiczne oddzielenie zasobów w klastrze.
  • Ingress: Zarządza dostępem zewnętrznym do usług w klastrze, zwykle poprzez HTTP/HTTPS.
  • ConfigMap: Przechowuje dane konfiguracyjne dla aplikacji.
  • Secret: Przechowuje poufne dane, takie jak hasła i klucze API.

Konfigurowanie Deploymentu i Serwisu dla aplikacji Go

Załóżmy, że mamy już zbudowany obraz Docker go-app. Teraz stwórzmy pliki konfiguracyjne dla Kubernetesa.

Deployment (deployment.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: go-app-deployment
  labels:
    app: go-app
spec:
  replicas: 3  # Uruchom 3 kopie aplikacji
  selector:
    matchLabels:
      app: go-app
  template:
    metadata:
      labels:
        app: go-app
    spec:
      containers:
      - name: go-app
        image: go-app  # Nazwa Twojego obrazu Docker
        ports:
        - containerPort: 8080
        env:
        - name: PORT
          value: "8080"

Opis pliku Deployment:

  1. apiVersion: apps/v1: Określa wersję API dla Deploymentu.
  2. kind: Deployment: Określa, że to jest definicja Deploymentu.
  3. metadata:: Zawiera metadane o Deploymentu.
    • name: go-app-deployment: Nazwa Deploymentu.
    • labels:: Etykiety, które można użyć do identyfikacji Deploymentu.
  4. spec:: Określa pożądaną konfigurację Deploymentu.
    • replicas: 3: Określa, że chcemy uruchomić 3 repliki aplikacji.
    • selector:: Określa, które Pody Deployment ma zarządzać.
      • matchLabels:: Wybiera Pody z etykietą app: go-app.
    • template:: Określa szablon dla Podów, które mają być utworzone.
      • metadata:: Zawiera metadane o Podzie.
        • labels:: Etykiety, które można użyć do identyfikacji Poda.
      • spec:: Określa konfigurację Poda.
        • containers:: Lista kontenerów, które mają być uruchomione w Podzie.
          • name: go-app: Nazwa kontenera.
          • image: go-app: Nazwa obrazu Docker, który ma być użyty do utworzenia kontenera.
          • ports:: Lista portów, na których kontener nasłuchuje.
            • containerPort: 8080: Port, na którym kontener nasłuchuje.
          • env: Zmienne środowiskowe dla kontenera.
            • name: PORT: Nazwa zmiennej środowiskowej
            • value: "8080": Wartość zmiennej środowiskowej

Service (service.yaml):

apiVersion: v1
kind: Service
metadata:
  name: go-app-service
spec:
  selector:
    app: go-app
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
  type: LoadBalancer

Opis pliku Service:

  1. apiVersion: v1: Określa wersję API dla Serwisu.
  2. kind: Service: Określa, że to jest definicja Serwisu.
  3. metadata:: Zawiera metadane o Serwisie.
    • name: go-app-service: Nazwa Serwisu.
  4. spec:: Określa pożądaną konfigurację Serwisu.
    • selector:: Określa, które Pody Serwis ma kierować ruch do.
      • app: go-app: Wybiera Pody z etykietą app: go-app.
    • ports:: Lista portów, na których Serwis nasłuchuje.
      • protocol: TCP: Protokół, którego Serwis używa.
      • port: 80: Port, na którym Serwis nasłuchuje z zewnątrz.
      • targetPort: 8080: Port, na którym Pody nasłuchują wewnątrz klastra.
    • type: LoadBalancer: Typ Serwisu. LoadBalancer udostępnia Serwis na zewnątrz klastra, tworząc zewnętrzny load balancer (jeśli jest dostępne w Twoim środowisku, np. w chmurze). W środowisku lokalnym (np. minikube) możesz użyć NodePort zamiast LoadBalancer.

Wdrażanie aplikacji na Kubernetes

Upewnij się, że masz dostęp do klastra Kubernetes (np. minikube, kind lub klaster w chmurze).

Uruchom następujące polecenia, aby wdrożyć aplikację:

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

Co robią te polecenia?

  • kubectl apply -f deployment.yaml: Wdraża Deployment z pliku deployment.yaml.
  • kubectl apply -f service.yaml: Wdraża Serwis z pliku service.yaml.

Sprawdź, czy aplikacja działa:

kubectl get deployments
kubectl get services
kubectl get pods

Co robią te polecenia?

  • kubectl get deployments: Wyświetla listę Deploymentów.
  • kubectl get services: Wyświetla listę Serwisów.
  • kubectl get pods: Wyświetla listę Podów.

Jeśli używasz LoadBalancer, sprawdź adres zewnętrzny Serwisu i otwórz go w przeglądarce. Jeśli używasz NodePort, znajdź port, na którym Serwis jest dostępny na każdym węźle klastra i otwórz go w przeglądarce.

Praca Domowa

  1. Zmodyfikuj aplikację Go: Dodaj możliwość odczytywania zmiennej środowiskowej MESSAGE i wyświetlania jej na stronie internetowej. Jeśli zmienna nie jest ustawiona, wyświetl domyślny komunikat.
  2. Zaktualizuj Dockerfile: Dodaj instrukcję, która ustawia zmienną środowiskową MESSAGE podczas budowania obrazu.
  3. Zaktualizuj Deployment: Dodaj możliwość przekazywania zmiennej środowiskowej MESSAGE do kontenera za pomocą konfiguracji Deploymentu.
  4. Wdróż aplikację: Zbuduj nowy obraz Docker, wdróż go na Kubernetes i sprawdź, czy zmienna środowiskowa MESSAGE jest poprawnie wyświetlana.

Podsumowanie i Co Dalej?

Gratulacje! Właśnie przeszliśmy razem przez proces wdrażania aplikacji Go do produkcji. Poznaliśmy podstawy Dockera i Kubernetesa, stworzyliśmy obraz Dockera dla naszej aplikacji i wdrożyliśmy ją na Kubernetes.

To, co dzisiaj zrobiliśmy, to tylko wierzchołek góry lodowej. W następnych wpisach przyjrzymy się bliżej konfiguracji aplikacji, monitoringowi, skalowaniu, zarządzaniu danymi i wiele innych.

Zachęcam Cię do samodzielnego eksperymentowania, czytania dokumentacji Dockera i Kubernetesa oraz oczywiście śledzenia naszego bloga. Im więcej ćwiczysz, tym lepiej zrozumiesz te technologie i tym łatwiej będzie Ci wdrażać swoje aplikacje w realnym świecie.

Przydatne linki

Do następnego razu!

Polecane artykuły