Dockerfile
Rama main · réplica de lectura del repositorio original.
# Unified host-agent image. One container, s6-supervised, bundles:
#
# fan-controller — adaptive Dell PowerEdge fan PID
# node_exporter — :9100 host CPU/mem/disk/net + textfile
# cadvisor — :8089 per-container metrics
# ipmi_exporter — :9290 chassis sensors
# smartctl_exporter — :9633 drive SMART
# nvidia_gpu_exporter — :9835 GPU temp/util/mem/power
# vmagent — push all of the above to central Prometheus
# (hardcoded remote_write URL, host label =
# kernel hostname). No per-host config; the
# dashboard is a pure receiver.
#
# Each sub-service probes its hardware on start and `sleep infinity` if
# absent — same image runs on Dell + non-Dell, GPU + headless, Debian +
# Unraid hosts with no per-host build.
#
# Base: Alpine (was Debian-slim through v0.2.0).
# - The fan-controller is pure Go static (CGO_ENABLED=0); the upstream
# exporter binaries are also Go-static. None require glibc.
# - nvidia-smi IS injected glibc-linked by the NVIDIA Container
# Runtime on GPU hosts. We install `gcompat` (musl→glibc shim) so
# nvidia_gpu_exporter's nvidia-smi shellouts still work.
# - Save ~60 MB on the base layer vs debian-slim.
#
# UPX: all Go binaries are compressed with `upx --best --lzma`. Costs
# ~50ms decompression-on-exec. For long-running services, free 30-50%
# image shrink. Skipped if upx fails on a particular binary (some
# binaries with complex section layouts refuse to pack).
# -------- go-builder: compile the Go v2 fan controller --------
# Same source tree the bash script lives in; we copy host-agent/go.mod
# + the Go source dirs. Built static (CGO_ENABLED=0) + stripped so the
# final image stays small. VERSION is stamped into main.version via
# -ldflags so `fan-controller --version` (if we ever add the flag)
# matches /etc/host-agent-version.
FROM golang:1.27-alpine AS go-builder
ARG VERSION=dev
RUN apk add --no-cache upx
WORKDIR /src
COPY go.mod ./
COPY cmd/ cmd/
COPY internal/ internal/
RUN CGO_ENABLED=0 go build \
-trimpath \
-ldflags="-s -w -X main.version=${VERSION}" \
-o /fan-controller \
./cmd/fan-controller \
&& upx --best --lzma /fan-controller || true
# -------- builder: fetch the Go binaries from upstream releases --------
# Run UPX --best --lzma against each. `|| true` so a refusal on any
# single binary doesn't abort the whole build — we get the uncompressed
# version in that case.
FROM alpine:3 AS builder
ARG NODE_EXPORTER_VERSION=1.8.2
ARG CADVISOR_VERSION=0.55.1
ARG IPMI_EXPORTER_VERSION=1.10.0
ARG SMARTCTL_EXPORTER_VERSION=0.13.0
ARG NVIDIA_GPU_EXPORTER_VERSION=1.4.1
ARG VMAGENT_VERSION=1.108.0
RUN apk add --no-cache curl ca-certificates tar xz upx
WORKDIR /tmp
RUN curl -fsSL -o node.tgz \
"https://github.com/prometheus/node_exporter/releases/download/v${NODE_EXPORTER_VERSION}/node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64.tar.gz" \
&& tar -xzf node.tgz \
&& cp "node_exporter-${NODE_EXPORTER_VERSION}.linux-amd64/node_exporter" /node_exporter \
&& upx --best --lzma /node_exporter || true
RUN curl -fsSL -o /cadvisor \
"https://github.com/google/cadvisor/releases/download/v${CADVISOR_VERSION}/cadvisor-v${CADVISOR_VERSION}-linux-amd64" \
&& chmod +x /cadvisor \
&& upx --best --lzma /cadvisor || true
RUN curl -fsSL -o ipmi.tgz \
"https://github.com/prometheus-community/ipmi_exporter/releases/download/v${IPMI_EXPORTER_VERSION}/ipmi_exporter-${IPMI_EXPORTER_VERSION}.linux-amd64.tar.gz" \
&& tar -xzf ipmi.tgz \
&& cp "ipmi_exporter-${IPMI_EXPORTER_VERSION}.linux-amd64/ipmi_exporter" /ipmi_exporter \
&& upx --best --lzma /ipmi_exporter || true
RUN curl -fsSL -o smartctl.tgz \
"https://github.com/prometheus-community/smartctl_exporter/releases/download/v${SMARTCTL_EXPORTER_VERSION}/smartctl_exporter-${SMARTCTL_EXPORTER_VERSION}.linux-amd64.tar.gz" \
&& tar -xzf smartctl.tgz \
&& cp "smartctl_exporter-${SMARTCTL_EXPORTER_VERSION}.linux-amd64/smartctl_exporter" /smartctl_exporter \
&& upx --best --lzma /smartctl_exporter || true
RUN curl -fsSL -o nvidia.tgz \
"https://github.com/utkuozdemir/nvidia_gpu_exporter/releases/download/v${NVIDIA_GPU_EXPORTER_VERSION}/nvidia_gpu_exporter_${NVIDIA_GPU_EXPORTER_VERSION}_linux_x86_64.tar.gz" \
&& tar -xzf nvidia.tgz \
&& cp nvidia_gpu_exporter /nvidia_gpu_exporter \
&& upx --best --lzma /nvidia_gpu_exporter || true
RUN curl -fsSL -o vmutils.tgz \
"https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/v${VMAGENT_VERSION}/vmutils-linux-amd64-v${VMAGENT_VERSION}.tar.gz" \
&& tar -xzf vmutils.tgz vmagent-prod \
&& mv vmagent-prod /vmagent \
&& upx --best --lzma /vmagent || true
# -------- runtime --------
FROM alpine:3
# Short git SHA baked at build time. vmagent reads /etc/host-agent-version
# and stamps it onto every sample as the `version` external_label, so the
# dashboard can show which build is running per host. Defaults to "dev"
# for local builds.
ARG VERSION=dev
ARG S6_OVERLAY_VERSION=3.2.0.2
RUN echo "$VERSION" > /etc/host-agent-version
# OS tools needed by sub-services:
# ipmitool, freeipmi — chassis sensors / Dell raw fan commands
# smartmontools — drive SMART (smartctl_exporter shells out)
# dmidecode — chassis detection by fan-controller
# gcompat — musl→glibc shim (lets glibc-linked
# nvidia-smi run on Alpine; nvidia-smi is
# injected by NVIDIA Container Runtime on
# GPU hosts, not bundled here)
# ca-certificates — TLS to GitHub releases, push endpoints
# mawk, xz, tini, procps — shell tooling for s6 run scripts +
# startup probes
RUN apk add --no-cache \
ipmitool freeipmi smartmontools dmidecode \
gcompat \
ca-certificates mawk xz tini procps
# s6-overlay (PID 1, restarts crashed sub-services individually)
ADD "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz" /tmp/
ADD "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz" /tmp/
RUN tar -Jxpf /tmp/s6-overlay-noarch.tar.xz -C / \
&& tar -Jxpf /tmp/s6-overlay-x86_64.tar.xz -C / \
&& rm /tmp/s6-overlay-*.tar.xz
# Exporter binaries (already UPX-compressed in builder stage)
COPY --from=builder /node_exporter /usr/local/bin/node_exporter
COPY --from=builder /cadvisor /usr/local/bin/cadvisor
COPY --from=builder /ipmi_exporter /usr/local/bin/ipmi_exporter
COPY --from=builder /smartctl_exporter /usr/local/bin/smartctl_exporter
COPY --from=builder /nvidia_gpu_exporter /usr/local/bin/nvidia_gpu_exporter
COPY --from=builder /vmagent /usr/local/bin/vmagent
# Fan controller (Go v2, UPX-compressed) + per-chassis profiles.
COPY --from=go-builder /fan-controller /usr/local/bin/fan-controller
COPY profiles/ /etc/fan-controller/profiles/
RUN chmod +x /usr/local/bin/fan-controller
# s6 service definitions: one per sub-service, each probes its hardware
COPY s6/ /etc/s6-overlay/s6-rc.d/
COPY docker-entrypoint.sh /usr/local/bin/host-agent-entrypoint
RUN chmod +x /usr/local/bin/host-agent-entrypoint
ENV S6_KEEP_ENV=1 \
S6_BEHAVIOUR_IF_STAGE2_FAILS=2 \
S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
ENTRYPOINT ["/usr/local/bin/host-agent-entrypoint"]