Nginx ve Gunicorn ile Python Flask Uygulamasını Linux Sunucuda Yayınlama

Burcu S
5 min readFeb 23, 2021

--

Merhabalar merhabalar… Bu yazıyı yazdığım için o kadar mutluyum ki… Çünkü tam olarak 3 gündür uğraştığım şeyi sonunda başarmış bulunuyorum. Evet bence de bu kadar zor olmamalıydı. Dokümantasyonlar da gayet güzel hazırlanmıştı niye böyle oldu ki…

Burada küçük (evet 1 paragraf) bir isyana giriyorum dileyen aşağıdan devam edebilir. NŞA’da bir şeyi araştırırken en dip noktasından başlarım ve karşılaştığım her kavramı da ayrı irdelerim ama gelin görün ki bazen iş dünyasında toplantıya yetişmesi gereken işi saatler kala ya da 1 gün önceden öğrenebiliyorsunuz :D Şimdilik yetişsin de sonra kurcalarım diyip ortasından dalabiliyorsunuz işlere. İşte günler süren, linux server üzerinde python flask ile geliştirdiğim web servisi yayına alma vakam da tam olarak böyle bir senaryoda gerçekleşti. Önce gelen kulaktan dolma “şunu yap, bunu yap” direktiflerini çözüme kavuşma umuduyla yapmaya başlayarak konuya ortasından dalmış bulundum (sonradan anladım) . Halbuki o iş hiç de öyle değilmiş. Olmayacak yöntemin derinlerine dalıp her şey beynimin içinde karmaşıklaşırken zamanımın kalmadığını da fark edince hiç benlik olmasa da dokümanda gördüğüm her kodu sırasıyla çalıştırmaya başladım. Sonuç olarak olası hatalarla birlikte yaklaşık 1 saat alacak bir işi 3 güne uzattım :) Buradaki ibretlik zaman kaybını göz önünde bulundurarak şu tavsiyeyi vermek istiyorum: Eğer kendinize has bir çalışma stiliniz varsa, işleri yüzeysel çalışmalarla geçiştirmekten rahatsız oluyorsanız ve bir konuya hakim olmak için benim gibi önce ön bilgi sonra mantığına ve dallanan tüm detaylarına hakim olmanız gerekiyorsa kısaca detaycı, sorgulayıcı ve mükemmelliyetçilik sıfatlarına ya da bunlardan herhangi birine sahipseniz bir işi yapmaya başlarken “o şöyle yapıyordu sen de oraya şunu ekle, bunu koy” gibi güzel tavsiyeleri, konuyu araştırmak için vaktiniz olmasa bile en başından reddedip sizi siz yapan metodlarınızı kullanın. Şimdi konumuza dönelim.

Bu yazıda sizlere hataları evrimleştirerek ürettiğim 13443535 farklı hatayı olmasa da karşılaştığım en olası hataları ve çözümlerini göstererek bir flask application’ı linux server üzerinde nasıl deploy edeceğimizi anlatacağım.

Hadi Başlayalım…

Python Virtual Environment ve Proje Dosyalarını Oluşturma

Python uygulamamızı çalıştırmak için bir virtual environment oluşturmamız gerekiyor. Virtual environment sayesinde projemizin kaynak yönetimini proje düzeyinde tutabiliyoruz. Bir başka deyişle modüllerimizi servera değil sadece kendi proje klasörümüz düzeyindeki sanal çalışma ortamımıza yükleyerek bir izolasyon sağlıyoruz.

Adımları anlatırken bir Python3.x kurulu olduğunu varsayacağım.

$ sudo apt install python3-venv

ile virtual environtımızı yüklüyoruz.

Serverda çalışırken çalıştığımız user myuser olsun. Kullanıcımızın klasörü altına bir proje klasörü açalım. (Bunun için kullanıcı klasörümüz altında olduğumuzu varsayıyorum.)

$ mkdir flaskapp$ cd flaskapp

diyerek proje klasörümüze geçiş yapıyoruz. Proje klasörümüz altında bir virtual environment oluşturmamız gerekli.

Benim python sürümüm python3.6.9 idi o sebeple

$ python3.6 -m venv flaskappenv

komutunu kullandım.

$ python3 -m venv flaskappenv

ile de yapabiliriz. Artık projemiz için bir sanal ortam oluşturduk. Şimdi gerekli paketlerin yüklenmesini gerçekleştireceğimiz için sanal ortamımızın aktif olması gerekiyor. Sanal ortamımızı flaskapp (proje) klasörü içerisinde olduğumuzu varsayarak:

$ source flaskappenv/bin/activate 

diyerek aktive edebiliriz.

Virtual environment aktifken python versiyonumuz ne olursa olsun pip komutunu kullanıyoruz. İhtiyacımız olan paketler:

$ (flaskappenv) pip install wheel$ (flaskappenv) pip install flask gunicorn

Eğer kodlarımız github üzerindeki bir repodaysa clonelayıp asıl .py dosyalarını flaskapp düzeyine taşıyabiliriz. (Ya da git klasörünü asıl proje klasörü olarak da kullanabilirsiniz.)

Şimdi flask uygulamamızın myflaskapp.py adında olduğunu varsayalım.

from flask import Flask
app = Flask(__name__)

@app.route("/")
def myflask():
return "Here is!"

if __name__ == "__main__":
app.run(host='0.0.0.0')

Şimdi uygulamamızı bir test edelim. Flask default olarak 5000 portunda çalıştığından eğer bir firewall varsa test esnasında

$ sudo ufw allow 5000 

diyerek deaktif edelim.

$(flaskappenv) python3 myflaskapp.py 

diyerek kodumuzu kontrol edelim. Şu an kodumuz http://0.0.0.0:5000'de çalıştığından serverımızın iç ip’si üzerinden istek atıp test edebiliriz. Burada problem olmadığını varsayarak devam ediyorum.

Gunicorn Konfigürasyonu

Projemiz normal bir python projesi olarak localde çalışıyor. Şimdi yavaş yavaş serve etme adımlarına geçelim. Gunicorn konfigürasyonumuzda kullanmak için bir wsgi noktası bir başka deyişle web server ağ geçidi interfacei oluşturmamız gerekiyor. Proje klasörümüz içerisinde myflaskapp.py adlı proje kodumuz bulunmaktaydı. Şimdi wsgi.py adlı bir python dosyası oluşturalım. wsgi.py dosyamızın içeriği:

from myflaskapp import app

if __name__ == "__main__":
app.run()

Böylece kodumuz ve gunicorn arasında bir interface (wsgi.py) oluşturduk diyebilliriz.

Proje sanal ortamımız aktif şekilde

$(flaskappenv) gunicorn myflaskapp:app 

diyerek gunicorn ile çalışma aşamasında bir problem olup olmadığına bakabiliriz. Ancak bu test içindi biz serve ederken app olarak doğrudan myflaskapp’i değil oluşturduğumuz arayüzü yani wsgi.py’yi kullanacağız. Arayüzümüze public olarak erişimin olabilmesi için bu arayüzü ve portu bind etmemiz gerekli.

$(flaskappenv) gunicorn --bind 0.0.0.0:5000 wsgi:app

Artık gunicorn ile ilgili konfigürasyonumuz tamamlandığından uygulamamızın server üzerinde servis olarak çalıştırılması aşamasına geçebiliriz. İşlemlerimiz sanal ortamla ilgili olmayacağından

(flaskappenv) deactive diyerek sanal ortamımızı deaktif edebiliriz.

Uygulamayı Servise Dönüştürme

Servis dosyamızı /etc/systemd/system/ klasörü içerisinde oluşturacağız.

Oluştururken servis adını proje klasörümün adı ile aynı seçiyorum.

$ sudo nano /etc/systemd/system/flaskapp.service

flaskapp.service içeriğimiz:

[Unit]
Description=Gunicorn instance to serve flaskapp
After=network.target

[Service]
User=myuser
Group=www-data
WorkingDirectory=/home/myuser/flaskapp
Environment="PATH=/home/myuser/flaskapp/flaskappenv/bin"
ExecStart=/home/myuser/myflaskapp/flaskappenv/bin/gunicorn --workers 3 --bind unix:/home/myuser/flaskapp/flaskapp.sock -m 007 wsgi:app

[Install]
WantedBy=multi-user.target

Ne yaptığımızı özetlersek: Öncelikle servisin açıklamasını ve hangi servisten sonra başlatılacağını belirtiyoruz. Servis kısmında ise servisimiz için gerekli parametreleri belirtiyoruz. Install kısmında ise servisin çalışma modunu veriyoruz.

$ workers 3 --bind unix:/home/myuser/flaskapp/flaskapp.sock -m 007 wsgi:app

Kısmında 3 worker process ile (Çekirdek başına genellikle 2–4 arası worker process olmakla birlikte bu kısmı kendi gerekliliklerinize göre ayarlayabilirsiniz.) başlatıyoruz. Sonrasında verdiğimiz pathe verdiğimiz isimde oluşturulacak olan socket dosyasını bind ediyoruz. -m 0007 ile bu socket dosyamızın izinlerini sadece belirlediğimiz user ve group parametrelerine açıyoruz. wsgi:app ise daha önce “ gunicorn — bind 0.0.0.0:5000 wsgi:app” ile test etme işleminde de bahsettiğim gibi arayüz ve port binding gerçekleştiriyoruz.

Önemli Nokta:

Eğer kullanıcı üzerinden değil roottan çalışıyorsanız. “-m 007” işlemimizi gözden kaçırmayın. Aldığım hatayı kaydetmemişim ancak eğer servisin çalışması esnasında bir hata alıyorsanız proje klasörünüzün -yazımız kapsamında flaskapp- izinlerini mutlaka kontrol edin. İzinlerimizde user ve group kısmımız “flaskapp.service” dosyasında belirttiğimiz user ve groupla aynı olmalı. Roottan çalıştıysanız bunlar root root olarak oluşturulacaktır. Bu aşamada:

$ sudo chown myuser:www-data -R /home/myuser/flaskapp

komutu ile gerekli güncellemeyi yapabilirsiniz.

$ ls -ld home/myuser/flaskapp/

komutu ile de kontrol edebilirsiniz.

Artık gunicorn tarafının konfigürasyonunu da tamamladık. Bu noktada servisimiz çalışır durumda olmalı. Bunu önce bir kontrol edelim.

$ systemctl start flaskapp$ systemctl enable flaskapp 

diyerek servisimizi başlatalım. Başlattıktan sonra

$ systemctl status flaskapp 

diyelim.

Eğer bir hata görmüyorsak “oh beee” diyip devam edebiliriz. (Eğer hata alıyorsanız proje klasörü içerisinde venv komutu ile oluşturduğumuz sanal ortam klasörümüzde bulunan bin dizininde, gunicorn bulunup bulunmadığını kontrol edebilirsiniz. Eğer gunicorn bulunmuyorsa bunu indirmeniz gerekiyor.)

Sırada uygulamamıza external ip’mizden ya da vereceğimiz domainden erişim sağlamak için yapmamız gereken Nginx konfigürasyonu var.

Nginx Konfigürasyonu

/etc/nginx klasörümüz altında sites-available ve sites-enabled olmak üzere 2 klasör bulunmakta. Bunlardan sites-available klasörü serverda bulunan fakat etkin olmayan siteleri barındırır. sites-enabled ise erişime açık, etkin siteleri tutar. Daha doğru bir ifadeyle sites-availableda etkin olan-olmayan tüm sitelerin konfigürasyonları bulunur ve siteler sites-enabled’da bulunmadığı sürece etkin değildir. Biz de sites-available’da konfigürasyon dosyamızı oluşturacağız ve sites-enabled klasörüne bunun bir sembolik linkini ekleyeceğiz. Böylece konfigürasyon kurallarını ayarladığımız flask uygulamamızı yayına almış olacağız.

$ sudo nano /etc/nginx/sites-available/flaskapp

diyerek dosyamızı oluşturmak üzere terminalden açıyoruz.

server {
listen dinlenecek_port_no;
server_name external_ip domain_name;

location / {
proxy_pass http://unix:/home/myuser/flaskapp/flaskapp.sock;
}
}

Artık sites-available içindeki flaskapp dosyamızı kaydedip sites-enabled’a sembolik link oluşturabiliriz.

$ sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled

diyerek ya da /etc/nginx/sites-available klasörü içerisinde

$ sudo ln -s “$(pwd)/flaskapp” /etc/nginx/sites-enabled

diyerek bunu oluşturabiliriz. Şimdi nginx servisimizi yeniden başlatmamız gerekmekte.

$ sudo systemctl restart nginx

Bundan sonra firewallda verdiğimiz port 5000 iznini tekrar kaldırabiliriz, sonrasında nginxe tüm erişimi vermemiz gerekli.

$ sudo ufw delete allow 5000$ sudo ufw allow ‘Nginx Full’

Önemli Nokta

nginx konfigürasyon dosyanızda belirttiğiniz portun port yönlendirmesi ayarına dikkat edin. (Genellikle örneklerde 80 verilir burada 80'in özel bir anlamı yoktur. Siz istediğiniz portu verebilirsiniz.) Uygulamanız için dinlenmesi gereken portunuzun serverda dışardan erişime açık bir port olması gerektiğini unutmayın.

Gözden kaçırdığınız bir nokta yoksa artık siteniz linux sunucuda yayında. Bir sonraki yazıda görüşmek üzere :)

Kaynaklar: 1, 2

--

--

Burcu S

Flutter Developer, Lover & Learner | Computer Engineer | For contact: linkedin.com/in/burcus/