Proxy TCP Traffic by Port
Configure APISIX or API7 Ingress Controller to proxy TCP traffic by gateway port to a MySQL Service.
Applies to:API7 Ingress Controller (drives API7 Gateway)APISIX Ingress Controller (drives Apache APISIX)
Goal
Route TCP connections to a Kubernetes MySQL Service based on the incoming gateway port, using either a Gateway API TCPRoute or an APISIX CRD stream route.
Before you begin
- Complete Set Up Ingress Controller and Gateway.
- Install MySQL Shell so you can initiate TCP connections with the example MySQL server.
- You have
kubectlconfigured against the cluster used in the setup guide. - Choose a free local port for the port-forward used during validation. The examples use
9100for the gateway stream listener and9100for the forwarded local port; change these if they conflict.
Set the variables this guide uses:
export MYSQL_ROOT_PASSWORD="my-secret-pw"
export GATEWAY_SERVICE_NAME="apisix-gateway" # your gateway's Service name in namespace aic
export FORWARD_PORT="9100"Steps
Step 1: Deploy the example MySQL upstream
Create a manifest file for an example MySQL upstream Service in the aic namespace:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql
namespace: aic
labels:
app: mysql
spec:
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:9.4
env:
- name: MYSQL_ROOT_PASSWORD
value: "${MYSQL_ROOT_PASSWORD}"
ports:
- containerPort: 3306
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
volumes:
- name: mysql-data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: aic
spec:
selector:
app: mysql
ports:
- name: mysql
port: 3306
targetPort: 3306Apply the manifest:
kubectl apply -f mysql.yamlWait for the Pod to become ready:
kubectl wait --for=condition=ready pod -l app=mysql -n aic --timeout=120sStep 2: Enable the gateway stream proxy
Upgrade the gateway Helm release to enable stream mode and expose TCP port 9100.
helm upgrade -n aic apisix apisix/apisix \
--reuse-values \
--set "service.stream.enabled=true" \
--set "service.stream.tcp[0]=9100"helm upgrade -n aic api7-ee-3-gateway api7/gateway \
--reuse-values \
--set "gateway.stream.enabled=true" \
--set "gateway.stream.only=false" \
--set "gateway.stream.tcp[0]=9100"Step 3: Configure TCP routing
Create a route that listens for TCP traffic on port 9100.
Add a TCP listener to your existing Gateway manifest:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
namespace: aic
name: apisix
spec:
gatewayClassName: apisix
listeners:
- name: http
protocol: HTTP
port: 80
- name: tcp
protocol: TCP
port: 9100
allowedRoutes:
kinds:
- kind: TCPRoute
infrastructure:
parametersRef:
group: apisix.apache.org
kind: GatewayProxy
name: apisix-configCreate a TCPRoute manifest:
apiVersion: gateway.networking.k8s.io/v1
kind: TCPRoute
metadata:
name: stream-route-mysql
namespace: aic
spec:
parentRefs:
- name: apisix
sectionName: tcp
rules:
- backendRefs:
- name: mysql
port: 3306Apply both manifests:
kubectl apply -f gateway.yaml -f tcp-route.yamlWhen listener_port_match_mode is explicit or auto, the sectionName: tcp reference adds a server_port match for 9100. The Gateway listener port must equal the physical gateway stream listener port. With the mode set to off, the controller does not add this match.
Create a stream route manifest:
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: stream-route-mysql
namespace: aic
spec:
ingressClassName: apisix
stream:
- name: stream-route-mysql
protocol: TCP
match:
ingressPort: 9100
backend:
serviceName: mysql
servicePort: 3306Apply the manifest:
kubectl apply -f tcp-route.yamlStep 4: Set the upstream transport scheme
Use tcp for a plain TCP backend or tls when the gateway must establish TLS to the backend. The L4 schemes apply only to stream routes.
Attach a BackendTrafficPolicy to the backend Service:
apiVersion: apisix.apache.org/v1alpha1
kind: BackendTrafficPolicy
metadata:
name: mysql-transport
namespace: aic
spec:
targetRefs:
- group: ""
kind: Service
name: mysql
sectionName: mysql
scheme: tcpApply the policy:
kubectl apply -f mysql-upstream-policy.yamlCreate an ApisixUpstream with the same name as the backend Service:
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
name: mysql
namespace: aic
spec:
ingressClassName: apisix
scheme: tcpApply the upstream configuration:
kubectl apply -f mysql-upstream.yamlTo attach stream plugins to the TCPRoute, see Apply Plugins to L4 Routes.
Validate
Forward the gateway stream port to your local machine:
kubectl port-forward -n aic "svc/${GATEWAY_SERVICE_NAME}" "${FORWARD_PORT}:9100" &Connect with the MySQL server as root. Enter the password stored in MYSQL_ROOT_PASSWORD when prompted:
mysqlsh --sqlc --host=127.0.0.1 --port="${FORWARD_PORT}" --user=root --passwordExpected: MySQL Shell opens a classic protocol session and prints output similar to:
Creating a Classic session to 'root@127.0.0.1:9100'
Your MySQL connection id is 9
Server version: 9.4.0 MySQL Community Server - GPLRoll back / Clean up
Delete the manifests created in this guide:
kubectl delete -f tcp-route.yaml
kubectl delete -f gateway.yaml
kubectl delete -f mysql.yaml
kubectl delete -f mysql-upstream-policy.yaml
kubectl delete -f mysql-upstream.yamlStop the local port-forward process if it is still running:
kill %1 2>/dev/null || trueTroubleshoot
| Symptom | Cause | Fix |
|---|---|---|
mysqlsh cannot connect or times out. | The gateway stream listener is not enabled, the TCPRoute is not accepted, or the port-forward targets the wrong Service. | Confirm the Helm release has service.stream.enabled=true and service.stream.tcp[0]=9100 (or the API7 Gateway equivalents), the TCPRoute parentRefs resolve, and GATEWAY_SERVICE_NAME matches your gateway Service. |
| MySQL Shell reports an authentication failure. | The root password in the MySQL Deployment does not match the password you entered. | Verify MYSQL_ROOT_PASSWORD is the value you set in mysql.yaml. |
| The controller ignores the TCPRoute. | The Gateway listener name or port does not match the TCPRoute parentRefs. | Check that the Gateway listener is named tcp and exposes port 9100, and that sectionName: tcp is present in the TCPRoute. |