Skip to content

Commit

Permalink
docs: deploy layotto state app on kubernetes
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoxiang10086 committed Sep 26, 2023
1 parent 525006d commit adb65e4
Show file tree
Hide file tree
Showing 9 changed files with 462 additions and 0 deletions.
14 changes: 14 additions & 0 deletions demo/state/k8s/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM golang:1.18-alpine AS build-env

WORKDIR /app

COPY ../../../deploy/k8s/state .

RUN go build -o client client.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=build-env /app/client .
COPY config.json /usr/share/layotto/configure/
EXPOSE 8443
CMD [ "./client", "-s", "state_demo" ]
60 changes: 60 additions & 0 deletions demo/state/k8s/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Prerequisites
This Layotto state SDK client demo requires you to have the following installed on your machine:

- [kubectl](https://kubernetes.io/docs/tasks/tools/)
- A Kubernetes cluster, such as [Minikube](https://minikube.sigs.k8s.io/docs/start/), [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/)
- [Helm v3](https://helm.sh/docs/intro/install/)

# Step 1 - Setup Layotto sidecar injector and Redis on your Kubernetes cluster
1. Use Kind to quickly build a local Kubernetes cluster
```
kind create cluster --name layotto-cluster
kubectl config use-context kind-layotto-cluster
```
2. Add xiaoxiang10086.github.io as an helm repo
```
helm repo add layotto https://xiaoxiang10086.github.io/layotto-helm-charts/
helm repo update
```
3. Install the Layotto chart on your cluster in the Layotto-system namespace
```
helm install layotto layotto/layotto-sidecar-injector --namespace layotto-system --wait
```

# Step 2 - Use Helm to deploy Redis on your Kubernetes cluster
`Redis` is an open source, advanced key-value store. It is often referred to as a data structure server since keys
can contain strings, hashes, lists, sets and sorted sets.

Here we use `Redis` to persist and retrieve state.

```
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install redis bitnami/redis --set image.tag=6.2 --set auth.enabled=false
```

# Step 3 - Deploy the layotto state client with the Layotto sidecar
1. Create a ConfigMap named `layotto-config` and populate its data from the `config.json` file
```
kubectl create configmap layotto-config --from-file=./config.json
```
2. Deploy Layotto state SDK client App
```
kubectl apply -f ./state-sdk-demo.yaml
```

Let's take a look at the important annotations in state-sdk-demo.yaml
- `layotto/sidecar-inject: "true"` - this tells the Layotto sidecar injector to inject a sidecar to this deployment.
- `layotto/config-volume: "layotto-config-vol` - this tells the Layotto sidecar injector which config Volume resource to
mount into layout container.

The `layotto-config` ConfigMap is mounted as a volume, and all contents stored in its `config.json` entry are mounted into
the layotto sidecar container at path `/runtime/configs`. The successfully mounted `config.json` file will be used as the configuration
file when Layotto starts.

# View program running results
If the following information is printed, the demo succeeded:

![pods.jpg](images/pods.jpg)
![log.jpg](images/log.jpg)

136 changes: 136 additions & 0 deletions demo/state/k8s/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright 2021 Layotto Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package main

import (
"context"
"flag"
"fmt"

client "mosn.io/layotto/sdk/go-sdk/client"
)

const (
key1 = "key1"
key2 = "key2"
key3 = "key3"
key4 = "key4"
key5 = "key5"
)

var storeName string

func init() {
flag.StringVar(&storeName, "s", "", "set `storeName`")
}

func main() {
// parse command arguments
flag.Parse()
if storeName == "" {
panic("storeName is empty.")
}

// create a layotto client
cli, err := client.NewClient()
if err != nil {
panic(err)
}
defer cli.Close()

ctx := context.Background()
value := []byte("hello world")
fmt.Printf("Start testing %v\n", storeName)

// Belows are CRUD examples.
// save state
testSave(ctx, cli, storeName, key1, value)

// get state
testGet(ctx, cli, storeName, key1)

// SaveBulkState with options and metadata
testSaveBulkState(ctx, cli, storeName, key1, value, key2)

keyTostate := testGetBulkState(ctx, cli, storeName, key1, key2)

// delete state
testDelete(ctx, cli, storeName, key1, keyTostate[key1].Etag)
testDelete(ctx, cli, storeName, key2, keyTostate[key2].Etag)
}

func testGetBulkState(ctx context.Context, cli client.Client, store string, key1 string, key2 string) map[string]*client.BulkStateItem {
state, err := cli.GetBulkState(ctx, store, []string{key1, key2, key3, key4, key5}, nil, 3)
if err != nil {
panic(err)
}
m := make(map[string]*client.BulkStateItem)
for _, item := range state {
fmt.Printf("GetBulkState succeeded.key:%v ,value:%v ,etag:%v ,metadata:%v \n", item.Key, string(item.Value), item.Etag, item.Metadata)
m[item.Key] = item
}
return m
}

func testDelete(ctx context.Context, cli client.Client, store string, key string, etag string) {
if err := cli.DeleteStateWithETag(ctx, store, key, &client.ETag{Value: etag}, nil, nil); err != nil {
panic(err)
}
fmt.Printf("DeleteState succeeded.key:%v\n", key)
}

func testSaveBulkState(ctx context.Context, cli client.Client, store string, key string, value []byte, key2 string) {
item := &client.SetStateItem{
// etag is used to implement Optimistic Concurrency Control (OCC)
// see https://docs.dapr.io/developing-applications/building-blocks/state-management/state-management-overview/#concurrency
Etag: &client.ETag{
Value: "2",
},
Key: key,
Metadata: map[string]string{
"some-key-for-component": "some-value",
},
Value: value,
Options: &client.StateOptions{
Concurrency: client.StateConcurrencyLastWrite,
Consistency: client.StateConsistencyStrong,
},
}
item2 := *item
item2.Key = key2

if err := cli.SaveBulkState(ctx, store, item, &item2); err != nil {
panic(err)
}
fmt.Printf("SaveBulkState succeeded.[key:%s etag:%s]: %s\n", item.Key, item.Etag.Value, string(item.Value))
fmt.Printf("SaveBulkState succeeded.[key:%s etag:%s]: %s\n", item2.Key, item2.Etag.Value, string(item2.Value))
}

func testGet(ctx context.Context, cli client.Client, store string, key string) {
item, err := cli.GetState(ctx, store, key)
if err != nil {
panic(err)
}
fmt.Printf("GetState succeeded.[key:%s etag:%s]: %s\n", item.Key, item.Etag, string(item.Value))
}

func testSave(ctx context.Context, cli client.Client, store string, key string, value []byte) {
if err := cli.SaveState(ctx, store, key, value); err != nil {
panic(err)
}
fmt.Printf("SaveState succeeded.key:%v , value: %v \n", key, string(value))
}
105 changes: 105 additions & 0 deletions demo/state/k8s/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"servers": [
{
"default_log_path": "stdout",
"default_log_level": "DEBUG",
"routers": [
{
"router_config_name": "actuator_dont_need_router"
}
],
"listeners": [
{
"name": "grpc",
"address": "0.0.0.0:34904",
"bind_port": true,
"filter_chains": [
{
"filters": [
{
"type": "grpc",
"config": {
"server_name": "runtime",
"grpc_config": {
"hellos": {
"helloworld": {
"type": "helloworld",
"hello": "greeting"
}
},
"state": {
"state_demo": {
"type": "redis",
"metadata": {
"redisHost": "redis-master.default.svc.cluster.local:6379",
"redisPassword": ""
}
}
},
"sequencer": {
"sequencer_demo": {
"type": "redis",
"metadata": {
"redisHost": "redis-master.default.svc.cluster.local:6379",
"redisPassword": ""
}
}
},
"lock": {
"lock_demo": {
"type": "redis",
"metadata": {
"redisHost": "redis-master.default.svc.cluster.local:6379",
"redisPassword": ""
}
}
},
"pub_subs": {
"pub_subs_demo": {
"type": "redis",
"metadata": {
"redisHost": "redis-master.default.svc.cluster.local:6379",
"redisPassword": ""
}
}
},
"app": {
"app_id": "app1",
"grpc_callback_port": 9999,
"grpc_callback_host": "host.docker.internal"
}
}
}
}
]
}
]
},
{
"name": "actuator",
"address": "0.0.0.0:34999",
"bind_port": true,
"filter_chains": [
{
"filters": [
{
"type": "proxy",
"config": {
"downstream_protocol": "Http1",
"upstream_protocol": "Http1",
"router_config_name": "actuator_dont_need_router"
}
}
]
}
],
"stream_filters": [
{
"type": "actuator_filter"
}
]
}
]
}
]
}
17 changes: 17 additions & 0 deletions demo/state/k8s/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module client

go 1.18

require mosn.io/layotto/sdk/go-sdk v0.0.0-20230920030758-dbf443e27376

require (
github.com/golang/protobuf v1.5.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/net v0.0.0-20190311183353-d8887717615a // indirect
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a // indirect
golang.org/x/text v0.3.0 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
google.golang.org/grpc v1.37.0 // indirect
google.golang.org/protobuf v1.26.0-rc.1 // indirect
mosn.io/layotto/spec v0.0.0-20230920030758-dbf443e27376 // indirect
)
Loading

0 comments on commit adb65e4

Please sign in to comment.