Projektując nowoczesne systemy w architekturze mikroserwisowej, inżynierowie stają przed dwoma kluczowymi wyzwaniami: jak zapewnić maksymalną wydajność i niskie opóźnienia oraz jak nie przepłacać za infrastrukturę chmurową w okresach niższego ruchu.
Połączenie języka Go (Golang) ze środowiskiem Google Cloud Run to jeden z najbardziej efektywnych wzorców architektonicznych w inżynierii backendowej. W tym artykule wyjaśniamy, dlaczego ten duet deklasuje tradycyjne podejścia oparte na ciężkich maszynach wirtualnych lub klastrach Kubernetes.
1. Błyskawiczny start: Zimne starty (Cold Starts) poniżej 300 ms
Jedną z największych bolączek architektury Serverless (takiej jak Cloud Functions czy Cloud Run) w technologiach takich jak Java, Python czy Node.js jest czas uruchamiania nowej instancji kontenera po skalowaniu od zera (Cold Start).
- Java / Spring Boot: Czas startu JVM i kontekstu frameworka często wynosi 5–15 sekund.
- Node.js / Python: Wymagają interpretacji kodu i ładowania setek modułów z
node_modules(1.5–3 sekundy). - Go: Kompiluje się do jednego, natywnego pliku binarnego.
Obraz kontenera w Go oparty na scratch lub distroless waży zaledwie 15–25 MB. Gdy Cloud Run otrzymuje nowe żądanie i musi powołać nową instancję kontenera, proces w Go jest gotowy do obsługi ruchu w czasie 150–300 ms. Dla użytkownika końcowego zimny start jest praktycznie niezauważalny.
# Przykładowy wieloetapowy build Dockera dla serwisu Go
FROM golang:1.24-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o server .
FROM gcr.io/distroless/static-debian12
COPY --from=builder /app/server /server
EXPOSE 8080
ENTRYPOINT ["/server"]
2. Model kosztowy: Realne Scale-to-Zero
Tradycyjne klastry Kubernetes (GKE, EKS) generują wysokie koszty stałe - węzły klastra muszą działać 24/7, nawet w nocy, gdy ruch spada do zera.
Google Cloud Run rozlicza się w modelu Pay-per-use:
- Płatność za ułamki sekund: Płacisz wyłącznie za czas, w którym procesor faktycznie przetwarza żądanie (z dokładnością do 100 ms).
- Scale to Zero: Jeśli przez godzinę serwis nie otrzymał ani jednego zapytania, liczba działających instancji spada do
0. Koszt infrastruktury wynosi dokładnie 0 PLN. - Darmowy pakiet (Free Tier): Każdego miesiąca Cloud Run oferuje 2 miliony darmowych żądań oraz pulę darmowych vCPU-sekund i RAM-sekund.
Dla średniej wielkości mikroserwisu backendowego miesięczny rachunek z Cloud Run może spaść z kilkuset dolarów (na dedykowanym klastrze VM) do zaledwie kilku dolarów, przy zachowaniu gotowości na gwałtowne skoki ruchu.
3. Współbieżność (Concurrency): Jedna instancja, tysiące połączeń
Większość środowisk serverless (np. tradycyjne AWS Lambda) działa w modelu 1 żądanie = 1 kontener. Jeśli w jednej chwili pojawi się 500 zapytań, chmura musi uruchomić 500 kontenerów, co drastycznie podnosi koszty i potęguje problem zimnych startów.
Cloud Run wspiera wielowątkową współbieżność (Concurrency do 1000 zapytań na jeden kontener). I tutaj Go błyszczy najbardziej:
- Lekkie Goroutines: W Go pojedynczy wątek logiczny zajmuje zaledwie ~2 KB pamięci RAM (w porównaniu do 1–2 MB dla wątku systemowego w Javie).
- Kontener z 1 vCPU i 256 MB RAM napisanym w Go potrafi równolegle obsługiwać 200–500 zapytań HTTP/gRPC bez degradacji wydajności i bez wycieków pamięci.
- W rezultacie do obsłużenia dużego ruchu wystarczy zaledwie 2–3 instancje Cloud Run zamiast setek pojedynczych instancji.
package main
import (
"context"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
})
mux.HandleFunc("/api/v1/orders", handleOrders)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
server := &http.Server{
Addr: ":" + port,
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
// Błyskawiczny start i graceful shutdown
go func() {
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
panic(err)
}
}()
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
server.Shutdown(ctx)
}
4. Brak Vendor Lock-in: Czyste kontenery OCI
Wybierając Cloud Run nie wiążesz się z autorskim API dostawcy chmurowego (jak w przypadku dedykowanych frameworków serverless):
- Usługa to zwykły kontener Docker spełniający standard OCI i nasłuchujący na zmiennej środowiskowej
PORT. - Ten sam kontener możesz uruchomić lokalnie (
docker run), przenieść na Kubernetes (KNative, vanilla k8s), AWS ECS, czy dowolny serwer VPS.
Podsumowanie
W itbsv rekomendujemy zestawienie Go + Google Cloud Run wszędzie tam, gdzie liczy się:
- Bardzo niski narzut operacyjny (brak konieczności łatania systemów operacyjnych maszyn wirtualnych).
- Odporność na nagłe skoki ruchu (ruch e-commerce, kampanie reklamowe).
- Optymalizacja kosztów chmurowych z gwarancją czasu odpowiedzi w milisekundach.