Featured image of post Tencent Cloud API Gateway Sunset? Open-Source Cluster Replacement

Tencent Cloud API Gateway Sunset? Open-Source Cluster Replacement

Tencent Cloud, API Gateway, k8s, k3s, ingress

Background: API Gateway Is Going Away

Heard Tencent Cloud’s API Gateway is being sunset. Some of our projects were (unfortunately) built on it. What to replace it with now?

Important: API Gateway product end-of-sale announcement

Back when I was still using Tencent Cloud’s managed k8s masters for free, after one upgrade they switched our Services to API Gateway behind the scenes and started billing by usage.

So I began looking into how to remove and replace it — and today that work finally pays off.

Option 1: Tencent Cloud k8s + NodePort

  • Keep the same cluster; join your own machines as worker nodes.
  • Remove all Services bound to internal IPs; expose everything via NodePort.
  • Install NGINX on worker nodes and reverse-proxy traffic to NodePort.

This ran for about a year. Then the managed cluster itself started charging a “management fee”. A few dozen RMB per month, hundreds per year — not acceptable for us. Time to migrate again.

Option 2: SuperEdge self-hosted cluster + NodePort + NGINX

For a self-hosted cluster, use whatever is convenient.

I wrote these back then:

I was basically an organic promoter for the project.

However… last year the old SuperEdge WeChat group was migrated; the new group seemed unmaintained. People I knew said the project moved into maintenance mode.

The real problem: the certs expired and wouldn’t renew; my cluster broke.

Honestly, it’s tiring — many big-company open-source projects in CN end up tied to KPIs. Hard to get in, harder to keep running, and easy to get dropped.

So I started looking again, with these goals:

  • Still all‑in on k8s
  • Still self‑hosted
  • Still zero cost

Then I found k3s — it’s great.

Final Approach: k3s + ingress-nginx

Deployment guide: https://zhuanlan.zhihu.com/p/696737105 (Chinese)

Example node list:

1
2
3
4
5
6
7
NAME              STATUS                     ROLES                       AGE     VERSION
aliyun-bj-199     Ready                      <none>                      34d     v1.29.4+k3s1
haru              Ready                      <none>                      6d16h   v1.29.4+k3s1
t7610             Ready                      <none>                      6d2h    v1.29.4+k3s1
vm-16-12-ubuntu   Ready                      control-plane,etcd,master   75d     v1.28.7+k3s1
vm-28-17-ubuntu   Ready,SchedulingDisabled   control-plane,etcd,master   75d     v1.28.7+k3s1
zj-hc1            Ready                      <none>                      33d     v1.29.4+k3s1

As you can see, there are two masters (vm-16-12-ubuntu and vm-28-17-ubuntu). They’re on cloud VMs, mutually reachable on the private network, and both have public IPs (important).

Other nodes are a mix of local, other clouds, and edge devices. They can talk over private network, but not all have public IPs.

Previously, we needed an API Gateway to forward traffic to worker nodes. That’s what Tencent Cloud’s API Gateway did, but we don’t have it anymore. Instead, we can use ingress-nginx.

In a k3s cluster, any node with a public IP can serve as an entry point.

We can configure ingress-nginx to forward traffic to services living on worker nodes.

All you need is to pass --node-external-ip when starting the server/agent to set the public IP.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# server
ExecStart=/usr/local/bin/k3s \
    server \
	--node-external-ip PUBLIC_IP \
    --flannel-backend wireguard-native \
	'--tls-san' \
	'PUBLIC_IP' \
	'--node-name' \
	'vm-16-12-ubuntu' \
	'--server' \
	'https://10.0.28.17:6443' \

# agent
ExecStart=/usr/local/bin/k3s \
    agent --node-name zj-hc1 \
    --lb-server-port 5443 \
    --node-ip 10.42.4.1  \
    -node-external-ip PUBLIC_IP \
    --server https://server:6443 \
    --token="YOUR_TOKEN" \

Now ports 80/443 on that node can accept external traffic (make sure your firewall allows it). Point your domain’s DNS A record(s) to that node’s public IP, and you’re good to go.

A Minimal Working Example

  • Self-signed or ACME certs supported
  • Any node with a public IP can act as an entry point
  • For LB, add multiple A records or use DNS load balancing
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
apiVersion: v1
kind: PersistentVolume
metadata:
  name: stirling-pdf
spec:
  capacity:
    storage: 30Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /mnt/cfs-a4nopkhh/stirling-pdf
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: kubernetes.io/hostname
              operator: In
              values:
                - zj-hc1
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: stirling-pdf
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 30Gi
  storageClassName: local-storage
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: stirling-pdf
  labels:
    app: stirling-pdf
spec:
  replicas: 1
  selector:
    matchLabels:
      app: stirling-pdf
  template:
    metadata:
      labels:
        app: stirling-pdf
    spec:
      containers:
        - name: stirling-pdf
          image: ccr.ccs.tencentyun.com/liguobao/s-pdf:ustc
          ports:
            - containerPort: 8080
          resources:
            requests:
              memory: "512Mi"
              cpu: "500m"
            limits:
              memory: "2048Mi"
              cpu: "1000m"
          readinessProbe:
            httpGet:
              path: /
              port: 8080
            initialDelaySeconds: 120
            periodSeconds: 60
          env:
            - name: DOCKER_ENABLE_SECURITY
              value: "false"
            - name: INSTALL_BOOK_AND_ADVANCED_HTML_OPS
              value: "false"
            - name: UI_APP_NAME
              value: "R2049 PDF"
            - name: ALLOW_GOOGLE_VISIBILITY
              value: "true"
            - name: LANGS
              value: "zh_CN"
            - name: APP_LOCALE
              value: "zh_CN"
          volumeMounts:
            - mountPath: /configs
              subPath: configs
              name: data
            - mountPath: /usr/share/tessdata
              subPath: tessdata
              name: data
            - mountPath: /customFiles/
              subPath: customFiles
              name: data
      volumes:
        - name: data
          persistentVolumeClaim:
            claimName: stirling-pdf
      imagePullSecrets:
        - name: regcred
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
  name: stirling-pdf-service
spec:
  selector:
    app: stirling-pdf
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080
  type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    kubernetes.io/tls-acme: "true"
  generation: 1
  name: ingress-pdf-house2048
spec:
  ingressClassName: traefik
  rules:
    - host: pdf.house2048.cn
      http:
        paths:
          - backend:
              service:
                name: stirling-pdf-service
                port:
                  number: 8080
            path: /
            pathType: Prefix
  tls:
    - hosts:
        - pdf.house2048.cn
      secretName: pdf-house2048-cn-tls
Built with Hugo
Theme Stack designed by Jimmy