Skip to content

Commit 840c251

Browse files
authored
Merge pull request #154 from fystack/feat/k8s-deployment
update k8s deployment documentation
2 parents 448a420 + 6721d1c commit 840c251

1 file changed

Lines changed: 87 additions & 122 deletions

File tree

deployments/kubernetes/k8s-instruction.md

Lines changed: 87 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,11 @@ Generate the peer configuration file that defines all nodes in your MPC cluster:
130130
mpcium-cli generate-peers -n 3
131131

132132
# This creates peers.json with structure:
133-
# [
134-
# {"id": "node0_<random>", "name": "node0"},
135-
# {"id": "node1_<random>", "name": "node1"},
136-
# {"id": "node2_<random>", "name": "node2"}
137-
# ]
133+
# {
134+
# "node0": "<uuid>",
135+
# "node1": "<uuid>",
136+
# "node2": "<uuid>"
137+
# }
138138
```
139139

140140
**Important**: Keep this `peers.json` file secure. You'll need it for generating identities and registering peers.
@@ -148,16 +148,16 @@ The event initiator is authorized to trigger MPC operations (keygen, signing, re
148148
mpcium-cli generate-initiator --encrypt
149149

150150
# Output files:
151-
# - initiator_identity.json (public key)
152-
# - initiator_private.key.age (encrypted private key)
151+
# - event_initiator.identity.json (public key and metadata)
152+
# - event_initiator.key.age (encrypted private key)
153153
```
154154

155155
**Save the decryption password securely** - you'll need it for applications that initiate MPC operations.
156156

157157
Extract the public key:
158158

159159
```bash
160-
cat initiator_identity.json | jq -r '.public_key'
160+
cat event_initiator.identity.json | jq -r '.public_key'
161161
# Example output: 6cdddd50b0e550f285c5e998cb9c9c88224680cd5922307b9c2e3c395f78dabc
162162
```
163163

@@ -196,10 +196,8 @@ Each identity directory contains:
196196

197197
```
198198
identity/node0/
199-
├── node0_identity.json
200-
├── node0_private.key.age (encrypted private key)
201-
├── node1_identity.json
202-
└── node2_identity.json
199+
├── node0_identity.json (public key and metadata)
200+
└── node0_private.key.age (encrypted private key)
203201
```
204202

205203
### Step 5: Prepare Secrets
@@ -244,36 +242,34 @@ Deploy NATS and Consul if not using external services. For production, use the o
244242
245243
Create secrets for each node containing identity files and passwords:
246244
247-
Command to generate storng Badger password:
245+
Generate and store passwords directly as a Kubernetes secret (passwords never touch disk or shell history):
248246
249-
```
250-
< /dev/urandom tr -dc 'A-Za-z0-9!@#$^&\*()-\_=+[]{}|;:,.<>?/~' | head -c 32; echo
247+
```bash
248+
kubectl create secret generic mpcium-secrets \
249+
-n mpcium \
250+
--from-literal=mpcium-db-password.cred="$(< /dev/urandom tr -dc 'A-Za-z0-9!@#' | head -c 32)" \
251+
--from-literal=mpcium-identity-password.cred="$(< /dev/urandom tr -dc 'A-Za-z0-9!@#' | head -c 32)"
251252
```
252253

253-
```yaml
254-
apiVersion: v1
255-
kind: Secret
256-
metadata:
257-
name: mpcium-secrets-node0
258-
namespace: mpcium
259-
type: Opaque
260-
stringData:
261-
mpcium-db-password.cred: "<badger_password>"
262-
mpcium-identity-password.cred: "<identity_decrypt_password>"
254+
To retrieve a password later:
255+
256+
```bash
257+
kubectl get secret mpcium-secrets -n mpcium \
258+
-o jsonpath='{.data.mpcium-db-password\.cred}' | base64 -d; echo
263259
```
264260

265261
```bash
266-
# Create identity secret from files
267-
kubectl create secret generic mpcium-identity-node0 \
262+
# Create identity secret containing all node identity files
263+
kubectl create secret generic mpcium-identity \
268264
-n mpcium \
269265
--from-file=identity/node0/node0_identity.json \
270266
--from-file=identity/node0/node0_private.key.age \
271-
--from-file=identity/node0/node1_identity.json \
272-
--from-file=identity/node0/node2_identity.json
267+
--from-file=identity/node1/node1_identity.json \
268+
--from-file=identity/node1/node1_private.key.age \
269+
--from-file=identity/node2/node2_identity.json \
270+
--from-file=identity/node2/node2_private.key.age
273271
```
274272

275-
Repeat for node1 and node2.
276-
277273
Create a ConfigMap containing the shared `peers.json` file (required by the distroless image at runtime):
278274

279275
```bash
@@ -304,6 +300,7 @@ data:
304300
mpc_threshold: 2
305301
event_initiator_pubkey: "<your-initiator-public-key>"
306302
event_initiator_algorithm: "ed25519"
303+
chain_code: "<64-char-hex-string>" # Required: 32-byte hex for HD key derivation
307304
backup_enabled: false
308305
backup_period_seconds: 300
309306
```
@@ -335,52 +332,25 @@ mpcium-cli register-peers \
335332
--config ./register-config.yaml
336333
```
337334

338-
### Step 6: Create Persistent Volume Claims
339-
340-
Each node needs persistent storage:
341-
342-
```yaml
343-
apiVersion: v1
344-
kind: PersistentVolumeClaim
345-
metadata:
346-
name: mpcium-data-node0
347-
namespace: mpcium
348-
spec:
349-
accessModes:
350-
- ReadWriteOnce
351-
resources:
352-
requests:
353-
storage: 5Gi
354-
```
355-
356-
This stores:
357-
358-
- BadgerDB data
359-
- Backups (`/app/data/backups`)
360-
361-
Repeat for node1 and node2.
335+
### Step 6: Deploy mpcium StatefulSet
362336

363-
### Step 7: Deploy mpcium Nodes
364-
365-
Create a Deployment for each node:
337+
Use a StatefulSet so each pod gets a stable name (`mpcium-0`, `mpcium-1`, etc.) that maps to the peer entries in Consul. The StatefulSet also manages PersistentVolumeClaims automatically via `volumeClaimTemplates`.
366338

367339
```yaml
368340
apiVersion: apps/v1
369-
kind: Deployment
341+
kind: StatefulSet
370342
metadata:
371-
name: mpcium-node0
343+
name: mpcium
372344
namespace: mpcium
373345
spec:
374-
replicas: 1
375-
strategy:
376-
type: Recreate
346+
replicas: 3
377347
selector:
378348
matchLabels:
379-
app: mpcium-node0
349+
app: mpcium
380350
template:
381351
metadata:
382352
labels:
383-
app: mpcium-node0
353+
app: mpcium
384354
spec:
385355
securityContext:
386356
runAsNonRoot: true
@@ -390,16 +360,22 @@ spec:
390360
containers:
391361
- name: mpcium
392362
image: fystacklabs/mpcium-prod:1.0.0
363+
env:
364+
- name: POD_NAME
365+
valueFrom:
366+
fieldRef:
367+
fieldPath: metadata.name
393368
args:
394369
- "start"
395-
- "--name=node0"
396-
- "--config=/config.yaml"
370+
- "--name=$(POD_NAME)"
371+
- "--config=/app/config.yaml"
372+
- "--peers=/app/peers.json"
397373
- "--password-file=/app/secrets/mpcium-db-password.cred"
398374
- "--identity-password-file=/app/secrets/mpcium-identity-password.cred"
399375
- "--decrypt-private-key"
400376
volumeMounts:
401377
- name: config
402-
mountPath: /config.yaml
378+
mountPath: /app/config.yaml
403379
subPath: config.yaml
404380
readOnly: true
405381
- name: peers
@@ -431,34 +407,32 @@ spec:
431407
name: mpcium-peers
432408
- name: identity
433409
secret:
434-
secretName: mpcium-identity-node0
410+
secretName: mpcium-identity
435411
defaultMode: 0400
436412
- name: secrets
437413
secret:
438-
secretName: mpcium-secrets-node0
414+
secretName: mpcium-secrets
439415
defaultMode: 0400
440-
- name: data
441-
persistentVolumeClaim:
442-
claimName: mpcium-data-node0
443416
- name: tmp
444417
emptyDir: {}
418+
volumeClaimTemplates:
419+
- metadata:
420+
name: data
421+
spec:
422+
accessModes: ["ReadWriteOnce"]
423+
resources:
424+
requests:
425+
storage: 5Gi
445426
```
446427

447-
Duplicate this manifest for `node1`, `node2`, etc., updating:
448-
449-
- Deployment/resource names (`mpcium-nodeX`)
450-
- CLI flag `--name=nodeX`
451-
- Secret and PVC references (`mpcium-identity-nodeX`, `mpcium-secrets-nodeX`, `mpcium-data-nodeX`)
452-
- Labels (`app: mpcium-nodeX`) to keep pods discoverable by node name
453-
454-
Deploy all nodes:
428+
Deploy:
455429

456430
```bash
457-
kubectl apply -f node0-deployment.yaml
458-
kubectl apply -f node1-deployment.yaml
459-
kubectl apply -f node2-deployment.yaml
431+
kubectl apply -f mpcium-statefulset.yaml
460432
```
461433

434+
Pod names will be `mpcium-0`, `mpcium-1`, `mpcium-2` — make sure your `peers.json` and Consul entries use these names (e.g. `{"mpcium-0": "<uuid>", ...}`).
435+
462436
## Post-Deployment Verification
463437

464438
### Step 1: Check Pod Status
@@ -565,12 +539,11 @@ healthcheck:
565539
address: "0.0.0.0:8080" # default
566540
```
567541

568-
Sample probes once enabled:
542+
**Important**: The `/health` endpoint returns HTTP 503 when the node is not yet ready (e.g. during peer ECDH key exchange setup). Do **not** use it as a `livenessProbe` — Kubernetes would kill pods that are still initializing. Use it only for `readinessProbe`, and use a TCP socket probe for liveness:
569543

570544
```yaml
571545
livenessProbe:
572-
httpGet:
573-
path: /health
546+
tcpSocket:
574547
port: 8080
575548
initialDelaySeconds: 30
576549
periodSeconds: 10
@@ -617,11 +590,9 @@ kubectl cp mpcium/<pod-name>:/app/data/backups/backup-<timestamp>.bak ./backup.b
617590
3. Perform rolling update:
618591

619592
```bash
620-
kubectl set image deployment/mpcium-node0 \
593+
kubectl set image statefulset/mpcium \
621594
-n mpcium \
622-
mpcium=fystack/mpcium:v0.3.4
623-
624-
# Repeat for other nodes
595+
mpcium=fystacklabs/mpcium-prod:v0.3.4
625596
```
626597

627598
**Note**: For critical updates affecting consensus, consider:
@@ -747,7 +718,7 @@ rules:
747718
resources: ["pods", "pods/log"]
748719
verbs: ["get", "list", "watch"]
749720
- apiGroups: ["apps"]
750-
resources: ["deployments"]
721+
resources: ["statefulsets"]
751722
verbs: ["get", "list", "watch", "update", "patch"]
752723
```
753724

@@ -776,8 +747,8 @@ kubectl describe pod -n mpcium <pod-name>
776747
**Solutions**:
777748

778749
```bash
779-
# Check if BadgerDB is corrupted
780-
kubectl exec -n mpcium -it <pod-name> -- ls -la /data/db/
750+
# Check if BadgerDB is corrupted (use debug container since distroless has no ls)
751+
kubectl debug -n mpcium <pod-name> -it --image=busybox --target=mpcium -- ls -la /proc/1/root/app/data/db/
781752
782753
# Check password is correct
783754
kubectl get secret mpcium-secrets-node0 -n mpcium -o json | \
@@ -816,8 +787,8 @@ kubectl get secret mpcium-identity-node0 -n mpcium -o json | \
816787
# Verify Consul is running
817788
kubectl get pods -n mpcium -l app=consul
818789
819-
# Test connectivity from pod
820-
kubectl exec -n mpcium -it <pod-name> -- \
790+
# Test connectivity from a debug container
791+
kubectl debug -n mpcium <pod-name> -it --image=busybox --target=mpcium -- \
821792
wget -O- http://consul:8500/v1/kv/mpc_peers/
822793
823794
# Verify peers are registered
@@ -835,8 +806,8 @@ curl http://localhost:8500/v1/kv/mpc_peers/?keys
835806
# Verify NATS is running
836807
kubectl get pods -n mpcium -l app=nats
837808
838-
# Test connectivity
839-
kubectl exec -n mpcium -it <pod-name> -- \
809+
# Test connectivity from a debug container
810+
kubectl debug -n mpcium <pod-name> -it --image=busybox --target=mpcium -- \
840811
nc -zv nats 4222
841812
842813
# Check NATS logs
@@ -863,19 +834,18 @@ kubectl get configmap mpcium-config -n mpcium -o yaml
863834

864835
### Debugging Commands
865836

837+
The mpcium image uses distroless (no shell, no `ls`, no `env`). Use an ephemeral debug container for filesystem inspection, or call the mpcium binary directly for commands it supports:
838+
866839
```bash
867-
# Launch an ephemeral debug container (distroless base image has no shell)
840+
# Launch an ephemeral debug container that shares the pod's process namespace
868841
kubectl debug -n mpcium <pod-name> -it --image=busybox --target=mpcium
869842
870-
# Check environment variables
871-
kubectl exec -n mpcium <pod-name> -- env | grep -i mpcium
872-
873-
# Inspect data/identity mounts
874-
kubectl exec -n mpcium <pod-name> -- ls -la /app
875-
kubectl exec -n mpcium <pod-name> -- ls -la /app/identity
876-
kubectl exec -n mpcium <pod-name> -- ls -la /app/data
843+
# Inside the debug container, inspect mounts:
844+
ls -la /proc/1/root/app
845+
ls -la /proc/1/root/app/identity
846+
ls -la /proc/1/root/app/data
877847
878-
# Verify mpcium binary
848+
# Verify mpcium binary version (this works directly since it's a Go binary)
879849
kubectl exec -n mpcium <pod-name> -- /app/mpcium version
880850
```
881851

@@ -902,34 +872,29 @@ kubectl logs -n mpcium <pod-name> > node0.log
902872

903873
```bash
904874
# 1. Stop all nodes
905-
kubectl scale deployment mpcium-node0 --replicas=0 -n mpcium
906-
kubectl scale deployment mpcium-node1 --replicas=0 -n mpcium
907-
kubectl scale deployment mpcium-node2 --replicas=0 -n mpcium
875+
kubectl scale statefulset mpcium --replicas=0 -n mpcium
908876
909877
# 2. Wait for shutdown
910-
kubectl wait --for=delete pod -l component=mpcium -n mpcium --timeout=60s
878+
kubectl wait --for=delete pod -l app=mpcium -n mpcium --timeout=60s
911879
912880
# 3. Start all nodes
913-
kubectl scale deployment mpcium-node0 --replicas=1 -n mpcium
914-
kubectl scale deployment mpcium-node1 --replicas=1 -n mpcium
915-
kubectl scale deployment mpcium-node2 --replicas=1 -n mpcium
881+
kubectl scale statefulset mpcium --replicas=3 -n mpcium
916882
```
917883

918884
#### Lost Node Recovery
919885

920886
```bash
921887
# If a node's data is lost but identity is intact:
922888
923-
# 1. Delete the PVC
924-
kubectl delete pvc mpcium-data-node0 -n mpcium
925-
926-
# 2. Recreate PVC (will provision new volume)
927-
kubectl apply -f node0/pvc.yaml
889+
# 1. Delete the specific pod (StatefulSet will recreate it)
890+
kubectl delete pod mpcium-0 -n mpcium
928891
929-
# 3. Restart deployment
930-
kubectl rollout restart deployment/mpcium-node0 -n mpcium
892+
# 2. If the PVC is corrupted, delete it and the pod
893+
kubectl delete pvc data-mpcium-0 -n mpcium
894+
kubectl delete pod mpcium-0 -n mpcium
895+
# StatefulSet will recreate both the pod and PVC
931896
932-
# 4. Node will start fresh (key shares may need resharing)
897+
# 3. Node will start fresh (key shares may need resharing)
933898
```
934899

935900
## Additional Resources

0 commit comments

Comments
 (0)