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

feat: support pod injection to deploy layotto as a sidecar in Kubernetes #993

Merged
merged 8 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion .github/workflows/layotto-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,12 @@ jobs:
- name: Push Image to DockerHub
run: |
make image.push.linux_amd64.layotto VERSION=latest
- name: Build layotto/layotto_injector:latest Image
run: |
make image.build.linux_amd64.layotto_injector VERSION=latest
- name: Push layotto/layotto_injector:latest Image to DockerHub
run: |
make image.push.linux_amd64.layotto_injector VERSION=latest
- name: Build layotto/proxyv2:latest Image
run: |
make image.proxyv2.build VERSION=latest
Expand All @@ -280,7 +286,7 @@ jobs:
make image.proxyv2.push VERSION=latest

build-push-linux-arm64-image:
name: "Linux ARMD64 Image"
name: "Linux ARM64 Image"
needs: [build-wasm-binary-linux-amd64-artifact,build-binary-linux-arm64-artifact,build-binary-linux-amd64-artifact,build-binary-darwin-amd64-artifact,build-binary-darwin-arm64-artifact]
if: github.ref_name == 'main'
runs-on: ubuntu-latest
Expand All @@ -302,3 +308,24 @@ jobs:
- name: Push Image to DockerHub
run: |
make image.push.linux_arm64.layotto VERSION=latest
- name: Build layotto/layotto_injector:latest Image
run: |
make image.build.linux_arm64.layotto_injector VERSION=latest
- name: Push layotto/layotto_injector:latest Image to DockerHub
run: |
make image.push.linux_arm64.layotto_injector VERSION=latest

package-push-injector-helm-chart:
name: "Layotto Injector Helm Chart"
if: github.ref_name == 'main'
runs-on: ubuntu-latest
steps:
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Package and Push Layotto Injector Latest Helm Chart
run: |
CHART_VERSION=v0.0.0-latest APP_VERSION=latest make helm-package
CHART_VERSION=v0.0.0-latest APP_VERSION=latest make helm-push
28 changes: 27 additions & 1 deletion .github/workflows/layotto-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,15 @@ jobs:
- name: Push Image to DockerHub
run: |
make image.push.linux_amd64.layotto
- name: Build Layotto Injector Latest Image
run: |
make image.build.linux_amd64.layotto_injector
- name: Push Layotto Injector Latest Image to DockerHub
run: |
make image.push.linux_amd64.layotto_injector

build-push-linux-arm64-image:
name: "Linux ARMD64 Image"
name: "Linux ARM64 Image"
needs: [build-wasm-binary-linux-amd64-artifact,build-binary-linux-arm64-artifact,build-binary-linux-amd64-artifact,build-binary-darwin-amd64-artifact,build-binary-darwin-arm64-artifact]
runs-on: ubuntu-latest
steps:
Expand All @@ -328,3 +334,23 @@ jobs:
- name: Push Image to DockerHub
run: |
make image.push.linux_arm64.layotto
- name: Build Layotto Injector Latest Image
run: |
make image.build.linux_arm64.layotto_injector
- name: Push Layotto Injector Latest Image to DockerHub
run: |
make image.push.linux_arm64.layotto_injector

package-push-injector-helm-chart:
name: "Layotto Injector Helm Chart"
runs-on: ubuntu-latest
steps:
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Package and Push Injector Latest Helm Chart
run: |
make helm-package
make helm-push
23 changes: 23 additions & 0 deletions cmd/layotto_injector/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package main

import (
log "github.com/sirupsen/logrus"

"mosn.io/layotto/pkg/injector/service"
)

func main() {
cfg, err := service.GetConfig()
if err != nil {
log.Fatalf("Error getting config: %v", err)
}
inj, err := service.NewInjector(cfg)
if err != nil {
log.Fatalf("Error creating layotto-injector: %v", err)
}
err = inj.Run()
if err != nil {
log.Fatalf("Error running layotto-injector: %v", err)
}
log.Info("Layotto sidecar injector shutdown gracefully")
}
11 changes: 11 additions & 0 deletions demo/state/k8s/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
FROM golang:1.18-alpine AS build-env

WORKDIR /app

COPY . .

RUN go build -o client client.go

FROM alpine:latest
COPY --from=build-env /app/client .
CMD [ "./client", "-s", "state_demo" ]
54 changes: 54 additions & 0 deletions demo/state/k8s/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# 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 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. Install the layotto sidecar injector chart on your cluster in the layotto-system namespace
```
helm install injector oci://docker.io/layotto/injector-helm --version v0.5.0 -n layotto-system --create-namespace --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)
149 changes: 149 additions & 0 deletions demo/state/k8s/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* 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"
"math"
"time"

"github.com/avast/retry-go"

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)

// prevent program exit
time.Sleep(time.Duration(math.MaxInt))
}

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) {
err := retry.Do(func() error {
return cli.SaveState(ctx, store, key, value)
},
retry.Attempts(3),
retry.Delay(time.Second),
)
if err != nil {
panic(err)
}
fmt.Printf("SaveState succeeded.key:%v , value: %v \n", key, string(value))
}
Loading
Loading