Azure App Servis ile Flask Uygulaması

Flask Uygulamasının VS Code Üzerinden Github ve Azure'a Yüklenmesi

Posted on March 16, 2023

Bu yazı, bir Flask uygulamasının VS Code üzerinden Github ve Azure üzerinde nasıl yayınlanacağına dair bir rehber olması amacyla tasarlanmıştır. Burada amaç Flask uygulanmasına odaklanmak değil, hazır bir uygulamanın github ve cloud a servis edilmesi ile ilgilidir.

Hazırlıklar


  • Github Hesabının Oluşturulması

    Flask uygulamasını Github üzerinden paylaşmak için bir Github hesabına ihtiyacımız var. Hesabınız yoksa Github'a kaydolmak için https://github.com/ adresine gidip kayıt işlemini tamamlayabilirsiniz.


  • VS Code Kurulumu

    Flask uygulamasının kodlanması, Github'a yüklenmesi ve Azure Ortamı ile etkileşim VS Code üzerinde yapılacağı için VS Code'u bilgisayarınıza indirin ve yükleyin.


  • Sanal ortamın yaratılması

    Öncelikle flask projesi için bir klasör yaratılmalıdır. Sonrasında bu klasör içine girip terminale geçiş yapılmalıdır.

    Aşağıdaki komutlar ile uygulama bağımlılıklarının depolanacağı bir sanal ortamın oluşturulmasına sağlanacaktır

    python -m venv .env

    Sanal ortamı aktif hale getirin:

    Windows için
    env\Scripts\activate
    Linux için
    source env/bin/activate

    Sanal ortamda Flask paketini yüklemek için aşağıdaki komutu kullanacağız:

    pip install flask
    Ayrıca, uygulamanızda kullanacağınız diğer paketleri de benzer şekilde yükleyebilirsiniz.

    Paketleri yükledikten sonra, pip freeze komutunu kullanarak tüm bağımlılıkları requirements.txt dosyasına kaydedeceğiz:

    pip freeze > requirements.txt

  • Flask Uygulamasının Kodlanması

    Proje klasörü içinde "app" adı altında bir klasör yaratcağız. Sonrasında onun içinde "static" ve "templates" adı altında 2 klasör daha yaratacağız. Ayrıca 3 tane de dosya yaratacağız. Görünüm şu şekildedir.

    Resim

    Kod içeren dosyalar ve kodlar aşağıdaki gibidir.

    __init__.py
    from flask import Flask, render_template
    
    app = Flask(__name__)
    
    @app.route('/')
    def index():
        return render_template('index.html')
    
    if __name__ == "__main__":
        app.run(port=5000, debug=True)
    

    index.html
    <!DOCTYPE html>
    <html>
    <head>
        <title>Merhaba Giriş Sayfası</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet"
                href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
    </head>
    <body>
        <div class="container-fluid bg-light py-3">
            <div class="container">
                <div class="row">
                    <div class="col-md-12 text-center">
                        <h1 class="display-3 mb-2">Merhaba Flask ve Bootstrap!</h1>
                        <p class="lead mb-5">Bu, Flask ve Bootstrap ile oluşturulan bir örnek sayfadır.</p>
                        <a href="#" class="btn btn-primary btn-lg">Daha Fazla</a>
                    </div>
                </div>
            </div>
        </div>
        <div class="container-fluid bg-dark text-white py-3">
            <div class="container">
                <div class="row">
                 <div class="col-md-12 text-center">
                        <br>
                        <p class="mb-0">Daha fazla Flask ve Bootstrap örneği için
                                <a href="#">buraya</a> tıklayın.</p>
                        <br>
                    </div>
                </div>
            </div>
        </div>
    </body>
    </html>
    

    startup.py
    from app import app

    Kodlamayı bitirdiğimizde projeyi çalıştırabiliriz.

    python __init__.py
    (env) D:\Workspace\Python\Flask\Flask_Projesi\app>python __init__.py
     * Serving Flask app '__init__'
     * Debug mode: on
    WARNING: This is a development server. Do not use it in a production deployment.
        Use a production WSGI server instead.
     * Running on http://127.0.0.1:5000
    Press CTRL+C to quit
     * Restarting with stat
     * Debugger is active!
     * Debugger PIN: 278-563-770
    127.0.0.1 - - [17/Mar/2023 00:04:28] "GET / HTTP/1.1" 200 -

Uygulamanın görüntüsü şu şekildedir.

Resim



  • Git yapılandırması

    Projemizin Github yapılandırması bu aşamada olacak. Sonrasında yapacağımız her commit ile kodlarımız Github Repository'e atılmış olacak.

    Öncelikle Github a yüklenmesini istemediğimiz dosya ve klasörlerin adını ".gitignore" dosyasına eklemeliyiz. Örneğin sanal ortam yada bir takım cache amaçlı klasörlerin Repository'e atılmasına gerek yoktur.

    Resim

    VS Code'un "Source Control" sekmesine gidip oradan "Initialize Repository" diyerek dosya ve klasölerimizi Git kapsamına almış olacağız. Esasında git yapılandırmasını terminalden de yapabiliriz ama burada VS Code'un bize sağladığı kolaylıktan faydalanmış olacağız.

    Resim

    Initialize Repository dediğimizde VS Code bizim için git yapılandırmasını hazır hale getirir. Esasında klasör içinde ".git" adında gizli bir klasör yaratılır. Commit tuşuna basmadan önce tuşun üstündeki alana "Açıklama girilir.Her commit aslında kodumuzun bir aşamasıdır ve ilerde bu aşamalar arasında geçiş yapma şansımız olacaktır. Bu nedenle aşamaların ne olduğunun anlaşılır bir şekilde yazılması önemlidir.

    Resim

    Commit tuşuna bastığımızda aşağıdaki gibi bir soru karşımıza gelir.

    Resim

    Bu mesaj, henüz değişikliklerin kaydedilmediğini veya sahneye konulmadığını (staged) belirtiyor. "There are no staged changes to commit" mesajı, değişikliklerin henüz sahneye konulmadığını gösterirken, "Would you like to stage all your changes and commit them directly?" mesajı, değişikliklerin sahneye konulup commit edilmesi için sizden onay istiyor.

    Git'te, dosyaların değişikliklerini kaydetmek için iki aşama vardır. İlk olarak, değişikliklerinizi "sahneye koymalısınız" (stage), yani dosyaları değişiklikler için hazır hale getirmelisiniz. Bu işlem, git add komutuyla yapılır. Daha sonra, değişiklikleri "commit" ederek (yani kaydederek) Git deposuna göndermelisiniz. Bu işlem, git commit komutuyla yapılır.

    Eğer henüz değişiklikleri sahneye koymadıysanız ve git commit komutunu çalıştırırsanız, Git size "There are no staged changes to commit" mesajını verir çünkü değişiklikler henüz sahneye konulmadı. Bu nedenle, Git değişiklikleri sahneye koymak isteyip istemediğinizi sorar.

    Eğer "yes" derseniz, Git tüm değişiklikleri sahneye koyar ve hemen ardından commit işlemini gerçekleştirir. Böylece, hem dosyaların değişiklikleri kaydedilir hem de Git deposuna gönderilir.

    Peki ne yapsaydık bu soru karşımıza gelmezdi

    "Source Control" yan tarafındaki üç nokta (...) işaretine basınca açılan menüde Changes sekmesi altında "Stage All Changes" dersek bu işlem değişikliklerin sahneye (staging area) konmasını sağlar.

    Resim

    İlk commit işleminde dosyalar olduğu gibi içeri alınır ancak sonrasında her bir değişiklik loglanır bir nevi. Dosyalardaki bu değişikliğin detayı istenirse incelenebilir

    Resim

    Şu ana kadar yapılanların hepsi lokalde gizli ".git" klasörü içinde yapıldı. Şimdi projemizi artık remote bir repository'e atabiliriz. Bunun için Publish Branch diyeceğiz. Eğer VS Code'da Github'a login değilsek login olmamız istenecek

    Resim

    Sonrasında Github bize Repository adını soracak (Default değer olarak mevcut klasörümüzün adı gelir ama bunu değiştirebiliriz)

    Resim

    Github'da projemizin yaratıldığını görebiliriz

    Resim

    Dikkatli bakarsak "static" klasörünün Github'da olmadığını görürüz. Çünkü aslında git commit esnasında da bu klasör eklenmemişti.

    Bunun nedeni, Git, boş bir klasörü doğrudan bir commit'e eklemeyi desteklemez. Böyle bir klasörü repository'nin içinde bulundurmak isterseniz, klasör içinde boş da olsa bir dosya oluşturmanız gerekir. Bu şekilde klasörü de stage'e alabilirsiniz.


  • Azure Hesabının Oluşturulması

    Flask uygulamasının Azure ortamına deploy edilebilmesi için bir Azure hesabına ihtiyacımız var. Azure'a https://azure.microsoft.com/tr-tr/free/ adresinden kayıt olabilirsiniz.

  • VS Code Üzerinde Azure Tools Extension'larının yüklenmesi

    Azure ortamında servisimizin yaratılması ve Github'la entegre bir şekilde Build ve Deployment süreçlerinin yapılabilmesi için gerekli olan extensionlar bu aşamada yüklenecek

    Resim

    Kurulumlar bittiğinde ekranda sol tarafta "A" harfinde yeni bir menü eklentisi görünecektir. Onu seçtiğimizde Azure ile ilgili yapabileceğimiz işlemler ekranda gözükecektir. Buradan Azur'a login olunmalıdır.

    Resim

  • Azure üzerinde WEB App Service'in Yaratılması

    Azure'da bir hesap açtığımızda bir subscription da yapılmış olacaktır. Bunun altında çeşitli seçenekler ekranda gözükür. Biz "App Services" seçeneği üstüne sağ tıkladığımıda gelen menüden "Create New App...(Advanced)" seçeneği ile ilerleyeceğiz. Buna tıkladığımızda sağ tarafta 6 adımdan oluşan bir seçim süreci başlayacaktır.

    Resim

    Sırasıyla adımlar şu şekildedir:

    Resim

    Resim

    Resim

    Resim

    Resim

    Resim

    Ben burada B2 planını seçtim.Diğer türlü kapasite sorunu yaşama ihtimali var. Zaten hemen bu yazının sonrasında sistemi kapattım.

    Resim

    Bu adımı (7.Nolu) "Skip" deyip geçiyoruz.

    Resim

    Resim

    Resim

    Eğer tüm adımlar başarı ile biterse uygulamamız "App Services" altında gözükecek. Bu aşamada konfigürasyonu tamamlamak için uygulamaya sağ tıklıyoruz.

    Resim

    Açılan Azure sayfasında Configuration sekmesinde Startup Command alanını doldurmamız gerekiyor. Biz Azur'un Linux tabanlı bir sunucudan uygulamayı servis etmesini istediğimiz için ( bknz: 4.adım ) "gunicorn" ile Flask uygulaması ayağa kaldırılacak. Aşağıdaki komutta "0.0.0.0" kullanıldığında, uygulamaya sunucu dışından erişim olacak demektir. Böylece herhangi bir istemci, uygulamaya erişmek için sunucunun IP adresini kullanabilir.

    Son olarak "startup:app" ile startup.py dosyasındaki app in çağrılması gerektiğini belirtiyoruz.

    gunicorn --bind=0.0.0.0 --timeout=700 startup:app

    Resim

    Configuration sekmesinde başka bir şeyi değiştirmiyoruz ve artık son olarak Deployment Center sekmesine geçiyoruz.

    Burada "*" ile işaretli olan zorunlu alanları aşağıdaki gibi seçiyoruz..

    Resim

    "Save" tuşuna bastığımız anda Azure, GitHub'la etkileşime geçecektir. Peki ne yapacaktır. Bu sorunun cevabı "Preview File" tuşuna basınca açılan YAML dosyasındadır. Save tuşuna basmadan önce bunu inceleyelim.

    # Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy
    # More GitHub Actions for Azure: https://github.com/Azure/actions
    # More info on Python, GitHub Actions, and Azure App Service: https://aka.ms/python-webapps-actions
    
    name: Build and deploy Python app to Azure Web App - mybasicflask
    on:
      push:
        branches:
          - master
      workflow_dispatch:
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v2
          - name: Set up Python version
            uses: actions/setup-python@v1
            with:
              python-version: '3.9'
          - name: Create and start virtual environment
            run: |
              python -m venv venv
              source venv/bin/activate
          - name: Install dependencies
            run: pip install -r requirements.txt
          # Optional: Add step to run tests here (PyTest, Django test suites, etc.)
          - name: Upload artifact for deployment jobs
            uses: actions/upload-artifact@v2
            with:
              name: python-app
              path: |
                .
                !venv/
    
      deploy:
        runs-on: ubuntu-latest
        needs: build
        environment:
          name: 'production'
          url: ${{ steps.deploy-to-webapp.outputs.webapp-url }}
    
        steps:
          - name: Download artifact from build job
            uses: actions/download-artifact@v2
            with:
              name: python-app
              path: .
    
          - name: 'Deploy to Azure Web App'
            uses: azure/webapps-deploy@v2
            id: deploy-to-webapp
            with:
              app-name: 'mybasicflask'
              slot-name: 'production'
              publish-profile: ${{ secrets.AzureAppService_PublishProfile_e2fa9a39020845b4b1a4f47a966ff939 }}

    Bu dosya Github'a iletilecek olan ve Github workflow'unu tanımlayan dosyadır. Bu GitHub aksiyonu, bir Python uygulamasını Azure Web App'e nasıl dağıtacağınızı otomatikleştirmek için tasarlanmıştır. İş akışı(Workflow), "on:" sekmesinden anlaşılacağı üzere, "push" yapılarak (burada biz save tuşuna basmakla push etmiş olacağız) veya manuel olarak tetiklenebilen workflow_dispatch olayı ile çalıştırılır. İş akışının iki aşaması vardır: build (derleme) ve deploy (dağıtım).

    Build aşaması, uygulamayı derleyerek ve gerekli bağımlılıkları kurarak bir sanal paket oluşturur. Sanal paket daha sonra bir "artifact" olarak yüklenir, böylece dağıtım aşamasında kullanılabilir hale getirilir. "Artifact" deiğimiz şey Github'dan indirebileeğimiz, bu projede en azından ZIP formatında ve içinde build edilmiş halde kodların bulunduğu bir dosyadır.Bizim projemiz python olduğu için aslında kodlarımız birebir halde bu dosya içindedir.

    Deploy aşamasında ise, build aşamasında oluşturulan sanal paketi indirilir ve Azure Web App'e dağıtılır. Bu aşama, ubuntu-latest çalışma ortamında çalışır ("runs-on: ubuntu-latest" kısmından anlıyoruz) ve build aşamasına ihtiyaç duyar ("needs: build" kısmından anlıyoruz).

    Biz workflow içerisine bir test tanımı yapmadık ama Depoloyment öncesi bir takım otomatik testler yapılabilirdi ("# Optional: Add step to run tests here (PyTest, Django test suites, etc.)" kısmında değinilen konu budur) .

    "Save" tuşuna basıp hemen Github'a gelip projemizin "Actions" sekmesine baktığımızda çalışan bir workflow göreceğiz. Belirli bir süre çalıştıktan sonra eğer bir hata ile karşılaşmaz isek web uygulamamıza erişebilir hale gelmiş olacağız.

    Ben başka bir projede hata almıştım. "Plan" kısmında "Free" bir makine seçmiştim ve onun disk kapasitesi yeterli olmamıştı. Sonra planı değiştirip tekrar worklow'u tetiklediğimde sorun düzeldi.

    Resim

    Workflow instance'ın içine girdiğimizde (Zamanla çok sayıda workflow instance'ı oluşabilir. Örneğin kodda bir değişiklik yapıp süreçleri işlettiğimizde yeni bir build ve deployment süreci işletilmiş olacaktır) "Build" ve "Deploy" aşamalarını görmüş oluruz.

    Resim

    Build sürecinde gerçekleştirilen eylemlerin ekran görüntüsüne erişmek için yukarıdaki ekranda "Build" yazan kısma tıklamak yeterlidir. Bu durumda aşağıdaki görüntüyü görmüş oluruz.

    Burada genel olarak öncelikle python sanal ortamı yaratılıyor ve "requirement.txt" dosyasında yer alan paketler sisteme yükleniyor. Sonrasında kodlar Zip'lenip "Artifact" haline getiriliyor. Bu işler normal olarak Github tarafından Azure Makinesine yaptırılıyor. Sonrasında ise "Artifact" dosyası Github'a Upload ediliyor.

    Resim

    Artifact dosyasına "workflow" içinde en alt kısımdan erişebilirsiniz.

    Resim

    Artifact dosyalar Github'da belirli bir süre saklanır. Ne kadar saklanacağı ayarlar kısmından değiştirilebilir.

    Resim

    Benzer şekilde "Deploy" aşamasınında alt adımlar da detaylı bir şekilde incelenebilir.

    Resim

    Build ve deploy sürelerine Github üzerinden erişilebilir.

    Resim

    Yazının başında development aşamasında eriştiğimiz ekrana artık Azure üzerinden "http://mybasicflask.azurewebsites.net/" linkinden erişebilir olduk. Bu link şu an çalışmaz çünkü yaz bittiğinde ben mevcut sunucuyu kapatmış olacağım.


Uygulamanın productiondaki son görüntüsü şu şekildedir.

Resim


Bu aşamadan sonra Proje için Github'a yapılacak her yüklemede otomatik olarak Build ve Deploy iş akışları (workflow) devreye girecek ve uygulamamızda yapılan değişiklikler anında Production'da kendiliğinden devreye alınmış olacaklar.