Documentation
¶
Overview ¶
Package registrymirror routes container image pulls through a pull-through registry cache on the LAN.
Why ¶
ephemerd nodes re-pull the same base image constantly. A production linux-amd64 node measured 294 GB inbound over 4.1 days across ~330 jobs — roughly 890 MB per job — and the single biggest contributor was one 1.1 GB CI image pulled 163 times in seven days, because dind pulls into a per-job containerd namespace and the per-repo cache was not retaining content. Layers that a LAN cache would serve at wire speed were crossing the WAN dozens of times a day per node. Pointing every pull at a pull-through cache makes the first pull the only WAN pull, and takes the node out of Docker Hub's anonymous rate limit besides.
How ¶
containerd's docker resolver picks a registry endpoint through a docker.RegistryHosts function: given the host part of a reference ("docker.io"), it returns an ordered list of endpoints to try. The resolver walks that list and moves to the next entry whenever one fails to answer, 404s, or returns any 4xx/5xx. So "mirror first, origin second" is expressed directly as a two-element host list, and fail-open falls out of the resolver's own retry loop — no health checking, no circuit breaker, no state.
The mirror entry is advertised with pull+resolve capabilities only. Pushes never touch it: a pull-through cache is not somewhere to publish, and the push path (pkg/dind/registry.go) is deliberately left on the origin registry.
No-mirror path ¶
Every entry point is nil-safe and returns no options when no mirror is configured, so an unconfigured node builds exactly the same pull call it built before this package existed.
Index ¶
- func RegistryHostFromRef(ref string) string
- type Creds
- type Mirror
- func (m *Mirror) Enabled() bool
- func (m *Mirror) Endpoint(registry string) (*url.URL, bool)
- func (m *Mirror) LogPull(ref string)
- func (m *Mirror) PullOpts(creds Creds) []client.RemoteOpt
- func (m *Mirror) RegistryHosts(creds Creds) docker.RegistryHosts
- func (m *Mirror) Resolver(creds Creds) remotes.Resolver
- func (m *Mirror) String() string
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RegistryHostFromRef ¶
RegistryHostFromRef peels the registry hostname off an image reference, applying Docker's rule that a first segment without a dot, colon, or the literal name "localhost" is not a host at all but the start of a Docker Hub repository path.
It exists here so the mirror lookup agrees with the host containerd's own reference parser will hand to RegistryHosts.
Types ¶
type Creds ¶
Creds supplies a username/secret pair for a registry host. It matches the shape docker.WithAuthCreds wants. A nil Creds means anonymous.
type Mirror ¶
type Mirror struct {
// contains filtered or unexported fields
}
Mirror is a resolved, immutable mirror policy shared by every pull path. A nil *Mirror means "no mirror configured" and every method on it is a no-op, so callers never need a branch.
func New ¶
func New(cfg config.RegistryMirrorConfig, log *slog.Logger) *Mirror
New builds a Mirror from configuration. It returns nil when mirroring is disabled or nothing is mapped — the caller keeps the nil and every method stays a no-op.
cfg must have passed config validation; malformed endpoints are dropped here with a warning rather than failing, because by this point the daemon is already running and degrading to origin pulls beats refusing to start.
func (*Mirror) Endpoint ¶
Endpoint returns the mirror base URL serving an upstream registry host, and whether one is configured. The argument is normalized, so both "docker.io" and "index.docker.io" find a docker.io mapping. Nil-safe.
func (*Mirror) LogPull ¶
LogPull emits one line per pull naming the endpoint actually tried first, so an operator can confirm from the daemon log that traffic is going to the cache. No-op when no mirror covers the reference.
func (*Mirror) PullOpts ¶
PullOpts returns the containerd RemoteOpts that route a pull through the mirror, authenticating with creds where credentials are needed.
It returns nil — meaning "add nothing to the pull" — when no mirror is configured AND no credentials were supplied. That is the important property for the no-mirror path: an unconfigured node's client.Pull call is byte-identical to what it was before this package existed, right down to containerd constructing its own default resolver internally.
Pass nil creds for an anonymous pull.
func (*Mirror) RegistryHosts ¶
func (m *Mirror) RegistryHosts(creds Creds) docker.RegistryHosts
RegistryHosts returns the docker.RegistryHosts function containerd's resolver consults for each reference. For a mirrored registry the list is [mirror, origin] (or [mirror] with fallback disabled); for every other registry it is exactly the containerd default, so a node with a docker.io mirror still reaches ghcr.io the ordinary way.
Nil-safe: a nil Mirror yields the containerd default for every host, which is what makes this usable as the single code path for "pull with credentials, no mirror" too.