Docker Görüntünüzün Boyutunu Minimize Edin
CI/CD gibi süreçlerimizi hızlandırmak için Docker Image boyutlarımızı minimize etmek önemlidir. Bu yazımızda basit bir Go uygulaması yazıp, hem Go’yu hem de Docker’ı optimize ederek 4 adımda Image boyutumuzu neredeyse 1GB’dan 1 MB’a kadar düşüreceğiz.
Ön Hazırlık⌗
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func (w http.ResponseWriter, _ *http.Request) {
fmt.Fprintf(w, "Hello World")
})
http.ListenAndServe(":80", nil)
}
Burada basit bir HTTP server başlatan 10 satırlık Go programı yazdık. Şimdi bunu adım adım Container haline getirelim.
Adım 1 - Yeni Bir Başlangıç⌗
Öncelikle yazabileceğimiz en kötü Dockerfile’ı yazalım. Burada kısaca golang 1.16 image’ını kullanıyoruz ve uygulamamızı build edip entrypoint olarak belirtiyoruz.
FROM golang:1.16
WORKDIR /app
COPY . .
RUN go build -o /bin/blog-app
ENTRYPOINT ["/bin/blog-app"]
Build Sonucu Image Boyutu: 868 MB
Bu iyi bir image değil, boyutu gereksiz büyük. Bunu optimize etmeye başlayalım.
Adım 2 - Minimalizm Bazen İyidir⌗
Şimdi yazdığımız bu Dockerfile’ı biraz daha optimize edelim ve alpine image’ı kullanalım. Bilmiyorsanız Alpine, image’ın boyutunu düşürmek için biçilmiş bir kaftandır, çünkü içerisinde temel os runtime’ları dışında pek bir binary yoktur. Buradan üstteki yaptıklarımızdan farklı olarak sadece ana image’ımızı Alpine olarak değiştiriyoruz.
FROM golang:1.16-alpine
WORKDIR /app
COPY . .
RUN go build -o /bin/blog-app
ENTRYPOINT ["/bin/blog-app"]
Build Sonucu Image Boyutu: 308 MB
~(-500MB)
Gördüğünüz gibi Alpine image’ına geçerek yaklaşık 500 MB tasarruf sağladık.
Adım 3 - Multi-Stage Build⌗
Go’nun güzel özelliklerinden biride static build alabilmenizdir. Yani Go programınızın çalışması için libc
gibi bağımlılıklara
ihtiyaç duymadan çalışabilir. Burada artık multi-stage build dediğimiz yapıya geçiyoruz. Yani build’imizi Alpine image’ımızda
yapıp, aldığımız build’i scratch
dediğimiz boş bir image içerisine kopyalayıp orada çalıştırıyoruz.
### Stage 1
FROM golang:1.16-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -o /app/blog-app
### Stage 2
FROM scratch
COPY --from=builder /app/blog-app /bin/blog-app
ENTRYPOINT ["/bin/blog-app"]
Build Sonucu Image Boyutu: 4.42 MB
~(-300MB)
Burada ise multi-stage build ile boş bir image’a geçerek 300 MB tasarruf sağlamış olduk. Yani sadece build ettiğimiz binary’nin boyutu kalıyor geriye. Hadi bunuda optimize edelim.
Adım 4 - UPX⌗
Artık son adıma geldik. Burada ise UPX kullanarak build ettiğimiz static binary’i daha da minify edelim.
### Stage 1
FROM golang:1.16-alpine AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -o /app/blog-app
RUN apk add upx
RUN upx --ultra-brute /app/blog-app
### Stage 2
FROM scratch
COPY --from=builder /app/blog-app /bin/blog-app
ENTRYPOINT ["/bin/blog-app"]
Build Sonucu Image Boyutu: 1.32 MB
~(-3MB)
Binary’mizide minify ettik ve 3 MB bir tasarruf sağlayarak image boyutumuzu 1 MB’a kadar indirmiş olduk. Unutmayın bu adımdaki boyut değişimi, sizin programızında ne kadar paket kullandığınıza ve ne kadar kod bulunduğa bağlı olarak değişebilir. Yani go build’i 100 MB çıkıyor ise UPX ile bunu 30 MB’a düşürebilirsiniz.
Sonuç⌗
REPOSITORY | TAG | IMAGE ID | SIZE |
---|---|---|---|
blog | step-1 | d96961d177b7 | 868MB |
blog | step-2 | 7c874d66fd2d | 308MB |
blog | step-3 | 92e68959939e | 4.42MB |
blog | step-4 | 0a753fc54c4c | 1.32MB |
Günün sonunda yukarıdaki gibi bir tablo karşımıza çıkıyor. Optimizasyonlar ile birlikte image boyutumuzu 868 MB‘dan 1.32 MB‘a düşürmüş oluyoruz. Tabiki burada unutmamız gereken bazı maddeler var.
- Image boyutu programınızın performansını etkilemez, ama bir çok süreci hızlandırabilir.
- Eğer programınızın os veya paket bağımlılıkları varsa bunları Dockerfile’a eklemeniz gerekir ve boyutu küçülteyim derken anlaması zor complex bir Dockerfile üretebilirsiniz.
- UPX her durumda gerekli olmayabilir, burada dikkat etmeniz gereken nokta sizin ne kadarlık bir boyuta kadar bunu tolere edebileceğiniz.
Umarım bu yazı sizin için faydalı olmuştur, bir sonraki yazılarda görüşmek üzere.