同一个 Dockerfile 上的不同条件

Different conditionals on the same Dockerfile

我有一个 Dockerfile,其中包含一些我想有条件地使用的命令:

  1. FROM + image_name(我有一个 M1 芯片 MacOS,所以我需要添加 --platform=linux/amd64 但我想部署在不需要它的 AWS EC2 linux 实例中)
  2. 在生产环境中,我想 运行 我的项目使用 nginx,所以我希望 Dockerfile 以此 RUN mkdir -p tmp/sockets 结尾。但是为了测试,我不需要 nginx,所以我希望我的 Dockerfile 以此结尾
# Expose port 
EXPOSE 3000

# Start rails
CMD ["rails", "server", "-b", "0.0.0.0"]

我想使用 multi stage dockerfile 来解决 FROM image 问题,但是生成的 Dockerfile 非常冗长,因为它除了 FROM image 部分外基本相同。 对于 nginx 部分,我想使用 shell 脚本,但我不确定如何编写公开端口和最终命令来启动 rails。 这些是文件:

run_dockerfile.sh

#!/bin/bash

if [ ${RUN_DOCKERFILE} = "PROD" ]; then 
    mkdir -p tmp/sockets
else
    ????
fi

我的 Dockerfile 看起来像这样:

# Start from the official ruby image
# To run Dockerfile with arm64 architecture (M1 chip MacOS for example)
FROM --platform=linux/amd64 ruby:2.6.6 AS ARM64

# Set environment
ARG BUILD_DEVELOPMENT
# if --build-arg BUILD_DEVELOPMENT=1, set RAILS_ENV to 'development' or set to null otherwise.
ENV RAILS_ENV=${BUILD_DEVELOPMENT:+development}
# if RAILS_ENV is null, set it to 'production' (or leave as is otherwise).
ENV RAILS_ENV=${RAILS_ENV:-production}

# Update and install JS & DB
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

# Create a directory for the application and use it
RUN mkdir /myapp
WORKDIR /myapp

# Gemfile and lock file need to be present, they'll be overwritten immediately
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock

# Install gem dependencies
RUN gem install bundler:2.2.32
RUN bundle install
RUN curl https://deb.nodesource.com/setup_12.x | bash
ADD https://dl.yarnpkg.com/debian/pubkey.gpg /tmp/yarn-pubkey.gpg
RUN apt-key add /tmp/yarn-pubkey.gpg && rm /tmp/yarn-pubkey.gpg
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update -qq && apt-get install -y yarn  && apt-get install -y npm
RUN yarn add bootstrap

COPY . /myapp

# So that webpacker compiles
RUN yarn config set ignore-engines true
RUN rm -rf bin/webpack*
RUN rails webpacker:install
RUN bundle exec rails webpacker:compile
RUN bundle exec rake assets:precompile

# This script runs every time the container is created, necessary for rails
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]


# Run run_dockerfile.sh
COPY run_dockerfile.sh run_dockerfile.sh
RUN chmod u+x run_dockerfile.sh && ./run_dockerfile.sh


##################################################

# Start from the official ruby image
# To run Dockerfile without arm64 architecture
FROM ruby:2.6.6 AS AMD64

# Set environment
ARG BUILD_DEVELOPMENT
# if --build-arg BUILD_DEVELOPMENT=1, set RAILS_ENV to 'development' or set to null otherwise.
ENV RAILS_ENV=${BUILD_DEVELOPMENT:+development}
# if RAILS_ENV is null, set it to 'production' (or leave as is otherwise).
ENV RAILS_ENV=${RAILS_ENV:-production}

# Update and install JS & DB
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

# Create a directory for the application and use it
RUN mkdir /myapp
WORKDIR /myapp

# Gemfile and lock file need to be present, they'll be overwritten immediately
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock

# Install gem dependencies
RUN gem install bundler:2.2.32
RUN bundle install
RUN curl https://deb.nodesource.com/setup_12.x | bash
ADD https://dl.yarnpkg.com/debian/pubkey.gpg /tmp/yarn-pubkey.gpg
RUN apt-key add /tmp/yarn-pubkey.gpg && rm /tmp/yarn-pubkey.gpg
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update -qq && apt-get install -y yarn  && apt-get install -y npm
RUN yarn add bootstrap

COPY . /myapp

# So that webpacker compiles
RUN yarn config set ignore-engines true
RUN rm -rf bin/webpack*
RUN rails webpacker:install
RUN bundle exec rails webpacker:compile
RUN bundle exec rake assets:precompile

# This script runs every time the container is created, necessary for rails
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]

# Run run_dockerfile.sh
COPY run_dockerfile.sh run_dockerfile.sh
RUN chmod u+x run_dockerfile.sh && ./run_dockerfile.sh

我有什么办法可以做到 .sh 或者有什么正确的建议吗?谢谢!

从你描述问题的方式来看,你根本不需要很多特殊情况。

一个重要的细节是,当您 运行 一个容器时,很容易覆盖图像的 CMD。例如,如果您有 two Compose files,您可以只设置服务的 command:

# docker-compose.yml
version: '3.8'
services:
  myapp:
    image: registry.example.com/myapp:${MYAPP_TAG:-latest}
    ports: ['3000:80']
# docker-compose.override.yml
# for developer use
version: '3.8'
services:
  myapp:
    build: .
    command: rails server -b 0.0.0.0 -p 80

您列出的其他变体应该无关紧要。如果您在 x86-64 主机上构建映像 FROM --platform=linux/amd64,并明确指定本机平台,您应该会得到一致的结果; RUN mkdir 您不会使用的目录是无害的。一个不一致的地方似乎是容器端口,但您可以明确告诉 rails server 使用哪个端口以便它匹配。我会在所有环境中使用相同的图像。

FROM --platform=linux/amd64 ruby:2.6.6 # even on an Intel/AMD host system
...
RUN mkdir tmp/sockets                  # even if it's unused
CMD ["nginx", "-g", "daemon off;"]     # can be overridden when the container runs