Docker - Building Multi Container Applications

Docker - Building Multi Container Applications

Bu yazımda diğer 2 docker serisinden öğrendiklerimizi kullanarak, backend, frontend ve database containlerları oluşturup, 3 containerı birbirleriyle iletişime geçirip uygulamamızı kurmayı, ayağa kaldırmayı öğreneceğiz ve öğrendiklerimiz içinde güzel bir pratik olacak bu.

STEP 1 - Get started

https://github.com/mucahidyazar/docker

  • Yukarıdaki linkten gerekli repoyu indiriyoruz.
    git clone https://github.com/mucahidyazar/docker.git
  • Ve tutorials branchine geçiyoruz.
    git checkout tutorials
  • Be burada multi-container adlı dizine geliyoruz. Burada 2 klasör var. 1. si başlangıç yani starter klasörü, diğeri bitiş yani fnished dosyalarımız.
  • Takıldığınız yerde fnished klasörüne bakıp kopya çekebilirsiniz.

STEP 2 - .dockerignore

  • İlk önce backend ve frontend klasörlerimize .dockerignore klasörü oluşturalım. Ve aşağıdaki gibi dockerın dokunmaması gereken dosya ve klasörleri belirleyelim. Ben standart olarak aşağıdaki şekilde oluşturuyorum.

STEP 3 - Network

  • İlk önce containerlarımızın birbirleriyle iletişime geçecekleri networku oluşturalım. Ben multi adında bir network oluşturuyorum.
    docker volume create multi
  • Daha sonra listeleyip volumemizin oluşturulduğundan emin olalım.
    docker volume ls

STEP 3 - Dockerfile (for mongodb)

  • Şimdi ilk container olarak mongo imagesinde bir mongodb çalışan container ayağa kaldıracağız.

docker run \
-v data:/data/db \
-e MONGO_INITDB_ROOT_USERNAME=root \
-e MONGO_INITDB_ROOT_PASSWORD=secret \
--name multi-mongo-container \
--network multi \
--rm \
-d \
mongo

Yukarıda ilk olarak data adında bir volume oluşturuyoruz ve mongo containerındaki /data/db yolunu bu data volumesine stroage olmasını yedeklemesini istiyoruz.
--name multi-mongo-container adıyla adlandırmak istediğimi belirtiyorum bu containerın.
-d ile detach modda çalıştırıyorum yani çalıştırdıktan sonra mongo terminalde kalmasın istiyorum.
--rm ile containerın stop olduğunda otomatik olarak silinmesini istiyoruz.
--network ile yukarıda oluşturduğumuz multi networkune mongo imagemizi başlatacağımız containerı bağlamak istiyoruz.
-e ile mongo database için belirlememiz gereken 2 tane environment variable’ı belirliyoruz. Bu environmentdaki varaiblesları daha sonra backend projemizde app.js içinde mongo databaseye bağlanmak için kullanacağız.
ve son olarak mongo imagesini çalıştırmasını söylüyoruz. Bu makinamızda yoksa dockerhub üzerinden indirir ve bu komutlarla çalıştırır.

  • Ve şimdi yine aşağıdaki kodla çalışan containerlarımızı görerek imageyi başarıyla çalıştırdığımızdan emin olalım.
    docker ps -a

STEP 4 - Dockerfile (for backend)

  • Ben aşağıda ki gibi Dockerfilemi oluşturuyorum. Daha önceki derslerde buradaki kavramları anlattığım için burayı detaylı olarak sizlere anlatmayacağım.

  • Fakat yukarıda görüyoruz ki node app.js yaparak uygulamamızı başlatıyoruz. Bu uygulamamızda bir değişiklik olduğunda server taraflı değişikliklerde yeniden çalıştırılmayacağı için değişiklikleri yakalayabilmek için yeniden containerımızı başlatmak zorunda kalırız. Bu yüzden burada nodemon adlı package ile değişiklik olduğunda backend servisimizin kendisini otomatik olarak yenilemesini sağlayan paketi yükleyeceğim.

  • Önce backend dizinine geliyoruz terminalde ve aşağıda ki kodla packagemizi yükleyeceğiz.
    yarn add -D nodemon
    npm install --save-dev nodemon
  • Daha sonra package.jsonu aşağıdaki şekilde düzenliyorum.

  • Ve Dockerfilemizdede CMD komutunu aşağıdaki şekildeki gibi düzeltiyoruz.

  • Ve şimdi de mongo imagesinde tanımladığımız environment variablesları backend içinde, config klasörü ve içindede env klasörü oluşturduktan sonra dev.env dosyası açarak içine aşağıdaki varaiblesları tanımlayalım.

  • Burada DB_NAME ve DB_PASSWORD mongo conta’nerımızda tanımladığımı environment variableslar olmalı yoksa erişimi sağlayamayız. Daha sonra bağlanacağımız databasenin host adresini DB_HOST, port adresini DB_PORT, ve adınıda DB_NAME olarak tanımlıyoruz.

  • Tabi bu variablesları kullanabilmek için node.js projemizde dotenv gibi ekstra paketleri yüklememiz ve kullanmamız gerekecek. Ben dotenv yi yükleyeceğim.
    yarn add dotenv
    npm install dotenv

  • Ve app.js içine gelerek yüklediğimiz bu dotenv modülünü require ile aşağıdaki gibi projenin en başına ekleyerek, içine dev.env dosyamızın yolunu yani pathini veriyoruz.

  • Ve daha sonra yine app.js içinde mongo databasesine bağlandığımız kod dizininde bu dev.environment variablesları buraya veriyoruz. Ve bu varaibleslar mongoda tanımladığımız varaibleslarla eşleşince başarıyla bağlantı sağlayabileceğiz.

  • Ve ikinci olarak da mongo container adımızı database bağlantı hostu için ayarladığımı DB_HOST environment variablesını ve portunu DB_PORT’u aralarında “:” işareti olacak şekilde yazıyoruz. Çünkü backendide aynı networke bağladığımızda container adıyla mongoya erişebiliyor olacağız.
  • Sonrada bağlanacağımız database ismini atadığımız environment variable’ı yazıyoruz.
  • Ve en son olarakda “?authSource=admin” diyererek ayarlamamızı yapıyoruz. Burası user credentialsala birlikte collection’a sahip databasenin adıdır. Kısaca username ve passwordların ayarlandığı yerdir.

  • Ve şimdi tüm ayarlamalarımızı yaptıktan sonra imagemizi oluşturabilir ve containerımızı çalıştırabiliriz.

  • İlk önce şağıdaki kodla imagemizi oluşturuyoruz.
    docker build -t multi-backend-image .

docker run
-p 80:80 \
-v ${pwd}:/app \
-v logs:/app/logs \
-v /app/node_modules \
--env-file ./config/env/dev.env \
--name multi-backend-container \
--network multi \
--rm \
multi-backend-image

  • Ve şimdide yukarıdaki kodla containerımızı çalıştırıyoruz.
    --rm ile containerımızın stop olunca silinmesini söylüyoruz
    --network ile containerımızı multi isimli networke dahil ediyoruz
    -p ile port binding yaparak containerımızı kendi OS sistemimizdeki 80 ile çalıştığı VM(Virtual Machine)’deki 80 portlarını bind etmesini bağlamasını söylüyoruz. Böylece containerın çalıştığı VM’deki 80 portu bizim makinamızdaki 80 portuna yansıyacak. Ve böylelikle localhost adresinden direkt olarak container’a erişim sağlayabileceğiz.
    --name ile containerımızı identifier ettiğimiz bir isimlendirme uyguluyoruz yani containerımızı isimlendiriyoruz. Burada backend için multi-backend-container koydum ben düzenli olması için.
    -v ${pwd}:/app volume belirlemesi ile şuan ki dizinde yani kodu yazdığımız dizindeki, yani backend klasörünün içindeki tüm herşeyi Dockerdaki /app klasörüne yani WORKDIR’e bağlamasını istiyoruz. Böylelikle localde yaptığımız değişiklikler anında, containerımızın çalıştığı VM’ye yansıyabilecek.
    -v logs:/app/logs volume tanımlaması ilede, logs adında bir volume oluşturmasını söylüyoruz Docker’a, ve bu logs volumesine, conteinerımızın logladığı VM’de çalışma dizindeki logların tutulduğu pathdeki(/app/logs) logları saklamasını söylüyoruz.
    -v /app/node_modules ile containerımızın npm install yaptıktan sonra buradaki node_modules dosyalarını anonymous volume oluşturarak saklamasını istiyoruz.
    --env-file ilede backend projemizin çalıştığı zaman ihtiyacı olacak environment varaiblesları içeren env dosyasının yolunu vererek, environment variableslarını dockera tanımlamasını söylüyoruz.
    Ve son olarak image adını yazıyoruz.

STEP 5 - Dockerfile (for frontend)

Backend ve mongo-containerımızı ayağa kaldırdığımıza göre şimdi sıra frontend uygulamamızı Dockerize etmeye geldi.

  • Dockerfile’mi oluşturup içini aşağıda ki gibi ayarladım.

  • Daha sonra imagemizi build alıyoruz aşağıda ki kodla.
    docker build -t multi-frontend-image .

docker run -v ${pwd}/src:/app/src --rm -it --name multi-frontend-container -p 3000:3000 multi-frontend-image

  • Ve son olarak da aşağıda ki kodu kullanarak oluşturduğumuz uygulamamızın imagesini containerımızı çalıştırarak kullanıyoruz.
    -v ${pwd}/src:/app/src ile uygulamamızın kendi OS sistemimiz üzerindeki src yolunu, pathini, containerımızın sistemi üzerindeki /app/src dizini ile bind ediyoruz bağlıyoruz. Böylelikle react uygulamamız localde yaptığımız değişiklikleri anında containera yansıtacaktır.
    --rm ile containerımızın stop olunca silinmesini söylüyoruz
    --network ile containerımızı multi isimli networke dahil ediyoruz
    -it ile interactive mod ile containerımızı başlatacağız ve böylelikle react uygulaması çalışırken teminali izleyip başarıyla başlayıp başlamadığını görebileceğiz.
    --name ile containerımızı identifier ettiğimiz bir isimlendirme uyguluyoruz yani containerımızı isimlendiriyoruz. Burada frontend için multi-frontend-container koydum ben düzenli olması için.
    -p ile port binding yaparak containerımızı kendi OS sistemimizdeki 3000 ile çalıştığı VM(Virtual Machine)’deki 3000 portlarını bind etmesini bağlamasını söylüyoruz. Böylece containerın çalıştığı VM’deki 3000 portu bizim makinamızdaki 3000 portuna yansıyacak. Ve böylelikle localhost adresinden direkt olarak container’a erişim sağlayabileceğiz.
    Ve son olarak image adını yazıyoruz.

  • Ve evet tüm adımları tamamlayıp uygulamalarımızı başarıyla Dockerize ettik. Final görüntüsü aşağıda ki gibi.

  • Peki biz bu yazımız da neler yaptık?
    3 container çalıştırdık.
    Network kurup containerlarımız arası iletişim kanalı iletişim kanalı oluşturup, bu kanal üzerinden haberleştik.
    2 tane Dockerfile oluşturduk.
    3 containerımıza özel farklı volumeler oluşturduk.
    Bir önceki yazılardan öğrendiklerimizi fazlasıyla tekrar ettik.

Bu yazımda anlatacaklarım bu kadardı. Bir sonra ki örnekte bunu docker-compose kullanarak nasıl çok daha az terminal kodu yazarak yapacağız bunu göreceğiz. Esen kalın.