Docker Compose Nedir? docker-compose.yml Dosyası ve Temel Key'ler
Docker ile çalışmaya başladığımda container'ları tek tek docker run komutlarıyla oluşturmak ve çalıştırmak bir noktadan sonra oldukça zahmetli hale gelebiliyor. Özellikle bir uygulamanın birden fazla container'a ihtiyacı varsa, her container için kullandığım komutları tek tek hatırlamak ve tekrar çalıştırmak yerine bunları bir dosyada tanımlamak çok daha pratik oluyor.
Docker Compose tam olarak bu noktada devreye giriyor. Docker Compose ile container oluşturma, image kullanma veya build etme, port mapping, volume mapping, environment variable tanımlama ve container'lar arasındaki bağlantıları tek bir yapı üzerinden yönetebiliyorum.
Kısacası daha önce terminal üzerinden yazdığım birçok Docker komutunu bir YAML dosyasında tanımlayıp, daha sonra tek bir komutla uygulayabiliyorum.
Docker Compose Ne İşe Yarar?
Docker Compose'u, Docker üzerinde çalıştıracağım uygulamanın bütün yapılandırmasını tek bir yerde toplamak için kullanıyorum.
Örneğin bir uygulamamın;
- Bir frontend container'ına,
- Bir backend container'ına,
- Bir MongoDB veya MySQL container'ına
ihtiyacı varsa, bunların her birini ayrı ayrı docker run komutlarıyla çalıştırmak yerine Docker Compose dosyasında tanımlayabiliyorum.
Daha sonra aşağıdaki komutla bütün yapıyı çalıştırmak mümkün:
docker compose up -d
Böylece daha önce yazdığım uzun komut zincirlerini tekrar tekrar yazmak zorunda kalmıyorum.
docker-compose.yml Dosyası
Docker Compose kullanmak için öncelikle genellikle docker-compose.yml isimli bir YAML dosyası oluşturuyorum.
Yeni Docker Compose sürümlerinde dosya adı yine bu şekilde kullanılabilmekle birlikte, Compose Specification nedeniyle artık version alanını kullanmak çoğu durumda zorunlu değildir.
Dosyanın temel yapısı key : value şeklindedir. YAML kullandığımız için girintilere de dikkat etmek gerekiyor.
Eğer bir key'in değeri liste (array) şeklindeyse, alt satırlarda - işareti kullanarak değerleri belirtiyoruz.
Örneğin:
ports: - "3000:3000" - "8080:8080"
Buradaki ports bir liste olduğu için her port mapping işlemini ayrı bir satırda - işaretiyle belirtiyoruz.
Docker Compose Temel Yapısı
Basit bir Docker Compose dosyasının yapısı genel olarak şu şekilde düşünülebilir:
services: todo-app: build: . container_name: todo-app ports: - "3000:3000" mongodb: image: mongo container_name: mongodb ports: - "27017:27017"
Burada iki farklı servis tanımladık: todo-app ve mongodb. Docker Compose bu servisleri ayrı container'lar olarak oluşturup yönetebilir.
Bazı Önemli Docker Compose Key'leri
version
Docker Compose'un geçmiş sürümlerinde kullanılan version alanı, Compose dosyasının hangi format/sürüm şemasına göre değerlendirileceğini belirtmek için kullanılıyordu.
Docker Compose'un güncel kullanımında ise Compose Specification ön plana çıktığı için version alanı çoğu durumda gerekli değildir. Eski kaynaklarda örneğin aşağıdaki gibi bir kullanım görebiliriz:
version: "3.8"
Dolayısıyla internette farklı Docker Compose örneklerinde farklı version değerleriyle karşılaşmam normal. Özellikle eski Docker Compose içeriklerini incelerken bu alanın neden kullanıldığını bilmek faydalı oluyor.
services
Docker Compose dosyasının en önemli bölümlerinden biri services alanıdır.
Burada çalıştırmak istediğimiz uygulamaları, yani servislerimizi tanımlıyoruz. Her servis genellikle ayrı bir container olarak çalıştırılır.
Örneğin:
services: todo-app: build: . mongodb: image: mongo
Burada todo-app ve mongodb olmak üzere iki servis tanımladık.
Docker Compose servisler için kendi network yapısını oluşturduğu için servisler birbirleriyle servis adı üzerinden iletişim kurabilir.
Örneğin todo-app içerisinden MongoDB'ye bağlanırken genellikle localhost yerine servis adını kullanırım:
mongodb:27017
Burada mongodb, Compose dosyasında tanımladığımız servis adıdır. Bu nedenle servis adlarını belirlerken uygulamanın diğer servisleri tarafından nasıl kullanılacağını da düşünmek gerekiyor.
Not: MongoDB'nin varsayılan portu 27017'dir. Bu nedenle 21017 yerine 27017 kullanılması gerekir.
build
build key'ini, kendi Docker image'ımızı bir Dockerfile üzerinden oluşturmak istediğimizde kullanıyoruz.
Örneğin Dockerfile dosyamız ile docker-compose.yml aynı klasördeyse:
services: todo-app: build: .
Buradaki ., Dockerfile'ın bulunduğu mevcut klasörü build context olarak kullanacağımızı ifade eder.
Böylece Compose'u çalıştırdığımda Dockerfile kullanılarak image build edilebilir.
Örneğin:
docker compose up -d --build
Bu komutla gerekli durumda image'ın yeniden build edilmesini de sağlayabiliyorum.
image
Eğer elimizde zaten bulunan veya Docker Registry üzerinden çekilebilecek hazır bir image'ı kullanmak istiyorsak image key'ini kullanıyoruz.
Örneğin MongoDB için:
services: mongodb: image: mongo
Belirli bir image tag'i kullanmak istersem onu da belirtebilirim:
services: redis: image: redis:7
Burada Docker, redis image'ının 7 tag'ini kullanacaktır.
Kısaca build kendi Dockerfile'ımdan image oluşturmak için, image ise hazır bir image kullanmak için tercih ediliyor.
ports
ports key'i ile container'ın port mapping ayarlarını yapabiliyoruz.
Daha önce Docker'da port mapping için kullandığımız:
docker run -p 3000:3000 ...
işlemini Compose içerisinde şu şekilde tanımlayabiliriz:
services: todo-app: ports: - "3000:3000"
Burada ilk port Docker Host üzerinde erişilecek portu, ikinci port ise container içerisindeki portu ifade eder.
Birden fazla port mapping yapmamız gerekiyorsa her birini ayrı satırda tanımlayabiliriz:
ports: - "3000:3000" - "8080:8080"
container_name
container_name key'i ile oluşturulacak container'a kendimiz bir isim verebiliriz.
Örneğin:
services: mongodb: image: mongo container_name: my-mongodb
Bu durumda oluşturulan container'ın adı my-mongodb olacaktır.
Böylece container'ı yönetirken bu ismi kullanabilirim:
docker stop my-mongodb docker start my-mongodb docker logs my-mongodb
Burada küçük bir not olarak, Compose projelerinde container_name kullanmak her zaman gerekli değildir. Compose zaten container'ları kendisi isimlendirir. Ancak özellikle container adına doğrudan erişmek istediğim durumlarda kullanışlı olabiliyor.
volumes
volumes key'i ile container'ın kalıcı veri ihtiyacını ve volume mapping işlemlerini yönetebiliriz.
Örneğin daha önce Docker'da:
docker run -v /opt/data:/data/db mongo
şeklinde yaptığımız volume mapping işlemini Compose içerisinde şu şekilde tanımlayabiliriz:
services: mongodb: image: mongo volumes: - /opt/data:/data/db
Burada /opt/data Docker Host üzerindeki klasör, /data/db ise container içerisindeki klasördür.
Birden fazla volume mapping yapacaksak yine liste yapısını kullanıyoruz:
volumes: - /opt/data:/data/db - /opt/logs:/var/log/app
Named Volume Kullanımı
Docker Compose içerisinde host üzerindeki belirli bir klasörü doğrudan göstermek yerine named volume da kullanabiliriz.
Bu durumda volume'ü Compose dosyasının en dış seviyesinde tanımlıyoruz:
services: mongodb: image: mongo volumes: - mongo-data:/data/db volumes: mongo-data:
Burada mongo-data Docker tarafından yönetilen named volume'dür. Container silinse bile volume ayrıca silinmediği sürece veriler korunabilir.
Yani burada iki farklı kullanım görebiliriz:
| Yöntem | Örnek | Kullanım |
|---|---|---|
| Bind Mount | /opt/data:/data/db |
Host üzerindeki belirli bir klasörü container'a bağlamak |
| Named Volume | mongo-data:/data/db |
Docker tarafından yönetilen kalıcı volume kullanmak |
Docker Compose ile Servisleri Birlikte Çalıştırmak
Docker Compose'un benim için en önemli avantajlarından biri, birden fazla container'ın birlikte çalışmasını kolaylaştırması.
Örneğin bir backend uygulamam ve MongoDB database'im olduğunu düşünelim:
services: todo-app: build: . ports: - "3000:3000" mongodb: image: mongo volumes: - mongo-data:/data/db volumes: mongo-data:
Bu yapıda todo-app kendi Dockerfile'ından build edilirken, mongodb hazır MongoDB image'ını kullanıyor.
Ayrıca iki servis aynı Compose ağı içerisinde çalıştığı için backend tarafından MongoDB'ye servis adı üzerinden ulaşılabilir:
mongodb:27017
Burada önemli nokta şu: container'lar birbirleriyle iletişim kurarken host üzerinde yaptığımız port mapping'e ihtiyaç duymaz. Port mapping daha çok host dışından container'a erişmek istediğimiz durumlarda kullanılır.
Docker Compose ile Çalıştırma
docker-compose.yml dosyamızı hazırladıktan sonra artık container'ları tek tek çalıştırmak yerine Compose üzerinden yönetebiliriz.
Container'ları oluşturup arka planda çalıştırmak için:
docker compose up -d
Dockerfile'da yaptığım değişikliklerin image'a yansıması için build işlemini de zorlamak istersem:
docker compose up -d --build
Çalışan Compose servislerini durdurmak için:
docker compose down
Böylece daha önce ayrı ayrı yazmam gereken birçok Docker komutunu tek bir Compose dosyası üzerinden yönetebiliyorum.
Özet
Docker Compose kullanırken en sık karşılaşacağımız key'leri kısaca şöyle özetleyebiliriz:
| Key | Görevi |
|---|---|
services |
Çalıştırılacak servisleri/container'ları tanımlar. |
build |
Dockerfile kullanılarak image oluşturulmasını sağlar. |
image |
Hazır bir Docker image'ının kullanılmasını sağlar. |
ports |
Host ile container arasındaki port mapping işlemini tanımlar. |
container_name |
Container'a özel bir isim verilmesini sağlar. |
volumes |
Bind mount veya named volume kullanarak kalıcı veri yönetimini sağlar. |
version |
Eski Compose dosya formatlarında sürüm belirtmek için kullanılır; güncel Compose Specification'da çoğu durumda gerekli değildir. |
Benim açımdan Docker Compose'un en büyük avantajı, Docker ile ilgili birçok ayarı tek bir dosyada tutabilmek. Özellikle birden fazla container'ın birlikte çalıştığı projelerde build, image, ports, volumes ve servisler arası bağlantıları tek bir docker-compose.yml dosyasından yönetmek, projeyi hem tekrar çalıştırmayı hem de başka bir makinede kurmayı oldukça kolaylaştırıyor.
