Complete deployment guide for Streamlit LLM Chatbot on OpenShift 4.19 with Tekton CI/CD pipeline....
- Architecture Overview
- Prerequisites
- Repository Structure
- Initial Setup
- OpenShift Configuration
- Tekton Pipeline Setup
- GitHub Webhook Configuration
- Manual Deployment
- CI/CD Workflow
- Troubleshooting
GitHub Repository (main branch)
β (webhook trigger)
Tekton EventListener
β
Pipeline Execution:
1. git-clone: Clone repository
2. buildah: Build RHEL 10 UBI container
3. buildah: Push to Quay.io
4. openshift-client: Deploy to OpenShift
β
Running Application
- Deployment with 1 replica
- Service (ClusterIP)
- Route (HTTPS with edge termination)
ocCLI (OpenShift 4.19+)tknCLI (Tekton Pipelines)gitCLI- Access to OpenShift 4.19+ cluster
- Quay.io account
- GitHub account
- OpenShift Pipelines Operator 1.18+ (Tekton 0.68+)
- Cluster admin access for initial setup
- Namespace creation permissions
llm-chatbot/
βββ README.md # This file
βββ app.py # Streamlit application
βββ requirements.txt # Python dependencies
βββ Dockerfile # RHEL 10 UBI container definition
βββ .gitignore # Git ignore rules
βββ k8s/
β βββ deployment.yaml # OpenShift deployment manifests
β βββ pipeline/
β βββ pipeline.yaml # Tekton pipeline and triggers
β βββ rbac-secrets.yaml # RBAC and secrets configuration
βββ docs/
βββ architecture.md # Detailed architecture documentation
# Clone the repository
git clone https://github.com/YOUR_ORG/llm-chatbot.git
cd llm-chatbot
# Update configuration values
# Edit k8s/deployment.yaml - replace YOUR_QUAY_ORG
# Edit k8s/pipeline/pipeline.yaml - replace YOUR_ORG and YOUR_QUAY_ORG- Log in to Quay.io
- Create new repository:
llm-chatbot - Set repository to Public or configure robot account for private access
- Generate robot account credentials:
- Settings β Robot Accounts β Create Robot Account
- Grant Write permissions
- Download credentials as Kubernetes Secret (YAML format)
- Save the file as
quay-robot-secret.yaml
# Edit the downloaded quay-robot-secret.yaml # Change the data key from .dockerconfigjson to config.json apiVersion: v1 kind: Secret metadata: name: quay-auth-secret namespace: llm-chatbot type: Opaque # Change from kubernetes.io/dockerconfigjson to Opaque data: config.json: <BASE64_ENCODED_DATA> # Change key name here
# Login to your OpenShift cluster
oc login --token=YOUR_TOKEN --server=https://api.your-cluster.com:6443
# or
oc login -u <username> --server=https://api.your-cluster.com:6443
# Verify connection
oc whoami
oc versionIf not already installed:
# Create subscription for OpenShift Pipelines
cat <<EOF | oc apply -f -
apiVersion: operators.coreos.com/v1alpha1
kind: Subscription
metadata:
name: openshift-pipelines-operator
namespace: openshift-operators
spec:
channel: latest
name: openshift-pipelines-operator-rh
source: redhat-operators
sourceNamespace: openshift-marketplace
EOF
# Wait for operator to be ready
oc get csv -n openshift-operators | grep openshift-pipelinesVerify installation:
oc get pods -n openshift-pipelines
# Should show tekton-pipelines-controller, tekton-triggers-controller, etc.# Create the namespace
oc apply -f k8s/deployment.yaml
# Verify namespace creation
oc get namespace llm-chatbot
oc project llm-chatbotYou have three methods to create the Quay authentication secret:
If you downloaded the Kubernetes Secret YAML from Quay.io:
# Apply the downloaded secret directly
# First, edit the downloaded file
#
# apiVersion: v1
# kind: Secret
# metadata:
# name: quay-auth-secret # Change name
# namespace: llm-chatbot # Add namespace
# type: Opaque # Change from kubernetes.io/dockerconfigjson to Opaque
# data:
# config.json: <BASE64_ENCODED_DATA> # Change the data key from .dockerconfigjson to config.json
# Then apply it
oc apply -f quay-robot-secret.yaml
# Verify secret creation
oc get secret quay-auth-secret -n llm-chatbotIf you want to manually create the secret with your Quay credentials:
oc create secret docker-registry quay-auth-secret \
--docker-server=quay.io \
--docker-username=YOUR_QUAY_USERNAME \
--docker-password=YOUR_QUAY_PASSWORD \
--docker-email=YOUR_EMAIL \
-n llm-chatbot
# Verify secret creation
oc get secret quay-auth-secret -n llm-chatbotIf you already have Docker credentials configured locally:
# Login to Quay first
podman login quay.io
# or
docker login quay.io
# Create secret from your local docker config
oc create secret generic quay-auth-secret \
--from-file=.dockerconfigjson=${HOME}/.docker/config.json \
--type=kubernetes.io/dockerconfigjson \
-n llm-chatbot
# Verify secret creation
oc get secret quay-auth-secret -n llm-chatbotImportant: Make sure the secret is named quay-auth-secret as this is referenced in the pipeline configuration.
# Generate a secure random string for webhook
WEBHOOK_SECRET=$(openssl rand -base64 32)
# Create the secret
oc create secret generic github-webhook-secret \
--from-literal=secret=${WEBHOOK_SECRET} \
-n llm-chatbot
# Save this secret - you'll need it for GitHub webhook configuration
echo "Your webhook secret: ${WEBHOOK_SECRET}"
# Verify secret creation
oc get secret github-webhook-secret -n llm-chatbot
# Verify the secret value
oc get secret github-webhook-secret -n llm-chatbot -o jsonpath='{.data.secret}' | base64 -dOpenShift Pipelines 1.11+ uses Tekton Resolvers instead of ClusterTasks or namespace-scoped Tasks. Resolvers dynamically fetch tasks from remote sources:
- β Hub Resolver: Fetches from Tekton Hub or Artifact Hub
- β Bundles Resolver: Fetches from OCI registries
- β Cluster Resolver: References tasks in other namespaces (e.g., openshift-pipelines)
- β Git Resolver: Fetches from Git repositories
Benefits:
- No need to manually install tasks
- Always get the latest task versions
- Centralized task management
- Better security and versioning
This deployment uses:
- Hub resolver for
git-cloneandbuildahtasks (from Artifact Hub) - Cluster resolver for
openshift-clienttask (pre-installed in openshift-pipelines namespace)
# Apply RBAC configurations
oc apply -f k8s/pipeline/rbac-secrets.yaml
# Apply pipeline definitions
oc apply -f k8s/pipeline/pipeline.yaml
# Verify pipeline creation
tkn pipeline list -n llm-chatbot
# View pipeline details (you'll see tasks are resolved remotely)
tkn pipeline describe llm-chatbot-pipeline -n llm-chatbotNote: You don't need to install tasks manually - they're fetched automatically by the resolvers when the pipeline runs!
# Check EventListener service was created
oc get svc -n llm-chatbot | grep el-llm-chatbot-listener
# Check webhook route
oc get route llm-chatbot-webhook -n llm-chatbot
# Get webhook URL
WEBHOOK_URL=$(oc get route llm-chatbot-webhook -n llm-chatbot -o jsonpath='{.spec.host}')
echo "Webhook URL: https://${WEBHOOK_URL}"- Navigate to your GitHub repository
- Go to Settings β Webhooks β Add webhook
- Configure webhook:
- Payload URL:
https://YOUR_WEBHOOK_URL(from Step 9) - Content type:
application/json - Secret: Use the webhook secret from Step 7
- SSL verification: Enable
- Events: Select "Just the push event"
- Active: Check the box
- Payload URL:
- Click Add webhook
- Test webhook by pushing a commit
# 1. Check if PipelineRuns are being created
oc get pipelineruns -n llm-chatbot --sort-by=.metadata.creationTimestamp
# 2. Check for errors in EventListener
oc logs -n llm-chatbot -l eventlistener=llm-chatbot-listener --tail=100
# or using tkn
# 1. Watch pipeline execution (tasks will be resolved automatically)
tkn pipelinerun logs -f -n llm-chatbot
# 2. Check pipeline status
tkn pipelinerun list -n llm-chatbotThis deployment uses two different service accounts with distinct purposes:
Location: k8s/deployment.yaml
Purpose: Used by the application pods themselves
apiVersion: v1
kind: ServiceAccount
metadata:
name: llm-chatbot
namespace: llm-chatbotWhat it does:
- Provides an identity for your application pods to run under
- Used for pod-to-pod communication within the cluster
- Can be granted specific RBAC permissions if the app needs to interact with Kubernetes API
- Allows linking image pull secrets if needed (e.g.,
oc secrets link llm-chatbot quay-auth-secret --for=pull) - Follows principle of least privilege - each app has its own identity
- Even if your app doesn't currently need special permissions, it's a best practice to create a dedicated SA
Why we use it:
- Security: Separates application identity from default service account
- Future-proofing: Easy to add permissions later if needed
- Auditing: Clear identity in logs and security events
- Image pulls: Can be linked to private registry secrets
Location: k8s/pipeline/rbac-secrets.yaml
Purpose: Used by Tekton pipeline tasks during CI/CD
apiVersion: v1
kind: ServiceAccount
metadata:
name: pipeline
namespace: llm-chatbot
secrets:
- name: quay-auth-secret
- name: github-webhook-secretWhat it does:
- Runs all Tekton pipeline tasks (git-clone, buildah, openshift-client)
- Has elevated permissions to build images (needs privileged SCC for buildah)
- Can create/update deployments, services, routes
- Has access to Quay credentials for pushing images
- Has access to GitHub webhook secrets
Permissions granted:
- Role (namespace-level): Manage pods, deployments, services, routes, configmaps, secrets
- ClusterRole (cluster-level): Use privileged SCC for buildah container builds
Why it's separate from app SA:
- Security: Pipeline needs more permissions than the application
- Isolation: Compromised app can't modify deployments
- Auditability: Clear separation of build-time vs runtime operations
You may need to link secrets to service accounts for different purposes:
# Link secret to pipeline SA (for pushing images during build)
oc secrets link pipeline quay-auth-secret -n llm-chatbot
# Link secret to app SA (for pulling images at runtime - if using private images)
oc secrets link llm-chatbot quay-auth-secret --for=pull -n llm-chatbotThe --for=pull flag is specifically for image pull operations, while without it, the secret is mounted as a regular secret for the pods to use.
Before setting up automation, test the pipeline manually:
# Create a manual PipelineRun
cat <<EOF | oc create -f -
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: llm-chatbot-manual-
namespace: llm-chatbot
spec:
pipelineRef:
name: llm-chatbot-pipeline
params:
- name: git-url
value: https://github.com/YOUR_ORG/llm-chatbot.git
- name: git-revision
value: main
- name: image-tag
value: manual-test
workspaces:
- name: source-workspace
persistentVolumeClaim:
claimName: pipeline-workspace-pvc
- name: dockerconfig-secret
secret:
secretName: quay-auth-secret
serviceAccountName: pipeline
EOF
# Watch pipeline execution (tasks will be resolved automatically)
tkn pipelinerun logs -f -n llm-chatbot
# Check pipeline status
tkn pipelinerun list -n llm-chatbot# Check deployment status
oc get deployment llm-chatbot -n llm-chatbot
oc rollout status deployment/llm-chatbot -n llm-chatbot
# Check pods
oc get pods -n llm-chatbot
# Check service
oc get svc llm-chatbot -n llm-chatbot
# Get application URL
APP_URL=$(oc get route llm-chatbot -n llm-chatbot -o jsonpath='{.spec.host}')
echo "Application URL: https://${APP_URL}"
# Test application
curl -k https://${APP_URL}/_stcore/healthOnce configured, the CI/CD workflow operates as follows:
- Developer pushes code to
mainbranch - GitHub webhook sends event to OpenShift EventListener
- Tekton Trigger validates webhook and creates PipelineRun
- Pipeline executes:
- Clones repository
- Builds container image with Buildah
- Pushes image to Quay.io with commit SHA as tag
- Updates OpenShift deployment
- Waits for rollout to complete
- Application automatically updates with new version
# Watch all pipeline runs
tkn pipelinerun list -n llm-chatbot
# Follow specific pipeline run
tkn pipelinerun logs PIPELINE_RUN_NAME -f -n llm-chatbot
# Check EventListener logs
oc logs -f deployment/el-llm-chatbot-listener -n llm-chatbot
# View recent events
oc get events -n llm-chatbot --sort-by='.lastTimestamp'# Edit app.py to change the title
sed -i 's/LLM Chatbot/LLM Chatbot v2/g' app.py
# Commit and push
git add app.py
git commit -m "Update application title"
git push origin main
# Watch for pipeline trigger
tkn pipelinerun list -n llm-chatbot -w- Visit your Quay repository
- Verify new image with commit SHA tag
- Check image size and layers
# Check deployment image
oc get deployment llm-chatbot -n llm-chatbot -o jsonpath='{.spec.template.spec.containers[0].image}'
# Access application
curl -k https://$(oc get route llm-chatbot -n llm-chatbot -o jsonpath='{.spec.host}')# Check git-clone task logs
tkn taskrun logs TASKRUN_NAME -n llm-chatbot
# Common issues:
# - Invalid Git URL
# - Private repository without credentials
# - Network connectivity issuesSolution: Verify Git URL and add SSH key if using private repository:
oc create secret generic git-ssh-key \
--from-file=id_rsa=~/.ssh/id_rsa \
-n llm-chatbot# Check buildah logs
tkn taskrun logs -f BUILDAH_TASKRUN -n llm-chatbot
# Common issues:
# - Insufficient permissions (needs privileged SCC)
# - Registry authentication failure
# - Resource limits exceededSolution: Verify SCC permissions:
oc adm policy add-scc-to-user privileged -z pipeline -n llm-chatbotVerify Quay credentials are properly formatted:
# Check the secret exists and has correct type
oc get secret quay-auth-secret -n llm-chatbot -o yaml
# The secret should have:
# - type: kubernetes.io/dockerconfigjson
# - data: .dockerconfigjson field (base64 encoded)
# Decode to verify format (should show quay.io entry)
oc get secret quay-auth-secret -n llm-chatbot -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d | jq .# Verify Quay credentials
oc get secret quay-auth-secret -n llm-chatbot -o yaml
# Test credentials manually
podman login quay.io -u YOUR_USERNAME# Check pod status
oc get pods -n llm-chatbot
# Check pod logs
oc logs POD_NAME -n llm-chatbot
# Describe pod for events
oc describe pod POD_NAME -n llm-chatbot# Verify image exists in Quay
# Check image pull secrets
oc get deployment llm-chatbot -n llm-chatbot -o jsonpath='{.spec.template.spec.imagePullSecrets}'
# If needed, link secret to service account
oc secrets link llm-chatbot quay-auth-secret --for=pull -n llm-chatbot# Check route
oc get route llm-chatbot -n llm-chatbot
# Check service endpoints
oc get endpoints llm-chatbot -n llm-chatbot
# Test from within cluster
oc run -it --rm debug --image=registry.access.redhat.com/ubi9/ubi:latest --restart=Never -- curl http://llm-chatbot:8501/_stcore/health# Check EventListener logs
oc logs -f deployment/el-llm-chatbot-listener -n llm-chatbot
# Verify webhook secret matches
oc get secret github-webhook-secret -n llm-chatbot -o jsonpath='{.data.secret}' | base64 -d
# Check GitHub webhook delivery
# Go to GitHub β Settings β Webhooks β Recent Deliveries# Check TriggerBinding and TriggerTemplate
oc get triggerbindings -n llm-chatbot
oc get triggertemplates -n llm-chatbot
# Review EventListener configuration
oc get eventlistener llm-chatbot-listener -n llm-chatbot -o yaml# Follow application logs
oc logs -f deployment/llm-chatbot -n llm-chatbot
# View last 100 lines
oc logs deployment/llm-chatbot -n llm-chatbot --tail=100# List all pipeline runs with status
tkn pipelinerun list -n llm-chatbot
# Get pipeline run duration
tkn pipelinerun describe PIPELINERUN_NAME -n llm-chatbot# Check pod resource usage
oc adm top pods -n llm-chatbot
# Check node resource usage
oc adm top nodes# Make changes to code
vim app.py
# Commit and push (triggers automatic pipeline)
git add .
git commit -m "Your update message"
git push origin main# Scale to 3 replicas
oc scale deployment llm-chatbot --replicas=3 -n llm-chatbot
# Verify scaling
oc get pods -n llm-chatbot -w# View rollout history
oc rollout history deployment/llm-chatbot -n llm-chatbot
# Rollback to previous version
oc rollout undo deployment/llm-chatbot -n llm-chatbot
# Rollback to specific revision
oc rollout undo deployment/llm-chatbot --to-revision=2 -n llm-chatbot# Delete all resources
oc delete namespace llm-chatbot
# Remove cluster-level RBAC (if not used by other apps)
oc delete clusterrolebinding pipeline-clusterrolebinding
oc delete clusterrole pipeline-clusterrole- OpenShift Pipelines Documentation
- Tekton Documentation
- Buildah Documentation
- Quay.io Documentation
- RHEL UBI Documentation
- Streamlit Documentation
- Fork the repository
- Create a feature branch
- Make your changes
- Test thoroughly
- Submit a pull request
[Your License Here]
For issues and questions:
- Open an issue on GitHub
- Contact: your-email@example.com
tkn pipeline start llm-chatbot-pipeline
-n llm-chatbot
--param git-url=https://github.com/pgustafs/llm-chatbot.git
--param git-revision=main
--param image-name=quay.io/pgustafs/llm-chatbot
--param image-tag=test-$(date +%s)
--param dockerfile-path=./Dockerfile
--param context-dir=.
--workspace name=source-workspace,claimName=pipeline-workspace-pvc
--workspace name=dockerconfig-secret,secret=quay-auth-secret
--serviceaccount pipeline
--showlog