mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
176 lines
9.8 KiB
Markdown
176 lines
9.8 KiB
Markdown
# QingLong 3.0 Worker deployment baseline
|
|
|
|
The Worker is an outbound-only, headless process. It opens one shared TLS 1.3
|
|
client authority to the Cluster Worker ingress and owns one local execution
|
|
journal. It does not expose an HTTP Service, database connection or Kubernetes
|
|
API credential.
|
|
|
|
The committed base intentionally references, but does not create,
|
|
`ql3-worker-config`, `ql3-worker-identity` or the mutable credential target.
|
|
The separate `credential-bootstrap` asset creates an empty, labelled
|
|
`ql3-worker-credential` exactly once so the delivery authority needs only
|
|
exact-name `get` and `update`, never broad Secret creation in the Worker
|
|
namespace. Copy the ConfigMap and TLS identity examples into a private overlay,
|
|
replace every placeholder and use a separately issued Worker ID, certificate,
|
|
`ql3w` token and PVC for every deployed Worker. Do not put the `ql3w` token in
|
|
the TLS identity Secret.
|
|
|
|
Never scale this Deployment above one replica. A replica is not a stateless
|
|
copy: the Worker ID, Session, credential, journal, logs, receipts and running
|
|
POSIX processes form one authority. Horizontal capacity is added by deploying
|
|
another independently named Worker with another identity and volume.
|
|
|
|
Build the image from the repository root:
|
|
|
|
```bash
|
|
node scripts/ql3-worker-image-lock.cjs
|
|
docker build \
|
|
--file deploy/containers/ql3-worker/Dockerfile \
|
|
--tag qinglong3-worker:3.0.0-alpha.2 \
|
|
--build-arg SOURCE_REVISION="$(git rev-parse HEAD)" \
|
|
.
|
|
```
|
|
|
|
Production rollout must take the Worker `@sha256:` reference from the same
|
|
verified `cluster` or `all` release set as control, control-ai and admin. A
|
|
version/source tag or a successful control image alone is not Worker release
|
|
authority. Render the private Worker overlay, run the offline deployment-lock
|
|
post-renderer with `--required-images=worker`, audit the locked manifest, and
|
|
apply only that locked output. See
|
|
[`docs/operations/ql3-release-set-deployment.md`](../../../docs/operations/ql3-release-set-deployment.md).
|
|
|
|
The builder and runtime dependency roots have separate npm v3 locks generated
|
|
from the reviewed workspace `pnpm-lock.yaml`. The runtime image contains only
|
|
`runtime-core`, `local-process`, `worker-runtime` and their 24 external runtime
|
|
packages. The bundled durable launcher is copied explicitly and made
|
|
executable; no QingLong 2.x server, UI, SQLite or PostgreSQL package is present.
|
|
|
|
Each Worker identity needs two namespaces: one Worker namespace containing only
|
|
that identity's Deployment, PVC, ConfigMap, TLS Secret and credential target,
|
|
and a separate staging namespace containing only immutable credential delivery
|
|
stages. Never deploy either into the shared `qinglong3-system` namespace. A
|
|
single Worker namespace is not sufficient because the stage authority must not
|
|
be able to list the TLS private-key Secret.
|
|
|
|
Before applying, create both namespaces and materialize the private
|
|
ConfigMap/TLS Secret. The committed names are `qinglong3-worker` and
|
|
`qinglong3-worker-credential-staging`; a private overlay must replace both for
|
|
every independently named Worker. It must also replace
|
|
`qinglong.io/worker-identity-generation` with a stable, non-secret generation
|
|
identifier derived by the operator's release process. Then apply either the
|
|
edge base or the node resource overlay and the credential-admin RBAC. Render
|
|
the private credential-bootstrap overlay and submit it with create-only
|
|
semantics before the first delivery:
|
|
|
|
```bash
|
|
kubectl kustomize deploy/kubernetes/ql3-worker/base > /private/workstation/path/worker-rendered.yaml
|
|
# or render deploy/kubernetes/ql3-worker/overlays/node
|
|
# Materialize and audit worker-locked.yaml using the release-set procedure,
|
|
# then apply only the locked output:
|
|
kubectl apply -f /private/workstation/path/worker-locked.yaml
|
|
kubectl apply -k deploy/kubernetes/ql3-worker/credential-admin
|
|
kubectl kustomize deploy/kubernetes/ql3-worker/credential-bootstrap \
|
|
| kubectl create -f -
|
|
```
|
|
|
|
An `AlreadyExists` response is a safety stop, not an instruction to apply,
|
|
replace or delete the target. The normal base/GitOps reconciliation set must
|
|
exclude this mutable Secret; if an external reconciler imports it, configure
|
|
that reconciler to ignore the target data, labels and annotations owned by
|
|
credential delivery. Never use server-side apply to continuously reconcile the
|
|
bootstrap manifest.
|
|
|
|
The first Pod remains pending until credential delivery populates the prepared
|
|
`ql3-worker-credential`. Publication is not complete after only replacing that
|
|
Secret. `WorkerCredentialKubernetesDeliveryAdapter` stages immutable Secrets in
|
|
the separate staging namespace, then reads the exact
|
|
single-replica `Recreate` Deployment, verifies that it projects the dedicated
|
|
Secret, and replaces the PodTemplate with delivery ID, credential ID,
|
|
generation, token digest and publication digest annotations under the same
|
|
`resourceVersion` fence. A crash between Secret and Deployment updates is
|
|
replayed from the durable delivery ledger; the adapter returns success only
|
|
after the PodTemplate converges. It owns no watcher, timer, controller loop or
|
|
Worker-side Kubernetes credential.
|
|
|
|
Run a delivery through
|
|
`createWorkerCredentialKubernetesKubeConfigTokenRequestSession`, using an
|
|
external OIDC/client-certificate identity mapped to the dedicated
|
|
`qinglong:worker-credential-operators` Kubernetes group. That group can only
|
|
create the `serviceaccounts/token` subresource for the exact delivery
|
|
ServiceAccount; it cannot use the delivery RBAC itself. The session requests a
|
|
TokenRequest credential, not an automatically mounted or persisted
|
|
ServiceAccount token:
|
|
|
|
```bash
|
|
kubectl -n qinglong3-worker-credential-staging create token \
|
|
ql3-worker-credential-admin --duration=10m
|
|
```
|
|
|
|
The command above is an operator troubleshooting equivalent, not the product
|
|
execution path: it prints the bearer token and therefore must never be used by
|
|
automation. The production session receives only the issuer kubeconfig,
|
|
constructs the delivery client in memory, validates the issued JWT subject and
|
|
at-most-600-second lifetime, runs 8 required-allow and 20 required-deny
|
|
SelfSubjectAccessReview checks before mutation, and invalidates the restricted
|
|
client in `finally`. It never returns the token or adapter. The ServiceAccount
|
|
has `automountServiceAccountToken: false`. In the staging namespace it can only
|
|
`get/list/create/delete` Secrets.
|
|
In the Worker namespace it can only `get/update` the exact prepared credential
|
|
Secret and exact Worker Deployment. It cannot read the TLS Secret, list Worker
|
|
Secrets, access Pods, create a target Secret, patch/delete workloads, mint a
|
|
replacement token or inspect cluster-scoped resources. Stage `create` and
|
|
`list` cannot safely be narrowed by `resourceNames`, which is why the empty
|
|
staging namespace is a required isolation boundary.
|
|
|
|
The issuer kubeconfig remains external operator authority and must use strong,
|
|
short-lived authentication; do not bind the token-issuer Role to the delivery
|
|
ServiceAccount, Worker Pod, control Pod or a broad system group. Approval and
|
|
durable product command binding are separate management-plane gates and are not
|
|
implied by possession of this Kubernetes role.
|
|
|
|
The projected TLS Secret, credential Secret and ConfigMap are not consumed directly. Kubernetes
|
|
projects them through symlinks and group-readable modes, while the Worker
|
|
credential boundary requires direct files, a `0700` parent and `0400` private
|
|
material. The non-root init container creates its own `private/` subdirectory
|
|
inside the fsGroup-writable tmpfs, copies the bounded inputs there and
|
|
initializes `0700` recovery directories on the PVC before the main process
|
|
starts. It never relies on changing the root-owned mount-point mode.
|
|
|
|
CA/key/certificate rotation uses the same `Recreate` boundary but remains an
|
|
explicit deployment operation: update `ql3-worker-identity`, advance
|
|
`qinglong.io/worker-identity-generation` in the private overlay and wait for the
|
|
old Pod to terminate before accepting the replacement Session. Never reuse the
|
|
credential adapter as TLS-key authority; separating the Secrets prevents its
|
|
least-privilege API client from reading the Worker private key.
|
|
|
|
The PVC is mandatory. Replacing it with `emptyDir` can erase the only evidence
|
|
needed to decide whether a process was spawned or completed after a restart.
|
|
The Pod uses `Recreate` and a 360-second termination grace so the node profile's
|
|
five-minute drain can keep heartbeating and settle completion evidence. An
|
|
incomplete drain deliberately does not exit successfully.
|
|
|
|
The process entrypoint itself retains one ref'ed lifecycle handle while it
|
|
waits for SIGINT/SIGTERM. A pending JavaScript Promise alone does not keep Node
|
|
alive; removing this handle makes an otherwise active Worker exit with code 0
|
|
and puts an `Always`-restart Pod into `CrashLoopBackOff`. The handle is cleared
|
|
with signal authority after proof-bearing stop, does not poll Kubernetes and
|
|
does not replace the application's single execution cadence.
|
|
|
|
There is no synthetic Kubernetes readiness or liveness probe. A running PID
|
|
does not prove that startup reconciliation completed or that the Worker Session
|
|
is schedulable, and an automatic liveness kill can destroy the only drain
|
|
owner. Operational readiness is the current durable Worker Session observed
|
|
through the Cluster control plane.
|
|
|
|
The manual `ql3-worker-kubernetes-rollout-live` workflow and
|
|
`qinglong/worker-kubernetes-rollout-live-contract@v2` prove the current Worker
|
|
image through three distinct Pod/Session generations, per-Session heartbeat,
|
|
credential and mTLS identity Recreate, startup reconciliation, shared PVC and
|
|
graceful offline transition. This single-node K3s/local-path evidence is not a
|
|
claim about multi-node CSI detach/attach or physical node loss.
|
|
|
|
For routers or other small non-Kubernetes devices, run the same
|
|
`ql3-worker` binary with `QL3_WORKER_CAPACITY_PROFILE=edge`, one concurrent run,
|
|
the 2-second cadence defaults and private persistent directories. Do not install
|
|
the Cluster PostgreSQL, control-plane, admin or UI closure on that device.
|