OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Services

zun — Summary

Reverse-specification of zun — branch stable/2026.2

  • Repository: https://opendev.org/openstack/zun — specified branch: stable/2026.2 @ 34135a563699 — analysed on: 2026-11-01
  • Scope: all processes and all axes (API, errors, interactions, scenarios, asynchronous processing) plus the rewrite pack. Change history (step 7) not requested.

Overview

Zun is the OpenStack Containers service: a REST API that runs application containers directly on compute nodes, without a virtual machine and without a user-managed cluster (README.rst:12, doc/source/install/overview.rst:5). A user asks for an image, resources, networks and volumes. zun-api stores the request, picks a node with Placement and sends a message. zun-compute on that node then drives the local Docker daemon, or containerd through CRI for capsules (pod-like groups). Containers get Neutron ports through kuryr-libnetwork or a CNI plugin, Cinder volumes are mounted on the node and bind-mounted, and images come from a Docker registry or from Glance. The default runtime is runc, which shares the node's kernel; Kata is an operator option. There is no conductor, no notification and no evacuation: a dead node leaves its containers in place, and the API answers 500 for them.

Architecture

flowchart LR
  client[Client: zun CLI, openstack appcontainer, Skyline, Heat] -->|HTTPS REST, port 9517| api[zun-api]
  client -->|WebSocket, token| ws[zun-wsproxy]
  api -->|REST token check| ks[Keystone]
  api -->|REST candidates and allocations| pl[Placement]
  api -->|REST networks, security groups| ne[Neutron]
  api -->|REST volumes| ci[Cinder]
  api -->|SQL| db[(MariaDB)]
  api -->|AMQP cast and call| mq{{RabbitMQ}}
  mq --> c[zun-compute on each node]
  c -->|SQL| db
  c -->|REST inventory| pl
  c -->|REST ports| ne
  c -->|REST attach, plus os-brick| ci
  c -->|REST image file, optional| gl[Glance]
  c -->|Docker API, unix socket| d[Docker]
  c -->|CRI gRPC, unix socket| cd[containerd]
  d -->|network plugin| ku[kuryr-libnetwork]
  ku --> ne
  cd -->|CNI| cni[zun-cni]
  cni -->|HTTP 127.0.0.1:9036| dm[zun-cni-daemon]
  dm -->|REST ports| ne
  dm -->|SQL| db
  ws -->|SQL| db
  ws -->|Docker API, tcp 2375| d
  d -->|docker pull| rg[Registry, Docker Hub by default]

Processes

ProcessEntry pointRoleListens to / consumes
zun-apizun/cmd/api.py:29, zun/api/wsgi.py:1 (setup.cfg:32)REST API, scheduling in-processport 9517
zun-computezun/cmd/compute.py:32runs containers, inventory, state syncAMQP topic zun-compute and zun-compute.<host>
zun-wsproxyzun/cmd/wsproxy.py:27WebSocket proxy for attach and exec[websocket_proxy], port in zun/conf/websocket_proxy.py:54
zun-cni-daemonzun/cni/cmd/cni_daemon.py:25plugs VIFs for capsules and the cni driverHTTP 127.0.0.1:9036
zun-cnizun/cni/cmd/cni.py:33CNI plugin binary, a thin client of the daemoncalled by containerd or by zun-compute
zun-db-managezun/cmd/db_manage.py:59Alembic migrations—
zun-statuszun/cmd/status.py:65upgrade checks—

External dependencies

SystemProtocolLibraryConfigurationUsage
MariaDB or MySQLSQLoslo.db, SQLAlchemy[database]/connectionall state
RabbitMQAMQPoslo.messagingtransport_urlAPI to node commands
KeystoneRESTkeystonemiddleware, keystoneauth[keystone_authtoken], [keystone_auth]authentication
PlacementRESTkeystoneauth[placement_client]inventory, candidates, allocations
NeutronRESTpython-neutronclient[neutron_client]ports, security groups, networks
CinderREST, plus iSCSI, RBD or otherspython-cinderclient, os-brick[cinder_client], [volume]volumes (optional)
GlanceRESTpython-glanceclient[glance_client], [glance]images (optional)
DockerDocker Engine APIdocker-py[docker]containers, images, networks
containerdCRI gRPC runtime.v1alpha2grpciofixed socket zun/container/cri/driver.py:45capsules (default capsule driver)
kuryr-libnetworkDocker network plugin, called by Docker—[network]containers, default network driver
Docker registryRegistry API, called by the daemon—[docker]/default_registry, registry tableimage pulls

Files of this report

FileContent
01-rest-apiendpoints, functional sheets, lifecycles, spec and code gaps
02-errorsexceptions, situations, translation, asynchronous errors
03-interactionsRPC, database, outbound REST, Docker, CRI, CNI, isolation settings
04-scenariossequence diagrams and diagnostics
05-async-processingRPC handlers, timers, start-up recovery, CNI daemon, WebSocket proxy
07-rewrite-packcontracts, client expectations, conformance strategy
_verificationreference check output
[_inventory/](_inventory/inventory_stable-2026.2.md), [_rewrite/](_rewrite/rewrite-pack-index_stable-2026.2.md)raw inventory; machine-readable contracts

Key figures

  • 63 routes for 73 handlers, 40 microversions, 101 JSON schemas, 82 policy rules ([_rewrite/](_rewrite/rewrite-pack-index_stable-2026.2.md)).
  • 33 RPC methods on the client, 2 of them dead; casts run in a green thread on the node.
  • 6 timers on the nodes and in the CNI daemon, one recovery routine at start-up.
  • 19 tables in models and migrations, 84 migrations, head 3f2b36231bee, 4 model and migration differences.
  • 155 exception classes in the inventory.
  • 221 API unit tests recorded into 256 exchanges, all passing.

Points of attention

  1. Isolation rests on the runtime. With the default runc, all containers on a node share its kernel, and nothing in the scheduler separates projects (zun/conf/container_driver.py:55, zun/conf/scheduler.py:64). Zun sets no seccomp, capabilities, user namespace or PID limit (03 § Isolation). privileged is denied by default (zun/common/policies/container.py:43).
  2. The Docker TCP port is part of the design. zun-wsproxy reaches Docker on 2375, and the install guide opens it without TLS (doc/source/install/compute-install.rst:224, zun/conf/docker.py:49). Access to that port is root on the node [general knowledge].
  3. Console tokens do not expire and are not checked against Keystone (zun/websocket/websocketproxy.py:210, zun/compute/manager.py:933).
  4. Image cache is per node and not per project: with ifnotpresent or never, an image is used as found, whoever pulled it (zun/image/docker/driver.py:90). Choosing a policy is admin-only by default (zun/common/policies/container.py:67).
  5. Real bugs seen at run time: out-of-range microversion and wrong Content-Type crash the error path instead of giving 406 and 415 (zun/api/http_error.py:65, zun/api/utils.py:89). auto_heal never rebuilds (zun/container/docker/driver.py:518).
  6. **NoValidHost is a 202**, then status Error (zun/compute/api.py:57).
  7. **ContainerHostNotUp is a 500**, with an obfuscated message (zun/common/exception.py:371).
  8. No reconciliation with Placement on some failure branches, and no evacuation of a dead node (05).
  9. Full-table reads per node per minute in sync_container_state and in delete_unused_containers (zun/compute/manager.py:1240, zun/compute/manager.py:1216).
  10. Tokens in messages: the user's token travels in the RPC context and may be used long after the request (zun/common/keystone.py:72).

Blind spots / [inferred]

  • No running deployment: frames are reconstructed from the code, no real Docker, Kuryr, Neutron or Cinder was exercised. The API tests mock the scheduler and compute.
  • zun/pci/ and SR-IOV paths were read only where they touch creation.
  • zun/compute/provider_tree.py and zun/scheduler/client/report.py come from Nova and were read through their entry points only.
  • contrib/ (old nova-docker driver) is out of scope. Its FixedIntervalLoopingCall in the inventory is not a Zun process.
  • kuryr-libnetwork is not in the workspace; its behaviour is described from the Zun side only.
  • Mappings marked [inferred] in each file were not followed to the end.

Source: reverse-spec-zun/00-summary_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.