Docker Katmanları: Performansı Artırma İpuçları

demironW47
GoTurkiye
Published in
4 min readJul 6, 2023
src

Dockerfile dosyası, bir docker container’ın nasıl oluşturulacağını belirlemek için kullanılan metin tabanlı bir dosyadır. Dosya içerisinde tanımlanan komutlar birer layer oluşturur ve bu layerlardan bazıları cache’de tutulur.

Bu dosya içerisinde yapılan esas işlem, projenin linux bir ortanda çalışması için gereken altyapının hazırlanmasıdır. Aşağıdaki örnekte go ile yazılmış bir proje için oluşturulan Dockerfile dosyası bulunmaktadır.

https://github.com/daddydemir/Kirtasite
  • Birinci satırdan projenin go dili ile geliştirilmiş bir proje olduğu ve go programlama dilinin 1.19 versiyonunda çalışacağı belirtilmiş. alphine ifadesi ise linux dağıtımını ifade etmektedir.
  • Üçüncü satırda app isminde bir klasör oluşturulmaktadır.
  • beşinci satırda projeye ait dosyalar app dizinine kopyalanmaktadır.
  • Şu anki aşamada go projelerinin üzerinde çalışabileceği küçük bir işletim sistemimiz ve projeye ait dosyalarımız var, bundan sonra yapmamız gereken projeye içerisindeki bağımlılıkların çekilmesi ve bunun için go mod download komutunu kullanıyoruz. devamındaki ifade ise derleme işlemini yapıp bize app isminde çalıştırılabilir bir dosya veriyor.
  • EXPOSE ifadesi projenin hangi portta çalışacağını ifade ediyor.
  • Son olarak CMD komutu ise container çalıştırıldığı zaman çalışacak olan komutların belirtildiği alandır.

&& (ampersand) ifadesini kullanmamın nedeni her bir komut için bir layer oluşturulmasından kaynaklanıyor. Bu örnekte çok büyük bir problem olmasa da layer sayısının az olması daha yönetilebilir olduğundan bu ifadeyi && kullanarak tek satırda yazdım.

yukarıdaki Dockerfile’ı derlediğimizde aşağıdaki çıktıyı alırız.

Çıktıdan anlaşıldığı üzere 4 tane layer oluşturulmuş. İlk komut olan FROM golang:1.19-alpine komutu diğer komutlara göre çok daha uzun sürmüş, bunun sebebi alphine’a ait dosyaların boyutu ve makinemdeki indirme hızından kaynaklanıyor.

Kaynak kodumda herhangi bir değişiklik yapmadan tekrar build aldığımda ise aşağıdaki çıktıyı üretiyor.

Projeye ait kodlarda herhangi bir değişiklik yapmadığım için 3 katmanda da cache’den getirdi. Kodumda değişlik yaptıktan sonra ise aşağıdaki çıktıyı verdi.

Bu durumda ise kodun benim bilgisayarımdan docker ortamına kopyalanması ve devamında gelen build işlemlerini tekrardan yaptı. Kodumdaki değişiklikleri algıladığı için kodun kopyalanması ve build alma işlemini cache’den değil sıfırdan yaptı. Bir katman değiştiğinde ondan sonra gelen katmanlar da değişeceği için COPY komutundan sonra birden fazla işlem olsaydı onlar da cache’den değil sıfırdan gerçekleştirilecekti.

Bunu daha optimize edebilir miyiz? Kodda değişiklik yaptığımı söyledim, bu değişiklik ekrana mesaj bastırmaktı. Yeni bir modül çağırmadım, aslında gereksiz bir şekilde bağımlılıkları yeniden çekti. Bunu düzeltebilirim, bağımlılıklarıma ait bilgilerin yazılı olduğu go.mod ve go.sum dosyalarımı ilk olarak docker’a kopyalarım daha sonra bağımlılıkları çekerim. Bu durumda go.mod ve go.sum’da herhangi bir değişiklik olmadığı için bunları cache’den getirir. devamında bağımlılıkları çekme komutunu da cache’den getirir ve böylelikle gereksiz yere bağımlılıkların çekilmesi önlenmiş olur. Optimize hali;

|---------------------|---------------------|---------------------|
| 1. çalıstıma | CACHE YOK | 262.8s |
|---------------------|---------------------|---------------------|
| 2. çalıstırma | CACHE VAR | 4.8s |
|---------------------|---------------------|---------------------|
| 3. çalıstırma | CACHE VAR | 14.7 |
|---------------------|---------------------|---------------------|

Tablodan da görüldüğü üzere cache’in dahil olduğu işlemlerin daha hızlı bir şekilde gerçekleştiğini söyleyebiliriz. Burada dikkat edilmesi gereken nokta 1. çalıştırmada benim bilgisayarımda alphine imajının olmamasından ötürü bu kadar uzun sürdü. Eğer bilgisayarımda alphine imajı olsaydı onuda cache’den getirip daha hızlı bir şekilde süreci tamamlayacaktı. Ben cache’deki bütün verileri temizleyebilmek için aşağıdaki komutu kullandım;

docker system prune -a

Not: Bu komut docker’a ait ne var ne yoksa siliyor, kullanırken iki kez düşünün. (docker zaten soruyor :)

Image Optimizasyonu (multi-stage)

Docker imaj optimizasyonu, hızlı ve ölçeklenebilir uygulama dağıtımı için önemlidir. Docker, uygulamaları hafif ve bağımsız konteynerlere paketleyerek taşınabilirliği artırır. Benim oluşturduğum imajın boyutu 414MB.

İmajın boyutunu küçültmek için kullanılan yöntem birden fazla stage kullanmaktır. Bizim kullandığımız ilk Dockerfile dosyasında sadece bir adet FROM komutu vardı ve bu komut projemizin alphine işletim sistemi üzerinde çalışacağını ifade ediyordu, ancak bizim işletim sistemi içerisinde bulunan bir çok yazılma ihtiyacımız yok, eğer bu yazılımlarım olmadığı bir linux dağıtımı bulabilirsek istediğimizi yapmaya bir adım yaklaşmış oluruz. Bu noktada devreye scratch giriyor. Scratch boş bir imajdır ve bütün imajların atasıdır. Statik olarak derlenen projemizin çıktısı scratch içerisinde herhangi bir bağımlılığa ihtiyaç duymaksızın çalışacaktır. Örnek Dockerfile’da yapılan işlem statik olarak derlenen ikili dosyayı scratch içerisine taşımak. Bu durumda dosya boyutu 13.7MB oldu. Büyük oranda imajı küçültmeyi başardık.

Umarım sizler için bilgilendirici bir içerik olmuştur, hepinize az bug’lı geliştirmeler dilerim.

--

--