itsrda

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 .env dosyasında tutun. Baştaki - işareti, dosya yoksa hata vermemesini sağlar. Dosyanın iznini chmod 600 .env ile kısıtlayın.
  • Restart=on-failure: Süreç beklenmedik şekilde ölürse 5 saniye sonra yeniden başlatır. systemctl stop ile 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.

Not: Komutları üretim ortamında çalıştırmadan önce yedek alın ve önce test ortamında deneyin. Bir adım sizde farklı davranıyorsa bize yazın, rehberi güncelleyelim.