一个 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 的容器中。
目前有几个问题希望和大家讨论一下: