Skip to main content
Version: Dev

Deploy AISIX Enterprise on OpenShift or OKD

AISIX Enterprise includes a customer-operated control plane and the AISIX gateways that serve application traffic. A complete OpenShift or OKD deployment therefore brings up the control plane first, exposes its operator and data-plane manager endpoints, and then deploys at least one connected gateway.

The AISIX application workloads run under the default restricted-v2 Security Context Constraint (SCC). The bundled PostgreSQL dependency pins its own user and group IDs, so its pod and container security contexts must be disabled before the first Helm installation. For production, use an external highly available PostgreSQL database instead of the bundled single-instance database.

Prerequisites​

  • An existing OpenShift or OKD 4.11 or later cluster.
  • An oc session that can create resources in the target projects.
  • Helm 3, OpenSSL, cURL, and jq.
  • The compute, storage, database, and network capacity described in Deploy the AISIX Enterprise Control Plane.

This procedure uses aisix for the control-plane project and aisix-dp for the gateway project. Keeping them separate gives each project its own UID range and access policy. If you use different names, substitute them consistently in every command.

Prepare the Projects and Route Hosts​

Select or create the control-plane and gateway projects:

oc get project aisix >/dev/null 2>&1 || oc new-project aisix
oc get project aisix-dp >/dev/null 2>&1 || oc new-project aisix-dp

Choose the public host names before installing the control plane. The example uses the cluster's wildcard applications domain:

export ROUTE_DOMAIN="$(oc get ingresses.config.openshift.io cluster \
-o jsonpath='{.spec.domain}')"

export CP_ROUTE_HOST="aisix.$ROUTE_DOMAIN"
export DPM_ROUTE_HOST="aisix-dpm.$ROUTE_DOMAIN"
export GATEWAY_ROUTE_HOST="aisix-gateway.$ROUTE_DOMAIN"

If you use organization-managed DNS, set these variables to those host names and ensure they resolve to the router. For edge Routes, provide matching certificates and keys or configure the Ingress Controller's default certificate. The data-plane manager host name is seeded into its TLS certificate from api.dpmgrBaseURL.

Trust the Route Certificate​

The default applications domain normally uses a wildcard certificate signed by the cluster's internal ingress CA. When that CA is not already trusted on the workstation, export it before using the dashboard or curl:

oc get configmap default-ingress-cert -n openshift-config-managed \
-o jsonpath='{.data.ca-bundle\.crt}' > ingress-ca.crt
export CURL_CA_BUNDLE="$PWD/ingress-ca.crt"

Import ingress-ca.crt into the browser or operating-system trust store before signing in. Skip these steps when the Routes or default Ingress Controller use a certificate that the workstation already trusts.

Deploy the Control Plane​

Review the production requirements in Deploy the AISIX Enterprise Control Plane, but do not run its generic Kubernetes Helm command. Save the required PostgreSQL SCC overrides as values.yaml:

values.yaml
postgresql:
primary:
podSecurityContext:
enabled: false
containerSecurityContext:
enabled: false

Install the control plane with the public Route URLs and generated secrets:

helm repo add api7 https://charts.api7.ai
helm repo update

helm install aisix-cp api7/aisix-cp \
--namespace aisix \
--values values.yaml \
--set-string api.publicBaseURL="https://$CP_ROUTE_HOST" \
--set-string api.dpmgrBaseURL="https://$DPM_ROUTE_HOST" \
--set-string secrets.masterKey="$(openssl rand -base64 32)" \
--set-string postgresql.auth.password="$(openssl rand -hex 24)" \
--set-string postgresql.auth.postgresPassword="$(openssl rand -hex 24)"

Do not disable postgresql.shmVolume. Without it, PostgreSQL falls back to the 64 MB /dev/shm provided by the container runtime, and parallel queries can fail with could not resize shared memory segment.

The control-plane API and data-plane manager do not pin user or group IDs, so restricted-v2 can assign them from the project range. Their containers use read-only root filesystems, disable privilege escalation, and drop all capabilities. The gateway follows the same policy but retains NET_BIND_SERVICE. Runtime-writable application paths use emptyDir volumes.

Do not grant AISIX service accounts the anyuid SCC. If the cluster administrator allocates fixed IDs, set the runAsUser, runAsGroup, and fsGroup keys under both api.podSecurityContext and dpm.podSecurityContext. Do not reuse the image UID and group IDs 10001 and 101 unless they belong to the project's allowed ranges.

If an organization policy applies an SCC stricter than restricted-v2, ask the cluster administrator to review the rendered manifests and allocate any required IDs or capabilities explicitly.

Expose and Verify the Control Plane​

Expose the control-plane API and dashboard with an edge Route:

oc create route edge aisix-cp \
--service=aisix-cp-api \
--port=http \
--hostname="$CP_ROUTE_HOST" \
--namespace aisix

The data-plane manager uses mutual TLS with connected gateways. Expose it with TLS passthrough so the router does not terminate the connection:

oc create route passthrough aisix-cp-dpm \
--service=aisix-cp-dpm \
--port=tls \
--hostname="$DPM_ROUTE_HOST" \
--namespace aisix

These commands use the default aisix-cp-api/http and aisix-cp-dpm/tls Service targets. If the release uses name overrides, run oc get service -n aisix and substitute the actual Service names and named ports.

Wait for the workloads and inspect their SCC and assigned UID:

oc get pods -n aisix --watch

# Stop the watch after every pod is ready, then inspect the admitted security context.
oc get pods -n aisix \
-o custom-columns='NAME:.metadata.name,SCC:.metadata.annotations.openshift\.io/scc,READY:.status.containerStatuses[*].ready'

for pod in $(oc get pods -n aisix -o name); do
printf '%s UID=' "$pod"
oc exec -n aisix "$pod" -- id -u
done

The AISIX and bundled PostgreSQL pods should report restricted-v2 and ready containers. Each running process should use a UID from the aisix project range.

Confirm that the data-plane manager certificate contains its passthrough Route hostname:

openssl s_client \
-connect "$DPM_ROUTE_HOST:443" \
-servername "$DPM_ROUTE_HOST" \
</dev/null 2>/dev/null |
openssl x509 -noout -ext subjectAltName

Sign In and Configure Gateway Resources​

Open https://$CP_ROUTE_HOST. The Helm installation notes print the command for reading the bootstrap administrator password. Sign in with the configured bootstrap email and that password, then replace it when prompted.

Create an organization-scoped admin token in Admin tokens with the write scope, then export the control-plane API URL and token:

export AISIX_CP="https://$CP_ROUTE_HOST/api"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"

Create an environment, an OpenAI provider key, the gpt-4o-mini model, and a caller API key. Save the caller key's plaintext value because it is returned only once:

ENV_RESPONSE=$(curl -fsS -X POST "$AISIX_CP/environments" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"display_name": "prod"}')
export ENV_ID=$(echo "$ENV_RESPONSE" | jq -er '.environment.id')

export OPENAI_API_KEY="YOUR_OPENAI_API_KEY"
PROVIDER_KEY_RESPONSE=$(curl -fsS -X POST "$AISIX_CP/provider_keys" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"provider": "openai",
"display_name": "OpenAI",
"api_key": "'"${OPENAI_API_KEY}"'",
"api_base": "https://api.openai.com/v1",
"allowed_environments": ["'"${ENV_ID}"'"]
}')
export PROVIDER_KEY_ID=$(echo "$PROVIDER_KEY_RESPONSE" | jq -er '.provider_key.id')

MODEL_RESPONSE=$(curl -fsS -X POST "$AISIX_CP/environments/$ENV_ID/models" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"display_name": "gpt-4o-mini",
"model_name": "gpt-4o-mini",
"provider_key_id": "'"${PROVIDER_KEY_ID}"'"
}')
export MODEL_ID=$(echo "$MODEL_RESPONSE" | jq -er '.model.id')

API_KEY_RESPONSE=$(curl -fsS -X POST "$AISIX_CP/environments/$ENV_ID/api_keys" \
-H "Authorization: Bearer ${AISIX_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"display_name": "openshift-caller",
"allowed_models": ["'"${MODEL_ID}"'"]
}')
export AISIX_API_KEY=$(echo "$API_KEY_RESPONSE" | jq -er '.plaintext')

Stop and correct any failed request before continuing. A missing resource ID makes the later requests fail.

Issue a Gateway Certificate​

Open the prod environment's Data planes view, issue a gateway certificate, and download cert.pem, key.pem, and ca.pem. The private key is shown only once.

Deploy the Gateway​

Store the certificate bundle in the gateway project:

oc -n aisix-dp create secret generic aisix-gateway-certificate \
--from-file=cert.pem=./cert.pem \
--from-file=key.pem=./key.pem \
--from-file=ca.pem=./ca.pem

Install one gateway replica against the data-plane manager Route. Confirm the endpoint in the environment's Kubernetes (Helm) instructions and substitute it if necessary:

helm install aisix-dp api7/aisix --namespace aisix-dp \
--set image.repository=ghcr.io/api7/aisix \
--set image.tag=dev \
--set controlPlane.baseURL="https://$DPM_ROUTE_HOST:443" \
--set controlPlane.certificate.existingSecret=aisix-gateway-certificate \
--set replicaCount=1

The gateway chart supports restricted-v2 without a custom SCC or service-account change. If the gateway needs fixed IDs allocated to its project, set podSecurityContext.runAsUser, podSecurityContext.runAsGroup, and podSecurityContext.fsGroup to those values.

Configure shared Redis before increasing the replica count when limits or cache entries must remain consistent across replicas.

Expose and Verify the Gateway​

Create an edge Route to the default plain-HTTP proxy Service port:

oc create route edge aisix-dp \
--service=aisix-dp \
--port=proxy \
--hostname="$GATEWAY_ROUTE_HOST" \
--namespace aisix-dp

This command uses the default gateway release and proxy Service port. If the release uses a name override or custom listeners, run oc get service -n aisix-dp and substitute the actual Service name and named port.

Wait for the gateway, then verify its SCC and UID:

oc rollout status deployment/aisix-dp -n aisix-dp --timeout=5m

export GATEWAY_POD="$(oc get pod -n aisix-dp \
-l 'app.kubernetes.io/instance=aisix-dp,app.kubernetes.io/component=gateway' \
-o jsonpath='{.items[0].metadata.name}')"

oc get pod -n aisix-dp "$GATEWAY_POD" \
-o jsonpath='{.metadata.annotations.openshift\.io/scc}{"\n"}'
oc exec -n aisix-dp "$GATEWAY_POD" -- id -u

The first line should report restricted-v2, and the second should show the UID used by the running process. Confirm that the UID is in the aisix-dp project range and that the dashboard reports the gateway as healthy with a current heartbeat.

Finally, check the Route and send a representative authenticated inference request through it:

export AISIX_PROXY="https://$GATEWAY_ROUTE_HOST"
curl -fsS "$AISIX_PROXY/readyz"

curl -fsS "$AISIX_PROXY/v1/models" \
-H "Authorization: Bearer ${AISIX_API_KEY}"

curl -sS -X POST "$AISIX_PROXY/v1/chat/completions" \
-H "Authorization: Bearer ${AISIX_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o-mini",
"messages": [
{"role": "user", "content": "Say hello from AISIX Enterprise."}
]
}'

The readiness request should return ok, the model list should contain gpt-4o-mini, and the inference response should contain an assistant message. Verify the request in the dashboard logs and usage views before directing production traffic to the gateway.

Next Steps​