Skip to content
This repository has been archived by the owner on Jan 9, 2025. It is now read-only.

Commit

Permalink
feat: add more info to docs (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
lostbean authored Jul 5, 2024
1 parent 9f1a759 commit 54400fb
Show file tree
Hide file tree
Showing 9 changed files with 446 additions and 15 deletions.
52 changes: 52 additions & 0 deletions .github/workflows/publish-demo-artifacts.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Publish demo artifacts

on:
push:
branches:
- main
tags:
- "v*.*.*"
pull_request:
branches:
- main

env:
MAIN_BRANCH: ${{ 'refs/heads/main' }}

jobs:
build-publish-demo:
runs-on: ubuntu-latest
if: github.ref == env.MAIN_BRANCH
steps:
- name: git checkout
uses: actions/checkout@v3

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push voting app UI - v1
uses: docker/build-push-action@v6
with:
context: ./examples/voting-app/voting-app-ui/
file: ./examples/voting-app/voting-app-ui/Dockerfile
platforms: linux/amd64,linux/arm64
push: true
tags: kurtosistech/demo-voting-app-ui:latest

- name: Build and push voting app UI - v2
uses: docker/build-push-action@v6
with:
context: ./examples/voting-app/voting-app-ui/
file: ./examples/voting-app/voting-app-ui/Dockerfile-v2
platforms: linux/amd64,linux/arm64
push: true
tags: kurtosistech/demo-voting-app-ui-v2:latest
58 changes: 45 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Kardinal is a traffic control and data isolation layer that enables engineers to
Kardinal injects production data and service dependencies into your dev and test workflows safely and securely. Instead of spinning up ephemeral environments with mocked services, fake traffic, and fake data, developers using Kardinal can put their service directly into the production environment to see how it works... without risking the stability of that environment.

Key features:

- Develop and test directly in production without risk
- Catch bugs that "only appear in prod" faster
- Stop maintaining multiple environments - do it all in production
Expand Down Expand Up @@ -58,7 +59,7 @@ Have questions or need assistance? We're here to help:

## Architecture

Kardinal main components are the Kardinal CLI and the Kardinal Manager. The Kardinal CLI allows the user to manage the development flows. The Kardinal Manager retrieves the latest configuration from the Kardinal Cloud and applies changes to the K8S user services topology.
Kardinal main components are the Kardinal CLI and the Kardinal Manager. The Kardinal CLI allows the user to manage the development flows. The Kardinal Manager retrieves the latest configuration from the Kardinal Cloud and applies changes to the K8S user services topology.

![kardinal-dev-overview](./img/kardinal-dev-overview.png?raw=true)

Expand All @@ -72,20 +73,27 @@ The Kardinal CLI is a standalone tool interacting with the Kardinal Cloud to man

The Kardinal Manager retrieves the latest user services topology from the Kardinal Cloud and applies the changes by interacting with the Istio client and K8S client. The Manager manages traffic using Istio objects such as virtual services and destination rules. The Manager also updates the K8S services and deployments.


## Quickstart

### How to run Kardinal and use the voting app example to test the dev flow

#### Prerequisites

- A local Kubernetes cluster ([Minikube](https://minikube.sigs.k8s.io/docs/start/?arch=%2Fmacos%2Fx86-64%2Fstable%2Fbinary+download used in this example)
You will need the following tools installed (they will be already available if you are using the nix shell provided by this repository):

- A local Kubernetes cluster ([Minikube](https://minikube.sigs.k8s.io/docs/start/?arch=%2Fmacos%2Fx86-64%2Fstable%2Fbinary+download) used in this example
- Istio resources installed in the local cluster (use the [getting started doc](https://istio.io/latest/docs/setup/getting-started/#download))

```bash
# Install with istioctl and default profile
minikube start --driver=docker --cpus=10 --memory 8192 --disk-size 32g
minikube addons enable ingress
minikube addons enable metrics-server
istioctl install --set profile=default -y
minikube dashboard
```

- Both `prod.app.localhost` and `dev.app.localhost` defined in the host file

```bash
# Add these entries in the '/private/etc/hosts' file
127.0.0.1 prod.app.localhost
Expand All @@ -95,51 +103,73 @@ istioctl install --set profile=default -y
#### Steps

##### Deploy the production voting app
1. Follow [this to build and run the cli][run-build-cli]

1. Use the `kardinal` provided by the Nix shell (enter using `nix develop`) or follow [this to build and run the cli][run-build-cli]
2. Deploy `Kardinal Manager` in the local kubernetes cluster and set the `Kardinal Control` location (we are going to use the cloud version on these steps)

```bash
./kardinal manager deploy kloud-kontrol
kardinal manager deploy kloud-kontrol
```

3. Copy the tenant UUID generated while running this command

```bash
# This log line will be printed in the terminal, copy the generated UUID
INFO[0000] Using tenant UUID 58d33536-3c9e-4110-aa83-bf112ae94a49
```

3. Deploy the voting-app application with Kardinal

```bash
./kardinal deploy --docker-compose ../examples/voting-app/docker-compose.yaml
kardinal deploy --docker-compose ../examples/voting-app/docker-compose.yaml
```

4. Check the current topology in the cloud Kontrol FE using this URL: https://app.kardinal.dev/{use-your-tenant-UUID-here}/traffic-configuration
5. Open the [production page in the browser](http://prod.app.localhost/) to see the production `voting-app`
5. Start the tunnel to access the services (you may have to provide you password for the underlying sudo access)

```bash
minukube tunnel
```

6. Open the [production page in the browser](http://prod.app.localhost/) to see the production `voting-app`

##### Deploy the voting app development version in the same cluster

1. Create a new flow to test a development `voting-app-ui-v2` version in production

```bash
./kardinal flow create voting-app-ui voting-app-ui-v2 --docker-compose ../examples/voting-app/docker-compose.yaml
kardinal flow create voting-app-ui voting-app-ui-v2 --docker-compose ../examples/voting-app/docker-compose.yaml
```

2. Check how the topology has changed, to reflect both prod and the dev version, in the cloud Kontrol FE using this URL: https://app.kardinal.dev/{use-your-tenant-UUID-here}/traffic-configuration
3. Open the [development voting-app-ui-v2 page in the browser](http://dev.app.localhost/) to see the development `voting-app-ui-v2`

##### Remove the voting app development version from the same cluster

1. Remove the flow created for the `voting-app-ui-v2`

```bash
./kardinal flow delete --docker-compose ../examples/voting-app/docker-compose.yaml
kardinal flow delete --docker-compose ../examples/voting-app/docker-compose.yaml
```

2. Check the topology again to, it's showing only the production version as the beginning, in the cloud Kontrol FE using this URL: https://app.kardinal.dev/{use-your-tenant-UUID-here}/traffic-configuration
3. Open the [development voting-app-ui-v2 page in the browser](http://dev.app.localhost/) to check that it was successfully removed
4. Open the [production page in the browser](http://prod.app.localhost/) to check that it didn't change

##### Clean

1. Remove `Kardinal Manager` from the cluster

```bash
./kardinal manager remove
kardinal manager remove
```

2. Remove the `voting-app` application from the cluster

```bash
kubectl delete ns prod
```


## Development instructions

1. Enter the dev shell and start the local cluster:
Expand Down Expand Up @@ -247,4 +277,6 @@ gomod2nix generate
```
<!--------------- ONLY LINKS BELOW THIS POINT ---------------------->
[run-build-cli]: #running-kardinal-cli
[run-build-cli]: #running-kardinal-cli
35 changes: 35 additions & 0 deletions examples/voting-app/load-generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import requests
import time

# The URL to send the POST requests to
host = "prod.app.localhost"
url = "http://127.0.0.1/"

# Headers to be included in the POST requests
headers = {
"Origin": f"http://{host}",
"Host": host,
}

# Data to be sent in the POST requests
data_options = ["option1", "option2"]
data_index = 0


# Function to send a burst of 5 POST requests
def send_burst(data):
print(f"New burst of {data}")
for _ in range(5):
response = None
try:
response = requests.post(url, headers=headers, data={"vote": data})
print(f"Sent '{data}' - Response status code: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"Error sending '{data}' - {e}")


# Send bursts of 5 POST requests every 5 seconds, alternating between 'Cats' and 'Dogs'
while True:
send_burst(data_options[data_index])
data_index = (data_index + 1) % 2
time.sleep(5)
27 changes: 27 additions & 0 deletions examples/voting-app/shell.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{pkgs, ...}: let
pyEnv = pkgs.python3.buildEnv.override {
extraLibs = [pkgs.python3Packages.click pkgs.python3Packages.requests];
ignoreCollisions = true;
};

pname = "demo-load-generator";
demo-load-genarator = pkgs.stdenv.mkDerivation {
inherit pname;
version = "1.0.0";

src = ./.;

installPhase = ''
mkdir -p $out/bin
echo "#!${pyEnv}/bin/python3" > $out/bin/${pname}
cat load-generator.py >> $out/bin/${pname}
chmod +x $out/bin/${pname}
'';
};
in
pkgs.mkShell {
buildInputs = [
demo-load-genarator
pyEnv
];
}
12 changes: 12 additions & 0 deletions examples/voting-app/voting-app-ui/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
FROM python:3.9-slim

WORKDIR /app

COPY . .

RUN pip install Flask redis

EXPOSE 5000

CMD ["python", "app.py"]

14 changes: 14 additions & 0 deletions examples/voting-app/voting-app-ui/Dockerfile-v2
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM python:3.9-slim

WORKDIR /app

COPY . .

RUN pip install Flask redis

ENV APP_VERSION v2

EXPOSE 5000

CMD ["python", "app.py"]

94 changes: 94 additions & 0 deletions examples/voting-app/voting-app-ui/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
from flask import Flask, render_template, request, redirect, url_for
import redis
import os

app = Flask(__name__)

redis_server = os.environ["REDIS"]

# Initialize Redis
r = redis.Redis(host=redis_server, port=6379)

# Getting app version
if "APP_VERSION" in os.environ and os.environ["APP_VERSION"]:
app_version = os.environ["APP_VERSION"]
else:
app_version = "v1"

print("app_version is: " + app_version)

if "OPTION1" in os.environ and os.environ["OPTION1"]:
option1 = os.environ["OPTION1"]
else:
option1 = "Option 1"

if "OPTION2" in os.environ and os.environ["OPTION2"]:
option2 = os.environ["OPTION2"]
else:
option2 = "Option 2"

if "OPTION3" in os.environ and os.environ["OPTION3"] and app_version != "v1":
option3 = os.environ["OPTION3"]
elif app_version != "v1":
option3 = "Option 3"

if "TITLE" in os.environ and os.environ["TITLE"]:
title = os.environ["TITLE"]
else:
title = "Vote For Your Favorite Option"

# Set up initial vote counts
# TODO: implement this on redis proxy
if not r.exists("option1"):
r.set("option1", 0)
if not r.exists("option2"):
r.set("option2", 0)

if app_version == "v1":
if not r.exists("option3"):
r.set("option3", 0)


@app.route("/", methods=["GET", "POST"])
def index():
if request.method == "POST":
vote = request.form["vote"]
if vote == "option1":
r.incr("option1")
elif vote == "option2":
r.incr("option2")
elif vote == "option3" and app_version != "v1":
r.incr("option3")
return redirect(url_for("index"))

# Get current vote counts
option1_votes = int(r.get("option1") or 0)
option2_votes = int(r.get("option2") or 0)
if app_version != "v1":
option3_votes = int(r.get("option3") or 0)

if app_version != "v1":
return render_template(
"index.html",
option1_votes=option1_votes,
option2_votes=option2_votes,
option3_votes=option3_votes,
title=title,
option1=option1,
option2=option2,
option3=option3,
)
else:
return render_template(
"index.html",
option1_votes=option1_votes,
option2_votes=option2_votes,
title=title,
option1=option1,
option2=option2,
)



if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0", port=80)
Loading

0 comments on commit 54400fb

Please sign in to comment.