Skip to content

Commit 54e405c

Browse files
serghei-devsergeyklay
authored andcommitted
feat(k8s): add plain Kubernetes deployment manifests
Adds single-replica Deployment (Recreate strategy), ClusterIP Service, ReadWriteOnce PVC for SQLite data, and a sample ConfigMap embedding WORKFLOW.md. Includes liveness/readiness probes on /livez and /readyz, read-only root filesystem with an emptyDir /tmp volume, and a quick-start README following the examples/docker/ pattern. Closes #248
1 parent f4cb02f commit 54e405c

5 files changed

Lines changed: 246 additions & 0 deletions

File tree

examples/k8s/README.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Kubernetes Examples
2+
3+
Plain Kubernetes manifests for deploying Sortie to a cluster. For the full
4+
guide — secrets, networking, storage classes, monitoring — see
5+
[Deploy Sortie to Kubernetes](https://docs.sortie-ai.com/guides/deploy-sortie-to-kubernetes/).
6+
7+
## Prerequisites
8+
9+
Build an agent-specific image using the Dockerfiles in `examples/docker/`:
10+
11+
```sh
12+
docker build -f examples/docker/claude-code.Dockerfile -t sortie-claude .
13+
```
14+
15+
Push it to a registry your cluster can pull from, then update the `image`
16+
field in `deployment.yaml`.
17+
18+
## Quick start
19+
20+
Create a Secret with your API keys:
21+
22+
```sh
23+
kubectl create secret generic sortie-secrets \
24+
--from-literal=ANTHROPIC_API_KEY="sk-..." \
25+
--from-literal=SORTIE_JIRA_API_KEY="..." \
26+
--from-literal=SORTIE_JIRA_ENDPOINT="https://your-org.atlassian.net" \
27+
--from-literal=SORTIE_JIRA_PROJECT="PROJ"
28+
```
29+
30+
Apply the manifests:
31+
32+
```sh
33+
kubectl apply -f examples/k8s/
34+
```
35+
36+
Verify the pod is ready:
37+
38+
```sh
39+
kubectl get pods -l app.kubernetes.io/name=sortie
40+
```
41+
42+
## Manifest files
43+
44+
| File | Description |
45+
|---|---|
46+
| `deployment.yaml` | Single-replica Deployment with Recreate strategy |
47+
| `configmap.yaml` | Sample WORKFLOW.md mounted into the container |
48+
| `service.yaml` | ClusterIP Service exposing port 7678 |
49+
| `pvc.yaml` | 1Gi ReadWriteOnce PVC for the SQLite database |
50+
51+
## Customization
52+
53+
1. **Image** — replace `sortie-claude:latest` in `deployment.yaml` with your
54+
registry image (e.g., `registry.example.com/sortie-claude:v1.0.0`).
55+
2. **Workflow** — edit the `WORKFLOW.md` content in `configmap.yaml` to match
56+
your tracker and agent setup.
57+
3. **Secrets** — the Deployment references a Secret named `sortie-secrets`.
58+
Add any environment variables your workflow requires.
59+
4. **Storage** — adjust the PVC size or storage class to match your cluster.

examples/k8s/configmap.yaml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# ConfigMap holding the WORKFLOW.md file that Sortie loads at startup.
2+
# Replace the workflow content below with your own configuration.
3+
apiVersion: v1
4+
kind: ConfigMap
5+
metadata:
6+
name: sortie-workflow
7+
labels:
8+
app.kubernetes.io/name: sortie
9+
app.kubernetes.io/component: orchestrator
10+
app.kubernetes.io/part-of: sortie
11+
data:
12+
WORKFLOW.md: |
13+
---
14+
tracker:
15+
kind: jira
16+
endpoint: $SORTIE_JIRA_ENDPOINT
17+
api_key: $SORTIE_JIRA_API_KEY
18+
project: $SORTIE_JIRA_PROJECT
19+
query_filter: "labels = 'agent-ready'"
20+
active_states:
21+
- To Do
22+
- In Progress
23+
in_progress_state: In Progress
24+
handoff_state: Human Review
25+
terminal_states:
26+
- Done
27+
- Won't Do
28+
29+
polling:
30+
interval_ms: 45000
31+
32+
workspace:
33+
root: /home/sortie/workspaces
34+
35+
agent:
36+
kind: claude-code
37+
command: claude
38+
max_concurrent_agents: 2
39+
40+
server:
41+
port: 7678
42+
---
43+
44+
You are a senior engineer working on {{ .issue.identifier }}: {{ .issue.title }}
45+
46+
{{ if .issue.description }}
47+
## Description
48+
49+
{{ .issue.description }}
50+
{{ end }}

examples/k8s/deployment.yaml

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Sortie Deployment — single-replica with Recreate strategy.
2+
# SQLite requires exclusive filesystem access; do not increase replicas.
3+
#
4+
# The default image assumes you have built an agent-specific image using one
5+
# of the Dockerfiles in examples/docker/. The base distroless image
6+
# (ghcr.io/sortie-ai/sortie) does not bundle an agent.
7+
apiVersion: apps/v1
8+
kind: Deployment
9+
metadata:
10+
name: sortie
11+
labels:
12+
app.kubernetes.io/name: sortie
13+
app.kubernetes.io/component: orchestrator
14+
app.kubernetes.io/part-of: sortie
15+
spec:
16+
replicas: 1
17+
strategy:
18+
type: Recreate
19+
selector:
20+
matchLabels:
21+
app.kubernetes.io/name: sortie
22+
template:
23+
metadata:
24+
labels:
25+
app.kubernetes.io/name: sortie
26+
app.kubernetes.io/component: orchestrator
27+
app.kubernetes.io/part-of: sortie
28+
spec:
29+
terminationGracePeriodSeconds: 30
30+
securityContext:
31+
runAsNonRoot: true
32+
runAsUser: 1000
33+
runAsGroup: 1000
34+
fsGroup: 1000
35+
seccompProfile:
36+
type: RuntimeDefault
37+
containers:
38+
- name: sortie
39+
# Replace with your own agent image built from examples/docker/.
40+
image: sortie-claude:latest
41+
args:
42+
- "--host"
43+
- "0.0.0.0"
44+
- "--log-format"
45+
- "json"
46+
- "--data-dir"
47+
- "/home/sortie/data"
48+
- "/home/sortie/WORKFLOW.md"
49+
ports:
50+
- name: http
51+
containerPort: 7678
52+
protocol: TCP
53+
envFrom:
54+
- secretRef:
55+
name: sortie-secrets
56+
securityContext:
57+
allowPrivilegeEscalation: false
58+
readOnlyRootFilesystem: true
59+
capabilities:
60+
drop:
61+
- ALL
62+
startupProbe:
63+
httpGet:
64+
path: /readyz
65+
port: http
66+
failureThreshold: 30
67+
periodSeconds: 2
68+
livenessProbe:
69+
httpGet:
70+
path: /livez
71+
port: http
72+
periodSeconds: 10
73+
readinessProbe:
74+
httpGet:
75+
path: /readyz
76+
port: http
77+
periodSeconds: 10
78+
resources:
79+
requests:
80+
cpu: 100m
81+
memory: 256Mi
82+
limits:
83+
cpu: 500m
84+
memory: 512Mi
85+
volumeMounts:
86+
- name: data
87+
mountPath: /home/sortie/data
88+
- name: workflow
89+
mountPath: /home/sortie/WORKFLOW.md
90+
subPath: WORKFLOW.md
91+
readOnly: true
92+
- name: tmp
93+
mountPath: /tmp
94+
volumes:
95+
- name: data
96+
persistentVolumeClaim:
97+
claimName: sortie-data
98+
- name: workflow
99+
configMap:
100+
name: sortie-workflow
101+
- name: tmp
102+
emptyDir:
103+
sizeLimit: 64Mi

examples/k8s/pvc.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# PersistentVolumeClaim for the Sortie SQLite database.
2+
# SQLite requires exclusive access — ReadWriteOnce is mandatory.
3+
apiVersion: v1
4+
kind: PersistentVolumeClaim
5+
metadata:
6+
name: sortie-data
7+
labels:
8+
app.kubernetes.io/name: sortie
9+
app.kubernetes.io/component: orchestrator
10+
app.kubernetes.io/part-of: sortie
11+
spec:
12+
accessModes:
13+
- ReadWriteOnce
14+
resources:
15+
requests:
16+
storage: 1Gi

examples/k8s/service.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# ClusterIP Service exposing the Sortie HTTP observability server.
2+
apiVersion: v1
3+
kind: Service
4+
metadata:
5+
name: sortie
6+
labels:
7+
app.kubernetes.io/name: sortie
8+
app.kubernetes.io/component: orchestrator
9+
app.kubernetes.io/part-of: sortie
10+
spec:
11+
type: ClusterIP
12+
selector:
13+
app.kubernetes.io/name: sortie
14+
ports:
15+
- name: http
16+
port: 7678
17+
targetPort: http
18+
protocol: TCP

0 commit comments

Comments
 (0)