Skip to content
Open
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
41 changes: 41 additions & 0 deletions .github/workflows/build-push-image.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Build and Push Container Image

on:
push:
branches: [master, main]
workflow_dispatch:

jobs:
build-push:
name: Build and Push Image
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Determine image tags
id: tags
env:
GIT_SHA: ${{ github.sha }}
run: |
echo "tags=latest $GIT_SHA" >> "$GITHUB_OUTPUT"

- name: Build image
id: build
uses: redhat-actions/buildah-build@v2
with:
image: quay-load
context: .
tags: ${{ steps.tags.outputs.tags }}
containerfiles: |
./Dockerfile
platforms: linux/amd64

- name: Push image to Quay.io
uses: redhat-actions/push-to-registry@v2
with:
image: ${{ steps.build.outputs.image }}
tags: ${{ steps.build.outputs.tags }}
registry: quay.io/${{ vars.QUAY_IMAGE_NAMESPACE || 'projectquay' }}
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
29 changes: 10 additions & 19 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
FROM ubuntu
FROM registry.access.redhat.com/ubi9/python-311

LABEL maintainer="syahmed@redhat.com"

WORKDIR /tmp
ARG DEBIAN_FRONTEND=noninteractive
USER root

# Install necessary libraries for subsequent commands
RUN apt-get update && \
apt-get install -y software-properties-common python3.6 python3-venv python3-pip python3-apt wget git dumb-init podman skopeo redis-server

# Create and activate virtual environment
RUN python3 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# Install necessary packages
RUN dnf install -y --nodocs \
wget git podman skopeo fuse-overlayfs \
&& dnf clean all \
&& rm -rf /var/cache/dnf

# Install vegeta for HTTP benchmarking
RUN wget https://github.com/tsenart/vegeta/releases/download/v12.8.3/vegeta-12.8.3-linux-amd64.tar.gz \
Expand All @@ -23,18 +21,11 @@ RUN wget https://github.com/tsenart/vegeta/releases/download/v12.8.3/vegeta-12.8
RUN mkdir -p /opt/snafu/ \
&& wget -O /tmp/benchmark-wrapper.tar.gz https://github.com/cloud-bulldozer/benchmark-wrapper/archive/refs/tags/v1.0.0.tar.gz \
&& tar -xzf /tmp/benchmark-wrapper.tar.gz -C /opt/snafu/ --strip-components=1 \
&& pip install --upgrade pip \
&& pip install -e /opt/snafu/ \
&& pip install --upgrade pip "setuptools<71" \
&& pip install --no-build-isolation -e /opt/snafu/ \
&& pip install "numpy<2" \
&& rm -rf /tmp/benchmark-wrapper.tar.gz

COPY . .

# Cleanup the installation remainings
RUN apt-get clean autoclean && \
apt-get autoremove --yes && \
rm -rf /var/lib/{apt,dpkg,cache,log}/

# Start the command
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["python3", "main.py"]
ENTRYPOINT ["python3", "main.py"]
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The test suite is designed to run on openshift platform using a simple configura
permissions (checkboxes) granted. Hold on to this token as it will be used
later.
- Once after the quay application is deployed. Do a `pg_dump` in the quay postgres pod to capture the initial snapshot into a sql file and keep it copied at `assets/quaydb.sql`.
- An elasticsearch cluster is needed to store results. You can spin one using the [Operator](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-deploy-eck.html)
- (Optional) An elasticsearch cluster to store results. You can spin one using the [Operator](https://www.elastic.co/guide/en/cloud-on-k8s/current/k8s-deploy-eck.html). If ES is not available, results are written to local JSON files on a PVC.

## Running tests

Expand All @@ -45,15 +45,16 @@ Now once we have the system ready, Deploy `deploy/test.job.yaml` on your openshi
* `QUAY_HOST` - Sting. Indicating quay host url to perform testing.
* `QUAY_OAUTH_TOKEN` - String. Application oauth token created in the prerequisites step.
* `QUAY_ORG`- String. Specifies the test organization.
* `ES_HOST` - String. Elastic search host url.
* `ES_PORT` - String. Elastic search port number.
* `ES_INDEX` - String. Elastic search index to store the results.
* `ES_HOST` - String. (Optional) Elastic search host url. If not set, results are written to local JSON files only.
* `ES_PORT` - String. (Optional) Elastic search port number.
* `ES_INDEX` - String. (Optional) Elastic search index to store the results.
* `RESULTS_DIR` - String. (Optional) Directory to write local result JSON files. Defaults to `./results`.
* `SKIP_PUSH` - String. Flag to skip pushes true/false.
* `PULL_LAYERS` - String (Works with pull only). Images with n number of layers to be pulled.
* `PULL_REPO_PREFIX` - String (Works with pull only). Prefix of the existing pull repo that has [image tags in this format](https://quay.io/repository/clair-load-test/clair-load-test?tab=tags). One can use this script [image_load.sh](https://github.com/vishnuchalla/quay-performance-scripts/blob/master/assets/image_load.sh) to build and push images accordingly.
* `PUSH_PULL_IMAGE` - Image which contains source code and used in push/pull jobs for testing purposes. Same image that is used for load testing i.e `quay-load` in our case.
* `CUSTOM_BUILD_IMAGE` - String. Custom base image to be used for push/pull activities.
* `PUSH_PUSH_ES_INDEX` - ES index to store quay push/pull results. It is separate as it follows different document structure.
* `PUSH_PULL_ES_INDEX` - (Optional) ES index to store quay push/pull results. It is separate as it follows different document structure.
* `PUSH_PULL_NUMBERS` - The amount of images to do push/pull operations on.
* `TARGET_HIT_SIZE` - String. Indicates the total amount of requests to hit the system with.
* `CONCURRENCY` - String. Indicates the rate(concurrency) at which the requests hits must happen in parallel.
Expand All @@ -62,6 +63,27 @@ Now once we have the system ready, Deploy `deploy/test.job.yaml` on your openshi

This should spin up a redis pod and a test orchestrator pod in your desired namespace and start running the tests. Tail the pod logs for more info.

### Running without Elasticsearch

Elasticsearch is optional. When ES env vars (`ES_HOST`, `ES_PORT`, `ES_INDEX`, `PUSH_PULL_ES_INDEX`) are not set, all results are written to local JSON files instead. The default `deploy/test.job.yaml` includes a PVC (`quay-perf-results`, 1Gi) mounted at `/results` for persistent storage.

```bash
oc apply -f deploy/test.job.yaml -n <namespace>
```

After the test completes, retrieve results from the PVC:
```bash
oc run pvc-reader --image=busybox --restart=Never -n <namespace> \
--overrides='{"spec":{"containers":[{"name":"pvc-reader","image":"busybox","command":["sleep","3600"],"volumeMounts":[{"name":"results","mountPath":"/results"}]}],"volumes":[{"name":"results","persistentVolumeClaim":{"claimName":"quay-perf-results"}}]}}'
oc cp <namespace>/pvc-reader:/results ./results
oc delete pod pvc-reader -n <namespace>
```

Result files:
- `<uuid>_push_results.json` — per-image push timings with summary
- `<uuid>_pull_results.json` — per-image pull timings with summary
- `./logs/<uuid>_<test>_result.json` — Vegeta per-second timeseries (always written)

### More about tests

The tests use [Vegeta](https://github.com/tsenart/vegeta) to trigger the load and index the results to specified elastic search instance. The list of apis involved in each phase as as below:
Expand Down
13 changes: 5 additions & 8 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,15 @@ def get_config(self):
'quay_org': os.environ.get("QUAY_ORG"),
'test_uuid': os.environ.get('TEST_UUID'),
'auth_token': os.environ.get("QUAY_OAUTH_TOKEN"),
'es_host': os.environ.get('ES_HOST'),
'es_port': os.environ.get('ES_PORT'),
'es_index': os.environ.get('ES_INDEX'),
'es_host': os.environ.get('ES_HOST', ''),
'es_port': os.environ.get('ES_PORT', ''),
'es_index': os.environ.get('ES_INDEX', ''),
'push_pull_image': os.environ.get('PUSH_PULL_IMAGE'),
'custom_build_image': os.environ.get('CUSTOM_BUILD_IMAGE', ''),
'pull_layers': int(os.environ.get('PULL_LAYERS', 0)),
'pull_repo_prefix': os.environ.get('PULL_REPO_PREFIX', ''),
'push_pull_es_index': os.environ.get('PUSH_PULL_ES_INDEX'),
'push_pull_es_index': os.environ.get('PUSH_PULL_ES_INDEX', ''),
'results_directory': os.environ.get('RESULTS_DIR', './results'),
'push_pull_numbers': int(os.environ.get("PUSH_PULL_NUMBERS", 50)),
'concurrency': int(os.environ.get("CONCURRENCY", 50)),
'target_hit_size': int(os.environ.get('TARGET_HIT_SIZE')),
Expand All @@ -52,11 +53,7 @@ def validate_config(self):
assert self.config["quay_org"], "QUAY_ORG is not set"
assert self.config["test_uuid"], "TEST_UUID is not set"
assert self.config["auth_token"], "AUTH_TOKEN is not set"
assert self.config["es_host"], "ES_HOST is not set"
assert self.config["es_port"], "ES_PORT is not set"
assert self.config["es_index"], "ES_INDEX is not set"
assert self.config["push_pull_image"], "PUSH_PULL_IMAGE is not set"
assert self.config["push_pull_es_index"], "PUSH_PULL_INDEX is not set"
assert self.config["push_pull_numbers"], "PUSH_PULL_NUMBERS is not set"
assert isinstance(self.config["concurrency"], int), "CONCURRENCY is not an integer"
assert self.config["target_hit_size"], "TARGET_HIT_SIZE is not set"
Expand Down
30 changes: 25 additions & 5 deletions deploy/test.job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ metadata:
labels:
quay-perf-test-component: redis
spec:
replicas: 1
replicas: 1
selector:
matchLabels:
quay-perf-test-component: redis
Expand Down Expand Up @@ -80,14 +80,16 @@ spec:
value: <es-host>
- name: ES_PORT
value: <es-port>
- name: PYTHONUNBUFFERED
value: "0"
- name: ES_INDEX
value: <es-index>
- name: PUSH_PULL_IMAGE
value: <image-for-testing>
- name: PUSH_PULL_ES_INDEX
value: <push-pull-es-index>
- name: PYTHONUNBUFFERED
value: "0"
- name: RESULTS_DIR
value: "/results"
- name: PUSH_PULL_IMAGE
value: <image-for-testing>
- name: PUSH_PULL_NUMBERS
value: <number-of-push-pull-images>
- name: TARGET_HIT_SIZE
Expand All @@ -102,10 +104,28 @@ spec:
value: <test-namespace>
- name: TEST_PHASES
value: "LOAD,RUN,DELETE"
volumeMounts:
- name: test-results
mountPath: /results
resources:
requests:
cpu: "1"
memory: "512Mi"
imagePullPolicy: Always
volumes:
- name: test-results
persistentVolumeClaim:
claimName: quay-perf-results
restartPolicy: Never
backoffLimit: 0
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: quay-perf-results
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Loading