Herkese Merhabalar arkadaşlar! Bugün hepimizin er ya da geç karşılaştığı bir konuya, Angular Docker ile deployment işlemine değineceğiz. Eski usül “build alıp dosyaları sunucuya FTP ile atma” döngüsünden bunaldıysanız ve her şeyi dockerize etmeye karar verdiyseniz tam olarak doğru yerdesiniz. Docker ilk başta korkutucu gelebilir, ancak söz veriyorum bu yazının sonunda Node ve Nginx image’lerini kullanarak yazacağımız tek bir Dockerfile ile uygulamanızı kolayca yayına alabilir hale geleceksiniz.
Peki neden Angular Docker yolculuğuna çıkmalıyız?
CI/CD süreci geliştirme ortamında artık bir standart haline geldi. Bununla birlikte kariyer hedefleri olan bir geliştirici olarak Docker teknolojisini öğrenmek gittikçe bir zorunluluk haline geliyor. Bu yüzden aşağıda size sadece kopyala-yapıştır yapacağınız bir Dockerfile bırakıp kapatmayacağım. Bunun yerine bütün adımları tek tek, neden o satırı yazdığımızı açıklayarak ilerleyeceğiz; sağlam bir Angular Docker imajının nasıl üretildiğini anlamanız için. Tabii ki tüm basamakların hemen altında tamamlanmış Dockerfile örneğini de paylaşacağım.
Burada kullanacağımız yaklaşıma multi-stage build deniyor. Mantığı aslında çok basit: bir aşamada Node kullanıp uygulamayı build ediyoruz, sonra sadece o build’in çıktısını alıp minik bir Nginx image’ine taşıyoruz. Böylece son imajımızın içinde node_modules gibi yüzlerce megabaytlık gereksiz yük taşımıyoruz. Detaylarını Docker’ın resmi multi-stage dokümantasyonunda da bulabilirsiniz.
1. Build Image Oluşturulması ve Uygulamanın Build Edilmesi
Bu bölümde uygulamamızı, içerisinde Node.js bulunan bir container kullanarak build alacağız.
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build
- FROM node:20-alpine AS build — Docker Hub üzerinde bulunan
node:20-alpineimage’ini kullanarak bir container oluşturuyoruz ve bunabuildadını veriyoruz. Önemli bir not: versiyon seçerken kullandığınız Angular sürümünü göz önünde bulundurmanız gerekiyor. Hangi Angular sürümü için hangi Node sürümünü tercih etmeniz gerektiğini Angular’ın resmi versiyon uyumluluk tablosunda bulabilirsiniz. - WORKDIR /app — İşlemlerimizi gerçekleştireceğimiz dizini container içerisinde oluşturuyoruz.
- COPY package.json package-lock.json ./ — Bağımlılık dosyalarımızı önce kopyalıyoruz. Burada küçük ama kritik bir numara var: bütün projeyi değil de sadece bu iki dosyayı önce kopyalarsak, kodumuz değiştiğinde Docker
npm cikatmanını cache’ten okuyabilir ve build sürelerimiz uçar gider. - RUN npm ci — Kopyaladığımız
package-lock.jsoniçindeki bağımlılıkları indiriyoruz. Burada bilereknpm installyerinenpm cikullanıyorum; lock dosyasına birebir sadık kaldığı için CI/CD ortamlarında çok daha güvenilir ve tekrarlanabilir sonuç verir. - COPY . . — Uygulamamızın geri kalan tüm kodunu app dizinine taşıyoruz.
- RUN npm run build — Ve son olarak production build’imizi alıyoruz.
2. Uygulamanın Nginx Üzerinden Servis Edilmesi
Bu bölümde ise az önce build aldığımız Angular uygulamasını Nginx kullanarak servis edeceğiz, yani yayına alacağız.
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=build /app/dist/application/browser /usr/share/nginx/html
EXPOSE 80
- FROM nginx:1.27-alpine — Docker Hub üzerindeki hafif Nginx image’ini kullanıyoruz.
- COPY nginx.conf … — Aşağıda paylaştığım
nginx.confdosyasını projenizin ana dizinine eklemeniz gerekiyor. Bu yazıda Nginx üzerinde fazla durmayacağız, ancak ayarların tüm detayını merak ediyorsanız Nginx’in resmi başlangıç rehberine göz atabilirsiniz. - COPY –from=build … — İşte multi-stage build’in büyüsü tam burada devreye giriyor: birinci aşamada oluşturduğumuz
buildcontainer’ı içindeki dist çıktısını alıp Nginx’in servis edeceği dizine taşıyoruz. Burada küçük ama önemli bir güncel detay var: Angular 17 ve sonrasında yeni application builder çıktıyıdist/<app-adı>/browseraltına koyuyor. Daha eski sürümlerde ise yol sadecedist/<app-adı>şeklindeydi, dolayısıyla kendiangular.jsonayarınıza göre bu yolu kontrol etmenizi tavsiye ederim. (Angular 17 ile gelen diğer yeniliklere Angular 17 ile Bizi Neler Bekliyor yazımdan ulaşabilirsiniz.) - EXPOSE 80 — HTTP isteklerinin erişilebilir olması için 80 portunu dışarı açıyoruz.
Önbellek tuzağından kurtaran nginx.conf
Varsayılan Nginx ayarları ile minik bir oynama yapıyoruz ve index.html dosyasının tarayıcı tarafından önbelleğe alınmasını engelliyoruz. Bu sayede ileride çıkacağımız yeni versiyonlarda kullanıcıların ekranında eski bir uygulamayla karşılaşması gibi can sıkıcı önbellek sorunlarından kurtulmuş oluyoruz. Ayrıca try_files satırı, Angular gibi bir SPA’da sayfa yenilendiğinde 404 hatası almamanız için şart.
events {}
http {
include /etc/nginx/mime.types;
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
if ($uri = '/index.html') {
add_header Cache-Control "no-store" always;
}
try_files $uri $uri/ /index.html;
}
}
}
Tamamlanmış Angular Docker dosyası
Ve Dockerfile dosyamız kullanıma hazır. Aşağıda tam halini paylaşıyorum. Ancak tavsiyem yukarıdaki adımları okumanız, kopyala-yapıştır yapıp geçmemeniz 🙂 Çünkü yetkin bir yazılımcı olarak Docker bilmeniz gerekenler listesinin başlarında yer alıyor ve kariyer yolculuğunuzda bir noktada bu tecrübe mutlaka sizden istenecektir.
FROM node:20-alpine AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
RUN npm run build:prod
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY --from=build /app/dist/application/browser /usr/share/nginx/html
EXPOSE 80
İmajı build edip çalıştırmak ise oldukça basit:
docker build -t angular-app .
docker run -p 8080:80 angular-app
Artık tarayıcınızdan http://localhost:8080 adresine giderek dockerize ettiğiniz uygulamanızı görebilirsiniz. Gördüğünüz gibi Angular Docker kombinasyonu ilk bakışta göründüğü kadar karmaşık değilmiş.
Söyleyeceklerim şimdilik bu kadar. Eğer Angular dünyasını daha derinlemesine keşfetmek isterseniz Angular vs React ve Angular’ın Dependency Injection yazılarıma da göz atabilirsiniz. Aklınıza takılan başka bir husus varsa sormaktan çekinmeyin, herkese iyi çalışmalar dilerim 🙂
