Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,19 @@ LOG_LEVEL := info

ISTIO_HOST := $(shell ifconfig -a | awk '/inet / {print $$2}' | grep -v '127.' | grep -v '192.' | head -n 1)

WITH_KIND ?= OFF
ifeq ($(WITH_KIND),ON)
NET_FLAGS := --network kind
endif

.PHONY: start-aigw-xds
start-aigw-xds:
cat etc/envoy-istio.yaml \
| sed "s/ISTIO_ENDPOINT/${ISTIO_HOST}/" \
> etc/envoy-xds.yaml
@echo "using ${MC_HOST} as Metadata Center Host"
docker run --entrypoint /bin/bash --name dev_aigw --rm -d \
$(NET_FLAGS) \
-e AIGW_META_DATA_CENTER_HOST=${MC_HOST} \
-e AIGW_META_DATA_CENTER_PORT=${MC_PORT} \
-v $(PWD)/etc/envoy-xds.yaml:/etc/envoy.yaml \
Expand Down Expand Up @@ -193,20 +199,33 @@ build-image:

LOG_LEVEL = info
PILOT_IMAGE = $(DOCKER_MIRROR)docker.io/istio/pilot:1.27.3
PILOT_CMD = pilot-discovery discovery \
--log_output_level $(LOG_LEVEL) \
--meshConfig /etc/istio.yaml \
--configDir /etc/config_crds \
--httpsAddr= --registries=
PILOT_BASE := pilot-discovery discovery \
--log_output_level $(LOG_LEVEL) \
--meshConfig /etc/istio.yaml \
--configDir /etc/config_crds \
--httpsAddr=

ifeq ($(WITH_KIND),ON)
PILOT_REGISTRY := --registries Kubernetes --kubeconfig /etc/kubeconfig.yaml
MAPPED_KIND_CONFIG := -v "$(PWD)/etc/kind-kubeconfig.yaml:/etc/kubeconfig.yaml"
else
PILOT_REGISTRY := --registries=
endif

PILOT_CMD := $(PILOT_BASE) $(PILOT_REGISTRY)

.PHONY: start-istio
start-istio:
docker run --name dev_istio \
--entrypoint bash --rm -it -d \
$(NET_FLAGS) \
--privileged=true \
--user root \
-e INJECT_ENABLED=false \
-e ENABLE_CA_SERVER=false \
-v $(PWD)/etc/istio.yaml:/etc/istio.yaml \
-v $(PWD)/etc/config_crds:/etc/config_crds \
$(MAPPED_KIND_CONFIG) \
-p 15010:15010 \
-p 8080:8080\
${PILOT_IMAGE} \
Expand All @@ -215,3 +234,10 @@ start-istio:
.PHONY: stop-istio
stop-istio:
docker stop dev_istio

.PHONY: start-mock-service
start-mock-service:
cd $(PWD)/test/service_discovery/
docker build -t mock-service:1.0 .
kind load docker-image mock-service:1.0 --name istio-test
kubectl apply -f mock_service_config.yaml
65 changes: 47 additions & 18 deletions docs/en/developer-guide.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,110 @@
## Developer Guide
# Developer Guide

## Two Ways to Start AIGW
## 1. Two Ways to Start AIGW

1. Local Independent Way: Using static configuration for service discovery, easy for local development.
2. Integrated with Istio: Using Istio as the control plane, leveraging Istio's service discovery capabilities, suitable for production environments.

## Environment Preparation
## 2. Environment Preparation

1. docker
2. golang 1.22+

## Start Metadata Center
## 3. Start Metadata Center

Both methods require starting the Metadata Center service, as
AIGW leverage the Metadata Center component to implement near real-time load metric collection.
Both methods require starting the Metadata Center service, as AIGW leverage the Metadata Center component to implement near real-time load metric collection.
Please refer to the [Metadata Center documentation](https://github.com/aigw-project/metadata-center/blob/main/docs/en/developer_guide.md ) to start the local Metadata Center service.
The Metadata Center defaults to listening on the local IP and port `8080`.

## Compilation
## 4. Compilation

Compile AIGW into a shared object:

```shell
make build-so
```

## Local Independent Way
## 5. Local Independent Way

For the convenience of local development, AIGW supports static configuration for service discovery,
i.e., specifying the address and port of service instances through configuration files.

### Static Configuration for Service Discovery
### 5.1 Static Configuration for Service Discovery

You can refer to the example at: [etc/clusters.json](../../etc/clusters.json), which defines `127.0.0.1:10001` as the instance of the `qwen3.service`.

## Start Service
#### Start Service

Start AIGW using [etc/envoy-local.yaml](../../etc/envoy-local.yaml) as the Envoy configuration file and [etc/clusters.json](../../etc/clusters.json) as the static service discovery configuration file:

```shell
make start-aigw-local
```

## Integrated with Istio Way
### 5.2 Integrated with Istio

Comming soon.

Integrating with Istio as the control plane, using Istio's service discovery capabilities, can automatically synchronize service instance information with the k8s cluster, suitable for production environments.

### Start Istio
#### Start Istio

For easy debugging, we start a local Istio control plane that watch the CRD files in the `etc/config_crds` directory.

```shell
make start-istio
```

### Service Discovery
#### Service Discovery

We use the ServiceEntry resource to define service instances, as shown in the [etc/config_crds/service-entry.yaml](etc/config_crds/service-entry.yaml) file.

### Start Service
#### Start Service
Both methods can start AIGW integrated with Istio:

```shell
make start-aigw-istio
make start-aigw-xds
```

## After Starting
### 5.3 Integration with Istio & Kubernetes

#### Prepare the Kubernetes cluster
Follow the [Kubernetes + Istio Setup Guidance](../../docs/zh/service_discovery_guide/setup_env_zh.md) to deploy a Kubernetes cluster with Kind and start Istio.

#### Start Istio & subscribe to the Kubernetes Service API
Export the Kind cluster kubeconfig to the `./etc` directory so Istio can subscribe to it:

```bash
kind get kubeconfig --name istio-test > ./etc/kind-kubeconfig.yaml
```

Start Istio:
```bash
make WITH_KIND=ON start-istio
```

#### Start the Mock Service
Launch the Mock Service as an upstream component that will be discovered by AIGW:
```bash
make start-mock-service
```

#### Start the AIGW Service
Start AIGW, which brings up a custom xDS server, subscribes CDS/EDS from Istio Pilot, and starts a gRPC server for Envoy to fetch configuration.
Data flow: Istio Pilot => AIGW custom xDS server => Envoy.
```bash
make WITH_KIND=ON start-aigw-xds
```

## 6. After Starting

Both two ways will start two services:
1. Port `10000`: AIGW service
2. Port `10001`: Mock inference service

It will also use the locally started Metadata Center for load metric collection by default, that listening on the local IP and port `8080`.

## Testing
## 7. Testing

Send a request using curl:

Expand All @@ -95,7 +124,7 @@ curl 'localhost:10000/v1/chat/completions' \
}'
```

## Stop Service
## 8. Stop Service

```shell
make stop-aigw
Expand Down
59 changes: 44 additions & 15 deletions docs/zh/developer-guide.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,110 @@
## 开发者指南
# 开发者指南

## 运行方式
## 1. 运行方式

AIGW 支持多种运行方式:
1. 本地独立运行,使用静态配置文件的方式进行服务发现,适合本地开发和调试
2. 集成 Istio 作为控制面,使用 Istio 提供的服务发现能力,适合生产环境

## 环境准备
## 2. 环境准备

1. docker
2. golang 1.22+

## 启动 Metadata Center
## 3. 启动 Metadata Center

两种方式都需要启动 Metadata Center 服务,因为 AIGW 依赖 Metadata Center 组件来实现准实时的负载指标统计,
请参考 [Metadata Center 文档](https://github.com/aigw-project/metadata-center/blob/main/docs/zh/developer_guide.md) 启动本地的 Metadata Center 服务。
Metadata Center 默认监听本地 IP 和 `8080` 端口。

## 编译
## 4. 编译

将 AIGW 编译为 shared object:

```shell
make build-so
```

## 本地独立运行方式
## 5. 本地独立运行方式

为了本地开发方便,AIGW 支持本地独立运行方式,使用静态配置的方式进行服务发现,也即通过配置文件指定服务实例的地址和端口。

### 静态配置服务发现
### 5.1 静态配置服务发现

示例可以查看:[etc/clusters.json](../../etc/clusters.json),该文件定义了 `127.0.0.1:10001` 作为 `qwen3.service` 这个服务的实例。

### 启动服务
#### 启动服务

将使用 [etc/envoy-local.yaml](../../etc/envoy-local.yaml) 作为 Envoy 的配置文件,并使用 [etc/clusters.json](../../etc/clusters.json) 作为静态服务发现的配置文件启动 AIGW:

```shell
make start-aigw-local
```

## 集成 Istio 运行方式
### 5.2 集成 Istio 运行方式

Comming soon.

集成 Istio 作为控制面,使用 Istio 的服务发现能力,可以与 k8s 集群自动同步服务实例信息,适合生产环境。

### 启动 Istio
#### 启动 Istio

为了方便调试,我们启动一个本地的 Istio 控制面,并且监听 `etc/config_crds` 目录下的 CRD 文件。

```shell
make start-istio
```

### 服务发现
#### 服务发现

我们使用 ServiceEntry 资源来定义服务实例,如 [etc/config_crds/service-entry.yaml](etc/config_crds/service-entry.yaml) 文件所示。

### 启动服务
#### 启动服务

将使用 [etc/envoy-istio.yaml](../../etc/envoy-istio.yaml) 作为 Envoy 的配置文件,并订阅本地 Istio 作为控制面来启动 AIGW:

```shell
make start-aigw-xds
```

## 服务启动检查
### 5.3 集成 Istio & k8s 运行方式

#### 配置k8s集群环境
参考[k8s + Istio 环境搭建指南](../../docs/zh/service_discovery_guide/setup_env_zh.md)使用Kind部署k8s集群并启动Istio;

#### 启动 Istio & 订阅 k8s Service API
导出Kind集群配置到./etc目录,供Istio订阅:
```bash
kind get kubeconfig --name istio-test > ./etc/kind-kubeconfig.yaml
```

启动Istio:
```bash
make WITH_KIND=ON start-istio
```

#### 启动 Mock Service
启动Mock Service,作为upstream服务组件被AIGW发现:
```bash
make start-mock-service
```

#### 启动服务
启动AIGW,拉起自定义xDS服务器,从Istio Pilot订阅CDS/EDS信息,并启动gRPC Server供Envoy拉取。
服务信息传递流程:Istio Pilot => AIGW 自定义xDS服务器 => Envoy。
```bash
make WITH_KIND=ON start-aigw-xds
```

## 6. 服务启动检查

两种启动方式,都将监听两个端口:
1. `10000` 端口:AIGW 服务
2. `10001` 端口:mock 推理服务

并将默认使用本地启动的 Metadata Center 进行负载指标统计,默认使用本地 IP 和 `8080` 端口。

## 测试
## 7. 测试

使用 curl 发送请求:

Expand All @@ -95,7 +124,7 @@ curl 'localhost:10000/v1/chat/completions' \
}'
```

## 停止服务
## 8. 停止服务

```shell
make stop-aigw
Expand Down
2 changes: 1 addition & 1 deletion etc/envoy-istio.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
node:
id: router~127.0.0.1~TODO_ID~TO_DOMAIN
id: router~127.0.0.1~aigw.default~cluster.local
cluster: cluster

admin:
Expand Down
26 changes: 26 additions & 0 deletions pkg/aigateway/discovery/staticdemo/cds_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

managertypes "github.com/aigw-project/aigw/pkg/aigateway/clustermanager/types"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not a good idea to reuse the static discovery, introduce a new implementation should be better.
For the code reusing we may introduce a common xds server to avoding copying code, it could be another PR as the second step.

"github.com/aigw-project/aigw/pkg/aigateway/discovery/common"
)

Expand Down Expand Up @@ -100,6 +101,28 @@ func (s *cdsServerImpl) processAllClusters() {
s.responseChan <- resp
}

func (s *cdsServerImpl) processSubscribedClusters(subscribeClusters []string) {
Copy link

@amazingapple amazingapple Nov 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add some INFO logs in the key steps to facilitate issue troubleshooting.

clusters := []*managertypes.ClusterInfo{}
for _, cname := range subscribeClusters {
c, err := s.provider.GetClusterInfo(cname)
if c == nil || err != nil {
api.LogErrorf("could not find cluster %s, err %v", cname, err)
continue
}
clusters = append(clusters, c)
}
nonce := common.GenerateNonce()
resources := make([]*discovery.Resource, 0, len(clusters))
for _, c := range clusters {
clustercfg := common.GenerateCluster(c.Name, c.Endpoints, false)
res := common.ConvertClusterToResource(clustercfg, c.Name)
resources = append(resources, res)
}

resp := common.GenerateDeltaDiscoveryResponseWithRemovedResources(resource.ClusterType, nonce, resources, nil)
s.responseChan <- resp
}

func (s *cdsServerImpl) DeltaClusters(stream cluster.ClusterDiscoveryService_DeltaClustersServer) error {
api.LogInfof("new delta clusters stream: %v", stream)

Expand Down Expand Up @@ -141,9 +164,12 @@ func (s *cdsServerImpl) DeltaClusters(stream cluster.ClusterDiscoveryService_Del
}

// TODO: delta watching cluster
subscribedClusters := []string{}
for _, r := range req.ResourceNamesSubscribe {
subscribedClusters = append(subscribedClusters, r)
api.LogInfof("delta watching cluster: %s", r)
}
s.processSubscribedClusters(subscribedClusters)
}
}
}
Loading