Skip to content

Commit

Permalink
Create automated deployment to Azure Container app (#39)
Browse files Browse the repository at this point in the history
* Refactor environment vars:
- Github secrets for buildArguments
- Github repository variables for non-secrets
- Azure Container app secrets for secrets

* Diagrams for system architecture
* Architecture design decisions
  • Loading branch information
bdb-dd authored Apr 29, 2024
1 parent 2f1dec8 commit c7ab526
Show file tree
Hide file tree
Showing 17 changed files with 394 additions and 9 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: prod-slack-altinn - Auto deploy

# When this action will be executed
on:
# Automatically trigger it when detected changes in repo
push:
branches:
[ release ]
paths:
- '**'
- '.github/workflows/prod-slack-altinn-AutoDeployTrigger-06da9e65-21d6-4cd0-803b-5776683d6cfc.yml'

# Allow manual trigger
workflow_dispatch:

jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment: prod-slack-altinn

steps:
- name: Checkout to the branch
uses: actions/checkout@v4

- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.DIGDIRASSISTANTSALTINN_AZURE_CREDENTIALS }}

- name: Build and push container image to registry
uses: azure/container-apps-deploy-action@v2
with:
appSourcePath: ${{ github.workspace }}
registryUrl: altinnaicontainers.azurecr.io
registryUsername: ${{ secrets.DIGDIRASSISTANTSALTINN_REGISTRY_USERNAME }}
registryPassword: ${{ secrets.DIGDIRASSISTANTSALTINN_REGISTRY_PASSWORD }}
containerAppName: digdir-assistants-altinn
resourceGroup: altinn-ai-assistant
imageToBuild: altinnaicontainers.azurecr.io/digdir-assistants-prod:${{ github.sha }}
environmentVariables: VITE_SLACK_APP_SUPABASE_API_URL=${{secrets.VITE_SLACK_APP_SUPABASE_API_URL}} VITE_SLACK_APP_SUPABASE_ANON_KEY=${{secrets.VITE_SLACK_APP_SUPABASE_ANON_KEY}}





Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: prod-slack-altinndevops - Auto deploy

# When this action will be executed
on:
# Automatically trigger it when detected changes in repo
push:
branches:
[ release ]
paths:
- '**'
- '.github/workflows/prod-slack-altinndevops-AutoDeployTrigger-bbf1dda7-6862-4a86-94e1-9466cec169a6.yml'

# Allow manual trigger
workflow_dispatch:

jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment: prod-slack-altinndevops

steps:
- name: Checkout to the branch
uses: actions/checkout@v4

- name: Azure Login
uses: azure/login@v2
with:
creds: ${{ secrets.DIGDIRASSISTANTS_AZURE_CREDENTIALS }}

- name: Build and push container image to registry
uses: azure/container-apps-deploy-action@v2
with:
appSourcePath: ${{ github.workspace }}
registryUrl: altinnaicontainers.azurecr.io
registryUsername: ${{ secrets.DIGDIRASSISTANTS_REGISTRY_USERNAME }}
registryPassword: ${{ secrets.DIGDIRASSISTANTS_REGISTRY_PASSWORD }}
containerAppName: digdir-assistants
resourceGroup: altinn-ai-assistant
imageToBuild: altinnaicontainers.azurecr.io/digdir-assistants-prod:${{ github.sha }}
environmentVariables: VITE_SLACK_APP_SUPABASE_API_URL=${{secrets.VITE_SLACK_APP_SUPABASE_API_URL}} VITE_SLACK_APP_SUPABASE_ANON_KEY=${{secrets.VITE_SLACK_APP_SUPABASE_ANON_KEY}}





90 changes: 81 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,96 @@ This diagram shows the main functional blocks and how data flows between them:

[#altinn-assistant](https://altinndevops.slack.com/archives/C06JQLHSZME/p1707478070231209) on Altinn Devops Slack

## Quickstart
## Local development quickstart

### Install dependencies:

`$ yarn install`

### Build packages
1. Install dependencies:

`$ yarn build:assistant-lib`
`$ yarn install`

### Build slack-app
2. Build all packages and apps

`$ yarn build:slack-app`
`$ yarn build`


### Run slack-app
3. Run slack-app and admin ui

`$ yarn run:slack-app`

Note: in order for your local bot endpoint to receive traffic from Slack, you need to configure a proxy service such as `ngrok`, and configure a Slack app to use the URL allocated by ngrok.

4. Configure a new Slack app in your test workspace

> It is recommended to create a few Slack workspace specifically for development and testing of Slack apps
![Your Slack apps](/documentation/slack/your-apps.jpg)

## Deploy to Azure Container app


1. Create a resouce group
2. Create a Container App Environment
3. Create a container repository
4. Create a Container App
> For step by step instructions see: [Create a Container App](/documentation/create-a-container-app.md)

## Manual job execution

1. Update crawler index


2. Update search phrase index

`$ cd cli`
`$ yarn run:generate-search-phrases <typesense collection name>`

Example collection name: TEST_altinn-studio-docs-search-phrases`




# System architecture

The Altinn Assistants architecture consists of the following technology components:

1. Front-end (Slack app, Admin web app)
2. Database-as-a-service (Supabase)
3. Serverless function runtime (Supabase Functions)
4. GPU-accelerated (Python app running in a GPU-enabled VM)
5. Text search index (Typesense Cloud)

Sequence diagram showing execution of a typical end user query:

```mermaid
sequenceDiagram
participant slack.com
participant slack-app
participant llm-api
participant supabase-db
participant supabase-functions
participant semantic-search
participant gpu-services
slack.com->>slack-app: new text message
slack-app->>supabase-db: GET /config (cached)
supabase-db->>slack-app: workspace + channel config
slack-app->>llm-api: stage 1: analyze user query, translate if necessary
llm-api->>slack-app: query analysis, translation
slack-app->>llm-api: stage 2: query relaxation
llm-api->>slack-app: generated search phrases
slack-app->>semantic-search: hybrid search (cosine similarity + keyword)
semantic-search->>slack-app: sorted doc list
slack-app->>gpu-services: POST /colbert/rerank w/user query
gpu-services->>slack-app: Re-ranked doc list
slack-app->>llm-api: Prompt + retrieved context
llm-api->>slack-app: Helpful response (streamed)
slack-app->>slack.com: Helpful response (streamed response)
slack-app->>slack.com: Finalized response w/metadata
slack-app->>supabase-functions: Log query, response and metadata
supabase-functions->>supabase-db: upsert team, channel, <br>user and message data
slack-app->>llm-api: Translate to user's language
llm-api->>slack-app: (streamed response)
slack-app->>slack.com: (streamed response)
slack-app->>slack.com: Finalized response w/metadata
slack-app->>supabase-db: Log query, response and metadata
```
36 changes: 36 additions & 0 deletions documentation/architectural-design-decisions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Architecture and design decisions

## Database as a service

When considering alternatives for storing stateful and static data for Altinn Assistant, we prioritized the following desirable characteristics:

1. **Real-time Data Synchronization**: Supabase provides real-time change data notification and synchronization capabilities and offers client implementations for multiple langauages, including TypeScript.
2. **Security**: Supabase provides enterprise-grade security features, including encryption at rest and in transit, fine-grained access control, and built-in auditing and logging.
3. **Integration with Other Services**: Supabase provides native integration with other services, such as Supabase Auth, Supabase Realtime, and Supabase Storage, making it easy to build comprehensive applications.
4. **Open-Source**: Supabase is open-source, which means that it is free, and the source code is publicly available for anyone to inspect, modify, and contribute to.
5. **Self-host and cloud hosting options**: Supabase provides a secure cloud hosting environment for database storage and query execution. Additionally, Supabase Functions can be hosted within a secure cloud subnetwork without the administrative overhead normally required.
6. **Web-based Interface**: Supabase provides a simplified web-based interface for managing databases, users, and permissions, making it easy to set up and manage databases. This interface was especially relevant during the early stages of prototyping and postponed the urgency of developing a custom admin interface.
7. **SQL and NoSQL Support**: Supabase supports both SQL and NoSQL data models, allowing developers to choose the best approach for their application's specific needs.
8. **Performance**: Supabase uses a highly optimized PostgreSQL engine, which is renowned for its performance and reliability. Supabase also provides multiple geographic locations for data storage, ensuring lower latency and faster query times.
9. **Language agnostic**: Supabase Functions support multiple programming languages, making it accessible to a wide range of developers.
10. **Scalability**: Supabase is designed to scale horizontally, allowing it to handle large volumes of data and high traffic, making it suitable for high-traffic applications.
11. **High Availability**: Supabase uses a distributed architecture, ensuring that data is replicated across multiple nodes, ensuring high availability even in the event of node failures or network outages.
12. **PostgreSQL Compatibility**: Supabase is fully compatible with PostgreSQL, allowing developers to leverage their existing knowledge and skills.
13. **API-based**: Supabase provides a robust API for interacting with the database, making it easy to integrate with other applications and services.
14. **Automated Backup and Recovery**: Supabase provides automated backup and recovery features, ensuring that data is safely backed up and can be quickly restored in the event of a disaster.



## Functions as a service

One of the key benefits of using Supabase DB is that most data operations for our application can be achieved without any backend code. Through properly designed access control policies, we can retrieve relevant data directly from the front end application running in the browser. Especially during the early stages of prototyping, this can be a significant time saver.

Should the need arise for complicated data manipulation or querying, we can add a serverless backend function for that specific purpose, without a costly rearchitecture effort. Additionally, each function can have it's own scaling configuration, source code language and security context. While not suitable for all workloads, Supabase Functions have the general benefits associated with serverless runtimes in general:

* **Cost savings**: Pay only for the resources consumed, which can lead to significant cost savings.
* **Increased scalability**: Infrastructure scaling is automated, ensuring your application can handle changing traffic patterns.
* **Reduced administrative burden**: No need to manage servers, patch software, or worry about security updates.
* **Faster deployment**: Developers can focus on writing code, rather than managing infrastructure.
* **Improved responsiveness**: Functions can respond quickly to changing traffic patterns, ensuring a better user experience.


Binary file added documentation/azure/create-secrets.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/azure/new-container-app_step-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/azure/new-container-app_step-2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/azure/new-container-app_step-3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/azure/setup-ci_step-1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added documentation/azure/setup-ci_step-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
123 changes: 123 additions & 0 deletions documentation/create-a-container-app.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@

# Create a Container App - step by step

### Step 1

- Select your new resource group and Container Apps Environment

![Create a Container app - step 1](/documentation/azure/new-container-app_step-1.png)

---


### Step 2

- Be sure to select the correct repository and image name.
- The image tag is not important, as we will specific a dynamic git commit in the deployment workflow.

![Create a Container app - step 2](/documentation/azure/new-container-app_step-2.jpg)

---

### Step 3

Recommended settings:
- Ingress should be enabled and ***Accepting traffic from anywhere***
- Ingress type: HTTP
- Client certificate mode: Ignore
- Transport: Auto
- Insecure connections: false
- Target port: 3000
- Session affinity: false

![Create a Container app - step 3](/documentation/azure/new-container-app_step-3.jpg)



### Step 4

Configure continuous integration:
- Sign-in with your Github credentials, allowing Azure to access the repository and store required secrets
- Select the correct Organization, Repository and Branch to match your own fork.
- Configure Registry settings to match your new Docker image repository.
- Azure access:
- Important: as of this writing, User-assigned identity does NOT work with Github Action secrets. Instead, select Service Principal.
- Click "Start continuous deployment"
- This will generate and commit a new Github Action workflow file in the `.github` folder of your code repository.
- Unfortunately, the template used is out of date and needs to be manually updated in a separate commit. We'll fix this in the last step.


![Setup CI](/documentation/azure/setup-ci_step-1.png)



### Step 5 - Add environment to Github actions config

- Under "Environments", add a suitable environment name, ex. "prod-slack-<your-slack-workspace-name>"

![Setup environments in Github](/documentation/github/setup-environments.jpg)


### Step 6 - configure Supabase variables

In your Supabase project settings area, under "API", locate your ANON KEY and project url. You will use these values in the next step.

![Supabase project settings](/documentation/supabase/locate-project-settings.jpg)


### Step 7 - Set environment secrets

Under "Secrets and variables" > "Actions", add the following variables for *each* environment:
- SLACK_APP_SUPABASE_ANON_KEY


You will note that there are some existing secrets under "Repository secrets", created for you by the Azure Container App CI setup wizard. You shouldn't need to modify these, except to cleanup any removed Container Apps.

![Setup environment secrets](/documentation/github/environment-secrets.jpg)


### Step 8 - Set repository variables

Still under "Secrets and variables", click "Variables".

Add the listed repository variables first, adjusting the values as desired. These values serve as a useful default for all environments, reducing the number of environment variables to maintain.



![Setup repository variables](/documentation/github/set-repository-variables.jpg)


### Step 9 - Set Environment variables

Finally, add environment specific variable values for each repository variable you want to override the default value of. Typical examples include:
- SLACK_APP_SUPABASE_API_URL
- TYPESENSE_DOCS_COLLECTION
- TYPESENSE_DOCS_SEARCH_PHRASE_COLLECTION


### Step 10 - Set container secrets

Add the following secrets to the container app using the Azure portal:

![Setup Azure container app secrets](/documentation/azure/create-secrets.jpg)


You will find your Slack secrets in the Slack app configuration UI.

### Step 11 - Fix deployment workflow

See workflows in the `.github` folder for working examples.

Adjust the generated workflow file to be similar to one of the working examples, substituting the correct environment name where applicable.

Confirm that Github action for build-and-deploy is successful.

### Step 12 - Change scaling and activation config

By default, your Container app will scale to zero instances. For environments with sporadic usage, this can be acceptable. However, for most test and production environments, this will cause an unacceptable delay for Slack users.

We recommend changing the scale configuration to a minimum of 1 instance, to avoid latency due to cold start.

For development and test environments, we recommend Revision mode: Single.

For production environments, we recommend Revision mode: Multiple, as it makes it much easier to rollback to a known good container instance, should there be problems with a deploy.
Loading

0 comments on commit c7ab526

Please sign in to comment.