10 - Sunucunun hazırlanması

June 17, 2018 5 minute read

Buraya kadar hep geliştirme aşamasıyla ilgili konulara değindik. Şimdi backend development tarafından çıkıp biraz devops deployment konusunu ele alalım. Olabildiğince az müdahale ile projemizi dış dünyanın erişimine sunacağız.

Özet

  • Docker ile sunucumuzu hazırlayacağız.
  • Ortama göre (development, production, test) sunucu ve proje ayarlarımızı değiştirebileceğimiz yapılandırma dosyaları oluşturacağız.
  • Bu yazıyla ilgili kodlara buradan erişebilirsiniz.

Gerçekten Docker’a gerek var mı?

Elbette hayır. Hatta yeni bir sunucuya da ihtiyacımız yok, şimdiye kadar kullandığımız geliştirme sunucumuzu ngrok gibi basit bir tünelleme aracıyla dışarıdan erişilebilir hale getirip bilgisayarımızı 24 saat açık bırakabiliriz. Ama sunucuyla ilgili bizim birtakım beklentilerimiz var. Mesela sürekli erişilebilir durumda olmasını istiyoruz ve gelen isteklerin kesintisiz, hızlı ve mutlaka alınıp cevaplandırılmasını istiyoruz. Bunun için sunucunun sağlıklı çalışmasını sağlamak ve insan kusurunu en aza indirmemiz gerekiyor.

Bizim veritabanı değişikliklerini uygulamak için bir script oluşturmamızın bir sebebi var, kaç farklı ortam ve sunucumuz olursa olsun, hata yapma olasılığımızı en aza indirmek. Bir önceki bölümde Docker ile sanallaştırma yaparken de amacımız buydu, production’da olabilecek her türlü senaryoyu önce yerelimizde gerçekleştirmek ki deployment aşamasında herhangi bir sürprizle karşılaşmayalım.

Peki deployment için bir script hazırlamak daha pratik olmaz mıydı? Olabilir elbette, benim halen daha basit projeler için kullandığım bir yöntemdir. Ancak benim size göstermek ve anlatmak istediğim şey, elle müdahaleye en aza indirmek ve bunu mümkün mertebe en kısa zamanda alışkanlık haline getirmek. Çok şey kazanırsınız. Çok yöntem var, hepsini anlatmam zor; ancak Docker ile başlayabiliriz.

Ortama göre sunucu ayarları

Önceki yazımızda Docker ile geliştirme yapmaya başlamıştık. Bunun için docker-compose.yml dosyası oluşturup Celery, RabbitMQ ve web sunucumuzun otomatik başlamasını sağlamıştık. Ancak bu ayarlarımız tamamen geliştirme ortamına göre hazırlanmıştı. Şimdi bu yapılandırmada ortak kısımları yine docker-compose.yml dosyasında tutacağız, diğer ayarları sunucu ortamına göre ayrı dosyalarda tutacağız1:

# docker-compose.yml
version: '3'

services:
  web:
    build: .
  celery:
    build: .
    command: celery worker -l info -A hello_django
    depends_on:
      - amqp
  celery-beat:
    build: .
    command: celery beat -l info -A hello_django --scheduler django_celery_beat.schedulers:DatabaseScheduler
    depends_on:
      - amqp
      - celery
  amqp:
    image: rabbitmq:3.7
# docker-compose.dev.yml
version: '3'

services:
  web:  # Test ortamında geliştirme sunucusu kullanmamız bizim için yeterli.
    command: python manage.py runserver 0:8000
    volumes:  # Geliştirme aşamasında konteyner içindeki kodlarımıza kolayca müdahale edebilelim.
      - .:/code
    ports:  # İlgili portları açıyoruz ki projemize erişebilelim.
      - 8000:8000
  celery:
    volumes:
      - .:/code
  celery-beat:
    volumes:
      - .:/code
# docker-compose.prod.yml
version: '3'

services:
  nginx:  # Production'da Nginx sunucusunu kullanacağız. 8000 portunu dinlemek için.
    image: nginx
    volumes:
     - ./conf/nginx.prod.conf:/etc/nginx/nginx.conf
     - ./assets/static:/assets/static  # Statik dosyalarımız Nginx tarafından sunulacak.
     - ./assets/media:/assets/media  # Media dosyalarımız Nginx tarafından sunulacak.
    restart: always  # Bir sebepten kapanırsa yeniden başlatması için.
    ports:
      - 80:80
  web:  # Production'da geliştirme sunucusu yerine Gunicorn kullanacağız.
    command: gunicorn hello_django.wsgi:application -b 0:8000
    entrypoint: /code/entrypoint.sh  # Bu dosyayı ayrıca inceleyin, otomatik bazı komutları çalıştırmak için.
    restart: always
    volumes:
      - ./db.sqlite3:/code/db.sqlite3
      - ./assets/static:/assets/static
      - ./assets/media:/assets/media
  celery:
    restart: always
  celery-beat:
    restart: always
  amqp:
    restart: always

Şunu da yapabilirdik, geliştirme aşamasında DB backend olarak SQLite3 kullanıp, Production’da PostgreSQL tercih edebilirdik. Bunu pek önermiyorum, geliştirme ortamınız production’u ne kadar iyi taklit ederse bizim için o kadar iyi. Peki bu değişiklikten sonra geliştirme ortamımızı nasıl çalıştıracağız?

# terminal
$ docker-compose.exe -f docker-compose.yml -f docker-compose.dev.yml up -d
Creating network "hello_django_default" with the default driver
Creating hello_django_web_1    ... done
Creating hello_django_amqp_1 ... done
Creating hello_django_celery_1 ... done
Creating hello_django_celery-beat_1 ... done

Güzel. Bu kısım Docker ayarlarımızın ortama göre ayrılması içindi. Ama bir de Django ayarlarımız var.

Ortama göre proje ayarları

Bildiğiniz gibi proje ayarlarımızı settings.py dosyamızda saklıyoruz. Bazı öyle ayarlar var ki kesinlikle geliştirme ve production ortamlarında aynı olamaz. Örneğin DEBUG production ortamında kesinlikle False olmalı. Ben bu tip değişiklikleri bir JSON dosyasında saklamayı ve bu özel JSON dosyalarının repo’ya yanlışlıkla gönderilmesini önlemek için de .gitignore dosyasına adının yazılmasını öneriyorum. Çünkü bu dosya sadece sunucu ayarları değil, sunucuya özel gizli bazı bilgileri de barındırabilecek. Örneğin SECRET_KEY, veritabanı erişim bilgileri, özel API anahtarları ve bunun gibi. Bunun için, settings.py dosyamıza basit bir kod ekliyoruz:

# hello_django/settings.py
...
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# 1. Önce proje dizinimizde env.json dosyası var mı araştır.
# 2. Yoksa varsayılan conf/env.dev.json dosyamızı kullan.
env_path = os.path.join(BASE_DIR, 'env.json')
if not os.path.exists(env_path):
    env_path = os.path.join(BASE_DIR, 'conf', 'env.dev.json')
with open(env_path, 'r') as env_file:
    env = json.loads(env_file.read())

DEBUG = env['DEBUG']
...

Böylece DEBUG ayarımız sunucuda oluşturacağımız env.json dosyasından gelecek, repomuzda örneği var:

{
    "DEBUG": false,
    "MEDIA_ROOT": "/assets/media",
    "STATIC_ROOT": "/assets/static"
}

Son bir şey daha, dikkatinizi çektiyse Production için hazırladığımız Docker Compose ayarlarımızda fazladan bir konteyner daha kullandık. Bizim 80 portuna erişebilmemiz, statik ve media dosyalarımıza erişebilmemiz için Nginx’e ihtiyacımız var. Her sunucunun Nginx yapılandırması farklı olur, örnek olarak bunu inceleyelim:

# conf/nginx.prod.json
# detaylı hali repomuzda var.
...
http {
    sendfile on;
    include mime.types;

    server {
        listen 80;
        server_name _;

        ...
        location /static/ {
            root /assets;
            ...
        }

        location /media/ {
            root /assets;
            ...
        }

        location / {
            proxy_pass http://web:8000;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
    }
}

Artık sunucuyu ayağa kaldırabiliriz

Bu yazıyı konumuz olmadığı için uzatmamak adına, sizlerin VPS tercihinizi zaten yaptığınızı (DigitalOcean iyi seçim), zaten bir domain sahibi olduğunuzu veya bu tip konuları netleştirdiğinizi varsayıyorum. Yapacaklarınız çok basit:

  1. Sunucunuza bağlanın, yerelinizde olduğu gibi docker ve docker-compose kurulumunuzu yapın.
  2. Kod reponuzu sunucunuza klonlayın, proje dizinine girin.
  3. Eğer NGINX servisi sunucunuzda çalışıyorsa kapatın, konteyner içinden kullanacağız.
  4. Ve konteynerları oluşturup çalıştırın.

Çalıştırmak için mutlaka production ayarlarımızı kullanacağız:

# terminal
$ docker-compose.exe -f docker-compose.yml -f docker-compose.prod.yml up -d
Creating network "hello_django_default" with the default driver
Creating hello_django_amqp_1  ... done
Creating hello_django_nginx_1  ... done
Creating hello_django_web_1    ... done
Creating hello_django_celery_1 ... done
Creating hello_django_celery-beat_1 ... done

Artık sunucuya erişebiliyor olmalısınız.

Sırada ne var?

10. yazımızı artık projemizi deploy edebilecek aşamada bitiriyoruz. İşlemediğimiz değinmediğimiz bir çok konu olduğunu biliyorsunuz. Bundan sonraki aşamalarda biraz da sizden gelen isteklere göre seriyi sürdürmeyi düşünüyorum. Neyi atladıysam, neyi detaylandırmamı istiyorsanız bana ulaşın, destekte bulunun, ben de bu seriyi elimden geldiğince devam ettirmeye çalışacağım. Umarım sizler için güzel bir yardımcı kaynak olmuştur.


  1. Docker-compose’un Production için elverişli olup olmadığı konusunda arkadaşlarımın da bazı fikirleri var. Fakat ben küçük projelerde veya başlangıç açamasında çok ciddi bir sıkıntı olacağını düşünmüyorum. Bu konuyla ilgili daha fazla bilgi için mutlaka Docker dökümantasyonuna bir göz atmanızı öneriyorum, Docker’in ne kadar kapsamlı bir şey olduğunu anlamanızı isterim. [return]