feat: working example
This commit is contained in:
parent
a99fdaf1f9
commit
ac08d5a715
14 changed files with 263 additions and 17 deletions
|
@ -3,7 +3,7 @@ FROM python:3.10-alpine
|
|||
|
||||
WORKDIR /app
|
||||
|
||||
COPY requirements.txt /app
|
||||
COPY src/requirements.txt /app
|
||||
RUN pip3 install -r requirements.txt
|
||||
|
||||
COPY src /app
|
||||
|
|
|
@ -16,8 +16,12 @@ cp example.env .env
|
|||
just run
|
||||
|
||||
# Provision infrastructure
|
||||
just terraform apply
|
||||
just deploy
|
||||
|
||||
# Deploy new code
|
||||
## RELEASE PROCESS
|
||||
# Release new code
|
||||
just release
|
||||
# At this point, you have to modify .env to point to new image
|
||||
# And then we can deploy the new image.
|
||||
just deploy
|
||||
```
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
"python312Packages.pip@latest",
|
||||
"terraform@latest",
|
||||
"azure-cli@latest",
|
||||
"azure-functions-core-tools@latest"
|
||||
"azure-functions-core-tools@latest",
|
||||
"black@latest",
|
||||
"postgresql@latest"
|
||||
],
|
||||
"env": {
|
||||
"VENV_DIR": ".venv"
|
||||
|
|
41
devbox.lock
41
devbox.lock
|
@ -38,6 +38,26 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"black@latest": {
|
||||
"last_modified": "2024-02-26T19:46:43Z",
|
||||
"resolved": "github:NixOS/nixpkgs/548a86b335d7ecd8b57ec617781f5e652ab0c38e#black",
|
||||
"source": "devbox-search",
|
||||
"version": "23.11.0",
|
||||
"systems": {
|
||||
"aarch64-darwin": {
|
||||
"store_path": "/nix/store/4aks55v8kdck409q1ga0qkdnrphabv53-python3.11-black-23.11.0"
|
||||
},
|
||||
"aarch64-linux": {
|
||||
"store_path": "/nix/store/y7kcifhk0v58pfhqdmpdbpd0yxwv860z-python3.11-black-23.11.0"
|
||||
},
|
||||
"x86_64-darwin": {
|
||||
"store_path": "/nix/store/41jmbap8q6gsjs8g59xwr6f222pdzpp8-python3.11-black-23.11.0"
|
||||
},
|
||||
"x86_64-linux": {
|
||||
"store_path": "/nix/store/g8k9vjxgsy8d3dizjw1jlcmj2iphy512-python3.11-black-23.11.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"just@latest": {
|
||||
"last_modified": "2024-02-26T19:46:43Z",
|
||||
"resolved": "github:NixOS/nixpkgs/548a86b335d7ecd8b57ec617781f5e652ab0c38e#just",
|
||||
|
@ -58,6 +78,27 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"postgresql@latest": {
|
||||
"last_modified": "2024-02-26T19:46:43Z",
|
||||
"plugin_version": "0.0.2",
|
||||
"resolved": "github:NixOS/nixpkgs/548a86b335d7ecd8b57ec617781f5e652ab0c38e#postgresql",
|
||||
"source": "devbox-search",
|
||||
"version": "15.6",
|
||||
"systems": {
|
||||
"aarch64-darwin": {
|
||||
"store_path": "/nix/store/j5m8ndrxpmk1444lzdk6p33vfzwrg6l7-postgresql-15.6"
|
||||
},
|
||||
"aarch64-linux": {
|
||||
"store_path": "/nix/store/3gbmk22frf8l07k3mhb16d43dy7bfd2s-postgresql-15.6"
|
||||
},
|
||||
"x86_64-darwin": {
|
||||
"store_path": "/nix/store/3a6lihh8nk9fvhf87vwkszfknjx27yfb-postgresql-15.6"
|
||||
},
|
||||
"x86_64-linux": {
|
||||
"store_path": "/nix/store/84xh621k32sc7ykqx37vcqdpjz9zxmdf-postgresql-15.6"
|
||||
}
|
||||
}
|
||||
},
|
||||
"python312Packages.pip@latest": {
|
||||
"last_modified": "2024-02-26T19:46:43Z",
|
||||
"plugin_version": "0.0.2",
|
||||
|
|
34
justfile
34
justfile
|
@ -4,10 +4,17 @@ set shell := ["devbox", "run"]
|
|||
set dotenv-load
|
||||
|
||||
export TF_VAR_name := env("APP_NAME")
|
||||
export GIT_SHA := `git rev-parse --short HEAD`
|
||||
|
||||
# Run server locally
|
||||
run:
|
||||
flask --app src/server run
|
||||
initdb --username=postgres || true
|
||||
devbox services start postgresql
|
||||
python src/app.py
|
||||
|
||||
run-docker:
|
||||
docker build -t local/${APP_NAME}:${GIT_SHA} .
|
||||
docker run -it --rm -p 8080:8080 local/${APP_NAME}:${GIT_SHA}
|
||||
|
||||
dependencies:
|
||||
pip install -r src/requirements.txt
|
||||
|
@ -15,11 +22,30 @@ dependencies:
|
|||
dependencies-lock:
|
||||
pip freeze -l > src/requirements.txt
|
||||
|
||||
deploy:
|
||||
release:
|
||||
#!/bin/env bash
|
||||
USERNAME="00000000-0000-0000-0000-000000000000"
|
||||
REGISTRY=$(terraform -chdir=terraform output -raw container_registry_name)
|
||||
|
||||
terraform-apply:
|
||||
TAG=${REGISTRY}.azurecr.io/develop:${GIT_SHA}-$(date +"%F-%H-%M-%S")
|
||||
|
||||
docker build -t ${TAG} .
|
||||
|
||||
# For podman support we must use this hacks,
|
||||
# otherwise az acr login would also do the docker login.
|
||||
docker login \
|
||||
--username=${USERNAME} \
|
||||
--password=$(az acr login --name ${REGISTRY} --expose-token 2>/dev/null | jq -r '.accessToken') \
|
||||
"${REGISTRY}.azurecr.io"
|
||||
|
||||
docker push ${TAG}
|
||||
|
||||
echo "Image pushed to ${TAG}"
|
||||
echo "Modify your .env with TF_VAR_image=${TAG}"
|
||||
|
||||
deploy:
|
||||
terraform -chdir=terraform init
|
||||
terraform -chdir=terraform apply
|
||||
|
||||
terraform-destroy:
|
||||
destroy:
|
||||
terraform -chdir=terraform destroy
|
||||
|
|
21
src/app.py
21
src/app.py
|
@ -1,11 +1,28 @@
|
|||
from flask import Flask
|
||||
from flask import Flask, render_template
|
||||
from database import get_db_connection, migrate
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
|
||||
@app.route("/health")
|
||||
def health():
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT 1")
|
||||
cursor.fetchone()
|
||||
return "OK"
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def hello_world():
|
||||
return "<p>Hello, World!</p>"
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM users")
|
||||
users = cursor.fetchall()
|
||||
print(users)
|
||||
return render_template("index.html", users=users)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
migrate()
|
||||
app.run(host="0.0.0.0", port=8080)
|
||||
|
|
28
src/database.py
Normal file
28
src/database.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
import os
|
||||
import psycopg2
|
||||
|
||||
conn = None
|
||||
|
||||
|
||||
def get_db_connection():
|
||||
global conn
|
||||
if not conn:
|
||||
conn = psycopg2.connect(
|
||||
host=os.environ.get("DB_HOST", "localhost"),
|
||||
database=os.environ.get("DB_NAME", "postgres"),
|
||||
user=os.environ.get("DB_USER", "postgres"),
|
||||
password=os.environ.get("DB_PASSWORD", "postgres"),
|
||||
sslmode=os.environ.get("DB_SSLMODE", "prefer"),
|
||||
)
|
||||
return conn
|
||||
|
||||
|
||||
def migrate():
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor()
|
||||
cur.execute(
|
||||
"CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name VARCHAR(255))"
|
||||
)
|
||||
cur.execute("INSERT INTO users (name) VALUES ('John')")
|
||||
cur.execute("INSERT INTO users (name) VALUES ('Oliver')")
|
||||
conn.commit()
|
|
@ -1,5 +1,5 @@
|
|||
blinker==1.7.0
|
||||
click==8.1.7
|
||||
Flask==3.0.2
|
||||
itsdangerous==2.1.2
|
||||
psycopg2-binary==2.9.9
|
||||
Werkzeug==3.0.1
|
10
src/templates/index.html
Normal file
10
src/templates/index.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<!doctype html>
|
||||
<title>Hello from Flask</title>
|
||||
|
||||
<h1>Here are some users</h1>
|
||||
|
||||
<ul>
|
||||
{% for (id, name) in users %}
|
||||
<li><strong>{{ id }}:</strong> {{ name}}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
|
@ -20,3 +20,22 @@ provider "registry.terraform.io/hashicorp/azurerm" {
|
|||
"zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
|
||||
]
|
||||
}
|
||||
|
||||
provider "registry.terraform.io/hashicorp/random" {
|
||||
version = "3.6.0"
|
||||
hashes = [
|
||||
"h1:R5Ucn26riKIEijcsiOMBR3uOAjuOMfI1x7XvH4P6B1w=",
|
||||
"zh:03360ed3ecd31e8c5dac9c95fe0858be50f3e9a0d0c654b5e504109c2159287d",
|
||||
"zh:1c67ac51254ba2a2bb53a25e8ae7e4d076103483f55f39b426ec55e47d1fe211",
|
||||
"zh:24a17bba7f6d679538ff51b3a2f378cedadede97af8a1db7dad4fd8d6d50f829",
|
||||
"zh:30ffb297ffd1633175d6545d37c2217e2cef9545a6e03946e514c59c0859b77d",
|
||||
"zh:454ce4b3dbc73e6775f2f6605d45cee6e16c3872a2e66a2c97993d6e5cbd7055",
|
||||
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
|
||||
"zh:91df0a9fab329aff2ff4cf26797592eb7a3a90b4a0c04d64ce186654e0cc6e17",
|
||||
"zh:aa57384b85622a9f7bfb5d4512ca88e61f22a9cea9f30febaa4c98c68ff0dc21",
|
||||
"zh:c4a3e329ba786ffb6f2b694e1fd41d413a7010f3a53c20b432325a94fa71e839",
|
||||
"zh:e2699bc9116447f96c53d55f2a00570f982e6f9935038c3810603572693712d0",
|
||||
"zh:e747c0fd5d7684e5bfad8aa0ca441903f15ae7a98a737ff6aca24ba223207e2c",
|
||||
"zh:f1ca75f417ce490368f047b63ec09fd003711ae48487fba90b4aba2ccf71920e",
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,13 +6,20 @@ resource "azurerm_resource_group" "main" {
|
|||
##
|
||||
# Database
|
||||
##
|
||||
resource "random_pet" "database_username" {
|
||||
separator = ""
|
||||
}
|
||||
resource "random_password" "database_password" {
|
||||
length = 16
|
||||
special = false
|
||||
}
|
||||
resource "azurerm_postgresql_server" "main" {
|
||||
name = var.name
|
||||
location = azurerm_resource_group.main.location
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
|
||||
administrator_login = "psqladmin"
|
||||
administrator_login_password = "H@Sh1CoR3!"
|
||||
administrator_login = random_pet.database_username.id
|
||||
administrator_login_password = random_password.database_password.result
|
||||
|
||||
sku_name = "B_Gen5_1"
|
||||
version = "11"
|
||||
|
@ -33,6 +40,26 @@ resource "azurerm_container_registry" "main" {
|
|||
sku = "Basic"
|
||||
}
|
||||
|
||||
data "azurerm_container_registry_scope_map" "main" {
|
||||
name = "_repositories_pull"
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
container_registry_name = azurerm_container_registry.main.name
|
||||
}
|
||||
|
||||
resource "azurerm_container_registry_token" "main" {
|
||||
name = var.name
|
||||
container_registry_name = azurerm_container_registry.main.name
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
scope_map_id = data.azurerm_container_registry_scope_map.main.id
|
||||
}
|
||||
|
||||
resource "azurerm_container_registry_token_password" "main" {
|
||||
container_registry_token_id = azurerm_container_registry_token.main.id
|
||||
|
||||
password1 {
|
||||
}
|
||||
}
|
||||
|
||||
##
|
||||
# Application
|
||||
##
|
||||
|
@ -56,12 +83,72 @@ resource "azurerm_container_app" "main" {
|
|||
resource_group_name = azurerm_resource_group.main.name
|
||||
revision_mode = "Single"
|
||||
|
||||
secret {
|
||||
name = "registry-token"
|
||||
value = one(azurerm_container_registry_token_password.main.password1).value
|
||||
}
|
||||
|
||||
registry {
|
||||
server = azurerm_container_registry.main.login_server
|
||||
username = azurerm_container_registry_token.main.name
|
||||
password_secret_name = "registry-token"
|
||||
}
|
||||
|
||||
ingress {
|
||||
allow_insecure_connections = false
|
||||
external_enabled = true
|
||||
target_port = 8080
|
||||
traffic_weight {
|
||||
latest_revision = true
|
||||
percentage = 100
|
||||
}
|
||||
}
|
||||
|
||||
secret {
|
||||
name = "db-username"
|
||||
value = "${random_pet.database_username.id}@${azurerm_postgresql_server.main.name}"
|
||||
}
|
||||
|
||||
secret {
|
||||
name = "db-password"
|
||||
value = random_password.database_password.result
|
||||
}
|
||||
|
||||
template {
|
||||
container {
|
||||
name = "maincontainerapp"
|
||||
image = "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest"
|
||||
image = var.image
|
||||
cpu = 0.25
|
||||
memory = "0.5Gi"
|
||||
|
||||
env {
|
||||
name = "DB_HOST"
|
||||
value = azurerm_postgresql_server.main.fqdn
|
||||
}
|
||||
env {
|
||||
name = "DB_SSLMODE"
|
||||
value = "require"
|
||||
}
|
||||
env {
|
||||
name = "DB_USER"
|
||||
secret_name = "db-username"
|
||||
}
|
||||
env {
|
||||
name = "DB_PASSWORD"
|
||||
secret_name = "db-password"
|
||||
}
|
||||
|
||||
liveness_probe {
|
||||
path = "/health"
|
||||
port = 8080
|
||||
transport = "HTTP"
|
||||
}
|
||||
|
||||
readiness_probe {
|
||||
path = "/health"
|
||||
port = 8080
|
||||
transport = "HTTP"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
output "database_fqdn" {
|
||||
value = azurerm_postgresql_server.main.fqdn
|
||||
output "container_registry_name" {
|
||||
value = azurerm_container_registry.main.name
|
||||
}
|
||||
|
|
|
@ -4,8 +4,14 @@ terraform {
|
|||
source = "hashicorp/azurerm"
|
||||
version = "3.94.0"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = "3.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "random" {}
|
||||
|
||||
provider "azurerm" {
|
||||
features {}
|
||||
|
|
|
@ -2,3 +2,9 @@ variable "name" {
|
|||
description = "The name of the application"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "image" {
|
||||
description = "The image to run"
|
||||
type = string
|
||||
default = "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest"
|
||||
}
|
||||
|
|
Reference in a new issue