-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDockerfile
More file actions
245 lines (212 loc) · 11.1 KB
/
Copy pathDockerfile
File metadata and controls
245 lines (212 loc) · 11.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# syntax=docker.io/docker/dockerfile:1
# Multi-stage build for every Go service + the vendored Qdrant image.
# Each service is an independent bake target (see docker-bake.hcl) so CI
# can push just one at a time without rebuilding the rest.
# Global ARG — must come BEFORE the first FROM so it's in scope for any
# stage that references ${GO_VERSION} in its own FROM. Stages that need
# to consume the value inside RUN/COPY must re-declare `ARG GO_VERSION`
# after their FROM (none of ours do — only the FROM uses it).
ARG GO_VERSION=1.26
# ─────────────────────────────────────────────────────────────────────────
# Qdrant — a curl-enabled Qdrant image for dev / k8s healthchecks.
# Kept from the original Dockerfile so compose and prod share a tag.
# ─────────────────────────────────────────────────────────────────────────
FROM qdrant/qdrant:latest AS qdrant
RUN apt-get update \
&& apt-get install -y --no-install-recommends curl \
&& rm -rf /var/lib/apt/lists/*
# ─────────────────────────────────────────────────────────────────────────
# Shared Go builder for every Falcon service. Layout (post-COPY):
# /src/packages/ — hblabs.co/falcon/packages (all shared libs)
# /src/<service>/ — hblabs.co/falcon/<service>
#
# All service go.mod files use `replace ../packages` so we must copy
# the shared module BEFORE running `go mod tidy`. Build caches are
# mounted so repeat builds reuse the GOCACHE and GOMODCACHE.
# ─────────────────────────────────────────────────────────────────────────
FROM golang:${GO_VERSION}-alpine AS main-builder
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
apk add --no-cache git
# Shared module — every service references it via `replace
# hblabs.co/falcon/packages => ../packages` in its go.mod.
COPY packages/ /src/packages/
# Per-service sources. Copying whole trees is fine; the build cache
# keys off file contents, not the COPY instruction itself.
COPY falcon-admin/ /src/falcon-admin/
COPY falcon-api/ /src/falcon-api/
COPY falcon-config/ /src/falcon-config/
COPY falcon-dispatch/ /src/falcon-dispatch/
COPY falcon-landing/ /src/falcon-landing/
COPY falcon-match-engine/ /src/falcon-match-engine/
COPY falcon-normalizer/ /src/falcon-normalizer/
COPY falcon-realtime/ /src/falcon-realtime/
COPY falcon-signal/ /src/falcon-signal/
COPY falcon-storage/ /src/falcon-storage/
# falcon-scout is a Go workspace (go.work at falcon-scout/), binding
# the main `scout/` binary together with every platform scraper in
# `platforms/<domain>/`. We copy the whole tree so go.work can resolve
# every module at build time.
COPY falcon-scout/ /src/falcon-scout/
# Each service gets a `go mod download` and a build. Splitting into
# separate RUN steps lets BuildKit cache per-service when only one
# changes. CGO disabled everywhere since we only ship static binaries.
ENV CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH}
WORKDIR /src/falcon-admin
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-admin .
WORKDIR /src/falcon-api
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-api .
WORKDIR /src/falcon-config
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-config .
WORKDIR /src/falcon-dispatch
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-dispatch .
# falcon-landing — single-binary static site server (Go templates + embed).
# The base64-encoded logo lives at falcon-signal/email/assets/falcon_logo.txt;
# we refresh the copy in falcon-landing/landing/static/ right before building
# so the go:embed pulls in the freshest asset without needing a checked-in
# binary PNG.
WORKDIR /src/falcon-landing
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
cp /src/falcon-signal/email/assets/falcon_logo.txt landing/static/falcon_logo.txt && \
go mod tidy && \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-landing .
WORKDIR /src/falcon-match-engine
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-match-engine .
WORKDIR /src/falcon-normalizer
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-normalizer .
WORKDIR /src/falcon-realtime
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-realtime .
WORKDIR /src/falcon-signal
# No -trimpath for signal: email/config.go uses runtime.Caller(0) to
# locate the assets/ folder at startup. -trimpath would strip the
# absolute build path to a module-relative string, which breaks the
# filesystem lookup in the alpine runtime. The copy below lands the
# assets at the same /src/... path the compiled binary remembers.
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && \
go build -buildvcs=false -ldflags="-s -w" -o /out/falcon-signal .
WORKDIR /src/falcon-storage
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go mod tidy && \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-storage .
# falcon-scout — built from the `scout/` subfolder. The parent's go.work
# wires in every platform module (`platforms/<domain>/`) so colly-based
# scrapers resolve without needing individual go mod download calls.
# GOFLAGS=-mod=mod tells the toolchain to respect the workspace.
WORKDIR /src/falcon-scout/scout
RUN --mount=type=cache,target=/root/.cache/go-build \
--mount=type=cache,target=/go/pkg/mod \
go build -trimpath -buildvcs=false -ldflags="-s -w" -o /out/falcon-scout .
# ─────────────────────────────────────────────────────────────────────────
# Shared runtime base. Centralises the alpine deps (user, ca-certs) and
# the BUILD_TIME env that every Go service logs on startup — lets you
# tell at a glance whether a running pod is the image you just pushed.
# BUILD_TIME is injected by docker-bake at build time via a bake HCL
# timestamp() default; falls back to "unknown" if built without bake.
# ─────────────────────────────────────────────────────────────────────────
FROM alpine:3.20 AS runtime-base
ARG BUILD_TIME=unknown
ENV BUILD_TIME=${BUILD_TIME}
# tzdata: required by services that pin a non-UTC timezone via
# time.LoadLocation (today: falcon-signal's cv-reminder loop, which
# gates sending on Berlin business hours). Without it, LoadLocation
# returns ErrLocationNotFound and the service silently falls back to
# UTC — off by ~1–2 h depending on DST.
RUN addgroup -S app && adduser -S app -G app && apk add --no-cache ca-certificates tzdata
# ─────────────────────────────────────────────────────────────────────────
# Per-service runtime images — all inherit from runtime-base. Each
# EXPOSEs a relevant port via metadata only; actual listen is in-binary.
# ─────────────────────────────────────────────────────────────────────────
FROM runtime-base AS falcon-admin
COPY --from=main-builder /out/falcon-admin /app
USER app
# Cluster default: every Falcon HTTP service listens on 8080. Local
# `go run ./falcon-admin` falls back to 8082 (set inside main.go) so
# port-forwarding doesn't collide with falcon-api on the same host.
ENV PORT=8080
EXPOSE 8080
ENTRYPOINT ["/app"]
FROM runtime-base AS falcon-api
COPY --from=main-builder /out/falcon-api /app
USER app
ENV PORT=8080
EXPOSE 8080
ENTRYPOINT ["/app"]
# falcon-config — one-shot bootstrap Job (see deployment/apps/10-config.yaml).
# No PORT / EXPOSE because it doesn't listen on anything; runs to
# completion and exits.
FROM runtime-base AS falcon-config
COPY --from=main-builder /out/falcon-config /app
USER app
ENTRYPOINT ["/app"]
FROM runtime-base AS falcon-dispatch
COPY --from=main-builder /out/falcon-dispatch /app
USER app
ENTRYPOINT ["/app"]
FROM runtime-base AS falcon-landing
COPY --from=main-builder /out/falcon-landing /app
USER app
ENV PORT=8080
EXPOSE 8080
ENTRYPOINT ["/app"]
FROM runtime-base AS falcon-match-engine
COPY --from=main-builder /out/falcon-match-engine /app
USER app
ENTRYPOINT ["/app"]
FROM runtime-base AS falcon-normalizer
COPY --from=main-builder /out/falcon-normalizer /app
USER app
ENTRYPOINT ["/app"]
FROM runtime-base AS falcon-realtime
COPY --from=main-builder /out/falcon-realtime /app
USER app
# Cluster default: uniform 8080 like every other HTTP service. The
# local .env overrides PORT=8090 so dev doesn't collide with api/landing
# which also use 8080 locally.
ENV PORT=8080
EXPOSE 8080
ENTRYPOINT ["/app"]
FROM runtime-base AS falcon-signal
COPY --from=main-builder /out/falcon-signal /app
# email/config.go reads assets at startup from the path packageDir()
# returns — which is the directory of email/config.go at compile time:
# /src/falcon-signal/email. Mirror that layout in the runtime image
# so os.ReadFile(filepath.Join(dir, asset.File)) succeeds.
COPY --from=main-builder --chown=app:app /src/falcon-signal/email/assets /src/falcon-signal/email/assets
USER app
ENTRYPOINT ["/app"]
FROM runtime-base AS falcon-storage
COPY --from=main-builder /out/falcon-storage /app
USER app
ENTRYPOINT ["/app"]
FROM runtime-base AS falcon-scout
COPY --from=main-builder /out/falcon-scout /app
USER app
ENTRYPOINT ["/app"]