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

HPA example. Upgrade helm-application dependency #254

Merged
merged 5 commits into from
Oct 18, 2024
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
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ examples-clean:
rm -f examples/gwcStatefulSet/charts/*.tgz
rm -f examples/jdbc/charts/*.tgz
rm -f examples/pgconfig-acl/charts/*.tgz
rm -f examples/pgconfig-wms-hpa/charts/*.tgz
${HELM} uninstall gs-cloud-common || /bin/true
${HELM} uninstall gs-cloud-datadir || /bin/true
${HELM} uninstall gs-cloud-statefulset || /bin/true
${HELM} uninstall gs-cloud-jdbc || /bin/true
${HELM} uninstall gs-cloud-pgconfig-acl || /bin/true
${HELM} uninstall gs-cloud-pgconfig-wms-hpa || /bin/true


.PHONY: dependencies
Expand All @@ -26,11 +28,13 @@ gen-expected: dependencies
${HELM} dependency update examples/jdbc
${HELM} dependency update examples/pgconfig-acl
${HELM} dependency update examples/gwcStatefulSet
${HELM} dependency update examples/pgconfig-wms-hpa
${HELM} template --namespace=default gs-cloud-common examples/common > tests/expected-common.yaml
${HELM} template --namespace=default gs-cloud-datadir examples/datadir > tests/expected-datadir.yaml
${HELM} template --namespace=default gs-cloud-jdbc examples/jdbc > tests/expected-jdbc.yaml
${HELM} template --namespace=default gs-cloud-pgconfig-acl examples/pgconfig-acl > tests/expected-pgconfig-acl.yaml
${HELM} template --namespace=default gs-cloud-statefulset examples/gwcStatefulSet > tests/expected-statefulset.yaml
${HELM} template --namespace=default gs-cloud-pgconfig-wms-hpa examples/pgconfig-wms-hpa > tests/expected-pgconfig-wms-hpa.yaml
sed -i 's/[[:blank:]]\+$$//g' tests/expected*.yaml

.PHONY: example-common
Expand Down Expand Up @@ -62,3 +66,8 @@ example-common-no-nfs: dependencies
example-pgconfig-acl: example-common-no-nfs
${HELM} dependency update examples/pgconfig-acl
${HELM} upgrade --install gs-cloud-pgconfig-acl examples/pgconfig-acl

.PHONY: example-pgconfig-wms-hpa
example-pgconfig-wms-hpa: example-common-no-nfs
${HELM} dependency update examples/pgconfig-wms-hpa
${HELM} upgrade --install gs-cloud-pgconfig-wms-hpa examples/pgconfig-wms-hpa
15 changes: 15 additions & 0 deletions examples/pgconfig-wms-hpa/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v2
appVersion: '1.0'
description: Basic example with Horizontal Pod Autoscaling applied to WMS instances in geoserver-cloud using pgconfig as catalog persistence component
name: gs-cloud-hpa
version: 0.1.0
dependencies:
- name: geoservercloud
repository: file://../../
version: 0.1.0
# Postgres dependency used for pgconfig database
# Note: bitnami postgres chart is updated at initialization to add Postgis extension
- name: 'postgresql'
version: 14.0.0
repository: 'https://charts.bitnami.com/bitnami'
condition: postgresql.enabled
204 changes: 204 additions & 0 deletions examples/pgconfig-wms-hpa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# HPA Example

This example demonstrates how to deploy a simple **GeoserverCloud** setup in your local cluster, allowing you to evaluate and understand the behavior of **Horizontal Pod Autoscaling (HPA)**.

## Setup Overview

The deployment includes the following components:

- **WebUI instance**: Provides access to the catalog configuration.
- **Gateway**: Serves as the entry point to the GeoserverCloud solution.
- **Two WMS instances**: These initial instances serve the WMS OGC protocol.
- **REST API instance**: Utilized by a script to create a minimal catalog for testing.
- **Local PostgreSQL**: Used with the PgConfig profile to minimize startup time by focusing on efficient catalog reads.
- **RabbitMQ**: Manages event communication across instances.

By following the steps in the next section, you will observe how HPA automatically scales the deployment (up to 100 containers!) in response to stress generated by the provided script.

## Considerations

- Make sure to review the documentation in the `../README.md` file. A local Kubernetes cluster and `kubectl` are required to run this demo.
- Be aware that running these tests may overload or freeze your machine if your hardware or configuration is inadequate.
- If needed, adjust the **maxReplicas** value in the `values.yaml` file under the HPA section. The default value is set to 100.

## Steps to Run the Example

At the root level of the repository, follow these steps:

### 1. Execute the example command:

```shell
make example-pgconfig-wms-hpa
```

### 2. Verify that all pods are running:

```shell
kubectl get pods
```

Ensure all pods have the status `STATUS = Running` and `READY = 1/1`. Example output:

```shell
NAME READY STATUS RESTARTS AGE
gs-cloud-pgconfig-wms-hpa-gsc-gateway-76b46b9c7f-gs976 1/1 Running 0 12m
gs-cloud-pgconfig-wms-hpa-postgresql-0 1/1 Running 0 12m
gs-cloud-pgconfig-wms-hpa-gsc-rest-7fdbcf799f-qshn5 1/1 Running 0 12m
gs-cloud-pgconfig-wms-hpa-gsc-webui-6cf8f88695-646xt 1/1 Running 0 12m
gs-cloud-common-rabbitmq-0 1/1 Running 0 12m
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-qs946 1/1 Running 0 11m
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-pth59 1/1 Running 0 10m
```

### 3. Define a DNS alias:

This step prevents the use of local references in the scripts creating an entry like the following in `/etc/hosts`

```shell
192.168.208.2 gscloud.local
```

You can modify the scripts if needed.

```shell
kubectl get ingress --no-headers gs-cloud-pgconfig-wm-geoserver-host1 | \
awk '{printf("%s\t%s\n", $4, $3)}' | sudo tee -a /etc/hosts
```

### 4. Initialize the catalog:

Run the following command to create a test layer:

```shell
./examples/pgconfig-wms-hpa/init-catalog.sh
```

Should output:

```shell
------------------------------------
Preparing initialization process ...
------------------------------------
Creating workspace 'hpa-test'...
hpa-test
------------------------------------
Creating WMS datastore 'swisstopo_wms'...
swisstopo_wms
------------------------------------
Publishing layer 'ch.bafu.grundwasserkoerper' from datastore 'swisstopo_wms'...
ch.bafu.grundwasserkoerper
------------------------------------
Catalog initialized successfully.
```

### 5. Monitor the pods:

Use the following command to continuously monitor the pod status (refreshes every second).
This will let you see the number of `wms` pods increase during stress testing, and decrease back to one pod after a while.

```shell
watch -n 1 kubectl top pod -l app.kubernetes.io/component=wms --sort-by cpu
```

Initially it will output something like this, with the only wms pod running by default:

```shell
Every 1.0s: kubectl top pod -l app.kubernetes.io/component=wms --sort-by cpu

NAME CPU(cores) MEMORY(bytes)
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-2frh9 3m 533Mi
```

The `kubectl get hpa` command is also useful to monitor

```shell
watch -n 1 kubectl get hpa gs-cloud-pgconfig-wms-hpa-gsc-wms

Every 1.0s: kubectl get hpa gs-cloud-pgconfig-wms-hpa-gsc-wms
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
gs-cloud-pgconfig-wms-hpa-gsc-wms Deployment/gs-cloud-pgconfig-wms-hpa-gsc-wms 3%/70% 1 16 16 23m
```

### 6. Stress test the server:

> [!NOTE]
> The `stress-server.sh` script uses Apache Benchmark (`ab` command). If you don't have it, install it with
>
> ```shell
> sudo apt-get install apache2-utils
> ```
>
> or with your Operating System's package manager.

In a new terminal window, run the stress test script. It'll run for 60 seconds making 100 concurrent WMS requests to the cluster:

```shell
./examples/pgconfig-wms-hpa/stress-server.sh
```

You will observe in the first terminal how the number of pods scales up and down dynamically during the stress test.

For example, at some point the number of `wms` pods scaled to 4 here as the CPU utilization increased:

```shell
Every 1.0s: kubectl top pod -l app.kubernetes.io/component=wms --sort-by cpu

NAME CPU(cores) MEMORY(bytes)
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-h8nmg 1888m 818Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-xvr2r 1532m 867Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-9kp2g 1318m 845Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-nb2cc 1305m 891Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-9hhlb 1285m 882Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-zpk4d 1267m 860Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-wmdxw 1102m 1398Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-lrlv8 1027m 834Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-6nv2j 968m 826Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-wmvjh 901m 821Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-dtx4k 865m 1301Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-9s4fk 584m 1447Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-gxbj4 460m 985Mi
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-2frh9 367m 953Mi
```

And with `kubectl get hpa` will observe the status of the **HorizontalPodAutoscaler**, similar to:

```shell
watch -n 1 kubectl get hpa gs-cloud-pgconfig-wms-hpa-gsc-wms

Every 1.0s: kubectl get hpa gs-cloud-pgconfig-wms-hpa-gsc-wms

NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
gs-cloud-pgconfig-wms-hpa-gsc-wms Deployment/gs-cloud-pgconfig-wms-hpa-gsc-wms 941%/70% 1 16 14 4m28s
```

Here, CPU consumption has increased to 941% out of the 70% average utilization configured. As a result, the Deployment was resized to 14 replicas:

```shell
kubectl get deployment gs-cloud-pgconfig-wms-hpa-gsc-wms
NAME READY UP-TO-DATE AVAILABLE AGE
gs-cloud-pgconfig-wms-hpa-gsc-wms 14/14 14 14 9m23s
```

And after a couple minutes the replicas start to ramp down until reaching one:

```shell
kubectl get deployment gs-cloud-pgconfig-wms-hpa-gsc-wms
NAME READY UP-TO-DATE AVAILABLE AGE
gs-cloud-pgconfig-wms-hpa-gsc-wms 1/1 1 1 9m25s

kubectl top pod -l app.kubernetes.io/component=wms --sort-by cpu
NAME CPU(cores) MEMORY(bytes)
gs-cloud-pgconfig-wms-hpa-gsc-wms-758dfd8765-2frh9 4m 971Mi

kubectl get hpa gs-cloud-pgconfig-wms-hpa-gsc-wms
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
gs-cloud-pgconfig-wms-hpa-gsc-wms Deployment/gs-cloud-pgconfig-wms-hpa-gsc-wms 3%/70% 1 16 1 15m
```

### 7. Clean up the environment:

To remove the deployment and restore your environment, run:

```shell
make examples-clean
```
38 changes: 38 additions & 0 deletions examples/pgconfig-wms-hpa/init-catalog.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/bash

GEOSERVER_URL="http://gscloud.local/geoserver-cloud"
USER="admin"
PASSWORD="geoserver"

WORKSPACE="hpa-test"
DATASTORE="swisstopo_wms"
WMS_URL="https://wms.geo.admin.ch/?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetCapabilities"
LAYER_NAME="ch.bafu.grundwasserkoerper"

echo -e "\n------------------------------------"
echo -e "Preparing initialization process ..."
curl -u $USER:$PASSWORD -X DELETE "$GEOSERVER_URL/rest/workspaces/$WORKSPACE?recurse=true"

echo -e "------------------------------------"
echo -e "Creating workspace '$WORKSPACE'..."
if ! curl -u $USER:$PASSWORD -X POST -H "Content-Type: text/xml" -d "<workspace><name>$WORKSPACE</name></workspace>" $GEOSERVER_URL/rest/workspaces --fail; then
echo -e "Error creating workspace\n"
exit 1
fi

echo -e "\n------------------------------------"
echo -e "Creating WMS datastore '$DATASTORE'..."
if ! curl -u $USER:$PASSWORD -X POST -H "Content-Type: text/xml" -d "<wmsStore><type>WMS</type><name>$DATASTORE</name><workspace>$WORKSPACE</workspace><capabilitiesURL>$WMS_URL</capabilitiesURL><enabled>true</enabled></wmsStore>" $GEOSERVER_URL/rest/workspaces/$WORKSPACE/wmsstores --fail; then
echo -e "\nError creating datastore"
exit 1
fi

echo -e "\n------------------------------------"
echo -e "Publishing layer '$LAYER_NAME' from datastore '$DATASTORE'..."
if ! curl -u $USER:$PASSWORD -X POST -H "Content-Type: text/xml" -d "<wmsLayer><name>$LAYER_NAME</name><defaultStyle><name>raster</name></defaultStyle></wmsLayer>" $GEOSERVER_URL/rest/workspaces/$WORKSPACE/wmsstores/$DATASTORE/wmslayers --fail; then
echo -e "Error publishing layer\n"
exit 1
fi

echo -e "\n------------------------------------"
echo -e "Catalog initialized successfully.\n"
5 changes: 5 additions & 0 deletions examples/pgconfig-wms-hpa/stress-server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

WMS_URL="http://gscloud.local/geoserver-cloud/wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&STYLES&LAYERS=hpa-test%3Ach.bafu.grundwasserkoerper&exceptions=application%2Fvnd.ogc.se_inimage&SRS=EPSG%3A2056&WIDTH=769&HEIGHT=359&BBOX=2628297.2396917907%2C1161127.5666655225%2C2745623.985655881%2C1215846.1146757442"

ab -Sdl -t 120 -c 64 "$WMS_URL"
Loading