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

使用 copilot.microsoft.com 生成,生成了 10 多次效果越来越差

为了方便同事接入 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 部署也比较简单

Chapters

Sponsor

Like this article? $1 reward

Comments