Skip to content
Clone
327df0c
128 lines 4.7 KB
# syntax=docker/dockerfile:1
# ---------------------------------------------------------------------------
# KenApi — GitDataAI 主业务后端(cargo workspace,二进制 app:serve / event / migrate / openapi)
#
# docker build -t kenapi:local .
# docker run --rm -p 7051:7051 ... kenapi:local # app serve
# docker run --rm -p 7031:7031 -p 7032:7032 ... kenapi:local app event # 事件系统
# docker run --rm -p 7051:7051 \
# -e APP_DATABASE__URL=postgres://... kenapi:local
#
# 与仓库内其它镜像共用的约定:
# * builder 只负责编译,runtime 只带二进制 + 运行期依赖;
# * BuildKit cache mount 复用 cargo registry / git / target,重复构建不重编依赖;
# * 非 root 运行(uid/gid 10001)、tini 作 PID 1、SIGTERM 优雅退出;
# * 配置从 /app/config 读取,密钥只走环境变量或挂载文件,不烘进镜像层。
# ---------------------------------------------------------------------------
ARG RUST_IMAGE=rust:1-bookworm
ARG RUNTIME_IMAGE=debian:bookworm-slim
FROM node:26-bookworm-slim AS identity-web
WORKDIR /web
# 前端由 pnpm 管理(pnpm-workspace.yaml / pnpm-lock.yaml);Node 25 起不再自带 corepack。
RUN npm install -g pnpm@12
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY view ./view
COPY packages ./packages
COPY openapi.json ./
COPY scripts ./scripts
COPY vite.config.ts tsconfig.json tsconfig.app.json tsconfig.node.json \
orval.config.ts biome.json ./
# 契约闸门:生成客户端必须与提交的 packages/apiclient 一致。
RUN bash scripts/contract-check.sh --client-only
RUN pnpm build
# ---------------------------------------------------------------------------
# builder
# ---------------------------------------------------------------------------
FROM ${RUST_IMAGE} AS builder
# protoc:lib/proto 的 build.rs 会编译 .proto。
# libssl-dev / pkg-config:native-tls 链(reqwest 等处)需要 OpenSSL 头文件。
ENV PROTOC_INCLUDE=/usr/include \
CARGO_TERM_COLOR=always
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
pkg-config \
libssl-dev \
cmake \
perl \
git \
curl \
ca-certificates \
unzip \
protobuf-compiler \
libprotobuf-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /build
# 工具链由 rust-toolchain.toml 固定(nightly-2026-10-27);rustup 在这一层装好,
# 后面所有 cargo 调用都用这个版本,不受基础镜像自带 stable 影响。
COPY rust-toolchain.toml ./
RUN rustup show > /dev/null
COPY Cargo.toml Cargo.lock openapi.json ./
COPY lib ./lib
COPY app ./app
COPY --from=identity-web /web/view/dist ./view/dist
RUN --mount=type=cache,id=kenapi-cargo-registry,target=/usr/local/cargo/registry \
--mount=type=cache,id=kenapi-cargo-git,target=/usr/local/cargo/git \
--mount=type=cache,id=kenapi-target,target=/build/target \
cargo build --release --locked --workspace --bins \
&& mkdir -p /out \
&& cp target/release/app /out/ \
&& /out/app openapi --check \
&& strip /out/app
# ---------------------------------------------------------------------------
# runtime
# ---------------------------------------------------------------------------
FROM ${RUNTIME_IMAGE} AS runtime
ARG APP_UID=10001
ARG APP_GID=10001
# libssl3:native-tls 运行期依赖;git:仓库维护/排障用;
# curl 只服务 HEALTHCHECK,tini 负责转发 SIGTERM 并回收子进程。
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
git \
ca-certificates \
libssl3 \
curl \
tini \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --system --create-home --uid ${APP_UID} --user-group gitdata \
&& mkdir -p /opt/gitdata/repos /opt/gitdata/objects \
&& chown -R gitdata:gitdata /opt/gitdata
WORKDIR /app
# 只带默认配置;config/local/ 与各类密钥由部署方挂载或注入。
COPY --chown=gitdata:gitdata config/default config/default
COPY --from=builder /out/app /usr/local/bin/
LABEL org.opencontainers.image.title="KenApi" \
org.opencontainers.image.description="GitDataAI monolith: business API and login web" \
org.opencontainers.image.version="1.0.0"
ENV RUST_BACKTRACE=0
USER gitdata
EXPOSE 7051 7031 7032
STOPSIGNAL SIGTERM
# 默认 CMD 是 apihost:/healthz 可用。`app event` 角色没有 HTTP /healthz,
# 部署时由 k8s 用 tcpSocket 探针覆盖镜像 HEALTHCHECK。
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD curl -fsS "http://127.0.0.1:${KENAPI_HEALTH_PORT:-7051}/healthz" >/dev/null || exit 1
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["app", "serve"]