Skip to content

Add new doc: "Deploy and run an Azure OpenAI/ChatGPT app on AKS" #632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 120 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
2174f53
Initial
aamini7 Nov 25, 2024
1fc3943
Init terraform
aamini7 Nov 26, 2024
4087f1d
Change readme
aamini7 Nov 26, 2024
af736b2
Add gitignore
aamini7 Nov 26, 2024
63a2f0c
Add metadata
aamini7 Nov 26, 2024
036fa6f
Remove jumpbox VM
aamini7 Nov 26, 2024
fe6948a
Fix SSH
aamini7 Nov 26, 2024
21ca3f6
Update provider
aamini7 Nov 26, 2024
7e072c4
Fix Breaking changes
aamini7 Nov 27, 2024
0e56a21
Create README template
aamini7 Nov 27, 2024
35128aa
Fix logs
aamini7 Nov 27, 2024
978325f
Update region
aamini7 Nov 27, 2024
5e9d165
Fix vnet
aamini7 Nov 27, 2024
3057190
Fix aks
aamini7 Nov 27, 2024
1045fd9
Remove depends on
aamini7 Nov 27, 2024
99153c1
Rename + small fix
aamini7 Nov 27, 2024
ff91a32
Remove log
aamini7 Nov 27, 2024
8bac634
Remove default var
aamini7 Nov 27, 2024
5ad6257
Update SKU
aamini7 Nov 27, 2024
7e1e26e
Change name again
aamini7 Nov 27, 2024
988e6a0
Fixes
aamini7 Jan 16, 2025
afccc7a
Inline + remove tags
aamini7 Jan 16, 2025
643431e
Inline a bunch
aamini7 Jan 16, 2025
0f0b3b3
More inlining
aamini7 Jan 16, 2025
f9c3b62
Massive clean up WIP
aamini7 Jan 16, 2025
2270443
Add back vars
aamini7 Jan 16, 2025
4fa3cc8
More clean up
aamini7 Jan 16, 2025
553de4e
More bullshit
aamini7 Jan 16, 2025
ed0d1d1
Format
aamini7 Jan 16, 2025
2f4aca4
Fix
aamini7 Jan 16, 2025
f188730
Reorganize
aamini7 Jan 16, 2025
0ee758d
Clean up
aamini7 Jan 16, 2025
606620d
Dead code
aamini7 Jan 16, 2025
fb9d8de
More inline
aamini7 Jan 16, 2025
063c89d
Fixes
aamini7 Jan 17, 2025
13ab276
Format
aamini7 Jan 17, 2025
27e463c
Fixes
aamini7 Jan 17, 2025
9710cf5
Fixes
aamini7 Jan 17, 2025
2aac95f
Extract var
aamini7 Jan 17, 2025
0c0f858
Fix vars
aamini7 Jan 17, 2025
dd3b131
Update openai model + cleanup
aamini7 Jan 17, 2025
b9ac095
Fixes
aamini7 Jan 17, 2025
09fa7ad
Fix names + region
aamini7 Jan 17, 2025
eec104a
Clean up
aamini7 Jan 17, 2025
fba749f
WIP
aamini7 Jan 17, 2025
89ac911
Remove deploy
aamini7 Jan 17, 2025
966d758
New directory structure
aamini7 Jan 17, 2025
a3e0f59
WIP
aamini7 Jan 22, 2025
96da54f
Rename
aamini7 Jan 23, 2025
54c1712
Add plan
aamini7 Jan 23, 2025
b526002
Clean
aamini7 Jan 23, 2025
faffb2b
Clean up
aamini7 Jan 23, 2025
f042cf6
Fix
aamini7 Jan 23, 2025
6f69f62
Fix bug
aamini7 Jan 23, 2025
aec0897
Move
aamini7 Jan 23, 2025
2f9c8eb
Fix bug
aamini7 Jan 23, 2025
8ef4d06
Fixes
aamini7 Jan 23, 2025
1dca7d9
Fixes
aamini7 Jan 23, 2025
d25cdfa
Fixes
aamini7 Jan 23, 2025
d7d9be2
Clean
aamini7 Jan 23, 2025
97e0bf6
Clean
aamini7 Jan 23, 2025
bae1765
Remove old files
aamini7 Jan 23, 2025
445d8fa
Undo hardcoding
aamini7 Jan 23, 2025
66527aa
Move
aamini7 Jan 23, 2025
66d27b9
Temporarily hardcode
aamini7 Jan 23, 2025
c2b1cab
Format
aamini7 Jan 23, 2025
356c53d
terraform fixes
aamini7 Feb 20, 2025
48043ea
Fix readme
aamini7 Feb 20, 2025
91d85a7
Fix key vault
aamini7 Feb 24, 2025
6e5544b
Fixes
aamini7 Feb 24, 2025
fcf07ed
Rename
aamini7 Feb 25, 2025
1bf3cf8
Change SKU
aamini7 Feb 25, 2025
dd6db60
Merge remote-tracking branch 'origin/main' into test_terraform
aamini7 Feb 25, 2025
ded7284
make inactive
aamini7 Feb 25, 2025
fc066b6
Rename
aamini7 Feb 25, 2025
d8c99d6
WIP
aamini7 Feb 25, 2025
3f29ae6
WIP
aamini7 Feb 25, 2025
fd3b178
Rename
aamini7 Feb 25, 2025
805a02f
Wip
aamini7 Feb 25, 2025
2eadfac
WIP
aamini7 Feb 25, 2025
de35561
Fixes
aamini7 Feb 25, 2025
9d8645a
Move
aamini7 Feb 25, 2025
2d99179
Add back network profile
aamini7 Feb 25, 2025
c2c6fe8
WIP
aamini7 Feb 25, 2025
e41428b
Fix
aamini7 Feb 25, 2025
d4692f2
wip
aamini7 Feb 26, 2025
502d08b
Fix
aamini7 Feb 26, 2025
91933da
WIP
aamini7 Feb 26, 2025
6d77e53
rename
aamini7 Feb 26, 2025
0d689d5
Working
aamini7 Feb 26, 2025
175eda5
Add venv to gitignore
aamini7 Feb 26, 2025
d677471
del
aamini7 Feb 26, 2025
d34bd3a
ui tweaks
aamini7 Feb 26, 2025
69921b3
Fix
aamini7 Feb 26, 2025
2ef60b7
fix
aamini7 Feb 26, 2025
eced908
Fixes
aamini7 Feb 26, 2025
bf975d8
Remove certmanager and nginx
aamini7 Feb 27, 2025
c40b313
clean
aamini7 Feb 27, 2025
c08e733
Fixes
aamini7 Feb 27, 2025
442fdc8
Minor tweak
aamini7 Feb 27, 2025
b8099ad
Fixes
aamini7 Feb 27, 2025
54e909f
Remove unused resources
aamini7 Feb 27, 2025
69124bd
Rename
aamini7 Feb 28, 2025
f5ea6c2
Fixes
aamini7 Feb 28, 2025
74780d5
Fixes
aamini7 Mar 1, 2025
24339d8
fix
aamini7 Mar 1, 2025
a87162b
Fix
aamini7 Mar 1, 2025
a3450bb
Fix
aamini7 Mar 1, 2025
55ae013
Fix
aamini7 Mar 1, 2025
b65a2b6
Fix
aamini7 Mar 3, 2025
4a96ec7
Reduce replica count
aamini7 Mar 3, 2025
6ad16bb
Update prompt
aamini7 Mar 3, 2025
3f2ad29
Update version
aamini7 Mar 3, 2025
f32e4fc
Update eta
aamini7 Mar 3, 2025
fdc5420
Fix
aamini7 Mar 3, 2025
4f75a25
Optimize app
aamini7 Mar 4, 2025
b8eb028
Add spinner
aamini7 Mar 4, 2025
dc7b464
Emoji
aamini7 Mar 4, 2025
0f6ba25
Remove comment
aamini7 Mar 4, 2025
fb40f0e
Minor tweaks
aamini7 Mar 5, 2025
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
40 changes: 40 additions & 0 deletions scenarios/AksOpenAiTerraform/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Local .terraform directories
**/.terraform/*

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Ignore transient lock info files created by terraform apply
.terraform.tfstate.lock.info

# Include override files you do wish to add to version control using negated pattern
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc

.venv
.vscode
71 changes: 71 additions & 0 deletions scenarios/AksOpenAiTerraform/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
title: Deploy and run an Azure OpenAI ChatGPT application on AKS via Terraform
description: This article shows how to deploy an AKS cluster and Azure OpenAI Service via Terraform and how to deploy a ChatGPT-like application in Python.
ms.topic: quickstart
ms.date: 09/06/2024
author: aamini7
ms.author: ariaamini
ms.custom: innovation-engine, linux-related-content
---

## Provision Resources with Terraform (~5 minutes)
Run terraform to provision all the Azure resources required to setup your new OpenAI website.
```bash
# Terraform parses TF_VAR_* as vars (Ex: TF_VAR_name -> name)
export TF_VAR_location="westus3"
export TF_VAR_kubernetes_version="1.30.9"
export TF_VAR_model_name="gpt-4o-mini"
export TF_VAR_model_version="2024-07-18"
# Terraform consumes sub id as $ARM_SUBSCRIPTION_ID
export ARM_SUBSCRIPTION_ID=$SUBSCRIPTION_ID
# Run Terraform
terraform -chdir=terraform init
terraform -chdir=terraform apply -auto-approve
```

## Login to Cluster
In order to use the kubectl to run commands on the newly created cluster, you must first login.
```bash
RESOURCE_GROUP=$(terraform -chdir=terraform output -raw resource_group_name)
az aks get-credentials --admin --name AksCluster --resource-group $RESOURCE_GROUP --subscription $SUBSCRIPTION_ID
```

# Install Helm Charts
Install nginx and cert-manager through Helm
```bash
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo add jetstack https://charts.jetstack.io
helm repo update

STATIC_IP=$(terraform -chdir=terraform output -raw static_ip)
DNS_LABEL=$(terraform -chdir=terraform output -raw dns_label)
helm upgrade --install ingress-nginx ingress-nginx/ingress-nginx \
--set controller.replicaCount=2 \
--set controller.nodeSelector."kubernetes\.io/os"=linux \
--set defaultBackend.nodeSelector."kubernetes\.io/os"=linux \
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-dns-label-name"=$DNS_LABEL \
--set controller.service.loadBalancerIP=$STATIC_IP \
--set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz
helm upgrade --install cert-manager jetstack/cert-manager \
--set crds.enabled=true \
--set nodeSelector."kubernetes\.io/os"=linux
```

## Deploy
Apply/Deploy Manifest File
```bash
export IMAGE="aamini8/magic8ball:latest"
# Uncomment below to manually build docker image yourself instead of using pre-built image.
# docker build -t <YOUR IMAGE NAME> ./magic8ball --push
export HOSTNAME=$(terraform -chdir=terraform output -raw hostname)
export WORKLOAD_IDENTITY_CLIENT_ID=$(terraform -chdir=terraform output -raw workload_identity_client_id)
export AZURE_OPENAI_DEPLOYMENT=$(terraform -chdir=terraform output -raw openai_deployment)
export AZURE_OPENAI_ENDPOINT=$(terraform -chdir=terraform output -raw openai_endpoint)
envsubst < quickstart-app.yml | kubectl apply -f -
```

## Wait for host to be ready
```bash
kubectl wait --for=condition=Ready certificate/tls-secret
echo "Visit: https://$HOSTNAME"
```
12 changes: 12 additions & 0 deletions scenarios/AksOpenAiTerraform/magic8ball/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.13-slim
WORKDIR /app

ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 8501
ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=8501", "--server.address=0.0.0.0"]
65 changes: 65 additions & 0 deletions scenarios/AksOpenAiTerraform/magic8ball/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import os
from openai import AzureOpenAI
import streamlit as st
from azure.identity import WorkloadIdentityCredential, get_bearer_token_provider

azure_deployment = os.getenv("AZURE_OPENAI_DEPLOYMENT")
azure_endpoint = os.getenv("AZURE_OPENAI_ENDPOINT")
workload_identity_client_id = os.getenv("WORKLOAD_IDENTITY_CLIENT_ID")

client = AzureOpenAI(
api_version="2024-10-21",
azure_endpoint=azure_endpoint,
azure_ad_token_provider=get_bearer_token_provider(
WorkloadIdentityCredential(client_id=workload_identity_client_id),
"https://cognitiveservices.azure.com/.default",
),
)


def ask_openai_api(messages: list[str]):
completion = client.chat.completions.create(
messages=messages, model=azure_deployment, stream=True, max_tokens=20
)
return completion


assistant_prompt = """
Answer as a magic 8 ball and make random predictions.
If the question is not clear, respond with "Ask the Magic 8 Ball a question about your future."
"""

# Init state
if "messages" not in st.session_state:
st.session_state.messages = [{"role": "system", "content": assistant_prompt}]
if "disabled" not in st.session_state:
st.session_state.disabled = False

st.title(":robot_face: Magic 8 Ball")
for message in st.session_state.messages[1:]: # Print previous messages
with st.chat_message(message["role"]):
st.markdown(message["content"])


def disable_chat():
st.session_state.disabled = True


if prompt := st.chat_input(
"Ask your question", on_submit=disable_chat, disabled=st.session_state.disabled
):
# Print Question
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.write(prompt)

# Print Response
with st.chat_message("assistant"):
messages = st.session_state.messages
with st.spinner("Loading..."):
response = st.write_stream(ask_openai_api(messages))
st.session_state.messages.append({"role": "assistant", "content": response})

# Re-enable textbox
st.session_state.disabled = False
st.rerun()
3 changes: 3 additions & 0 deletions scenarios/AksOpenAiTerraform/magic8ball/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
streamlit~=1.40.1
azure-identity~=1.20.0
openai~=1.65.2
95 changes: 95 additions & 0 deletions scenarios/AksOpenAiTerraform/quickstart-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: magic8ball-configmap
data:
AZURE_OPENAI_ENDPOINT: $AZURE_OPENAI_ENDPOINT
AZURE_OPENAI_DEPLOYMENT: $AZURE_OPENAI_DEPLOYMENT
WORKLOAD_IDENTITY_CLIENT_ID: $WORKLOAD_IDENTITY_CLIENT_ID
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: magic8ball
labels:
app.kubernetes.io/name: magic8ball
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/name: magic8ball
template:
metadata:
labels:
app.kubernetes.io/name: magic8ball
azure.workload.identity/use: "true"
spec:
serviceAccountName: magic8ball-sa
containers:
- name: magic8ball
image: $IMAGE
imagePullPolicy: Always
ports:
- containerPort: 8501
envFrom:
- configMapRef:
name: magic8ball-configmap
---
apiVersion: v1
kind: Service
metadata:
name: magic8ball
spec:
selector:
app.kubernetes.io/name: magic8ball
ports:
- port: 80
targetPort: 8501
protocol: TCP
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: magic8ball-sa
annotations:
azure.workload.identity/client-id: $WORKLOAD_IDENTITY_CLIENT_ID
azure.workload.identity/tenant-id: $TENANT_ID
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: magic8ball
annotations:
cert-manager.io/issuer: letsencrypt-dev
spec:
ingressClassName: nginx
tls:
- hosts:
- $HOSTNAME
secretName: tls-secret
rules:
- host: $HOSTNAME
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: magic8ball
port:
number: 80
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: letsencrypt-dev
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: $EMAIL
privateKeySecretRef:
name: tls-secret
solvers:
- http01:
ingress:
ingressClassName: nginx
41 changes: 41 additions & 0 deletions scenarios/AksOpenAiTerraform/terraform/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading