Kubernetes
A Kubernetes cluster runs the container image of an application with TX Text Control .NET Server like any other web application, with two differences: an editing session of the Document Editor lives in one pod, and the backend process it talks to listens on a TCP port inside that pod. Both shape the manifests, from the ports of a Deployment to the session affinity of the component in front of it.
The manifests below deploy the image from Dockerfile, first with the backend inside the application pods and then as a separate Deployment.
What You Deploy
Ingress or Gateway, with cookie affinity
|
v
Service myapp (ClusterIP, port 80 -> 8080)
|
v
Pods of the Deployment: your application, each with its Document Editor backend
- The image contains the application, the engine, the fonts and the Document Editor backend. Build and push it as described in Dockerfile.
- No secret for TX Text Control at run time. The license is compiled into your assembly when you build it, and the credentials of the Text Control Private NuGet Feed are only needed during the image build.
- A runtime license per pod. Each pod that runs TX Text Control needs one. See Runtime Licenses.
Deploy the Application
The Deployment runs three replicas of the image, and the Service makes them reachable in the cluster:
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry.example.com/myapp:1.0.0
ports:
- containerPort: 8080
env:
- name: DocumentEditorWorker__ListenAddress
value: "127.0.0.1"
- name: DocumentEditorWorker__Port
value: "4283"
readinessProbe:
httpGet:
path: /healthz
port: 8080
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 30
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 8080
The manifests work as follows:
containerPort: 8080is the port the .NET container images listen on. The Service maps its port 80 to it.The environment variables bind the Document Editor backend to the loopback address of the pod, so that only the application in the same pod reaches it. They set the
DocumentEditorWorkersection, which Program.cs binds toDocumentEditorWorkerOptions, as Prepare the Application shows. Pass the same values toUseTXWebSocketMiddleware.The probes need a health endpoint in the application. Add one in Program.cs:
builder.Services.AddHealthChecks(); // Reachable at /healthz for the probes of Kubernetes app.MapHealthChecks("/healthz");A liveness probe on the application doesn't notice a stopped backend. The application keeps answering requests, and only the editors fail. Watch the log of the pods for messages of the backend, which the application forwards with the prefix
[TXTextControl.Web.Server.Core].replicas: 3means three runtime licenses.
Keep an Editing Session on One Pod
The Service distributes requests over all pods. An editor that reconnects after a lost connection has to reach the pod that holds its session, as Scaling Containers describes, so the component in front of the Service has to keep each browser on one pod:
- An ingress controller or a Gateway API implementation with cookie-based affinity is the usual place for this. Which settings it offers depends on the implementation. The ingress-nginx controller that many examples use is retired, and Kubernetes points to the Gateway API instead.
- In Azure Kubernetes Service, Application Gateway for Containers forwards WebSocket connections without configuration when it's used through the Gateway API, and it offers session affinity with a cookie. See Session affinity and WebSockets.
- The Service itself offers only
sessionAffinity: ClientIP, which isNoneby default. It keys on the address of the client, so browsers that share an address, for example behind a company proxy, land on the same pod, and a changing address breaks the affinity. Use it for traffic inside the cluster, not for browsers. - Idle connections. An open editor that nobody types in sends nothing for a while. Set the idle timeout of the ingress or load balancer higher than the interval at which the application sends WebSocket ping frames;
WebSocketOptions.KeepAliveIntervalis two minutes by default, as Add the Middleware to the Pipeline describes.
Run the Backend in Its Own Deployment
The Document Editor backend can run in pods of its own, built as described in Multiple Processes. Its image already starts the backend on 0.0.0.0:4283:
apiVersion: apps/v1
kind: Deployment
metadata:
name: txbackend
spec:
replicas: 2
selector:
matchLabels:
app: txbackend
template:
metadata:
labels:
app: txbackend
spec:
containers:
- name: txbackend
image: myregistry.example.com/mybackend:1.0.0
ports:
- containerPort: 4283
livenessProbe:
tcpSocket:
port: 4283
periodSeconds: 60
apiVersion: v1
kind: Service
metadata:
name: txbackend
spec:
selector:
app: txbackend
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
ports:
- protocol: TCP
port: 4283
targetPort: 4283
In the application, remove AddHostedService<DocumentEditorWorkerManager>() and the environment variables above, and connect the middleware to the Service:
app.UseTXWebSocketMiddleware("txbackend", 4283);
What matters here:
- The middleware resolves the name once, when the application starts, and the Service must exist at that moment. The cluster IP of a Service stays the same while the Service exists, so replaced backend pods don't force a restart of the application pods.
sessionAffinity: ClientIPkeeps each application pod on one backend pod. The client of this Service is the application pod, not the browser, so its address is stable. The timeout defaults to 10800 seconds and accepts at most 86400.- The TCP probe only opens a connection to port 4283. It shows that the backend is listening, not that an editor can work with it, and a long
periodSecondskeeps the number of these connections small. - Every backend pod also needs a runtime license, because TX Text Control runs in it.
When Pods Stop
A pod that stops takes its editing sessions with it: Kubernetes sends SIGTERM, the application shuts down and stops the backend and the worker processes with the documents of all open editors. This affects rolling updates, scaling in and node maintenance alike. Save documents regularly, as Scaling Containers describes.
Next Steps
- Scaling Containers: which requests need affinity, and which don't.
- Multiple Processes: the image of the separate backend.
- WebSocket Architecture: the connection of the editor through proxies.
- Performance and Scalability: memory and sizing for many concurrent editors.