Raspberry Pi'de Flask Uygulamasını systemd Servisi Olarak Çalıştırmak
Terminal kapanınca ölen Flask uygulamasını, elektrik kesintisinden sonra kendiliğinden ayağa kalkan bir servise dönüştürmenin adım adım yolu: gunicorn, systemd birimi, günlükler ve sık yapılan hatalar.
Bir Flask uygulamasını python app.py ile başlatıp terminali açık bırakmak, denemeler için yeterlidir. Gerçek bir iş için, örneğin bir ofiste her gün kullanılan bir panel için, bu yöntem iki gün içinde sorun çıkarır: SSH oturumu kapanır, elektrik gidip gelir, süreç bellek yetmediği için ölür ve kimse fark etmez. Çözüm, uygulamayı işletim sisteminin kendi hizmet yöneticisi olan systemd'ye teslim etmektir.
Bu yazıda Raspberry Pi OS (Debian tabanlı) üzerinde bir Flask uygulamasını gunicorn ile çalıştırıp systemd servisi haline getireceğiz. Aynı adımlar herhangi bir Debian veya Ubuntu sunucuda da geçerlidir.
1. Neden Flask'in kendi sunucusu değil?
flask run ve app.run() geliştirme sunucusunu başlatır. Resmi belgeler de bunun üretimde kullanılmaması gerektiğini söyler: tek iş parçacıklıdır, hata ayıklayıcı açık kalırsa uzaktan kod çalıştırmaya kapı aralar ve çökme durumunda kendini toparlamaz. Üretimde gunicorn gibi bir WSGI sunucusu kullanılır.
Bir Raspberry Pi 3 gibi sınırlı bellekli (1 GB) bir cihazda 2 ya da 3 işçi (worker) süreci çoğu iç panel için fazlasıyla yeterlidir.
2. Sanal ortam ve gunicorn
Uygulama dizininde ayrı bir sanal ortam kurmak, sistem Python paketlerini kirletmez ve güncellemelerde sürpriz yaşatmaz.
cd ~/uygulama
python3 -m venv venv
source venv/bin/activate
pip install flask gunicorn
Önce elle deneyin. app:app ifadesi "app.py dosyasındaki app nesnesi" demektir:
venv/bin/gunicorn --workers 2 --bind 127.0.0.1:5000 app:app
Başka bir terminalden curl -I http://127.0.0.1:5000/ ile yanıt geldiğini doğrulayın. Çalışıyorsa Ctrl+C ile durdurun.
3. systemd birim dosyası
/etc/systemd/system/uygulama.service dosyasını oluşturun:
[Unit]
Description=Uygulama (Flask + gunicorn)
After=network-online.target mariadb.service
Wants=network-online.target
[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi/uygulama
EnvironmentFile=-/home/pi/uygulama/.env
ExecStart=/home/pi/uygulama/venv/bin/gunicorn --workers 2 --bind 127.0.0.1:5000 app:app
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Satırların anlamı:
- After / Wants: Ağ ve veritabanı hazır olmadan başlama. Elektrik kesintisinden sonra Pi açıldığında servis veritabanından önce kalkarsa ilk istekler hata verir;
After=bunu önler. - User: Servisi root olarak çalıştırmayın. Uygulamanın yalnızca kendi dizinine erişmesi yeterlidir.
- EnvironmentFile: Parolaları ve gizli anahtarları kod yerine bir
.envdosyasında tutun. Baştaki-işareti, dosya yoksa hata vermemesini sağlar. Dosyanın izninichmod 600 .envile kısıtlayın. - Restart=on-failure: Süreç beklenmedik şekilde ölürse 5 saniye sonra yeniden başlatır.
systemctl stopile bilerek durdurduğunuzda yeniden başlatmaz. - NoNewPrivileges / PrivateTmp: Ucuz ama etkili sertleştirme ayarlarıdır.
4. Etkinleştirme ve doğrulama
sudo systemctl daemon-reload
sudo systemctl enable --now uygulama
systemctl status uygulama --no-pager
enable servisi açılışta başlatır, --now ise hemen başlatır. Durum çıktısında active (running) görmelisiniz.
5. Günlükleri okumak
systemd, servisin çıktısını journal'a yazar:
journalctl -u uygulama -n 50 --no-pager # son 50 satır
journalctl -u uygulama -f # canlı izleme
journalctl -u uygulama --since "1 hour ago" # son bir saat
Hata ayıklarken en çok işe yarayan komut budur. Uygulama başlamıyorsa neredeyse her zaman sebebi son birkaç satırda yazar.
6. Kod güncellemesi sonrası
Python kodu bellekte yüklü olduğundan dosyayı değiştirmek yetmez; servisi yeniden başlatmalısınız:
sudo systemctl restart uygulama
Jinja şablonları da çoğu kurulumda önbelleğe alınır. Bir HTML şablonunu değiştirip sonucu görmüyorsanız aynı komutu çalıştırın.
7. Sık yapılan hatalar
| Belirti | Olası neden | Çözüm |
|---|---|---|
status=203/EXEC |
ExecStart yolu yanlış veya dosya çalıştırılabilir değil |
Tam yolu kontrol edin, ls -l ile bakın |
ModuleNotFoundError |
Paket sistemde değil sanal ortamda | ExecStart'ta venv/bin/gunicorn kullanın |
| Açılışta veritabanı hatası | Servis veritabanından önce başladı | After=mariadb.service ekleyin |
Permission denied |
Dosya sahibi farklı | chown -R pi:pi ~/uygulama |
| Sürekli yeniden başlıyor | Kod hata veriyor | journalctl -u uygulama -n 100 |
8. Sonraki adım: dışarıya açmadan önce
Gunicorn'u doğrudan internete açmak iyi fikir değildir. Önüne bir ters vekil (nginx ya da Caddy) koymak veya Cloudflare Tunnel kullanmak, TLS ve istek sınırlaması gibi işleri sizin yerinize halleder. Bu konuyu Cloudflare Tunnel yazımızda ele aldık.
Özetle: sanal ortam, gunicorn, kısa bir birim dosyası ve enable --now. Bu dört adımla uygulamanız elektrik kesintisinden sonra da kimse müdahale etmeden geri döner.