Fix asciinema S3 deployment
Validate and deploy asciinema / Validate manifests (push) Successful in 13s
Validate and deploy asciinema / Deploy to Kubernetes (push) Successful in 1m6s

This commit is contained in:
feedc0de committed 2026-10-04 17:17:46 +02:00
1 parent 9d0e7f5c95
commit 9fd543ddcb
4 files changed
+25 -73

No files matched your search

+6 -6
View File
@@ -11,13 +11,13 @@ asciinema has two durable data sets:
searchable terminal text, and stream state. Its 8 Gi RBD claim uses the
retained `rook-ceph-block-ec` StorageClass.
- The private Rook Ceph RGW bucket `asciinema` stores uploaded asciicast files.
Downloads use expiring pre-signed URLs through `https://s3.brunner.ninja`.
The server reads casts through the cluster-local RGW Service and serves them
from the asciinema endpoint.
The public RGW ingress rejects ExAws signatures for some mutating methods, so
`custom.exs` uses the cluster-local RGW Service for server-side writes, moves,
and deletes while generating browser-visible pre-signed GET URLs with the
public endpoint. This mirrors the internal/public endpoint split used by other
S3-backed applications in the cluster.
All S3 operations use the cluster-local RGW Service. Although the S3 adapter
generates a pre-signed URL when reading a recording, asciinema consumes that URL
server-side to populate its local cache; it is not sent to the browser. Keeping
the URL internal also avoids routing storage reads through Traefik.
The application needs neither Redis nor Valkey. Its cache and temporary upload
workspace are bounded `emptyDir` volumes and may be discarded when the pod
+2 -65
View File
@@ -11,12 +11,9 @@ data:
TZ: Europe/Vienna
DB_POOL_SIZE: "5"
S3_BUCKET: asciinema
# Mutating requests bypass the ingress because RGW rejects some ExAws
# DELETE/HEAD signatures after they pass through the public proxy.
# asciinema downloads stored casts server-side before serving them. Keep all
# S3 operations on the cluster-local RGW endpoint; no public S3 URL is needed.
S3_ENDPOINT: http://rook-ceph-rgw-my-store.rook-ceph.svc.cluster.local
# The custom adapter uses this endpoint only when producing browser-visible
# pre-signed GET URLs.
S3_PUBLIC_ENDPOINT: https://s3.brunner.ninja
S3_REGION: keller
SMTP_HOST: smtp-relay.gmail.com
SMTP_PORT: "587"
@@ -36,59 +33,6 @@ data:
# instance should remain single-user.
SIGN_UP_DISABLED: "false"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: asciinema-custom-config
namespace: asciinema
labels:
app.kubernetes.io/name: asciinema
data:
custom.exs: |
import Config
# asciinema has one S3 endpoint setting for both internal operations and
# browser-facing pre-signed URLs. Ceph RGW in this cluster needs separate
# endpoints, so delegate all mutations to the upstream adapter and only
# customize URL generation.
defmodule Asciinema.FileStore.SplitEndpointS3 do
@behaviour Asciinema.FileStore
alias ExAws.{Config, S3}
@impl true
def uri(path) do
endpoint = System.fetch_env!("S3_PUBLIC_ENDPOINT") |> URI.parse()
config =
:s3
|> Config.new()
|> Map.put(:scheme, "#{endpoint.scheme}://")
|> Map.put(:host, endpoint.host)
|> Map.put(:port, endpoint.port)
{:ok, url} =
S3.presigned_url(config, :get, bucket(), "uploads/" <> path)
url
end
@impl true
defdelegate put_file(dst_path, src_local_path, content_type),
to: Asciinema.FileStore.S3
@impl true
defdelegate move_file(from_path, to_path), to: Asciinema.FileStore.S3
@impl true
defdelegate delete_file(path), to: Asciinema.FileStore.S3
defp bucket, do: System.fetch_env!("S3_BUCKET")
end
config :asciinema, Asciinema.FileStore,
adapter: Asciinema.FileStore.SplitEndpointS3
---
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -200,19 +144,12 @@ spec:
volumeMounts:
- name: cache
mountPath: /var/cache/asciinema
- name: custom-config
mountPath: /opt/app/etc/custom.exs
subPath: custom.exs
readOnly: true
- name: tmp
mountPath: /tmp
volumes:
- name: cache
emptyDir:
sizeLimit: 2Gi
- name: custom-config
configMap:
name: asciinema-custom-config
- name: tmp
emptyDir:
sizeLimit: 512Mi
-1
View File
@@ -20,7 +20,6 @@ rules:
- configmaps
resourceNames:
- asciinema
- asciinema-custom-config
verbs:
- get
- patch
+17 -1
View File
@@ -33,6 +33,18 @@ if grep -Eqi 'redis|valkey' \
exit 1
fi
if grep -Eq 'S3_PUBLIC_ENDPOINT|SplitEndpointS3|asciinema-custom-config' \
"${project_dir}/asciinema.yaml"; then
echo "asciinema must use the cluster-local RGW endpoint for server-side reads." >&2
exit 1
fi
if ! grep -Fq 'S3_ENDPOINT: http://rook-ceph-rgw-my-store.rook-ceph.svc.cluster.local' \
"${project_dir}/asciinema.yaml"; then
echo "asciinema must use the cluster-local RGW Service." >&2
exit 1
fi
validate_file() {
local manifest=$1
shift
@@ -43,8 +55,12 @@ validate_file() {
docker run --rm --interactive \
ghcr.io/yannh/kubeconform:v0.7.0@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c \
-strict -summary "$@" < "${manifest}"
elif command -v podman >/dev/null 2>&1; then
podman run --rm --interactive \
ghcr.io/yannh/kubeconform:v0.7.0@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c \
-strict -summary "$@" < "${manifest}"
else
echo "Required command not found: install kubeconform or Docker" >&2
echo "Required command not found: install kubeconform, Docker, or Podman" >&2
exit 1
fi
}