Docker ile Örnek Django Uygulaması

Bu yazimda sizlerle docker ile ayni fiziksel makina uzerinde calisan bir django uygulamasi alt yapisi nasil hazirlanir onu paylasacagim. Bu alt yapi ile birlikte gerektiginde sisteme ilave uzerinde django uygulamamizin calistigi containerlar ekleyip sitemizin performansini {artira/koruya}bilecegiz.

Sistemin yapisi su sekilde olacak:

Workflow

  1. Nginx gelen trafigi django containerlar arasinda dagitacak.
  2. Nginx olusturulan mystatic isimli volume uzerinden static dosyalari sunacak.
  3. Django containerlar DB containeri ile haberlesecek.
  4. Django containerlar mystatic isimli volume uzerindeki dizinlere erisebilecek.

Simdi bu alt yapiyi docker ile nasil olusturabiliriz onu gorelim.

Ornek Uygulama

Ben github uzerinde bir depo olusturdum. Depoda icinde uWSGI+Django calisan bir image olusturmamizi saglayacak Dockerfile ve konfigurasyon dosyalari, kullanici sayfayi ziyaret edince ekrana uygulamanin uzerinde calistigi containerin hostnameini bir kac resim ile beraber sayfada gosteren basit bir Django uygulamasi, ornek uygulamamizin DB backupi ve Nginx konfigurasyon dosyasi yer aliyor. Makaleyi bu ornek uygulama uzerinden anlatadagim. Bu depoyu local makinaniza indirip yukaridaki resimde gosterilen yapiyi kendi makinanizda test edebilirsiniz.

Ornek uygulamayi local makinanizda kurmak istemiyorsanizDocker Agi olusturma kismina gecebilirsiniz.

Depoyu /tmp dizinine indirmek icin:

$ git clone https://github.com/mesuutt/docker-nginx-django-workflow.git /tmp/workflow

Depoda docker compose ile yapiyi ayaga kaldirabileceginiz docker-compose.yml dosyasida bulunuyor. Docker compose ile tek seferde yukaridaki resimde gosterilen yapiyi kendi makinanizda olusturabilirsiniz. Docker compose kullanmiyorsaniz yine depoda bulunan run.sh dosyasini calistirarak da ornek yukaridaki resimde gosterilen yapiyi ayaga kaldirabilirsiniz.

run.sh dosyasini calistirmak yerine docker compose ile containerlari ayaga kaldirmak isterseniz docker-compose up komutunu calistirdiktan sonra uygulamanin calisabilmesi icin DB olusturup, sql backupimizi acip, uygulamamizin statik dosyalarini gerekli yerlere kopyalamamiz gerekiyor. Bu islemleri docker compose ile yapamadigimizdan kendimiz yapmamiz gerekiyor. Ornek uygulamayi indirdiginiz dizine gidip bu islemi asagida gosterildigi gibi kendimiz yapabiliriz.

# Ornek uygulamayi indirdigimiz dizine gidiyoruz.
$ cd /tmp/workflow

# Ornek uygulamanin kullanacagi databasei olusturuyoruz
$ docker exec pgdb createdb -U postgres myappdb

# DB backupini containera kopyalayip aciyoruz
$ docker cp postgres/myappdb.dump pgdb:/
$ docker exec pgdb psql -U postgres -f /myappdb.dump myappdb

# Gerekli static dosyalari olusturdugumuz
# docker volume kopyaliyoruz.
$ docker cp volume_content/myapp django1:/static

# Uygulamanin static dosyalarini Nginx'in sunacagi dizine topluyoruz.
$ docker exec django1 /srv/app/manage.py collectstatic --noinput

# Uygulamalari yeriden baslatiyoruz.
$ docker exec django1 supervisorctl restart app-uwsgi
$ docker exec django2 supervisorctl restart app-uwsgi

Bu islemleride yaptiktan sonra Nginx’in calistigi containerinin IPsini tarayicinizdan acarak ornek uygulamayi test edebilirsiniz. Sayfayi acip bir kac defa sayfayi yenilediginizde sayfanin farkli Django instancelerdan geldigini goreceksiniz (sayfanin ust kisminda istek hangi Django containera yonlendirildiyse o containerin hostnamei yaziyor).


Simdi yukaridaki resimde gosterilen yapiyi nasil olusturabilecegimizi adim adim anlatmaya calisacagim.

Docker Agi Olusturma

Ilk olarak mynetwork adinda bir docker bridge agi olusturacagiz ve butun containerlarimizi bu aga dahil edecegiz. Bu sayede containerlar kendi aralarinda kolayca haberlesebilecek ve birbirlerine containerlarin isimleriyle ulasabilecekler.

Not: Docker 1.9 surumunden once containerlari calistirirken(docker run) --link parametresini kullanarak; calisacak olan containerin link parametresinde belirtilen container ile haberlesebilmesini saglayabiliyorduk. Fakat 1.9 surumu ile birlikte Docker networks duyuruldu. Docker 1.9+ ile beraber --link parametresi kullanmak yerine birbirleriyle haberlesmesini istedigimiz containerlari ayni aga dahil ediyoruz.

Docker networksun bize kattigi guzel seyler var. Bu konu hakkinda da cok yakin zamanda detayli bir yazi yazacagim insAllah.

Ise ilk olarak yeni bir bridge network olusturarak baslayalim. Sonradan olusturacagimiz containerlari bu networke dahil edecegiz ve bu sayede aga dahil olan butun containerlarin birbirleriyle haberlesebilmesini saglayacagiz.

Docker networkumuzu olusturalim:

$ docker network create mynetwork

Docker Volume Olusturma

Simdi ise yukaridaki resimde gosterilen mystatic isimli named volume(Turkce’ye nasil cevirsem bilemedim) olusturalim.

Volumeu dockerin calistigi makinadaki bir dizin olarak dusunebilirsiniz (Baska bir makina uzerinde de olabiliyor). Bu dizini olusturucagimiz containerlara mount edebiliyoruz. Bu sayede butun containerlar ayni dizine erisebiliyor.

Not: docker run -v /host/dizini:/container/dizini ... seklinde de host da bulunan bir dizini containera mount edebiliyoruz.

Bu olusturacagimiz volume u ileride olusturacagimiz nginx ve django containerlara mount edecegiz. Nginx uygulamamizin static dosyalarini bu volume uzerinden sunacak ve Django containerlar kullanicinin upload ettigi dosyalari isleyip bu volumedeki ilgili dizine atabilecekler.

$ docker volume create --name=mystatic

Gerekli Olan Containerlari Olusturma

Simdi ise sira geldi containerlari olusturmaya. Sirayla 1 adet Postgresql, 2 adet Django ve 1 adet Nginx container olusturacagiz.

Not: Bu makalede Nginx ve Postgresql docker imagelarini docker hubdan indirip kullandim. Siz isterseniz imagelarin Dockerfileini indirip kendiniz build edebilirsiniz.

Postgresql Container

Ilk olarak Postgresql docker imageini Docker hubdan indiriyoruz.

$ docker pull postgres

Postgresql imageimizi indirdik. Simdi ise pgdb adinda bir postgresql container olusturalim ve daha once olusturmus oldugumuz mynetwork agina ekliyelim.

$ docker run -d \
--net=mynetwork \
--name pgdb \
-e POSTGRES_PASSWORD=12345 \
postgres

Artik calisan bir postgresql containerimiz var. Simdi containerda bir shell acalim ve uygulamamizin kullanacagi databaseimizi olusturalim.

$ docker exec -it pgdb /bin/bash
root@e7c085fe3fbc:/# createdb -U postgres myappdb

myappdb adinda bir db olusturduk.

Simdi ise ornek uygulamamizin db backupini container a kopylayip acalim

$ docker cp postgres/myappdb.dump psql:/
$ docker exec pgdb psql -U postgres -f /myappdb.dump myappdb

Not:mynetwork, mystatic ve myappdb isimlendirmeleri cok yaratici degilmi :)

Django Container

Simdi ornek django uygulamamizi calistiracagimiz 2 adet container olusturalim. Yukarida da belirttigimiz gibi Nginx kullanicilardan gelen istekleri bazen django1 containerindaki Django uygulamasina bazen ise django2 containerinda calisan Django uygulamasina yonlendirecek.

$ git clone https://github.com/mesuutt/docker-nginx-django-workflow.git  /tmp/workflow
$ cd /tmp/workflow
$ docker build mesuutt/uwsgi-django .
$ docker run -d \
--net=mynetwork \
--name django1 \
-e DJANGO_SETTINGS_MODULE='myapp.settings.local'
-v mystatic:/static \
mesuutt/uwsgi-django

$ docker run -d \
--net=mynetwork \
--name django2 \
-e DJANGO_SETTINGS_MODULE='myapp.settings.local'
-v mystatic:/static \
mesuutt/uwsgi-django

Ben iki adet contaner olusturdum. Siz isterseniz 1,2,3… size ne kadar gerekiyorsa o kadar olusturabilirsiniz :).

Nginx Container

Bu containerda calisan nginx, gelen trafigi django containerlara yonlendirerek yuk dagilimi yapacak ve uygulamanin statik dosyalarini sunacak.

Nginx icinde custom bir image hazirladim. Bu custom imagein official imagedan tek farki processleri nginx kullanicisiyla degil UID‘i 1000 olan bir kullanici ile calistiriyor olmasi. Buna ihtiyac duyulmasinin sebebi su:

App containerlarda calisan uWSGI processlerin sahibi olan kullanicinin idsi ile nginx containerlarda statik dosyalari sunan kullanici idsi ayni olmali ki statik dosyalara/dizinlere okuma/yazma hatasi meydana gelmesin.

$ cs nginx/
$ docker build mesuutt/nginx .

Nginx containerimizi calistiralim:

$ docker run -d \
--net=mynetwork \
--name nginx \
-v mystatic:/static \
-v /tmp/workflow/nginx/myapp.conf:/etc/nginx/conf.d/default.conf \
mesuutt/nginx

Artik calisan bir nginx containerimiz da var.

Nginx containera mount ettigim uygulamamızın virtual host dosyasinin icerigi:

# myapp.conf

upstream uwsgi {
    # Olusturdugumuz Django containerlarin hostlari
    # ve uzerlerinde calisan uWSGI'larin dinledigi portlari
    # buraya ekleyerek Nginxe gelen istekleri
    # bu Django containerlar arasinda
    # dagitmasini soylemis oluyoruz.
    # Dikkat ettiyseniz django containerlarin IPleri yerine
    # isimleri yaziyor. Bu containerlari ayni aga
    # ekledigimiz icin mumkun oluyor.
    server django1:9000;
    server django2:9000;
}


server {
    listen 80;
    root /srv/app;

    access_log /var/log/nginx/myapp-access.log;
    error_log /var/log/nginx/myapp-error.log error;

    location /static {
        alias /static/myapp/staticfiles;
        access_log off;
        expires 30d;
    }

    location /media {
        alias /static/myapp/media;
        access_log off;
    }

    location / {
        include /etc/nginx/uwsgi_params;
        uwsgi_pass uwsgi;

        uwsgi_param Host $host;
        uwsgi_param X-Real-IP $remote_addr;
        uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
        uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
    }
}

Artik tarayicinizdan nginx containerin IPsine giderek ornek uygulamamizi test edebilirsiniz. Sayfanin ust kisminda hostname in degilstigini goreceksiniz.


Internette nginx, uwsgi ve Django nun beraber geldigi Docker imagelar ve Dockerfile lar bulunuyor. Bunlari gelistirme ortaminda kullanabiliriz (Bir containeri calistirarak butun development environmentimiz ayaga kaldirabiliyoruz) fakat Python web uygulamanizin calistigi birden fazla Django container olusturayim derseniz bu image ve Dockerfilelar isinizi gormez.

Ben internette baktim icinde Nginx, Postgresql bulunmayan sadece uWSGI kururulu gelen bir image/Dockerfile bulamadim. Bende icinde Dockerfile ve yardimci konfigurasyon dosyalarinin bulundugu bir github deposu olusturdum. Kendi Python ile hazirladiginiz web projenizi (Django/Flask vb.) yukarida belirtilen gibi bir yapida calistirmak isterseniz kullanabilirsiniz.

Yapmaniz gereken sey cok basit;

$ git clone https://github.com/mesuutt/docker-uwsgi.git

Artik bu image dan istediginiz kadar container olusturabilirsiniz. Bu makaledeki ornek uygulamanin Django docker image ini da bunun ile hazirladim.

Depoya su adresden ulasabilirsiniz: https://github.com/mesuutt/docker-uwsgi

comments powered by Disqus