Helm charts for wger deployment on Kubernetes.
If you know what you are doing, you can go ahead and run these commands to install wger. Otherwise, keep on reading!
helm repo add github-wger https://wger-project.github.io/helm-charts
helm upgrade \
--install wger github-wger/wger \
--version 0.1.5 \
-n wger \
--create-namespace
This will install the chart with the defaults, stated in values.yaml.
This chart bootstraps a wger deployment on a Kubernetes cluster using the Helm package manager, alongside with a PostgreSQL for a database and Redis as a caching service.
For a more productive environment you have to enable nginx as a reverse proxy. This will enable gunicorn in the wger image and will require persistent storages for at least django's media and static files.
- Kubernetes 1.15+
- Helm 3.0+
- PV infrastructure on the cluster if persistence is needed (recommended)
- Ingress infrastructure for exposing the installation
You can install the chart by adding our helm repository and then installing it normally via helm upgrade.
helm repo add github-wger https://wger-project.github.io/helm-charts
helm upgrade \
--install wger github-wger/wger \
--version 0.1.5 \
-n wger \
--create-namespace
-f values.yaml
First you may want to make a copy of values.yaml and modify it for your needs.
Please see the parameters section.
The following table contains the configuration parameters of the chart with their default values. For additional configuration of the Groundhog2k's PostgreSQL and Redis charts, please check the additional information.
Name | Description | Type | Default Value |
---|---|---|---|
app.global.image.registry |
Image to use for the wger deployment | String | docker.io |
app.global.image.repository |
Image to use for the wger deployment | String | wger/server |
app.global.image.tag |
Takes the Chart.yaml appversion when empty. wger is developed as a rolling release |
String | latest |
app.global.image.PullPolicy |
Pull policy to use for the image | String | Always |
app.global.annotations |
Annotations to attach to each resource, apart from the ingress and the persistence objects | Dictionary | {} |
app.global.replicas |
Number of webserver instances that should be running. | Integer | 1 |
app.global.securityContext |
Pod security context | Object | see values.yaml |
Name | Description | Type | Default Value |
---|---|---|---|
app.django.secret.name |
Name of the secret | String | django |
app.django.secret.key |
Key for the SECRET_KEY |
String | randAlphaNum 50 |
app.django.cache.timeout |
Cache timeout in seconds | String | 1296000 |
Celery requires persistent volumes.
Name | Description | Type | Default Value |
---|---|---|---|
celery.enabled |
Enable celery for sync | Boolean | True |
celery.annotations |
Annotations | Dictionary | {} |
celery.replicas |
Enable celery for sync | Integer | 1 |
celery.replicasWorker |
Enable celery for sync | Integer | 1 |
celery.securityContext |
Pod security context | Object | see values.yaml |
celery.syncExercises |
sync exercises | Boolean | True |
celery.syncImages |
sync exercise images | Boolean | True |
celery.syncVideos |
sync exercise videos | Boolean | True |
celery.ingredientsFrom |
source for ingredients, possible values WGER ,OFF |
String | WGER |
celery.flower.enabled |
enable flower webinterface for celery | Boolean | False |
celery.flower.secret.name |
Name of the secret | String | flower |
celery.flower.secret.password |
Password for the webinterface | String | randAlphaNum 50 |
Name | Description | Type | Default Value |
---|---|---|---|
app.jwt.secret.name |
Name of the secret | String | jwt |
app.jwt.secret.key |
Key for the SIGNING_KEY |
String | randAlphaNum 50 |
app.jwt.accessTokenLifetime |
Duration of the access token, in minutes | String | 10 |
app.jwt.refreshTokenLifetime |
Duration of the refresh token, in hours | String | 24 |
Name | Description | Type | Default Value |
---|---|---|---|
app.axes.enabled |
Enable axes Bruteforce protection | Boolean | false |
app.axes.lockoutParameters |
List (comma separated string) | "ip_address" |
|
app.axes.failureLimit |
Limit of failed auth | String | 10 |
app.axes.cooloffTime |
in Minutes | String | 30 |
app.axes.ipwareProxyCount |
Count of proxies | String | 0 |
app.axes.ipwareMetaPrecedenceOrder |
Proxy header magnitude | List (comma separated string) | "HTTP_X_FORWARDED_FOR,REMOTE_ADDR" |
Name | Description | Type | Default Value |
---|---|---|---|
app.nginx.enabled |
Enable nginx as a proxy. This will enable persistent volumes, gunicorn and disable DJANGO_DEBUG |
Boolean | false |
app.nginx.image |
Image to use for the nginx proxy | String | nginx:stable |
app.nginx.imagePullPolicy |
Pull policy to use for the image | String | IfNotPresent |
Name | Description | Type | Default Value |
---|---|---|---|
ingress.enabled |
Whether to enable ingress. If false , the options from below are ignored |
Boolean | false |
ingress.ingressClassName |
The ClassName that this ingress should use | String | `` |
ingress.url |
The URL that this ingress should use | String | fit.example.com |
ingress.tls |
Whether to enable TLS. If using cert-manager, the correct annotations have to be set | Boolean | true |
ingress.annotations |
Annotations to attach to the ingress | Dictionary | {} |
Name | Description | Type | Default Value |
---|---|---|---|
service.type |
Sets the http service type, valid values are NodePort , ClusterIP or LoadBalancer . |
String | ClusterIP |
service.port |
Port for the service | Integer | 8000 |
service.annotations |
Annotations to attach to the service | Dictionary | {} |
Name | Description | Type | Default Value |
---|---|---|---|
app.persistence.enabled |
Whether to enable persistent storage. If false , the options from below are ignored |
Boolean | false |
app.persistence.existingClaim.media |
Name of the pvc for the media data when existingClaim is enabled | String | null |
app.persistence.existingClaim.static |
Name of the pvc for the static data when existingClaim is enabled | String | null |
app.persistence.existingClaim.enabled |
Whether to use a existing persistent storage claim. If false , the options from below are ignored |
Boolean | false |
app.persistence.storageClass |
StorageClass for the PVCs | String | "" |
app.persistence.accessModes |
Access modes for the PVCs | Array | ["ReadWriteMany"] |
app.persistence.size |
PVC size | String | 8Gi |
app.persistence.annotations |
Annotations to attach to the persistence objects (PVC and PV) | Dictionary | {} |
app.persistence.enabled |
Whether to enable persistent storage. If false , the options from below are ignored |
Boolean | false |
Name | Description | Type | Default Value |
---|---|---|---|
app.resources.requests.memory |
Amount of memory that the app requests for running. Keep this value low to allow the pod to get admitted on a node. | String | 128Mi |
app.resources.requests.cpu |
Amount of CPU that the app requests for running. Keep this value low to allow the pod to get admitted on a node. | String | 100m |
app.resources.limits.memory |
Maximum amount of memory that the app is allowed to use. | String | 512Mi |
app.resources.limits.cpu |
Maximum amount of CPU that the app is allowed to use. | String | 500m |
Name | Description | Type | Default Value |
---|---|---|---|
app.environment |
Array of objects, representing additional environment variables to set for the deployment. | Array | see _helpers.yaml and values.yaml |
There are more possible ENV variables, than the ones used in the deployment. Please check prod.env.
The following settings are declared in the groundhog2k Helm charts.
wger-app requires for the django database migrations the superuser privileges, so we grant the postgres.userDatabase.name
SUPERUSER
with a postgres.extraScripts
.
Name | Description | Type | Default Value |
---|---|---|---|
postgres.enabled |
Enable the PostgreSQL chart | Boolean | True |
postgres.settings.superuser |
Superuser name | String | postgres |
postgres.settings.superuserPassword |
Password of superuser | String | postgres |
postgres.userDatabase.name |
Database name to use for wger | String | wger |
postgres.userDatabase.user |
Username to use for wger | String | wger |
postgres.userDatabase.password |
Password for wger user | String | wger |
postgres.extraScripts |
A configmap used to grant privileges | String | wger-pg-init |
postgres.service.port |
PostreSQL service port | Integer | 5432 |
postgres.storage.persistentVolumeClaimName |
PVC name when existing storage volume should be used | String | Nil |
postgres.storage.requestedSize |
Size for new PVC, when no existing PVC is used | Integer | 8Gi |
postgres.storage.className |
Storage class name when no existing storage used, takes the cluster default when Nil |
String | Nil |
Name | Description | Type | Default Value |
---|---|---|---|
redis.enabled |
Enable the redis chart | Boolean | true |
redis.auth.enabled |
Whether to enable redis login. | Boolean | false |
redis.auth.password |
Password for redis login. Not required if redis.auth.enabled is false |
String | randAlphaNum 25 |
redis.service.serverPort |
Redis server service port | Integer | 6379 |
redis.storage.persistentVolumeClaimName |
PVC name when existing storage volume should be used | String | Nil |
redis.storage.requestedSize |
Size for new PVC, when no existing PVC is used | String | Nil |
redis.storage.className |
Storage class name when no existing storage used, takes the cluster default when Nil |
String | Nil |
Celery is used to sync exercises or ingredients. The user for the flower webinterface is wger
.
export POD=$(kubectl get pods -n wger -l "app.kubernetes.io/name=wger-celery-worker" -o jsonpath="{.items[0].metadata.name}")
kubectl -n wger exec -ti $POD -- bash
celery -A wger events
celery events
is a simple curses monitor displaying task and worker history.
If you have enabled flower you can, for example use port forwarding to connect to the web interface.
export POD=$(kubectl get pods -n wger -l "app.kubernetes.io/name=wger-celery-worker" -o jsonpath="{.items[0].metadata.name}")
kubectl -n wger port-forward ${POD} 8080:5555
Open the browser to http://localhost:8080
Get the password for the flower webinterface:
kubectl -n wger get secret flower -o jsonpath='{.data.password}' | base64 -d
Bruteforce protection. Depending on your setup, you may need to configure axes to your proxy setup otherwise it will block by default the IP which can be your reverse proxy.
- https://django-axes.readthedocs.io/en/latest/4_configuration.html#configuring-reverse-proxies
- https://django-axes.readthedocs.io/en/latest/5_customization.html#customizing-lockout-parameters
python3 manage.py axes_reset
python3 manage.py axes_reset_ip [IP]
python3 manage.py axes_reset_username [USERNAME]
To temporary disable privacy mode to see the blocked ip in the log you can login to the container and add the following setting:
echo "AXES_SENSITIVE_PARAMETERS = []" >>settings.py
wger is developped in a rolling release manner, so the docker image of the release is :latest
, the hightest version tag :X.x-dev
is the same as the :latest
image. Older version tags are not changed or "bugfixed".
This means we cannot upgrade with changing the image tag.
As a consequence the default values.yaml
has set imagePullPolicy
to Always
, this means if the kubelet has a container image with that exact digest cached locally, the kubelet uses its cached image; otherwise, the kubelet pulls the image with the resolved digest, and uses that image to launch the container.
To upgrade you can restart the deployment (k8s v1.15):
kubectl -n wger rollout restart deploy wger-app wger-celery wger-celery-worker
For PostgreSQL and Redis upgrades, please check the Groundhog2k documentation, linked at the end.
It is sadly not possible to automatically upgrade between postgres versions, you need to perform the upgrade manually. Since the amount of data the app generates is small a simple dump and restore is the simplest way to do this.
If you pulled new changes from this repo and got the error message "The data directory was initialized by PostgreSQL version 12, which is not compatible with this version 15." this is for you.
See also docker-library/postgres#37
The following requires a persistent storage for the postgresql database.
Before doing the upgrade, go inside the container and dump the database:
export POD=$(kubectl get pods -n wger -l "app.kubernetes.io/name=postgres" -o jsonpath="{.items[0].metadata.name}")
kubectl -n wger exec -ti $POD -c postgres -- bash
pg_dumpall --clean --username wger -f /var/lib/postgresql/data/dump.sql
If you however missed that, you need to know which postgres version you where running before, stop the current postgres and wger app.
# stop the current wger deployment
kubectl -n wger scale --replicas=0 deploy wger-app
# stop the postgres sts
kubectl -n wger scale --replicas=0 sts wger-postgres
Create a job dumping the database job-dump.yaml
, fill in the postgres version you where running:
apiVersion: batch/v1
kind: Job
metadata:
name: dbdump
spec:
template:
spec:
containers:
- name: dbdump
image: postgres:14
lifecycle:
postStart:
exec:
command: ["/bin/bash", "-c", "until `pg_dumpall --clean --username wger -f /var/lib/postgresql/data/dump.sql && runuser -u postgres -- pg_ctl stop`; do sleep 2; done"]
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: wger-db
env:
- name: PGDATA
value: "/var/lib/postgresql/data/pg"
- name: POSTGRES_HOST_AUTH_METHOD
value: trust
volumes:
- name: wger-db
persistentVolumeClaim:
claimName: wger-db
restartPolicy: Never
backoffLimit: 4
kubectl -n wger apply -f job-dump.yaml
Now move away the current db in your storage, so that the new postges image will create a new one -> this needs to be done accessing your storage from outside the cluster or add it to the command
in the job-dump.yaml
:
# move the old database -> can be removed after the upgrade was successful
mv /var/lib/postgresql/data/pg /var/lib/postgresql/data/pg-$(date +%Y-%m-%d)
Upgrade wger chart, but disable the wger django app, so that the database will not be created, for this you can temporary set the app replicas to 0
in your values.yaml
:
app:
global:
replicas: 0
helm upgrade \
--install wger github-wger/wger \
--version 0.1.5 \
-n wger \
--create-namespace
-f values.yaml
Now you should have running the new postgres version. Go inside the new container and import the database dump with:
cat /var/lib/postgresql/data/dump.sql | psql --username wger --dbname wger
Also reset the database password to the one you used, the default is wger
:
psql --username wger --dbname wger -c "ALTER USER wger WITH PASSWORD 'wger'"
Start the wger app, don't forget to set back the replicas in your values.yaml
as well:
kubectl -n wger scale --replicas=1 deploy wger-app
To uninstall remove the helm release we called wger
during installation:
helm -n wger delete wger
Please check the contributing guidelines of the wger project.
Generally:
- if you have a problem, create an issue in the issue tracker
- if you have a cool idea, create a fork and send pull requests
- assure that your code is well-formed (hint:
helm lint
is a useful command). This is enforced using continuous integration.
The deployment can be scaled using app.global.replicas
to allow for more web server replicas. Persistence should be enabled as well to ensure that the different webservers have access to the same static and media shares.
Generally persistent volumes needs to be configured depending on your setup.
Please have a look at DEVEL.md.
Feel free to contact us if you found this useful or if there was something that didn't behave as you expected. We can't fix what we don't know about, so please report liberally. If you're not sure if something is a bug or not, feel free to file a bug anyway.
- discord: https://discord.gg/rPWFv6W
- issue tracker: https://github.com/wger-project/helm-charts/issues
- twitter: https://twitter.com/wger_project