This application is a Function-as-a-Service (FaaS) platform that enables users to authenticate, register custom functions, and execute them on demand. The architecture is built on microservices and leverages APISIX as an API Gateway and NATS for message brokering.
The system consists of the following components:
- Authentication-service: Handles user registration, login and token validation.
- Registry-service: Manages user function metadata and stores it in NATS Key-Value buckets.
- Spawner-service: Dynamically spawns containers to execute user functions.
- Publisher-service: Publishes execution requests to NATS topics for worker processing.
The platform supports scalable execution through:
- Workers: Additional workers can subscribe to NATS topics, automatically distributing the workload.
- Microservices: Services can run multiple instances behind APISIX, enabling load balancing, thanks Docker compose replication.
- Each message received by the spawner-service instance starts a Go routine to manage the container lifecycle. This allows multiple containers to be processed concurrently within the same service instance, up to the configured limit, because each requests correspond to another go routine creation.
The worker-spawner manages resource utilization by limiting concurrent container executions. New containers are created as demand increases.
The authentication system implements JWT (JSON Web Tokens) validation for secure user identification. Tokens are generated with a 72-hour expiration period and include user ID, username, and application-specific claims. The APISIX API gateway enforces JWT validation for all routes except /auth/*, using the same shared secret key (from secret.txt). The implementation uses the HS256 signing algorithm and includes standard JWT claims (iss, iat, exp) along with custom claims (key: "faas-app-key") for additional security.
The whole project can be automatically built and deployed using the ./bin/run.sh script on UNIX machines or .\bin\run.windows.ps1 on Windows machines. The script will build all the needed docker images and then start the docker compose process, alternatively, you can build and deploy the services manually.
First build the docker images:
docker build -t apisix-configurator:latest ./api-gateway
docker build -t auth-service:latest ./auth-service
docker build -t registry-service:latest ./registry-service
docker build -t spawner-service:latest ./spawner-service
docker build -t publisher-service:latest ./publisher-serviceTo deploy the services using docker compose, simply run the following command:
docker compose upTo manually scale services in docker compose, use the --scale option:
docker compose up --scale <service-name>=<number>To use swarm, first initialize the swarm, then deploy the stack:
docker swarm init
docker stack deploy -c docker-compose.yml faasTo manually scale services in swarm, use the docker service scale command:
docker service scale faas_auth-service=<number> faas_registry-service=<number> faas_execution-service=<number>To remove the swarm stack, use the docker stack rm command:
docker stack rm faas- The auth service is available INTERNALLY at http://auth-service:8081, EXTERNALLY at http://localhost/auth.
- The registry service is available INTERNALLY at http://registry-service:8082, EXTERNALLY at http://localhost/registry.
- The publisher service is available INTERNALLY at http://publisher-service:8083, EXTERNALLY at http://localhost/publisher.
The entrypoint to all the services, this is the only service that is exposed to the outside world.
User auth & registration
Details can be found here: Auth service
Registering & unregistering functions
Details can be found here: Registry service
It exposes a REST API to spawn the worker that will execute the desired function.
Details can be found here: Publisher service
It executes functions by spawning workers as containers from an image reference and a string argument.
Details can be found here: Spawner service
curl -X POST http://localhost/auth/register -d '{"username":"user","password":"password"}'This will return a 200 OK status code if the registration was successful and the user uuid.
curl -X POST http://localhost/auth/login -d '{"username":"user","password":"password"}'This will return an authorization token that can be used to authenticate the user in the other services.
You can either create a new image locally or use an existing image from Docker Hub.
FROM python:3.9-slim
WORKDIR /usr/src/app
COPY simple_function.py .
RUN chmod +x simple_function.py
ENTRYPOINT ["python", "simple_function.py"]import sys
def simple_function(input_string):
return input_string.upper()
if __name__ == "__main__":
input_string = sys.argv[1]
result = simple_function(input_string)
print(result)docker build -t simple_function:latest .docker tag simple_function:latest <docker-hub-username>/simple_function:latestcurl -X POST http://localhost/registry/register -d '{
"name":"HumanFriendlyName",
"description":"ServiceDescription",
"payload":"docker-image-reference",
}' -H "Authorization: Bearer <token>"curl -X POST http://localhost/publisher/publish -d '{
"name":"HumanFriendlyName",
"argument":"string-argument"
}' -H "Authorization Bearer <token>"To test other features such as function CRUD operations, please refer to the README.md files in the respective services.
Since we did not use kubernetes, there was no classic standard way to dinamically replicate the services. However, we can use the docker-compose or docker swarm scaling options to replicate the services.
As of now, the services are statically programmed to be launched with healthchecks and automatic reloads in case of failure. This is done by the docker-compose.yml.
The static scaling has been created with this numbers:
- 3 auth-service
- 3 registry-service
- 3 publisher-service
- 10 spawner-service
- 1 apisix-configurator (that dies after the configuration is done)
- 2 nats
And one of all accessories services for APISIX.
Moreover, each service is programmed to be able to switch to another NATS server in case of failure. The NATS servers are configured in cluster mode with a replication factor of 2.