Üretimdeki PHP için Docker: iş uygulamaları için pratik bir kurulum kılavuzu
Docker bir PHP uygulamasının dizüstü bilgisayarda, test sırasında ve üretimde aynı şekilde çalışmasını sağlar ve sürüm ve geri alma işlemlerini stresli olmak yerine rutin hale getirir. Aynı zamanda hareketli parçalar da ekler ve dikkatsiz bir kurulum, sıradan bir sunucudan daha kötüdür. Bu kılavuz, gerçek yapılandırmayla birlikte üretime hazır bir temel çizgiyi ve ne zaman uğraşmamanız gerektiğini gösterir.
Kısaca
- Aynı ortamlar, hızlı geri almalar ve kolay katılım istiyorsanız Docker buna değer. Paylaşımlı barındırmayla ilgili tek bir küçük broşür sitesi için bu genellikle gereksizdir.
- CI'da değişmez bir görüntü oluşturun: çok aşamalı yapı, yalnızca üretim bağımlılıkları, root olmayan kullanıcı, OPcache açık, hatalar günlüğe kaydedilir ve hiçbir zaman görüntülenmez.
- Resim etiketine (Git taahhüt karması) göre serbest bırakın, başlangıçtan sonra durumu kontrol edin ve kontrol başarısız olursa otomatik olarak önceki etikete geri dönün.
- Konteynerler tek kullanımlıktır, veriler ise tek kullanımlık değildir: veritabanını ve yüklemeleri birimlerde veya yönetilen hizmetlerde saklayın ve bunları Docker dışında yedekleyin.
- En yaygın üretim hataları: görüntüdeki sırlar, yayınlanmış veritabanı bağlantı noktaları, sınırsız günlükler ve sahip olduğunuz belleğe uymayan php-fpm sınırları.
Birçok iş uygulamasında barındırmanın gerçek maliyeti sunucu değil, insanlardır. Yalnızca bir geliştiricinin anlayabileceği kurulum, FTP üzerinden yapılan dağıtım ve üretimden farklı davranan bir hazırlama ortamı, uzun vadede pahalıdır. Konteynerler, uygulamayı tam PHP sürümü ve uzantılarıyla paketleyerek bu sorunu çözer. Kendi PHP sitelerimizi ve istemci sistemlerimizi Docker tabanlı bir yığın üzerinde çalıştırıyoruz ve bu kılavuz, başladığımız temel çizgiyi açıklamaktadır.
Docker buna değdiğinde ve değmediğinde
- Buna değer: çeşitli hizmetler (web, PHP, veritabanı, kuyruk), birden fazla geliştirici, tekrarlanabilir sürümlere ve hızlı geri almalara ihtiyaç veya sağlayıcılar arasında geçiş yapma planı.
- Gerekli değil: Yönetilen barındırmada nadiren değişen basit bir içerik sitesi. Daha fazla karmaşıklığın bir getirisi olması gerekir.
- İyi operasyonların yerini alamaz: hala yama uygulamaya, yedeklemeye ve izlemeye ihtiyacınız var. Yedekleme ve izleme kılavuzumuza bakın.
Hedef mimari
Tek sunucudaki PHP uygulaması için sağlam ve basit bir kurulum:
- Uçta ters proxy ve TLS (ana makinede Nginx veya Caddy veya Cloudflare gibi bir CDN, Cloudflare kurulum kılavuzumuza bakın).
- Statik dosyalar sunan ve PHP isteklerini ileten web kapsayıcısı ( Nginx).
- Görüntünün içine yerleştirilmiş kodunuzu içeren PHP-FPM kapsayıcısı.
- Kalıcı bir birime sahip kendi kapsayıcısındaki veritabanı veya daha iyisi, kritik veriler için yönetilen bir veritabanı hizmeti.
- Yalnızca hayatta kalması gereken veriler için birimler: veritabanı dosyaları ve kullanıcı yüklemeleri.
Çok aşamalı bir Docker dosyası
Çok aşamalı bir yapı, bağımlılıkları tek aşamada yükler ve yalnızca sonucu ince bir çalışma zamanı görüntüsüne kopyalar. Son görüntüde Composer, derleyiciler veya test araçları yoktur, bu da görüntüyü küçültür ve saldırganlara daha az kullanım olanağı sağlar:
# ---- stage 1: PHP dependencies ----
FROM composer:2 AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --no-scripts --prefer-dist --no-interaction --optimize-autoloader
# ---- stage 2: runtime ----
FROM php:8.3-fpm-alpine
RUN apk add --no-cache icu-libs libzip \
&& apk add --no-cache --virtual .build icu-dev libzip-dev $PHPIZE_DEPS \
&& docker-php-ext-install -j"$(nproc)" pdo_mysql intl zip opcache \
&& apk del .build
COPY docker/php.ini /usr/local/etc/php/conf.d/zz-app.ini
COPY docker/www.conf /usr/local/etc/php-fpm.d/zz-www.conf
WORKDIR /var/www/app
COPY --from=vendor /app/vendor ./vendor
COPY --chown=www-data:www-data . .
USER www-data
EXPOSE 9000
PHP sürümünü bilinçli olarak seçin ve hala desteklenip desteklenmediğini kontrol edin. PHP 8 yükseltme kılavuzumuz bunun nasıl planlanacağını açıklıyor. Hiçbir zaman yeniden oluşturmadığınız bir görüntü bilinen güvenlik açıklarını biriktirdiğinden, temel görüntülerin sürümlerini sabitleyin ve bunları bir programa göre güncelleyin. .git, .env, testler ve yerel verileri hariç tutan bir .dockerignore dosyası tutun, böylece görüntüde hassas hiçbir şey kalmaz.
Üretim PHP ve php-fpm yapılandırması
# docker/php.ini
expose_php = 0
display_errors = 0
log_errors = 1
memory_limit = 256M
upload_max_filesize = 10M
post_max_size = 12M
opcache.enable = 1
opcache.memory_consumption = 128
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0 ; the code in an image never changes while it runs
# docker/www.conf
[www]
pm = dynamic
pm.max_children = 12
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 5
pm.max_requests = 500
request_terminate_timeout = 30s
catch_workers_output = yes
decorate_workers_output = no
opcache.validate_timestamps = 0 değerini ayarlamak, değişmez bir görüntü için en iyi performansı verir, ancak bu yalnızca çalışan bir kapsayıcı içindeki dosyaları asla düzenlemezseniz çalışır: bunun yerine yeni bir görüntü yayınlarsınız. Havuzu gerçek sayılardan boyutlandırın: yük altındaki bir PHP çalışanının ortalama belleğini ölçün, PHP sunucuda ne kadar bellek kullanabileceğine karar verin ve bölün. Eğer bir çalışan 60 MB kullanıyorsa ve siz de PHP 1 GB verebilirsiniz, pm.max_children yaklaşık 16'dır. Bunu çok daha yüksek bir değere ayarlamak, trafik artışı sırasında sunucunun değişmesine veya çekirdeğin işlemleri sonlandırmasına neden olur. Çok düşük ve istek kuyruğu. "Max_children'a ulaşıldı" uyarıları için php-fpm günlüğünü kontrol edin.
Doğru varsayılanlara sahip bir Oluşturma dosyası
x-logging: &default-logging
driver: json-file
options: { max-size: "10m", max-file: "5" } # logs cannot fill the disk
services:
web:
image: ghcr.io/your-org/app-web:${TAG}
ports: ["127.0.0.1:8080:80"] # only the local reverse proxy can reach it
depends_on: [php]
restart: unless-stopped
logging: *default-logging
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://127.0.0.1/health.php"]
interval: 15s
timeout: 3s
retries: 3
php:
image: ghcr.io/your-org/app:${TAG}
env_file: .env # secrets stay on the server, not in the image
restart: unless-stopped
read_only: true
tmpfs: [/tmp]
mem_limit: 512m
volumes: [uploads:/var/www/app/storage/uploads]
logging: *default-logging
db:
image: mysql:8.4
env_file: .env.db
restart: unless-stopped
volumes: [db_data:/var/lib/mysql] # no "ports:" - the database is not public
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1"]
interval: 15s
retries: 5
logging: *default-logging
volumes:
uploads:
db_data:
web görüntüsü, public/ klasörünüzün ve site yapılandırmanızın kopyalandığı, PHP görüntüsüyle aynı ardışık düzende oluşturulmuş küçük bir Nginx görüntüsüdür. Anahtar noktalar: yayınlanan bağlantı noktalarını 127.0.0.1 bağlayın, veritabanını asla yayınlamayın, yeniden başlatma ilkelerini ayarlayın, belleği bir kapsayıcının ana bilgisayarı kapatamayacağı şekilde sınırlayın ve günlük boyutunu sınırlayın. Cloudflare kılavuzunda açıkladığımız gibi, Docker tarafından yayınlanan bağlantı noktalarının ana bilgisayar güvenlik duvarı kurallarını atlayabileceğini unutmayın. Docker soketini asla bir uygulama konteynerine monte etmeyin, çünkü bu konteynerin ana bilgisayar üzerinde kontrol edilmesini sağlar.
Sürümler ve geri almalar
Görüntü tabanlı kurulumun en iyi özelliği, her sürümün bir etiket olması ve geri almanın "önceki etiketi başlat" olmasıdır. Görüntüleri Git taahhüt karma değeriyle etiketleyin, hiçbir zaman latest değerine güvenmeyin ve sürüm komut dosyasının sağlığı doğrulamasını sağlayın:
#!/usr/bin/env bash
# /srv/app/release.sh <git-sha>
set -euo pipefail
TAG="${1:?usage: release.sh <git-sha>}"
cd /srv/app
PREV="$(cat .current_tag 2>/dev/null || true)"
export TAG
docker compose pull php web
docker compose up -d --no-deps php web
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:8080/health.php > /dev/null; then
echo "$TAG" > .current_tag
echo "healthy: $TAG"; exit 0
fi
sleep 2
done
echo "unhealthy after release, rolling back to ${PREV:-none}" >&2
if [ -n "$PREV" ]; then TAG="$PREV" docker compose up -d --no-deps php web; fi
exit 1
Veritabanı değişikliklerinin dikkatli yapılması gerekir: kodun önceki sürümünün yeni şemayla çalışmaya devam etmesi için geçişleri yazın (önce sütunları ekleyin, daha sonraki bir sürümde bunları kaldırın). Hızlı geri dönüşü güvenli kılan şey budur. Şemayı değiştiren herhangi bir sürümden önce veritabanı yedeğini alın.
CI'dan inşaat ve nakliye
Görüntüyü bir kez CI'da oluşturun ve tam olarak bu yapıyı dağıtın. Minimal bir GitHub Actions iş akışı:
name: build-and-release
on:
push:
branches: [main]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ghcr.io/your-org/app:${{ github.sha }}
- name: Release over SSH
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
install -m 600 /dev/null key && echo "$DEPLOY_KEY" > key
ssh -i key -o StrictHostKeyChecking=accept-new deploy@your-server "/srv/app/release.sh ${{ github.sha }}"
Görüntü oluşturulmadan önce testlerinizi ve statik analizinizi aynı işlem hattında çalıştırın ve yalnızca yayın betiğini çalıştırabilen bir dağıtım anahtarı kullanın. Risk profiliniz gerektiriyorsa bir görsel güvenlik açığı taraması ekleyin.
Günlükler, veriler ve güvenlik
- Günlükler stdout ve stderr'e gider, Docker tarafından döndürülür ve önemli herhangi bir şey için, konteynerde hayatta kalmaları için merkezi bir yere gönderilir.
- Veriler konteynerin dışında yaşar. Birim bir yedekleme değildir. Yedekleme kılavuzumuzdaki yaklaşımı kullanın: şifrelenmiş, site dışında, geri yükleme testi yapılmış.
- Sırlar asla görüntülere dönüştürülmez. Bunları çalışma zamanında env dosyaları veya bir sır yöneticisi aracılığıyla iletin ve Git'ten uzak tutun.
- Root olmayan bir kullanıcı olarak çalıştırın, mümkünse salt okunur bir dosya sistemi kullanın ve ihtiyacınız olmayan şeyleri bırakın.
- Görüntüleri taze tutun. Temel görüntüdeki ve PHP güvenlik düzeltmelerini almak için düzenli olarak yeniden oluşturun.
- Kapsayıcılar güvenli olmayan kodu düzeltmediğinden, PHP güvenlik kontrol listemizdeki uygulama düzeyindeki kontrolleri uygulayın.
Üretime hazırlık kontrol listesi
- Yalnızca üretim bağımlılıklarını içeren çok aşamalı görüntü, sabitlenmiş temel görüntü, root olmayan kullanıcı.
- OPcache etkin, günlüğe kaydedilen hatalar görüntülenmiyor, php-fpm ölçülen bellekten boyutlandırılıyor.
- Yeniden başlatma ilkeleri, sağlık denetimleri, bellek sınırları, günlük döndürme ve genel veritabanı bağlantı noktası içermeyen dosya oluşturun.
- Otomatik durum kontrolü ve geri alma ile taahhüt karma etiketiyle yayınlanır.
- Veritabanı ve yüklemeler için yedeklemeler ve test edilmiş geri yükleme.
- Sistem durumu, disk, hatalar ve sertifikalarla ilgili izleme ve uyarılar.
Nasıl yardımcı olabiliriz?
PHP uygulamalarını konteynerleştiriyoruz, CI işlem hatlarını ve sürümlerini kuruyoruz ve bunları ödemelere, CRM'ye ve mesajlaşmaya bağlıyoruz. Entegrasyon hizmetimize bakın veya bir çağrı rezervasyonu yapın; mevcut barındırma ve sürüm sürecinizi inceleyelim.
Sık sorulan sorular
Docker PHP üretimde çalıştırmak için iyi midir?
İyi kullanıldığında evet. Aynı ortamları, tekrarlanabilir yayınları ve hızlı geri dönüşleri sağlar. Doğru yapılandırmaya ihtiyacı vardır: değişmez görüntüler, durum kontrolleri, kaynak sınırları, günlük rotasyonu, sır yönetimi ve kapsayıcı dışındaki verilerin yedeklenmesi.
Veritabanı üretimde Docker çalışmalı mı?
Kalıcı bir birim kullanıyorsa, hiçbir zaman halka açık değilse ve yedekleri test edilmişse, küçük ve orta ölçekli sistemler için kullanılabilir. Kritik veriler için çoğu ekip, sizin için yük devretme, güncelleme ve yedekleme işlemlerini gerçekleştiren yönetilen bir veritabanı hizmetini tercih eder.
Docker sürümünü nasıl geri alabilirim?
Her görüntüyü Git taahhüt karması ile etiketleyin ve önceki etiketi saklayın. Geri almak için kapsayıcıları önceki etiketle başlatın. Eski kodun yeni şemayla çalışmaya devam etmesi için veritabanı geçişlerini yazın.
Kaç tane php-fpm çalışanı yapılandırmalıyım?
Yük altındaki bir çalışanın ortalama belleğini ölçün, PHP ne kadar bellek kullanabileceğine karar verin ve bölün. Değerin çok yüksek ayarlanması takas yapılmasına, çok düşük ayarlanması ise isteklerin sıraya alınmasına neden olur. max_children uyarıları için php-fpm günlüğünü izleyin.
PHP uygulamalarındaki en büyük Docker güvenlik hatası nedir?
Görüntüye veya Git'e sırlar koymak ve veritabanı bağlantı noktalarını internette yayınlamak. Ayrıca root olarak çalıştırmaktan, Docker soketini kapsayıcılara monte etmekten ve temel görüntüyü asla güncellemekten kaçının.
Ücretsiz keşif görüşmesi
Dağınık bir yığın veya takılmış bir entegrasyon getirin - Integrono en akıllı ilk adımı haritalar.
Bütçe tuzakları, bağımlılıklar ve satın al / yapıştır / inşa kararında kıdemli bir bakış alırsınız - ücretli iş başlamadan. CEO döngüde kalır.