一个 rails 的项目没有互联网环境,离线部署,只能用光盘刻录数据。以往没有这种部署经验,基本是摸着石头过河。
系统镜像 Centos 7.4 Ruby 2.7.3 Rails 6.1.4
以往的项目都在 AWS 或者阿里云上,基本都是实体机环境直接安装依赖 cap 部署。
这次因为项目依赖较多集成了一些调用 py 的 OCR,和人脸识别功能,数据库还需要对接第三方的 Oracle 数据。因为乱七八糟的环境较多,所以当时考虑用 docker 安装部署。
FROM ruby:2.7-slim
WORKDIR /
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
apt-get update && \
apt-get install -y wget
RUN wget http://test.example.com/chi_sfz.traineddata && \
wget http://test.example.com/chi_sim.traineddata && \
wget http://test.example.com/chi_sim_vert.traineddata
RUN apt-get install -y g++ autoconf automake libtool pkg-config libpng-dev libjpeg62-turbo-dev libtiff5-dev zlib1g-dev libleptonica-dev ca-certificates git libicu-dev libpango1.0-dev libcairo-dev make && \
git clone --depth 1 https://gitee.com/mirrors/Tesseract-OCR.git /tesseract && \
cd /tesseract && \
./autogen.sh && \
./configure && \
make
FROM ruby:2.7-slim
ENV APP_HOME=/app \
DEBIAN_FRONTEND=noninteractive \
RAILS_ENV=production \
EDITOR=vim \
LD_LIBRARY_PATH=/opt/oracle/instantclient \
NODE_OPTIONS="--max-old-space-size=8192" \
BUILD_PACKAGES="build-essential" \
DEV_PACKAGES="apt-utils git curl gnupg ca-certificates tzdata openssl ruby-dev imagemagick nodejs cron vim libpq-dev libxml2-dev libxslt-dev libaio1 cmake python3 python3-dev python3-pip python3-wheel python3-setuptools tesseract-ocr libtesseract-dev" \
REMOVE_PACKAGES="cmake"
COPY --from=0 /tesseract /tesseract
RUN mkdir $APP_HOME && \
echo 'gem: --no-document' >> ~/.gemrc && \
echo -e "registry=https://registry.npm.taobao.org\nsass_binary_site=https://npm.taobao.org/mirrors/node-sass/\nphantomjs_cdnurl=http://npm.taobao.org/mirrors/phantomjs\nELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/" >> ~/.npmrc && \
bundle config set mirror.https://rubygems.org https://gems.ruby-china.com && \
gem sources --add https://gems.ruby-china.com/ --remove https://rubygems.org/ && \
bundle config set path 'vendor/bundle' && \
bundle config set without 'development test' && \
gem install bundler && \
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list && \
apt-get update && \
apt-get install -y $BUILD_PACKAGES $DEV_PACKAGES --no-install-recommends && \
pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && \
pip3 install dlib && \
pip3 install face_recognition && \
apt-get remove -y $REMOVE_PACKAGES && \
echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
apt-get update && \
apt-get install yarn && \
rm -rf /var/lib/apt/lists/* && \
apt-get clean autoclean && \
apt-get autoremove --yes && \
rm -rf /var/lib/{apt,dpkg,cache,log}/ && \
yarn config set registry https://registry.npm.taobao.org/
RUN cd /tesseract && make install && /sbin/ldconfig && \
rm -rf /tesseract
COPY --from=0 /*.traineddata /usr/local/share/tessdata/
COPY instantclient /opt/oracle/instantclient
COPY start.sh /
WORKDIR $APP_HOME
RUN chmod +x /start.sh
# CMD ["/start.sh"]
这个方案是把所有运行以及编译环境安装好,这样在本地打包的时候可以复用这个 docker 去 bundle 安装其他依赖。
CURRENT_DIR = $(shell pwd)
RUBY_DOCKER_TAG = myrails:2.7-slim
NGINX_DOCKER_TAG = openresty/openresty:alpine
REDIS_DOCKER_TAG = redis:alpine
POSTGRES_DOCKER_TAG = postgres:alpine
SRC_PACKAGE = src.tar.gz
DOCKER_PACKAGE = docker.tar.gz
.PHONY: make_docker_deps package release clean docker build_src clean_tmp
build_src:
docker run --rm -v ${CURRENT_DIR}:/app -i -t ${RUBY_DOCKER_TAG} make release
clean:
docker run --rm -v ${CURRENT_DIR}:/app -i -t ${RUBY_DOCKER_TAG} make clean_tmp
release: make_docker_deps package
make_docker_deps:
bundle install
bundle exec rake assets:clobber
yarn install
bundle exec rake assets:precompile
bundle exec rake webpacker:compile
package:
@echo 'release...'
tar \
--exclude='./tmp' \
--exclude='./.git' \
--exclude='./log/' \
--exclude='./node_modules' \
--exclude='./yarn-error.log' \
--exclude='./docker_conf/instantclient' \
--exclude='*.docker' \
--exclude='${SRC_PACKAGE}' \
--exclude='*.tar.gz' \
-czf ${SRC_PACKAGE} .
@echo "release tar end."
clean_tmp:
/bin/rm -rf ./node_modules/*
/bin/rm -rf ./vendor/*
/bin/rm -f ${SRC_PACKAGE} *.docker ${DOCKER_PACKAGE}
docker\:base:
docker build ./docker_conf -t ${RUBY_DOCKER_TAG} -f docker_conf/Dockerfile
docker:
docker save -o ruby.docker ${RUBY_DOCKER_TAG}
docker save -o nginx.docker ${NGINX_DOCKER_TAG}
docker save -o redis.docker ${REDIS_DOCKER_TAG}
docker save -o postgres.docker ${POSTGRES_DOCKER_TAG}
tar czvf ${DOCKER_PACKAGE} ruby.docker nginx.docker redis.docker postgres.docker
这里用 make 去打包生成部署的文件,运行环境
version: '3.5'
services:
openresty:
image: openresty/openresty:alpine
restart: always
ports:
- 80:80
- 443:443
environment:
- TZ=Asia/Shanghai
volumes:
- $PWD/docker_conf/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf:ro
- $PWD/docker_conf/dhparam.pem:/usr/local/openresty/nginx/conf/dhparam.pem:ro
- $PWD/docker_conf/nginx-selfsigned.crt:/usr/local/openresty/nginx/conf/nginx-selfsigned.crt:ro
- $PWD/docker_conf/nginx-selfsigned.key:/usr/local/openresty/nginx/conf/nginx-selfsigned.key:ro
depends_on:
- ruby
networks:
- local_network
ruby:
image: myrails:2.7-slim
restart: always
environment:
- TZ=Asia/Shanghai
- RAILS_ENV=production
- PIDFILE=/tmp/server.pid
- RAILS_SERVE_STATIC_FILES=true
- RAILS_LOG_TO_STDOUT=true
- DATABASE_HOST=postgres
- DATABASE_USERNAME=username
- DATABASE_PASSWORD=password
- REDIS_URL=redis://redis:6379/0/cache
volumes:
- "$PWD:/app"
- "$PWD/docker_conf/start.sh:/start.sh"
- "/project/storage:/app/storage"
depends_on:
- postgres
- redis
networks:
- local_network
command: ["bash", "/start.sh"]
postgres:
image: postgres:alpine
restart: always
shm_size: '1gb'
ports:
- 5432:5432
volumes:
- "/project/dbdata:/var/lib/postgresql/data"
- "$PWD/docker_conf/postgres.conf:/etc/postgresql/postgresql.conf"
environment:
- TZ=Asia/Shanghai
- POSTGRES_USER=username
- POSTGRES_PASSWORD=password
networks:
- local_network
redis:
image: redis:alpine
restart: always
environment:
- TZ=Asia/Shanghai
volumes:
- "$PWD/docker_conf/redis.conf:/etc/redis/redis.conf"
- "/project/redisdb:/data"
networks:
- local_network
networks:
local_network:
external: true
这里是用 docker-compose 配合 systemd 写的服务去控制项目的运行 项目存储的文件都放在/project 目录
目前项目的部署和启动没有什么大的问题。但是相比实体机安装,cap 部署,这种方式无疑速度最慢,在部署过程中重启 docker-compose 冲断服务上时间更长。 另外这个项目也会对接一些 ftp 的终端,需要检测别的设备 ftp 上传的文件。目前计划在 docker-compose 中新增一个 ftp 容器,然后把 ftp 目录分别挂在到 rails 和 ftp 的容器中。
目前有几个问题希望和大家讨论一下: