Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Valkey trigger application tutorial #136

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
213 changes: 213 additions & 0 deletions content/en/docs/spin-operator/tutorials/pub-sub-with-redis.md
@@ -0,0 +1,213 @@
---
title: Publish-Subscribe With Redis
description: Learn how to create a Spin application that responds to messages on pub-sub Redis channels and runs in Kubernetes
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
categories: [Spin Operator]
tags: [Tutorials]
weight: 100
---

## Prerequisites

For this tutorial, we will be using:
- [Spin](https://developer.fermyon.com/spin/v2/install) to build and deploy our event-driven WebAssembly application,
- [Redis](https://redis.io/docs/install/install-redis/) to generate events in our real-time messaging scenario, and
- [Rancher Desktop](https://rancherdesktop.io/) to manage Kubernetes on our Desktop. ([This page](../../spin-operator/tutorials/integrating-with-rancher-desktop.md) documents integrating Rancher Desktop and SpinKube.)
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

## Create a Kubernetes Cluster

First, we create our Kubernetes cluster:

```bash
k3d cluster create wasm-cluster --image ghcr.io/spinkube/containerd-shim-spin/k3d:v0.13.1 -p "8081:80@loadbalancer" --agents 2
```

tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
## Install CRDs for SpinKube

Next, we apply the necessary Custom Resource Definitions (CRDs) to our Kubernetes cluster:

```bash
kubectl apply -f https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.crds.yaml
kubectl apply -f https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.runtime-class.yaml
kubectl apply -f https://github.com/spinkube/spin-operator/releases/download/v0.1.0/spin-operator.shim-executor.yaml
```

## Install Spin Operator

Then, we install Spin Operator which handles the `SpinApp` application that we are about to create:

```bash
helm install spin-operator \
--namespace spin-operator \
--create-namespace \
--version 0.1.0 \
--wait \
oci://ghcr.io/spinkube/charts/spin-operator
```

## Redis

Let's dive in and get Redis sorted because we are going to need information about our Redis installation in our Spin application's config. We will use the following `helm` commands to get the job done:
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

```bash
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install my-redis bitnami/redis
```

The `helm` installation process from above prints a lot of useful information to the terminal. For example, the endpoints to communicate with Redis (read/write vs read-only):

```bash
my-redis-master.default.svc.cluster.local for read/write operations (port 6379)
my-redis-replicas.default.svc.cluster.local for read-only operations (port 6379)
```

In addition, there are pre-written commands that you can cut and paste. For example:
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

```bash
export REDIS_PASSWORD=$(kubectl get secret --namespace default my-redis -o jsonpath="{.data.redis-password}" | base64 -d)
```

Go ahead and run the command above to set the password. And, if need be, you can run the following command to see the actual password printed in your terminal:
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

```bash
echo $REDIS_PASSWORD
```

## Create a Redis Message Handler Using Rust

We use Spin's convenient `redis-rust` template to scaffold our Rust-based Redis message handler:

```bash
spin new -t redis-rust redis-message-handler
```

The command above will provide the prompts for you to add the Description, Redis address and Redis channel (We use the `my-redis-master.default.svc.cluster.local` from above to help configure the Redis address, and the channel is arbitrary i.e. `channel-one`):

```bash
Description: Redis message handler using Rust
Redis address[redis://localhost:6379]: redis://my-redis-master.default.svc.cluster.local:6379
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
Redis channel: channel-one
```

## Configure Spin Application

We change into our application directory, and can see the layout that Spin has scaffolded for us:

```bash
cd redis-message-handler
tree .
```

The above `tree .` command, produces the following output:

```bash
.
├── Cargo.toml
├── spin.toml
└── src
└── lib.rs
```

If we open the application manifest (`spin.toml` file) we see that Spin has already pre-populated the [Redis trigger configuration](https://developer.fermyon.com/spin/v2/redis-trigger#the-spin-redis-trigger):

```toml
// --snip --
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
[application.trigger.redis]
address = "redis://my-redis-master.default.svc.cluster.local:6379"
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

[[trigger.redis]]
channel = "channel-one"
component = "redis-message-handler"
// --snip --
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
```

By default, Spin does not authenticate to Redis. You can work around this by providing the password in the `redis://` URL. For example: `address = "redis://:p4ssw0rd@localhost:6379"`
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

> Do not use passwords in code committed to version control systems.

## Application Logic

In this example, we want to write the logic for our Spin application to listen to messages published on `channel-one`. So, we open the `src/lib.rs` file and paste the following code:

```rust
use anyhow::Result;
use bytes::Bytes;
use spin_sdk::redis_component;
use std::str::from_utf8;

/// A simple Spin Redis component.
#[redis_component]
fn on_message(message: Bytes) -> Result<()> {
println!("{}", from_utf8(&message)?);
Ok(())
}
```

## Build

With the logic and configuration in place, we can build the Spin application:

```bash
spin build
```

## Publish

We will now push the application image to a registry. You can use any container registry you prefer (like DockerHub). But for this tutorial, we’ll use a simple one that does not require authentication:

```bash
spin registry push ttl.sh/redis-message-handler:0.1.0
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
```

To read the configuration we can use the `spin scaffold` command:
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

```bash
spin kube scaffold --from ttl.sh/redis-message-handler:0.1.0
```

As we can see, our `SpinApp` is all set and using the `containerd-shim-spin` executor:

```yaml
apiVersion: core.spinoperator.dev/v1alpha1
kind: SpinApp
metadata:
name: redis-message-handler
spec:
image: "ttl.sh/redis-message-handler:0.1.0"
executor: containerd-shim-spin
replicas: 2
```

## Deploy

We deploy our application to our cluster using the `spin kube` command:
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

```bash
spin kube deploy --from ttl.sh/redis-message-handler:0.1.0
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
```

## Test

We want to run the Redis server and publish a message. First, we run the Redis container image:
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

```bash
kubectl run --namespace default redis-client --restart='Never' --env REDIS_PASSWORD=$REDIS_PASSWORD --image docker.io/bitnami/redis:7.2.4-debian-12-r9 --command -- sleep infinity
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved
```

Then, we want to attach to the pod:
tpmccallum marked this conversation as resolved.
Show resolved Hide resolved

```bash
kubectl exec --tty -i redis-client --namespace default -- bash
```

And, access the Redis CLI from inside the cluster:

```bash
REDISCLI_AUTH="$REDIS_PASSWORD" redis-cli -h my-redis-master
```

Which then provides us with the prompt (`my-redis-master:6379>`) at which point we can publish our message:

```bash
my-redis-master:6379> PUBLISH channel-one message-one
```
Copy link
Contributor

Choose a reason for hiding this comment

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

Lastly, shall we show the redis app pod logs printing receipt of the message?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @vdice
I made it all the way to the end of this document on my machine, but I get integer 0 when I publish the message on channel-one. There are no error messages; everything seems to be running, and the prompts are in a ready state.
I feel like I am missing something; should I be writing any Rust code after the // Implement me comment?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hi @tpmccallum, that's all good on the publishing side. What I meant here was to then show the logs from the consuming side, eg the Spin app that is subscribing to messages published to the channel-one channel. For example, here are the logs for a redis SpinAapp when I published "HELLO" to the messages channel:

$ kubectl logs -f -l core.spinoperator.dev/app-name=redis-spinapp
Active Channels on redis://:DTatx2DV4p@redis-master.redis.svc.cluster.local:6379:
	messages: [redis-rust]
HELLO

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, nice one @vdice!