Run a service locally
Running a service locally is helpful to test and debug. The following sections provide commands for starting and stopping a single service locally.
Prerequisites
In order to run your service locally, you’ll need to have the following prerequisites:
-
Docker 20.10.14 or higher.
-
Access to the
gcr.io/kalix-public
container registry. This is a public container registry that provides a Kalix Runtime container suited for local development. Make sure this registry is not blocked by your firewall.
The samples provided with the Kalix SDKs all have |
Starting your service
To start your service locally, run the following command from the root of your project:
- Java
-
mvn kalix:runAll
- Scala
-
sbt runAll
This command will start your Kalix service and a Kalix Runtime using the included docker-compose.yml
file.
If you prefer, you can instead start docker-compose
manually by running docker-compose up
in one terminal and in another terminal start your Kalix service with:
- Java
-
mvn kalix:run
- Scala
-
sbt run
Invoking your service
After you start the service it will accept invocations on localhost:9000
. You can use gRPCurl to invoke your service.
As an example, we will use the customer-registry
sample.
As an example, we will use the customer-registry
sample.
You can inspect your services with the gRPC list
and describe
commands:
grpcurl -plaintext localhost:9000 list
grpcurl -plaintext localhost:9000 describe customer.api.CustomerService
Be sure to use gRPCurl |
Get a customer value from the CustomerService
:
- Linux or macOS
-
grpcurl \ -d '{"customer_id": "vip", "email": "vip@example.com", "name": "Very Important", "address": {"street": "Road 1", "city": "The Capital"}}' \ --plaintext localhost:9000 \ customer.api.CustomerService/Create
-
The message payload in JSON format with
-d
-
The address of the Kalix Runtime (using
plaintext
instead of TLS) -
The RPC procedure name which is deduced from the protobuf definition of the component you’re calling.
-
- Windows 10+
-
grpcurl ^ -d '{"customer_id": "vip", "email": "vip@example.com", "name": "Very Important", "address": {"street": "Road 1", "city": "The Capital"}}' ^ --plaintext localhost:9000 ^ customer.api.CustomerService/Create
-
The message payload in JSON format with
-d
-
The address of the Kalix Runtime (using
plaintext
instead of TLS) -
The RPC procedure name which is deduced from the protobuf definition of the component you’re calling.
-
Shutting down the service
Use Ctrl+C
to shut down the service. When stopping your service, it will also shutdown the Kalix Runtime container and any other container you have defined in docker-compose.yml
. Unless you have chosen to start docker-compose
manually in a separate terminal, in which case you will need to stop it manually as well.
Running service with persistence enabled
By default, Kalix services are configured to run locally with persistence disabled. This means the Kalix Runtime will use an in-memory datastore for the state of your services. This is useful for local development since it allows you to quickly start and stop your service without having to worry about cleaning the database.
However, if you want to run your service with persistence enabled to keep the data when restarting, you may update the docker-compose
file as below.
version: "3"
services:
kalix-runtime:
image: gcr.io/kalix-public/kalix-runtime:1.2.5
# uncomment volumes when persistence is enabled
volumes: (1)
- ./target/kalix:/var/kalix
ports:
- "${ADVERTISED_HTTP_PORT}:9000"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
PERSISTENCE_ENABLED: "true" (2)
# those variables are defined in the .env file
# when running multiple services on your local machine, make sure that
# the ports are unique by editing the corresponding .env files
ADVERTISED_HTTP_PORT: ${ADVERTISED_HTTP_PORT}
USER_SERVICE_HOST: ${USER_SERVICE_HOST}
USER_SERVICE_PORT: ${USER_SERVICE_PORT}
1 | Maps local directory to the Kalix Runtime database directory /var/kalix . |
2 | Sets PERSISTENCE_ENABLED environment variable to true . |
Running multiple services locally
A typical Kalix application is composed of one or more services deployed to the same Kalix project. When deployed under the same Kalix project, two different services can make calls to each other or subscribe to each other’s event streams by simply using their logical names.
The same can be done on your local machine by configuring the services to run on different ports and by configuring them to "discover" each other using some extra configurations.
In this section, we will show you how to configure your local development environment to run two services and have them call each other.
For that we will use two of our existing samples: customer-registry
and customer-registry-subscriber
.
For that we will use two of our existing samples: customer-registry
and customer-registry-subscriber
.
The customer-registry sample provides a service to register customers and the customer-registry-subscriber subscribes to an event stream produced by the customer-registry service, building a View from it.
Customer Registry Sample
The docker-compose.yml
file from customer-registry is left untouched and use the usual default ports. The user service will run on port 8080 and the Kalix Runtime on port 9000.
version: "3"
services:
kalix-runtime:
image: gcr.io/kalix-public/kalix-runtime:1.2.5
container_name: java-protobuf-eventsourced-customer-registry
ports:
- "${ADVERTISED_HTTP_PORT}:9000"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
JAVA_TOOL_OPTIONS: >
# jvm -D properties can be added under this environment map (note: remove this comment when adding properties)
# those variables are defined in the .env file
# when running multiple services on your local machine, make sure that
# the ports are unique by editing the corresponding .env files
ADVERTISED_HTTP_PORT: ${ADVERTISED_HTTP_PORT}
USER_SERVICE_HOST: ${USER_SERVICE_HOST}
USER_SERVICE_PORT: ${USER_SERVICE_PORT}
Customer Registry Subscriber Sample
On the other hand, in the customer-registry-subscriber we will use port 8081 and 9001 respectively to avoid port conflicts with the customer-registry service.
The ports can be configured in the .env
located at the project’s root folder.
# this is the port where the kalix runtime container will be exposed
# when running multiple services on your local machine, make sure that this port is unique
ADVERTISED_HTTP_PORT=9001
# this is the port where the user services (your application) will open
# when running multiple services on your local machine, make sure that this port is unique
USER_SERVICE_PORT=8081
# this variable defines the host where the kalix runtime (running in docker)
# will reach the user service in local development
USER_SERVICE_HOST=host.docker.internal
Moreover, since customer-registry-subscriber needs to subscribe to customer-registry and since we will be running it on our local machine, we need to show it where to find the customer-registry service.
This is done by passing an extra property (kalix.dev-mode.service-port-mappings.customer-registry
) to its Kalix Runtime to let it create a mapping between the logical name customer-registry and the host and port where the customer-registry service is running.
Note that you need to add service port mappings to the Kalix Runtime configuration for the service that depends on the other service. Here, customer-registry-subscriber depends on customer-registry, therefore we add the service port mapping to the Kalix Runtime associated with the customer-registry-subscriber.
version: "3"
services:
kalix-runtime:
image: gcr.io/kalix-public/kalix-runtime:1.2.5
container_name: java-protobuf-eventsourced-customer-registry-subscriber
ports:
- "${ADVERTISED_HTTP_PORT}:9000"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
JAVA_TOOL_OPTIONS: >
-Dkalix.dev-mode.service-port-mappings.customer-registry=host.docker.internal:9000
# those variables are defined in the .env file
# when running multiple services on your local machine, make sure that
# the ports are unique by editing the corresponding .env files
ADVERTISED_HTTP_PORT: ${ADVERTISED_HTTP_PORT}
USER_SERVICE_HOST: ${USER_SERVICE_HOST}
USER_SERVICE_PORT: ${USER_SERVICE_PORT}
With both services configured, we can start them independently by running mvn kalix:runAll
sbt runAll
in two separate terminals.
From a third terminal, we can create a customer on customer-registry service.
- Linux or macOS
-
grpcurl \ -d '{"customer_id": "vip", "email": "vip@example.com", "name": "Very Important", "address": {"street": "Road 1", "city": "The Capital"}}' \ --plaintext localhost:9000 \ customer.api.CustomerService/Create
- Windows 10+
-
grpcurl ^ -d '{"customer_id": "vip", "email": "vip@example.com", "name": "Very Important", "address": {"street": "Road 1", "city": "The Capital"}}' ^ --plaintext localhost:9000 ^ customer.api.CustomerService/Create
While watching the logs in customer-registry-subscriber service, we will see it receiving the customer created event. After that we can query its View.
grpcurl --plaintext localhost:9001 customer.view.AllCustomersView/GetCustomers
Running other services with Docker
In the previous example, we run two services independently with mvn kalix:runAll
sbt runAll
. Each time, the service is started together with a Kalix Runtime running in a Docker container.
An alternative is to build a larger docker-compose
file containing dependent services. In the case of customer-registry-subscriber, we can have a second docker-compose
file containing a Kalix Runtime for customer-registry-subscriber, a Kalix Runtime for customer-registry and the customer-registry itself.
At the root of the customer-registry-subscriber service, we can find an alternative docker-compose
file called docker-compose-integration.yml
.
version: "3"
services:
kalix-runtime:
image: gcr.io/kalix-public/kalix-runtime:1.2.5
container_name: java-protobuf-eventsourced-customer-registry-subscriber
depends_on:
- kalix-runtime-customer-registry
ports:
- "9001:9000"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
JAVA_TOOL_OPTIONS: >
-Dkalix.dev-mode.service-port-mappings.customer-registry=host.docker.internal:9000
ADVERTISED_HTTP_PORT: 9001
USER_SERVICE_HOST: host.docker.internal
USER_SERVICE_PORT: "8081"
kalix-runtime-customer-registry:
image: gcr.io/kalix-public/kalix-runtime:1.2.5
container_name: java-protobuf-eventsourced-customer-registry
ports:
- "9000:9000"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
JAVA_TOOL_OPTIONS: >
# jvm -D properties can be added under this environment map (note: remove this comment when adding properties)
USER_SERVICE_HOST: host.docker.internal
USER_SERVICE_PORT: "8080"
customer-registry:
image: kcr.us-east-1.kalix.io/acme/eventsourced-customer-registry:latest
ports:
- "8080:8080"
environment:
HOST: customer-registry
But first, we need to build an image for customer-registry. For local development, we don’t need to publish it to a remote container registry. It suffices to build it locally.
We can build the image by calling the following command at the root of customer-registry:
- Java
-
mvn package docker:build
- Scala
-
sbt docker:publishLocal
Next we can run customer-registry-subscriber and instead pass docker-compose-integration.yml
to it.
Run the following command at the root of customer-registry-subscriber:
- Java
-
mvn -Dkalix.dev-mode.docker-compose-file=docker-compose-integration.yml kalix:runAll
- Scala
-
sbt -Dkalix.dev-mode.docker-compose-file=docker-compose-integration.yml runAll
This time, mvn kalix:runAll
sbt runAll
will start docker-compose-integration.yml
instead. The customer-registry service and its companion Kalix Runtime will run alongside customer-registry-subscriber and its own Kalix Runtime.
Note that the configuration is exactly the same except that now we are running one single mvn kalix:runAll
sbt runAll
command and the docker-compose
file we are using contains all the dependencies required by customer-registry-subscriber.
This approach can be extended to any service you might want to integrate with. It can be any other Kalix service that you plan to deploy to the same Kalix project or even external services. The only requirement is to have a Docker image for it.