Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,3 @@ jobs:
run: uv run ruff check
- name: Test
run: uv run pytest

docker:
name: Docker
runs-on: ubuntu-latest
needs: test
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Build
uses: docker/build-push-action@v6
with:
push: false
# To be changed
tags: andig/evopt:latest
35 changes: 35 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Deploy

on:
workflow_dispatch:
inputs:
image_tag:
description: Image tag to deploy
required: false
default: latest

env:
IMAGE: evcc/optimizer
RESOURCE_GROUP: rg-optimizer-prod

jobs:
deploy:
name: Deploy
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v6

- uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- name: Deploy infrastructure
uses: azure/cli@v2
with:
inlineScript: |
az deployment group create \
--resource-group ${{ env.RESOURCE_GROUP }} \
--template-file infra/main.bicep \
--parameters \
containerImage=${{ env.IMAGE }}:${{ inputs.image_tag || 'latest' }}
30 changes: 30 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Publish Docker

on:
workflow_dispatch:

env:
IMAGE: evcc/optimizer

jobs:
publish:
name: Build & Push
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}
- name: Setup Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ env.IMAGE }}:${{ github.sha }}
${{ env.IMAGE }}:latest
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ FROM python:3.13-slim
COPY --from=builder --chown=app:app /app/.venv /app/.venv

# Run the application
ENV PYTHONUNBUFFERED=1
ENV OPTIMIZER_TIME_LIMIT=25
ENV OPTIMIZER_NUM_THREADS=1
ENV GUNICORN_CMD_ARGS="--workers 4 --max-requests 32"
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
DOCKER_IMAGE := evcc-io/optimizer
DOCKER_IMAGE ?= evcc/optimizer

default: build docker-build

Expand Down
112 changes: 112 additions & 0 deletions infra/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
@description('Container image to deploy')
param containerImage string = 'evcc/optimizer:latest'

@description('Azure region for all resources')
param location string = 'germanywestcentral'

resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
name: 'kv-optimizer-prod'
location: location
properties: {
sku: {
family: 'A'
name: 'standard'
}
tenantId: subscription().tenantId
enableRbacAuthorization: true
}
}

resource logAnalytics 'Microsoft.OperationalInsights/workspaces@2025-02-01' = {
name: 'optimizer-logs'
location: location
properties: {
sku: {
name: 'PerGB2018'
}
retentionInDays: 30
}
}

resource containerAppEnv 'Microsoft.App/managedEnvironments@2025-01-01' = {
name: 'optimizer-env'
location: location
properties: {
appLogsConfiguration: {
destination: 'log-analytics'
logAnalyticsConfiguration: {
customerId: logAnalytics.properties.customerId
sharedKey: logAnalytics.listKeys().primarySharedKey
}
}
}
}

resource containerApp 'Microsoft.App/containerApps@2025-01-01' = {
name: 'optimizer'
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
managedEnvironmentId: containerAppEnv.id
configuration: {
ingress: {
external: true
targetPort: 7050
}
secrets: [
{
name: 'jwt-token-secret'
keyVaultUrl: '${keyVault.properties.vaultUri}secrets/jwt-token-secret'
identity: 'system'
}
]
}
template: {
containers: [
{
name: 'optimizer'
image: containerImage
resources: {
cpu: json('2')
memory: '4Gi'
}
env: [
{ name: 'OPTIMIZER_TIME_LIMIT', value: '25' }
{ name: 'OPTIMIZER_NUM_THREADS', value: '1' }
{ name: 'GUNICORN_CMD_ARGS', value: '--workers 4 --max-requests 32' }
{ name: 'JWT_TOKEN_SECRET', secretRef: 'jwt-token-secret' }
]
probes: [
{
type: 'startup'
tcpSocket: {
port: 7050
}
periodSeconds: 5
failureThreshold: 10
}
]
}
]
scale: {
minReplicas: 1
maxReplicas: 5
rules: [
{
name: 'http-scaling'
http: {
metadata: {
concurrentRequests: '10'
}
}
}
]
}
}
}
}


output fqdn string = containerApp.properties.configuration.ingress.fqdn
Loading