1- name : Deploy to Azure
1+ name : Deploy Memgraph on AKS
22
33on :
44 push :
5- branches : [ dev ]
5+ branches : [ main ]
66 workflow_dispatch :
77
88permissions :
9- id-token : write
9+ id-token : write # OIDC login
1010 contents : read
1111
1212env :
13- ENVIRONMENT : dev
14- AZURE_RESOURCE_GROUP : GitHub
15- AZURE_LOCATION : germanywestcentral
13+ # Azure authentication
14+ ARM_SUBSCRIPTION_ID : ${{ secrets.AZURE_SUBSCRIPTION_ID }}
15+ ARM_TENANT_ID : ${{ secrets.AZURE_TENANT_ID }}
16+ ARM_CLIENT_ID : ${{ secrets.AZURE_CLIENT_ID }}
17+
18+ # Stack parameters
19+ ENVIRONMENT : dev
20+ NODE_VM_SIZE : Standard_B2ms # change here if you need a bigger node
1621
1722jobs :
18- deploy-infrastructure :
23+ deploy :
1924 runs-on : ubuntu-latest
20- timeout-minutes : 30 # Add timeout to prevent infinite runs
25+ defaults : { run: { shell: bash } }
26+
2127 steps :
22- - uses : actions/checkout@v3
23-
24- - name : Azure Login
25- uses : azure/login@v2
26- with :
27- client-id : ${{ secrets.AZURE_CLIENT_ID }}
28- tenant-id : ${{ secrets.AZURE_TENANT_ID }}
29- subscription-id : ${{ secrets.AZURE_SUBSCRIPTION_ID }}
30- allow-no-subscriptions : false
31- audience : api://AzureADTokenExchange
32- auth-type : SERVICE_PRINCIPAL
33-
34- - name : Setup Terraform
35- uses : hashicorp/setup-terraform@v2
36-
37- - name : Terraform Init
38- run : |
39- cd infra/azure
40- terraform init
41-
42- - name : Import Existing Resources
43- run : |
44- cd infra/azure
45- chmod +x ./import_resources.sh
46- ENVIRONMENT=${{ env.ENVIRONMENT }} \
47- SUBSCRIPTION_ID=${{ secrets.AZURE_SUBSCRIPTION_ID }} \
48- TENANT_ID=${{ secrets.AZURE_TENANT_ID }} \
49- OBJECT_ID=${{ secrets.AZURE_SP_OBJECT_ID }} \
50- CI_MODE=true \
51- ./import_resources.sh
52-
53- - name : Terraform Apply
54- timeout-minutes : 120 # Increase timeout to 2 hours for AKS node pool operations
55- run : |
56- cd infra/azure
57- # Set TF_PLUGIN_TIMEOUT to increase the timeout for Azure provider operations
58- export TF_PLUGIN_TIMEOUT=120m
59- terraform apply -auto-approve \
60- -var="environment=${{ env.ENVIRONMENT }}" \
61- -var="memgraph_username=${{ secrets.MEMGRAPH_USERNAME }}" \
62- -var="memgraph_password=${{ secrets.MEMGRAPH_PASSWORD }}" \
63- -var="subscription_id=${{ secrets.AZURE_SUBSCRIPTION_ID }}" \
64- -var="tenant_id=${{ secrets.AZURE_TENANT_ID }}" \
65- -var="object_id=${{ secrets.AZURE_SP_OBJECT_ID }}"
66-
67- - name : Get AKS Credentials
68- run : |
69- # Wait for the AKS cluster to be ready
70- echo "Waiting for AKS cluster to be ready..."
71- for i in {1..30}; do
72- if az aks show --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
73- --name aks-ai-agent-${{ env.ENVIRONMENT }} --query "provisioningState" -o tsv | grep -q "Succeeded"; then
74- echo "AKS cluster is ready"
75- break
76- fi
77- echo "Waiting for AKS cluster to be ready (attempt $i)..."
78- sleep 10
79- done
80-
81- # Get credentials
82- az aks get-credentials --resource-group ${{ env.AZURE_RESOURCE_GROUP }} \
83- --name aks-ai-agent-${{ env.ENVIRONMENT }}
84-
85- - name : Create K8s Secret for Memgraph
86- run : |
87- # Check if kubectl is working and the cluster is accessible
88- if ! kubectl get nodes &>/dev/null; then
89- echo "Cannot connect to Kubernetes cluster. Waiting for it to become available..."
90- for i in {1..10}; do
91- sleep 15
92- if kubectl get nodes &>/dev/null; then
93- echo "Successfully connected to Kubernetes cluster"
94- break
95- fi
96- echo "Attempt $i: Still waiting for Kubernetes cluster..."
97- if [ $i -eq 10 ]; then
98- echo "Failed to connect to Kubernetes cluster after multiple attempts. Continuing anyway..."
99- fi
100- done
101- fi
102-
103- # Create a credentials hash to force pod restart when credentials change
104- CREDENTIALS_HASH=$(echo -n "${{ secrets.MEMGRAPH_USERNAME }}${{ secrets.MEMGRAPH_PASSWORD }}" | sha256sum | awk '{print $1}')
105- echo "CREDENTIALS_HASH=$CREDENTIALS_HASH" >> $GITHUB_ENV
106-
107- # Create secret directly using kubectl command
108- kubectl create secret generic memgraph-credentials \
109- --from-literal=username="${{ secrets.MEMGRAPH_USERNAME }}" \
110- --from-literal=password="${{ secrets.MEMGRAPH_PASSWORD }}" \
111- --dry-run=client -o yaml | kubectl apply -f -
112-
113- echo "Memgraph credentials secret created successfully"
114-
115- - name : Deploy to AKS
116- run : |
117- # Replace the placeholder with the actual credentials hash and environment
118- cat infra/k8s/memgraph.yaml | \
119- sed "s/\${CREDENTIALS_HASH}/$CREDENTIALS_HASH/g" | \
120- sed "s/\${ENVIRONMENT}/${{ env.ENVIRONMENT }}/g" > memgraph_deploy.yaml
121-
122- # Split the manifest into PVC and non-PVC resources
123- csplit -z -f part_ memgraph_deploy.yaml "/^---$/" '{*}'
124-
125- # Apply PVCs only if they do not exist
126- for f in part_*; do
127- if grep -q 'kind: PersistentVolumeClaim' "$f"; then
128- PVC_NAME=$(grep 'name:' "$f" | head -1 | awk '{print $2}')
129- if kubectl get pvc "$PVC_NAME" &>/dev/null; then
130- echo "PVC $PVC_NAME already exists, skipping creation."
131- else
132- kubectl apply -f "$f"
133- fi
134- else
135- kubectl apply -f "$f"
136- fi
137- done
138-
139- # Force restart if the deployment already exists
140- POD_NAME=$(kubectl get pods -l app=memgraph -o jsonpath="{.items[0].metadata.name}" 2>/dev/null || echo "")
141- if [[ -n "$POD_NAME" ]]; then
142- echo "Forcing restart of existing Memgraph pod..."
143- kubectl delete pod $POD_NAME
144- fi
145-
146- # Remove the temporary files
147- rm memgraph_deploy.yaml part_*
148-
149- - name : Verify Deployment
150- run : |
151- echo "Checking deployment status..."
152- kubectl get pods
153- kubectl get services
154-
155- echo "Waiting for Memgraph pod to be ready (may take up to 10 minutes)..."
156- # Check for failed pods with a simpler approach that doesn't rely on complex jsonpath
157- FAILED_PODS_FAILED=$(kubectl get pods -l app=memgraph --field-selector=status.phase=Failed -o jsonpath="{.items[*].metadata.name}" 2>/dev/null || echo "")
158- FAILED_PODS_CRASH=$(kubectl get pods -l app=memgraph -o jsonpath="{.items[?(@.status.containerStatuses[0].state.waiting.reason=='CrashLoopBackOff')].metadata.name}" 2>/dev/null || echo "")
159-
160- FAILED_PODS="$FAILED_PODS_FAILED $FAILED_PODS_CRASH"
161- if [[ -n "$FAILED_PODS" ]]; then
162- echo "Found failed pods, cleaning up before proceeding:"
163- for pod in $FAILED_PODS; do
164- if [[ -n "$pod" ]]; then
165- echo "Deleting failed pod: $pod"
166- kubectl delete pod $pod
167- fi
168- done
169- echo "Waiting for cleanup to complete..."
170- sleep 30
171- fi
172-
173- # Wait for pod to be ready with increased timeout
174- if ! kubectl wait --for=condition=ready pod -l app=memgraph --timeout=10m; then
175- echo "Error: Memgraph pod did not become ready within the timeout period."
176- echo "Checking Memgraph pod logs:"
177- POD_NAME=$(kubectl get pods -l app=memgraph -o jsonpath="{.items[0].metadata.name}" 2>/dev/null || echo "")
178-
179- if [[ -n "$POD_NAME" ]]; then
180- echo "Pod details:"
181- kubectl describe pod $POD_NAME
182-
183- echo "Pod logs:"
184- kubectl logs $POD_NAME --tail=100
185- else
186- echo "No Memgraph pod found to check logs."
187- fi
188-
189- echo "Checking node resource utilization:"
190- kubectl describe nodes
191-
192- echo "Deployment verification failed! Please check the Kubernetes cluster's resources and capacity."
193- exit 1
194- fi
195-
196- echo "Memgraph deployment successful!"
197-
198- echo "Waiting for LoadBalancer to expose an address..."
199- for i in {1..45}; do
200- EXTERNAL_IP=$(kubectl get service memgraph -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null || echo "")
201- DNS_NAME=$(kubectl get service memgraph -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' 2>/dev/null || echo "")
202- if [[ -n "$EXTERNAL_IP" || -n "$DNS_NAME" ]]; then
203- HOST=${EXTERNAL_IP:-$DNS_NAME}
204- echo "Memgraph is accessible at: ${HOST}:7687"
205- # make host usable by subsequent steps
206- echo "MEMGRAPH_URI=${HOST}" >> $GITHUB_ENV
207- echo "Performing basic connectivity test..."
208- if nc -z -w 5 ${HOST} 7687; then
209- echo "Successfully connected to Memgraph on port 7687!"
210- else
211- echo "Warning: Could not connect to Memgraph on port 7687. The service may not be fully ready yet."
212- fi
213- break
214- fi
215- echo "Waiting for address (attempt $i)..."
216- sleep 10
217- done
218-
219- if [[ -z "$EXTERNAL_IP" && -z "$DNS_NAME" ]]; then
220- echo "Warning: LoadBalancer address not available within timeout."
221- kubectl describe service memgraph
222- else
223- MEMGRAPH_HOST=${EXTERNAL_IP:-$DNS_NAME}
224- echo "=========================================================="
225- echo "Memgraph Connection Information:"
226- echo "Host: $MEMGRAPH_HOST"
227- echo "Port: 7687 (Bolt), 7444 (HTTP API), 3000 (UI)"
228- echo "Username: ${{ secrets.MEMGRAPH_USERNAME }}"
229- echo "Password: [Configured in secrets]"
230- echo "Connection URL: bolt://${{ secrets.MEMGRAPH_USERNAME }}@$MEMGRAPH_HOST:7687"
231- echo "=========================================================="
232- fi
28+ # ─────────────────────────── repo ───────────────────────────
29+ - uses : actions/checkout@v4
30+
31+ # ────────────────────── Azure login (OIDC) ──────────────────
32+ - name : Azure login
33+ uses : azure/login@v2
34+ with :
35+ client-id : ${{ env.ARM_CLIENT_ID }}
36+ tenant-id : ${{ env.ARM_TENANT_ID }}
37+ subscription-id : ${{ env.ARM_SUBSCRIPTION_ID }}
38+ auth-type : SERVICE_PRINCIPAL
39+ audience : api://AzureADTokenExchange
40+
41+ # ──────────────────────── Terraform ─────────────────────────
42+ - uses : hashicorp/setup-terraform@v2
43+
44+ - name : Terraform init
45+ run : terraform -chdir=infra init
46+
47+ - name : Terraform apply
48+ run : |
49+ terraform -chdir=infra apply -auto-approve \
50+ -var="subscription_id=${{ env.ARM_SUBSCRIPTION_ID }}" \
51+ -var="location=germanywestcentral" \
52+ -var="environment=${{ env.ENVIRONMENT }}" \
53+ -var="node_vm_size=${{ env.NODE_VM_SIZE }}"
54+
55+ # ─────────────────── Get AKS kubeconfig ─────────────────────
56+ - name : Pull cluster credentials
57+ run : |
58+ az aks get-credentials \
59+ --resource-group rg-aks-memgraph-${{ env.ENVIRONMENT }} \
60+ --name aks-memgraph-${{ env.ENVIRONMENT }} \
61+ --overwrite-existing
62+
63+ # ────────────────── Build / store credentials ──────────────
64+ - name : Build credentials hash
65+ id : hash
66+ run : |
67+ echo "value=$(echo -n '${{ secrets.MEMGRAPH_USERNAME }}${{ secrets.MEMGRAPH_PASSWORD }}' \
68+ | sha256sum | cut -d' ' -f1)" >> "$GITHUB_OUTPUT"
69+
70+ - name : Create / update K8s secret
71+ env :
72+ MEMGRAPH_USERNAME : ${{ secrets.MEMGRAPH_USERNAME }}
73+ MEMGRAPH_PASSWORD : ${{ secrets.MEMGRAPH_PASSWORD }}
74+ run : |
75+ kubectl create secret generic memgraph-credentials \
76+ --from-literal=username="$MEMGRAPH_USERNAME" \
77+ --from-literal=password="$MEMGRAPH_PASSWORD" \
78+ --dry-run=client -o yaml | kubectl apply -f -
79+
80+ # ──────────────────── Apply Kubernetes YAML ─────────────────
81+ - name : Apply StorageClass
82+ run : kubectl apply -f infra/k8s/storageclass.yaml
83+
84+ - name : Apply Memgraph manifest
85+ env :
86+ CREDENTIALS_HASH : ${{ steps.hash.outputs.value }}
87+ ENVIRONMENT : ${{ env.ENVIRONMENT }}
88+ run : envsubst < infra/k8s/memgraph.yaml | kubectl apply -f -
89+
90+ # ──────────────────────── Smoke test ────────────────────────
91+ - name : Wait for rollout
92+ run : kubectl rollout status deployment/memgraph --timeout=5m
0 commit comments