Build phpPgAdmin on unified PHP base

This commit is contained in:
2026-09-10 13:43:40 +02:00
parent 49f1692e44
commit d912e4f623
6 changed files with 151 additions and 40 deletions
+5
View File
@@ -0,0 +1,5 @@
.git
.gitea
*.md
*.sh
*.yaml
+78 -2
View File
@@ -5,6 +5,7 @@ on:
pull_request:
env:
IMAGE: registry.brunner.ninja/feedc0de/phppgadmin
KUBECONFORM_VERSION: v0.7.0
KUBECTL_VERSION: v1.36.3
KUBERNETES_API: https://host.containers.internal:6443
@@ -12,6 +13,74 @@ env:
NAMESPACE: default
jobs:
image:
name: Build, test and publish image
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Check out source
uses: actions/checkout@v4
- name: Log in to Quay
uses: docker/login-action@v3
with:
registry: registry.brunner.ninja
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_TOKEN }}
- name: Build image
env:
COMMIT_SHA: ${{ gitea.sha }}
run: |
set -euo pipefail
docker build \
--pull \
--load \
--label "org.opencontainers.image.base.name=registry.brunner.ninja/feedc0de/php-apache:f76304f1014f15354ec05bba7eaca376993afe72" \
--label "org.opencontainers.image.revision=${COMMIT_SHA}" \
--label "org.opencontainers.image.source=https://code.brunner.ninja/feedc0de/phppgadmin-docker" \
--tag "${IMAGE}:${COMMIT_SHA}" \
.
- name: Smoke-test image
env:
COMMIT_SHA: ${{ gitea.sha }}
run: |
set -euo pipefail
docker run --rm --entrypoint httpd "${IMAGE}:${COMMIT_SHA}" -t
docker run --rm --entrypoint php "${IMAGE}:${COMMIT_SHA}" -r '
foreach (["pgsql", "pdo_pgsql", "mbstring", "Zend OPcache"] as $extension) {
if (!extension_loaded($extension)) {
fwrite(STDERR, "Missing PHP extension: {$extension}\n");
exit(1);
}
}
'
container_id="$(docker run --detach --publish 127.0.0.1::80 "${IMAGE}:${COMMIT_SHA}")"
trap 'docker rm --force "${container_id}" >/dev/null 2>&1 || true' EXIT
port="$(docker inspect --format '{{ (index (index .NetworkSettings.Ports "80/tcp") 0).HostPort }}' "${container_id}")"
for attempt in {1..30}; do
if curl --fail --silent --show-error \
--output "${RUNNER_TEMP}/phppgadmin.html" \
"http://127.0.0.1:${port}/phppgadmin/" \
&& grep -Fq 'phpPgAdmin' "${RUNNER_TEMP}/phppgadmin.html"; then
exit 0
fi
sleep 1
done
docker logs "${container_id}"
exit 1
- name: Push image
if: gitea.ref == 'refs/heads/main'
env:
COMMIT_SHA: ${{ gitea.sha }}
run: |
set -euo pipefail
docker push "${IMAGE}:${COMMIT_SHA}"
docker tag "${IMAGE}:${COMMIT_SHA}" "${IMAGE}:latest"
docker push "${IMAGE}:latest"
validate:
name: Validate manifests
runs-on: ubuntu-latest
@@ -34,7 +103,9 @@ jobs:
deploy:
name: Deploy to Kubernetes
if: gitea.ref == 'refs/heads/main'
needs: validate
needs:
- image
- validate
runs-on: ubuntu-latest
steps:
- name: Check out source
@@ -70,9 +141,14 @@ jobs:
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
sed \
"s|deployment.brunner.ninja/revision: manual|deployment.brunner.ninja/revision: ${COMMIT_SHA}|" \
phppgadmin.yaml > "${RUNNER_TEMP}/phppgadmin.yaml"
phppgadmin.yaml \
| sed \
"s|registry.brunner.ninja/feedc0de/phppgadmin:latest|${IMAGE}:${COMMIT_SHA}|" \
> "${RUNNER_TEMP}/phppgadmin.yaml"
grep -Fq "deployment.brunner.ninja/revision: ${COMMIT_SHA}" \
"${RUNNER_TEMP}/phppgadmin.yaml"
grep -Fq "image: ${IMAGE}:${COMMIT_SHA}" \
"${RUNNER_TEMP}/phppgadmin.yaml"
"${RUNNER_TEMP}/kubectl" apply \
--dry-run=server \
--validate=false \
+23
View File
@@ -0,0 +1,23 @@
ARG PHP_APACHE_TAG=f76304f1014f15354ec05bba7eaca376993afe72
FROM registry.brunner.ninja/feedc0de/php-apache:${PHP_APACHE_TAG}
ARG PHPPGADMIN_VERSION=7.14.7
ARG PHPPGADMIN_SHA256=7c0e89922abcf88fd81a52590930f3b5121032d1d37234a54a8f6415152dc292
RUN curl --fail --show-error --location \
--output /tmp/phppgadmin.tar.gz \
"https://github.com/ReimuHakurei/phpPgAdmin/archive/refs/tags/v${PHPPGADMIN_VERSION}-mod.tar.gz" \
&& printf '%s %s\n' "${PHPPGADMIN_SHA256}" /tmp/phppgadmin.tar.gz | sha256sum --check \
&& install -d /usr/share/webapps/phppgadmin /etc/phppgadmin \
&& tar --extract --gzip --file /tmp/phppgadmin.tar.gz \
--strip-components=1 --directory /usr/share/webapps/phppgadmin \
&& cp /usr/share/webapps/phppgadmin/conf/config.inc.php-dist \
/etc/phppgadmin/config.inc.php \
&& rm -f /usr/share/webapps/phppgadmin/conf/config.inc.php \
&& ln -s /etc/phppgadmin/config.inc.php \
/usr/share/webapps/phppgadmin/conf/config.inc.php \
&& rm /tmp/phppgadmin.tar.gz
COPY phppgadmin.conf /etc/httpd/conf/extra/phppgadmin.conf
RUN printf '%s\n' 'Include conf/extra/phppgadmin.conf' >> /etc/httpd/conf/httpd.conf
+32 -34
View File
@@ -1,15 +1,15 @@
# phpPgAdmin on Kubernetes
Production deployment for the brunner.ninja phpPgAdmin service. The repository
uses a maintained public container image, a single plain Kubernetes manifest,
Traefik ingress, health probes, zero-unavailable rolling updates, and restricted
Gitea Actions deployment credentials.
uses a small private image derived from the shared PHP/Apache base, a single
plain Kubernetes manifest, Traefik ingress, health probes, zero-unavailable
rolling updates, and restricted Gitea Actions deployment credentials.
## Architecture
- `tozd/phppgadmin` supplies phpPgAdmin, Nginx, PHP, and PostgreSQL client tools.
- The image is pinned to an immutable multi-platform manifest digest rather
than a mutable tag.
- The repository image adds phpPgAdmin to the same Apache/PHP layer used by the
homepage deployments.
- CI deploys the image by its immutable Git commit tag rather than `latest`.
- `default/phppgadmin-config` mounts the complete multi-server configuration at
`/etc/phppgadmin/config.inc.php`.
- A ClusterIP Service exposes TCP port 80 to Traefik.
@@ -27,29 +27,19 @@ The deployment is stateless. phpPgAdmin does not store database passwords or
sessions on a PVC; users authenticate directly to the selected PostgreSQL
server. The ConfigMap lists server addresses but contains no credentials.
## Why there is no Dockerfile
## Container image
This repository previously built phpPgAdmin from the Arch User Repository on
top of a private base image and pushed the result to the private Quay registry.
That duplicated public image storage and made every repository commit rebuild
an otherwise unchanged application.
The Dockerfile installs the verified phpPgAdmin 7.14.7 release directly on
`registry.brunner.ninja/feedc0de/php-apache`. The common image already provides
Apache, PHP, PostgreSQL/PDO PostgreSQL, and OPcache, so Kubernetes nodes share
those layers with the PHP homepage deployments. The phpPgAdmin layer contains
only the application and its Apache alias configuration. Its base tag is the
same immutable php-apache Git commit used by the homepage values files.
The current deployment instead uses the actively maintained public
[`tozd/phppgadmin`](https://gitlab.com/tozd/docker/phppgadmin) image. It supports
the same configuration-file mount used here. The pinned image contains
phpPgAdmin 7.14.7, which is also the current AUR package version at the time of
this repository conversion.
The committed image is:
```text
tozd/phppgadmin@sha256:4ee690768d54b0c057dbc4ed5c1baa152c1a5ef0ebd6eb12f1e310e545eb4489
```
It was inspected and smoke-tested before deployment. Pinning prevents an
upstream rebuild from silently changing the running software. To upgrade,
inspect and test the new upstream digest, replace the image line in
`phppgadmin.yaml`, and merge the change through the normal deployment workflow.
The release archive is checked against the SHA-256 published by the Arch User
Repository package before extraction. phpPgAdmin is exposed at
`/phppgadmin/`, and the existing ConfigMap is mounted at
`/etc/phppgadmin/config.inc.php`.
## Repository contents
@@ -60,16 +50,20 @@ inspect and test the new upstream digest, replace the image line in
- `ci-deployer.yaml`: restricted Gitea Actions ServiceAccount, Role, binding,
and revocable token.
- `create-ci-kubeconfig.sh`: one-time CI identity bootstrap.
- `.gitea/workflows/deploy.yml`: validation on every push and pull request;
deployment after successful validation of `main`.
- `Dockerfile` and `phppgadmin.conf`: small phpPgAdmin layer and Apache alias
on the shared php-apache image.
- `.gitea/workflows/deploy.yml`: image build and validation on every push and
pull request; image publishing and deployment after a successful `main` run.
There is no Helm chart, Kustomize overlay, Argo CD application, private image,
or container build pipeline.
phpPgAdmin is intentionally not deployed through the generic PHP homepage Helm
chart. It is stateless, has no CephFS web root, and needs its own ConfigMap and
Traefik redirect Middleware.
## Prerequisites
- Kubernetes and kubectl.
- An amd64 worker node. The pinned upstream image currently publishes amd64.
- The unified `registry.brunner.ninja/feedc0de/php-apache` image in Quay.
- The `quay-pull-secret` image pull secret in the `default` namespace.
- Traefik with the `traefik.io/v1alpha1` Middleware CRD and ingress class
`traefik`.
- The existing Traefik `websecure` entrypoint and default
@@ -133,9 +127,13 @@ The workflow validates every push and pull request. A successful push to
Service, Ingress, and Middleware, then polls the Deployment for up to ten
minutes.
No image is built or pushed. The workflow requires only one Gitea Actions
secret:
The workflow builds and smoke-tests the image for every push and pull request.
On `main` it pushes the immutable Git commit tag plus `latest`, substitutes the
immutable tag in the deployment manifest, and deploys it. It requires these
Gitea Actions secrets:
- `QUAY_USERNAME`: Quay robot-account username with read/write access.
- `QUAY_TOKEN`: token for that robot account.
- `KUBE_CONFIG_BASE64`: the restricted phpPgAdmin deployment kubeconfig,
base64-encoded on one line.
+8
View File
@@ -0,0 +1,8 @@
Alias /phppgadmin "/usr/share/webapps/phppgadmin"
<Directory "/usr/share/webapps/phppgadmin">
DirectoryIndex index.php
AllowOverride All
Options FollowSymLinks
Require all granted
</Directory>
+5 -4
View File
@@ -241,10 +241,9 @@ spec:
terminationGracePeriodSeconds: 30
containers:
- name: phppgadmin
# Public, maintained image pinned to the verified multi-platform
# manifest digest published on 2026-08-08.
image: tozd/phppgadmin@sha256:4ee690768d54b0c057dbc4ed5c1baa152c1a5ef0ebd6eb12f1e310e545eb4489
imagePullPolicy: IfNotPresent
# CI replaces latest with the immutable source commit tag on main.
image: registry.brunner.ninja/feedc0de/phppgadmin:latest
imagePullPolicy: Always
ports:
- name: http
containerPort: 80
@@ -278,6 +277,8 @@ spec:
- name: phppgadmin-config
configMap:
name: phppgadmin-config
imagePullSecrets:
- name: quay-pull-secret
---
apiVersion: v1
kind: Service