Scale and roll out¶
A service runs as a fixed number of instances, set by resources.autoscaling.minInstances in its
descriptor, and each instance's size is set by resources.instanceType. The instances form one cluster:
the service's entities are sharded across them, and each entity lives on exactly one instance at a time.
Deploys and restarts replace instances one at a time without refusing requests, and scaling adds or
removes instances without touching the others.
{
"name": "orders",
"service": {
"image": "registry.example.com/acme/orders:1.4.2",
"resources": {
"instanceType": "medium",
"autoscaling": { "minInstances": 3 }
}
}
}
Instance count¶
minInstances is honoured as a fixed count. It defaults to 1, which suits a development cluster. Set it
explicitly for production.
maxInstances (default 10) and targetCpuPercent (default 80) are validated and stored, but nothing
acts on them: the platform renders no autoscaler, and a service does not scale on load. maxInstances
must be at least minInstances, and targetCpuPercent between 1 and 100, or the descriptor is refused.
Use an odd count. When the network partitions a service's instances, the side with the majority keeps running and the minority stops, so that two halves never both act as the service. With two instances a partition leaves one on each side, neither is a majority, and both stop until Kubernetes restarts them. Three instances survive losing one. One instance has no partition to survive. See Clusters and instances.
Instance size¶
instanceType names a size, so a descriptor carries no raw CPU and memory numbers. Requests equal
limits:
instanceType |
CPU | Memory |
|---|---|---|
small (default) |
500m | 512Mi |
medium |
1000m | 1024Mi |
large |
2000m | 2048Mi |
For a Python service the size applies to the sidecar, which runs the runtime. Your process's container has a small fixed size of 100m CPU and 128Mi memory.
Deploys roll without downtime¶
Applying a descriptor that changes what the instances run, such as the image or an environment variable,
replaces the instances one at a time. The Deployment rolls with maxSurge: 1 and maxUnavailable: 0:
- One new instance starts beside the existing ones.
- It joins the service's existing cluster and becomes ready.
- Only then is one old instance stopped, and its entities hand off to the instances that remain.
- Repeat until every instance runs the new version.
This holds at one instance too: the new instance joins the old one's cluster before the old one stops, so a single-instance service deploys with no gap.
Each stopping instance keeps serving for five seconds before it is told to stop. A pod leaves its
Service's endpoints the moment its deletion starts, but each node's network proxy learns that up to a
second later, and in that second a request can still be routed to the old instance. The pause, a
preStop sleep, lets those requests be answered rather than refused.
Scaling does not roll¶
Changing minInstances from 3 to 4 starts one new instance, which joins the cluster and takes a share of
the entities. The three running instances keep running. Changing it back stops one. Only a change to
what the instances run causes a rollout.
Restart rolls¶
ankka services restart orders
Restart replaces every instance, with the same rolling procedure as a deploy, even though nothing in the descriptor changed. Use it to pick up a new image pushed under the same tag, or to clear a problem held in memory. Entities rebuild their state from the journal, so a restart loses nothing that was persisted.
Scale with the descriptor, not kubectl¶
The platform restores each service's Deployment to what its descriptor says on every reconciliation, so
kubectl scale is undone within moments. Change minInstances and apply the descriptor again, or pause
the service:
ankka services pause orders # zero instances; the descriptor and the data are kept
ankka services resume orders
See Pause, resume, restart and delete.
No liveness probe¶
Instances have a readiness probe and deliberately no liveness probe. An instance that is slow, for example during a long garbage collection, is left to recover rather than restarted: restarting it would move its entities to other instances and back, which costs more than the pause did.