Docker ile bir yazılımın yaşam süreci : Bölüm 1

Yayın Tarihi : 03/09/2017Tahmini okuma süresi : 16 dakika

Yazılım hayatınız ile ilgili kendinize bir iyilik yapmak istiyorsanız, yapabileceğiniz en güzel şeylerden biri de Docker’ı kullanmayı öğrenmek olur sanırım. Docker’ın sağladığı avantajlar ve kolaylıklar göz önüne alındığında “daha önce neden kullanmamışım ben bunu” demeyi size garantileyecek kadar iyi bir altyapı sunuyor bizlere. Sunucu ortamının yönetimini o kadar basit bir hale getiriyor ki saniyeler içerisinde hızlıca cluster kurup, en iyi ayarlanmış sistemleri dahi utandıracak güzellikte bir yapı sunuyor bizlere.

Peki; nedir bu Docker?

Docker aslında zaten Linux’larda yıllardır var olan LXC yani Linux Containers‘ı yönetmek için GoLang ile yazılmış bir ara yazılım aslında. Fakat bu işi Linux’un kendi yerel araçlarından çok çok daha hızlı ve kolay bir biçimde yapabildiği için özellikle yazılım geliştirenler tarafından çok yüksek oranda tercih ediliyor. Tercih sebeplerinden bir başkası ise; “kodum bende çalışıyordu ama sunucuda çalışmıyor” sıkıntısını ortadan kaldırmak. Çünkü kendi makinemizde bir container nasıl çalışıyorsa başka yerde çalıştırıldığında da istisnasız aynı biçimde çalışır. Container’lar ile sunucu içerisindeki yazılımlar sunucu ortamından izole edilip sanki ayrı bir makinenin içinde tek başlarına çalışıyor gibi iş yaparlar. Bu izolasyonla beraber Docker bize izole ağ desteği gibi konularda da yardımcı olup yazılımlarımızın birbiri ile belirlediğimiz sınırlar içerisinde iletişime geçmesine de yardımcı oluyor.

Konu daha fazla derinleşmeden Docker’ın demirbaşları olan image’ler ve container’lara da bir açıklama getirelim. Image’ler aslında container’ları yaratmamız için gereken tam teşekküllü şablonlar olarak da görülebilirler. Yani image bir mimari çizim ise container evdir. Tabi bu örneği baz alarak evin yapılmasının uzun süren bir süreç olduğunu varsayıp container’ların oluşturulmasının da uzun süreceğini düşünebilirsiniz fakat öyle değil. Image’ler build edilirken gerekli tüm işlemler ve yüklemeler dağıtılabilirliği korumak adına önden yapıldığı (ya da yapılması tavsiye edildiği) için bir container’ı yaratmak çok çok kısa sürecektir. Tabi image’i hazırlayan kişi bu işlemlerin çalışma sırasında yapılması için de bir config hazırlayabilir fakat böyle bir durumda image’den container’ı ayağa kaldırmak vakit harcayan bir süreç haline gelir ki bu da asla istenmeyen bir durum olan verilen servisin durmasına sebep olur. Bu yüzden image’lerinizi her zaman çalıştır ve unut mottosu ile hazırlamanızı (bir şahsi görüş olarak) tavsiye ederim.

Güzel; sanallaştırmaya karşı avantajları neler?

Sanallaştırılmış bir sunucu kurduğumuzda host makinede hypervisor üzerinde çalışan tam bir işletim sistemi için de kaynak ayırmamız gerekiyor ki; zaman zaman bu konuk işletim sistemi sadece kendi başına çok fazla kaynak bile tüketebiliyor. Üstelik aynı sunuculardan sayıca çok fazla ihtiyacımız olduğunda her biri için bir de sanal işletim sistemi kurmak ve kaynağımızın bir kısmını bu işletim sistemleri ile gereksiz yere paylaşmak açıkçası donanımı tam verimli kullanmak açısından (hardware utilization) pek de verimli değil. Ayrıca çok fazla sayıdaki sunucuyu bir düzende çalıştırmak için bir de ayrı bir load balancer kurulması ve herhangi bir terslik durumuna karşı monitörlenmesi de gerekmekte. Bunun yanında suncularımız için güvenlik ayarları (gerek ağ, gerek sistem), gerekli paket kurulumları gibi bir çok şeyin yapılması ve dönem dönem bakımlarının yapılması ya da güncellenmesi de cabası.

İşler Docker’a geldiğinde çok daha fazla basitleşiyor. Öncelikle işletim sistemi sadece bire iniyor ki o da zaten host makinenin kendi işletim sistemi. Bu sistemin üzerinde hypervisor yerine Docker Engine (Deamon) çalışıyor ve container yönetimini bizim için çok kolaylaştırıyor. Bunun yanında Docker bizim için cluster servisi olan Docker Swarm’ı, izole networkleri ve bu networklerde minik bir DNS hizmeti ile automatic service discovery işini çözüp, Swarm modu için de yük dengeleme (load balancing) işini de kendi hallediyor. Ayrıca konuk bir işletim sistemi için kaynak harcamadığımız için aynı fiziksel sunucu üzerinde sanallaştırmadan kat kat daha fazla container çalıştırabiliyoruz. Bir şahsi örnek vermem gerekirse MacBook Pro üzerinde sırf makineyi tıkamak için deneme yapıp için 100 tane web server çalıştırdığımda makinede sanki hiçbir şey çalışmıyormuşcasına ve neredeyse hiç kaynak harcamadan 100 tane sunucuyu (apache + php7 + tonla ek apache modülü ile) swarm modunda mükemmel çalıştırmıştı.

Yapı olarak sanallaştırılmış ve dockerlaştırılmış bir sunucudaki kaynak tüketimini göstermek istersek katmanları şöyle bir grafik ile sanırım kaba taslak gösterebiliriz;

BARE METAL MACHINE HOST OS HYPERVISOR GUEST OS BINS/ LIBS APP A GUEST OS BINS/ LIBS APP A GUEST OS BINS/ LIBS APP B Virtualized Server BARE METAL MACHINE HOST OS DOCKER ENGINE BINS/LIBS APP A APP A APP A BINS/LIBS APP B APP B Dockerized Server

Görüleceği üzere konuk işletim sistemleri sunucuda olması gerekenden çok daha fazla kaynak tüketimine sebep oluyor. Ayrıca sanallaştırmadaki izolasyon çok katı sınırlarla çizildiği için içerideki servis kendine ayrılan sanal makinenin tamamını kullanamazsa o kaynak her halükarda host makineden ayrılmış oluyor. Daha da kötüsü, içerideki servis tüm kaynağını tüketirse konuk sisteme ayrılan kaynak kendi kendini disk dışında genişletemiyor ki genelde performans için diskleri sabit bir kalıpla ayarlarız. İşin sanallaştırma tarafındaki konuk sistemlere ayrılan gereksiz disk alanından bahsetmiyorum bile, çünkü arada muazzam bir fark var. Sanallaştırma her ne kadar fiziksel sunucu içinde benzer yapılar için homojen bir dağılım gibi görünse de, içerideki hava kabarcıkları diye tabir edebileceğimiz kullanılmayan kaynak host makineye geri gelmediğinden, sanallaştırılmış sistemde servislerin ne kadar yük kaldırabileceğini ve karşılığında ne kadar kaynak tüketeceğini iyi test edip, kaynakları ona göre planlayarak host makineden yararlanmak gerekiyor. Ya da bu işi otomatize edecek monitör araçlarını kullanarak zaten ekstra kaynak tüketen konuk işletim sistemine bir de bu monitörleme çözümlerinin de yükünü bindirmek gibi milyon tane ince detay işin içine giriyor. Özellikle bakım gibi durumlarda bazen dakikalarla ölçülen sanal sunucuların aktive olması ya da yeniden başlaması Docker ile milisaniyelere kadar düşebiliyorken hızlıca tüm sistemimize güncelleme yapıp herşeyi hayata alabiliyoruz. Bu yüzden sınırlı bütçe ile çok fazla sunucu kurmak istiyorsak aslında Docker’ın bize çok büyük yardımı dokunuyor.

Tamam herşey güzel de nereden başlamalı?

Öncelikle şunu bilmeliyiz ki Docker native olarak sadece Linux üzerinde çalışıyor. Windows Server üzerinde çalışan hali de sadece windows container’larını çalıştırabiliyor fakat yazılım geliştirmek için olan Docker for Windows ve Docker for Mac gibi iki versiyonu var ki bunlar aslında host makine üzerinde küçücük (25 MB) bir Linux çalıştırıp (yanlış hatırlamıyorsam windows üzerinde hyper-v ile MacOS üzerinde xhyv ile) local Docker client’ını bu Linux’daki Docker Engine’e bağlayarak sanki kendi host işletim sisteminde kuruluymuş gibi çalıştırabiliyor. Docker’ın bu kısmı neredeyse tam otomatik bir yapı gibi aslında. Sadece yazılımı yükleyip ilk kurulum ekranında Docker’a ilk ayarları için yetki vermek gerekiyor o kadar. İşin Windows tarafı ile ilgili net bir şey söyleyemesem de MacOS’larda Docker’ı Applications klasörümüze sürükleyip açtığımızda bizden ilk ayar için yönetici yetkisi isteyip Docker’ı çok kısa süre içerisinde yerel terminalimizden kullanılabilir hale getiriyor. Her platform için kurulum yönergelerini zaten Docker’ın kurulum yönergeleri sayfasından bulabilirsiniz. Ben burada bir kurulum tarifi vermek istemiyorum. Özellikle de Linux tarafında bazen yeni versiyonlarla ek adımlar gelebildiği gibi her dağıtım için de farklı yönergeler olabiliyor. Bu yüzden en iyisi orijinal Docker dökümanlarından bakıp Linux sunucunuza öyle kurmak. Sonrasında kurulumun sorunsuz olduğunu görebilmek adına yapmamız gereken tek şey terminalimizi açıp docker komutunu vermek. Eğer basit kullanım klavuzu çıktısını karşımızda görüyorsak Docker sistemimizde düzgün olarak başlamış ve local terminalimize bağlanmış demektir. Bundan sonra istersek docker version komutunu vererek client’ımız ve Docker Engine’imiz ile ilgili bilgi alabiliriz.

Kurulumlar sorunsuz şimdi sırada ne var?

Şimdi bir ritüel haline gelmiş olan hello-world image’inden bir container oluşturup çalıştırarak nelerin döndüğüne bir göz atalım. Terminalimizden önce aşağıdaki komutu verelim ve neler olduğunu inceleyelim;

docker run -it hello-world

Bu komuttan sonra ekranda muhtemelen aşağıdakine benzer bir çıktı göreceğiz;

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b04784fba78d: Pull complete
Digest: sha256:f3b3b28a45160805bb16542c9531888519430e9e6d6ffc09d72261b0d26ff74f
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://cloud.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/engine/userguide/

Docker varsayılan yöntem olarak oluşturmaya çalıştığımız container’ın image’lerini local repository’den bulup yaratmaya çalışır fakat olur da aradığı image local repository’de bulunmuyorsa image’i Docker Hub’dan çekerek container’ı oluşturmaya çalışır. Eğer aradığımız image Docker Hub’da da bulunmuyorsa doğal olarak hata alırız. Üstteki komutla Docker ilk önce kendi local repository’sine bakarak hello-world image’ini aradı, bulamadığında da Docker Hub’dan bu image’i indirerek container’ı oluşturup çalıştırdı. Docker’ın run komutuna gönderdiğimiz ek argümanlardan -i anahtarı ile interaktif modu açarak standard input’u bize bağladı. -t anahtarı ile de yalancı bir terminal yaratıp bunu da bizim terminalimizle ilişkilendirdi ve içeride çalışan yazılımın ürettiği çıktıyı kendi terminalimize aktardı.

Hep container oluşturmaktan bahsediyoruz ama bu container’lar nerede diye soracak olursak tüm oluşturulan container’ları görmek için docker ps -a komutunu verebiliriz. Ya da yok ben sadece o anda çalışan container’ları görelim dersek de docker ps yazarak sadece o anda çalışan container’ların listesini detaylıca alabiliriz. Örneğin;

CONTAINER ID   IMAGE         COMMAND    CREATED          STATUS                      PORTS         NAMES
5fd83e5997a1   hello-world   "/hello"   15 seconds ago   Exited (0) 13 seconds ago                 happy_montalcini

Bu çıktılara bakarak CONTAINER ID ve NAMES kolonları altındaki verilerden container’lar ile işlemler yapabiliriz. Örneğin yaratılan bir container’ı silmek istersek aşağıdaki komutu verebiliriz;

docker rm 5fd83e5997a1
# ya da
docker rm happy_montalcini

Üstteki komutla 5fd83e5997a1 id’sine sahip olan ya da happy_montalcini isimli container’ı kaldırabiliriz. Bu isimlendirmeleri, biz özellikle yarattığımız container’a bir isim vermediğimiz sürece (ki ilerideki bölümlerde özellikle isim vermemiz gerekecek) Docker kendi isim kütüphanesinden rastgele iki ismi kombine ederek uydurur.

Container’lar ile ilgili diğer işlemlere geçmeden önce şu soruyu cevaplamamız gerekiyor; aynı run komutunu tekrar çalıştırırsak ne olur? Tekrar çalıştırdığımızda yukarıdaki işlemleri tekrar yapar ve yeni bir container daha oluşturur. Yani hello-world image’inden bir container daha oluşturarak aynı özelliğe sahip iki container yaratmış olur. Bu yüzden bir container’ı tekrar çalıştırmak istiyorsak bir dahaki sefere run komutu ile değil Docker’ın start komutu ile aynı container’ı tekrar çalıştırabiliriz. Örneğin;

docker start 5fd83e5997a1

Bu kadar bahsediyoruz ama image’ler ne alemde? Image’ler bizim local repository’mizde biz silmediğimiz sürece duruyor olacaklar. Local repository’mizdeki image’leri görmek için de docker images komutunu vererek indirilmiş image’leri görebiliriz.

REPOSITORY            TAG                 IMAGE ID            CREATED             SIZE
hello-world           latest              1815c82652c0        2 months ago        1.84kB

Ayrıca daha önce bahsettiğimiz Docker Hub üzerinden bir image aramak istersek Docker’ın search alt komut setini kullanabilir. Örneğin MySQL image’ini aramak istersek;

docker search mysql

komutunu verebilir ve aradığımız kelime ile ilgili orijinal repoları ya da kullanıcıların yarattığı repoları arayabiliriz. Olur da herhangi bir image’i container oluşturmadan sadece indirmek istersek de pull alt komut seti işimizi görecektir;

docker pull mysql

Bu komuttan sonta Docker bizim için Hub’dan orijinal MySQL image’ini indirecek ve kendi local repositorymize ekleyecektir. Olurda özellikle bir versiyona ait bir image’i indirmek istersek de pull komutundan sonraki image isminin sonuna iki nokta koyup versiyon bilgisini eklememiz yeterli olacaktır. Örneğin;

docker pull mysql:5.9.1

Eğer kullanmadığımız bir image’i silmek istersek ve bu image’den türetilmiş herhangi bir container bulunmuyorsa şu komutla istediğimiz image’i de silebiliriz;

docker rmi hello-world

Bu komuttan sonra Docker hello-world ismine sahip image’i silecektir. Olur da aynı image’den birden fazla versiyonlarda varsa image adının sonuna iki noktadan sonra versiyon bilgisini (TAG başlığı altındaki bilgi) ekleyip sadece o versiyona ait image’i silebiliriz. Bir örnekle pekiştirmek istersek;

docker rmi hello-world:3

Bu komutla da Docker hello-world image’inin 3 numaralı versiyonunu (eğer repomuzda olsaydı) silecektir. Eğer olurda image’in yaratılması sırasında bir sorun olursa ya da image yaratılırken herhangi bir şekilde isimlendirilmemişse isim verisine erişemeyeceğimiz için local repository’mizdeki image’in id’si ile de silebiliriz;

docker rmi 1815c82652c0

Docker bu komutla 1815c82652c0 id’ye ait image’i ismi ya da versiyonu atanmamış dahi olsa silecektir.

Şimdiye kadar kullanılan komutlar aslında Docker’da tanımlanmış kısayol komutlardı. Komutların asıl uzun hallerine bir de bakacak olursak aslında Docker’ın bizim için komutlarda bir konvansiyon yarattığını ve aslında hepsini kullanırken belli bir şablon üzerinden gittiğini ve de aslında birini kullanabiliyorsak hepsini kullanabileceğimizi görebiliriz.

Mesela daha önce kullandığımız container’larımızı ve image’lerimizi yöneten komutların uzun hallerine de bir göz atalım. Mesela container silen şu kısa komut;

docker rm hello-world

yerine şu uzun komutu da verebilirdik;

docker container rm hello-world

ya da tüm container’ların listesini aldığımız şu komuta bakacak olursak;

docker ps -a

aynı komutu aslında uzun hali ile şöyle de yazabilirdik;

docker container ls -a

ya da bir başka komut olan image’leri sildiğimiz;

docker rmi hello-world

komutu yerine uzun uzun şu komutu da yazabilirdik;

docker image rm hello-world

Dikkat edeceğiniz üzere komutların uzun hallerinde Docker bizim için hep bir şablon ile devam ediyor. Bir komuttan örnekle gidecek olursak docker ile ana komuta ardından image diyerek image’lerin alt başlığına ve rm diyerek o image’i silmek istediğimize ve de hello-world diyerek hangi image olduğunu belirtip işimizi halledebiliyoruz. Docker bu yapıyı her kısım için uyguladığı için oldukça anlaşılır ve tahmin edilebilir bir yapısı var. Ayrıca her yapılan komut ve alt başlık için de detaylı kullanım klavuzu da barındırdığı için Docker’ı bir kere kullanmaya başladığımızda kolay kolay pek tıkanmıyoruz aslında. Örneğin image sildiğimiz komut için bir yardım almak istersek;

docker image rm --help

komutunu verip image silmek ile ilgili alt detaylarla ilgili bilgi alabiliriz. Ya da komple image alt başlığı ile ilgili detaylı bilgi almak istersek de şu komutu kullanabiliriz;

docker image --help

Bu şablonu takip ederek Docker ile ilgili herhangi bir işlem için detaylı kullanım bilgisi alabilmek mümkün olduğu için komut çok spesifik bir yapıya sahip olmadığı sürece alacağımız yardımlar her işimizi görecektir. Tek yapmamız gereken Docker’ın nasıl bir düzeneğe ve çalışma mantığına sahip olduğunu kavrayabilmek ve yardıma ihtiyacımız olduğunda komutlara --help anahtarını eklemek.

Haydi, daha fazla detaya inelim!

Daha önce yukarıda bahsettiğimiz çalıştır ve unut mottosunu biraz desteklemek üzere container’ları çalıştırırken kullandığımız komutlara biraz daha değinelim. Daha önce kullandığımız container çalıştıran komutlarda henüz kullanmadığımız bir anahtar olan -d ile çalıştırdığımız container’ın arka planda çalışmasını ve ön tarafa herhangi bir veri göndermemesini, daha da önemlisi terminalimizin çalışan container’a bağlanmamasını sağlayabiliriz. Şöyle ki;

docker run -d -p 6373:6379 redis

Yukarıdaki komut ile arka planda çalışan bir Redis sunucusuna sahip olabiliriz mesela. Ayrıca bu komuttan hareketle yeni bir anahtar olan -p ve o anahtara veri olarak paslanan 6373:6379 verisine de bir göz atalım. Öncelikle komutumuz bu sunucunun -d anahtarı ile arkada çalışacağını söyledi ve -p anahtarı ile de dışarıdan (iki noktanın sol tarafı) 6373 nolu porta yapılacak istekleri container’ın içerisine (iki noktanın sağ tarafı) 6379 nolu porta iletmesini söyledik. Bu iki noktalı notasyonu ayrıca container’ların volume ayarı gibi herhangi bir klasörü container’ın içerisindeki bir klasöre bağladığımız komutlarda (ilerideki örneklerde karşımıza çıkacak) da kullanıyoruz.

Komutlarda biraz daha fazla detay görebilmek adına biraz daha detaylı bir komut vererek bu sefer de sunucumuzda bir MySQL container’ı çalıştıralım;

docker run -d -p 3306:3306 \
--name veritabani \
--restart always \
-e MYSQL_ROOT_PASSWORD='root' \
mysql

“E yuh artık birden böyle örneğe geçilir mi?” diyenler olacaktır telaş yapmaya gerek yok çünkü satır satır, kelime kelime herşeyi açıklayacağız. Öncelikle ters slash’lar ile ekrana sığabilmesi için birkaç satıra yayılmış komut aslında tek bir satırda da yazılabilir. Zaten ilk satırdan bildiğimiz -d anahtarı ile detach modda çalışmasını istediğimiz container’ımızdan içeride ve dışarıda 3306 portunu dinlemesini istedik. İkinci satırdaki --name anahtarı ile anlaşılacağı üzere çalışacak container’ımıza veritabanı ismini verdik ve istersek bundan sonra bu isimle container’ımızı yönetebiliriz. Üçüncü satırdaki --restart anahtarına pasladığımız always değeri ile herhangi bir sorun olup container’ımızın durması durumunda tekrardan başlatılmasını istediğimizi Docker Engine’e iletmiş olduk. Dördüncü satırda -e anahtarı ile container’ın çalıştığı izole ortam için ortam değişkeni (environment variables) tanımlamış olduk. Eğer olur da birden fazla ortam değişkeni tanımlamamız gerekirse de yine -e anahtarını istediğimiz sayıda kullanarak gerektiği kadar ortam değişkeni tanımlayabiliriz. Beşinci ve son satırda kullanacağımız image’in adını belirttik sadece. Eğer image isminden sonra başka bir executable yolu yazarsak ve image’imiz buna müsaitse o image içinde varsayılan komut yerine bizim istediğimiz komutu çalıştırır. Her ne kadar mysql image’i bunun için uygun değilse de çoğunlukla farklı entrypoint seçmek için kullanılır. Yani eğer şöyle bir komut verseydik;

docker run -it busybox /bin/sh

Docker bizim için busybox image’i içinde shell’i bir container olarak başlatacaktır. Eğer olur da bir şekilde çalışan bir container’ın içinde varsayılanın dışında bir komut başlatmak istersek ya da bir şekilde container içindeki shell’e ulaşmamız gerekirse Docker’ın exec komutu işinizi fazlası ile görecektir. Bir örnek vermek istersek de;

docker exec -it webserver /bin/sh

gibi bir komut ile webserver ismini verdiğimiz container’ın içindeki /bin klasöründe bulunan shell’i çalıştırıp zaten bildiğimiz -i ve -t anahtarları ile terminalimize bağlayacaktır. Zaten shell’imizdeki kullanıcı adının ve makineadının değişiminden container’ın içine girdiğimizi de görebilirsiniz.

Tam bu noktada bir uyarı yapmak gerekirse, container’ların içine erişmek için sakın ama sakın SSH yapmaya çalışmak ya da image’larınızı build ederken içlerine zorla SSH server gömmeye çalışmak gibi bir hataya düşmeyin. Hatta bu tip bir girişimde bulunmanız durumunda alacağınız muhtemel tepki yüksek oranda “bu nasıl docker kullanmak?” ya da yabancıların tabiri ile “WTF?” gibi bir şey olacaktır. Container’lar yapısal olarak içerisinde sadece bir işlemin çalışması için ayarlanmalıdır. İstenirse birden fazla sunucu da tek container’ın içinde de çalışabilir fakat Docker için önerilen yapı her türlü sunucuların birbirinden ayrılıp farklı container’lar içerisinde çalıştırılmak üzere ayarlanmasıdır. Böylece bir yerde tıkanma yaratan servisin sayısı Swarm gibi yapılarda arttırılıp darboğaz gibi bir durumun oluşması rahatlıkla engellenebilir.

Bu erişimin dışında tamamen izole edilen bir container’ın içerisine ya da container içerisinden bir dosyayı host makine’ye aktarmak isterseniz de Docker’ın cp alt komut setini kullanabilirsiniz. Birer örnekle iki yöne olan aktarımı da açıklamak istersek;

# Host makineden container içine kopyalamak
docker cp ~/config.cfg webserver:/home/config.cfg

# ya da container içinden host makineye kopyalamak
docker cp webserver:/home/config.cfg ~/config.cfg 

Üstteki satırlarla ilk komutta home klasörümüz içindeki config.cfg dosyasını webserver isimli container’ın içinde /home/config.cfg yoluna kopyalamış olduk. Hemen altında da aynı komutu ters yöne çalıştırmış olduk.

Eğer olur da container’ımız durduğunda continer’ın silinmesini istiyorsak, yani geçici olarak o container’ı yaratıyorsak da run komutuna --rm anahtarını ekleyerek container’ın durduğu zaman silinmesini sağlayabiliriz. Örneğin;

docker run -it --rm busybox /bin/ping -c 5 8.8.8.8

Bu komuttan sonra docker ps ya da docker ps -a komutunu verip container listesini aldığımızda ping atan busybox image’ından türeyen bir container’ın ortalıkta olmadığını göreceksiniz.

Bir başka sık kullanılan anahtar da herhangi bir klasörü veya dosyayı container içinde bir klasöre ya da dosyaya bağlayan -v anahtarı. Bunun için verilebilecek en güzel örnek bir web sunucusu olabilir. Mesela sunucumuzda bir nginx çalıştırıp üzerinden bir klasördeki web sayfasını yayınlamak istersek suna benzer bi komut verebiliriz;

docker run -d -p 80:80 -v ~/MyWebSite:/usr/share/nginx/html nginx

Bu komutla diğerlerinden farklı olarak -v anahtarı ile local makine üzerindeki home klasörümüz aldındaki MyWebSite klasörünü (yine iki noktanın sol tarafı) container içindeki /usr/share/nginx/html klasör yoluna (yine iki noktanın sağ tarafı) bağlamış olduk. Nginx’in varsayılan web sitesini yayınladığı klasör olan bu klasör ile herhangi bir web browser ile localhost’u açtığımızda sitemizi görebiliriz. Sadece klasör bağlamak için kullanılmayan bu anahtar ile dosya da bağlayabildiğimiz için istersek nginx’i config’ini değiştirerek birden fazla web sitesi için çalışmasını da sağlayabiliriz. Örneğin;

docker run -d \
-p 80:80 \
-p 443:443 \
-v ~/MyWebSite:/usr/share/nginx/html \
-v ~/MyConfig/nginx.conf:/etc/nginx/nginx.conf \
nginx

Komutu ile de nginx’in varsayılan config’inin üzerine yazarak istediğimiz ayarları nginx’e yapabiliriz. Burada önceki komutlarla karşılaştırıldığında birden fazla port ve birden fazla klasör veya dosyayı bağlayabildik.

Bir diğer en çok kullanılan komut olan logs ile de bir container’ın ekrana bastığı çıktıları takip edebiliriz. Özellikle arkaplanda çalışmasını istediğimiz bir container’ın çalışma sırasında herhangi bir hata vermesi sonucu neden çalışmadığını görmenin en kolay yollardan birisi de bu. Mesela hata vermesi için bir container başlatalım;

docker run -d -p 3306:3306 --name veritabani mysql

Bu komuttan sonra MySQL container’ı root kullanıcısı için ortam değişkenlerinden parolayı okumaya çalışacaktır fakat okuyamadığı için hata verip duracaktır. Container arka planda çalıştığı için de verdiği hatayı göremeyeceğimizden logs komutunu kullanarak durma sebebini görebiliriz;

docker logs veritabani

Eğer olur da çalışan ve devamlı olarak döngüye girmiş bir container’da devamlı olarak nelerin loglandığını görmek istersek de logs komutuna -f (follow) anahtarını ekleyerek çıktıları takip etmesini ve devamlı ekrana dökmesini sağlayabiliriz.

Sanırım şimdilik bu kadar bilgi Docker’a bir giriş yapmak için yeterli olacaktır. Makalenin uzunluğu ya da komutların uzunluğu gözünüzü asla korkutmasın çünkü birkaç defa kullandıktan sonra ve de özellikle development sırasında kullanmaya başladıktan sonra çok hızlı bir biçimde kavrayıp sanki günlük bir yazılımı kullanıyormuşcasına seri ve verimli kullanmaya başlayacaksınız.

Bundan sonraki bölümlerde kendi image’lerimizi yaratıp image’lerimizi ve container’larımızı yönetmekle ilgili daha detaylı komutlardan, günlük development için container’ları nasıl verimli kullanacağımızdan, çok makineli bir cluster kurup ayarlayıp yönetmekten ve de Docker için hazırlanmış üçüncü parti arayüz yazılımlarından bahsedeceğiz.

Bir sonraki Docker makalemizde görüşmek üzere…