Apache APISIX Integrates with Consul KV to Expand Its Capabilities in Service Discovery

Tao Yang

Update At 2/25/2022

Background Information

Consul is a service mesh solution. One of its cores, Consul KV, is a distributed key-value database whose primary purpose is to store configuration parameters and metadata, while also allowing users to store indexed objects.

In the microservice architecture model, when the upstream services change due to capacity expansion, hardware failure, etc., the way to maintain the upstream service information by manually writing the configuration can lead to a steep increase in maintenance cost. In response, Apache APISIX provides a service discovery registry to dynamically obtain the latest service instance information to reduce the maintenance cost for users.

Currently, Apache APISIX supports the Consul KV-based service discovery registry with the consul_kv module contributed by the community.

How It Works

Apache APISIX leverages the consul_kv module of the Consul KV distributed key-value storage capability to decouple the provider and consumer of a service and implement the two core functions of a service discovery registry.

  1. Service registration: Service providers register their services with the registry.
  2. Service Discovery: Service consumers find the routing information of service providers through the registry.

Built on this foundation, Apache APISIX will be more flexible and adaptable to existing microservice architectures to better meet user needs.

APISIX Consul Integration

How to Enable Consul in Apache APISIX

The test environments in this article are built in Docker using docker-compose.

  1. Download Apache APISIX.
1# Pull the Git repository of apisix-docker
2git clone https://github.com/apache/apisix-docker.git
  1. Create Consul folder and configuration files.
1# Create Consul folder
2mkdir -p ~/docker-things/consul/ && cd "$_"
3# Create configuration files
4touch docker-compose.yml server1.json
  1. Edit the docker-compose.yml file.
1version: '3.8'
2
3services:
4  consul-server1:
5    image: consul:1.9.3
6    container_name: consul-server1
7    restart: always
8    volumes:
9      - ./server1.json:/consul/config/server1.json:ro
10    networks:
11      - apisix
12    ports:
13      - '8500:8500'
14    command: 'agent -bootstrap-expect=1'
15
16networks:
17  apisix:
18    external: true
19    name: example_apisix
  1. Edit the server1.json file.
1{
2  "node_name": "consul-server1",
3  "server": true,
4  "addresses": {
5    "http": "0.0.0.0"
6  }
7}
  1. Add Consul-related configuration information to the Apache APISIX configuration file apisix_conf/config.yaml.
1# config.yml
2# ...other config
3discovery:
4  consul_kv:
5    servers:
6      - "http://consul-server1:8500"
7    prefix: "upstreams"
  1. Start Apache APISIX and Consul.
1# Go to the example, consul folder, start APISIX and Consul
2docker-compose up -d
  1. Register the test service to Consul. example contains two web services that you can use directly to test.
1# Check the docker-compose.yml of the example
2# You can see two Web services
3$ cat docker-compose.yml | grep web
4# Outputs
5web1:
6  - ./upstream/web1.conf:/etc/nginx/nginx.conf
7web2:
8  - ./upstream/web2.conf:/etc/nginx/nginx.conf
  1. Confirm the IP addresses of these Web services.
1$ sudo docker inspect -f='{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(sudo docker ps -aq) | grep web
2# Outputs
3/example-web1-1 - 172.26.0.7
4/example-web2-1 - 172.26.0.2
  1. Make a request to Consul's HTTP API in the terminal to register the test service.
1# Register with the corresponding IP
2curl \
3  -X PUT \
4  -d ' {"weight": 1, "max_fails": 2, "fail_timeout": 1}' \
5  http://127.0.0.1:8500/v1/kv/upstreams/webpages/172.26.0.7:80
6
7curl \
8  -X PUT \
9  -d ' {"weight": 1, "max_fails": 2, "fail_timeout": 1}' \
10  http://127.0.0.1:8500/v1/kv/upstreams/webpages/172.26.0.2:80

The path after /v1/kv/ follows the format {Prefix}/{Service Name}/{IP}:{Port}.

{Prefix} is the prefix written when configuring Consul in APISIX, while {Service Name} and {IP}:{Port} need to be determined by the user according to the upstream service.

The format of the data is {"weight": <Num>, "max_fails": <Num>, "fail_timeout": <Num>}.

  1. Check whether the test service is registered successfully.
1$ curl "http://127.0.0.1:8500/v1/kv/upstreams/webpages?keys"

The following return message indicates successful registration.

1["upstreams/webpages/172.26.0.2:80","upstreams/webpages/172.26.0.7:80"]%

Create a Route and Enable Consul

Add Consul to the route using the Admin API provided by Apache APISIX.

The X-API-KEY and upstream.service_name need to be determined before adding them.

  • X-API-KEY: For the Admin API access token, in this example, we use the default edd1c9f034335f136f87ad84b625c8f1.
  • upstream.service_name: The name of the upstream service, which specifies the service in a registry that will be bound to a route, should be set to the URL used to register the test service when using Consul, and the {IP}:{Port} part should be removed at the end. We can also use the Memory Dump API provided by Apache APISIX to get the URL of the service and confirm whether the upstream service is discovered properly.
1$ curl http://127.0.0.1:9092/v1/discovery/consul_kv/dump | jq
2# Output
3{
4  "services": {
5    # This key is the required URL
6    "http://consul-server1:8500/v1/kv/upstreams/webpages/": [
7      {
8        "port": 80,
9        "host": "172.26.0.7",
10        "weight": 1
11      },
12      {
13        "port": 80,
14        "host": "172.26.0.2",
15        "weight": 1
16      }
17    ]
18  },
19  "config": {
20    # ...configs
21  }
22}

Add a Route

Here the request with URL /consul/* is routed to http://consul-server1:8500/v1/kv/upstreams/webpages/. Also, the discovery_type must be set to consul_kv to start the corresponding module.

1curl http://127.0.0.1:9080/apisix/admin/routes -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X POST -d '
2{
3    "uri": "/consul/*",
4    "upstream": {  
5        "service_name": "http://consul-server1:8500/v1/kv/upstreams/webpages/",
6        "type": "roundrobin",
7        "discovery_type": "consul_kv"
8    }
9}'

Test and Verify the Result

The request results show that the new route in Apache APISIX has been able to find the correct service address through Consul and request it to both nodes based on the load balancing policy.

1# the first request
2curl -s http://127.0.0.1:9080/consul/
3# Output
4hello web1%
5
6# the second request
7curl -s http://127.0.0.1:9080/consul/
8# Output
9hello web2%
10
11# Note: It is also possible that both requests will return
12#       the same result as web1 or web2.
13#       This is caused by the nature of load balancing and
14#       you can try to make more requests.

Summary

The first half of this article describes how Apache APISIX works with Consul to implement the Consul KV-based service discovery registry to solve the problem of service information management and maintenance. The second half of this article focuses on how to use Apache APISIX in Docker with Consul. Of course, the application in the actual scenario needs to be analyzed according to the business scenario and the existing system architecture.

More instructions on using the Consul registry in Apache APISIX can be found in the official documentation.

Apache APISIX is also currently working on additional plugins to support the integration of additional services, so if you are interested, feel free to start a discussion in GitHub Discussion, or via the mailing list to communicate.