Simple Azure FastAPI app (to learn Azure step by step)

Anh-Thi Dinh
Vietnamese
Mục tiêu: tạo được một web app đơn giản (có thể không cần frontend thì càng tốt), có vài endpoint có thể chạy được. Làm starter cho bất cứ project nào sau này!
⚠️
Hãy cẩn thận, khi bạn create quá nhiều (tầm 5 cái) app services thì sẽ có lỗi 429 “App Service Plan Create operation is throttled for subscription xxx. Please contact support if issue persists.”. Khi ấy bạn phải đợi 48h mới có thể tạo cái mới (trong cùng region)
Solution là có thể tạo 1 subscription mới.
Source code:

References

  • Port mặc định là 80, nếu muốn đổi cái khác thì dùng WEBSITE_PORT, ref.

Create and deploy via Application Code

☝️
Nhược điểm của cái này là nếu cài 1 dependencies thêm thì làm khá phức tạp (ví dụ muốn cài ffmpeg trong container của App Service. Chỉ có cách là dùng Azure Function (1 service khác, có plan) → Trong trường hợp đó thì nên dùng Docker container!
  • ⚠️ Don’t install VSCode Azure extension, otherwise you loose your RAM!
  • Rename folder cho dễ làm việc
    • 1mv msdocs-python-fastapi-webapp-quickstart fastapi-quickstart
  • Có chút thay đổi chỗ tạo python env
    • 1conda create -n fastapi-quickstart python=3.11
      2conda activate fastapi-quickstart
  • Thử deploy app dùng Azure CLI
    • ⚠️ Ngay cái bước “Create a web app in Azure”, nếu dùng Azure CLI thì command az webapp up sẽ tự động tạo hết các resource group, app service plan, webapp service luôn!!!
      • Do đó nếu dùng Azure Portal để tạo thủ công mấy cái ở trên thì phải deploy bằng cách khác chứ ko dùng Azure CLI được
    • Lưu ý: để biết giá trị $RESOURCE_GROUP_NAME$APP_SERVICE_NAME thì xem ở Overview.
  • 🐞 Error: didn't respond to HTTP pings on port: 8000, failing site start. See container logs for debugging. ← chưa có up cái codes lên!!!!

Github CI/CD, auto deploy.

  • 🎯 Mục tiêu: Auto CD/CI từ Github (trong cái link tut không có option này).
  • Nếu dùng fastapi mới (ko dùng gunicorn mà dùng fastapi)
    • 1fastapi run main.py
      Startup command.

Nếu muốn install 1 package như ffmpeg trong App service?

  • Nếu dùng docker, mình có thể install nó thông qua apt install -y ffmpeg.
  • Cách hay nhất vẫn là thiết lập qua 1 docker container riêng!

Create and deploy with Docker Container

☝️
Lý do chọn cái này vì muốn cài custom dependencies trong container của App Service (ví dụ cài ffmpeg).
  • ⚠️ Mọi bước đều chú ý đến location and region cho đồng bộ và có quota!
  • ❓Build via docker + auto deploy CD/CI github, is it possible? ← Yes, mỗi khi code thay đổi (trên Github), nó sẽ build lại image + create container,…
  • Để có thể test được, tốt nhất là pay-as-you-go. Thậm chí đang dùng free $200 credits vẫn không được. Ví dụ sẽ bị lỗi không increase quota được.
  • Tạo Resource Group: cái này sẽ chung cho tất cả các cái trong cùng 1 project.
    • Có thể tạo trên Azure Portal > search “Resource Group”
    • Hoặc dùng code (source)
    • Lưu ý chọn location cho kỹ, ko đổi được (cái đang làm: “France Central”)
  • Tạo Azure Container Registry hay ACR (để upload docker image)
  • Có 2 cách build và push image lên ACR:
  • 🐞 Khi create 1 App Service mới mà gặp lỗi Cannot perform credential operations for *** as admin user is disabled. Kindly enable admin user as per docs
    • 1az acr update -n <acrName> --admin-enabled true
  • ⚠️ Tại bước Container, có mục Startup command, hãy sửa cái mặc định thành cái mong muốn.
  • Lúc tạo App Service, tới bước chọn Startup command, điền fastapi run app.py (còn nếu quên thì có thể chỉnh lại trong cái app service mới tạo → Settings → Configuration → General Settings)
  • Check xem 1 web service có chạy ok không → vào service → Deployment → “Deployment Center” → Logs
  • Nếu muốn test thử xem cái dependencies đã cài riêng trong docker có chạy được ko (eg. ffmpeg) thì có thể vào Developement Tools → SSH
    • Có thể SSH chưa được hỗ trợ, check trong Settings → Configuration
      • Dù cho trong Settings → Configuration → SSH nó nói “SSH isn't supported for your selected stack version.” nhưng vẫn có thể mở SSH được! Phải vào chỗ Developement Tools → SSH để xác nhận mới chính xác!
    • Good to read: Enabling SSH on Linux Web App for Containers
    • ⚠️ Don’t forget to restart the app service to update the latest img if things don’t work as expetected!

Update image and re-deploy App Service

Nếu image được update (change Dockerfile chẳng hạn), làm sao update lên ACR và deploy lại App Service?
  • Chỉ cần build và push lên ACR là nó tự động (nếu cùng tag, tuy nhiên thỉnh thoảng phải restart lại App Service. Nhớ là phải enable “Continuous deployment”!
    • Sau khi turn on CD thì trong Container Registries cũng sẽ tạo 1 webhook tương ứng (check trong CR → Services → Webbooks)
  • Nếu khác tag thì sau khi push lên ACR, trong App Service → Deployment → Deployment Center → chọn lại Tag đã push.

Auto CD-CI + deploy from Github

Mong muốn là chỉ cần sửa code rồi push lên Github là nó sẽ tự động build image + update và deploy lại App Service luôn! Chưa kể có thể tự động chạy tests.
  • Trong App Service → Deployment → Deployment Center → Chọn “Source” là “Github Actions…” → kết nối với Github và chọn các thông số tương ứng → Preview file (file mà nó sẽ ghi vào Github Action, có đầy đủ các setup mới nhất.
  • Trước đó cần kích hoạt SCM trước, ko thì sẽ có warning “SCM basic authentication is disabled for your app. Click here to go to your configuration settings to enable.” và ko thể Save được. ← Chỉ cần nhấn vào warning, vào chỗ Configuration để turn on SCM là được!

Access diagnostic logs

1# turn on container logging
2az webapp log config --name <app-name> --resource-group <resource-group-name> --docker-container-logging filesystem
3
4# check log stream
5az webapp log tail --name <app-name> --resource-group <resource-group-name>
If you don't see console logs immediately, check again in 30 seconds. Use Ctrl+C to stop log stream. (source)
Alternative way: https://<app-name>.scm.azurewebsites.net/api/logs/docker.

Good to know

  • (Source) You can use the /home directory in your custom container file system to persist files across restarts and share them across instances

Good to read

Health check

Different envs (prod / dev)

  • Deployment slots ← need plan Standard or Premium (cannot use it with plan Basic) ← activate in Settings → Scale up (App Service plan)
  • Tránh cold start cho prod, có thể dùng auto swap (ko hỗ trợ web app trên Linux và Web App for Containers)
  • Vào Deployment → Deployment slots → tạo slot mới xong, trong slot mới, vào Deployment → Deployment Center chỉnh source Github và chọn nhánh dev.
    • ⚠️
      Lưu ý, cái file .yml tự động tạo có chút lỗi chỗ jobs.build.steps['Build and push container image to registry'].with.tags và chỗ jobs.deploy.steps['Deploy to Azure Web App'].with.images. Cả 2 cái đều để dư secrets.AzureAppService_PublishProfile_xxx dẫn đến tình trạng khi vào Deployment → Deployment Center của từng slot, sẽ thấy chỗ Image name bị dư kiểu v2acr2/v2daimg.
      Nếu có lỡ bị dư kiểu này, có thể sửa trực tiếp trên file .yml rồi push lên Github là nó tự động fix.
  • Lưu ý khi create new env var, có setting “Deployment slot setting”. Tick vào đó để var này không swapped. Source.
  • Để có thể swap 2 slots, read this. ← swap slots giúp cho production không bị tình trạng “cold start”. Ví dụ nếu deploy auto qua Github thì sẽ có khoảng thời gian cold start (ko thể truy cập được ở 1 số instance lúc đang build). Swap với slots đã warm up hết rùi sẽ “làm cái rụp”.
    • Lưu ý là cái này là swap (trao đổi), cái của prod sẽ qua cái của “bị swap” và ngược lại. Chỉ có những settings “deployment slot settings” mới cố định ko swapped mà thôi!
    • Sau khi swap dev ↔ prod thì phiên bản trên Azure của dev là của thằng prod và ngược lại. Ngay cả khi trên Github ko cho thấy điều đó. Khi ta push lại nhánh trên Github thì 1 trigger sẽ deploy lại lần nữa!

Workflow (personal exp)

Nên có 3 branches trên Github - prod, preproddev. Chỉ nên dùng Github Action với preproddev, còn prod trên Azure sẽ được swap với preprod. Tác dụng của preprod là có settings y chang như prod để có thể test trước khi apply vào prod.
Dùng preprod swap với prod thay vì dùng Github Action với prod là để tránh tình trạng “cold start” mỗi khi build lại. Swap sẽ đảm bảo các instance đã warmup hết rùi trước khi chuyển sang prod.
Còn nhánh prod, nếu muốn manually deploy (ko thông qua swap):
1# build for Azure Container Registry
2# (not recommended)
3# login first
4az login
5az acr login --name v2acr2
6# build image
7docker build -t v2acr2.azurecr.io/v2daimg:prod --platform=linux/amd64 .
8# push to ACR (no need if setting up auto deploy via Github)
9docker push v2acr2.azurecr.io/v2daimg:prod
Trong Deployment → Deployment Center → Settings → Source: Container Registry:… → image tag “prod” and turn ON Continuous deployment.

Swap slots

Cần lưu ý là Azure CLI az webapp deployment slot swap nói rằng để tên của slot vào là được, trong khi vào trong Deloyment → Deployment slots thì thấy tên là ideta-v2da-dev nhưng trong command CLI phải là “dev”!!
Lưu ý 2: production không có tên slot, tên cố định là “production”!
Do đó nếu hình sau đây,
Thì lệnh swap giữa preprod và production sẽ là
1az webapp deployment slot swap --resource-group v2da --name "ideta-v2da" --slot "preprod" --target-slot production

Connect to Storage Account

  • Có thể create thêm File shares trong Storage Accounts → Data Storage → Files shares
  • ☝️ Lưu ý là cái trên chỉ thiết lập file đã upload có giới hạn thời gian chứ không tự xoá sau khi hết hạn! Để có thể tự xoá, cần configure a lifecycle management policy (read next section).
    • Upload riêng, tạo sas riêng. Sau khi tạo sas thì mới có url limit trong 1h. Check the codes trong playground azure-storage-sas.ipynb.

Auto delete / configure a lifecycle management policy (blob)

  • Enable access time tracking (nếu ko enable thì last access time sẽ assigned to the date the lifecycle policy was enabled): read this.

Tạo và connect to Storage Account trong App Service

Sau khi tạo thì ngay trong container của App Service sẽ có folder dạng /test và folder này nằm trong Storage Account.
  • Enable và tạo một Storage Account trước.
  • Trong Storage Account → Data Storage → Containers → tạo mới 1 Container trước vì bước tiếp theo sẽ cần.
  • Trong App Service → Settings → Configuration → Path mappings → New Azure Storage Mount
    • Nếu vào Settings → Configuration → Path mappings → New Azure Storage Mount mà không thấy option “Basic” có thể chọn được thì cần phải tạo một Storage Account trước!
    • Chỗ “Mount path”, chọn /test
    • ⚠️ Lưu ý là mỗi slot sẽ kết nối khác nhau, thế nên nên chọn “Deployment slot setting” là true và làm tương tự cho các slot khác!
    • Tạo xong đợi xíu.
  • Mở SSH lên thử df –h (SSH trong App Service → Development Tools → SSH)

Azure Files

Clean up on Azure

1az group delete --name <resource-group-name> --no-wait
Còn nếu muốn delete resource group trên Azure Portal thì phải vào group đó chọn Delete mới được!