Docker Bölüm 4: Dockerfile

Docker

Bir image oluşturmak için var olan tüm katmanların madde madde açıklandığı ve tüm işlemlerin detaylıca belirtildiği text dosyalarıdır.

Dockerfile, YAML adı verilen, insanlar tarafından kolayca okunup anlaşılabilir bir dil ile kodlanmaktadır. Satırlardan meydana gelir ve her bir satır bir işlemi gösterir.

Dockerfile Dosya Yapısı

Dockerfile için kullanılabilecek komutlar: https://docs.docker.com/engine/reference/builder/

FROM

Hangi image dosyasının referans alınacağını belirtir.

FROM image:tag şeklinde kullanılır. Ör:

Referans alınacak image dosyası ne kadar küçükse o kadar iyi, çünkü container yüklenirken işlemi buna göre oldukça kısalabilir.

RUN

Container içerisinde komut çalıştırmak için kullanılır. Bir komut bu şekilde işletildiğinde container ayakta olduğu sürece sürekli çalıştırılır.

RUN komut

Veya

RUN [“executable_file”, “değer1”, “değer2”]

şeklinde kullanılır. Ör:

ADD

Container içerisinde internet ortamından veya dışarıdan dosya veya data kopyalamak için kullanılır.

ADD kaynak_dosya hedef_dosya

veya

ADD [“./webfile/”, “/var/html/www”]

veya

ADD [–chown=user:group] kaynak hedef

şeklinde kullanılır. Ör:

COPY

ADD komutu gibi kaynak ve hedef arasında dosya kopyalamayı sağlar. ADD komutundan tek farkı ise sadece host üzerinden yani lokaldeki dosyaları aktarmayı sağlamasıdır. Bunun dışında kullanım açısından bir değişiklik yoktur.

COPY kaynak_dosya hedef_dosya

veya

COPY [–chown=user:group] kaynak hedef

şeklinde kullanılır. Ör:

WORKDIR

Container içerisinde çalışma dizini oluşturmak için kullanılır. Bu şekilde belirtilen dizin üzerinde tüm işlemler gerçekleştirilir. Eğer dizin yok ise otomatik olarak oluşturulacaktır.

WORKDIR dizin

WORKDIR $DIRPATH/$DIRNAME

şeklinde kullanılır. Ör:

VOLUME

Container içerisine kalıcı (persistent) depolama alanı olarak kullanılacak alanı eklemek için kullanılır.

VOLUME [“mount_point”]

veya

VOLUME mount_point

şeklinde kullanılır. Ör:

EXPOSE

Normal şartlar altında container’lar dış dünyadan izoledir ve haberleşemezler. EXPOSE komutu ile container’lar hem birbirleri ile hem de dış dünya ile haberleşebilirler. Eğer protocol bilgisi verilmezse default olarak TCP protocol’ünü tanımlar.

EXPOSE [port/protocol]

şeklinde kullanılır. Ör:

LABEL

Image sahini veya diğer metadataları eklemek için kullanılır.

LABEL key=value key=value

şeklinde kullanılır. Ör:

USER

Container içerisinde hangi kullanıcı ile işlem yapılacağını belirtmek için kullanıyoruz.

USER user_name[:group]

şeklinde kullanılır. Ör:

ENV

Container içerisinde kullanılacak ortam değişkenlerini tanımlamak için kullanılır.

ENV key value

şeklinde kullanılır. Ör:

CMD

Container oluşturulduktan sonra çalıştırılması istenen uygulamaları çalıştırmak için kullanılır. RUN komutundan temel farklı yalnızca 1 kez ve container ayağa kalkınca çalıştırılır.

CDM komut

CMD [“executable_file”, “değer1”, “değer2”]

CMD [“parametre1”, “parametre2”]

şeklinde kullanılır. Ör:

Dockerfile üzerinde 1 tane CMD komutu kullanılması gerekir. Birden fazla girildiği durumlarda en son girilen CMD komutu geçerli olur ve işletilir.

ENTRYPOINT

Container oluşturulduğunda varsayılan parametreleri tanımlamak ve çalıştırmak için kullanılır. CMD gbi çalışır, container ayağa kalkınca çalıştırılırlar; fakat CMD’ye göre daha esnek kullanıma sahiptir.

ENTRYPOINT path

ENTRYPOINT [“executable_file”, “değer1”, “değer2”]

şeklinde kullanılır. Ör:

Dockerfile Yazım Formatları

Shell

Shell yazım formatında komutlar herhangi bir kapsayıcı içerisine alınmadan, terminal ekranına yazar gibi yazılırlar. Ör:

Exec

Json yazım formatıdır. Genelde köşeli parantezlerle yazılan komutlardır. Önerlien yazım şekli budur.

Dockerfile’dan Image Oluşturma

Öncelikle Nginx çalışacak örnek bir Dockerfile oluşturalım.

Şimdi Dockerfile’ı image haline getirelim. Bunun için kullanacağımız komut:

Buradaki nokta, bulunduğumuz dizindeki Dockerfile’ın derlenmesi gerektiğini belirtir. Eğer başka bir dizinde ise:

Eğer bu Dockerfile’ı Docker Hub’a göndereceksek, buna göre tag kısmını düzenlememiz gerekir:

Şimdi gelelim build işleminin tetiklenmesine. Bu işlemi yaptığımızda neler oluyor?

  1. İlk satırda Ubuntu işletim sisteminin imge’inin kullanacağını bildirdiğimiz için, ilgili image indirme işlemi başlayacak.
  2. LABEL komutundakine göre etiketleme işlemi gerçekleşecek.
  3. Son katman silinecek ve RUN komutu işletilerek gerekli kurulumlar yapılacak.
  4. Çalışma dizini oluşturulacak ve bir önceki katman silinecek
  5. Host üzerindeki dosyalar çalışma dizinine kopyalanacak ve bir önceki katman silinecek.
  6. Container’ın iletişim kurulacak TCP portu atanacak ve bir önceki katman silinecek.
  7. Nginx’e gönderilen komut çalıştırılacak.

Build işlemi tamamlanmış oldu.

Şimdi oluşturduğumuz image’den bir container ayağa kaldıralım:

Şimdi kontrol edelim:

Türkçe karakterler bozuk çıkmış 😊 Ama olsun, maksat zaten deneme yapmaktı.

Bir kez daha aynı Dockerfile üzerinden yeni bir image oluşturmak istediğimizde bunun çok daha kısa sürdüğünü göreceksiniz. Bunun nedeni her bir katman için cache kullanmasıdır.

Eğer bunu istemiyorsak build kısmında –no-cache parametresini ekleyebiliriz.

.dockerignore

Bu dosya, image build ederken eklenmesini istemediğimiz dosyaların bir listesini eklemek için kullanıyoruz.

Çok Katmanlı Dockerfile

Dockerfile’da çok fazla komut olması çok fazla katmanın oluşmasına neden olur, çok fazla katman oluşması da image boyutlarını oldukça artırır. Bunun en büyük dezavantajı container’ın ayağa kalması esnasında olur ve container ayakta kaldıkça da performans problemlerine yol açar. İkinci bir durum da karmaşıklık arttığı için güvenlik problemlerine yol açar. Bu sebeple Dockerfile’ın katmanlı modelde geliştirilmesi tavsiye edilir.

Katmanlı Dockerfile nasıl yapılır? Öncelikle bir Dockerfile oluştururuz, bu her zamanki gibi FROM ibaresi ile başlayan base image kullanımı ile başlar ve temel işlemler yapılır. Sonra bir Dockerfile daha oluşturulur ve yine FROM kısmında aynı base image tanımlanır ve ilk oluşturduğumuz Dockerfile’dan oluşan konfigürasyon dosyası da bu Dockerfile’da kullanılır. Bu şekilde ihtiyaç duyduğumuz kadar yeni Dockerfile oluşturarak çok katmanlı Dockerfile kullanmış oluruz.

Şimdi iki katmanlı bir örnek yapalım:

İlk satırımız birinci katmanımız ve Alpine image’inden oluşturuyoruz ve adını derle veriyoruz. Eğer bir isim vermezsek buna default 0 verilir. Sonra ikinci FROM ifadesine kadar birinci katmanda olması gerekenleri yazıyoruz.

İkinci FROM ifadesi ise ikinci katmanımızın başlangıcını gösteriyor. Yukarıda da dediğimiz gibi birinci katman ile aynı image kullanılmak zorundadır.

İkinci katmanda base belirildikten sonra yani FROM ifadesinden hemen sonra COPY komutunu görüyoruz. Bu birinci katmanı devralma işlemini yapıyor bizim için. Birinci katmandan oluşturduğumuz ve dosyaları içine attığımız /app/bin/merhaba dizinini /app/merhaba dizini içerisine kopyalıyoruz ve CMD ile katmanımızı sonlandırıyoruz.

Şimdi 3 katmanlı bir Dockerfile’a bakalım:

Görüldüğü gibi her bir FROM ifadesi aslında yeni bir katmana denk geliyor. Böylece image daha küçük ve stabil halde kullanılabilir.

Dockerfile Bestpractice

  1. Dockerfile’da komutlar yazılırken sıralamaya göre baştan başlayarak okuyup container ayağa kaldırılır. Bu sebeple komutların doğru sırayla yazılması önemlidir. Mesela şu örneğe bakalım:FROM debian
    COPY . /app
    RUN apt-get update
    RUN apt-get -y install openjdk-8-jdk ssh vim
    CMD [“java”, “-jar”, “/app/target/app.jar”]

    Görüleceği gibi hemen ikinci satırda kopyalama işi yapılmış, oysaki burada yapılması gereken COPY komutunun CMD’den bir önceki satırda yazılmış olmasıdır. Bu şekilde kullanım container bellek kullanımını olumsuz etkilemektedir. Öncelikle sistemde yapılması gereken update’ler, sonrasında installation’lar akabinde çalışmaya yönelik komutar verilmelidir.

  2. Dockerfile kodlanırken, image’in içinde lüzumsuz, kullanılmayacak dosyalar kopyalanmamalıdır. Bu image boyutlarının gereksiz büyümesine ve performans problemleri yaşanmasına neden olacaktır. Mesela şu örneğe bakalım:FROM debian
    RUN apt-get update
    RUN apt-get -y install openjdk-8-jdk ssh vim
    COPY . /app
    CMD [“java”, “-jar”, “/app/target/app.jar”]

    Görüldüğü gibi COPY komutu ile dizindeki tüm dosya ve klasörler kopyalanıyor. Bu gerçekten gerekli değilse, sadece amaca yönelik dosyaların yer aldığı dosyaların yüklenmesi gereklidir. Ör: COPY target/app.jar /app

  3. RUN komutu ile işletilecek komutlar aşağıdaki gibi birden çok satırda yazılmamalıdır.FROM debian
    RUN apt-get update
    RUN apt-get -y install openjdk-8-jdk ssh vim
    COPY target/app /app
    CMD [“java”, “-jar”, “/app /app.jar”]

    Daha önce de konuştuğumuz gibi her bir komut yeni bir katman şeklinde, ayrı ayrı haslenerek saklandığı için bu oluşacak image dosyasının büyümesine neden olacaktır. Doğru yazım şu şekilde olmalıdır:

    RUN apt-get update \
    && apt-get -y install \
    openjdk-8-jdk ssh vim

  4. Gereksiz kurulum işlemlerinden kaçınılmalıdır, bu da image’in büyümesine neden olur. Bununla birlikte kurulumda çoğu araç bizim ihtiyacımız olmasa da gerekli gördüğü kurulumları da otomatik yapar. Bu da doğal olarak image boyutlarının büyümesine neden olur. Bunun yerine sadece ihtiyacımız olan kurulumları yapmalıyız. Şimdi örneğimizden devam edersek:FROM debian
    RUN apt-get update \
    && apt-get -y install \
    openjdk-8-jdk ssh vim
    COPY target/app /app
    CMD [“java”, “-jar”, “/app/app.jar”]

    Öncelikle ssh ve vim kurumlarına ihtiyacımız yok. Bunları çıkartalım ve jdk kurulumlarında kurulumda önerilenleri de yüklemesinin önüne geçmek için: –no-install-recommends eklemek lazım. Yani şöyle olmalıdır:

    FROM debian
    RUN apt-get update \
    && apt-get -y install –no-install-recommends openjdk-8-jdk

  5. RUN komutu ile kurulan programlar için önbelleğe alınan dosyaların silinmesi gerekir. Bu da gereksiz yere image boyutlarının büyümesine neden olur. Doğrusu aşağıdaki gibidir:FROM debian
    RUN apt-get update \
    && apt-get -y install –no-install-recommends openjdk-8-jdk \
    && rm -rf /var/lib/apt/lists/*
    COPY target/app /app
    CMD [“java”, “-jar”, “/app/app.jar”]

    apt gibi paket yükleyicileri kurulumlar için gereken dosyaları önbelleklerinde tutmaktadır ve yükleme işleminden sonra da bu dosyalar silinmemektedir. Bu yüzden bizim bu işlemi yapmamız gerekir.

  6. Dockerfile’da FROM ibaresi ile base olarak kullandığımız image, işimizi görmeye yönelik image olmalıdır.FROM debian
    RUN apt-get update \
    && apt-get -y install –no-install-recommends openjdk-8-jdk \
              && rm -rf /var/lib/apt/lists/*
    COPY target/app /app
    CMD [“java”, “-jar”, “/app/app.jar”]

    Mesela bizim ihtiyacımız olan jdk olduğu için jdk ile ilgili image’i kullanmak daha doğru olacaktır:

    FROM openjdk
    COPY target/app /app
    CMD [“java”, “-jar”, “/app/app.jar”]

  7. Son sürümü kullanmak her zaman doğru olmayabilir. Bu yüzden uygulamanız için gereken versiyon ne ise onu kullanmalısınız:FROM openjdk:8
    COPY target/app /app
    CMD [“java”, “-jar”, “/app/app.jar”]
  8. Kullanılacak base image’in, alpine yüklü, en küçük boyutta olanın kullanılması daha doğru olacaktır.
  9. Çok katmanlı Dockerfile kullanımı daha önce de bahsettiğimiz gibi image boyutlarının ufak olmasını sağlayacaktır.
Not: Kaynak olarak Murat AKSU’dan faydalanılmıştır.

Data Science Earth

Data Science Earth ekibi, üst düzey Veri Bilim çözümleri üretmek amacı ile toplanmış akademisyenler ve uzmanlardan oluşmaktadır. Öncelikli olarak veri bilincini geliştirmeyi ve küreselleşen rekabet ortamında verinin gücünün doğru kullanılmasını sağlamayı amaçlamaktadır.

Sponsor

QuestionPro 35 farklı soru seçim özelliği ile anket çalışmalarımıza güç katmaktadır.