Ubuntu 22.04 üzerinde Docker Compose ile Laravel Nasıl Kurulur ve Ayarlanır


Giriş

Bir uygulamayı konteynerleştirmek, bir uygulamayı ve bileşenlerini, konteyner olarak bilinen hafif ortamlarda çalıştırılabilir hale getirme sürecine atıfta bulunur. Bu tür ortamlar izole ve geçicidir ve uygulamaları geliştirmek, test etmek ve üretime dağıtmak için kullanılabilir.

Bu rehberde, geliştirme için bir Laravel uygulamasını konteynerleştirmek amacıyla Docker Compose kullanacağız. İşinizi bitirdiğinizde, üç ayrı servis konteynerinde çalışan bir demo Laravel uygulamanız olacak:

  • PHP7.4-FPM çalıştıran bir app servisi;
  • MySQL 8.0 çalıştıran bir db servisi;
  • Laravel uygulamasını son kullanıcıya sunmadan önce PHP kodunu işlemek için app servisini kullanan bir nginx servisi.

Daha akıcı bir geliştirme süreci sağlamak ve uygulama hata ayıklamayı kolaylaştırmak için, uygulama dosyalarını paylaşılan hacimler kullanarak senkronize tutacağız. Ayrıca docker compose exec komutlarını kullanarak app konteynerinde Composer ve Artisan çalıştırmanın nasıl yapılacağını göreceğiz.

Frontend uygulamalarınızı GitHub üzerinden Linux-Console.net Uygulama Platformu ile dağıtın. Linux-Console.net, uygulamanızın ölçeklenmesine odaklansın.

Öne Çıkan Noktalar:

  • Konteynerleştirme geliştirmeyi öngörülebilir kılar: Laravel’i Docker’da çalıştırmak, makineler arasında tutarlı ortamlar sağlar, “Bilgisayarımda çalışıyor” sorunlarını ortadan kaldırır ve iş birliğini kolaylaştırır.
  • Hizmetlerin ayrılması bakım kolaylığını artırır: PHP, MySQL ve Nginx'i ayrı konteynerlere ayırmak, sorunları izole etmeye yardımcı olur, güncellemeleri basitleştirir ve gerçek dünya üretim ortamlarını yansıtır.
  • Çevresel değişkenler, esnek yapılandırmanın merkezindedir: Bir .env dosyası kullanmak, geliştiricilerin uygulama koduna dokunmadan veritabanı kimlik bilgilerini ve diğer ayarları değiştirmelerine olanak tanır.
  • Özel Docker imajları kontrol ve güvenliği artırır: Kendi PHP imajınızı oluşturmak, projenizin ihtiyacı olanları yüklemenizi sağlar ve böylece ortamı hafif ve güvenli tutar.
  • Dosya izinleri konteyner kullanıcılarıyla eşleşmelidir: Laravel’in yazılabilir dizinleri için doğru sahiplik ve izinlerin ayarlanması, çalışma zamanı hatalarını önler ve sorunsuz yerel geliştirmeyi destekler.
  • Override dosyalarını kullanmak birden fazla ortamı destekler: docker-compose.override.yml, canlı yenileme gibi yerel ayarlamalar yapmayı sağlar—üretim yapılandırmalarını değiştirmeden. Bu, geliştirme ve dağıtımı temiz bir şekilde ayrılmış tutar.
  • Sorun gidermeye, konteyner davranışını anlamakla başlamak gerekir: Günlükleri nasıl inceleyeceğinizi, çalışan servisleri nasıl kontrol edeceğinizi ve bağlantıyı test etmeyi bilmek, konteyner tabanlı kurulumlarda çoğu sorunu hızlı bir şekilde çözmenin anahtarıdır.
  • Docker tabanlı bir kurulum iş akışınıza göre ölçeklenir: Bir kez oluşturulduğunda, aynı yapılandırma yerel, test veya üretim ortamlarını destekleyebilir ve gelecekteki Laravel projeleri için sağlam, tekrar kullanılabilir bir temel oluşturur.

Önkoşullar

  • Sudo ayrıcalıklarına sahip kök olmayan bir kullanıcı olarak bir Ubuntu yerel makinesine veya geliştirme sunucusuna erişim. Eğer uzaktan bir sunucu kullanıyorsanız, etkin bir güvenlik duvarının kurulu olması tavsiye edilir. Bunları kurmak için lütfen Ubuntu için İlk Sunucu Kurulum Kılavuzumuza başvurun.
  • Ubuntu'da Docker'ı Yükleme ve Kullanma adlı kılavuzun 1. ve 2. Adımlarını takip ederek sunucunuza Docker yüklendi.
  • Ubuntu'da Docker Compose'u Kurma ve Kullanma Rehberinin 1. Adımını takip ederek sunucunuza Docker Compose yüklendi.

Adım 1 — Demo Uygulamayı Alma

Başlamak için, demo Laravel uygulamasını Github deposundan alacağız. İlgilendiğimiz tutorial-01 dalı, How To Install and Configure Laravel with Nginx on Ubuntu 22.04 (LEMP) rehberinde oluşturduğumuz temel Laravel uygulamasını içeriyor.

Bu eğitimle uyumlu uygulama kodunu almak için, tutorial-1.0.1 sürümünü ev dizininize indirerek:

cd ~

curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-1.0.1.zip -o travellist.zip

Uygulama kodunu açmak için unzip komutuna ihtiyacımız olacak. Bu paketi daha önce yüklemediyseniz, şimdi şu şekilde yükleyebilirsiniz:

sudo apt update

sudo apt install unzip

Şimdi, uygulamanın içeriğini açın ve daha kolay erişim için açılmış dizini yeniden adlandırın:

unzip travellist.zip

mv travellist-laravel-demo-tutorial-1.0.1 travellist-demo

travellist-demo dizinine gidin:

cd travellist-demo

Bir sonraki adımda, uygulamayı kurmak için bir .env yapılandırma dosyası oluşturacağız.

Adım 2 — Uygulamanın .env Dosyasının Ayarlanması

Laravel yapılandırma dosyaları, uygulamanın kök dizini içinde yer alan config adlı bir dizinde bulunur. Ek olarak, ortam bağımlı yapılandırmayı ayarlamak için, örneğin kimlik bilgileri ve dağıtımlar arasında değişebilecek herhangi bir bilgi için, bir .env dosyası kullanılır. Bu dosya sürüm kontrolüne dahil edilmez.

Uyarı: Ortam yapılandırma dosyası, veritabanı kimlik bilgileri ve güvenlik anahtarları dahil olmak üzere sunucunuzla ilgili hassas bilgiler içerir. Bu nedenle, bu dosyayı asla kamuya açık şekilde paylaşmamalısınız.

.env dosyasında bulunan değerler, config dizininde yer alan normal yapılandırma dosyalarında belirlenen değerlere üstünlük sağlar. Yeni bir ortamda yapılan her kurulum, hangi ortamda çalıştığına bağlı olarak değişebilecek veritabanı bağlantı ayarları, hata ayıklama seçenekleri, uygulama URL'si gibi öğeleri tanımlamak için özel bir ortam dosyası gerektirir.

Şimdi, kurmakta olduğumuz geliştirme ortamı için yapılandırma seçeneklerini özelleştirmek amacıyla yeni bir .env dosyası oluşturacağız. Laravel, kendi .env dosyamızı oluşturmak için kopyalayabileceğimiz örnek bir .env dosyasıyla birlikte gelir:

cp .env.example .env

Bu dosyayı nano veya tercih ettiğiniz metin düzenleyici ile açın:

nano .env

Mevcut .env dosyası, travellist demo uygulamasından, 127.0.0.1 veritabanı sunucusunu kullanacak şekilde yerel bir MySQL veritabanı ayarlarını içeriyor. DB_HOST değişkenini, Docker ortamımızda oluşturacağımız veritabanı servisine işaret edecek şekilde güncellememiz gerekiyor. Bu rehberde, veritabanı servisimizi db olarak adlandıracağız. Listedeki DB_HOST değerini veritabanı servis adıyla değiştirin:

APP_NAME=Travellist
APP_ENV=dev
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=travellist
DB_USERNAME=travellist_user
DB_PASSWORD=password
...

İsterseniz veritabanı adını, kullanıcı adını ve şifreyi de değiştirebilirsiniz. Bu değişkenler, servislerimizi yapılandırmak için docker-compose.yml dosyasını ayarlayacağımız bir sonraki adımda kullanılacaktır.

Düzenlemeyi bitirdiğinizde dosyayı kaydedin. nano kullandıysanız, bunu Ctrl+x tuşlarına basarak, ardından Y ve Enter ile onaylayarak yapabilirsiniz.

Adım 3 – Uygulamanın Dockerfile Dosyasını Ayarlamak

Her ne kadar hem MySQL hem de Nginx servislerimiz Docker Hub’dan alınan varsayılan imajlara dayansa da, uygulama konteyneri için yine de özel bir imaj oluşturmamız gerekiyor. Bunun için yeni bir Dockerfile oluşturacağız.

Bizim travellist imajımız, Docker Hub'daki php:7.4-fpm resmi PHP imajına dayanacak. Bu temel PHP-FPM ortamının üzerine, birkaç ek PHP modülü ve Composer bağımlılık yönetim aracını kuracağız.

Ayrıca yeni bir sistem kullanıcısı da oluşturacağız; bu, uygulamayı geliştirirken artisan ve composer komutlarını çalıştırmak için gereklidir. uid ayarı, konteyner içindeki kullanıcının Docker'ı çalıştırdığınız ana makinenizdeki sistem kullanıcısıyla aynı uid'ye sahip olmasını sağlar. Bu şekilde, bu komutlarla oluşturulan herhangi bir dosya, ana makinede doğru izinlerle çoğaltılır. Bu aynı zamanda, konteynerler içinde çalışan uygulamayı geliştirmek için ana makinede tercih ettiğiniz kod editörünü kullanabileceğiniz anlamına gelir.

Şu içeriğe sahip yeni bir Dockerfile oluştur:

nano Dockerfile

Aşağıdaki içeriği Dockerfile'ınıza kopyalayın:

FROM php:7.4-fpm

# Arguments defined in docker-compose.yml
ARG user
ARG uid

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

# Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
    chown -R $user:$user /home/$user

# Set working directory
WORKDIR /var/www

USER $user

İşiniz bittiğinde dosyayı kaydetmeyi unutmayın.

Dockerfile’ımız, kullandığımız temel imajı tanımlayarak başlıyor: php:7.4-fpm.

Sistem paketlerini ve PHP eklentilerini kurduktan sonra, composer yürütülebilir dosyasını en güncel resmi imajından kendi uygulama imajımıza kopyalayarak Composer'ı kuruyoruz.

Yeni bir sistem kullanıcısı, Dockerfile'ın başında belirtilen user ve uid argümanları kullanılarak oluşturulur ve ayarlanır. Bu değerler, Docker Compose tarafından derleme sırasında enjekte edilecektir.

Son olarak, varsayılan çalışma dizinini /var/www olarak ayarlıyoruz ve yeni oluşturulan kullanıcıya geçiyoruz. Bu, uygulama konteynerinde composer ve artisan komutlarını çalıştırırken normal bir kullanıcı olarak bağlandığınızdan ve doğru dizinde bulunduğunuzdan emin olmanızı sağlar.

Adım 4 — Nginx Yapılandırmasını ve Veritabanı Yedek Dosyalarını Ayarlama

Docker Compose ile geliştirme ortamları oluştururken, bu hizmetleri kurmak veya başlatmak için yapılandırma veya başlangıç dosyalarını hizmet konteynerleriyle paylaşmak çoğu zaman gereklidir. Bu uygulama, uygulamayı geliştirirken ortamınızı ince ayar yapmak amacıyla yapılandırma dosyalarına değişiklik yapmayı kolaylaştırır.

Şimdi, servis konteynerlerimizi yapılandırmak ve başlatmak için kullanılacak dosyalarla bir klasör oluşturacağız.

Nginx'i kurmak için, uygulamanın nasıl sunulacağını yapılandıracak bir travellist.conf dosyasını paylaşacağız. Şu klasörü oluşturun: docker-compose/nginx

mkdir -p docker-compose/nginx

O dizin içinde travellist.conf adlı yeni bir dosya açın:

nano docker-compose/nginx/travellist.conf

Aşağıdaki Nginx yapılandırmasını o dosyaya kopyalayın:

server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

Bu dosya, Nginx'in 80 numaralı portu dinlemesini ve varsayılan index sayfası olarak index.php'yi kullanmasını yapılandıracaktır. Belgelerin kök dizinini /var/www/public olarak ayarlayacak ve ardından Nginx'in *.php dosyalarını işlemek için 9000 portundaki app servisini kullanmasını yapılandıracaktır.

Düzenlemeyi bitirdiğinizde dosyayı kaydedin ve kapatın.

MySQL veritabanını kurmak için, konteyner başlatıldığında içe aktarılacak bir veritabanı dökümü paylaşacağız. Bu, o konteynerde kullanacağımız MySQL 8.0 imajı tarafından sağlanan bir özelliktir.

docker-compose klasörünün içinde MySQL başlatma dosyalarınız için yeni bir klasör oluşturun:

mkdir docker-compose/mysql

Yeni bir .sql dosyası açın:

nano docker-compose/mysql/init_db.sql

Aşağıdaki MySQL dökümü, Laravel on LEMP rehberimizde oluşturduğumuz veritabanına dayanmaktadır. Bu, places adında yeni bir tablo oluşturacaktır. Ardından tabloyu bir dizi örnek mekan ile dolduracaktır.

Dosyaya aşağıdaki kodu ekleyin:

DROP TABLE IF EXISTS `places`;

CREATE TABLE `places` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `visited` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `places` (name, visited) VALUES ('Berlin',0),('Budapest',0),('Cincinnati',1),('Denver',0),('Helsinki',0),('Lisbon',0),('Moscow',1),('Nairobi',0),('Oslo',1),('Rio',0),('Tokyo',0);

places tablosu üç alan içerir: id, name ve visited. visited alanı, gidilecek yerleri belirlemek için kullanılan bir bayraktır. Örnek yerleri değiştirmekten veya yenilerini eklemekten çekinmeyin. İşiniz bittiğinde dosyayı kaydedip kapatın.

Uygulamanın Dockerfile ve servis yapılandırma dosyalarını ayarlamayı bitirdik. Sonraki adımda, servislerimizi oluştururken bu dosyaları kullanmak için Docker Compose'u ayarlayacağız.

Adım 5 — Docker Compose ile Çoklu Konteyner Ortamı Oluşturma

Docker Compose, Docker üzerinde çalışan uygulamalar için çoklu konteyner ortamları oluşturmanıza olanak tanır. Tamamen özelleştirilebilir ortamları oluşturmak için servis tanımlarını kullanır ve bu ortamlar birden çok konteynerin ağları ve veri hacimlerini paylaşabilmesini sağlar. Bu, uygulama bileşenleri arasında sorunsuz bir entegrasyon sağlar.

Hizmet tanımlarımızı ayarlamak için, docker-compose.yml adında yeni bir dosya oluşturacağız. Genellikle bu dosya uygulama klasörünün kökünde bulunur ve konteynerleştirilmiş ortamınızı, konteynerlerinizi oluşturmak için kullanacağınız temel imajları ve hizmetlerinizin nasıl etkileşimde bulunacağını tanımlar.

docker-compose.yml dosyamızda üç farklı servisi tanımlayacağız: app, db ve nginx.

app servisi, daha önce oluşturduğumuz Dockerfile'a dayanarak travellist adlı bir imaj oluşturacaktır. Bu servis tarafından tanımlanan konteyner, PHP kodunu analiz etmek ve sonuçları ayrı bir konteynerda çalışacak olan nginx servisine göndermek için bir php-fpm sunucusu çalıştıracaktır. mysql servisi, MySQL 8.0 sunucusu çalıştıran bir konteyner tanımlar. Servislerimiz, travellist adında bir köprü ağı paylaşacaktır.

Uygulama dosyaları, hem app hem de nginx servislerinde bind mount kullanılarak senkronize edilecektir. Bind mountlar, host makine ile konteynerler arasında yüksek performanslı iki yönlü senkronizasyon sağladıkları için geliştirme ortamlarında faydalıdır.

Uygulama klasörünün kök dizininde yeni bir docker-compose.yml dosyası oluşturun:

nano docker-compose.yml

Tipik bir docker-compose.yml dosyası, bir sürüm tanımıyla başlar, ardından tüm servislerin tanımlandığı bir services düğümü gelir. Paylaşılan ağlar genellikle dosyanın en alt kısmında tanımlanır.

Başlamak için, bu hazır kodu docker-compose.yml dosyanıza kopyalayın:

version: "3.9"
services:


networks:
  travellist:
    driver: bridge

Şimdi services düğümünü app, db ve nginx servislerini içerecek şekilde düzenleyeceğiz.

app Servisi

app servisi, travellist-app adında bir konteyner kuracaktır. Bu servis, docker-compose.yml dosyasının bulunduğu aynı yolda yer alan bir Dockerfile’a göre yeni bir Docker imajı oluşturur. Oluşturulan yeni imaj, travellist adıyla yerel olarak kaydedilecektir.

Uygulama olarak sunulan belge kökü nginx konteynerinde bulunsa da, Laravel Artisan aracıyla komut satırı görevlerini çalıştırabilmemiz için uygulama dosyalarına app konteynerinin içinde de bir yerde ihtiyacımız var.

Aşağıdaki servis tanımını docker-compose.yml dosyanızdaki services düğümünün altına kopyalayın:

  app:
    build:
      args:
        user: sammy
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    image: travellist
    container_name: travellist-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travellist

Bu ayarlar şunları yapar:

  • build: Bu yapılandırma, Docker Compose'a app servisi için yerel bir görüntü oluşturmasını söyler; belirtilen yol (context) ve talimatlar için Dockerfile kullanılır. user ve uid argümanları, oluşturma sırasında kullanıcı oluşturma komutlarını özelleştirmek için Dockerfile'a enjekte edilir.
  • image: Oluşturulacak görüntü için kullanılacak isim.
  • container_name: Bu servis için konteyner adını ayarlar.
  • restart: Hizmet durdurulmadıkça her zaman yeniden başlat.
  • working_dir: Bu servis için varsayılan dizini /var/www olarak ayarlar.
  • volumes: Mevcut dizinin içeriğini konteyner içindeki /var/www dizinine senkronize edecek bir paylaşılan hacim oluşturur. Bunun sizin belge kökünüz olmadığını unutmayın, çünkü belge kökünüz nginx konteynerinde bulunacaktır.
  • networks: Bu hizmetin travellist adlı bir ağı kullanacak şekilde ayarlanmasını sağlar.

db Servisi

db servisi, Docker Hub'dan önceden oluşturulmuş bir MySQL 8.0 imajını kullanır. Docker Compose, docker-compose.yml dosyasının bulunduğu aynı dizindeki .env değişken dosyalarını otomatik olarak yüklediği için, veritabanı ayarlarımızı daha önce oluşturduğumuz Laravel .env dosyasından alabiliriz.

app servisten hemen sonra services düğümünüzde aşağıdaki servis tanımını ekleyin:

  db:
    image: mysql:8.0
    container_name: travellist-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/mysql:/docker-entrypoint-initdb.d
    networks:
      - travellist

Bu ayarlar şunları yapar:

  • image: Bu kapsayıcı için kullanılacak Docker imajını tanımlar. Bu durumda, Docker Hub’dan MySQL 8.0 imajını kullanıyoruz.
  • container_name: Bu servis için konteyner adını ayarlar: travellist-db.
  • restart: Bu hizmetin her zaman yeniden başlatılmasını sağlar, aksi açıkça durdurulmadıkça.
  • environment: Yeni konteynerde ortam değişkenlerini tanımlar. Laravel .env dosyasından alınan değerleri, belirtilen ortam değişkenlerine göre otomatik olarak yeni bir veritabanı ve kullanıcı oluşturacak olan MySQL servisimiz için kullanıyoruz.
  • volumes: Uygulama veritabanını başlatmak için kullanılacak bir .sql veritabanı dökümünü paylaşmak üzere bir hacim oluşturur. MySQL imajı, konteyner içindeki /docker-entrypoint-initdb.d dizinine yerleştirilen .sql dosyalarını otomatik olarak içe aktaracaktır.
  • networks: Bu hizmetin travellist adlı bir ağı kullanacak şekilde ayarlanmasını sağlar.

nginx Servisi

nginx servisi, hafif bir Linux dağıtımı olan Alpine üzerine kurulmuş önceden oluşturulmuş bir Nginx imajını kullanır. travellist-nginx adında bir konteyner oluşturur ve ports tanımını kullanarak host sistemdeki 8000 portundan konteyner içindeki 80 portuna yönlendirme yapar.

db servisten hemen sonra services düğümünüzde aşağıdaki servis tanımını ekleyin:

  nginx:
    image: nginx:1.17-alpine
    container_name: travellist-nginx
    restart: unless-stopped
    ports:
      - 8000:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d
    networks:
      - travellist

Bu ayarlar şunları yapar:

  • image: Bu konteyner için kullanılacak Docker imajını tanımlar. Bu durumda, Alpine Nginx 1.17 imajını kullanıyoruz.
  • container_name: Bu servis için konteyner adını ayarlar: travellist-nginx.
  • restart: Bu hizmeti her zaman yeniden başlatın, açıkça durdurulmadığı sürece.
  • ports: Dış erişime, konteyner içinde 80 numaralı portta çalışan web sunucusuna 8000 portu üzerinden izin verecek bir port yönlendirmesi ayarlar.
  • volumes: İki paylaşılan hacim (volume) oluşturur. Birincisi, mevcut dizindeki içerikleri konteyner içindeki /var/www dizinine senkronize eder. Bu sayede, uygulama dosyalarında yerel değişiklikler yaptığınızda, bu değişiklikler konteyner içindeki Nginx tarafından sunulan uygulamaya hızlı bir şekilde yansır. İkinci hacim ise docker-compose/nginx/travellist.conf konumundaki Nginx yapılandırma dosyamızın, konteynerin Nginx yapılandırma klasörüne kopyalanmasını sağlar.
  • networks: Bu hizmetin travellist adlı bir ağı kullanacak şekilde ayarlanmasını sağlar.

Tamamlanan docker-compose.yml Dosyası

İşte tamamlanmış docker-compose.yml dosyamızın görünümü:

version: "3.9"
services:
  app:
    build:
      args:
        user: sammy
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    image: travellist
    container_name: travellist-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travellist

  db:
    image: mysql:8.0
    container_name: travellist-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/mysql:/docker-entrypoint-initdb.d
    networks:
      - travellist

  nginx:
    image: nginx:1.17-alpine
    container_name: travellist-nginx
    restart: unless-stopped
    ports:
      - 8000:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d/
    networks:
      - travellist

networks:
  travellist:
    driver: bridge

İşi bitirdiğinizde dosyayı kaydettiğinizden emin olun.

Adım 6 — Uygulamayı Docker Compose ile Çalıştırma

Artık uygulama imajını oluşturmak ve kurulumumuzda belirttiğimiz servisleri çalıştırmak için docker compose komutlarını kullanacağız.

Aşağıdaki komutla app görüntüsünü oluşturun:

docker compose build app

Bu komutun tamamlanması birkaç dakika sürebilir. Benzer bir çıktı göreceksiniz:

Building app
Sending build context to Docker daemon  377.3kB
Step 1/11 : FROM php:7.4-fpm
 ---> 8c08d993542f
Step 2/11 : ARG user
 ---> e3ce3af04d87
Step 3/11 : ARG uid
 ---> 30cb921ef7df
Step 4/11 : RUN apt-get update && apt-get install -y     git     curl     libpng-dev     libonig-dev     libxml2-dev     zip     unzip
. . .
 ---> b6dbc7a02e95
Step 5/11 : RUN apt-get clean && rm -rf /var/lib/apt/lists/*
 ---> 10ef9dde45ad
. . .
Step 6/11 : RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
. . .
 ---> 920e4f09ec75
Step 7/11 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
 ---> dbbcd44e44af
Step 8/11 : RUN useradd -G www-data,root -u $uid -d /home/$user $user
 ---> db98e899a69a
Step 9/11 : RUN mkdir -p /home/$user/.composer &&     chown -R $user:$user /home/$user
 ---> 5119e26ebfea
Step 10/11 : WORKDIR /var/www
 ---> 699c491611c0
Step 11/11 : USER $user
 ---> cf250fe8f1af
Successfully built cf250fe8f1af
Successfully tagged travellist:latest

Derleme tamamlandığında, ortamı arka planda çalıştırabilirsiniz:

docker compose up -d
Creating travellist-db    ... done
Creating travellist-app   ... done
Creating travellist-nginx ... done

Bu, konteynerlerinizi arka planda çalıştıracaktır. Aktif servislerinizin durumu hakkında bilgi göstermek için şunu çalıştırın:

docker compose ps

Böyle bir çıktı göreceksiniz:

      Name                    Command              State                  Ports                
-----------------------------------------------------------------------------------------------
travellist-app     docker-php-entrypoint php-fpm   Up      9000/tcp                            
travellist-db      docker-entrypoint.sh mysqld     Up      3306/tcp, 33060/tcp                 
travellist-nginx   nginx -g daemon off;            Up      0.0.0.0:8000->80/tcp,:::8000->80/tcp

Ortamınız artık çalışır durumda.

Artık her konteynerleştirilmiş servisin doğru şekilde yanıt verip vermediğini basit curl komutları kullanarak doğrulayabilirsiniz.

  1. Nginx web servisini test etmek için:

    curl -I http://localhost:8000

    Beklenen çıktı:

    HTTP/1.1 200 OK
    Server: nginx/1.17.10
    Content-Type: text/html; charset=UTF-8

    Bu, Nginx konteynerinin çalıştığını ve Laravel uygulamanızdan yanıtlar sağladığını doğrular.

  2. PHP-FPM'i Nginx üzerinden test etmek için:

    curl -s http://localhost:8000 | grep "Berlin"

    Uygulama düzgün çalışıyorsa, bu komut Berlin kelimesini içeren karşılama sayfasının HTML'sini döndürür.

Uygulamayı kurmayı bitirmek için hâlâ birkaç komut çalıştırmamız gerekiyor. Servis konteynerlerinde komutları çalıştırmak için docker compose exec komutunu kullanabilirsiniz. Örneğin, uygulama dizinindeki dosyalar hakkında detaylı bilgi göstermek için ls -l komutunu kullanabilirsiniz:

docker compose exec app ls -l
total 256
-rw-r--r-- 1 sammy sammy    737 Apr 18 14:21 Dockerfile
-rw-r--r-- 1 sammy sammy    101 Jan  7  2020 README.md
drwxr-xr-x 6 sammy sammy   4096 Jan  7  2020 app
-rwxr-xr-x 1 sammy sammy   1686 Jan  7  2020 artisan
drwxr-xr-x 3 sammy sammy   4096 Jan  7  2020 bootstrap
-rw-r--r-- 1 sammy sammy   1501 Jan  7  2020 composer.json
-rw-r--r-- 1 sammy sammy 179071 Jan  7  2020 composer.lock
drwxr-xr-x 2 sammy sammy   4096 Jan  7  2020 config
drwxr-xr-x 5 sammy sammy   4096 Jan  7  2020 database
drwxr-xr-x 4 sammy sammy   4096 Apr 18 14:22 docker-compose
-rw-r--r-- 1 sammy sammy   1017 Apr 18 14:29 docker-compose.yml
-rw-r--r-- 1 sammy sammy   1013 Jan  7  2020 package.json
-rw-r--r-- 1 sammy sammy   1405 Jan  7  2020 phpunit.xml
drwxr-xr-x 2 sammy sammy   4096 Jan  7  2020 public
-rw-r--r-- 1 sammy sammy    273 Jan  7  2020 readme.md
drwxr-xr-x 6 sammy sammy   4096 Jan  7  2020 resources
drwxr-xr-x 2 sammy sammy   4096 Jan  7  2020 routes
-rw-r--r-- 1 sammy sammy    563 Jan  7  2020 server.php
drwxr-xr-x 5 sammy sammy   4096 Jan  7  2020 storage
drwxr-xr-x 4 sammy sammy   4096 Jan  7  2020 tests
-rw-r--r-- 1 sammy sammy    538 Jan  7  2020 webpack.mix.js

Şimdi uygulama bağımlılıklarını yüklemek için composer install komutunu çalıştıracağız:

docker compose exec app rm -rf vendor composer.lock

docker compose exec app composer install

Böyle bir çıktı göreceksiniz:

No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.
. . .
Lock file operations: 89 installs, 0 updates, 0 removals
  - Locking doctrine/inflector (2.0.4)
  - Locking doctrine/instantiator (1.4.1)
  - Locking doctrine/lexer (1.2.3)
  - Locking dragonmantank/cron-expression (v2.3.1)
  - Locking egulias/email-validator (2.1.25)
  - Locking facade/flare-client-php (1.9.1)
  - Locking facade/ignition (1.18.1)
  - Locking facade/ignition-contracts (1.0.2)
  - Locking fideloper/proxy (4.4.1)
  - Locking filp/whoops (2.14.5)
. . .
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 89 installs, 0 updates, 0 removals
  - Downloading doctrine/inflector (2.0.4)
  - Downloading doctrine/lexer (1.2.3)
  - Downloading dragonmantank/cron-expression (v2.3.1)
  - Downloading symfony/polyfill-php80 (v1.25.0)
  - Downloading symfony/polyfill-php72 (v1.25.0)
  - Downloading symfony/polyfill-mbstring (v1.25.0)
  - Downloading symfony/var-dumper (v4.4.39)
  - Downloading symfony/deprecation-contracts (v2.5.1)
. . .
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.

Uygulamayı test etmeden önce yapmamız gereken son şey, artisan Laravel komut satırı aracıyla benzersiz bir uygulama anahtarı oluşturmaktır. Bu anahtar, kullanıcı oturumlarını ve diğer hassas verileri şifrelemek için kullanılır:

docker compose exec app php artisan key:generate
Application key set successfully.

Şimdi tarayıcınıza gidin ve sunucunuzun alan adını veya IP adresini 8000 numaralı port üzerinden erişin:

http://server_domain_or_IP:8000

Not: Bu demoyu yerel bilgisayarınızda çalıştırıyorsanız, uygulamaya tarayıcınızdan erişmek için http://localhost:8000 adresini kullanın.

Böyle bir sayfa göreceksiniz:

Laravel uygulamasını bir tarayıcı kullanmadan, uygulama konteynerinin içinden de test edebilirsiniz:

docker compose exec app curl http://nginx

Her şey doğru şekilde yapılandırılmışsa, Laravel’in varsayılan karşılama sayfasının HTML çıktısını görmelisiniz.

<html>
<head>
    <title>Travel List</title>
</head>

<body>
    <h1>My Travel Bucket List</h1>
    <h2>Places I'd Like to Visit</h2>
    <ul>
        <li>Berlin</li>
        <li>Budapest</li>
        <li>Denver</li>
        <li>Helsinki</li>
        <li>Lisbon</li>
        <li>Nairobi</li>
        <li>Rio</li>
        <li>Tokyo</li>
    </ul>

    <h2>Places I've Already Been To</h2>
    <ul>
        <li>Cincinnati</li>
        <li>Moscow</li>
        <li>Oslo</li>
    </ul>
</body>
</html>

Hizmetleriniz tarafından oluşturulan günlükleri kontrol etmek için logs komutunu kullanabilirsiniz:

docker compose logs nginx
Attaching to travellist-nginx
. . .
travellist-nginx | 172.24.9.1 - - [18/Apr/2022:14:49:16 +0000] "GET / HTTP/1.1" 200 627 "-" "curl/7.82.0"
travellist-nginx | 172.24.9.1 - - [18/Apr/2022:14:51:27 +0000] "GET / HTTP/1.1" 200 627 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0"
travellist-nginx | 172.24.9.1 - - [18/Apr/2022:14:51:27 +0000] "GET /favicon.ico HTTP/1.1" 200 0 "http://localhost:8000/" "Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0"

Aşağıdaki curl komutunu da günlük girişlerini tetiklemek için kullanabilirsiniz:

curl http://localhost:8000

Sonra tekrar çalıştır:

docker compose logs nginx

Artık isteğiniz için eşleşen bir erişim günlük kaydını görmelisiniz.

Tüm hizmetlerin durumunu koruyarak Docker Compose ortamınızı duraklatmak istiyorsanız, şunu çalıştırın:

docker compose pause
Pausing travellist-db    ... done
Pausing travellist-nginx ... done
Pausing travellist-app   ... done

Ardından hizmetlerinize şunlarla devam edebilirsiniz:

docker compose unpause
Unpausing travellist-app   ... done
Unpausing travellist-nginx ... done
Unpausing travellist-db    ... done

Docker Compose ortamınızı kapatmak ve tüm konteynerlerini, ağlarını ve hacimlerini kaldırmak için şunu çalıştırın:

docker compose down
Stopping travellist-nginx ... done
Stopping travellist-db    ... done
Stopping travellist-app   ... done
Removing travellist-nginx ... done
Removing travellist-db    ... done
Removing travellist-app   ... done
Removing network travellist-laravel-demo_travellist

Tüm Docker Compose komutlarının genel bir bakışı için lütfen Docker Compose komut satırı referansına bakın.

Laravel Dosya ve Dizin İzinlerini Düzeltme

Laravel'ı Docker konteynerleri içinde çalıştırırken, geliştiricilerin karşılaştığı en yaygın sorunlardan biri dosya ve dizin izinleriyle ilgilidir. Laravel, app konteyneri içindeki PHP-FPM sürecinin bazı dizinlere yazabilir olmasını gerektirir. Bu dizinler yazılabilir değilse, uygulama hataları kaydetmede, yapılandırma dosyalarını önbelleğe almada veya oturum verilerini doğru şekilde saklamada başarısız olabilir.

Şu tür mesajlarla karşılaşabilirsiniz:

The stream or file "/var/www/storage/logs/laravel.log" could not be opened in append mode: failed to open stream: Permission denied

veya

file_put_contents(/var/www/bootstrap/cache/config.php): Failed to open stream: Permission denied

Bu hatalar, konteyner içinde PHP çalıştıran kullanıcının gerekli dizinlere yazma iznine sahip olmadığını gösterir.

Aşağıdaki adımlar, bu sorunları hem yerel hem de üretim ortamlarında nasıl teşhis edip düzelteceğinizi açıklar.

Yazılabilir Dizin İzinlerini Ayarla

Laravel, web sunucusu kullanıcısı tarafından yazılabilir iki dizin gerektirir:

  • /var/www/storage
  • /var/www/bootstrap/cache

İzin hataları görüyorsanız, bunları konteynerin içinden sahiplik ve izinleri ayarlayarak düzeltebilirsiniz:

docker compose exec app chown -R sammy:sammy /var/www/storage /var/www/bootstrap/cache

docker compose exec app chmod -R 775 /var/www/storage /var/www/bootstrap/cache

Dockerfile'ınız farklı bir kullanıcı adı kullanıyorsa (user yapı argümanı ile tanımlanmış), sammyyi o kullanıcı adıyla değiştirin.

Sahipliği aşağıdaki komutla doğrulayabilirsiniz:

docker compose exec app ls -ld /var/www/storage /var/www/bootstrap/cache

Çıktı, kullanıcının ve grubun konteyner içindeki PHP-FPM süreciyle eşleştiğini göstermelidir.

Bunun yerine izinleri ana makineden ayarlamak istiyorsanız, şunu çalıştırın:

sudo chown -R $(id -u):$(id -g) storage bootstrap/cache

sudo chmod -R 775 storage bootstrap/cache

Bu, hem Laravel dizinlerinin konteyner hem de sizin ana kullanıcı tarafından yazılabilir olmasını sağlar.

Görüntü Oluşturma Sırasında Dizin Sahipliğini Ayarlama

Her yeniden derlemeden sonra izin sorunlarının tekrar etmesini önlemek için, Docker imajı oluşturma sürecinde dizin sahipliğini ayarlayabilirsiniz. Dockerfile dosyanızı açın ve kök olmayan kullanıcıya geçmeden önce sonlara doğru şu satırı ekleyin:

RUN chown -R $user:www-data /var/www

Bu komut, /var/www altındaki tüm dosya ve dizinlerin Dockerfile'ınızda belirttiğiniz kullanıcıya ait olmasını sağlar.

Daha hedeflenmiş ve güvenli bir yaklaşım için, Laravel'in yazması gereken dizinleri yalnızca ayarlayabilirsiniz:

RUN chown -R $user:www-data /var/www/storage /var/www/bootstrap/cache

Bu değişikliği yaptıktan sonra, imajı yeniden oluşturun ve konteynerları yeniden başlatın:

docker compose build app

docker compose up -d

Konteyner bir sonraki kez başladığında, gerekli izinlere zaten sahip olacak.

Ana Bilgisayar ve Konteyner Arasındaki Kullanıcı Kimliği Tutarlılığını Doğrula

Bind mount kullanırken, Docker dosyaları hostunuz ile konteyner arasında paylaşır. Eğer host kullanıcısının sayısal UID'si konteynerde tanımlanan UID'den farklıysa, dosya sahipliği çatışmaları meydana gelebilir. Bu, kullanıcı kimliklerinin makineler arasında değiştiği sistemlerde yaygındır.

UID değerlerini doğrulamak için şunu çalıştırın:

id -u $(whoami)

docker compose exec app id -u

Bu iki sayı eşleşmiyorsa, gerçek UID'nizi yapı sürecine geçirmek için docker-compose.yml dosyanızı güncelleyin:

build:
  args:
    user: sammy
    uid: 1000

Sonra görüntüyü yeniden oluşturun:

docker compose build app

docker compose up -d

Bu, konteyner kullanıcısının ana makine kullanıcınızla eşleşmesini sağlar ve bağlanmış dosyalarda izin uyuşmazlıklarını önler.

Composer Önbellek Dizin İzinlerini Düzelt

Composer, indirilen bağımlılıkları ve önbellek dosyalarını konteyner içindeki kullanıcının ev dizininde saklar (/home/sammy/.composer). Eğer bu dizin yazılabilir değilse, şu tür uyarılar görebilirsiniz:

Cannot create cache directory /home/sammy/.composer/cache

Bunu, önbellek dizinini oluşturarak ve doğru sahipliği atayarak düzeltebilirsiniz:

docker compose exec app mkdir -p /home/sammy/.composer/cache

docker compose exec app chown -R sammy:sammy /home/sammy/.composer

Bu, Composer'ın bağımlılıkları ve önbellek dosyalarını izin hatası olmadan saklayabilmesini sağlar, bu da gelecekteki composer install çalıştırmalarını hızlandırır.

Üretim Ortamları için Önerilen Uygulamalar

Üretimde, izin yönetimi çalışma zamanında değil, görüntü oluşturulurken ele alınmalıdır. Bu, tutarlılığı ve güvenliği artırır.

Üretim için hazır bir yapılandırma için bu uygulamaları takip edin:

  • Uygulama kodu için bind mount kullanımından kaçının: Bunun yerine, uygulama dosyalarını derleme sırasında imajın içine kopyalayın. Bind mountlar yalnızca geliştirme için kullanılmalıdır.

  • Derleme sırasında izinleri ayarlayın: Doğru sahiplik ve erişim haklarını dağıtımdan önce sağlamak için Dockerfile dosyanıza RUN chown ve chmod komutlarını ekleyin.

  • En az ayrıcalıklı kullanıcıyı kullanın: PHP sürecinin root olmayan bir kullanıcı altında çalıştığından emin olun (mevcut ayar $user bunu zaten yapıyor).

  • Yazılabilir olmayan dizinlerde izinleri kısıtlayın: Çoğu uygulama dosyası yalnızca okunabilir olmalıdır. Bunu şu şekilde uygulayabilirsiniz:

    chmod -R 755 /var/www
    
    chmod -R 775 /var/www/storage /var/www/bootstrap/cache

    Bu yapılandırma, Laravel'in sadece gerektiğinde yazmasını sağlarken diğer uygulama dosyalarının değiştirilmesini korur.

Bu adımları izleyerek, Laravel konteynerleriniz hem yerel hem de üretim ortamlarında kararlı ve öngörülebilir dosya izinlerine sahip olacaktır. Bu, tekrarlayan hataları azaltır ve Laravel'in verileri kaydetmesini, dosyaları önbelleğe almasını ve oturumları güvenilir şekilde yazmasını sağlar.

Yerel ve Üretim Ortamları için docker-compose.override.yml Dosyasını Anlamak

Docker Compose, çoklu konteyner uygulamalarını tek bir yapılandırma dosyası kullanarak tanımlamanıza olanak tanır. Çoğu durumda, bu yapılandırma docker-compose.yml adlı bir dosyada saklanır. Ancak, yerel geliştirme ve üretim gibi birden çok ortamı yönetmek genellikle ortama özgü ayarlamalar gerektirir. İşte bu noktada docker-compose.override.yml dosyası işe yarar.

Varsayılan olarak, Docker Compose otomatik olarak temel Compose dosyasıyla aynı dizinde docker-compose.override.yml adında bir dosya arar. Bulunduğunda, herhangi bir docker compose komutunu çalıştırdığınızda her iki dosyadaki yapılandırmaları birleştirir. Bu yaklaşım, geliştiricilerin temiz ve yeniden kullanılabilir bir temel yapılandırmayı korurken, yerel veya üretim kullanımı için kurulumun belirli yönlerini özelleştirmelerine olanak tanır.

docker-compose.override.yml Dosyasının Rolü

docker-compose.override.yml dosyası, temel Compose yapılandırmasının bir uzantısıdır. Sadece belirli bir ortama uygulanacak özellikleri değiştirir veya ekler ve ana docker-compose.yml dosyasında değişiklik yapılmasını gerektirmez. Override dosyası, geliştiricilerin kaynak kodunu monte etmeleri, hata ayıklama araçlarını etkinleştirmeleri veya test için ortam değişkenlerini değiştirmeleri gerektiren yerel geliştirme iş akışlarında özellikle kullanışlıdır.

docker compose up komutunu çalıştırdığınızda, Docker otomatik olarak docker-compose.yml ve docker-compose.override.yml dosyalarının içeriklerini birleştirir. Eğer bir servis, hacim veya yapılandırma her iki dosyada da tanımlanmışsa, override dosyasındaki değerler öncelik kazanır.

Farklı bir override dosyası kullanmak için, örneğin üretim için hazırlanmış bir dosya, dosyayı manuel olarak -f bayrağı ile belirtebilirsiniz:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Bu yaklaşım, tek bir tutarlı komut yapısını kullanırken farklı ortamlar için ayrı yapılandırmaları korumanıza olanak tanır.

Yerel Kalkınma için Yapılandırma

Yerel geliştirme ortamları genellikle sık değişiklikler, canlı kod yeniden yükleme ve ayrıntılı günlük kaydı gerektirir. docker-compose.override.yml dosyası, bu özellikleri diğer ortamları etkilemeden eklemeyi mümkün kılar.

Tipik bir yerel geçersiz kılma dosyası şunları içerebilir:

  • Canlı kod güncellemeleri için bağlama noktaları
  • Önceden oluşturulmuş görüntüler yerine yerel derleme yönergeleri
  • Geliştirme özelinde ortam değişkenleri
  • Hata ayıklama ve test hizmetleri

Örneğin:

version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - ./src:/app
      - ./logs:/app/logs
    environment:
      - DATABASE_URL=postgres://postgres:postgres@db:5432/app_db
      - LOG_LEVEL=debug
    ports:
      - "8000:8000"
    depends_on:
      - db

  db:
    environment:
      - POSTGRES_PASSWORD=postgres

Bu örnekte:

  • app servisi, yerel bir Dockerfile'dan (Dockerfile.dev) doğrudan oluşturulur, böylece değişiklikleri bir registry üzerinden imajları yeniden oluşturmadan test etmek kolay olur.
  • Kaynak kod, bind mount kullanılarak bağlanır, bu sayede konteyner yerel dosya değişikliklerini gerçek zamanlı olarak yansıtabilir.
  • Geliştirme sırasında daha ayrıntılı çıktılar sağlamak için LOG_LEVEL=debug gibi ortam değişkenleri ayarlanır.
  • Veritabanı, yerel testler için uygun olan basit kimlik bilgilerini kullanıyor ancak üretimde kullanılmamalıdır.

Bu yapılandırma, temel yapılandırmayı değiştirmeden daha hızlı ve esnek bir geliştirme ortamı sağlar.

Prodüksiyona Hazırlık

Üretim ortamları, istikrar, tekrarlanabilirlik ve güvenlik gerektirir. Konteynerler, yerel olarak oluşturulmak yerine önceden hazırlanmış imajlardan çalıştırılmalıdır ve hassas bilgiler güvenli bir şekilde yönetilmelidir. Yerel geliştirme için otomatik olarak uygulanan geçersiz kılma dosyasının aksine, üretim geçersiz kılmaları açıkça belirtilmelidir.

Bir üretim geçersiz kılma dosyası (docker-compose.prod.yml) genellikle yerel derleme adımlarını görüntü referanslarıyla değiştirmek, bağlama noktaları yerine adlandırılmış hacimler kullanmak ve kaynak sınırları ve ölçeklendirme gibi dağıtım ile ilgili özellikleri tanımlamak için tasarlanır.

Örnek:

version: "3.9"

services:
  app:
    image: registry.example.com/myapp:1.2.3
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - LOG_LEVEL=info
    volumes:
      - app-data:/var/lib/app
    ports:
      - "8000:8000"
    restart: unless-stopped
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.5"
          memory: 512M

  db:
    image: postgres:15
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password

secrets:
  db_password:
    external: true

volumes:
  app-data:
  db-data:

Bu yapılandırmada:

  • app servisi, yerel olarak inşa etmek yerine bir kayıtta saklanan önceden oluşturulmuş bir imajı (myapp:1.2.3) referans alır.

  • Bağlama noktaları, tutarlı ve izole veri depolamayı sağlamak için isimlendirilmiş hacimlerle değiştirilir.

  • Hassas kimlik bilgileri Docker sırları kullanılarak güvenli bir şekilde saklanır.

  • deploy bölümü, yük altında daha iyi kararlılık için ölçeklendirme davranışını ve kaynak sınırlarını tanımlar.

  • Yapılandırma, dağıtım sırasında açıkça uygulanmalıdır:

    docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Bu yapı, üretim dağıtımlarının tutarlı, kontrollü olmasını ve yerel geliştirme öğelerinden arınmış olmasını sağlar.

Yerel ve Üretim Geçersiz Kılmalarını Karşılaştırma

Yapılandırma ÖzelliğiYerel (docker-compose.override.yml)Üretim (docker-compose.prod.yml)
Otomatik olarak uygulandıEvetHayır (kullanmak için -f gereklidir)
Kaynak kod yönetimiCanlı güncellemeler için bağlama noktalarıKayıttan önceden oluşturulmuş görüntüler
Çevre değişkenleriGeliştirici odaklı değerlerProdüksiyon güvenli değerler
Kayıt TutmaAyrıntılı (debug)Kısa (info veya error)
Gizli bilgi yönetimiDüz değerler veya .envDocker sırları veya yönetilen sırlar
ÖlçeklemeTek konteynerBirden çok kopya
Performans ayarlamasıGerekli değilKaynak sınırları tanımlandı
Hata ayıklama araçlarıEtkinEngelli
Yeniden başlatma politikasıManuel veya yok
restart: unless-stopped

Birden fazla Docker Compose dosyası kullanmak, ortam özelindeki yapılandırmaları temel kurulumdan ayırmanın temiz bir yolunu sağlar. Temel docker-compose.yml, ortak uygulama yapısını tanımlarken, docker-compose.override.yml yerel geliştirme için bunu ayarlar. Üretime dağıtırken, açık bir override dosyası tahmin edilebilir ve güvenli bir yapılandırma sağlar.

Bu desen, ekiplerin tutarlı bir iş akışını sürdürmesine olanak tanır. Geliştiriciler uygulamayı yerel olarak başlatmak için şunu kullanabilir:

docker compose up

ve üretime dağıtmak için kullan:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Her iki ortam da aynı temel yapılandırmayı paylaşırken, kendi özel gereksinimlerine uygun bağımsız ayarları korur. Bu ayrım, bakım kolaylığını artırır, yapılandırma hatalarını azaltır ve yerel ile üretim ortamlarının uyumlu kalmasını sağlar.

Ortamınızı Sorun Giderme

Bu bölüm, uygulamanızı Docker Compose ile çalıştırırken karşılaşabileceğiniz yaygın sorunlar için sorun giderme adımlarını sağlar.

502 Bad Gateway (Nginx 502 yanıtları gösteriyor)

Belirtiler:

  • Tarayıcı http://localhost:8000 adresine erişirken 502 Bad Gateway hatası gösteriyor.
  • Nginx loglarında recv() failed (111: Connection refused) veya connect() failed (111: Connection refused) while connecting to upstream gibi satırlar bulunur.

Muhtemel nedenler:

  • PHP-FPM (yani app servisi) çalışmıyor veya beklenen soket/port üzerinde dinlemiyor.
  • Nginx fastcgi_pass yanlış host/porta işaret ediyor.
  • nginx ile app arasında konteyner adı uyumsuzluğu veya ağ sorunları.
  • app başlatılırken çöktü (örneğin, eksik eklentiler veya yapılandırma hataları nedeniyle).

Nasıl onaylanır:

  1. Nginx günlüklerini inceleyin:

    docker compose logs nginx | tail -n 100

    502 mesajlarını veya üst akış bağlantı hatalarını arayın.

  2. app konteyner durumunu ve günlüklerini kontrol edin:

    docker compose ps app
    
    docker compose logs app --tail=200
  3. app konteyneri içinde PHP-FPM'in dinlediğini doğrulayın:

    docker compose exec app ss -ltnp | grep :9000 || docker compose exec app ss -lxp | grep php-fpm

    Beklenen: ya 9000 portunda bir TCP dinleyicisi ya da nginx tarafından kullanılan bir Unix soketi (bu compose ile daha az yaygın).

  4. Compose ağında nginx'in app'i çözümleyip erişebildiğini doğrulayın:

    docker compose exec nginx ping -c 2 app
    
    docker compose exec nginx wget -qO- --timeout=2 http://app:9000/ || true

    Eğer ping başarısız olursa veya wget zaman aşımına uğrarsa, ağda veya servis adında bir uyuşmazlık vardır.

Yaygın düzeltmeler:

  • PHP-FPM çalışmıyorsa, ölümcül PHP hataları veya başlatma hataları için app günlüklerini inceleyin. Temel hatayı düzeltin (genellikle eksik eklenti veya izin sorunu) ve yeniden başlatın:

    docker compose restart app
    
    docker compose logs app --tail=100
  • Eğer Nginx yapılandırması farklı bir upstream kullanıyorsa (ör. fastcgi_pass 127.0.0.1:9000;), docker-compose/nginx/travellist.conf dosyasını fastcgi_pass app:9000; olarak güncelleyin (compose servis adınızla eşleşecek şekilde) ve Nginx'i yeniden yükleyin:

    # After editing config in host
    
    docker compose exec nginx nginx -s reload
    
    docker compose logs nginx --tail=50
  • Eğer PHP-FPM bir Unix soketi üzerinde dinliyorsa (örneğin, /run/php/php7.4-fpm.sock) ama Nginx TCP bekliyorsa veya tam tersi durum varsa, Compose kurulumları için TCP app:9000 olarak standartlaştırın veya konteynerler arasında soketi doğru şekilde bağlayın (önerilen: çoklu konteyner Compose’da TCP kullanmak).

Eksik PHP uzantıları (Composer veya Artisan çalıştırılırken hatalar veya çalışma zamanı hataları)

Belirtiler:

  • Composer veya Laravel komutları çalıştırılırken Class "PDO" not found, ext-pdo is missing veya Call to undefined function json_encode gibi mesajlar alırsınız.
  • app günlüklerinde tanımsız sınıflara veya işlevlere atıfta bulunan PHP hataları.

Nasıl onaylanır:

  1. app konteynerinin PHP modül listesini inceleyin:

    docker compose exec app php -m | sort

    İhtiyacınız olan uzantı isimlerini arayın (Laravel için tipik uzantılar: pdo_mysql, mbstring, bcmath, openssl, tokenizer, xml, ctype, json, gd).

  2. Genellikle gerekli uzantıları belirten composer hata metnini kontrol edin:

    docker compose exec app composer install

Düzeltmeler:

  • Dockerfile'ınıza eksik uzantıları ekleyin ve imajı yeniden oluşturun. Örnek ( pdo_mysql ve gd için):

    RUN apt-get update && apt-get install -y libpng-dev libxml2-dev \
        && docker-php-ext-install pdo_mysql mbstring xml gd

    Sonra yeniden oluşturun ve yeniden başlatın:

    docker compose build app
    
    docker compose up -d app
    
    docker compose logs app --tail=100
  • Eğer bir eklenti sistem kütüphanelerine ihtiyaç duyuyorsa (örneğin gd için libpng-dev gerekliyse), docker-php-ext-install komutundan önce ilgili apt-get install satırlarını ekleyin.

  • Yerleşik olmayan bir PHP uzantısına ihtiyacınız varsa, PECL üzerinden yüklemeyi düşünün ve php.ini dosyasında etkinleştirin.

Veritabanı bağlantı hataları (Laravel MySQL'e bağlanamıyor)

Belirtiler:

  • Laravel veritabanı bağlantı hatalarını gösterir (SQLSTATE[HY000] [2002] Bağlantı reddedildi veya Kullanıcı için erişim reddedildi).
  • Göçler başarısız olur veya uygulama veri odaklı sayfalar için boş bir sayfa gösterir.

Nasıl onaylanır:

  1. DB konteyner günlüklerini başlatma hataları için kontrol edin:

    docker compose logs db --tail=200
  2. DB kapsayıcısının çalıştığını doğrulayın:

    docker compose ps db
  3. Kimlik bilgileri ve sunucunun .env ile docker-compose.yml arasında tutarlı olduğunu doğrulayın. Laravel .env dosyasında, DB_HOST compose servis adı (db) olmalıdır, 127.0.0.1 veya localhost değil:

    DB_HOST=db
    DB_PORT=3306
    DB_DATABASE=travellist
    DB_USERNAME=travellist_user
    DB_PASSWORD=password
  4. İçeriden db konteyneri testi:

    docker compose exec db mysql -u"${DB_USERNAME}" -p"${DB_PASSWORD}" -e "SHOW DATABASES;"

    Veya, ağ bağlantısını doğrulamak için app konteynerinden test yapın:

    docker compose exec app sh -c 'which mysql >/dev/null 2>&1 || echo "no mysql client"; mysql -h db -u"${DB_USERNAME}" -p"${DB_PASSWORD}" -e "SELECT VERSION();"'

Yaygın nedenler ve çözümler:

  • .env dosyasında yanlış sunucu: DB_HOST=db olarak güncelleyin, konteynerleri yeniden oluşturun veya ortam değişkenlerini yeniden yükleyin:

    docker compose down
    
    docker compose up -d
    
    docker compose exec app php artisan migrate
  • Uygulama başlarken veritabanı hazır değil: Uygulamanızda bir tekrar deneme mekanizması kullanın veya bir sağlık kontrolü ekleyip sıralamaya başlayın. Ayrıca manuel olarak da bekleyebilirsiniz:

    docker compose exec db mysqladmin -u"${DB_USERNAME}" -p"${DB_PASSWORD}" ping
    
    # expected: mysqld is alive
  • Başlatma SQL'i başarısız: ./docker-compose/mysql altındaki dosyaları kontrol edin, bir sözdizimi veya izin hatası veritabanı başlatılmasını durdurabilir. İçe aktarma hataları için veritabanı loglarını inceleyin.

  • İzinler/hatalı kimlik bilgileri: veritabanı ve kullanıcının mevcut olduğunu doğrulayın. Veritabanını yeniden oluşturun veya izinleri vermek için SQL çalıştırın.

  • Host düzeyinde port kullanımı: Eğer curl telnet://localhost:3306 komutunu denediyseniz ve db servisi bir host portu açmıyorsa, bu bağlantı tasarım gereği reddedilecektir. Testleri konteynerlerin içinden yapın veya yalnızca yerel hata ayıklama için docker-compose.yml dosyasında portu açın (ports: - "3306:3306").

İzin ve dosya sistemi sorunları (depolama, önbellek, günlük dosyaları)

Belirtiler:

  • Laravel, önbellek veya log yazmayla ilgili istisnalar fırlatır: "/var/www/storage/logs/laravel.log" dosyası veya akışı açılamadı.
  • Kapsayıcı süreçleri tarafından oluşturulan dosyalar root tarafından sahiplenilir ve ana bilgisayar kullanıcı tarafından yazılabilir değildir.

Nasıl onaylanır:

  1. Ana makineden dosya izinlerini kontrol edin:

    ls -l
    
    ls -l storage bootstrap/cache
    
    docker compose exec app id -u   # user id inside container
  2. Kapsayıcı günlüklerini izin hataları için inceleyin.

Düzeltmeler:

  • Dockerfile'ın, ana bilgisayar kullanıcınızla eşleşen kullanıcıyı ve UID'yi ayarladığından emin olun (Dockerfile'ınız zaten ARG uid ile bir kullanıcı oluşturuyor). docker-compose.yml içindeki build argümanlarındaki uid'nin ana bilgisayar UID'nizle eşleştiğini doğrulayın (genellikle 1000).

  • Sahipliği hosttan veya konteynerin içinden düzeltin:

    docker compose exec app chown -R sammy:sammy /var/www/storage /var/www/bootstrap/cache

    veya sunucudan:

    sudo chown -R $(id -u):$(id -g) storage bootstrap/cache
  • Geliştirme sırasında bağlama montajları izin uyumsuzluklarına neden olabilir; konteynerleri başlattıktan sonra sahipliği ayarlamak için konteyner komutlarını çalıştırmayı tercih edin.

Composer veya satıcıyla ilgili hatalar

Belirtiler:

  • composer install, app içinde başarısız olursa veya vendor klasörü eksikse sınıf bulunamaz.
  • composer kapsayıcıda bellek tükenmesi bildiriyor.

Nasıl onaylanır:

  • app konteynırı içinde composer çalıştırın ve çıktıyı yakalayın:

    docker compose exec app composer install --no-interaction --prefer-dist

Düzeltmeler:

  • Eğer composer bellek limitlerine ulaşılırsa, daha fazla bellek verin veya composer çevresel değişkenini ayarlayın:

    docker compose exec app php -d memory_limit=-1 /usr/bin/composer install
  • Composer çalıştırılabilir dosyasının mevcut olduğundan emin olun (Dockerfile'ınız composer'ı kopyalıyor). Eğer yoksa, composer katmanı ile imajı yeniden oluşturun.

  • Eğer vendor bind mount overlay nedeniyle eksikse, bind mount'un imajda oluşturulan vendor klasörünü gizlemediğinden emin olun. En iyi uygulama, konteynerler çalıştıktan sonra composer install komutunu çalıştırmaktır, böylece vendor bağlanan klasörde oluşturulur; ya da üretim için vendor klasörünü imaj oluşturma sırasında konteynere ekleyin.

Uygulama boş sayfa döndürüyor veya 500 hatası veriyor

Belirtiler:

  • Tarayıcı boş bir yanıt veya HTTP 500 gösteriyor.
  • Nginx günlükleri 200 gösteriyor ama sayfa boş; app günlükleri ölümcül hatalar gösteriyor.

Nasıl onaylanır:

  • Uygulama günlüklerini inceleyin:

    docker compose exec app tail -n 200 storage/logs/laravel.log
    
    docker compose logs app --tail=200
  • Ortamı kontrol edin: .env dosyasındaki APP_DEBUG=true, Laravel'in geliştirme sırasında ayrıntılı hata göstermesini sağlar (prodüksiyonda etkinleştirmeyin).

Düzeltmeler:

  • laravel.log dosyasında gösterilen temel hatayı düzeltin.

  • .env dosyasının var olduğundan ve APP_KEY'in ayarlandığından emin olun:

    docker compose exec app php artisan key:generate
  • Göçleri çalıştır, önbelleği temizle:

    docker compose exec app php artisan migrate --force
    
    docker compose exec app php artisan config:cache
    
    docker compose exec app php artisan route:cache
    
    docker compose exec app php artisan view:cache

Konteyner oluşturma hataları (docker compose build sırasında hatalar)

Belirtiler:

  • docker compose build, apt veya build adımı hatalarıyla başarısız oluyor.

Nasıl onaylanır:

  • Günlüklerle birlikte derlemeyi yeniden çalıştır:

    docker compose build app

Düzeltmeler:

  • Başarısız olan adım çıktısını okuyun. Tipik sorunlar:

    • Derleme ortamında Ağ/DNS: tekrar deneyin veya Docker'ın internete erişebildiğinden emin olun.
    • Eksik paketler veya yanlış apt kaynağı: Yüklemeden önce apt-get update ekleyin ve sonrasında apt listelerini temizleyin.
    • İmaj içine kopyalanan dosyalardaki izinler: doğru COPY yollarını sağlayın.

Sağlam kurulumlar için örnek Dockerfile parçacığı:

RUN apt-get update && apt-get install -y --no-install-recommends \
    libpng-dev libxml2-dev zip unzip \
  && docker-php-ext-install pdo_mysql mbstring xml gd \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

Konteynerler arasında ağ veya DNS sorunları

Belirtiler:

  • Bir konteyner başka bir konteynere servis adıyla ulaşamaz (örneğin, app db:3306 ile db'ye ulaşamaz).
  • ping servis adıyla başarısız oluyor.

Nasıl onaylanır:

docker compose exec app ping -c 3 db

docker compose exec app getent hosts db

docker network inspect travellist

Düzeltmeler:

  • Hizmetlerin docker-compose.yml dosyasında aynı ağı paylaştığını doğrulayın (her ikisi de travellist'i referans göstermelidir).

  • Eğer servis adlarını veya ağ adlarını değiştirdiyseniz, DB_HOST ve Nginx upstreamlerini buna göre güncelleyin.

  • Ağı yeniden oluştur (bu geçici konteynerleri yok eder, dikkatli olun):

    docker compose down
    
    docker network prune   # optional and destructive
    
    docker compose up -d

Faydalı tanı komutları

Bunları hedefli inceleme için çalıştırın:

  • Çalışan konteynerleri ve portları göster:

    docker compose ps
  • Bir hizmet için son kayıtlar:

    docker compose logs -f --tail=200 nginx
  • Bir konteyner içinde bir shell çalıştırın:

    docker compose exec app sh
    
    docker compose exec db bash
  • Konteyner ortam değişkenlerini inceleyin:

    docker compose exec app env | sort
    
    docker compose exec db env | grep MYSQL
  • Konteyner işlem listesini kontrol et:

    docker compose exec app ps aux
    
    docker compose exec app ss -ltnp
  • MySQL sağlığını kontrol et:

    docker compose exec db mysqladmin -u"${DB_USERNAME}" -p"${DB_PASSWORD}" ping

Hata ayıklama için ana bilgisayar portları ne zaman açılmalı

Varsayılan olarak bu kurulumdaki db servisi host portlarına bağlı değildir. Portları yalnızca yerel hata ayıklama için açın. MySQL'i geçici olarak host üzerinde açmak için docker-compose.yml dosyasında db altına ekleyin:

ports:
  - "3306:3306"

Sonra yeniden başlat:

docker compose up -d db

Hata ayıklamadan sonra, veritabanını ağa açmamak için ports eşlemesini kaldırın.

SSS

1. Ana bilgisayarda Composer kurulması gerekiyor mu?

Hayır. Composer’ı tamamen uygulama konteyneri içinde çalıştırabilirsiniz; bu, bu Compose iş akışı için önerilen yaklaşımdır. Kılavuzdaki Dockerfile Composer’ı içeriyor, bu yüzden docker compose exec app composer install veya docker compose run --rm app composer require vendor/package gibi komutları çalıştırın. Composer’ı konteyner içinde çalıştırmak, doğru PHP sürümü ve eklentilerin kullanılmasını sağlar ve ana sistemde uyumsuz sahipliklere sahip vendor dosyalarının oluşmasını engeller.

2. Laravel göçlerini kapsayıcı içinden çalıştırabilir miyim?

Evet. Migration işlemlerini uygulama konteynerinden çalıştırın, böylece uygulamanızla aynı çalışma zamanı ve ağı kullanırlar: docker compose exec app php artisan migrate. Tek seferlik veya CI görevleri için docker compose run --rm app php artisan migrate --force komutunu kullanabilirsiniz. Migration'ları çalıştırmadan önce, veritabanı konteynerinin sağlıklı olduğundan ve bağlantıları kabul ettiğinden emin olun; örneğin docker compose exec db mysqladmin -u$ {DB_USERNAME} -p$ {DB_PASSWORD} ping komutuyla veya yanıt verene kadar tekrar deneyerek.

3. Ek PHP uzantılarını nasıl yüklerim?

Gerekli sistem paketlerini ve uzantı komutlarını Dockerfile'a ekleyin, ardından imajı yeniden oluşturun. Örneğin, bağımlılıkları yüklemek ve yaygın uzantıları derlemek için şunları kullanabilirsiniz:

RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev \
  && docker-php-ext-configure gd --with-jpeg \
  && docker-php-ext-install pdo_mysql mbstring gd

Bir PECL uzantısına ihtiyacınız varsa pecl install ve docker-php-ext-enable kullanın. Dockerfile’ı düzenledikten sonra değişiklikleri uygulamak için docker compose build app ve ardından docker compose up -d app komutlarını çalıştırın.

4. Nginx konteynerini HTTPS için nasıl açabilirim?

Nginx konfigürasyonunuza bir HTTPS sunucu bloğu ekleyin, sertifikanızı ve anahtarınızı konteynıra bağlayın ve docker-compose'ta 443 numaralı portu yayınlayın. İşte docker-compose.yml dosyasında minimal değişikliklerle bir örnek:

services:
  nginx:
    ports:
      - "8000:80"
      - "443:443"
    volumes:
      - ./docker-compose/nginx:/etc/nginx/conf.d
      - ./certs:/etc/nginx/certs:ro

Daha sonra /etc/nginx/certs/fullchain.pem ve /etc/nginx/certs/privkey.pem dosyalarını referans alan bir SSL sunucu bloğu ekleyin. Üretim ortamında, özel anahtarları uygulama katmanında saklamak yerine sertifikaları güvenli bir şekilde almak ve yenilemek için Traefik gibi bir ters proxy veya sertifika yöneticisi ya da harici bir yük dengeleyici kullanın.

5. Kod değişikliklerinden sonra Laravel'i nasıl yeniden oluşturabilirim?

Ne değiştiğine bağlı. Proje dizini için bind mount kullanıyorsanız, PHP, şablonlar, rotalar ve kontrollerde yapılan değişiklikler hemen yansır; gerekirse Laravel önbelleğini temizleyin, örneğin:

docker compose exec app php artisan config:clear

docker compose exec app php artisan route:clear

docker compose exec app php artisan view:clear

Eğer composer bağımlılıklarını veya Dockerfile’ı (yeni eklentiler veya sistem paketleri) değiştirdiyseniz, yeniden yükleyin veya yeniden oluşturun: Bağımlılık değişiklikleri için docker compose exec app composer install, Dockerfile değişiklikleri için docker compose build app && docker compose up -d. PHP-FPM veya Nginx yapılandırmasını değiştirdiğinizde etkilenen servisi yeniden başlatın: docker compose restart app veya docker compose restart nginx.

Sonuç

Bu rehberde, altyapımızı bir YAML dosyasında tanımlamak için Docker Compose kullanarak üç konteynerli bir Docker ortamı kurduk.

Bu noktadan itibaren, Laravel uygulamanız üzerinde geliştirme ve test için yerel bir web sunucusu kurmanıza ve ayarlamanıza gerek duymadan çalışabilirsiniz. Ayrıca, kolayca çoğaltılabilen ve dağıtılabilen geçici bir ortamla çalışıyor olacaksınız; bu durum, uygulamanızı geliştirirken ve üretim ortamına geçerken faydalı olabilir.

Daha fazla Laravel eğitimi için, aşağıdaki makalelere göz atın:

  • Laravel uygulamanızı Linux-Console.net üzerinde barındırın
  • Ubuntu'da Nginx ile Laravel Nasıl Kurulur ve Yapılandırılır (LEMP Yığını)
  • Laravel Eloquent API Kaynakları ile Modelleri JSON'a Dönüştürme Nasıl Yapılır
  • Docker Compose ile Yeni Bir Laravel Uygulamasını Başlatma

Linux-Console.net Topluluğu ile öğrenme sürecine katıldığınız için teşekkürler.