DEV Community

Clariza Look
Clariza Look

Posted on • Edited on

Migrating to Self-Hosted 3scale API Management on ROSA Kubernetes

I was tasked to migrate from a Red Hat-hosted 3scale portal to a self-hosted version in ROSA (Red Hat OpenShift Service on AWS). This presented quite a challenge as my knowledge in Kubernetes was mostly theoretical, based on studying for the Kubernetes and Cloud Native Associate (KCNA) certification exam.

The goal was to recreate a self-hosted version of 3scale using an operator in ROSA, but what I thought would be a straightforward deployment turned into a valuable learning experience.

What is Red Hat-Managed/hosted 3scale?

When using Red Hat-hosted 3scale (also known as "SaaS" or managed 3scale), all infrastructure complexities are abstracted away. Red Hat handles the deployment, maintenance, updates, and scaling of the platform.

As a user, you simply access a provided portal URL and focus on managing your APIs rather than worrying about the underlying infrastructure. Your daily tasks revolve around the actual API management activities like adding backends, configuring products, creating applications, setting up authentication, and managing rate limits.

It's a convenient option that requires minimal operational overhead, allowing your team to focus on API strategy rather than platform management.

How 3Scale Api Management Portal looks like

What is self-hosted 3scale?

In contrast, self-hosted 3scale brings both flexibility and responsibility. You gain complete control over your deployment configuration, integration with internal systems, customization options, and data locality.

Since the infrastructure runs on Kubernetes (in my case, ROSA - Red Hat OpenShift Service on AWS), you have access to all the native Kubernetes capabilities for scaling, monitoring, and management.

However, this freedom comes with the need to manage the entire application lifecycle within the Kubernetes ecosystem: installation via operators or templates, configuration through custom resources, scaling via horizontal pod autoscalers, implementing backup strategies, and handling upgrades.

You're responsible for ensuring high availability with proper pod distribution, performance tuning through resource allocation, and troubleshooting any issues that arise in both the 3scale application components and the underlying Kubernetes resources.

The Migration

Migrating from managed to self-hosted represented a significant shift in responsibilities, and I was about to discover just how much Red Hat had been handling behind the scenes.

This blog post documents a real-world troubleshooting journey that encountered and overcame significant challenges:

  1. Missing Routes for Admin Access

  2. DNS resolution issues preventing access to Red Hat's container registry

  3. Architecture mismatch between my ARM-based MacBook for and the x86_64 docker container images required for deployment

  4. PVC Access Mode Issues

  5. Resource Constraints

  6. Missing Service for App Components

By sharing this experience, I hope to help others who might encounter similar issues during their deployment process, especially those who are transitioning from theoretical Kubernetes knowledge to practical application.

The Initial Deployment Attempt

We started by creating a dedicated namespace for our 3scale deployment:

oc create namespace 3scale-backup 
Enter fullscreen mode Exit fullscreen mode

After switching to this namespace (oc project 3scale-backup), we downloaded the 3scale API Management Platform template:

curl -o amp.yml https://raw.githubusercontent.com/3scale/3scale-amp-openshift-templates/master/amp/amp.yml 
Enter fullscreen mode Exit fullscreen mode

Then we tried to deploy 3scale using this template:

oc new-app --file=amp.yml \ --param WILDCARD_DOMAIN=apps.[domain of your openshift].openshiftapps.com \ --param ADMIN_PASSWORD=password123 
Enter fullscreen mode Exit fullscreen mode

The template processing appeared successful, creating numerous resources:

  • Imagestreams
  • Deployment configs
  • Services
  • Routes
  • Persistent volume claims
  • Secrets
oc get all -n [your namespace] 
Enter fullscreen mode Exit fullscreen mode

3Scale API Management Rosa deployment

However, when checking the status of the pods, we noticed that many deployments were either not starting, with errors, crashLoopBackOff or stuck in initialization phases:

oc get pods 
Enter fullscreen mode Exit fullscreen mode

Get Pods

While some components like Redis and database pods were running fine, critical components like backend-listener, backend-worker, and backend-cron were not deploying at all.

backend-worker issues

The system components were also failing during initialization.

Challenge 1: Missing Routes for Admin Access

Our first challenge was that the URLs for accessing the admin portal https://3scale-admin.apps.[YOUR-ROSA-DOMAIN].openshiftapps.com were showing "Application is not available".

The reason was simple - the template had not created the necessary routes for our self-hosted 3scale services. We manually created them:

oc create route edge system-admin --service=system-provider --hostname=3scale-admin.apps.[YOUR-ROSA-DOMAIN].openshiftapps.com oc create route edge system-developer --service=system-developer --hostname=3scale.apps.[YOUR-ROSA-DOMAIN].openshiftapps.com oc create route edge system-master --service=system-master --hostname=master.apps.[YOUR-ROSA-DOMAIN].openshiftapps.com 
Enter fullscreen mode Exit fullscreen mode

However, after creating the routes, the admin portal still wasn't accessible. Digging into the logs with oc logs system-app-1-hook-pre, we discovered a more fundamental issue.

Challenge 2: DNS Resolution Issues

The pre-deployment hook was failing with a specific error:

ThreeScale::Core::APIClient::ConnectionError: connection refused: backend-listener:80 
Enter fullscreen mode Exit fullscreen mode

Further investigation revealed that the backend components weren't deployed at all. When checking the deployment configs:

oc get dc/backend-listener NAME REVISION DESIRED CURRENT TRIGGERED BY backend-listener 0 1 0 config,image(amp-backend:2.12) 
Enter fullscreen mode Exit fullscreen mode

We saw that backend-listener, backend-worker, and backend-cron had REVISION 0 and CURRENT 0, indicating they hadn't been deployed.

oc get dc/backend-listener

The root cause was found in the imagestream:

oc describe imagestream amp-backend 
Enter fullscreen mode Exit fullscreen mode

This showed an error:

error: Import failed (InternalError): Internal error occurred: registry.redhat.com/3scale-amp2/backend-rhel8:3scale2.12: Get "https://registry.redhat.com/v2/": dial tcp: lookup registry.redhat.com on 100.10.0.11:23: no such host 
Enter fullscreen mode Exit fullscreen mode

Our OpenShift cluster couldn't resolve the hostname registry.redhat.com due to DNS issues. This was confirmed by attempting to run:

nslookup registry.redhat.com 
Enter fullscreen mode Exit fullscreen mode

Which returned "No answer" from the DNS server.

Or technically it does not pull the image required from the RedHat registry to the pods.

So our workaround was to manually pull the image to the docker (locally), then push that image to the namespace's private RedHat registry.

Challenge 3: Architecture Mismatch

While working to address the DNS issues , we discovered another challenge - we were trying to pull Red Hat's container images on an ARM64-based machine (likely an Apple Silicon Mac), but the images were only available for x86_64 architecture.

When attempting to pull the images directly:

docker login [RedHat Credentials] docker pull registry.redhat.io/3scale-amp2/backend-rhel8:3scale2.12 
Enter fullscreen mode Exit fullscreen mode

We received:

no matching manifest for linux/arm64/v8 in the manifest list entries 
Enter fullscreen mode Exit fullscreen mode

The Solution Process

We implemented a multi-step solution to overcome these challenges:

Step 1: Authentication with Red Hat Registry

First, we logged in to the Red Hat Container Registry:

docker login registry.redhat.io 
Enter fullscreen mode Exit fullscreen mode

Step 2: Architecture-aware Image Pulling

Because I was using macOS and having the docker desktop installed in it, the pulled image does not match with the operating system's arch.

To overcome the architecture mismatch, we explicitly specified the platform when pulling:

docker pull --platform linux/amd64 registry.redhat.io/3scale-amp2/backend-rhel8:3scale2.12 
Enter fullscreen mode Exit fullscreen mode

This successfully pulled the image by using Rosetta 2 emulation on macOS.

Redhat Docker Image Pull

Step 3: Exposing the OpenShift Registry

To make our OpenShift registry accessible:

oc patch configs.imageregistry.operator.openshift.io/cluster --patch '{"spec":{"defaultRoute":true}}' --type=merge 
Enter fullscreen mode Exit fullscreen mode

Step 4: Pushing Images to Internal Registry

We pushed the pulled images to our OpenShift internal registry:

# Get credentials TOKEN=$(oc whoami -t) REGISTRY=$(oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}') # Login to registry docker login -u kubeadmin -p $TOKEN $REGISTRY # Tag and push docker tag registry.redhat.io/3scale-amp2/backend-rhel8:3scale2.12 $REGISTRY/[namespace]/amp-backend:2.12 docker push $REGISTRY/[namespace]/amp-backend:2.12 
Enter fullscreen mode Exit fullscreen mode

Step 5: Updating ImageStreams

We updated the imagestream to point to our locally pushed image:

oc tag $REGISTRY/[namespace]/amp-backend:2.12 amp-backend:2.12 --source=docker 
Enter fullscreen mode Exit fullscreen mode

This automatically triggered the deployment due to the ImageChange trigger on the deployment config.

Results

After implementing these steps for the backend-listener component, the deployment began successfully (at least for this resource!).

Challenge 4: PVC Access Mode Issues

So we went to the 3Scale self-hosted Admin Portal and checked that still it's not working.

We checked the pods and found out that some of them are having issues.

apicast-production-1-deploy Issue

The error logs show that several deployments are failing because their pods are taking too long to become available (timeout errors):

  • apicast-production-1-deploy: "pods took longer than 1800 seconds to become available"
  • system-sidekiq-1-deploy: "pods took longer than 1200 seconds to become available"
  • system-sphinx-1-deploy: "pods took longer than 1200 seconds to become available"

This typically happens when pods are stuck in a pending or initializing state for too long.

So we checked the logs of the problematic pods and checked the PVC as well.

# Check logs for apicast-production deployment oc logs apicast-production-1-deploy # Check logs for system-sidekiq deployment oc logs system-sidekiq-1-deploy # Check logs for system-sphinx deployment oc logs system-sphinx-1-deploy # Check events for the pending pod oc describe pod system-app-1-hook-pre 
Enter fullscreen mode Exit fullscreen mode

We discovered a storage issue where the system-storage PVC was failing to provision:

oc get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE backend-redis-storage Bound pvc-ss987s5b-026a-4srg-au97-549d8958933a 1Gi RWO gp3 53m mysql-storage Bound pvc-72s43210-s033-4c8w-ar53-043bf3kk1496 1Gi RWO gp3 53m system-redis-storage Bound pvc-s3r1111d2-4d57-41dd-9066-c488eda666d4 1Gi RWO gp3 53m system-storage Pending 
Enter fullscreen mode Exit fullscreen mode

The error was related to access modes:

failed to provision volume with StorageClass "gp3": rpc error: code = InvalidArgument desc = Volume capabilities MULTI_NODE_MULTI_WRITER not supported. Only AccessModes[ReadWriteOnce] supported. 
Enter fullscreen mode Exit fullscreen mode

We fixed it by creating a new PVC with the correct access mode:

# First, delete pods using this PVC oc delete pod system-app-1-hook-pre # Back up the current PVC definition oc get pvc system-storage -o yaml > system-storage-pvc.yaml # Delete the stuck PVC oc delete pvc system-storage # Create a new PVC with the correct settings oc create -f - <<EOF apiVersion: v1 kind: PersistentVolumeClaim metadata: name: system-storage namespace: [Namespace] labels: app: 3scale-api-management threescale_component: system threescale_component_element: app spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: gp3 EOF 
Enter fullscreen mode Exit fullscreen mode

After fixing the PVC issue, restart the deployments:

oc rollout retry dc/system-app oc rollout retry dc/apicast-production oc rollout retry dc/backend-listener oc rollout retry dc/system-sidekiq oc rollout retry dc/system-sphinx 
Enter fullscreen mode Exit fullscreen mode

The PVC issue got fixed, and the system-storage PVC is now correctly bound to a volume.

oc get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE backend-redis-storage Bound pvc-ss987s5b-026a-4srg-au97-549d8958933a 1Gi RWO gp3 53m mysql-storage Bound pvc-72s43210-s033-4c8w-ar53-043bf3kk1496 1Gi RWO gp3 53m system-redis-storage Bound pvc-s3r1111d2-4d57-41dd-9066-c488eda666d4 1Gi RWO gp3 53m system-storage Bound pvc-2286196a-8885-490s-11c1-654320bd8a5a6 1Gi RWO gp3 116s 
Enter fullscreen mode Exit fullscreen mode

Challenge 5: Resource Constraints

Even after resolving the PVC issue, pods were still stuck in Pending state due to insufficient resources:

oc describe pod system-app-2-mr25z 
Enter fullscreen mode Exit fullscreen mode
... Warning FailedScheduling 2m34s default-scheduler 0/9 nodes are available: 2 Insufficient cpu, 3 node(s) had untolerated taint {node-role.kubernetes.io/infra: }, 3 node(s) had untolerated taint {node-role.kubernetes.io/master: }, 6 node(s) had volume node affinity conflict. 
Enter fullscreen mode Exit fullscreen mode

We reduced the resource requirements to make the pods fit on the available nodes:

oc patch dc/system-app -p '{"spec":{"template":{"spec":{"containers":[{"name":"system-master","resources":{"requests":{"cpu":"25m","memory":"400Mi"}}},{"name":"system-provider","resources":{"requests":{"cpu":"25m","memory":"400Mi"}}},{"name":"system-developer","resources":{"requests":{"cpu":"25m","memory":"400Mi"}}}]}}}}' oc patch dc/apicast-production -p '{"spec":{"template":{"spec":{"containers":[{"name":"apicast-production","resources":{"requests":{"cpu":"25m","memory":"128Mi"}}}]}}}}' oc patch dc/system-sidekiq -p '{"spec":{"template":{"spec":{"containers":[{"name":"system-sidekiq","resources":{"requests":{"cpu":"25m","memory":"250Mi"}}}]}}}}' oc patch dc/system-sphinx -p '{"spec":{"template":{"spec":{"containers":[{"name":"system-sphinx","resources":{"requests":{"cpu":"25m","memory":"250Mi"}}}]}}}}' 
Enter fullscreen mode Exit fullscreen mode

After applying these patches, we restarted the failed components:

# Retry system-sidekiq and system-sphinx deployments oc rollout retry dc/system-sidekiq oc rollout retry dc/system-sphinx 
Enter fullscreen mode Exit fullscreen mode

That got fixed too!!

 ➜ oc get services NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE apicast-production ClusterIP xxx.xx.xxx.xxx <none> 8080/TCP,8090/TCP 83m apicast-staging ClusterIP xxx.xx.xxx.xxx <none> 8080/TCP,8090/TCP 83m backend-listener ClusterIP xxx.xx.xxx.xxx <none> 3000/TCP 83m backend-redis ClusterIP xxx.xx.xxx.xxx <none> 6379/TCP 83m system-developer ClusterIP xxx.xx.xxx.xxx <none> 3000/TCP 83m system-master ClusterIP xxx.xx.xxx.xxx <none> 3000/TCP 83m system-memcache ClusterIP xxx.xx.xxx.xxx <none> 11211/TCP 83m system-mysql ClusterIP xxx.xx.xx.xxx <none> 3306/TCP 83m system-provider ClusterIP xxx.xx.x.xxx <none> 3000/TCP 83m system-redis ClusterIP xxx.xx.xxx.xxx <none> 6379/TCP 83m system-sphinx ClusterIP xxx.xx.xxx.xxx <none> 9306/TCP 83m zync ClusterIP xxx.xx.xxx.xx <none> 8080/TCP 83m zync-database ClusterIP xxx.xx.xx.xxx <none> 5432/TCP 83m 
Enter fullscreen mode Exit fullscreen mode
➜ oc get routes NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD backend backend-3scale.apps.[YOUR-DOMAIN].openshiftapps.com backend-listener http edge/Allow None system-admin 3scale-admin.apps.[YOUR-DOMAIN].openshiftapps.com system-app 3000 edge/Allow None system-developer 3scale.apps.[YOUR-DOMAIN].openshiftapps.com /developer system-app 3001 edge/Allow None system-master master.apps.[YOUR-DOMAIN].openshiftapps.com system-app 3002 edge/Allow None system-provider 3scale.apps.[YOUR-DOMAIN].openshiftapps.com system-app 3000 edge/Allow None zync-3scale-api-hhhjs api-3scale-apicast-production.apps.[YOUR-DOMAIN].openshiftapps.com apicast-production gateway edge/Redirect None zync-3scale-api-phh9n api-3scale-apicast-staging.apps.[YOUR-DOMAIN].p1.openshiftapps.com apicast-staging gateway edge/Redirect None zync-3scale-master-nhhht HostAlreadyClaimed system-master http edge/Redirect None zync-3scale-provider-q9hh9 HostAlreadyClaimed system-developer http edge/Redirect None zync-3scale-provider-shh6z HostAlreadyClaimed system-provider http edge/Redirect None 
Enter fullscreen mode Exit fullscreen mode

Since the containers are starting up, it should be a matter of minutes before we can access the admin portal.

We were just pod status, and when system-app shows 3/3 ready, tried accessing the admin portal at:

https://3scale-admin.apps.[YOUR-DOMAIN].openshiftapps.com

But then, it is still UNAVAILABLE.

Challenge 6: Missing Service for App Components

Even after all pods were running, the admin portal was not accessible. The issue was that we created routes pointing to a service named "system-app" which didn't exist:

oc get routes NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD system-admin 3scale-admin.apps.[YOUR-DOMAIN].openshiftapps.com system-app 3000 edge/Allow None system-developer 3scale.apps.[YOUR-DOMAIN].openshiftapps.com /developer system-app 3001 edge/Allow None system-master master.apps.[YOUR-DOMAIN].openshiftapps.com system-app 3002 edge/Allow None system-provider 3scale.apps.[YOUR-DOMAIN].openshiftapps.com system-app 3000 edge/Allow None 
Enter fullscreen mode Exit fullscreen mode
oc describe service system-app Error from server (NotFound): services "system-app" not found 
Enter fullscreen mode Exit fullscreen mode

We fixed this by creating the missing service:

bash oc create -f - <<EOF apiVersion: v1 kind: Service metadata: name: system-app namespace: [NAMESPACE] labels: app: 3scale-api-management spec: ports: - name: provider port: 3000 protocol: TCP targetPort: 3000 - name: developer port: 3001 protocol: TCP targetPort: 3001 - name: master port: 3002 protocol: TCP targetPort: 3002 selector: deploymentConfig: system-app type: ClusterIP EOF 
Enter fullscreen mode Exit fullscreen mode

Final Result
After working through all these challenges, we finally had a fully operational 3scale deployment:

bash oc get pods NAME READY STATUS RESTARTS AGE apicast-production-4-7hh00 1/1 Running 0 2m12s apicast-staging-1-6hh00 1/1 Running 0 83m backend-cron-2-6955a 1/1 Running 0 23m backend-listener-1-5hh00 1/1 Running 0 26m backend-redis-1-mhh005 1/1 Running 0 57m backend-worker-2-lr8gb 1/1 Running 0 23m system-app-3-7ln8g 3/3 Running 0 85s system-memcache-1-xddig 1/1 Running 0 80m system-mysql-1-ee4wt 1/1 Running 0 80m system-redis-1-45hh0 1/1 Running 0 80m zync-1-l7ghy 1/1 Running 0 80m zync-database-1-dt3l9 1/1 Running 0 80m zync-que-1-wwri9 1/1 Running 2 (80m ago) 80m 
Enter fullscreen mode Exit fullscreen mode

With all components running, finally, we were able to access the 3scale admin portal and begin configuring our APIs.

3Scale Api Management On Rosa Intro

Verify Deployment

# Check all pods are running oc get pods # Expected output should show all pods in Running or Completed state: # - system-app-X-XXXXX (3/3 Running) # - apicast-production-X-XXXXX (1/1 Running) # - apicast-staging-X-XXXXX (1/1 Running) # - system-sidekiq-X-XXXXX (1/1 Running) # - system-sphinx-X-XXXXX (1/1 Running) # - backend-* pods (1/1 Running) # - system-mysql-X-XXXXX (1/1 Running) # - system-redis-X-XXXXX (1/1 Running) # - zync-* pods (1/1 Running) 
Enter fullscreen mode Exit fullscreen mode

Key Lessons Learned

  1. DNS Resolution is Critical: Ensure your OpenShift cluster can resolve external registry hostnames before attempting deployments that rely on them.

  2. Architecture Awareness: When working with enterprise container images on ARM-based development machines, be explicit about architecture requirements using the --platform flag.

  3. Manual Image Mirroring: In restricted environments, manually pulling and pushing images to an internal registry is a viable workaround.

  4. ImageStream Mechanics: Understanding how OpenShift's ImageStreams work is essential for troubleshooting deployment issues.

  5. Network Policies: In enterprise environments, network policies may restrict access to external registries, requiring coordination with network administrators.

Common Issues and Solutions

  • Pod stuck in Pending: Usually resource constraints - reduce resource requests
  • PVC mounting issues: Check storage class and access modes
  • Routes not working: Ensure services exist and selectors match pods
  • Application not available: Create missing system-app service
  • Database connection issues: Check that system-mysql pod is running and accessible

Final Verification Checklist

  • All pods are in Running state (except completed deployment/hook pods)
  • Routes are accessible and don't show "Application not available"
  • Can log into Admin Portal successfully
  • Can access Developer Portal
  • Master Portal is accessible (if needed)
  • Default passwords have been changed

Once we successfully access the 3scale portals, we can begin migrating the existing 3scale components from another environment or start adding the new components in 3scale.

A. Backend APIs

  • API definitions and configurations
  • Authentication settings
  • Rate limiting rules

B. Products (API Products)

  • Product configurations
  • Application plans
  • Pricing rules
  • Methods and metrics

C. Applications

  • Application keys and secrets
  • Application plans assignments
  • Usage statistics (if needed)

D. Accounts and Users

  • Developer accounts
  • Admin users
  • Access permissions

E. Policies

  • Custom policies
  • Policy chains
  • Configuration settings

F. Developer Portal

  • Custom pages and templates
  • Documentation
  • CMS content

Conclusion

Deploying complex solutions like 3scale API Management in restricted network environments or across architecture boundaries presents unique challenges. By understanding the underlying issues and implementing a systematic approach to manually mirror images, we were able to overcome these obstacles.

While this process requires more manual effort than a standard deployment, it demonstrates the flexibility of OpenShift's container management capabilities and provides a path forward for deployments in environments with similar restrictions.

Top comments (0)