Docker ve Docker Compose Kullanarak Ubuntu 16.04 Üzerinde Sürekli Entegrasyon (CI) Test Ortamı Yapılandırma
Sürekli Entegrasyon (Continuous Integration – CI), modern yazılım geliştirme süreçlerinin temel taşlarından biridir. Geliştiricilerin kod değişikliklerini düzenli olarak merkezi bir depoya entegre etmelerini ve bu entegrasyonun otomatik olarak test edilmesini sağlayan bir yazılım geliştirme uygulamasıdır. Bu makalede, Docker ve Docker Compose teknolojilerini kullanarak Ubuntu 16.04 işletim sistemi üzerinde nasıl bir CI test ortamı kurulacağını adım adım inceleyeceğiz. Ubuntu 16.04, bazı kurumsal ortamlarda hala yaygın olarak kullanılan stabil bir sürüm olması nedeniyle bu makale için tercih edilmiştir, ancak belirtilen adımlar ve prensipler, daha yeni Ubuntu sürümleri için de büyük ölçüde geçerlidir.
Giriş: Sürekli Entegrasyon ve Konteynerleşmenin Gücü
Yazılım geliştirme süreçleri, günümüzde hiç olmadığı kadar hızlı ve karmaşık hale gelmiştir. Çok sayıda geliştiricinin aynı anda çalıştığı projelerde, kod entegrasyonu ve test süreçleri büyük zorluklar yaratabilir. İşte bu noktada Sürekli Entegrasyon (CI) devreye girer. CI, her kod değişikliğinin otomatik olarak birleştirilmesini (build) ve test edilmesini sağlayarak, entegrasyon sorunlarının erken aşamada tespit edilmesine olanak tanır. Bu sayede, geliştirme döngüsü hızlanır, hatalar daha az maliyetle düzeltilir ve yazılımın kalitesi artar.
CI ortamlarının en büyük zorluklarından biri, testlerin her zaman tutarlı ve izole bir ortamda çalışmasını sağlamaktır. “Benim bilgisayarımda çalışıyordu!” sendromu, geliştiricilerin sıklıkla karşılaştığı bir durumdur. Bu sorunu çözmek için konteyner teknolojileri, özellikle Docker, mükemmel bir çözüm sunar. Docker, uygulamaları ve bağımlılıklarını hafif, taşınabilir ve izole edilmiş konteynerler içinde paketlemeyi sağlar. Docker Compose ise, birden fazla Docker konteynerini içeren uygulamaları tanımlamak ve çalıştırmak için bir araçtır.
Bu makalede, Ubuntu 16.04 üzerinde Docker ve Docker Compose’u kuracak, basit bir örnek uygulama için CI test ortamını yapılandıracak ve bu ortamı nasıl kullanacağımızı adım adım göstereceğiz. Amaç, geliştiricilere ve DevOps mühendislerine, tutarlı, tekrarlanabilir ve verimli bir test altyapısı kurmaları için gerekli bilgi ve araçları sağlamaktır.
Ön Gereksinimler ve Ortam Kurulumu
CI test ortamımızı yapılandırmaya başlamadan önce, Ubuntu 16.04 sunucumuzda gerekli ön gereksinimleri karşılamamız ve Docker ile Docker Compose’u kurmamız gerekmektedir.
Ubuntu 16.04 Sisteminin Hazırlanması
İlk adım, Ubuntu 16.04 sisteminizin güncel olduğundan emin olmaktır. Terminali açın ve aşağıdaki komutları çalıştırın:
sudo apt update
sudo apt upgrade -y
Bu komutlar, sistemdeki paket listesini günceller ve mevcut tüm paketleri en son sürümlerine yükseltir.
Docker Kurulumu
Ubuntu 16.04 üzerinde Docker CE (Community Edition) kurulumu için en güvenli ve önerilen yöntem, Docker’ın resmi deposunu kullanmaktır.
1. Gerekli Paketlerin Kurulumu
Docker deposunu kullanmak için bazı önkoşul paketlerini yüklememiz gerekir:
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
* apt-transport-https: HTTPS üzerinden depoları kullanmak için.
* ca-certificates: SSL/TLS sertifikaları için.
* curl: URL’lerden veri almak için.
* software-properties-common: Depo ekleme/kaldırma gibi işlemleri kolaylaştırmak için.
2. Docker’ın GPG Anahtarını Ekleme
Docker’ın resmi GPG anahtarını sistemimize ekleyerek, indirdiğimiz paketlerin doğruluğunu teyit edebiliriz:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
Bu komut, Docker’ın GPG anahtarını indirir ve apt anahtar halkasına ekler.
3. Docker Deposunu Ekleme
Şimdi, Docker’ın kararlı (stable) deposunu Ubuntu 16.04 için apt kaynaklarımıza ekleyebiliriz:
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable"
Burada xenial, Ubuntu 16.04’ün kod adıdır. amd64 ise işlemci mimarisini belirtir.
4. Docker CE Kurulumu
Depoyu ekledikten sonra, apt paket listesini tekrar güncellemeli ve Docker CE’yi kurmalıyız:
sudo apt update
sudo apt install -y docker-ce
Kurulum tamamlandıktan sonra, Docker servisi otomatik olarak başlayacaktır.
5. Docker Servisini Kontrol Etme
Docker servisinin çalıştığından emin olmak için aşağıdaki komutu kullanın:
sudo systemctl status docker
Çıktıda active (running) ifadesini görmelisiniz.
6. Docker Komutlarını Sudo Olmadan Çalıştırma (İsteğe Bağlı ama Önerilen)
Her Docker komutunu çalıştırırken sudo yazmaktan kaçınmak için, mevcut kullanıcınızı docker grubuna ekleyebilirsiniz:
sudo usermod -aG docker $USER
Bu değişikliğin etkili olması için oturumu kapatıp tekrar açmanız veya sistemi yeniden başlatmanız gerekebilir. Oturumunuzu yeniden başlattıktan sonra, sudo kullanmadan bir Docker komutu çalıştırarak test edebilirsiniz:
docker run hello-world
Bu komut, Docker’ın doğru şekilde çalıştığını gösteren bir “Hello from Docker!” mesajı döndürmelidir.
Docker Compose Kurulumu
Docker Compose, birden fazla konteynerden oluşan uygulamaları tanımlamak ve çalıştırmak için bir araçtır. Kurulumu da oldukça basittir.
1. Docker Compose İndirme
Docker Compose’un en son kararlı sürümünü doğrudan GitHub deposundan indirebiliriz. Önce mevcut en son sürümü kontrol edin (örneğin, [https://github.com/docker/compose/releases](https://github.com/docker/compose/releases) adresinden). Bu makalenin yazıldığı an itibarıyla 1.29.2 son kararlı sürümdür, ancak siz her zaman en yenisini kullanmalısınız.
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
Bu komut, Docker Compose ikili dosyasını indirir ve /usr/local/bin dizinine kaydeder. $(uname -s) ve $(uname -m) komutları, sırasıyla işletim sistemi adını (Linux) ve makine mimarisini (x86_64) otomatik olarak algılar.
2. Çalıştırma İzinleri Verme
İndirdiğimiz dosyaya çalıştırma izinleri vermemiz gerekir:
sudo chmod +x /usr/local/bin/docker-compose
3. Docker Compose Kurulumunu Doğrulama
Kurulumun başarılı olduğunu doğrulamak için sürüm kontrolü yapın:
docker-compose --version
Çıktıda indirdiğiniz sürüme benzer bir ifade görmelisiniz (örn: docker-compose version 1.29.2, build 5bd41ab1).
Artık Ubuntu 16.04 sistemimiz üzerinde Docker ve Docker Compose başarıyla kurulmuştur. CI test ortamımızı yapılandırmak için hazırız.
Sürekli Entegrasyon (CI) Kavramları ve Docker ile Entegrasyonu
CI’ın temel amacı, yazılım geliştirme sürecindeki entegrasyon sorunlarını mümkün olduğunca erken tespit etmektir. Bu, geliştiricilerin sık sık (günde birkaç kez) kodlarını merkezi bir depoya göndermeleri ve her gönderimin ardından otomatik bir build ve test sürecinin tetiklenmesiyle sağlanır.
CI İş Akışı
Tipik bir CI iş akışı aşağıdaki adımları içerir:
1. Kod Değişikliği: Geliştirici, yerel makinesinde kod değişiklikleri yapar.
2. Commit ve Push: Değişiklikler, Git gibi bir sürüm kontrol sistemine commit edilir ve merkezi depoya (örn. GitHub, GitLab) push edilir.
3. CI Sunucusu Tetiklenmesi: Merkezi depoya yapılan her push, CI sunucusunu (örn. Jenkins, GitLab CI, CircleCI) tetikler.
4. Kod Çekme: CI sunucusu, en son kodu depodan çeker.
5. Build: Uygulama derlenir veya paketlenir (örn. Java için JAR, Node.js için bağımlılıkların yüklenmesi).
6. Test: Birim testleri (unit tests), entegrasyon testleri (integration tests) ve bazen uçtan uca testler (end-to-end tests) otomatik olarak çalıştırılır.
7. Raporlama: Test sonuçları toplanır ve geliştiricilere, takıma veya bir dashboard’a raporlanır.
8. Geri Bildirim: Testler başarısız olursa, geliştiriciye anında geri bildirim sağlanır, böylece sorun hızla düzeltilebilir. Testler başarılı olursa, kod bir sonraki aşamaya (örn. Sürekli Dağıtım – CD) geçebilir.
Neden Docker CI İçin İdealdir?
Docker, CI süreçlerine paha biçilmez faydalar sağlar:
* Ortam Tutarlılığı: Docker konteynerleri, uygulamanın tüm bağımlılıklarını (işletim sistemi, kütüphaneler, çalışma zamanı) içerir. Bu, geliştirme, test ve üretim ortamları arasında “çalışan bir şeyin başka bir yerde çalışmaması” sorununu ortadan kaldırır. Testler her zaman aynı ortamda çalışır.
* İzolasyon: Her test çalıştırması veya her proje için ayrı bir Docker konteyneri veya konteyner seti kullanılabilir. Bu, testlerin birbirini etkilemesini engeller ve temiz bir başlangıç ortamı sağlar.
* Hızlı Geri Dönüşler: Konteynerler hızlı bir şekilde başlatılıp durdurulabilir. Bu, test ortamının hızlı bir şekilde kurulup kaldırılmasına olanak tanır, böylece geliştiriciler daha hızlı geri bildirim alabilirler.
* Bağımlılık Yönetimi: Veritabanları, mesaj kuyrukları gibi harici bağımlılıklar da Docker konteynerleri olarak tanımlanabilir ve Docker Compose ile kolayca yönetilebilir. Bu, test ortamını kurma karmaşıklığını azaltır.
* Taşınabilirlik: Dockerfile ve Docker Compose dosyaları, ortamın yapılandırmasını kod olarak tanımlar. Bu dosyalar sürüm kontrol sisteminde tutulabilir ve farklı makinelerde kolayca çoğaltılabilir.
* Ölçeklenebilirlik: İhtiyaç duyulduğunda, aynı CI iş akışını paralel olarak birden fazla konteynerde çalıştırarak testleri hızlandırmak mümkündür.
Örnek Bir Uygulama ve Test Senaryosu
CI ortamımızı somutlaştırmak için basit bir Python Flask uygulaması ve ona ait birim testleri kullanacağız. Ayrıca, uygulamanın bir PostgreSQL veritabanına bağımlılığı olduğunu varsayacağız.
Uygulama Yapısı
Proje dizinimiz aşağıdaki gibi olacaktır:
my-flask-app/
├── app/
│ ├── __init__.py
│ ├── app.py
│ └── database.py
├── tests/
│ ├── __init__.py
│ └── test_app.py
├── requirements.txt
├── Dockerfile
└── docker-compose.yml
requirements.txt
Uygulamanın Python bağımlılıkları:
Flask
psycopg2-binary
pytest
app/app.py
Basit bir Flask uygulaması. Bir ana sayfa ve veritabanı bağlantısını kontrol eden bir uç nokta içerir.
from flask import Flask, jsonify
import os
import psycopg2
app = Flask(__name__)
def get_db_connection():
conn = psycopg2.connect(
host=os.environ.get('DB_HOST', 'localhost'),
database=os.environ.get('DB_NAME', 'testdb'),
user=os.environ.get('DB_USER', 'testuser'),
password=os.environ.get('DB_PASSWORD', 'testpassword')
)
return conn
@app.route('/')
def hello():
return "Merhaba, Docker CI Dünyası!"
@app.route('/db_status')
def db_status():
try:
conn = get_db_connection()
cur = conn.cursor()
cur.execute('SELECT 1')
cur.close()
conn.close()
return jsonify(status="Veritabanı bağlantısı başarılı!"), 200
except Exception as e:
return jsonify(status=f"Veritabanı bağlantı hatası: {str(e)}"), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
app/database.py (Basit bir veritabanı başlatma betiği)
Bu betik, testler için basit bir tablo oluşturmak amacıyla kullanılacak.
import psycopg2
import os
def init_db():
conn = None
try:
conn = psycopg2.connect(
host=os.environ.get('DB_HOST', 'localhost'),
database=os.environ.get('DB_NAME', 'testdb'),
user=os.environ.get('DB_USER', 'testuser'),
password=os.environ.get('DB_PASSWORD', 'testpassword')
)
cur = conn.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS messages (id SERIAL PRIMARY KEY, text VARCHAR NOT NULL);")
conn.commit()
print("Veritabanı başarıyla başlatıldı ve tablo oluşturuldu.")
cur.close()
except Exception as e:
print(f"Veritabanı başlatma hatası: {e}")
finally:
if conn:
conn.close()
if __name__ == '__main__':
init_db()
tests/test_app.py
Uygulama için birim ve entegrasyon testleri (Pytest kullanarak).
import pytest
from app.app import app, get_db_connection
import os
import time
import psycopg2
@pytest.fixture
def client():
app.config['TESTING'] = True
with app.test_client() as client:
yield client
def test_hello_route(client):
response = client.get('/')
assert response.status_code == 200
assert b"Merhaba, Docker CI Dünyası!" in response.data
def test_db_status_route_success(client):
# Veritabanının hazır olduğundan emin olmak için kısa bir bekleme
# Gerçek CI ortamlarında healthcheck veya wait-for-it scriptleri kullanılır
time.sleep(5)
# Test için geçici olarak veritabanı bağlantısını simüle edelim veya
# gerçek bir veritabanı bağlantısı kurmaya çalışalım.
# Bu testin çalışması için docker-compose'daki db servisi ayakta olmalı.
# Basit bir entegrasyon testi için, veritabanı bağlantısını kontrol edelim.
try:
conn = get_db_connection()
cur = conn.cursor()
cur.execute('SELECT 1')
cur.close()
conn.close()
db_connected = True
except Exception:
db_connected = False
if db_connected:
response = client.get('/db_status')
assert response.status_code == 200
assert b"Veritabanı bağlantısı başarılı!" in response.data
else:
# Eğer test ortamında veritabanı yoksa, bu testin geçmesini sağlamak için
# veya farklı bir hata kodu beklemek için bu kısmı düzenleyebilirsiniz.
# Bu senaryoda, veritabanı bağlantısı başarısız olacağı için 500 bekleriz.
response = client.get('/db_status')
assert response.status_code == 500
assert b"Veritabanı bağlantı hatası" in response.data
Dockerfile
Uygulamamızı bir Docker imajına dönüştürmek için Dockerfile:
# Temel imaj olarak Python 3.6 kullan (Ubuntu 16.04 ile uyumlu ve stabil)
FROM python:3.6-slim-stretch
Çalışma dizinini ayarla
WORKDIR /app
Bağımlılıkları kopyala ve kur
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
Uygulama kodunu kopyala
COPY app/ .
Veritabanı başlatma betiğini kopyala
COPY app/database.py .
Uygulama portunu açığa çıkar
EXPOSE 5000
Veritabanını başlat (sadece test ortamında veya ilk çalıştırmada)
Bu komutu entrypoint'e koymak yerine docker-compose'da 'command' olarak kontrol etmek daha iyi
CMD ["python", "app.py"]
Not: Yukarıdaki Dockerfile sadece uygulamanın bağımlılıklarını ve kodunu içerir. Testleri çalıştırmak için ayrı bir mekanizma veya aynı imajı farklı bir komutla kullanacağız.
Docker Compose ile CI Ortamını Tanımlama
Şimdi docker-compose.yml dosyasını oluşturarak CI test ortamımızı tanımlayacağız. Bu dosya, uygulamamızı, veritabanımızı ve testlerimizi çalıştıracak servisleri içerecek.
docker-compose.yml
version: '3.8'
services:
# Uygulama servisi
app:
build:
context: .
dockerfile: Dockerfile
image: my-flask-app:latest # İsteğe bağlı olarak bir imaj adı verilebilir
ports:
- "5000:5000"
environment:
# Veritabanı bağlantı bilgileri
DB_HOST: db
DB_NAME: testdb
DB_USER: testuser
DB_PASSWORD: testpassword
depends_on:
db:
condition: service_healthy # db servisi sağlıklı olana kadar bekle
# Veritabanı servisi (PostgreSQL)
db:
image: postgres:9.6 # Ubuntu 16.04 döneminde popüler olan bir sürüm
environment:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpassword
volumes:
- db_data:/var/lib/postgresql/data # Veritabanı verilerini kalıcı hale getir
healthcheck:
test: ["CMD-SHELL", "pg_isready -U testuser -d testdb"]
interval: 5s
timeout: 5s
retries: 5
# Test servisi
test:
build:
context: .
dockerfile: Dockerfile
environment:
DB_HOST: db
DB_NAME: testdb
DB_USER: testuser
DB_PASSWORD: testpassword
depends_on:
app:
condition: service_healthy # Uygulama servisi sağlıklı olana kadar bekle
db:
condition: service_healthy # Veritabanı servisi sağlıklı olana kadar bekle
# Testleri çalıştırmadan önce veritabanını başlatma betiğini çalıştır
entrypoint: ["/bin/sh", "-c"]
command:
- |
python database.py &&
pytest tests/
volumes:
- ./tests:/app/tests # Test dosyalarını bağla
- ./app:/app/app # Uygulama dosyalarını bağla (testlerin uygulamaya erişmesi için)
volumes:
db_data:
Bu docker-compose.yml dosyasını detaylı olarak inceleyelim:
* version: '3.8': Docker Compose dosya formatının sürümünü belirtir.
* services: Uygulamamızın farklı bileşenlerini tanımlar.
* app servisi:
* build: Uygulama kodumuzun bulunduğu dizinden Dockerfile kullanarak bir Docker imajı oluşturmasını sağlar.
* image: Oluşturulan imaja bir isim verir.
* ports: Konteynerin 5000 portunu host makinenin 5000 portuna eşler.
* environment: Uygulamanın veritabanı bağlantısı için gerekli ortam değişkenlerini tanımlar. DB_HOST: db ifadesi, db servisinin konteyner ağındaki adını kullanır.
* depends_on: app servisinin db servisine bağımlı olduğunu belirtir. condition: service_healthy ile db servisi tamamen sağlıklı olana kadar app servisinin başlamasını bekler.
* db servisi:
* image: postgres:9.6: PostgreSQL veritabanı için resmi Docker imajını kullanır. Ubuntu 16.04’ün çıktığı dönemde popüler olan bir PostgreSQL sürümüdür.
* environment: PostgreSQL için veritabanı adı, kullanıcı adı ve şifre gibi ortam değişkenlerini ayarlar.
* volumes: db_data adında bir Docker birimini /var/lib/postgresql/data dizinine bağlar. Bu, veritabanı verilerinin konteyner silinse bile kalıcı olmasını sağlar.
* healthcheck: Veritabanı servisinin sağlıklı olup olmadığını kontrol etmek için bir komut tanımlar. pg_isready komutu, PostgreSQL’in bağlantı kabul etmeye hazır olup olmadığını kontrol eder. Bu, depends_on ile birlikte kritik bir özelliktir.
* test servisi:
* build: app servisiyle aynı Dockerfile‘ı kullanarak bir imaj oluşturur. Bu, testlerin uygulama ile aynı bağımlılıklara sahip olmasını sağlar.
* environment: app servisi gibi, testlerin de veritabanına erişebilmesi için ortam değişkenlerini ayarlar.
* depends_on: test servisinin hem app hem de db servislerinin sağlıklı bir şekilde çalışmasını beklediğini belirtir. Bu, testlerin başlamadan önce tüm bağımlılıkların hazır olmasını garanti eder.
* entrypoint ve command: Bu, test servisinin ana işlevidir.
* python database.py: Testler başlamadan önce veritabanında gerekli tabloları oluşturur.
* pytest tests/: pytest komutunu çalıştırarak tests/ dizinindeki tüm testleri başlatır.
* volumes: Test dosyalarını ve uygulama dosyalarını konteyner içine bağlar. Bu, Dockerfile‘ı her değiştirdiğimizde imajı yeniden oluşturmak yerine, yerel dosyalarda yapılan değişikliklerin doğrudan konteyner içinde görünmesini sağlar, geliştirme ve test döngüsünü hızlandırır.
* volumes: db_data adında bir Docker birimini tanımlar. Bu, veritabanı verilerinin kalıcılığını sağlar.
CI İş Akışını Tetikleme ve Yönetme
docker-compose.yml dosyamızı oluşturduktan sonra, CI iş akışımızı manuel olarak tetikleyebiliriz.
Manuel Olarak Testleri Çalıştırma
Proje dizininizin kök dizininde (yani docker-compose.yml dosyasının bulunduğu yerde) aşağıdaki komutları çalıştırın:
docker-compose build
docker-compose up -d db app
* docker-compose build: Dockerfile kullanarak app ve test servisleri için Docker imajlarını oluşturur.
* docker-compose up -d db app: db ve app servislerini arka planda (-d) başlatır. depends_on ve healthcheck sayesinde, app servisi db servisi tamamen hazır olana kadar başlamayacaktır.
Servisler çalışmaya başladıktan sonra, db ve app servislerinin sağlıklı olduğunu kontrol edebilirsiniz:
docker-compose ps
docker-compose logs db app
db ve app servisleri Up (healthy) durumuna geldiğinde, test servisini çalıştırabiliriz:
docker-compose run --rm test
* docker-compose run --rm test: test servisini çalıştırır. --rm bayrağı, konteynerin işi bittikten sonra otomatik olarak kaldırılmasını sağlar, böylece temiz bir ortam bırakılır.
* Bu komut, test servisinin entrypoint ve command kısmında belirtilen python database.py && pytest tests/ komutunu çalıştıracaktır.
Testler çalışacak ve sonuçları terminalde görünecektir. Eğer tüm testler geçerse, test servisi 0 çıkış koduyla sonlanır. Herhangi bir test başarısız olursa, genellikle 1 veya başka bir sıfır olmayan çıkış kodu döndürülür. Bu çıkış kodu, bir CI aracının (Jenkins, GitLab CI vb.) testlerin başarılı mı yoksa başarısız mı olduğunu anlaması için kritik öneme sahiptir.
Testler bittikten sonra, çalışan servisleri durdurabilir ve oluşturulan birimleri temizleyebilirsiniz:
docker-compose down -v
* docker-compose down: Tüm servisleri durdurur ve kaldırır.
* -v: Veritabanı verilerini içeren db_data birimini de kaldırır. Bu, bir sonraki çalıştırmada tamamen temiz bir veritabanı ortamı sağlar.
CI Sunucusu ile Entegrasyonun Mantığı
Gerçek bir CI ortamında, bu manuel adımlar otomatikleştirilir. Bir CI sunucusu (Jenkins, GitLab CI, CircleCI, GitHub Actions vb.) genellikle aşağıdaki adımları izleyen bir yapılandırma dosyası (örn. Jenkinsfile, .gitlab-ci.yml) kullanır:
1. Kod Çekme: CI sunucusu, depodan en son kodu çeker.
2. Ortamı Hazırlama: CI sunucusu, Docker ve Docker Compose’un kurulu olduğu bir ortamda çalışır.
3. Build ve Test: CI sunucusu, proje dizinine gider ve yukarıda gösterilen docker-compose build, docker-compose up -d db app, ve docker-compose run --rm test komutlarını sırasıyla çalıştırır.
4. Sonuçları Analiz Etme: CI sunucusu, docker-compose run --rm test komutunun çıkış kodunu kontrol eder. Çıkış kodu 0 ise testler başarılı, değilse başarısız olarak işaretlenir.
5. Raporlama: Test çıktıları (loglar) ve başarı/başarısızlık durumu CI sunucusunun arayüzünde gösterilir ve ilgili kişilere bildirim gönderilir.
6. Temizleme: Testler bittikten sonra, docker-compose down -v komutu ile ortam temizlenir.
Bu yaklaşım, her CI çalıştırması için temiz, izole ve tekrarlanabilir bir test ortamı sağlar.
Optimizasyon ve En İyi Uygulamalar
CI test ortamınızın verimli ve güvenli olması için bazı optimizasyonlar ve en iyi uygulamalar mevcuttur.
Dockerfile Optimizasyonları
* Katmanlama (Layering): Dockerfile’daki her komut bir katman oluşturur. Sık değişmeyen katmanları (örn. bağımlılık kurulumu) Dockerfile’ın üst kısımlarına yerleştirerek Docker’ın build önbelleğini daha etkin kullanın.
* .dockerignore Kullanımı: Gereksiz dosyaların (örn. .git, __pycache__, .env) imaj içine kopyalanmasını engellemek için .dockerignore dosyası oluşturun. Bu, imaj boyutunu küçültür ve build süresini hızlandırır.
* Minimal Temel İmajlar: python:3.6-slim-stretch gibi slim veya alpine tabanlı imajlar kullanarak imaj boyutunu minimumda tutun. Bu, indirme sürelerini ve disk kullanımını azaltır.
* Tek Sorumluluk Prensibi: Her konteynerin tek bir sorumluluğu olmalıdır (örn. uygulama, veritabanı, testler).
Docker Compose Optimizasyonları
* healthcheck Kullanımı: depends_on ile birlikte healthcheck kullanmak, servislerin gerçekten kullanıma hazır olana kadar diğer servislerin başlamasını beklemelerini sağlar. Bu, özellikle veritabanları gibi başlatılması zaman alan servisler için kritiktir.
* restart: on-failure: Üretim ortamında, servislerin beklenmedik bir şekilde durması durumunda otomatik olarak yeniden başlatılmasını sağlayabilir. CI ortamında bu genellikle gerekli değildir.
* Ortam Değişkenleri: Hassas bilgileri (veritabanı şifreleri) docker-compose.yml dosyasına doğrudan yazmak yerine, CI sunucusunun ortam değişkenleri veya Docker’ın secrets yönetimi aracılığıyla sağlamak daha güvenlidir.
* Sanal Ağlar: Docker Compose, servisler arasında otomatik olarak bir köprü ağı oluşturur. Servislerin birbirleriyle isimleriyle iletişim kurabilmesi bu sayede mümkündür (örn. DB_HOST: db).
Test Stratejileri
* Hızlı Testler Önce: Birim testleri en hızlı testlerdir ve CI sürecinin başında çalıştırılmalıdır. Hata tespiti ne kadar erken olursa, düzeltme maliyeti o kadar düşük olur.
* Kademeli Testler: Birim testleri başarılı olduktan sonra entegrasyon testleri, ardından uçtan uca testler çalıştırılabilir. Her kademe, bir önceki kademenin başarısına bağlı olmalıdır.
* Test Verileri: Testler için tutarlı ve tekrarlanabilir test verileri kullanın. database.py örneğimizdeki gibi, testlerden önce veritabanını belirli bir duruma getiren betikler kullanışlıdır.
Önbellekleme (Caching)
* Docker Build Cache: Docker, imaj katmanlarını önbelleğe alır. Dockerfile’ı doğru yapılandırarak (sık değişmeyen katmanları üste koyarak), build sürelerini önemli ölçüde azaltabilirsiniz.
* CI Aracı Önbellekleri: Jenkins, GitLab CI gibi CI araçları, bağımlılıkları (örn. pip cache) veya build çıktılarını önbelleğe alma yeteneğine sahiptir. Bu, her CI çalıştırmasında her şeyi sıfırdan indirme veya derleme ihtiyacını azaltır.
Kaynak Yönetimi
* CI sunucusunun yeterli CPU ve belleğe sahip olduğundan emin olun. Çok sayıda paralel test çalıştırması veya büyük imajlar sistem kaynaklarını zorlayabilir.
* Docker Compose’da resources altında limits ve reservations kullanarak konteynerler için CPU ve bellek sınırları belirleyebilirsiniz.
Güvenlik
* İmaj Tarama: Kullanılan Docker imajlarının (özellikle temel imajların) bilinen güvenlik açıklarına karşı taranması için araçlar kullanın (örn. Trivy, Clair).
* Bağımlılık Güncellemeleri: Uygulama bağımlılıklarını (Python paketleri, npm paketleri vb.) düzenli olarak güncel tutun.
* En Az Yetki Prensibi: Konteynerleri mümkün olan en düşük yetkilerle çalıştırın. root kullanıcısı yerine özel bir kullanıcı kullanın.
Sorun Giderme (Troubleshooting)
CI ortamı kurulumunda veya test çalıştırmalarında sorunlarla karşılaşmak doğaldır. İşte bazı yaygın sorunlar ve çözüm ipuçları:
* Konteyner Başlamıyor:
* docker-compose logs komutu ile ilgili servisin loglarını kontrol edin. Hata mesajları genellikle sorunun kökenini gösterir.
* docker-compose ps ile servislerin durumunu kontrol edin. Exited veya Restarting durumları bir soruna işaret eder.
* Servisler Arası Bağlantı Sorunları:
* Ortam değişkenlerinin (örn. DB_HOST) doğru ayarlandığından ve servis adlarının docker-compose.yml dosyasındaki isimlerle eşleştiğinden emin olun.
* depends_on ve healthcheck yapılandırmasını gözden geçirin. Bir servis hazır olmadan diğerinin başlaması bağlantı hatalarına yol açabilir.
* Konteyner içine girip (docker exec -it ) ping db gibi komutlarla ağ bağlantısını test edebilirsiniz.
* Testler Başarısız Oluyor:
* Test servisinin loglarını (docker-compose logs test) dikkatlice inceleyin. Pytest gibi test çerçeveleri, başarısız testler hakkında detaylı bilgi sağlar.
* Bağımlılıkların (örn. Python paketleri) doğru şekilde kurulduğundan ve uygulamanın tüm gereksinimlerini karşıladığından emin olun.
* Test verilerinin tutarlı olup olmadığını kontrol edin.
* İmaj Oluşturma (Build) Hataları:
* Dockerfile‘daki komutları tek tek kontrol edin. Herhangi bir RUN komutunun başarısız olması, build’in durmasına neden olur.
* requirements.txt gibi bağımlılık dosyalarının doğru yolda olduğundan ve içeriğinin geçerli olduğundan emin olun.
* Disk Alanı Tükenmesi:
* Docker imajları ve konteynerleri zamanla disk alanı tüketebilir. Gereksiz imajları ve konteynerleri temizlemek için docker system prune -a komutunu kullanın (dikkatli olun, tüm durdurulmuş konteynerleri, ağları, imajları ve build cache’i temizler).
* Veritabanı birimleri gibi kalıcı verilerin çok fazla yer kaplamadığından emin olun.
Sonuç
Bu makalede, Docker ve Docker Compose kullanarak Ubuntu 16.04 üzerinde nasıl Sürekli Entegrasyon (CI) test ortamı kurulacağını detaylı bir şekilde inceledik. CI’ın temel prensiplerinden başlayarak, Docker’ın kurulumunu, Docker Compose ile çoklu konteynerli bir uygulamanın ve test ortamının nasıl tanımlanacağını ve bu ortamın nasıl çalıştırılacağını adım adım gösterdik. Ayrıca, CI sürecinizi daha verimli ve güvenli hale getirmek için çeşitli optimizasyonlar ve en iyi uygulamalar hakkında bilgi verdik.
Docker ve Docker Compose, geliştiricilere ve DevOps ekiplerine, yazılım geliştirme süreçlerinde tutarlılık, izolasyon ve taşınabilirlik sağlayarak CI’ın gerçek potansiyelini ortaya çıkarma gücü verir. Bu araçlar sayesinde, “benim makinemde çalışıyordu” sorunları geçmişte kalır ve ekipler daha hızlı, daha güvenilir ve daha kaliteli yazılım teslim edebilirler.
Unutulmamalıdır ki, Ubuntu 16.04’ün yaşam döngüsü sona ermiştir ve güvenlik güncellemeleri almamaktadır. Yeni projeler için daha güncel ve desteklenen bir işletim sistemi sürümü (örn. Ubuntu 20.04 LTS veya 22.04 LTS) kullanılması şiddetle tavsiye edilir. Ancak, bu makalede açıklanan temel prensipler ve Docker/Docker Compose komutları, çoğu Linux tabanlı işletim sistemi ve Docker sürümü için geçerliliğini korumaktadır. Bu bilgileri temel alarak, kendi projeleriniz için sağlam ve verimli bir CI test altyapısı kurabilirsiniz. Gelecek adımlar olarak, Jenkins, GitLab CI gibi popüler CI araçlarıyla bu Docker Compose tabanlı ortamı entegre etmeyi ve daha karmaşık test senaryolarını (performans testleri, güvenlik testleri) otomatikleştirmeyi düşünebilirsiniz.