目录

GSoC Proposal: Support for Kubernetes Service Discovery(en)

Abstract

While using ShenYu, I found errors in the documentation of the registry component and submitted an Issue. Through communication with the community developers and reading the source code, I confirmed the issue and submitted a PR to fix it. This gave me great confidence in applying for this project.

This application mainly involves the architecture of the Apache ShenYu registry center, how to register microservices deployed in Kubernetes to ShenYu (overall solution design, code details), achievements, time plan, commitments, etc.

In GSoC 2023, I only submitted a proposal to ShenYu.

1 Background

1.1 Apache ShenYu

Apache ShenYu is a Java native API Gateway for service proxy, protocol conversion and API governance. Currently, ShenYu has good usability and performance in microservice scenarios.

However, ShenYu’s support for Kubernetes is still relatively weak.

1.2 Task Description

To make ShenYu a fully “Cloud-Native” gateway, it is not something that can be achieved overnight.

The topic of the current study is the first step towards the cloud-native transformation of ShenYu: Enabling microservices deployed in Kubernetes to be registered in ShenYu Admin and using Kubernetes as the registration center.

2 Analyze the service registration mechanism of Apache ShenYu

After studying the official documents “Client Registry Design” and “Register Center Source Code Analysis of Http Register” on the Apache ShenYu blog, I created a core flowchart of the ShenYu service registration mechanism. I removed caching components such as Disruptor because these details are not relevant to this topic.

ShenYu register center architecture
ShenYu register center architecture

As shown in the diagram, Microservice represents the microservice that needs to be integrated with ShenYu. ShenYu Admin is a ShenYu component used to handle service registration and plugin rule configuration, while ShenYu Gateway is the actual ShenYu gateway component that handles and forwards network requests.

The yellow lines represent the service registration process, while the green lines represent the flow path when a user initiates a network request.

Based on this diagram, let me introduce the service registration process and features of Apache ShenYu.

2.1 Service registration process

  1. After startup, the microservice sends its service information (metadata, URI, port) to the Register Center via the Register Client.
  2. ShenYu Admin monitors data changes in the Register Center in real-time, and performs data parsing when changes occur.
  3. ShenYu Admin synchronizes the microservice’s detailed information to ShenYu Gateway via the Data Sync module. Gateway then caches this data as a basis for subsequent processing/forwarding of network traffic.

Some noteworthy details:

  1. The Register Client does not need to be manually implemented by the microservice. ShenYu provides an SDK. For example, for gRPC protocol service registration, simply import the following dependencies and configure the connection information for the register center:

    <dependency>
        <groupId>org.apache.shenyu</groupId>
        <artifactId>shenyu-spring-boot-starter-plugin-grpc</artifactId>
        <version>${project.version}</version>
    </dependency>
    
  2. There is a Disruptor queue between the Register Client and Register Center, and between the Register Center and ShenYu Admin, which provides decoupling and buffering.

  3. The “Data Sync” process between ShenYu Admin and ShenYu Gateway actually uses another Register Center for implementation, with technical details hidden in the diagram.

2.2 Service Registration Features of Apache ShenYu

The biggest feature of service registration in Apache ShenYu is that the registration action is initiated by the microservice side.

This brings several characteristics:

  1. More microservice information can be carried during registration. Compared with Kubernetes’ native service discovery, Kubernetes cannot directly perceive the details of each backend interface running inside the Pod, and must be declared in YAML. ShenYu provides Java annotations, allowing microservices to register each backend interface directly to ShenYu Admin during code writing without additional configuration.
  2. Decoupling of Register Client and Register Server is achieved using the registration center, making extension easier. However, there is a new cost: each microservice must have the permission to read and write the registration center. Because microservices do not communicate directly with ShenYu Admin (except for the HTTP registration method), but instead write microservice data into the registration center.

3 Solution Design - Using Kubernetes as Register Center

Register Center is essentially a middleware for storing microservice metadata, where Register Client writes and Register Server reads. So what we need to do is to store the metadata and uri (including port) information of microservices in Kubernetes and listen to it on the Register Server side.

3.1 Choosing the Storage Method

There are currently three main ways to store information in K8s:

  1. Store information in Kubernetes Pod Annotations/Labels. As mentioned earlier, ShenYu’s service registration is independent and actively registered by each microservice, and a microservice corresponds to a process, which is a Kubernetes Pod. It seems better to use Pod Annotations/Labels (such as dubbo-go), but since ShenYu’s metadata exists independently of a specific microservice instance (i.e., Pod), it is not used.
  2. Store information in ConfigMap. This is consistent with the original intention of ConfigMap to store configuration, so I decided to adopt this approach. At the same time, use Labels to filter specific ConfigMap.
  3. Store information in Secert. This is similar to ConfigMap, with data encryption added, but it is not very meaningful and therefore not used.

3.2 Storage Details

3.2.1 Single ConfigMap or Multiple ConfigMaps

We can choose to use a single ConfigMap to store all the key-value data of the microservices, but this is clearly not reasonable:

  1. Every time a microservice changes, the same ConfigMap needs to be modified.
  2. There is a limit to the size of Kubernetes object resources.

Therefore, we use multiple ConfigMaps to store microservice data, using Labels for filtering.

3.2.2 Detailed Design of ConfigMap

Let’s first take a look at how data is stored when using etcd as the registry center:

shenyu
   ├──regsiter
   ├    ├──metadata
   ├    ├     ├──${rpcType}
   ├    ├     ├      ├────${contextPath}
   ├    ├     ├               ├──${ruleName} : save metadata data of MetaDataRegisterDTO
   ├    ├──uri
   ├    ├     ├──${rpcType}
   ├    ├     ├      ├────${contextPath}
   ├    ├     ├               ├──${ip:prot} : save uri data of URIRegisterDTO
   ├    ├     ├               ├──${ip:prot}

We can see that the data is mainly divided into the following layers:

register->metadata/uri->${rpcType}->${contextPath}->details

Now we will design a new storage method for ConfigMap:

  1. Top-level design

First, I store all ConfigMaps in the shenyu namespace, and their names all start with register-. I also add a label shenyu.apache.org/register: true to distinguish them from other ConfigMaps that are not related to the registration center.

  1. Handling metadata/uri

Next, the metadata and uri are stored in two separate ConfigMaps, with the prefixes register-metadata- and register-uri-, respectively. They are also labeled with shenyu.apache.org/register-type: metadata and shenyu.apache.org/register-type: uri, respectively. (The different prefixes are for human readability, while the different labels are for programmatic filtering.)

  1. Handling rpcType

The next level is rpcType. Similarly, the prefix format is register-metadata-${rpcType}- or register-uri-${rpcType}-, such as register-metadata-http. The label shenyu.apache.org/register-rpc-type: ${rpcType} is also used, such as shenyu.apache.org/register-rpc-type: http.

  1. Handling contextPath

For filtering, the label shenyu.apache.org/register-context-path: ${contextPath} is added.

For naming, since contextPath may not conform to the naming rules of ConfigMaps (not a DNS subdomain), it is concatenated with the ruleName or ip:port and then hashed. A mechanism is used to ensure that ConfigMap names are not duplicated, such as adding a timestamp.

  1. Handling ruleName and ip:port

ruleName records metadata, while ip:port records uri information, and both are stored as JSON.

Considering that a microservice may be particularly large and cause individual contextPath data to exceed the capacity limit, I plan to store only one ruleName or ip:port record per ConfigMap, using the labels shenyu.apache.org/register-rule-name: ${ruleName} and shenyu.apache.org/register-ip-port: ${ipPort}. The content format of the ConfigMap is data: {...}.

3.2.3 Example

In the following example, a microservice named app1 is created with a contextPath of /app1. Two interfaces are registered and two instances (Pods) are running, with IP addresses 10.42.0.34:8189 and 10.42.0.36:8189.

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: shenyu
  name: register-metadata-http-asdfio-1679973837912
  labels:
    "shenyu.apache.org/register": "true"
    "shenyu.apache.org/register-type": "metadata"
    "shenyu.apache.org/register-context-path": "app1"
    "shenyu.apache.org/register-rule-name": "app1--app1-hello"
data:
  data: |
    {"appName":"app1","contextPath":"/app1","path":"/app1/hello","pathDesc":"spring annotation register","rpcType":"http","serviceName":"org.apache.shenyu.examples.http.controller.SpringMvcMappingPathController","methodName":"hello","ruleName":"/app1/hello","enabled":true,"pluginNames":[],"registerMetaData":true,"timeMillis":1679973837912,"addPrefixed":false}    

---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: shenyu
  name: register-metadata-http-dfgjkl-1679973837913
  labels:
    "shenyu.apache.org/register": "true"
    "shenyu.apache.org/register-type": "metadata"
    "shenyu.apache.org/register-context-path": "app1"
    "shenyu.apache.org/register-rule-name": "app1--app1-request-**"
data:
  data: |
    {"appName":"app1","contextPath":"/app1","path":"/app1/request/**","rpcType":"http","serviceName":"org.apache.shenyu.examples.http.controller.RequestController","ruleName":"/app1/request/**","enabled":true,"pluginNames":[],"registerMetaData":true,"timeMillis":1679973837911,"addPrefixed":false}    

---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: shenyu
  name: register-metadata-http-oujijk-1679973837914
  labels:
    "shenyu.apache.org/register": "true"
    "shenyu.apache.org/register-type": "uri"
    "shenyu.apache.org/register-context-path": "app1"
    "shenyu.apache.org/register-ip-port": "10.42.0.34:8189"
data:
  data: |
    {"protocol":"http://","appName":"app1","contextPath":"/app1","rpcType":"http","host":"10.42.0.34","port":8189}    
---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: shenyu
  name: register-metadata-http-cxvhyu-1679973837915
  labels:
    "shenyu.apache.org/register": "true"
    "shenyu.apache.org/register-type": "uri"
    "shenyu.apache.org/register-context-path": "app1"
    "shenyu.apache.org/register-ip-port": "10.42.0.36:8189"
data:
  data: |
    {"protocol":"http://","appName":"app1","contextPath":"/app1","rpcType":"http","host":"10.42.0.36","port":8189}    

3.3 Register Client and Register Server Code Implementation

The core of implementing the Register Client and Register Server lies in implementing the ShenyuClientRegisterRepository and ShenyuServerRegisterRepository SPIs.

3.3.1 ShenyuClientRegisterRepository

Here is the definition of ShenyuClientRegisterRepository:

/**
 * Shenyu client register repository.
 */
@SPI
public interface ShenyuClientRegisterRepository {
    default void init(ShenyuRegisterCenterConfig config) {}
    void persistInterface(MetaDataRegisterDTO metadata);
    default void persistURI(URIRegisterDTO registerDTO) {}
    default void persistApiDoc(ApiDocRegisterDTO apiDocRegisterDTO) {}
    default void close() {}
}
  • Init(): I will generate a Java Client that operates on Kubernetes based on user configuration, referring to the client construction and ConfigMap operation code in spring-cloud-kubernetes.
  • persistInterface(): Store metadata in ConfigMap according to the storage method mentioned above, using Kubernetes Java Client.
  • persistURI(): Store metadata in ConfigMap using Kubernetes Java Client.
  • persistApiDoc(): This is a recently added interface that is still under development and has not yet been implemented by other registry centers.
  • close(): Close the Kubernetes Java Client.

3.3.2 ShenyuServerRegisterRepository

Here is the definition of ShenyuServerRegisterRepository :

/**
 * Shenyu client server register repository.
 */
@SPI
public interface ShenyuClientServerRegisterRepository {
    
    default void init(ShenyuRegisterCenterConfig config) {}
    
    default void init(ShenyuClientServerRegisterPublisher publisher, ShenyuRegisterCenterConfig config) {}

    default void close() {}
}

The main work to be done is mainly on the second method:

  1. Initialize the Kubernetes Java Client.
  2. Use Kubernetes List-Watch mechanism to subscribe to ConfigMap updates.
  3. Handle ConfigMap changes in a timely manner when detected.

4 Details of registering microservices deployed in Kubernetes with ShenYu.

In addition to the core design of the registration center described in the previous section, there are some details that need to be addressed in this project.

4.1 Network Connectivity Issues

When a microservice actively registers with ShenYu Admin, it can only obtain the Pod IP of the microservice (which is virtual and can only be used within Kubernetes). However, all user network requests are forwarded through ShenYu Gateway, so if ShenYu Gateway is not inside Kubernetes, the network will be interrupted.

The solution is to deploy both ShenYu Admin and ShenYu Gateway within Kubernetes. After reviewing the documentation and conducting experiments, the various CNI plugins of Kubernetes have implemented cross-node access to Pod IP, so we do not need to worry.

So how do we deploy the various components of ShenYu inside Kubernetes? Fortunately, the community has already developed an initial version of the ShenYu Helm Chart, which can quickly deploy ShenYu in Kubernetes.

4.2 Kubernetes Authorization Issues - Service Account

Since each microservice and ShenYu Admin need to read and write ConfigMap, and both microservices and ShenYu Admin are in Pods, a Service Account needs to be configured for each Pod to grant it access to Kubernetes. For more information, please refer to Configure Service Accounts for Pods.

The configuration looks like this:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: shenyu-configmap-full-access
  namespace: shenyu
rules:
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["*"]
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: shenyu-kubernetes-register
  namespace: shenyu
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: shenyu-configmap-full-access-binding
  namespace: shenyu
subjects:
- kind: ServiceAccount
  name: shenyu-register
  namespace: shenyu
roleRef:
  kind: Role
  name: shenyu-configmap-full-access
  apiGroup: rbac.authorization.k8s.io

Then we need to set the spec.serviceAccountName of ShenYu Admin and all microservice Pods to shenyu-kubernetes-register.

Manually creating ServiceAccount, Role, and RoleBinding can be cumbersome. Is there a simpler way?

Of course! This project will attempt to update the ShenYu Helm Chart to automatically generate the correct ServiceAccount and other objects. Users only need to modify the spec.serviceAccountName of the Pod during deployment.

5 Results for the Shenyu community

Upon completion of this project, the following contributions will be made to the Apache Shenyu community:

  1. Take a big step towards supporting cloud-native by allowing microservices deployed in Kubernetes to register with ShenYu
  2. Contribute a new registration center to ShenYu, based on Kubernetes Configmap
  3. Optimize ShenYu’s Kubernetes ecosystem, such as strengthening the ShenYu Helm Chart
  4. Provide experience for other developers’ “cloud” development journeys, such as how to quickly develop and debug ShenYu’s Kubernetes-related components locally.

6 Project Deliverables

  1. Support deploying microservices deployed in Kubernetes to ShenYu.
  2. Design the storage details of metadata and URI of ShenYu microservice registration using ConfigMap.
  3. Implement a Kubernetes registry center (mainly ShenyuClientRegisterRepository and ShenyuServerRegisterRepository SPI).
  4. Improve other details, such as automatically creating Service Account in ShenYu Helm Chart.
  5. Improve development and user documentation, perform necessary manual testing, and add unit tests, integration tests, etc.

7 Proposed Schedule

TimelineWork content
Preparing(Before Accepted GSoC contributor projects announced)Maintain communication with the community, continue to contribute, discover deployment and cloud related bugs, submit issues and pull requests.
May 4 - 28(Community Bonding Period)Familiarize oneself with the Kubernetes Java Client; Explore the best development process for integrating ShenYu and Kubernetes.
May 29 - July 09(Before Midterm Evaluations)Complete the demo, implement support for registering microservices within Kubernetes, and submit the midterm evaluation.
July 10 - August 21(Work Period)Optimize code details, write tests, and improve documentation.
August 21 - 28(Final week)Submit the final work product and prepare the final mentor evaluation.

8 About Me

I am a second-year master’s student in computer science who loves programming, especially interested in the field of cloud-native technology.

I have made contributions to DevStream, mainly in DevOps-related work. There, I experienced the joy of open source and fell in love with the collaborative coding, asynchronous communication, and collaborative atmosphere unique to open source.

At ShenYu, as a newcomer in the gateway field, I am eager to contribute to the cloud-native transformation of ShenYu.

Related Skill

  • Kubernetes:Medium
  • Java:Medium
  • Spring-framework:Elementary(Keep Learning)

9 Community engagement

10 Other commitments

About GSoC

  • Time: The period for coding in GSoC happens to coincide with my summer vacation. Therefore, I have the entire summer to participate in GSoC projects and contribute to the community. I plan to dedicate 40 hours per week to this project in GSoC.
  • Ability: I have some knowledge of Kubernetes and have validated the core technologies of the project (such as observing the data storage format of etcd as a registry center and testing the network of Kubernetes). I believe that through my efforts and active communication with the community, I can complete this project.

Continued Involvement

After the end of GSoC, I will continue to contribute to the ShenYu community.

  • Continue to promote the cloud-native transformation of ShenYu
  • Participate in the maintenance of ShenYu Client Golang (I am also interested in the Go language)
  • Optimize the user experience for ShenYu, such as the documentation issues mentioned earlier.

References

  1. https://shenyu.apache.org/docs/design/register-center-design/
  2. https://shenyu.apache.org/blog/RegisterCenter-SourceCode-Analysis-Http-Register
  3. https://shenyu.apache.org/docs/user-guide/property-config/register-center-access/#http-registry-config
  4. https://dubbogo.github.io/zh-cn/docs/md/registry-center/design-and-implementation-of-dubbo-go-and-k8s-registry.html
  5. https://kubernetes.io/docs/concepts/configuration/configmap/#configmap-object
  6. https://github.com/spring-cloud/spring-cloud-kubernetes
  7. https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
  8. https://github.com/devstream-io/devstream