Docker Image Nasıl Hazırlanır?
Öncelikle bu yazıya başlamadan önce eğer docker ,container mimarisi ve kubernetes konsunda bilginiz yok ise aşağıdaki yazıları okumanızı tavsiye ederim.
Dockerfile’ın Yapısı
Dockerfile oluşturulacak image özellikleri, içereceği uygulamaları ve konfigürasyonlarının belli bir paterne uygun yazılan bir dosyadır. En basit anlatımla talimatlar dizesidir ve bu dosyanın adı kesinlikle Dockerfile şeklinde olmalıdır.
Dockerfile Instructions(Argümanlar)
Ufaktan Dockerfile konusunda ön bilgi verdikten sonra Dockerfile oluştururken kullanılan instructions(Argümanları) kullanım amaçlarını ve ufak örnekler vereceğim.
FROM
Baz Image belirtmek için kullanılır. Bu aşamadan sonra yapılan tüm işlemler referans alınan image üzerinde yapılır. Burada ubuntu’nun en son sürümü alınmış.
# Base image
FROM ubuntu:latest
Eğer farklı bir sürüm kullanmak isteseydik. Bu şekilde yazacaktık.
# Base image
FROM ubuntu:18.04
MAINTAINER
Dockerfile’ı oluşturan kişi bilgilerinin vermek için kullanılır. Eğer bir image oluşturup ve bunu Docker Hub üzerinde paylaşacaksınız kullanmanızda fayda olabilir.
MAINTAINER Arif KIZILTEPE <kzltpsgm@gmail.com>
RUN
Build işlemi sırasında çalıştırılmasını istediğiniz komutları belirtmek için kullanılır. Örneğin baz ubuntu
Image’ına vim editör kurmak istiyorsanız RUN
kullanmamız gerekecektir.
RUN apt-get install vim
Bir Dockefile içerisinde birden fazla RUN
işlemi yapabiliriz. Örneğin vim editör ile birlikte http server kuracağız ve http server servisini start edeceğiz.
RUN apt-get install vim apache2
RUN systemctl start apache2.service
RUN systemctl enable apache2.service
Ve bir RUN
işlemi içerisinde birden fazla işlemi de yapabiliriz. Aslında RUN
işlemi sonrasında ubuntu cli ile ne yapabiliyorsanız buraya uyarlayabilirsiniz. Aşağıdaki gördüğünüz örnekte &&
ile farklı amaçlar için kullanılan komutları tek bir komut gibi çalışmasını sağladım.
RUN apt-get install vim apache2 && \
systemctl start apache2.service && \
systemctl enable apache2.service
ADD ve COPY
Dockerfile instructions(Argümanları) içinde karıştırılan ikililerden biri. Bu yüzden bu ikiliyi tek başlık altında anlatmak daha sağlıklı olacaktır. ADDh
ost dosya sisteminde veya internetten image içerisine dosya eklemek için kullanılır. ADD
argümanının 2 farklı kullanım şekli vardır.
ADD <src>... <dest>
ADD ["<src>",... "<dest>"]
Host dosya sisteminden image içerisine dosya kopyalamak için.
ADD [ "./BuildDir/", "/app/bin"]
İnternetten Image’a dosya kopyalanması için.
ADD [ "https://curl.haxx.se/download/curl-7.50.1.tar.gz", "/tmp/curl.tar.gz" ]
COPY
argümanıADD
argümanı ile aynı şekilde kullanılır fakat internetten dosya indiremez sadece Host dosya sisteminden kopyalama işlemi yapabilir.
COPY ./BuildDir/ /app/bin
Şöyle ekstra bir bilgi daha ekleyeyim. Sizin bir klasörünüz var ve siz bu klasördeki *.log dosyalarını image içerisine kopyalamak istemiyorsunuz bunun için Dockerfile’ın bulundugu dizinde .dockerignore
adında bir dosya açıp içerisinde belirtebilirsiniz.
EXPOSE
EXPOSE
işlemi yapılmadığı durumda docker networking container’ların birbirlerinin portlarına bağlanması izin vermez. Container içerisinde çalışan uygulamanın erişilmesi gereken portu EXPOSE
işlemi ile erişilebilir duruma getirmek gerekir.
EXPOSE 8080
Belirtilen port aynı docker daemon üzerindeki container’lar tarafından erişilebilir durumdadır. Bu portu host işletim sisteminden açmak için -p 8080
kullanılır.
WORKDIR
Herhangi bir işlem öncesinde veya image’ın tümünün çalışma dizinini değiştirmek için kullanılır. Daha basit bir anlatım ile normal koşullarda ubuntu bir bilgisayara ssh yaptığınızda o kullanıcının wORKDIR
dizini kullanıcı home dizini olarak belirlenir ve çalıştırılan komutlar bu dizinde çalıştırılır. Aynı şekilde Dockerfile içinde belirtilen RUN, CMD, ADD, COPY veya ENTRYPOINT komutları bu dizinde çalıştırılır.
WORKDIR /java/jdk/bin
ENV ve ARG
Çokça karıştırılan ikililerden bir diğeri de ENV
ve ARG.
Her ikisi de değişken tanımlamak için Dockerfile içerisinde kullanılır. Fakat ENV
ileride çalışan konteynerler için ARG
ise Docker image build işlemi sırasında kullanılır.
Örnek vermek gerekirse docker üzerinde ocr işlemi yapacak python projesi çalıştıracağız. Python işlemlerini yaparken işletim sisteminden LANG değişkeninin alıyor. İşte buradaki örnekte çalışan konteyner için atanması gereken bir işlem olduğu için ENV
kullanmak doğru olacaktır.
ENV LANG C.UTF-8
ARG
için bir örnek vermek gerekirse FROM
işlemi sırasında baz image olarak ubuntu:latest
kullanıştık. Docker image build işlemi sırasında ubuntu sürümünü değiştirmek için ARG
kullanmak doğru olacaktır.
ARG version=16.10
FROM node:$NODE_VERSION
ARG
değişkenlerini Dockerfile kullanır.ENV
değişkenlerini Konteyner kullanır.- ARG değişkenleri
build
işlemi sırasında kullanılır. Build işlemi sırasında bu değişken değiştirilebilir.
(docker build --build-arg version=20.04
).
ENV
değişkenlerirun
işlemi sırasında kullanılır.
(docker run -e "LANG=C.UTF-8"
).
ARG
değişkenlerinedocker run
işleminde verilen değer görülemez ve değiştirilemez.docker build
sırasında farklı değer atanabilir.ENV
değişkenlerinedocker run
sırasında farklı değer atanabilir.docker build
işleminde verilen değer görülemez ve değiştirilemez.
CMD
Docker container başladığında çalıştırılacak olan Linux komutunu başlatır. Bu komut docker container’ında çalışan ilk process’dir. Container’ların görevi bir process yönetmek olduğu için container içerisinde process ölürse container kapanır.
CMD [ "command", "param1", "param2" ]CMD [ "httpd", "-v" ]
CMD
argümanı kullanım şekillerinden biri bir sonraki adımda anlatacağım ENTRYPOINT
argümanının parametrelerini tutmak için kullanılır.
ENTRYPOINT [ "httpd" ]
CMD [ "-v" ]
Bir farklı kullanım ise shell from olarak kullanmaktır. Yani CMD
argümanındann sonra yazılan her komut bin/sh -c
‘ye parametre olarak verilir ve komut çalıştırılır.
CMD httpd -v
ENTRYPOINT
CMD
argümanı gibi docker container başladığında çalıştırılacak olan Linux komutunu başlatır.
CMD
‘ye benzer şekilde iki formatı vardır.
ENTRYPOINT [ "command", "param1", "param2" ]ENTRYPOINT [ "httpd", "-v" ]
ENTRYPOINT
argümanı CMD
‘ye farkla alacağı parametreyi docker run işlemi sırasında da verebilir.
ENTRYPOINT [ "/bin/ping" ]
docker run test/myubuntu:latest 8.8.8.8
Yukarıdaki örnekte ENTRYPOINT
argümanın çalıştıracağı komuta verilmesi gereken parametre run işlemi sırasında verildi. ENTRYPOINT
dinamik bir image oluşturmaya fayda sağlar.
- Dockerfile içinde
ENTRYPOINT
veyaCMD
argümanlarından birini kullanmamız gerekmektedir. ENTRYPOINT
‘e run işlemi sırasında çalıştıracağı komuta parametre atanabilir.CMD
komutu,ENTRYPOINT
komutu için default parametreleri tutmak içinde kullanılabilir.
ENTRYPOINT [ "httpd" ]
CMD [ "-v" ]
VOLUME
Belirtilen ada sahip host ile container arasında bağlama noktası oluşturmak için kullanılır. Dockerfile içerisindeki aşağıdaki örnekte /opt/properties dizini oluşturulur ve bu alan VOLUME
olarak tanımlanır.
RUN mkdir /opt/properties
VOLUME /opt/properties
Run işlemi sırasında container içerisindeki /opt/properties dizin host üzerinde istediğimiz bir alan ile bağlanabilir. Bu bağlantıyı mount işlemi olarak da düşünebiliriz.
docker run test/myubuntu:latest -v /opt/myubuntu/properties:/opt/properties
HEALTHCHECK
Çalışan container’ın durumunu kontrol etmek için kullanılır.
HEALTHCHECK [<options>] CMD <command>
<options>
--interval=<duration> (default: 30s) #Sıklık
--timeout=<duration> (default: 30s) #Zaman Aşımı
--retries=<number> (default: 3) #Tekrar
Komutun sonucu
- 0: success — Container sağlıklı ve kullanıma hazır
- 1: unhealthy — Container düzgün çalışmıyor.
- 2: reserved — Bu çıkış kodunu kullanmamak gereklidir.
Örneğin, kurduğumuz apache2 uygulamaısnı her 30 dakikada kontrol etmek ve 3 saniye içinde cevap vermesini sağlamak için aşağıdaki gibi bir check kullanabiliriz.
HEALTHCHECK --interval=30m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
LABEL
Image’a bazı meta veriler eklemek için kullanılır.
Örneğin bir image oluşturduk ve bu image meta bilgilerine image hakkında bilgi yazmak istiyoruz.
LABEL "about"="Test için buraya bir şeyler yazmam gerekiyor..."
Image oluşturma işlemi tamamlandıktan sonra inspect ile bunu görüntüleyebiliriz.
docker image inspect
docker image inspect <imagename>
USER
Dockerfile içerisinde yapılan tüm işlemler root kullanıcısı ile yapılır. Bu argüman ile Dockerfile içerisinde istediğimiz bir adımda kullanıcı değiştirebiliriz. Örneğin apache2 adında bir kullanıcı oluşturup USER
ile bu kullanıcıya geçiş yapabiliriz. Bu işlemden sonra gelen RUN, CMD, ADD, COPY veya ENTRYPOINT komutları bu kullanıcı ile çalıştırılır.
RUN useradd apache2
USER apache2
Buraya kadar elimden geldiğince Dockerfile içinde kullanılan argümanları anlatmaya çalıştım. Şimdi kendi işlerim için geliştirdiğim projelerin Dockerfile içeriğini anlatarak daha iyi anlaşılmasını sağlayacağım.
- Python OCR Projesi
#Baz imajımız buildpack-deps kullanıyorum. buildpack-deps debian tabanlı docker için geliştirilmiş image'dır. Bu aşamadan sonra yapılacak tüm işlemler bu imaj içerisinde yapılacak.FROM buildpack-deps:buster#Dockerfile oluşturan kişi olarak bilgilerini ekliyorum.MAINTAINER Arif KIZILTEPE <kzltpsgm@gmail.com>#Python projemiz için gerekli olan <key>=<value> şeklinde tanım yapıyoruz. Burada ARG kullanmamamızın nedeni ARG build aşamasında kullanılıyordu. Fakat biz run aşamasında kullanavağız.ENV LANG C.UTF-8#RUN ile baz imajı update ediyoruz ve uygulama için çalışacak paketleri kuruyoruz. Install işleminde mutlaka -y ile otomatik evet opsiyonunu ekleyin çünkü build işlemi sırasında hata alabilirsiniz.RUN apt-get update
RUN apt-get install -y build-essential libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev libtesseract-dev tesseract-ocr liblzma-dev unzip vim ssh net-tools#Python3.6.10 dosyası wget ile indiriliyor ve /usr/src altına açlışıyor. Burada ADD komutunu da kullanailirdik dosya indirme işlemi için.RUN wget --quiet --no-cookies https://www.python.org/ftp/python/3.6.10/Python-3.6.10.tgz -O /usr/src/Python-3.6.10.tgz
RUN cd /usr/src/ && \
tar xzvf Python-3.6.10.tgz#Python3.6.10 manuel konfigürasyon ve kurulum işlemleri tamamlanır.RUN cd /usr/src/Python-3.6.10 && \
./configure --enable-optimizations && \
make altinstall
RUN pip3.6 install --user --upgrade pip#Proje dosyaları ADD argümanı ile host sunucudan container içerisindeki /opt içerisine kopyalanır ve unzip ile dosya açılır. Burada COPY argümanı da kullanabilirdik.ADD proje.zip /opt
RUN cd /opt && \
unzip /opt/proje.zip#Python projesi için gerekli paketler kurulur.RUN pip3.6 install -r /opt/proje/Kurulum/requirements.txt
RUN pip3.6 install -r /opt/proje/Kurulum/requirements.txt
RUN cd /usr/src/Python-3.6.10 && \
make altinstall#Root kullanıcısın şifresini değiştiriyoruz ve ssh aktif ediyoruz.RUN echo "root:Passw0rd" | chpasswd && \
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config && \
sed -i 's/#Port 22/Port 22/' /etc/ssh/sshd_config && \
sed -i 's/#ListenAddress 0.0.0.0/ListenAddress 0.0.0.0/' /etc/ssh/sshd_config#Hem host sunucu üzerinden hemde containerlar arasında 22 ve 8080 portları EXPOSE argümanı ile açılır.EXPOSE 8080
EXPOSE 22#Yukarıdaki tüm komutlar root kullanıcısının WORKDIR dizini olan "/" üzerinde çalışıyordu. Bu dizini projenin dizini olarak değiştiriyorum.WORKDIR /opt/proje#Python içerisine paket eklemek içinRUN /usr/src/Python-3.6.10/python -c "import nltk;nltk.download('punkt')"#Bir container'ın en önemli parçası olan çalıştırılması gereken ilk process'i çalıştırıyorum.CMD /usr/src/Python-3.6.10/python app.py > /opt/proje/logs/logfile.log 2>&1
Ben daha sonra oluşturduğum image’ı kubernetes’e bağlı node’lara yayacağım için build
işlemini master node üzerinde yapıyorum. Bu aşamada isterseniz stand-alone docker üzerinde de yapabilirsiz. Dockerfile bulunduğu klasörde build işlemini çalıştırıyoruz.
docker image build -t 32bitbilgisayar/figo .#--tag , -t Oluşturulacak image'e isim ve sürüm atamak için kullanılır. ‘name:tag’
Build işlem süresi, Dockerfile içerisindeki yapılacak işlemlere göre değişiklik gösterebilir. İşlem başarı ile tamamlandıktan sonra listeleyerek görüntüleyebiliriz.
docker image list
Bir sonraki yazımda Kubernetes yaml ile bu uygulamayı 3 worker sunucuya dağıtılmasını anlatacağım. Fakat uygulamayı test etmek açısında run
işlemi ile container oluşturuyorum.
docker run -d -p 30080:8080 -p 30022:22 32bitbilgisayar/figo#--detach , -d Container'ın arkaplanda çalışmasını sağlar.
#--publish , -p Image içerisinde expose edilen portların host sunucu üzerinden yayınlanmasını sağlar.
Evet başarı ile çalışmaya başladı. Bir sonraki adıma geçmeden önce container’ı siliyorum.
docker rm -f ae8008af72c7
# --force , -f Silme işlemini zorlamak için kullanılır.
# Silme işlemi için CONTAINER ID yada CONTAINER NAME kullanabilirsiniz.