You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

执行kamal deploy时Docker镜像为何总是从头重建?

解决Kamal部署时Docker镜像重复从头构建的问题

问题现象

执行kamal deploy命令时,Docker镜像每次都会从头开始构建,包括重复编译Ruby、安装依赖等耗时步骤,严重拖慢部署效率。

相关配置

Dockerfile

FROM debian:bullseye-slim as base
ENV BUNDLER_VERSION="2.5.5" \
    BUNDLE_DEPLOYMENT="1" \
    BUNDLE_PATH="/usr/local/bundle" \
    BUNDLE_WITHOUT="development" \
    NODE_VERSION="20.11.0" \
    NPM_VERSION="10.4.0" \
    RAILS_ENV="production" \
    RUBY_INSTALL_VERSION="0.9.3" \
    RUBY_VERSION="3.3.0" \
    YARN_VERSION="1.22.19"

ENV PATH="/opt/rubies/ruby-${RUBY_VERSION}/bin:/usr/local/node/bin:${PATH}"

RUN apt-get update && \
    apt-get install -y \
        autoconf \
        build-essential \
        curl \
        fish \
        git \
        libpq-dev \
        libvips \
        pandoc \
        pkg-config \
        postgresql-client \
        vim \
        wget

RUN wget "https://github.com/postmodern/ruby-install/releases/download/v${RUBY_INSTALL_VERSION}/ruby-install-${RUBY_INSTALL_VERSION}.tar.gz" \
  && tar -xzvf "ruby-install-${RUBY_INSTALL_VERSION}.tar.gz" \
  && cd "ruby-install-${RUBY_INSTALL_VERSION}" \
  && make install

RUN ruby-install -p https://github.com/ruby/ruby/pull/9371.diff ruby "${RUBY_VERSION}"

WORKDIR /rails

FROM base as build

RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \
    /tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \
    npm install -g "yarn@${YARN_VERSION}" && \
    rm -rf /tmp/node-build-master

COPY Gemfile Gemfile.lock ./
RUN gem install bundler -v "${BUNDLER_VERSION}"
RUN bundle install && \
    rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
    bundle exec bootsnap precompile --gemfile

COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile

COPY . .

RUN bundle exec bootsnap precompile app/ lib/

RUN HOST=example.com \
    BASE_URL=https://example.com \
    RAILS_MASTER_KEY_DUMMY=1 \
    SECRET_KEY_BASE_DUMMY=1 \
    ./bin/rails assets:precompile

FROM base

COPY --from=build /usr/local/bundle /usr/local/bundle
COPY --from=build /rails /rails

RUN useradd rails --create-home --shell /bin/bash && \
    chown -R rails:rails db log storage tmp
USER rails:rails

ENTRYPOINT ["/rails/bin/docker-entrypoint"]

EXPOSE 3000
CMD ["./bin/rails", "server"]

config/deploy.yml

service: code
image: dorianmariefr/code
servers:
  web:
    hosts:
      - 165.232.149.13
    labels:
      traefik.http.routers.code.rule: Host(`code.dorianmarie.com`)
      traefik.http.routers.code_secure.entrypoints: websecure
      traefik.http.routers.code_secure.rule: Host(`code.dorianmarie.com`)
      traefik.http.routers.code_secure.tls.certresolver: letsencrypt
      traefik.http.routers.code_secure.tls: true
registry:
  username: dorianmariefr
  password:
    - KAMAL_REGISTRY_PASSWORD
env:
  clear:
    HOST: code.dorianmarie.com
    BASE_URL: https://code.dorianmarie.com
  secret:
    - RAILS_MASTER_KEY
    - POSTGRES_PASSWORD
traefik:
  options:
    publish:
      - "443:443"
    volume:
      - "/letsencrypt/acme.json:/letsencrypt/acme.json"
  args:
    entryPoints.web.address: ":80"
    entryPoints.websecure.address: ":443"
    entryPoints.web.http.redirections.entryPoint.to: websecure
    entryPoints.web.http.redirections.entryPoint.scheme: https
    entryPoints.web.http.redirections.entrypoint.permanent: true
    certificatesResolvers.letsencrypt.acme.email: "dorian@dorianmarie.fr"
    certificatesResolvers.letsencrypt.acme.storage: "/letsencrypt/acme.json"
    certificatesResolvers.letsencrypt.acme.httpchallenge: true
    certificatesResolvers.letsencrypt.acme.httpchallenge.entrypoint: web
accessories:
  db:
    image: postgres:16.1
    host: 165.232.149.13
    port: 5432
    env:
      clear:
        POSTGRES_USER: code
        POSTGRES_DB: code_production
      secret:
        - POSTGRES_PASSWORD
    directories:
      - data:/var/lib/postgresql/data

解决方案

1. 启用Kamal构建缓存

在config/deploy.yml中添加build配置,开启远程缓存功能,让Kamal复用之前构建的镜像层:

build:
  cache: true

该配置会让Kamal在构建时调用Docker的构建缓存机制,只要基础层(如Ruby安装、系统依赖)未发生变化,就不会重新执行编译步骤。

2. 优化Dockerfile分层顺序

确保Dockerfile中不变的步骤放在最前面,Docker会自动缓存这些层,仅当后续可变步骤(如代码复制、资产预编译)发生变化时才重新执行:

  • 保留base阶段的系统依赖安装、Ruby安装步骤在最前端,只要版本号不变,这些层就会被永久缓存。
  • build阶段先复制Gemfile/Gemfile.lock、package.json/yarn.lock,再执行依赖安装,仅当依赖文件更新时才会重新安装依赖。

3. 预构建Ruby基础镜像(可选)

如果Ruby版本和补丁长期稳定,可以单独构建包含Ruby的基础镜像并推送到镜像仓库,之后在Dockerfile中直接引用该镜像,彻底避免每次部署都编译Ruby:

# 替换原base阶段,使用预构建的Ruby基础镜像
FROM your-registry/ruby-3.3.0-bullseye:latest as base
# 后续构建步骤保持不变

验证

修改配置后重新执行kamal deploy,观察构建日志,若出现Using cache的提示,说明已成功复用镜像层,不会再从头编译Ruby。

内容的提问来源于stack exchange,提问作者Dorian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 22:12:06