Apache Web Sunucusunu Docker Container Ortamında Çalıştırma

Docker’ı ilk öğrendiğimde en çok kafamı karıştıran şey şuydu: “Zaten Apache kurmasını biliyorum, neden container içinde çalıştırayım ki?” Ama zamanla anladım ki bu soru tamamen yanlış sorulmuş. Asıl soru şu olmalı: “Containerization bana ne kazandırıyor?” Cevap ise oldukça net: ortam bağımsızlığı, hızlı deployment, kolay ölçekleme ve temiz bir izolasyon katmanı. Bu yazıda Apache’yi Docker ortamında nasıl çalıştıracağımızı, production’a yakın senaryolarda nasıl yapılandıracağımızı ve karşılaşacağınız gerçek dünya sorunlarını nasıl çözeceğinizi adım adım inceleyeceğiz.

Docker ile Apache: Neden Bu Kombinasyon?

Klasik bir bare-metal veya VM kurulumunda Apache’yi yapılandırmak zaman alır, bağımlılıklar birbirine girer ve “bende çalışıyor, sende neden çalışmıyor” tartışmaları kaçınılmaz olur. Docker ile bu problemlerin büyük bölümü ortadan kalkar. Birden fazla projeyi aynı sunucuda farklı Apache versiyonlarıyla çalıştırabilirsiniz, test ortamınızı saniyeler içinde ayağa kaldırabilirsiniz ve en önemlisi deployment süreciniz tekrarlanabilir hale gelir.

Ancak şunu da söylemeliyim: Docker her şeyin çözümü değil. Container içindeki Apache’yi yönetmek, bazı durumlarda daha fazla katman anlamına gelir. Log yönetimi, volume bağlama, network konfigürasyonu gibi konularda dikkatli olmak gerekir. Bu yazı boyunca hem teknik detayları hem de bu dikkat edilmesi gereken noktaları ele alacağız.

Temel Kurulum: İlk Apache Container’ı

En basit haliyle başlayalım. Docker Hub üzerindeki resmi Apache imajını kullanarak hızlıca bir container ayağa kaldırabiliriz:

docker pull httpd:2.4
docker run -d --name my-apache -p 8080:80 httpd:2.4

Tarayıcınızdan http://localhost:8080 adresine gittiğinizde Apache’nin varsayılan “It works!” sayfasını görürsünüz. Güzel başlangıç ama production için bu yeterli değil. Şimdi daha gerçekçi bir senaryo inşa edelim.

Container’ın içine bakıp Apache konfigürasyonunu anlamak için şu komutu kullanabilirsiniz:

docker exec -it my-apache /bin/bash
# Container içinde:
cat /usr/local/apache2/conf/httpd.conf
ls /usr/local/apache2/htdocs/

Resmi httpd imajında dokümanlar /usr/local/apache2/htdocs/ altında, konfigürasyon ise /usr/local/apache2/conf/ altında bulunur. Bu yolları aklınıza kazıyın, çünkü volume mount ederken sürekli kullanacaksınız.

Dockerfile ile Özelleştirilmiş Apache İmajı

Gerçek projelerde kendi Dockerfile’ınızı yazmanız gerekir. Diyelim ki bir PHP uygulaması değil, saf static bir site sunuyorsunuz ve bazı özel Apache modüllerine ihtiyacınız var:

FROM httpd:2.4

# Konfigürasyon dosyasını kopyala
COPY ./config/httpd.conf /usr/local/apache2/conf/httpd.conf

# Ek virtual host konfigürasyonu
COPY ./config/vhosts/ /usr/local/apache2/conf/extra/

# Site dosyalarını kopyala
COPY ./public_html/ /usr/local/apache2/htdocs/

# Sıkıştırma için mod_deflate aktif
RUN sed -i 's/#LoadModule deflate_module/LoadModule deflate_module/' /usr/local/apache2/conf/httpd.conf

# mod_headers aktif et
RUN sed -i 's/#LoadModule headers_module/LoadModule headers_module/' /usr/local/apache2/conf/httpd.conf

EXPOSE 80

CMD ["httpd-foreground"]

Bu Dockerfile’ı build edip çalıştıralım:

docker build -t my-apache-custom:1.0 .
docker run -d 
  --name apache-prod 
  -p 80:80 
  -p 443:443 
  my-apache-custom:1.0

Önemli bir nokta: httpd-foreground komutu, Apache’nin container içinde foreground’da çalışmasını sağlar. Docker container’ları bir process’e ihtiyaç duyar ve o process durduğunda container da durur. Apache’yi daemon modunda başlatırsanız container hemen kapanır.

Volume Mount ile Geliştirme Ortamı

Development ortamında her değişiklikte image rebuild etmek can sıkıcıdır. Volume mount kullanarak local dosyalarınızı doğrudan container içine bağlayabilirsiniz:

docker run -d 
  --name apache-dev 
  -p 8080:80 
  -v $(pwd)/public_html:/usr/local/apache2/htdocs/ 
  -v $(pwd)/config/httpd.conf:/usr/local/apache2/conf/httpd.conf 
  httpd:2.4

Bu yöntemle local’de dosyada değişiklik yaptığınızda, container içinde anında yansır. Apache’yi reload etmeniz gerekirse:

docker exec apache-dev apachectl graceful

graceful komutu aktif bağlantıları kesmeden Apache’yi yeniden yükler. Development ortamında bunu sık kullanacaksınız.

Docker Compose ile Multi-Container Yapısı

Gerçek dünyada Apache tek başına nadiren çalışır. Genellikle bir reverse proxy, bir uygulama sunucusu ve bir veritabanı ile birlikte çalışır. İşte tipik bir LAMP benzeri stack için docker-compose.yml:

version: '3.8'

services:
  apache:
    build:
      context: .
      dockerfile: Dockerfile.apache
    container_name: apache_web
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./public_html:/usr/local/apache2/htdocs/
      - ./config/httpd.conf:/usr/local/apache2/conf/httpd.conf
      - ./config/extra:/usr/local/apache2/conf/extra/
      - ./logs/apache:/usr/local/apache2/logs/
      - ./ssl:/usr/local/apache2/ssl/
    environment:
      - TZ=Europe/Istanbul
    depends_on:
      - php-fpm
    networks:
      - web_network
    restart: unless-stopped

  php-fpm:
    image: php:8.2-fpm
    container_name: php_fpm
    volumes:
      - ./public_html:/var/www/html/
      - ./config/php/php.ini:/usr/local/etc/php/php.ini
    networks:
      - web_network
    restart: unless-stopped

  mariadb:
    image: mariadb:10.11
    container_name: mariadb_db
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - web_network
    restart: unless-stopped

networks:
  web_network:
    driver: bridge

volumes:
  db_data:

Bu compose dosyasıyla birlikte bir .env dosyası oluşturun:

DB_ROOT_PASSWORD=guclu_bir_sifre_koy
DB_NAME=uygulama_db
DB_USER=uygulama_user
DB_PASSWORD=baska_bir_guclu_sifre

Tüm stack’i ayağa kaldırmak için:

docker-compose up -d
docker-compose logs -f apache

Apache ve PHP-FPM Entegrasyonu

Apache’yi PHP-FPM ile kullanmak, mod_php’ye göre çok daha performanslı bir yapı sunar. Bunun için Apache konfigürasyonunuzda mod_proxy_fcgi modülünü aktif etmeniz ve PHP isteklerini PHP-FPM container’ına yönlendirmeniz gerekir.

Compose ortamında Apache’nin PHP-FPM’e bağlanması için aşağıdaki virtual host konfigürasyonunu kullanın:

<VirtualHost *:80>
    ServerName myapp.local
    DocumentRoot /usr/local/apache2/htdocs

    <Directory /usr/local/apache2/htdocs>
        Options Indexes FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # PHP-FPM ile entegrasyon
    # "php-fpm" container adı DNS olarak çözümlenir
    <FilesMatch .php$>
        SetHandler "proxy:fcgi://php-fpm:9000"
    </FilesMatch>

    # mod_fcgid yerine mod_proxy_fcgi kullanıyoruz
    ProxyPassMatch ^/(.*.php(/.*)?)$ fcgi://php-fpm:9000/var/www/html/$1

    ErrorLog /usr/local/apache2/logs/error.log
    CustomLog /usr/local/apache2/logs/access.log combined
</VirtualHost>

Docker Compose’un iç DNS’i sayesinde container adları birbirini çözümler. Yani php-fpm ismiyle diğer container’a ulaşabilirsiniz. Bu çok güzel bir özellik ve bunu aktif olarak kullanmalısınız.

SSL/TLS Konfigürasyonu

Container ortamında SSL yaygın olarak iki şekilde yapılır: ya Apache container’ının kendisinde SSL sonlandırmak, ya da önüne bir Nginx veya Traefik reverse proxy koymak. Ben her iki yöntemi de göstereyim ama container ortamında önüne reverse proxy koymak daha temiz bir yaklaşım.

Ama önce Apache container içinde SSL nasıl yapılır görelim. Dockerfile.apache dosyasına ekleyeceğiniz satırlar:

FROM httpd:2.4

# SSL modülünü aktif et
RUN sed -i 
    -e 's/#LoadModule ssl_module/LoadModule ssl_module/' 
    -e 's/#LoadModule socache_shmcb_module/LoadModule socache_shmcb_module/' 
    -e 's/#Include conf/extra/httpd-ssl.conf/Include conf/extra/httpd-ssl.conf/' 
    /usr/local/apache2/conf/httpd.conf

# Özel SSL konfigürasyonu
COPY ./config/ssl.conf /usr/local/apache2/conf/extra/httpd-ssl.conf

EXPOSE 80 443
CMD ["httpd-foreground"]

SSL konfigürasyonu (config/ssl.conf):

Listen 443

SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES
SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES
SSLHonorCipherOrder on
SSLProtocol all -SSLv3
SSLProxyProtocol all -SSLv3
SSLPassPhraseDialog  builtin
SSLSessionCache        "shmcb:/usr/local/apache2/logs/ssl_scache(512000)"
SSLSessionCacheTimeout  300

<VirtualHost _default_:443>
    DocumentRoot "/usr/local/apache2/htdocs"
    ServerName yourdomain.com:443
    
    SSLEngine on
    SSLCertificateFile "/usr/local/apache2/ssl/server.crt"
    SSLCertificateKeyFile "/usr/local/apache2/ssl/server.key"
    SSLCertificateChainFile "/usr/local/apache2/ssl/chain.pem"
    
    # Modern SSL header'ları
    Header always set Strict-Transport-Security "max-age=63072000"
</VirtualHost>

SSL sertifikalarınızı volume olarak mount edin, asla image içine gömmeyin:

docker run -d 
  -v /etc/letsencrypt/live/yourdomain.com/fullchain.pem:/usr/local/apache2/ssl/server.crt 
  -v /etc/letsencrypt/live/yourdomain.com/privkey.pem:/usr/local/apache2/ssl/server.key 
  my-apache-ssl:1.0

Log Yönetimi ve Monitoring

Container ortamında log yönetimi özellikle dikkat gerektiren bir konu. İki temel yaklaşım var: logları volume üzerinden dışarıya almak veya Apache’yi stdout/stderr’e yazacak şekilde yapılandırmak. Docker ekosistemiyle daha iyi entegrasyon için ikinci yöntem önerilir.

Apache’yi logları stdout/stderr’e yazacak şekilde yapılandırmak için:

# httpd.conf içinde
ErrorLog /proc/self/fd/2
CustomLog /proc/self/fd/1 combined

Bu sayede docker logs apache_web komutu ile tüm logları görebilirsiniz ve docker-compose logs -f ile takip edebilirsiniz.

Volume tabanlı log yönetimi için ise:

# Log dizinini oluştur ve izinleri ayarla
mkdir -p ./logs/apache
chmod 755 ./logs/apache

# Container'ı başlatırken volume ekle
docker run -d 
  --name apache_web 
  -v $(pwd)/logs/apache:/usr/local/apache2/logs/ 
  httpd:2.4

Log rotation için host üzerinde logrotate konfigürasyonu ekleyin. /etc/logrotate.d/docker-apache dosyası:

/path/to/logs/apache/*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    sharedscripts
    postrotate
        docker exec apache_web apachectl graceful > /dev/null 2>&1 || true
    endscript
}

Performans Optimizasyonu

Container ortamında Apache’nin performansını artırmak için birkaç kritik ayar var. mpm_event modülü kullanmak yerine mpm_worker veya memory kullanımını optimize etmek için aşağıdaki konfigürasyonu inceleyin:

# mpm_event konfigürasyonu - container için optimize
<IfModule mpm_event_module>
    StartServers             2
    MinSpareThreads         25
    MaxSpareThreads         75
    ThreadLimit             64
    ThreadsPerChild         25
    MaxRequestWorkers      150
    MaxConnectionsPerChild   0
</IfModule>

# Keepalive ayarları
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5

# Compression
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css
    AddOutputFilterByType DEFLATE application/javascript application/json
    DeflateCompressionLevel 6
</IfModule>

# Browser caching
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpg "access plus 1 month"
    ExpiresByType image/png "access plus 1 month"
    ExpiresByType text/css "access plus 1 week"
    ExpiresByType application/javascript "access plus 1 week"
</IfModule>

Container’a resource limit koymayı da unutmayın. Docker compose’da:

services:
  apache:
    image: httpd:2.4
    deploy:
      resources:
        limits:
          cpus: '1.0'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 128M

Güvenlik Hardening

Container ortamında bile Apache güvenliğini ihmal etmemek gerekir. Container izolasyonu bir güvenlik katmanı sağlar ama Apache seviyesinde de önlem almak gerekir.

Temel güvenlik konfigürasyonu:

# Server bilgilerini gizle
ServerTokens Prod
ServerSignature Off

# Dizin listelemeyi kapat
Options -Indexes

# Gereksiz HTTP metodlarını kapat
<LimitExcept GET POST HEAD>
    deny from all
</LimitExcept>

# .htaccess ve gizli dosyalara erişimi engelle
<FilesMatch "^.">
    Require all denied
</FilesMatch>

# Clickjacking koruması
Header always set X-Frame-Options "SAMEORIGIN"
Header always set X-Content-Type-Options "nosniff"
Header always set X-XSS-Protection "1; mode=block"
Header always set Referrer-Policy "strict-origin-when-cross-origin"

Container’ı root olmayan kullanıcı ile çalıştırmak için Dockerfile’a ekleyin:

FROM httpd:2.4

# Apache'nin 80 yerine yüksek port kullanması için
# Non-root user ile çalıştırma
RUN sed -i 's/Listen 80/Listen 8080/' /usr/local/apache2/conf/httpd.conf

# Dosya izinleri
RUN chown -R www-data:www-data /usr/local/apache2/logs/ 
    && chown -R www-data:www-data /usr/local/apache2/htdocs/

USER www-data
EXPOSE 8080

Health Check ve Restart Politikaları

Production ortamında container’ın sağlıklı çalışıp çalışmadığını kontrol etmek kritik önem taşır:

FROM httpd:2.4

# Health check ekle
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 
    CMD curl -f http://localhost/ || exit 1

CMD ["httpd-foreground"]

Docker Compose’da daha detaylı health check:

services:
  apache:
    image: my-apache:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s
    restart: unless-stopped

Health check için basit bir endpoint ekleyin. /usr/local/apache2/htdocs/health dosyasını oluşturun:

echo "OK" > public_html/health

Container durumunu izlemek için:

docker inspect --format='{{.State.Health.Status}}' apache_web
docker events --filter container=apache_web --filter event=health_status

Gerçek Dünya Senaryosu: Çoklu Site Yönetimi

Diyelim ki tek bir Apache container üzerinde birden fazla domain barındırmak istiyorsunuz. Bunu virtual host konfigürasyonları ile yapabilirsiniz:

# Proje yapısı
.
├── docker-compose.yml
├── config/
│   ├── httpd.conf
│   └── vhosts/
│       ├── site1.conf
│       └── site2.conf
├── sites/
│   ├── site1/
│   └── site2/
└── logs/

httpd.conf dosyasında vhost dizinini include edin:

# Ana httpd.conf'a ekle
IncludeOptional conf/extra/vhosts/*.conf

Her site için ayrı virtual host dosyası (config/vhosts/site1.conf):

<VirtualHost *:80>
    ServerName site1.example.com
    ServerAlias www.site1.example.com
    DocumentRoot /usr/local/apache2/htdocs/site1
    
    <Directory /usr/local/apache2/htdocs/site1>
        AllowOverride All
        Require all granted
    </Directory>
    
    ErrorLog /usr/local/apache2/logs/site1-error.log
    CustomLog /usr/local/apache2/logs/site1-access.log combined
</VirtualHost>

Docker Compose’da volume mount’ları düzenleyin:

services:
  apache:
    image: httpd:2.4
    volumes:
      - ./config/httpd.conf:/usr/local/apache2/conf/httpd.conf
      - ./config/vhosts:/usr/local/apache2/conf/extra/vhosts
      - ./sites/site1:/usr/local/apache2/htdocs/site1
      - ./sites/site2:/usr/local/apache2/htdocs/site2
      - ./logs:/usr/local/apache2/logs

Bu yapıyla yeni bir site eklemek istediğinizde sadece yeni bir vhost dosyası oluşturup Apache’yi reload etmeniz yeterli:

# Yeni site için vhost dosyası ekledikten sonra
docker exec apache_web apachectl configtest
docker exec apache_web apachectl graceful

configtest komutu çok önemli, konfigürasyonu uygulamadan önce sözdizimi hatalarını yakalar.

Troubleshooting: Sık Karşılaşılan Sorunlar

Permission sorunları: Container içindeki Apache, host’taki dosyaları okuyamıyorsa UID/GID uyumsuzluğu olabilir.

# Container'daki Apache UID'sini bul
docker exec apache_web id www-data
# Host'ta dosya sahipliğini güncelle
chown -R 33:33 ./public_html/

Port çakışması: 80. portu başka bir servis kullanıyorsa:

# Hangi process 80. portu kullanıyor
ss -tlnp | grep :80
# Ya da
lsof -i :80

Container içinde modül aktif değil hatası: apachectl -M ile yüklü modülleri listeleyin:

docker exec apache_web apachectl -M | grep deflate

Konfigürasyon hatası debug: Ayrıntılı hata mesajı için log level’ı artırın:

LogLevel debug

Bu satırı geçici olarak ekleyin, production’da warn veya error seviyesinde tutun.

Sonuç

Apache’yi Docker container ortamında çalıştırmak, başlangıçta fazladan karmaşıklık gibi görünse de uzun vadede çok ciddi avantajlar sağlıyor. Ortam tutarlılığı, hızlı deployment, kolay ölçekleme ve izolasyon bu avantajların başında geliyor. Bu yazıda ele aldığımız konuları özetleyecek olursak:

  • Resmi httpd imajını temel alarak özel Dockerfile’lar yazabilirsiniz
  • Volume mount ile development ortamında verimli çalışabilirsiniz
  • Docker Compose ile Apache, PHP-FPM ve veritabanını birlikte yönetebilirsiniz
  • SSL konfigürasyonunu container içinde veya reverse proxy üzerinden yapabilirsiniz
  • Log yönetimini stdout/stderr veya volume bazlı çözebilirsiniz
  • Health check ve restart politikalarıyla production güvenilirliğini artırabilirsiniz
  • Çoklu site yönetimini virtual host konfigürasyonlarıyla container içinde de yapabilirsiniz

Bir sonraki adım olarak bu yapıyı Kubernetes üzerinde çalıştırmayı veya Traefik ile otomatik SSL yönetimini incelemenizi öneririm. Container dünyası bir kez içine girince geri dönmek istemiyorsunuz, söz veriyorum.

Yorum yapın