All docsStellar ControlMission control · by Stellar Systems v1.1.0

Deployment

Kubernetes

Stellar Control on Kubernetes: an example manifest with NATS and JetStream, the configuration and its publication, the all-in-one stellar-control behind an Ingress, a simulated target, credentials, probes, metrics and scaling.

Kubernetes runs containers on a cluster from declarative manifests. Stellar Control runs there from the image stellar-control (see Docker), delivered by Stellar Systems from the registry given with the delivery, and NATS from its official image. This page gives an example manifest, to adapt to your cluster: its ingress controller, storage classes, secret store and monitoring. It follows the Nomad job of Stellar Control: the same image, commands, environment, ports and health checks.

What the manifest runs#

ObjectNameRole
NamespacestellarEverything below
ConfigMapnats-configConfiguration of the NATS server: JetStream on its volume, the WebSocket for the edges
StatefulSet, PersistentVolumeClaimnats, data-nats-0NATS with JetStream, its storage kept across restarts
Servicesnats (client 4222, monitoring 8222, WebSocket 8443), nats-headlessNATS in the cluster
ConfigMapstellar-configGlobal configuration of Stellar Control
ConfigMapstellar-repositoryGit remote and branch of the configuration repository
Deploymentstellar-controlThe services of the core in one container, after the publication of the configuration
Service, Ingressstellar-controlThe API, the web console and their WebSockets over HTTPS
Ingressnats-websocketThe NATS WebSocket over HTTPS, for drivers, transports and gateways on other networks
Deploymentsimulator-sim-1A simulated target of the example configuration
CronJob (suspended)stellar-publishPublishes the configuration again after a change

The manifest#

Replace the placeholders <…>: the image (<registry>/stellar-control:<version>, an immutable tag), the URL of the configuration repository, and the host names control.example.org and nats.example.org.

YAML
# Stellar Control on Kubernetes: NATS with JetStream, the services of the core in one container,
# a simulated target. Replace the placeholders <…>, then: kubectl apply -f stellar.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: stellar
---
# NATS: its configuration. JetStream on the volume of the StatefulSet; the WebSocket for the edges
# on other networks, behind the Ingress that terminates its TLS.
apiVersion: v1
kind: ConfigMap
metadata:
  name: nats-config
  namespace: stellar
data:
  nats-server.conf: |
    server_name: stellar-nats
    listen: 0.0.0.0:4222
    http: 0.0.0.0:8222

    jetstream {
      store_dir: /data/jetstream
      max_file_store: 8GB        # within the volume of 10 Gi below
    }

    websocket {
      port: 8443
      no_tls: true               # TLS terminated by the Ingress
    }
---
apiVersion: v1
kind: Service
metadata:
  name: nats-headless
  namespace: stellar
spec:
  clusterIP: None
  selector:
    app.kubernetes.io/name: nats
  ports:
    - name: client
      port: 4222
---
apiVersion: v1
kind: Service
metadata:
  name: nats
  namespace: stellar
spec:
  selector:
    app.kubernetes.io/name: nats
  ports:
    - name: client
      port: 4222
    - name: monitoring
      port: 8222
    - name: websocket
      port: 8443
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: nats
  namespace: stellar
spec:
  serviceName: nats-headless
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: nats
  template:
    metadata:
      labels:
        app.kubernetes.io/name: nats
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
      terminationGracePeriodSeconds: 30
      containers:
        - name: nats
          image: docker.io/library/nats:2.12.15
          args: ["-c", "/etc/nats/nats-server.conf"]
          ports:
            - name: client
              containerPort: 4222
            - name: monitoring
              containerPort: 8222
            - name: websocket
              containerPort: 8443
          livenessProbe:
            httpGet:
              path: /healthz
              port: monitoring
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /healthz
              port: monitoring
            periodSeconds: 5
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
            limits:
              memory: 512Mi
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: config
              mountPath: /etc/nats
              readOnly: true
            - name: data
              mountPath: /data
      volumes:
        - name: config
          configMap:
            name: nats-config
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 10Gi
---
# The global configuration of Stellar Control; the Deployments set NATS, the API and the metrics
# through the environment, as the Nomad job does.
apiVersion: v1
kind: ConfigMap
metadata:
  name: stellar-config
  namespace: stellar
data:
  stellar.yaml: |
    archive:
      streams:
        # JetStream reserves this volume on the disk of the server: keep it within its storage.
        max_age: 7d
        max_bytes: 256MB
    observability:
      log_format: json
      log_level: info
---
# The configuration repository: its Git remote (a private one needs credentials, see git-sync).
apiVersion: v1
kind: ConfigMap
metadata:
  name: stellar-repository
  namespace: stellar
data:
  GITSYNC_REPO: <https URL of the configuration repository>
  GITSYNC_REF: main
---
# The services of the core in one container. One replica: the editor keeps its drafts in its pod.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: stellar-control
  namespace: stellar
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app.kubernetes.io/name: stellar-control
  template:
    metadata:
      labels:
        app.kubernetes.io/name: stellar-control
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9100"
        prometheus.io/path: /metrics
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        fsGroup: 10001
      initContainers:
        # The configuration repository, once, at the commit of GITSYNC_REF.
        - name: git-sync
          image: registry.k8s.io/git-sync/git-sync:v4.7.1
          args: ["--root=/git", "--link=config", "--one-time"]
          envFrom:
            - configMapRef:
                name: stellar-repository
          env:
            - name: HOME
              value: /tmp
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              memory: 128Mi
          volumeMounts:
            - name: repository
              mountPath: /git
            - name: tmp
              mountPath: /tmp
        # Compiled and published before the services start, as the prestart task publish-config
        # of the Nomad job.
        - name: publish-config
          image: <registry>/stellar-control:<version>
          command: ["stellar", "compile", "/git/config", "--publish", "nats://nats:4222",
                    "--output", "/tmp/snapshot.ir"]
          env:
            - name: STELLAR_CONFIG
              value: /etc/stellar/stellar.yaml
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              memory: 128Mi
          volumeMounts:
            - name: config
              mountPath: /etc/stellar
              readOnly: true
            - name: repository
              mountPath: /git
              readOnly: true
            - name: home
              mountPath: /var/lib/stellar
            - name: tmp
              mountPath: /tmp
      containers:
        - name: stellar-control
          image: <registry>/stellar-control:<version>
          command: ["stellar-control"]
          env:
            - name: STELLAR_CONFIG
              value: /etc/stellar/stellar.yaml
            - name: STELLAR__NATS__URL
              value: nats://nats:4222
            - name: STELLAR__API__LISTEN
              value: 0.0.0.0:8080
            - name: STELLAR__API__WEB_DIR
              value: /usr/share/stellar/web
            - name: STELLAR__OBSERVABILITY__METRICS_ADDR
              value: 0.0.0.0:9100
            - name: STELLAR_INSTANCE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
          ports:
            - name: http
              containerPort: 8080
            - name: metrics
              containerPort: 9100
          livenessProbe:
            httpGet:
              path: /healthz
              port: metrics
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /v1/openapi.json
              port: http
            periodSeconds: 10
            timeoutSeconds: 2
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]
          resources:
            requests:
              cpu: 200m
              memory: 384Mi
            limits:
              memory: 384Mi
          volumeMounts:
            - name: config
              mountPath: /etc/stellar
              readOnly: true
            - name: home
              mountPath: /var/lib/stellar
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: config
          configMap:
            name: stellar-config
        - name: repository
          emptyDir: {}
        - name: home
          emptyDir: {}
        - name: tmp
          emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
  name: stellar-control
  namespace: stellar
  labels:
    app.kubernetes.io/name: stellar-control
spec:
  selector:
    app.kubernetes.io/name: stellar-control
  ports:
    - name: http
      port: 8080
    - name: metrics
      port: 9100
---
# The API, the web console and their WebSockets over HTTPS.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: stellar-control
  namespace: stellar
  annotations:
    # ingress-nginx: WebSockets kept open for an hour without a message.
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
  ingressClassName: nginx
  tls:
    - hosts: [control.example.org]
      secretName: stellar-control-tls
  rules:
    - host: control.example.org
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: stellar-control
                port:
                  name: http
---
# The NATS WebSocket for the drivers, transports and gateways on other networks
# (wss://nats.example.org), and the leaf nodes of their benches.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nats-websocket
  namespace: stellar
  annotations:
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
  ingressClassName: nginx
  tls:
    - hosts: [nats.example.org]
      secretName: nats-websocket-tls
  rules:
    - host: nats.example.org
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nats
                port:
                  name: websocket
---
# A simulated target of the example configuration: sim-1 behind its gateway sim-gw-1.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: simulator-sim-1
  namespace: stellar
spec:
  replicas: 1
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app.kubernetes.io/name: simulator-sim-1
  template:
    metadata:
      labels:
        app.kubernetes.io/name: simulator-sim-1
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 10001
        runAsGroup: 10001
        fsGroup: 10001
      initContainers:
        - name: git-sync
          image: registry.k8s.io/git-sync/git-sync:v4.7.1
          args: ["--root=/git", "--link=config", "--one-time"]
          envFrom:
            - configMapRef:
                name: stellar-repository
          env:
            - name: HOME
              value: /tmp
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]
          volumeMounts:
            - name: repository
              mountPath: /git
            - name: tmp
              mountPath: /tmp
      containers:
        - name: simulator
          image: <registry>/stellar-control:<version>
          command: ["stellar-simulator"]
          args: ["--simulation", "platform-v3-sim", "--target", "sim-1",
                 "--gateway", "sim-gw-1", "--driver", "platform-v3-sim-1"]
          env:
            - name: STELLAR_REPOSITORY
              value: /git/config
            - name: STELLAR_NATS_URL
              value: nats://nats:4222
            - name: RUST_LOG
              value: info
          securityContext:
            readOnlyRootFilesystem: true
            allowPrivilegeEscalation: false
            capabilities:
              drop: ["ALL"]
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              memory: 64Mi
          volumeMounts:
            - name: repository
              mountPath: /git
              readOnly: true
            - name: tmp
              mountPath: /tmp
      volumes:
        - name: repository
          emptyDir: {}
        - name: tmp
          emptyDir: {}
---
# Publishes the configuration repository again after a change, without restarting the services:
#   kubectl -n stellar create job --from=cronjob/stellar-publish publish-$(date +%s)
apiVersion: batch/v1
kind: CronJob
metadata:
  name: stellar-publish
  namespace: stellar
spec:
  schedule: "0 0 1 1 *"
  suspend: true          # never on a schedule: a template of Jobs
  jobTemplate:
    spec:
      backoffLimit: 3
      ttlSecondsAfterFinished: 86400
      template:
        spec:
          restartPolicy: Never
          securityContext:
            runAsNonRoot: true
            runAsUser: 10001
            runAsGroup: 10001
            fsGroup: 10001
          initContainers:
            - name: git-sync
              image: registry.k8s.io/git-sync/git-sync:v4.7.1
              args: ["--root=/git", "--link=config", "--one-time"]
              envFrom:
                - configMapRef:
                    name: stellar-repository
              env:
                - name: HOME
                  value: /tmp
              volumeMounts:
                - name: repository
                  mountPath: /git
                - name: tmp
                  mountPath: /tmp
          containers:
            - name: publish-config
              image: <registry>/stellar-control:<version>
              command: ["stellar", "compile", "/git/config", "--publish", "nats://nats:4222",
                        "--output", "/tmp/snapshot.ir"]
              env:
                - name: STELLAR_CONFIG
                  value: /etc/stellar/stellar.yaml
              securityContext:
                readOnlyRootFilesystem: true
                allowPrivilegeEscalation: false
                capabilities:
                  drop: ["ALL"]
              volumeMounts:
                - name: config
                  mountPath: /etc/stellar
                  readOnly: true
                - name: repository
                  mountPath: /git
                  readOnly: true
                - name: home
                  mountPath: /var/lib/stellar
                - name: tmp
                  mountPath: /tmp
          volumes:
            - name: config
              configMap:
                name: stellar-config
            - name: repository
              emptyDir: {}
            - name: home
              emptyDir: {}
            - name: tmp
              emptyDir: {}
Shell
kubectl apply -f stellar.yaml
kubectl -n stellar rollout status statefulset/nats
kubectl -n stellar rollout status deployment/stellar-control
kubectl -n stellar logs deployment/stellar-control -c publish-config   # the publication
kubectl -n stellar logs deployment/stellar-control                     # the services
kubectl -n stellar port-forward service/stellar-control 8080:8080      # without the Ingress

The API and the web console answer on https://control.example.org/ (or http://127.0.0.1:8080/ through the port forward). Until a configuration is published, the API answers 503 on what needs it: see Running the Services.

NATS#

The example runs one NATS server as a StatefulSet: a stable name, and its JetStream storage on a PersistentVolumeClaim of the default storage class, kept when the pod is replaced. Keep max_file_store within the volume, and the archive of Stellar Control (archive.streams.max_bytes, archive.max_bytes) within max_file_store (see Storage of a deployment).

For production, the recommended way is the official Helm chart of NATS, nats-io/k8s: a cluster of three servers with JetStream replicated, TLS, the lame duck mode at shutdown, a NATS box and the Prometheus exporter. Give it the same settings: JetStream on file storage, the resolver of your accounts, and the WebSocket when edges connect from other networks. Point STELLAR__NATS__URL at its Service. See NATS and JetStream for what Stellar Control keeps there and its replication (1 in development, 3 in production).

The configuration#

Global configuration. The ConfigMap stellar-config is mounted as /etc/stellar/stellar.yaml (STELLAR_CONFIG). As in the Nomad job, the address of NATS, the listen addresses and the directory of the web console come from the environment (STELLAR__NATS__URL, STELLAR__API__LISTEN, STELLAR__API__WEB_DIR, STELLAR__OBSERVABILITY__METRICS_ADDR): any key can be set so, see Environment overrides. Each pod names its instance after itself (STELLAR_INSTANCE).

Configuration repository. The init container git-sync (kubernetes/git-sync) clones the remote of the ConfigMap stellar-repository at GITSYNC_REF into a volume of the pod, and stops (--one-time). For a private repository, give it a read-only token from a Secret (GITSYNC_USERNAME, GITSYNC_PASSWORD) or an SSH key (--ssh-key-file). A volume holding the repository (a PersistentVolumeClaim filled by your pipeline) works as well: mount it in place of the emptyDir.

Publication. The init container publish-config runs stellar compile /git/config --publish nats://nats:4222 before the services start, as the prestart task publish-config of the Nomad job: every start of the pod publishes the repository as it is at GITSYNC_REF. To publish a change without restarting the services, run a Job from the suspended CronJob stellar-publish:

Shell
kubectl -n stellar create job --from=cronjob/stellar-publish publish-$(date +%s)
kubectl -n stellar logs job/publish-<…> -c publish-config

A pipeline that reaches NATS can publish instead (stellar compile --publish, see Running the Services). A new snapshot applies to each target at a safe point, never in the middle of a run or a pass.

NATS accounts and credentials#

In production, NATS has an operator, an account for Stellar Control with a signing key, and its users (see Setting up a server and NATS Accounts and Credentials). On Kubernetes they are Secrets, filled from your secret store rather than written in a file of a repository:

YAML
# The NATS identities made with nsc (see NATS and JetStream, "Setting up a server").
apiVersion: v1
kind: Secret
metadata:
  name: nats-resolver
  namespace: stellar
stringData:
  resolver.conf: |
    <the resolver.conf of nsc generate config --mem-resolver>
---
apiVersion: v1
kind: Secret
metadata:
  name: stellar-nats
  namespace: stellar
stringData:
  services.creds: |
    <the admin.creds of nsc>
  bootstrap.creds: |
    <the bootstrap.creds of nsc>
  signing.nk: |
    <the seed of the signing key of the account, SA…>
  account: <the public key of the account, A…>

The NATS server includes the resolver: mount the Secret nats-resolver at /etc/nats-resolver in the StatefulSet and add include "../nats-resolver/resolver.conf" to nats-server.conf. Stellar Control then connects with the credentials of the services, and its reconciler signs the JWTs of the drivers, transports and gateways with the signing key, which it refuses when it is readable by others: an init container copies it with mode 0400 into a volume in memory. The additions to the Deployment stellar-control:

YAML
# Additions to the Deployment stellar-control:
#   kubectl -n stellar patch deployment stellar-control --patch-file nats-auth-patch.yaml
spec:
  template:
    spec:
      initContainers:
        # The reconciler refuses a signing key readable by others: a copy of mode 0400, owned
        # by the user of the image, in a volume in memory.
        - name: signing-key
          image: <registry>/stellar-control:<version>
          command: ["install", "-m", "0400", "/etc/stellar-nats/signing.nk", "/run/stellar/signing.nk"]
          volumeMounts:
            - name: nats-secrets
              mountPath: /etc/stellar-nats
              readOnly: true
            - name: signing-key
              mountPath: /run/stellar
        - name: publish-config
          env:
            - name: STELLAR_NATS_CREDENTIALS
              value: /etc/stellar-nats/services.creds
          volumeMounts:
            - name: nats-secrets
              mountPath: /etc/stellar-nats
              readOnly: true
      containers:
        - name: stellar-control
          env:
            - name: STELLAR__NATS__CREDENTIALS
              value: /etc/stellar-nats/services.creds
            - name: STELLAR__RECONCILER__SIGNING_KEY_FILE
              value: /run/stellar/signing.nk
            - name: STELLAR__RECONCILER__ACCOUNT
              valueFrom:
                secretKeyRef:
                  name: stellar-nats
                  key: account
          volumeMounts:
            - name: nats-secrets
              mountPath: /etc/stellar-nats
              readOnly: true
            - name: signing-key
              mountPath: /run/stellar
              readOnly: true
      volumes:
        - name: nats-secrets
          secret:
            secretName: stellar-nats
            defaultMode: 0440
        - name: signing-key
          emptyDir:
            medium: Memory

The simulator, like every driver, transport and gateway, starts with the bootstrap credentials: STELLAR_NATS_CREDENTIALS=/etc/stellar-nats/bootstrap.creds, with the Secret mounted the same way; the Job stellar-publish with the credentials of the services. With TLS to NATS, mount the CA from a Secret or ConfigMap and set STELLAR__NATS__TLS__CA (services) and STELLAR_NATS_CA (the CLI, the simulator), with a tls:// URL.

People sign in through an OpenID Connect provider with the STELLAR__AUTH__… variables of Identity and Roles, as the auth_… variables of the Nomad job set them.

Exposing it#

The API and the web console. The Ingress stellar-control routes the host to the port 8080 of the Service, with TLS from the Secret stellar-control-tls (from cert-manager, for instance). The web console and the CLI use WebSockets on the same host: the annotations of the example keep them open with ingress-nginx; other controllers have their own timeouts. See Ingress.

NATS for the edges on other networks. Drivers, transports and gateways on a bench network that only lets HTTPS out connect to the NATS WebSocket, wss://nats.example.org:443, or through a leaf node on site (see Edges behind a firewall). Two ways to expose it with TLS:

  • Through the Ingress (the example): the Ingress nats-websocket terminates TLS and passes the WebSocket to the port 8443 of NATS, which then listens without TLS (no_tls: true) inside the cluster only.
  • NATS terminating TLS: a websocket { tls { … } } block with the certificate from a Secret (as in the Nomad job), and a Service of type LoadBalancer on 443, or a TLS passthrough of the ingress controller. The same holds for a leaf node port (7422) with TLS.

The client port 4222 stays inside the cluster: the Service nats is a ClusterIP.

Probes#

ContainerLivenessReadiness
stellar-control, and each serviceGET /healthz on the metrics port 9100: ok while the process runsGET /v1/openapi.json on 8080 (the API), as the health check of the Nomad job
natsGET /healthz on 8222GET /healthz on 8222

A service started before NATS answers retries its connection in the background, so its pod starts in any order; the publication, in an init container, retries until NATS answers through the restarts of the pod. See probes.

Metrics#

Each container of Stellar Control serves /metrics in the Prometheus format on port 9100. The pod template carries the prometheus.io/scrape, prometheus.io/port and prometheus.io/path annotations that many Prometheus configurations read. With the Prometheus Operator, a ServiceMonitor on the port metrics of the Service:

YAML
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: stellar-control
  namespace: stellar
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: stellar-control
  endpoints:
    - port: metrics
      path: /metrics
      interval: 30s

NATS serves its own monitoring on 8222 (/varz, /jsz); the Helm chart of NATS adds its Prometheus exporter. See Observability for the metrics and the suggested alerts.

Separate services and scaling#

The all-in-one stellar-control runs with one replica, as in the Nomad job (group mcs, 0 to 1). To scale, run one Deployment per service instead, from the same image and environment, each with its command:

DeploymentCommandReplicasHow they share the work
stellar-apistellar-api1 or moreAll active, stateless: the Service spreads the requests; the Ingress points to it; readiness on /v1/openapi.json
stellar-executorstellar-executor1 or moreAll active: runs spread by identifier, targets held by leases
stellar-schedulerstellar-scheduler1 or moreAll active: each run submitted once
stellar-reconcilerstellar-reconciler1, or 2–3 for failoverSingle active: one leader, the others stand by; the init containers git-sync and publish-config go here
stellar-compute, stellar-alarms, stellar-transferssame1, or 2–3 for failoverSingle active: one holds the lease, the others take over within reconciler.leader_ttl (5 s); no load sharing
stellar-editorstellar-editorexactly 1, strategy RecreateIts drafts are working copies on its disk: a PersistentVolumeClaim on editor.workdir keeps them
simulator-<target>stellar-simulatorexactly 1 per simulated targetOne driver and gateway instance per target

With a separate editor, give it STELLAR__EDITOR__LISTEN=0.0.0.0:8090 and a Service, and the API STELLAR__API__EDITOR_URL=http://<that Service>:8090. A HorizontalPodAutoscaler suits stellar-api only: the standby replicas of the single-active services add availability, not capacity. See Running the Services.

The web editor#

The all-in-one starts the web editor when editor.repository is set, as the Nomad job does: STELLAR__EDITOR__REPOSITORY (the Git remote of the configuration repository), STELLAR__EDITOR__FORGE__KIND and the forge, STELLAR__EDITOR__LISTEN=127.0.0.1:8090, STELLAR__API__EDITOR_URL=http://127.0.0.1:8090, and its service token in STELLAR_FORGE_TOKEN from a Secret. Its working copies are in /var/lib/stellar/editor, on the volume home: an emptyDir loses the drafts not yet proposed at each restart, a PersistentVolumeClaim keeps them.

Hardened containers#

As in the Nomad job, the containers of Stellar Control run as the user of the image (10001), with a read-only root file system, no capability and no privilege escalation; the writable places are volumes of the pod (/tmp, /var/lib/stellar). Add NetworkPolicies so that only the pods of Stellar Control and the ingress controller reach NATS, and only the ingress controller reaches the API.

↑↓ to moveEnter to open