Everything from the earlier stories at once, across three NATS clusters in three Kubernetes clusters: a five-server one in the home cluster, a development NATS cluster pinned to one zone, and a production NATS cluster in another region whose placement tag is not its name. Four services run in two environments, each service an account.
Trust roots and NATS clusters #
The trust roots and the gateway list are the same in every Kubernetes cluster; GitOps keeps them alike. The gateway certificates come from the Issuer nats-gateway-ca in nats-system of each Kubernetes cluster, a private CA, as in the supercluster story.
01-natsoperatortrust.yaml
apiVersion: nats.mikluko.io/v1beta1
kind: NatsOperatorTrust
metadata:
name: acme
namespace: nats-system
spec:
operatorJWT: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ...
systemAccountJWT: eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ...
kubectl --context prod-east apply -f https://nats-operator.io/docs/stories/09-acceptance/01-natsoperatortrust.yaml
kubectl --context dev-east apply -f https://nats-operator.io/docs/stories/09-acceptance/01-natsoperatortrust.yaml
kubectl --context prod-west apply -f https://nats-operator.io/docs/stories/09-acceptance/01-natsoperatortrust.yaml01-prod-east.yaml
# One NatsCluster per Kubernetes cluster, each in its own GitOps tree.
# Only what differs between them is commented.
apiVersion: cluster.nats.mikluko.io/v1beta1
kind: NatsCluster
metadata:
name: prod-east # in the home cluster
namespace: nats-system
spec:
version: 2.15.0
replicas: 5
resources:
requests:
cpu: "6"
memory: 29Gi
limits:
memory: 29Gi
jetstream:
# Overrides the memory store derived from limits.memory.
limits:
maxMemoryStore: 10Gi
volumeClaimTemplate:
spec:
storageClassName: gp3
resources:
requests:
storage: 500Gi
serverTags:
cluster: prod-east
region: us-east-2
auth:
trustRef:
name: acme
systemCredentials:
secretKeyRef:
name: cluster-controller-creds
gateway:
discovery: Explicit
remotes:
- name: prod-east
url: tls://nats.prod-east.acme.example:7222
- name: dev-east
url: tls://nats.dev-east.acme.example:7222
- name: prod-west
url: tls://nats.prod-west.acme.example:7222
tls:
certManager:
issuerRef:
kind: Issuer
name: nats-gateway-ca
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
external-dns.alpha.kubernetes.io/hostname: nats.prod-east.acme.example
advertise: nats.prod-east.acme.example:7222
kubectl --context prod-east apply -f https://nats-operator.io/docs/stories/09-acceptance/01-prod-east.yaml01-dev-east.yaml
apiVersion: cluster.nats.mikluko.io/v1beta1
kind: NatsCluster
metadata:
name: dev-east
namespace: nats-system
spec:
version: 2.15.0
replicas: 3
resources:
requests:
cpu: "1500m"
memory: 3Gi
limits:
memory: 3Gi
jetstream:
volumeClaimTemplate:
spec:
storageClassName: gp3
resources:
requests:
storage: 100Gi
serverTags:
cluster: dev-east
region: us-east-2
# Scheduling passes through: dev pins every server to one zone.
podTemplate:
spec:
nodeSelector:
topology.kubernetes.io/zone: us-east-2a
auth:
trustRef:
name: acme
systemCredentials:
secretKeyRef:
name: dev-east-cluster-controller-creds
gateway:
discovery: Explicit
remotes:
- name: prod-east
url: tls://nats.prod-east.acme.example:7222
- name: dev-east
url: tls://nats.dev-east.acme.example:7222
- name: prod-west
url: tls://nats.prod-west.acme.example:7222
tls:
certManager:
issuerRef:
kind: Issuer
name: nats-gateway-ca
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
external-dns.alpha.kubernetes.io/hostname: nats.dev-east.acme.example
advertise: nats.dev-east.acme.example:7222
kubectl --context dev-east apply -f https://nats-operator.io/docs/stories/09-acceptance/01-dev-east.yaml01-prod-west.yaml
apiVersion: cluster.nats.mikluko.io/v1beta1
kind: NatsCluster
metadata:
name: prod-west
namespace: nats-system
spec:
version: 2.15.0
replicas: 3
resources:
requests:
cpu: "1500m"
memory: 3Gi
limits:
memory: 3Gi
jetstream:
volumeClaimTemplate:
spec:
storageClassName: gp3
resources:
requests:
storage: 100Gi
# Tags are free: this NATS cluster's placement tag is not its name.
serverTags:
cluster: west
region: us-west-2
auth:
trustRef:
name: acme
systemCredentials:
secretKeyRef:
name: prod-west-cluster-controller-creds
gateway:
discovery: Explicit
remotes:
- name: prod-east
url: tls://nats.prod-east.acme.example:7222
- name: dev-east
url: tls://nats.dev-east.acme.example:7222
- name: prod-west
url: tls://nats.prod-west.acme.example:7222
tls:
certManager:
issuerRef:
kind: Issuer
name: nats-gateway-ca
service:
type: LoadBalancer
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: external
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
external-dns.alpha.kubernetes.io/hostname: nats.prod-west.acme.example
advertise: nats.prod-west.acme.example:7222
kubectl --context prod-west apply -f https://nats-operator.io/docs/stories/09-acceptance/01-prod-west.yamlEach NatsCluster reports every other member’s gateways connected.
01-status-natscluster-prod-west.yaml
status:
observedGeneration: 1
conditions:
- type: Ready
status: "True"
reason: AllServersReady
- type: Settled
status: "True"
reason: AllGroupsCurrent
- type: GatewaysConnected
status: "True"
reason: AllMembersReachable
message: 2 of 2 remote members connected
gateways:
- name: dev-east
connected: true
inbound: 3
outbound: 3
- name: prod-east
connected: true
inbound: 5
outbound: 3
endpoints:
client: nats://prod-west.nats-system.svc:4222
gateway: nats.prod-west.acme.example:7222
The auth plane #
The NATS operator, the system account, and the production account chain: checks exports a service to monitoring, which exports streams and services to core and to the collector. The development chain repeats it under -dev names and is left out.
01-auth.yaml
# All of it in the home cluster, prod-east.
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsOperator
metadata:
name: acme
namespace: nats-system
spec:
systemAccountRef:
name: sys
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsSystemAccount
metadata:
name: sys
namespace: nats-system
spec:
operatorRef:
name: acme
---
# The production chain of four services; the development chain is left out.
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsAccount
metadata:
name: checks-prod
namespace: nats-system
spec:
operatorRef:
name: acme
exports:
- name: run
type: Service
subject: "checks.run.>"
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsAccount
metadata:
name: monitoring-prod
namespace: nats-system
spec:
operatorRef:
name: acme
limits:
jetstream:
memoryStorage: 36Gi
diskStorage: 300Gi
exports:
- name: executions
type: Stream
subject: "monitoring.execution.>"
- name: state-changes
type: Stream
subject: "monitoring.state.>"
- name: execute
type: Service
subject: "monitoring.execute"
- name: configure
type: Service
subject: "monitoring.configure"
imports:
- accountRef:
kind: NatsAccount
name: checks-prod
export: run
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsAccount
metadata:
name: core-prod
namespace: nats-system
spec:
operatorRef:
name: acme
imports:
- accountRef:
kind: NatsAccount
name: monitoring-prod
export: state-changes
- accountRef:
kind: NatsAccount
name: monitoring-prod
export: configure
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsAccount
metadata:
name: collector-prod
namespace: nats-system
spec:
operatorRef:
name: acme
limits:
jetstream:
diskStorage: 50Gi
imports:
- accountRef:
kind: NatsAccount
name: monitoring-prod
export: executions
kubectl --context prod-east apply -f https://nats-operator.io/docs/stories/09-acceptance/01-auth.yamlEvery account reaches every server of the supercluster through the resolver.
01-status-natsaccount-monitoring-prod.yaml
status:
observedGeneration: 1
conditions:
- type: Ready
status: "True"
reason: Distributed
- type: Distributed
status: "True"
reason: AllServersCurrent
message: 11 of 11 servers hold this JWT
distribution:
servers: 11
current: 11
Every account carries a service and a readonly user, and every other Kubernetes cluster two controller users.
01-users.yaml
# Every account carries the same two users, `service` and `readonly`. The
# monitoring team's live in its own namespace, admitted by the grant below.
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: monitoring-prod-service
namespace: monitoring
spec:
accountRef:
kind: NatsAccount
namespace: nats-system
name: monitoring-prod
permissions:
publish:
allow: [">"]
subscribe:
allow: [">"]
credentials:
secretKeyRef:
name: nats-service-creds
---
# The readonly preset: publish only to the JetStream introspection
# subjects, and subscribe only to replies under `_INBOX.readonly`.
# `permissions` is refused beside a preset.
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: monitoring-prod-readonly
namespace: monitoring
spec:
accountRef:
kind: NatsAccount
namespace: nats-system
name: monitoring-prod
preset: readonly
credentials:
secretKeyRef:
name: nats-readonly-creds
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: checks-prod-service
namespace: nats-system
spec:
accountRef:
kind: NatsAccount
name: checks-prod
permissions:
publish:
allow: [">"]
subscribe:
allow: [">"]
credentials:
secretKeyRef:
name: checks-prod-service-creds
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: checks-prod-readonly
namespace: nats-system
spec:
accountRef:
kind: NatsAccount
name: checks-prod
preset: readonly
credentials:
secretKeyRef:
name: checks-prod-readonly-creds
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: core-prod-service
namespace: nats-system
spec:
accountRef:
kind: NatsAccount
name: core-prod
permissions:
publish:
allow: [">"]
subscribe:
allow: [">"]
credentials:
secretKeyRef:
name: core-prod-service-creds
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: core-prod-readonly
namespace: nats-system
spec:
accountRef:
kind: NatsAccount
name: core-prod
preset: readonly
credentials:
secretKeyRef:
name: core-prod-readonly-creds
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: collector-prod-service
namespace: nats-system
spec:
accountRef:
kind: NatsAccount
name: collector-prod
permissions:
publish:
allow: [">"]
subscribe:
allow: [">"]
credentials:
secretKeyRef:
name: collector-prod-service-creds
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: collector-prod-readonly
namespace: nats-system
spec:
accountRef:
kind: NatsAccount
name: collector-prod
preset: readonly
credentials:
secretKeyRef:
name: collector-prod-readonly-creds
---
# The controllers of the remote Kubernetes clusters, two per remote (story 6).
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: dev-east-cluster-controller
namespace: nats-system
spec:
accountRef:
kind: NatsSystemAccount
name: sys
preset: cluster-controller
credentials:
secretKeyRef:
name: dev-east-cluster-controller-creds
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: dev-east-jetstream-controller
namespace: nats-system
spec:
accountRef:
kind: NatsSystemAccount
name: sys
preset: jetstream-controller
credentials:
secretKeyRef:
name: dev-east-jetstream-controller-creds
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: prod-west-cluster-controller
namespace: nats-system
spec:
accountRef:
kind: NatsSystemAccount
name: sys
preset: cluster-controller
credentials:
secretKeyRef:
name: prod-west-cluster-controller-creds
---
apiVersion: auth.nats.mikluko.io/v1beta1
kind: NatsUser
metadata:
name: prod-west-jetstream-controller
namespace: nats-system
spec:
accountRef:
kind: NatsSystemAccount
name: sys
preset: jetstream-controller
credentials:
secretKeyRef:
name: prod-west-jetstream-controller-creds
---
# What lets the monitoring namespace's users reach the accounts in
# nats-system.
apiVersion: nats.mikluko.io/v1beta1
kind: NatsReferenceGrant
metadata:
name: monitoring-users
namespace: nats-system
spec:
from:
- group: auth.nats.mikluko.io
kind: NatsUser
namespace: monitoring
to:
- group: auth.nats.mikluko.io
kind: NatsAccount
name: monitoring-prod
- group: auth.nats.mikluko.io
kind: NatsAccount
name: monitoring-dev
kubectl --context prod-east apply -f https://nats-operator.io/docs/stories/09-acceptance/01-users.yamlJetStream and balancing #
The monitoring team adopts the streams its application created, pools them, and balances within its account; the platform team balances each NATS cluster.
02-jetstream.yaml
# The monitoring team's JetStream, in prod-east. Its streams were created by
# the application at runtime before any of this existed, so they are adopted.
apiVersion: nats.mikluko.io/v1beta1
kind: NatsConnection
metadata:
name: prod
namespace: monitoring
spec:
servers: ["nats://prod-east.nats-system.svc:4222"]
credentials:
secretKeyRef:
name: nats-service-creds
---
apiVersion: jetstream.nats.mikluko.io/v1beta1
kind: NatsStream
metadata:
name: requests
namespace: monitoring
labels:
traffic: requests
spec:
connectionRef:
name: prod
adoptionPolicy: Adopt
name: REQUESTS
---
apiVersion: jetstream.nats.mikluko.io/v1beta1
kind: NatsStream
metadata:
name: responses
namespace: monitoring
labels:
traffic: responses
spec:
connectionRef:
name: prod
adoptionPolicy: Adopt
name: RESPONSES
---
apiVersion: jetstream.nats.mikluko.io/v1beta1
kind: NatsBalancer
metadata:
name: prod
namespace: monitoring
spec:
connectionRef:
name: prod
pools:
- name: requests
selector:
matchLabels:
traffic: requests
- name: responses
selector:
matchLabels:
traffic: responses
---
# The platform team's, one per NATS cluster; prod-east shown.
apiVersion: nats.mikluko.io/v1beta1
kind: NatsConnection
metadata:
name: prod-east-sys
namespace: nats-system
spec:
servers: ["nats://prod-east.nats-system.svc:4222"]
credentials:
secretKeyRef:
name: jetstream-controller-creds
---
apiVersion: jetstream.nats.mikluko.io/v1beta1
kind: NatsSystemBalancer
metadata:
name: prod-east
namespace: nats-system
spec:
connectionRef:
name: prod-east-sys
moves:
placement: true
leader: true
kubectl --context prod-east apply -f https://nats-operator.io/docs/stories/09-acceptance/02-jetstream.yaml02-status-natsstream-requests.yaml
status:
observedGeneration: 2
conditions:
- type: Ready
status: "True"
reason: Synced
- type: Adopted
status: "True"
reason: FoundUnowned
message: adopted existing stream REQUESTS; spec written from the server
- type: Synced
status: "True"
reason: MatchesSpec
ownership:
origin: Adopted
server:
leader: prod-east-2
02-status-natsbalancer.yaml
status:
observedGeneration: 1
conditions:
- type: Ready
status: "True"
reason: Balancing
- type: Holding
status: "False"
reason: Settled
- type: Overlapping
status: "False"
reason: PoolsDisjoint
# One stream a pool: skewed by one whichever server leads it. Every stream
# of the account has a resource in a pool, so there is no (default) pool.
pools:
- name: requests
streams: 1
leaderSkew: 1
- name: responses
streams: 1
leaderSkew: 1
02-status-natssystembalancer.yaml
status:
observedGeneration: 1
conditions:
- type: Ready
status: "True"
reason: Balancing
- type: Holding
status: "False"
reason: Settled
# monitoring-prod, the one account with streams in prod-east, carries no
# jetstream-stepdown export: no leader is moved.
capabilities:
placement: true
leader: None
leaderReason: 1 of 1 accounts carry no jetstream-stepdown export; their leaders are not moved