Golang ile backend servisler geliştirirken sadelik ve hız benim için hep öncelikliydi. Yazdığım uygulamaların taşınabilir ve izole bir şekilde çalışmasını sağlamak için yıllardır Docker kullanıyorum. Bu yazıda, Go uygulamalarını nasıl containerize ettiğimi, farklı base imajlar arasında nasıl seçim yaptığımı ve özellikle scratch ile nasıl minimal imajlar oluşturduğumu kendi deneyimlerimle anlatacağım.
Başlangıç olarak aşağıdaki gibi çok basit bir Go HTTP servisi kullandım. Bu servis, Berlin saatini döndürüyor:
package main
import (
"fmt"
"net/http"
"time"
)
func handler(w http.ResponseWriter, r *http.Request) {
loc, _ := time.LoadLocation("Europe/Berlin")
currentTime := time.Now().In(loc)
fmt.Fprintf(w, "Berlin time: %s", currentTime.Format("15:04:05"))
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Bu örnek küçük gibi görünse de bir detay var: time.LoadLocation() timezone dosyalarına ihtiyaç duyuyor. Bu da Docker imajı seçerken göz önünde bulundurmamız gereken bir konu.
Dockerfile: Minimal ve Performanslı Build Süreci
Bu uygulamayı build etmek için aşağıdaki gibi iki aşamalı bir Dockerfile hazırladım:
FROM golang:alpine AS build
RUN apk --no-cache add tzdata
WORKDIR /app
ADD . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp
FROM scratch AS final
COPY --from=build /app/myapp .
COPY --from=build /usr/share/zoneinfo /usr/share/zoneinfo
ENV TZ=Europe/Berlin
CMD ["/myapp"]
scratch base imajı tamamen boştur. Ne bir paket yöneticisi vardır, ne bir shell. Sadece sizin koyduğunuz binary ve dosyalar bulunur. Bu sayede imaj boyutu ciddi anlamda küçülür (çoğu zaman 5MB’tan az). Ancak bu sadeliğin bir bedeli var: ihtiyacınız olan her şeyi kendiniz taşımalısınız. Örneğin, yukarıdaki örnekte timezone dosyalarını tzdata üzerinden aldım ve scratch imajına manuel olarak kopyaladım. Aksi takdirde saat bilgisi yanlış çıkıyor.
Docker Base İmajlarını Seçerken Nelere Dikkat Ettim?
Geçmişte farklı imajlarla denemeler yaptım. Her birinin farklı avantajları var:
🧊 alpine, alpine3.22, alpine3.21
- Küçük ve sade bir dağıtım. Ancak
golang:alpineimajı baz alındığında toplam boyut genellikle 500MB–1GB civarına ulaşabiliyor. apkpaket yöneticisi sayesindetzdatagibi ek ihtiyaçlar kolayca kurulabiliyor.- Build aşamasında oldukça hızlı çalışıyor.
- Scratch öncesi minimal build ortamı olarak gayet uygun.
🧱 bookworm, bullseye
- Debian tabanlı imajlardır.
bookwormgüncel,bullseyebir önceki sürüm. - Ancak
golang:bookwormveyagolang:bullseyegibi imajlar 500MB ile 1GB arasında olabilir. - Sistem bağımlılıkları, native C kütüphaneleri veya debug araçları gerekiyorsa tercih edilir.
aptpaket yöneticisiyle çalıştığı için çoğu geliştiriciye daha tanıdık gelebilir.
⚙️ scratch
- En minimal yapı, hiçbir şey içermez
- Maksimum güvenlik, minimum imaj boyutu
- Ancak timezone dosyaları ve diğer gerekli bağımlılıkları kendiniz taşımalısınız
- Sadece statik olarak derlenmiş binary’lerle kullanılabilir (
CGO_ENABLED=0şart)
Hangi İmaj Ne Zaman Kullanılır?
- Geliştirme aşamasında hızlı çalışmak için
alpineya dabookwormtercih ederim. - Üretim ortamında, özellikle küçük servislerde
scratchmükemmel bir seçim. - Sistem bağımlılığı olan, native kütüphane kullanan uygulamalarda
alpineveyabullseyedaha risksiz.
Kendi uygulamalarımda genelde önce golang:alpine ile build edip, son aşamada scratch’a geçerek temiz ve küçük bir üretim imajı elde ediyorum. Bu yapı sayesinde CI/CD süreçleri hızlanıyor, image scanning araçları daha az uyarı veriyor ve dağıttığım container’lar hem güvenli hem hızlı çalışıyor.
Tecrübeyle Sabit: Minimalizm Gerçekten İşe Yarıyor
Yıllardır Go ile yazdığım servisleri Docker ile taşırken öğrendiğim en önemli şey şu oldu: ne kadar az şey, o kadar az sorun. scratch gibi sade yapıların avantajını ancak denediğinizde fark ediyorsunuz. Daha hızlı build, daha küçük boyut, daha az yüzey alanı.
Sen de Go ile geliştiriyorsan ve Docker kullanıyorsan, bu yaklaşımı mutlaka denemeni öneririm. Hafiflik, sadelik ve performans gerçekten kazandırıyor.
Diğer Bağlantılar