This directory contains the complete Flux configuration for the WProofreader stack.
Apply root.yaml one time.
Flux then installs the Gateway API CRDs, cert-manager, Traefik, MySQL, WProofreader Server,
and Admin-panel in dependency order.
It reads the product charts from wproofreader-helm, admin-panel-helm, and mysql-server-helm,
and the values files from this repository.
The Kubernetes installation guide
installs the same charts with Helm commands.
It uses the same namespace (wsc), release names, and Secrets.
| Component | Version |
|---|---|
| Kubernetes | 1.33 or later (Flux 2.9 needs 1.33; the Gateway API CRDs need 1.31) |
| Flux | 2.9 |
| Gateway API CRDs | v1.6.1, standard channel |
| cert-manager | v1.21.1 |
| Traefik chart | 41.5.0 (Traefik 3.7) |
| mysql-server-helm | chart 1.0.0 or later, MySQL 8.4 |
| wproofreader-helm | chart 1.4.0 or later (WProofreader Server 6.18.1.0 or later) |
| admin-panel-helm | chart 1.0.0 or later (Admin-panel 3.0.0 or later) |
The product charts are pinned to release tags in sources/charts.yaml.
See Pinned versions.
flux/
├── root.yaml # the ONE file you apply: GitRepository `wproofreader-gitops` + root Kustomization `wproofreader-root`
├── kustomizeconfig.yaml # lets kustomize change generated ConfigMap names in HelmRelease.valuesFrom
├── clusters/
│ └── local/ # what the root syncs: one Flux Kustomization for each layer (dependsOn sets the order)
│ ├── sources.yaml -> flux/sources
│ ├── shared.yaml gateway-api-crds, cert-manager, traefik, cluster-resources
│ └── demo-wproofreader-stack.yaml -> flux/environments/demo (one file for each environment)
├── sources/ # GitRepository objects for the 3 product charts (pinned release tags) and Gateway API v1.6.1,
│ # HelmRepository objects for jetstack and traefik
├── shared/
│ ├── cert-manager/ # Namespace, HelmRelease v1.21.1, values.yaml (in a generated ConfigMap)
│ ├── traefik/ # Namespace, HelmRelease 41.5.0 (crds: Skip), values.yaml
│ └── cluster-resources/ # self-signed ClusterIssuer
└── environments/ # one directory for each environment = one complete stack
└── demo/
├── kustomization.yaml # namespace, the 3 HelmReleases, and 3 generated values ConfigMaps
├── mysql.yaml # HelmReleases (order: mysql -> wproofreader-app -> admin-panel)
├── wproofreader-app.yaml
├── admin-panel.yaml
└── values/ # plain Helm values files (also for `helm install --values`)
├── mysql.yaml
├── wproofreader-app.yaml
└── admin-panel.yaml # host name, Gateway, TLS, and /wscservice routing
All Kustomization paths start at the repository root (./flux/...).
A HelmRelease reads values from ConfigMaps or Secrets (valuesFrom).
It cannot read a values file from Git directly.
Thus, each kustomization puts its values files into generated ConfigMaps:
configMapGenerator:
- name: admin-panel-values
files:
- values.yaml=values/admin-panel.yaml
configurations:
- ../../kustomizeconfig.yamlKustomize adds a hash of the content to the ConfigMap name.
kustomizeconfig.yaml tells kustomize to use the same name in HelmRelease.spec.valuesFrom.
When you change a values file, the ConfigMap gets a new name,
and the HelmRelease reconciles. helm-controller upgrades the release only if
the values are different.
A change to a comment does not restart the workloads.
To see the manifests that kustomize-controller applies, run flux build:
flux build kustomization demo-wproofreader-stack \
--kustomization-file flux/clusters/local/demo-wproofreader-stack.yaml \
--path ./flux/environments/demo --dry-runFlux deploys the stack in two levels:
- Flux Kustomizations, one for each layer, in
clusters/local/. - HelmReleases of an environment, in
environments/<environment>/.
On both levels, dependsOn sets the order.
An object starts only when all objects in its dependsOn list are ready.
flowchart LR
subgraph layers["Flux Kustomizations (clusters/local/)"]
sources --> gwcrds[gateway-api-crds]
sources --> certmanager[cert-manager]
gwcrds --> traefik
certmanager --> clusterres[cluster-resources]
traefik --> stack[demo-wproofreader-stack]
clusterres --> stack
end
subgraph releases["HelmReleases (environments/demo/)"]
mysql --> wpr[wproofreader-app]
mysql --> ap[admin-panel]
wpr --> ap
end
stack -. applies .-> releases
| Layer | Installs | Starts after | Is ready when |
|---|---|---|---|
sources |
GitRepository and HelmRepository objects | (first layer) | Flux has downloaded each chart source |
gateway-api-crds |
Gateway API CRDs v1.6.1 | sources |
the CRDs are established |
cert-manager |
cert-manager and its CRDs | sources |
the cert-manager Pods and webhook are ready |
traefik |
Traefik and the traefik GatewayClass |
gateway-api-crds |
the Traefik Pod is ready |
cluster-resources |
the selfsigned ClusterIssuer |
cert-manager |
the ClusterIssuer is ready |
demo-wproofreader-stack |
the three HelmReleases of the environment | traefik and cluster-resources |
all three HelmReleases are ready |
Inside demo-wproofreader-stack, the HelmReleases start in this order:
| HelmRelease | Starts after | What happens |
|---|---|---|
mysql |
(first) | MySQL starts and creates admin_panel_db and the admin_panel user. |
wproofreader-app |
mysql |
The db-manager hook Job creates cloud_service and its users. Then WProofreader Server starts. |
admin-panel |
mysql and wproofreader-app |
The migration hook Job creates the tables in admin_panel_db. Then the web, worker, and scheduler Pods start. |
Each Kustomization has wait: true.
Thus, a layer is ready only when all objects that it applied are healthy.
Each HelmRelease waits until Helm reports the release as ready. helm-controller runs helm install
and helm upgrade, so the db-manager Job and the migration Job run as usual Helm
pre-install,pre-upgrade hooks.
If a layer is not ready, the layers after it do not start. To find the layer that blocks the deployment, run:
flux get kustomizations
flux get helmreleases --all-namespacesThis procedure deploys the demo environment from this repository, without changes.
Use it on a test cluster to see how the stack starts and how Flux shows it.
To deploy your own configuration, first make your own copy of the repository. See Make your own copy. Then use the same steps with your copy.
kubectl, thefluxCLI 2.9,openssl, andgit.- A WProofreader license ticket ID.
- A Kubernetes cluster. See step 1.
You can use any Kubernetes cluster that meets these requirements:
- Kubernetes 1.33 or later.
flux check --prestops on earlier versions. - A LoadBalancer implementation.
The Traefik Service must get an external address.
If it stays
<pending>, the stack deploys, but you cannot reach it from outside the cluster. - A default StorageClass. MySQL keeps its data on an 8 GiB volume.
- Approximately 6 CPUs and 12 GiB of memory that are free for the stack.
Make sure that kubectl uses this cluster:
kubectl config current-context
kubectl versionThe server version must be 1.33 or later.
These examples create a local test cluster. Use one of them, or use a cluster that you already have.
minikube
minikube start --cpus=6 --memory=12gminikube 1.36 or later starts Kubernetes 1.33 or later by default.
For an older minikube, add --kubernetes-version=v1.33.1.
In a second terminal, start the tunnel and keep it running. It gives LoadBalancer addresses to Services:
minikube tunnelThe tunnel can ask for your password, because it opens ports 80 and 443.
kind
kind create cluster --name wproofreader --image kindest/node:v1.33.1In a second terminal, start cloud-provider-kind and keep it running. It gives LoadBalancer addresses to Services:
sudo cloud-provider-kindGive Docker approximately 6 CPUs and 12 GiB of memory.
Managed cluster (EKS, AKS, GKE, or other)
A managed cluster usually has a LoadBalancer implementation and a default StorageClass. Make sure that the Kubernetes version is 1.33 or later. A LoadBalancer Service can cost money and can be reachable from the internet. Remove the stack when you complete the test.
Clone this repository:
git clone https://github.com/WebSpellChecker/wproofreader-gitops.git
cd wproofreader-gitopsflux check --pre
flux install
flux checkflux install installs the controllers of the same version as the CLI into
the flux-system namespace.
Skip this step if Flux can read the repositories without credentials. If you use private copies of the repositories, create a Secret with a read-only token. Replace the placeholders:
flux create secret git github-creds \
--url=https://github.com/<organization>/wproofreader-gitops \
--username='<user>' --password='<read_only_token>'Then add this field to spec of each GitRepository that reads a private repository,
in root.yaml and sources/charts.yaml:
secretRef:
name: github-credsCreate the namespace and the four Secrets before the first reconciliation.
Flux reads these Secrets, but it does not create or delete them.
Use the commands in the repository README.
They create the wsc namespace and the Secrets mysql-credentials, wproofreader-db,
wproofreader-license, and admin-panel-secrets.
kubectl apply -f flux/root.yamlThe root Kustomization wproofreader-root syncs flux/clusters/local/.
That directory has one Flux Kustomization for each layer.
sources is first.
Then gateway-api-crds and cert-manager start, then traefik and cluster-resources.
The last layer, demo-wproofreader-stack, applies the three HelmReleases
in flux/environments/demo/.
If flux bootstrap already manages this repository, do not apply root.yaml.
Change sourceRef.name: wproofreader-gitops to flux-system in the files in clusters/local/,
and set the bootstrap --path to flux/clusters/local.
flux get kustomizations --watch
flux get helmreleases --all-namespaces --watchA Kustomization shows dependency ... is not ready until its dependencies are ready.
The first reconciliation takes approximately 10 minutes.
Most of the time is for image downloads.
In demo-wproofreader-stack, mysql installs first.
wproofreader-app waits for mysql, and admin-panel waits for mysql and wproofreader-app.
You can monitor the Pods in the wsc namespace, also the Pods of the hook Jobs:
kubectl -n wsc get pods --watchThe wproofreader-app-db-provision Job completes before the WProofreader Server Pod starts.
The admin-panel-migrate Job completes before the Admin-panel web, worker,
and scheduler Pods start.
wproofreader-root becomes ready when all layers are ready.
To reconcile immediately, run flux reconcile kustomization wproofreader-root --with-source.
To show all objects, run flux tree kustomization wproofreader-root.
Get the external address of Traefik:
LB_IP="$(kubectl -n traefik get service traefik \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')"
echo "${LB_IP}"Check the Gateway, the routes, and the certificate:
kubectl -n wsc get gateway,httproute,certificateThe Gateway shows PROGRAMMED True, and the certificate shows READY True.
Send requests to Admin-panel and WProofreader Server.
wproofreader.example.com is the host name of the demo environment.
If you changed the host name in your copy, use your host name in this step and in step 8.
The --resolve option sends the host name to the Traefik address, so you do not need a DNS record.
If a DNS record for your host name already points to the Traefik address,
you can remove --resolve:
H=wproofreader.example.com
curl --silent --output /dev/null --write-out '%{http_code}\n' \
--resolve "${H}:80:${LB_IP}" "http://${H}/up"
curl --silent --insecure --output /dev/null --write-out '%{http_code}\n' \
--resolve "${H}:443:${LB_IP}" "https://${H}/up"
curl --silent --insecure --resolve "${H}:443:${LB_IP}" \
"https://${H}/wscservice/api?cmd=ver"
curl --silent --insecure --resolve "${H}:443:${LB_IP}" \
"https://${H}/wscservice/api?cmd=license_status"The first command shows 301 (redirect to HTTPS).
The second command shows 200.
The third response contains "ProgramVersion" and the WProofreader Server version,
and the fourth response contains "valid":true.
To open Admin-panel in a web browser, add this line to /etc/hosts.
Replace <LB_IP> with the address from step 7:
<LB_IP> wproofreader.example.com
If a DNS record for your host name already points to the Traefik address, skip this change.
Get a setup link:
kubectl -n wsc exec deployment/admin-panel-web -- \
php artisan app:setup-token --regenerate --no-ansiOpen the setup URL from the output. The certificate is self-signed, so the browser shows a warning. Create the administrator, then sign in. The demo writes email to the log. To get invitation links, read the log of the web Pod:
kubectl -n wsc logs deployment/admin-panel-webCopy the demo environment and its cluster file:
cp -r flux/environments/demo flux/environments/production
cp flux/clusters/local/demo-wproofreader-stack.yaml \
flux/clusters/local/production-wproofreader-stack.yaml- Set
namespace:inenvironments/production/kustomization.yaml. - Change the values files in
environments/production/values/: host name, issuer, storage sizes, and resources. Also change the namespace in these addresses:config.db.host,config.serviceDb.host, andconfig.appServerInternalUrlinadmin-panel.yaml, anddatabase.hostinwproofreader-app.yaml. Do not change the release names. The addressesmysql-primary.<namespace>andwproofreader-app.<namespace>use them. - In the new cluster file, set
metadata.name: production-wproofreader-stackandpath: ./flux/environments/production. Add the file toclusters/local/kustomization.yaml. - Create the namespace and its Secrets. The HelmReleases do not create the namespace.
- Commit and push.
Flux does not create a stack from a directory automatically.
Each environment needs its cluster file.
For a second cluster, copy clusters/local/ and keep only the environments of that cluster.
The product charts are pinned to release tags in sources/charts.yaml with ref.tag.
Change the tag, then commit and push.
The tag must exist in the remote chart repository.
If the release moves the chart to a different directory, also change chart: in
the HelmRelease in environments/<environment>/.
The HelmReleases use reconcileStrategy: Revision, so Flux packages a new revision also when
the chart version in Chart.yaml stays the same.
Before you upgrade, back up admin_panel_db and cloud_service.
A rollback does not undo database migrations.
The cert-manager and Traefik HelmReleases pin their chart version.
Change the Traefik chart version and the Gateway API tag in sources/gateway-api.yaml together.
To remove a local test cluster, delete it, for example minikube delete
or kind delete cluster --name wproofreader.
To remove one environment and keep the shared components, delete its cluster file from Git:
git rm flux/clusters/local/demo-wproofreader-stack.yaml
# remove the line demo-wproofreader-stack.yaml from flux/clusters/local/kustomization.yaml
git commit -am "Remove the demo environment"
git push
flux reconcile kustomization wproofreader-root --with-source
flux get helmreleases -n wscFlux deletes the Kustomization and its three HelmReleases, and helm-controller uninstalls the releases. The namespace, the Secrets, and the MySQL volume stay. To delete them also:
kubectl delete namespace wscThis deletes the MySQL data. Back up the databases first.
To remove all layers of the root, and then Flux:
kubectl delete -f flux/root.yaml
flux uninstallThe Gateway API CRDs stay, because their Kustomization has prune: false.
The cert-manager CRDs also stay, because Helm does not delete CRDs.
The wsc namespace also stays, with the Secrets, the admin-panel-tls certificate Secret,
the completed db-manager Job, and the MySQL volume.
To delete them, run kubectl delete namespace wsc.
This deletes the MySQL data.
By default, kustomize-controller runs with cluster-admin rights.
Thus, a person who can merge changes into this repository can change any resource in the cluster.
If different teams operate different environments, use the Flux multi-tenancy lockdown
(--no-cross-namespace-refs and --default-service-account),
and give each environment Kustomization a serviceAccountName.
The HelmReleases in this directory refer to chart sources in flux-system.
With the lockdown, move these sources into the namespace of each environment.