Skip to content

Commit eaacd8b

Browse files
Merge branch 'main' into helm-external-llm
2 parents cdfdc96 + ab54d6d commit eaacd8b

File tree

18 files changed

+521
-17
lines changed

18 files changed

+521
-17
lines changed

.github/workflows/push-release-charts.yaml

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ jobs:
3232
fi
3333
base_commit=$(git rev-parse HEAD~1) # push event
3434
merged_commit=$(git log -1 --format='%H')
35+
charts_updated=()
36+
build_push_chart() {
37+
local chart="$1"
38+
helm dependency update ${chart}
39+
helm package $chart
40+
helm push ${chart}-0-latest.tgz oci://ghcr.io/opea-project/chart
41+
}
3542
# args: <parent-dir|include-string> <path-cut-depth> <exclude-pattern>
3643
update_charts () {
3744
dir="$1"
@@ -40,21 +47,36 @@ jobs:
4047
common_charts=$(git diff --name-only ${base_commit} ${merged_commit} | \
4148
grep "^$dir" | grep -vE "$exclude" | \
4249
cut -d'/' -f$depth | sort -u )
43-
echo "Charts to be updated: $common_charts"
50+
echo "" && echo "Charts to be updated: $common_charts" && echo ""
4451
echo "${{ secrets.ACTION_TOKEN }}" | helm registry login ghcr.io -u opea --password-stdin
45-
pushd $dir
52+
pushd $dir > /dev/null
4653
for chart in ${common_charts}; do
4754
if [ ! -d $chart ]; then continue; fi
4855
echo "Updating $chart"
49-
helm dependency update ${chart}
50-
helm package $chart
51-
helm push ${chart}-0-latest.tgz oci://ghcr.io/opea-project/charts
56+
charts_updated+=($chart)
57+
build_push_chart $chart
58+
done
59+
popd > /dev/null
60+
}
61+
update_dependent_charts() {
62+
local charts=`.github/workflows/scripts/get_chart_dependency.sh "${CHARTS_DIR}" ${charts_updated[@]} | sort -u`
63+
echo "" && echo "Remaining dependent charts to be updated: $charts" && echo ""
64+
echo "${{ secrets.ACTION_TOKEN }}" | helm registry login ghcr.io -u opea --password-stdin
65+
for i in ${charts}; do
66+
if [ ! -d $i ]; then continue; fi
67+
local dir=$(dirname $i)
68+
local chart=$(basename $i)
69+
echo "Updating $chart in directory $dir"
70+
pushd $dir > /dev/null
71+
build_push_chart $chart
72+
popd > /dev/null
5273
done
53-
popd
5474
}
5575
# Update dependency for components first
5676
${CHARTS_DIR}/update_dependency.sh
5777
# Update components: exclude other Helm chart docs than README.md
5878
update_charts "$CHARTS_DIR/common/" 3 '.*/[^R][^/]*\.md$'
5979
# Update Examples: exclude non-helm subdirs, files not in subdirs, other docs
6080
update_charts "$CHARTS_DIR" 2 "$CHARTS_DIR"'/(common/|assets/|[^/]*$|.*/[^R][^/]*\.md$)'
81+
# Update dependent chart
82+
update_dependent_charts
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#!/bin/bash
2+
3+
# Copyright (C) 2025 Intel Corporation
4+
# SPDX-License-Identifier: Apache-2.0
5+
6+
# Function to check dependencies using helm dependency list
7+
check_dependencies() {
8+
local chart_dir="$1"
9+
local target_charts=("$@")
10+
11+
# Get the name of the current chart
12+
local chart_name
13+
chart_name=$(basename "$chart_dir")
14+
15+
# Skip if the current chart is one of the target charts
16+
for target in "${target_charts[@]:1}"; do
17+
if [[ "$chart_name" == "$target" ]]; then
18+
return
19+
fi
20+
done
21+
22+
# Get the list of dependencies using helm dependency list
23+
local dependencies
24+
dependencies=$(helm dependency list "$chart_dir" 2>/dev/null | grep 'file://' | awk '{print $1}')
25+
26+
# Check if any of the dependencies match the target charts
27+
for dep in $dependencies; do
28+
for target in "${target_charts[@]:1}"; do
29+
if [[ "$dep" == "$target" ]]; then
30+
echo "$chart_dir"
31+
return
32+
fi
33+
done
34+
done
35+
}
36+
37+
# Main script
38+
if [[ $# -lt 1 ]]; then
39+
echo "Usage: $0 <chart_directory> [<chart1> <chart2> ... <chartN>]"
40+
exit 1
41+
fi
42+
43+
chart_directory="$1"
44+
shift
45+
target_charts=("$@")
46+
47+
# Find all Helm charts in the specified directory
48+
chart_dirs=$(find "$chart_directory" -type d -exec test -e {}/Chart.yaml \; -print)
49+
50+
# Iterate over each chart directory and check dependencies
51+
for chart_dir in $chart_dirs; do
52+
check_dependencies "$chart_dir" "${target_charts[@]}"
53+
done

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ You can use [Terraform](https://www.terraform.io/) to create infrastructure to r
6464

6565
- [AWS/EKS: Create managed Kubernetes cluster on AWS for OPEA](cloud-service-provider/aws/eks/terraform/README.md)
6666
- [Azure/AKS: Create managed Kubernetes cluster on Azure for OPEA](cloud-service-provider/azure/aks/terraform/README.md)
67+
- [GCP/GKE: Create managed Kubernetes cluster on GCP for OPEA](cloud-service-provider/gcp/gke/terraform/README.md)
6768

6869
## Additional Content
6970

cloud-service-provider/aws/eks/terraform/main.tf

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,19 @@ provider "aws" {
33
}
44

55
provider "kubernetes" {
6-
config_path = "~/.kube/config"
6+
host = data.aws_eks_cluster.this.endpoint
7+
cluster_ca_certificate = base64decode(data.aws_eks_cluster.this.certificate_authority[0].data)
8+
token = data.aws_eks_cluster_auth.this.token
9+
}
10+
11+
data "aws_eks_cluster" "this" {
12+
name = module.eks.cluster_name
13+
depends_on = [module.eks]
14+
}
15+
16+
data "aws_eks_cluster_auth" "this" {
17+
name = module.eks.cluster_name
18+
depends_on = [module.eks]
719
}
820

921
data "aws_availability_zones" "available" {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
**/bin/
2+
*.out
3+
*.swp
4+
**/Chart.lock
5+
**/charts/*.tgz
6+
7+
bazel-*
8+
compile_commands.json
9+
.gitconfig
10+
terraform.tfstate
11+
terraform.tfstate.backup
12+
.terraform
13+
.terraform.lock.hcl
14+
.terraform/*
15+
.gitignore
16+
opea-chatqna.plan
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# OPEA applications GCP GKE deployment guide
2+
3+
This guide shows how to deploy OPEA applications on Google Cloud Platform (GCP) Google Kubernetes Engine (GKE) using Terraform.
4+
5+
## Prerequisites
6+
7+
- Access to GCP GKE
8+
- [Terraform](https://developer.hashicorp.com/terraform/tutorials/gcp-get-started/install-cli), [GCP CLI](https://cloud.google.com/sdk/docs/install-sdk) and [Helm](https://helm.sh/docs/helm/helm_install/),[kubectl](https://kubernetes.io/docs/tasks/tools/) installed on your local machine.
9+
10+
## Setup
11+
12+
The setup uses Terraform to create GKE cluster with the following properties:
13+
14+
- 1-node GKE cluster with 100 GB disk and `n4-standard-8` preemptible SPOT instance (8 vCPU and 32 GB memory)
15+
- Cluster autoscaling up to 5 nodes
16+
17+
Pre GKE Cluster setup
18+
19+
- After you've installed the gcloud SDK, initialize it by running the following command.
20+
21+
```bash
22+
gcloud init
23+
```
24+
25+
- This will authorize the SDK to access GCP using your user account credentials and add the SDK to your PATH. This steps requires you to login and select the project you want to work in. Finally, add your account to the Application Default Credentials (ADC). This will allow Terraform to access these credentials to provision resources on GCloud.
26+
27+
```bash
28+
gcloud auth application-default login
29+
```
30+
31+
In here, you will find four files used to provision a VPC, subnets and a GKE cluster.
32+
33+
- vpc.tf provisions a VPC and subnet. A new VPC is created for this tutorial so it doesn't impact your existing cloud environment and resources. This file outputs region.
34+
35+
- main.tf provisions a GKE cluster and a separately managed node pool (recommended). Separately managed node pools allows you to customize your Kubernetes cluster profile — this is useful if some Pods require more resources than others. You can learn more here. The number of nodes in the node pool is defined also defined here.
36+
37+
- opea-chatqna.tfvars is a template for the project_id, cluster_name and region variables.
38+
39+
- versions.tf sets the Terraform version to at least 0.14.
40+
41+
## Update your opea-chatqna.tfvars file
42+
43+
Replace the values in your opea-chatqna.tfvars file with your project_id, cluster_name and region. Terraform will use these values to target your project when provisioning your resources. Your opea-chatqna.tfvars file should look like the following.
44+
45+
```bash
46+
# opea-chatqna.tfvars
47+
project_id = "REPLACE_ME"
48+
region = "us-central1"
49+
```
50+
51+
You can find the project your gcloud is configured to with this command.
52+
53+
```bash
54+
gcloud config get-value project
55+
```
56+
57+
The region has been defaulted to us-central1; you can find a full list of gcloud regions - https://cloud.google.com/compute/docs/regions-zones
58+
59+
Initialize the Terraform environment.
60+
61+
```bash
62+
terraform init
63+
```
64+
65+
## GKE cluster
66+
67+
By default, 1-node cluster is created which is suitable for running the OPEA application. See `main.tf` upto max_node_count = 5, if you want to tune the cluster properties, e.g., number of nodes, instance types or disk size.
68+
69+
## Persistent Volume Claim
70+
71+
OPEA needs a volume where to store the model. For that we need to create Kubernetes Persistent Volume Claim (PVC). OPEA requires `ReadWriteOnce` option since multiple pods needs access to the storage and they can be on different nodes. On GKE, We are installing Storage Class that support n4-standard-8 which is hyper-balanced . Thus, each OPEA application below uses the file `eks-fs-pvc.yaml` to create Storage Class and PVC in its namespace.
72+
73+
## OPEA Applications
74+
75+
### ChatQnA
76+
77+
Use the commands below to create GKE cluster.
78+
79+
```bash
80+
terraform plan --var-file opea-chatqna.tfvars -out opea-chatqna.plan
81+
terraform apply "opea-chatqna.plan"
82+
```
83+
84+
Once the cluster is ready, update kubectl config
85+
86+
```bash
87+
gcloud container clusters get-credentials "cluster_name"-gke --region us-central1 --project "project_id"
88+
```
89+
90+
Now you should have access to the cluster via the `kubectl` command.
91+
92+
Deploy ChatQnA Application with Helm
93+
94+
```bash
95+
helm install -n chatqna --create-namespace chatqna oci://ghcr.io/opea-project/charts/chatqna --set service.type=LoadBalancer --set global.modelUsePVC=model-volume --set global.HUGGINGFACEHUB_API_TOKEN=${HFTOKEN}
96+
```
97+
98+
Create the Storage Class and PVC as mentioned [above](#-persistent-volume-claim)
99+
100+
```bash
101+
kubectl apply -f gke-fs-pvc.yaml -n chatqna
102+
```
103+
104+
After a while, the OPEA application should be running. You can check the status via `kubectl`.
105+
106+
```bash
107+
kubectl get pod -n chatqna
108+
```
109+
110+
You can now start using the OPEA application.
111+
112+
```bash
113+
OPEA_SERVICE=$(kubectl get svc -n chatqna chatqna -ojsonpath='{.status.loadBalancer.ingress[0].hostname}')
114+
curl http://${OPEA_SERVICE}:8888/v1/chatqna \
115+
-H "Content-Type: application/json" \
116+
-d '{"messages": "What is the revenue of Nike in 2023?"}'
117+
```
118+
119+
Cleanup
120+
121+
Delete the cluster via the following command.
122+
123+
```bash
124+
helm uninstall -n chatqna chatqna
125+
terraform destroy -var-file opea-chatqna.tfvars
126+
```
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright (C) 2025 Intel Corporation
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
---
5+
apiVersion: storage.k8s.io/v1
6+
kind: StorageClass
7+
metadata:
8+
name: balanced-storage
9+
provisioner: pd.csi.storage.gke.io
10+
volumeBindingMode: WaitForFirstConsumer
11+
allowVolumeExpansion: true
12+
parameters:
13+
type: hyperdisk-balanced
14+
provisioned-throughput-on-create: "250Mi"
15+
provisioned-iops-on-create: "7000"
16+
---
17+
18+
apiVersion: v1
19+
kind: PersistentVolumeClaim
20+
metadata:
21+
name: model-volume
22+
spec:
23+
accessModes:
24+
- ReadWriteOnce
25+
storageClassName: balanced-storage
26+
resources:
27+
requests:
28+
storage: 50Gi
29+
---
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# GKE cluster
2+
data "google_container_engine_versions" "gke_version" {
3+
location = var.region
4+
version_prefix = "1.30."
5+
}
6+
7+
resource "google_container_cluster" "primary" {
8+
name = "${var.cluster_name}-gke"
9+
location = var.region
10+
11+
# We can't create a cluster with no node pool defined, but we want to only use
12+
# separately managed node pools. So we create the smallest possible default
13+
# node pool and immediately delete it.
14+
remove_default_node_pool = true
15+
initial_node_count = var.initial_node_count
16+
node_locations = var.node_locations
17+
18+
network = google_compute_network.vpc.name
19+
subnetwork = google_compute_subnetwork.subnet.name
20+
}
21+
22+
# Separately Managed Node Pool
23+
resource "google_container_node_pool" "primary_nodes" {
24+
name = google_container_cluster.primary.name
25+
location = var.region
26+
cluster = google_container_cluster.primary.name
27+
28+
version = data.google_container_engine_versions.gke_version.release_channel_default_version["STABLE"]
29+
node_count = var.gke_num_nodes
30+
node_locations = var.node_locations
31+
autoscaling {
32+
max_node_count = var.max_node_count
33+
min_node_count = var.min_node_count
34+
}
35+
36+
37+
node_config {
38+
disk_size_gb = var.disk_size_gb
39+
disk_type = "hyperdisk-balanced"
40+
oauth_scopes = [
41+
"https://www.googleapis.com/auth/logging.write",
42+
"https://www.googleapis.com/auth/monitoring",
43+
]
44+
45+
labels = {
46+
env = var.cluster_name
47+
}
48+
49+
preemptible = true
50+
image_type = var.image_type
51+
machine_type = var.machine_type
52+
tags = ["gke-node", "${var.cluster_name}-gke"]
53+
metadata = {
54+
disable-legacy-endpoints = "true"
55+
}
56+
}
57+
}
58+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#This value required as part of Google Project id.
2+
project_id = "arun-poc"
3+
# Kubernetes cluster Name
4+
cluster_name = "opea-cluster"
5+
region = "us-central1"
6+
initial_node_count = 1
7+
max_node_count = 5
8+
min_node_count = 1
9+
node_locations = ["us-central1-a"]
10+
disk_size_gb = 100
11+
image_type = "COS_CONTAINERD"
12+
machine_type = "n4-standard-8"

0 commit comments

Comments
 (0)