轻松搭出属于你的 AI 网关:5 分钟搞定 NewAPI 上云

为了方便同事接入 AI,也方便同事可以自主申请 AI 账号
New API 作为一款「新一代大模型网关与 AI 资产管理平台」,提供丰富的模型接入、接口管理、计费统计等功能,非常适合在 Kubernetes 集群中进行部署。本文带你一步步完成在 K8s 环境中的部署。
环境
- 阿里云 k8s
- 已经有分布式存储作为存储类(nas-sc)
- Caddy
- LDAP
- openkruise
内部服务使用 Caddy 作为 Ingress 入口网关轻轻松松,顺便解决证书问题;使用 openkruise cloneset 管理,对标 Deployment
k8s 部署
官方文档还是比较清晰的,不需要额外拓展。默认部署在 newapi 命名空间下
吐槽一下 Bitnami,博通收啥黄啥。
Redis 部署
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: redis
name: redis
namespace: newapi
spec:
storageClassName: nas-sc
accessModes:
- ReadWriteMany
resources:
requests:
storage: 8Gi
---
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
labels:
app: redis
name: redis
namespace: newapi
spec:
replicas: 1
selector:
matchLabels:
app: redis
updateStrategy:
type: InPlaceIfPossible
template:
metadata:
labels:
app: redis
spec:
initContainers:
- name: volume-permissions
image: ccr.ccs.tencentyun.com/k7scn/os-shell:bitnami-latest
imagePullPolicy: "IfNotPresent"
command:
- /bin/bash
- -ec
- |
chown -R 1001:1001 /data
securityContext:
runAsUser: 0
seLinuxOptions: {}
resources:
limits:
cpu: 150m
ephemeral-storage: 2Gi
memory: 192Mi
requests:
cpu: 100m
ephemeral-storage: 50Mi
memory: 128Mi
volumeMounts:
- name: redis
mountPath: /data
containers:
- image: ccr.ccs.tencentyun.com/k7scn/redis:bitnami-8.2
imagePullPolicy: IfNotPresent
name: redis
env:
- name: REDIS_PASSWORD
value: da06b1fa13bc643ede15newapi8anewapiacfed04ac12e8bf5e6b78406d9
- name: REDIS_DISABLE_COMMANDS
value: FLUSHALL
ports:
- containerPort: 6379
protocol: TCP
resources:
requests:
cpu: 500m
memory: 512Mi
volumeMounts:
- mountPath: /bitnami/redis/data
name: redis
restartPolicy: Always
volumes:
- name: redis
persistentVolumeClaim:
claimName: redis
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: newapi
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
name: redis
部署 mysql
本来打算用 bitnami 的 mysql 折腾了一下初始化有点问题换成官方的了
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: mysql
name: mysql
namespace: newapi
spec:
storageClassName: nas-sc
accessModes:
- ReadWriteMany
resources:
requests:
storage: 8Gi
---
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
labels:
app: mysql
name: mysql
namespace: newapi
spec:
replicas: 1
selector:
matchLabels:
app: mysql
updateStrategy:
type: InPlaceIfPossible
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: ccr.ccs.tencentyun.com/k7scn/mysql:hub-8.4.7
imagePullPolicy: IfNotPresent
name: mysql
env:
- name: MYSQL_USER
value: newapi
- name: MYSQL_PASSWORD
value: da06b1fa13bc6syzede15f3860b8newapiacfed04ac12e8bf5e6b78406d9
- name: MYSQL_ROOT_PASSWORD
value: oaPei9xoh7shipimoom0zysAhnewapi7iDair7EVie1Va0ahth8eu3row
- name: MYSQL_DATABASE
value: newapi
ports:
- containerPort: 3306
protocol: TCP
resources:
requests:
cpu: 500m
memory: 512Mi
volumeMounts:
- mountPath: /var/lib/mysql
name: mysql
restartPolicy: Always
volumes:
- name: mysql
persistentVolumeClaim:
claimName: mysql
---
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: newapi
spec:
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
name: mysql
部署 newapi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: newapi
name: newapi
namespace: newapi
spec:
storageClassName: nas-sc
accessModes:
- ReadWriteMany
resources:
requests:
storage: 8Gi
---
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
labels:
app: newapi
name: newapi
namespace: newapi
spec:
replicas: 1
selector:
matchLabels:
app: newapi
updateStrategy:
type: InPlaceIfPossible
template:
metadata:
labels:
app: newapi
spec:
containers:
- image: ccr.ccs.tencentyun.com/k7scn/new-api:v0.9.15-patch.2
imagePullPolicy: IfNotPresent
name: newapi
env:
- name: SQL_DSN
value: newapi:da06b1fa13bc6syzede15f3860b8newapiacfed04ac12e8bf5e6b78406d9@tcp(mysql.newapi.svc:3306)/newapi?charset=utf8mb4&parseTime=True&loc=Local
- name: REDIS_CONN_STRING
value: redis://default:da06b1fa13bc643ede15newapi8anewapiacfed04ac12e8bf5e6b78406d9@redis.newapi.svc:6379
- name: TZ
value: Asia/Shanghai
- name: ERROR_LOG_ENABLED
value: "true"
- name: BATCH_UPDATE_ENABLED
value: "true"
- name: SESSION_SECRET
value: newapi-zysHuuch3joF9doe8dooviequuth4ohf6
- name: CRYPTO_SECRET
value: newapi-xeiRic0beuPaeQu7eanech3nai6iasyz
- name: FRONTEND_BASE_URL
value: https://newapi.ysicing.cloud
ports:
- containerPort: 3000
protocol: TCP
resources:
requests:
cpu: 500m
memory: 512Mi
volumeMounts:
- mountPath: /data
subPath: data
name: newapi
- mountPath: /app/logs
subPath: logs
name: newapi
restartPolicy: Always
volumes:
- name: newapi
persistentVolumeClaim:
claimName: newapi
---
apiVersion: v1
kind: Service
metadata:
name: newapi
namespace: newapi
spec:
selector:
app: newapi
ports:
- port: 3000
targetPort: 3000
name: newapi
部署 Dex
由于官方不支持 LDAP,暂时只能折中使用 OIDC 方式支持,username 是 sAMAccountName 还是 uid 根据 ladp 实际情况配置,正常只需要调整我打码的部分,Dex 还是很灵活的,官方文档也很齐全
---
kind: ConfigMap
apiVersion: v1
metadata:
name: dex
namespace: newapi
data:
dex.yaml: |
issuer: https://newapi.ysicing.cloud/dex
storage:
type: memory
web:
http: 0.0.0.0:5556
staticClients:
- id: newapi
redirectURIs:
- "https://newapi.ysicing.cloud/oidc/callback"
name: "newapi"
secret: newapi-secret
connectors:
- type: ldap
id: ldap
name: "LDAP"
config:
host: "ldap.ysicing.cloud:389"
insecureNoSSL: true
startTLS: false
bindDN: "****隐藏****"
bindPW: "****隐藏****"
userSearch:
baseDN: "****隐藏****"
filter: "(objectClass=person)"
username: "sAMAccountName"
idAttr: "sAMAccountName"
emailAttr: "mail"
nameAttr: "cn"
---
# apiVersion: apps/v1
# kind: Deployment
apiVersion: apps.kruise.io/v1alpha1
kind: CloneSet
metadata:
name: dex
namespace: newapi
labels:
app: dex
spec:
selector:
matchLabels:
app: dex
replicas: 1
updateStrategy:
type: InPlaceIfPossible
inPlaceUpdateStrategy:
gracePeriodSeconds: 10
template:
metadata:
labels:
app: dex
spec:
containers:
- name: dex
image: ccr.ccs.tencentyun.com/k7scn/dex
args:
- dex
- serve
- /etc/dex/dex.yaml
resources:
requests:
cpu: 100m
memory: 100Mi
livenessProbe:
tcpSocket:
port: 5556
initialDelaySeconds: 5
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 3
periodSeconds: 10
ports:
- containerPort: 5556
name: dex
volumeMounts:
- name: dex
mountPath: /etc/dex
volumes:
- name: dex
configMap:
name: dex
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: dex
namespace: newapi
spec:
selector:
app: dex
type: ClusterIP
ports:
- name: dex
protocol: TCP
port: 5556
targetPort: 5556
配置 Caddy
由于是复用之前的 Caddy,这里只列出 newapi.caddy 配置
newapi.ysicing.cloud {
@dex {
path /dex*
}
import LOG /var/log/caddy/newapi.ysicing.cloud.log
import COMCFG
import TLS
handle @dex {
reverse_proxy dex.newapi.svc.cluster.local:5556
}
reverse_proxy newapi.newapi.svc.cluster.local:3000
}
配置
第一次访问,走安装向导即可。
OIDC 配置
开启 OIDC 登录,需要允许新用户注册,不然登录会失败

OIDC 配置页,只需要配置 3 个
- Well-Known URL: https://newapi.ysicing.cloud/dex/.well-known/openid-configuration
- Client ID: newapi
- Client Secret: newapi-secret
配置 ClaudeCode
51
需要开启透传,还需要重写 UA, 请求头覆写
{
"User-Agent": "claude-cli/2.0.31 (external, cli)"
}
其他注意
改完要记得保存 😂, 如果你的环境没要求,其实 compose 部署也比较简单
