我想我很难理解我需要什么文件才能在空实例上使用Rails应用程序运行容器。
我有一个要运行的docker-compose.prod.yml
:
version: "3.8"
services:
db:
image: postgres
environment:
POSTGRES_USER: ${POSTGRES_USER:-default}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-default}
volumes:
- ./tmp/db:/var/lib/postgresql/data
app:
image: "username/repo:${WEB_TAG:-latest}"
depends_on:
- db
command: bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
volumes:
- .:/myapp
- public-data:/myapp/public
- tmp-data:/myapp/tmp
- log-data:/myapp/log
environment:
POSTGRES_USER: ${POSTGRES_USER:-default}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-default}
POSTGRES_HOST: ${POSTGRES_HOST:-default}
web:
build: nginx
volumes:
- public-data:/myapp/public
- tmp-data:/myapp/tmp
ports:
- "80:80"
depends_on:
- app
volumes:
public-data:
tmp-data:
log-data:
db-data:
因此,在该实例中,我有一个docker-compose.prod.yml
文件。由于我正在传递环境和图像web标记的变量,所以我创建了一个包含这些变量的.env
文件。最后,由于我正在构建nginx
,所以我有一个包含图像的文件夹:
FROM arm64v8/nginx
# インクルード用のディレクトリ内を削除
RUN rm -f /etc/nginx/conf.d/*
# Nginxの設定ファイルをコンテナにコピー
ADD nginx.conf /etc/nginx/myapp.conf
# ビルド完了後にNginxを起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/myapp.conf
和配置文件nginx.conf
user root;
worker_processes 1;
events{
worker_connections 512;
}
# ソケット接続
http {
upstream myapp{
server unix:///myapp/tmp/sockets/puma.sock;
}
server { # simple load balancing
listen 80;
server_name localhost;
#ログを記録しようとするとエラーが生じます
#root /myapp/public;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
location / {
proxy_pass http://myapp;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
}
所以,docker-compose.prod.yml
、nginx
目录中有2个文件和.env
文件。
当我执行:docker-compose -f docker-compose.prod.yml --env-file .env run app rake db:create db:migrate
时,它会下载postgres
和app
图像,但一旦它开始为db:create db:migrate
执行rake
,我就会得到以下错误:
Status: Downloaded newer image for user/repo:48
Creating rails-app_db_1 ... done
Creating rails-app_app_run ... done
rake aborted!
No Rakefile found (looking for: rakefile, Rakefile, rakefile.rb, Rakefile.rb)
/usr/local/bundle/gems/rake-13.0.6/exe/rake:27:in `<top (required)>'
(See full trace by running task with --trace)
但当我添加Rakefile
时,它一直在请求其他从属文件,所以要么我需要整个项目本身(从GitHub上的repo中克隆它(,要么我做错了。
欢迎对我需要什么文件或是否需要更改命令有任何想法!非常感谢。
当您的docker-compose.yml
文件显示时
volumes:
- .:/myapp
这意味着映像中/myapp
目录的内容——可能是整个应用程序——将被忽略,并替换为主机目录中的内容。因此,使用此设置,您需要复制整个应用程序源代码,否则将无法工作。
但也包括:
volumes:
- public-data:/myapp/public
您的应用程序的静态资产存储在一个名为Docker的卷中。这很难在系统之间传输,并且会忽略底层映像中的任何更改。
我会在这个设置中更新一些东西,既可以避免大部分卷,也可以让操作变得更容易。
Docker有一个内部网络层,您可以使用容器的Compose服务名称作为主机名在容器之间进行通信。(请参阅Docker文档中Compose中的Networking。(这意味着你可以设置Nginx反向代理,通过正常的HTTP/TCP 与Rails应用程序对话
upstream myapp {
server http://app:9292; # or the port from your config.ru/puma.rb file
}
这消除了对tmp-data
卷的需要。
就像将应用程序代码构建到图像中一样,也可以将静态资产构建到图像。更新Nginx镜像Dockerfile,使其看起来像:
# Artificial build stage to include app; https://stackoverflow.com/a/69303997
ARG appimage
FROM ${appimage} AS app
FROM arm64v8/nginx
# Replace the Nginx configuration
RUN rm -f /etc/nginx/conf.d/*
COPY nginx.conf /etc/nginx/nginx.conf
# Copy in the static assets
COPY --from=app /myapp/public /myapp/public
# Use the default CMD from the base image; no need to rewrite it
这样就不需要public-data
卷了。
在主应用程序映像中,应该声明一个ENTRYPOINT
和CMD
,这样就不需要重复冗长的command:
。对于ENTRYPOINT
,我建议使用一个shell脚本:
#!/bin/sh
# entrypoint.sh
# Remove a stale pid file
rm -f tmp/pids/server.pid
# Run the main container CMD under Bundler
exec bundle exec "$@"
确保此文件是可执行的(chmod +x entrypoint.sh
(,并将其添加到存储库中,可能在Dockerfile
和Gemfile
旁边的顶级目录中。在Dockerfile中,将此脚本声明为ENTRYPOINT
,并使Puma为CMD
:
ENTRYPOINT ["./entrypoint.sh"] # must be JSON-array syntax
CMD ["puma", "-C", "config/puma.rb"] # could be shell syntax
我们唯一没有接触的卷装载是数据库存储和日志目录。对于数据库存储,Docker命名的卷可能是合适的,因为您永远不需要直接查看文件。命名卷在某些平台(MacOS和Windows的某些情况下(上速度要快得多,但在系统之间传输更困难。相反,对于日志,我会使用绑定装载,因为您通常希望直接从主机系统读取它们。
这将docker-compose.yml
文件缩减为:
version: "3.8"
services:
db: { unchanged: from the original question }
app:
image: "username/repo:${WEB_TAG:-latest}"
depends_on:
- db
volumes:
- ./tmp/log:/myapp/log
environment:
POSTGRES_USER: ${POSTGRES_USER:-default}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-default}
POSTGRES_HOST: ${POSTGRES_HOST:-default}
web:
image: "username/web:${WEB_TAG:-latest}"
ports:
- "80:80"
depends_on:
- app
我们已经删除了几乎所有的volumes:
,以及对主机目录的所有引用,除了数据库存储和输出日志的目录。我们还删除了command:
覆盖,因为它重复Dockerfile中的内容。(在其他类似的SO问题中,我可能会删除不必要的networks:
、container_name:
和hostname:
声明,以及过时的links:
和expose:
选项。(
如果你已经做到了,你需要一种方法来构建你的图像并将它们推送到存储库。您可以有第二个Compose文件,仅描述如何构建图像:
# docker-compose.override.yml
# in the same directory as docker-compose.yml
version: '3.8'
services:
app:
build: .
web:
build:
context: ./nginx
args:
appimage: username/repo:${WEB_TAG:-latest}
Compose在这种情况下的一个缺点是它不知道一个图像依赖于另一个图像。这意味着您需要首先手动构建基本映像。
export WEB_TAG=20220305
docker-compose build app
docker-compose build
docker-compose push
这似乎有很多设置。但是,完成了这项工作后,我们唯一需要复制到新系统的就是docker-compose.yml
文件和生产.env
设置。
# on the local system
scp docker-compose.yml .env there:
# on the remote system
export WEB_TAG=20220305
docker-compose run app
rake db:create db:migrate
docker-compose up -d
如果Docker在本地没有副本,它会自动从存储库中提取图像。(在像Kubernetes这样的环境中,每次构建都需要使用一个唯一的图像标记,这很有帮助;docker system prune
可以清理旧的未使用的图像。(如果你给文件提供了默认名称docker-compose.yml
和.env
,那么你就不需要在命令行中提及它们。