- bölüme hoşgeldiniz! 🤘🏻 Serinin önceki bölümlerine ulaşmak için aşağıdaki bağlantıları kullanabilirsiniz:
Daha çok geliştirme üstüne duracak olsam da, Docker Compose’u test ortamı ya da farklı ihtiyaçlar için de kullanabilirsiniz. Özellikle tek bir makine üstünde birden fazla container ile çalışmayı düşünüyorsanız Docker Compose mutlaka incelemeniz gereken bir araç!
Güncelleme: Docker 1.13 ile birlikte Docker Compose ile tanımladığınız bir projeyi Swarm ile birden çok makineye dağıtabiliyorsunuz! 🎉 Seride buna da yer vermek farz oldu tabii ki!
Özetle; uygulamanızı, projenize yerleştireceğiniz docker-compose.yml
adında bir dosya aracılığıyla servisler olarak
tanımlamanıza olanak tanır ve çalıştırdığınızda ilgili container’ları, disk bölümlerini, ağları vb. her şeyi oluşturup
işiniz bittiğinde de tamamen kaldırır. O zaman kurulum ile hemen işe koyulalım! 😊
Kurulum
Kurulum için şuradaki dökümanı takip edebilirsiniz. macOS ve brew kullanıyorsanız;
brew install docker-compose
komutuyla kurulumu tamamlayabilirsiniz. Eğer makinenizde Python (pip >= 6.0) kullanıyorsanız;
pip install docker-compose
komutunu kullanabilirsiniz. Alternatif olarak aşağıdaki komutlar ile de kurulumu gerçekleştirebilirsiniz:
# curl -L https://github.com/docker/compose/releases/download/1.9.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
Kurulumu başarıyla tamamladıysanız aşağıdaki gibi sürüm numarası çıktısını görebilmelisiniz:
$ docker-compose --version
docker-compose version 1.11.2, build dfed245
Yazının bundan sonraki bölümünde şu adreste bulabileceğiniz örnek projeyi kullanarak devam edeceğim. Dilerseniz indirip benimle beraber adımları tekrarlayabilir ve denemeler yapabilirsiniz.
docker-compose.yml
Normalde projenizin ana dizinine tek bir docker-compose.yml
dosyası koymak yeterli olmakla beraber, bir dizin birden
fazla alt projeye ev sahipliği yapıyorsa bu durumda alt dizinlere de birer tane docker-compose.yml
dosyası
yerleştirebilirsiniz. Bu durumda -f
parametresiyle dosya yolunu belirtmelisiniz.
Dosyanın biçimi, kullanılabilecek anahtar kelimeler vb. için şuradaki
belgeyi inceleyebilirsiniz. Klasik bir docker-compose.yml
dosyası yaklaşık olarak aşağıdaki gibidir:
YML dosyası yazarken, bu dosya biçimine has yazım kurallarına dikkat etmelisiniz. Kapsamı iyice genişletmemek adına yazım kurallarına girmeyeceğim ancak özellikle girintilemeye özen göstermelisiniz.
İlk satır dosyanın biçimlendirmesinde kullanılan tanımlamaların (spec) sürümünü belirtiyor ve ilk satırda olma zorunluluğu var. Docker 1.13 öncesinde sürüm numarası olarak yalnızca “2” kullanılırken, artık “3” de kullanılabiliyor. Sürüm numarası, dosyanın biçiminde yapılabilecek değişikliklerden etkilenilmemesi için düşünülmüş bir özellik. Yani eski bir projenizde sürüm olarak “2” kullanıyorsanız, artık desteklenmeyen bir Docker sürümü kullanmıyorsanız, projeniz olduğu gibi çalışacaktır. Yeni bir projeye başladığınızda sürümü “3” olarak belirtmeli ve yazım kurallarını da bu sürüme uyacak şekilde uygulamalısınız.
Dosyada tanımlamanız gereken ikinci kısım: servisler. Her bir servisi, ayrı birer container gibi düşünebilirsiniz. “services” kelimesinin altında, 1 girintileme ile ayırdığınız her şey bir servistir ve çalışacak container’ın adı burada yazdığınız kelimeyle anılacaktır.
İlk servisimiz adından da anlaşılabileceği gibi API hizmeti sunan bir container’dan ibaret. Aynı servisten birden çok
kez başlatma ihtimaline karşılık Compose, yazdığınız ismi, YML dosyasının bulunduğu dizin adı ve bir rakamla
harmanlayıp container adı (ör: dockercomposedemo_api_1
) yaratır. Yalnızca tek bir container başlatacağınızdan
eminseniz servis tanımlarına
container_name: demo-api
yazarak bu isimle açılmasını sağlayabilirsiniz. Bu durumda birden fazla container başlatmaya çalışırsanız hata alırsınız.
Servisimizin adının altında bir kez daha girintileme ile yazdığımız her şey servisi tanımlayan ayarlar olarak algılanır. Şimdi gelin; sırasıyla yazdıklarımızı anlamaya çalışalım:
build: .
Kısaca imajı oluşturulacak Dockerfile dosyasının yerini (build context) belirtmek için kullanılır. Bizim projemizde Dockerfile, YML dosyasıyla aynı dizinde bulunduğu için “.” (nokta) ile bu dizini işaret ediyoruz. Dilerseniz farklı bir dizindeki Dockerfile’ı işaret edebilir, Dockerfile yerine farklı bir isim kullanıyorsanız ya da imajı yaratabilmek için ek parametrelere ihtiyaç duyuyorsanız bunları da bu kısımda belirtebilirsiniz. Detaylar için tık tık.
Swarm modunda bu parametre gözardı edilmekte ve bir alttaki image parametresi kullanılmaktadır.
image: tunix/docker-compose-demo
İmajın derlenmesi sonucunda çıkacak Docker imajının adını bu komutla belirtiyoruz. Hiç bir şey yazmazsanız yine
bulunduğunuz dizin ve verdiğiniz servis adını harmanlayarak bir isim (ör: dockercomposedemo_api
) oluşturulacaktır.
ports
YML dosya biçiminde “-” ile başlayan kısımlar üstündeki satırı dizi (array) olarak tanımlar. Yani birden fazla “-” ile başlayan satır yazarak, ports dizisine birden çok erişim noktası (port) ekleyebilirsiniz. Bizim örneğimiz için konuşursak; ev sahibi (host) makinedeki 8080. erişim noktasını, container içerisindeki 8080 numaralı erişim noktasına bağlıyoruz.
Bir erişim noktası, ev sahibi makinede yalnızca 1 kez kullanılabilir. Bu nedenle herhangi bir servisi, aynı ev sahibi makinede birden fazla kez çalıştırmayı düşünüyorsanız bu kısımda yalnızca container’a ait olan erişim noktasını belirtmelisiniz. Bu durumda, Compose container’ınıza ev sahibi makineden rastgele bir erişim noktası atar.
links
Altındaki tanımdan anlayacağınız üzere, “links” de bir dizi olup birden çok tanımlama
kabul edebilir. Bir container, diğerine erişeceğinde bunun en
kolay yolu bu container’ları birbirine bağlamaktır. Yazım şekline bakarsak “:” karakterinin solundaki servis adı,
sağındakiyse takma addır. Container içinden veritabanına ulaşacağımızda bu takma adı kullanıyor olacağız. Bu, bizi
gereksiz yere IP tanımlamaktan kurtacaktır. Proje içerisindeki
application.yml
dosyasına bakarsanız MySQL için kullandığımız bağlantı cümlesinde (connection string) takma ad olan demodb
kelimesini
kullandığımızı görebilirsiniz. Takma ad kullanmanın avantajı; servis adı değişse bile takma adın değişmemesidir.
Böylece kodumuzda değişiklik yapmak zorunda kalmıyoruz.
Swarm modunda bu parametre gözardı edilmektedir.
command
Container başlatılırken çalıştırılacak komutun ne olacağını belirler.
Serinin önceki yazılarında bahsettiğim gibi, bu tanım aslında Dockerfile içinde de yapılabiliyor. Burada tekrar yapma sebebim, Compose’un yapısından kaynaklanıyor. Tahmin edebileceğiniz gibi MySQL başlayana kadar DB container’ındaki 3306 erişim noktası açık değil. Bu durumda uygulamam ayağa kalkarken bağlanamayıp hata verecektir. Bunu engellemek için erişim noktasını sürekli yoklayıp açılınca uygulamayı başlatan wait-for-it.sh adında bir betik (script) kullanıyorum.
environment
İmaj içerisinde tanımlı olan çevresel değişkenleri, yeni bir container başlatırken bu şekilde değiştirebiliyor, tanımlayabiliyorsunuz. DB servisi için belirttiğim değişkenler, container ayağa kalktığında çalışan betiğin, benim istediğim isimle bir veritabanı yaratmasını ve yine istediğim kullanıcı adı ve şifreyle çalışmasını sağlıyor.
Yalnızca 20 satırlık bu docker-compose.yml dosyasıyla API container’ının imajının yaratılmasını, ondan da bir container ayağa kaldırılmasını ve MySQL imajından DB adında bir container yaratılmasını sağlayabiliyorsunuz. Tabii ki yapabilecekleriniz bunlarla sınırlı değil. Ekleyebileceğiniz diğer parametrelerle container kayıtlarının nereye yazılacağını, container yükleme (deployment) stratejilerinin belirlenmesini, bağlanacak dosya/dizinleri ve daha pek çok ayarı yapabilirsiniz.
Serinin ilerleyen bölümlerinde farklı proje örneklerinde Compose’u nasıl kullanabileceğinizi işliyor olacağım.
Compose ile Çalışmak
YML dosyasını inceledikten sonra sıra geldi container’larımızı çalıştırmaya! Bunun için YML dosyasının bulunduğu dizine gidin ve şu komutu çalıştırın:
docker-compose up -d
Bu komut container’ları yaratacak, servisleri başlatacak ve sonrasında da çıkış yapacak. -d
ya da uzun haliyle
--daemon
parametresi, komutun arka planda (daemon) çalışmasını sağlar. Çalıştıklarını da teyid edelim:
Dikkat ederseniz API servisimizin erişim noktası (8080) dışarıya açık ve erişilebilir durumdayken, DB servisimiz için aynı durum geçerli değil. Bunun sebebi, Compose’un YML dosyasında bu erişim noktasını açmamış olmamızdan kaynaklanıyor. Eğer veritabanınıza dışarıdan erişebilmek isterseniz dosyayı değiştirip erişim noktasını açmalı ve servisleri yeniden ayağa kaldırmalısınız.
Erişim noktası açık olmamasına rağmen, API servisi veritabanına bağlanabiliyor çünkü Compose container’ların kendi
arasında iletişimi için özel bir ağ yarattı ve yukarıda bahsettiğim gibi, DB servisinin IP adresi demodb
adıyla API
servisine ait container tarafından ulaşılabilir durumda.
Compose’un yarattığı bu ağ, servislerin izolasyonu açısından oldukça önemli. Serinin ilerleyen bölümlerinde göreceğimiz üzere, YML dosyasında yapacağınız bir takım değişikliklerle birden çok Compose YML dosyasıyla yönettiğiniz servisleri aynı ağa bağlamanız mümkün.
Artık servisimiz ayakta olduğuna göre istek atıp üstünde çalışabiliriz:
docker-compose down
komutunu vererek tüm servislerin kapatılmasını ve bunlara bağlı olarak yaratılmış ağ ile tüm container’ların silinmesini sağlayabilirsiniz:
Kolaylık olması açısından oluşturulan imajlar silinmiyor. Bu sayede bir sonraki sefer docker-compose up -
d komutunu
verdiğinizde servisleriniz çok daha hızlı ayağa kaldırılır. Eğer imajları da silmek isterseniz;
docker-compose down --rmi all
komutunu verebilirsiniz.
İpucu #1
Diyelim ki; Ubuntu bazlı bir Docker imajınız var ve her ayağa kalkışında aşağıdaki komutları çalıştırıyor:
apt-get update
apt-get upgrade
Daha farklı bir senaryo olarak, container’ınız ayağa kalkarken bir betik çalıştırarak git deponuzdaki kodun son halini de alıyor olabilir örneğin. Bu gibi senaryolarda down komutuyla her şeyi kaldırmak yerine;
docker-compose stop
komutuyla ağı ve tüm servisleri geçici olarak pasifize edebilir, ihtiyacınız olduğunda da;
docker-compose start
komutuyla yeniden başlatabilirsiniz.
İpucu #2
Klasik Docker komutlarından farklı olarak Compose da logs
, restart
, rm
, exec
vb. pek çok komutu sağlıyor.
Bunların amaçlarını ve kullanım şekillerini örneğin;
docker-compose logs --help
komutuyla görüntüleyebilirsiniz.
İpucu #3
Yukarıdaki örnekte bir API servisi, bir de DB servisi çalıştırmıştık. Aşağıdaki komutla istediğiniz servisten, istediğiniz adette başlatabilirsiniz:
docker-compose scale api=2 db=1
Tabii uygulamanızın da buna hazır olması kaydıyla! 😉
Bu bölümünün de sonuna geldik. Bir sonraki bölümde Docker Machine ve Docker Swarm’dan bahsedeceğim.
Gidişâta göre bu planda zaman zaman değişiklikler yapacak olsam da serinin tüm bölümlerinin planına şuradan ulaşabilirsiniz.