用Raspberry Pi 4的Docker将OpenProject运行起来

似乎需要更加努力才能支持建筑业界的功能。
※如COLLADA2GLTF或IFCconvert之类的工具。想要使用的人请继续努力吧…虽然构建是成功的。不过太长就不写了。

背景

最近我尝试在Redmine上使用GTD,已经运营了大约两周。然而,我发现为了让它更易用,需要安装近10个插件;而且有很多地方不够灵活。简单搜索后,我发现OpenProject在默认情况下拥有我需要的功能。因此,我决定尝试在官方不支持的arm64架构上使用Docker来运行OpenProject。结果基本上成功了,所以我决定为了全世界的某个人而发布这篇文章。

环境建设

大致的构建方法如下:

    • 公式の Dockerfile を元に arm64 で動くように変更を加える

 

    • All in One コンテナではなく、docker-compose を使用するバージョンの arm64 版を目指す

 

    https を有効にするために letsencrypt を利用する自作の proxy を立てる

首先,我们的目标是使All in One容器运行。

克隆公式的GitHub存储库

git clone https://github.com/opf/openproject.git
# 真面目な人はこの後、タグをチェックアウトしてね。私は面倒なのでデフォルトブランチで作業します。

修改docker/prod/Dockerfile

将gosu更改为arm64版本使用。
#克隆并修改所下载的内容(包括中间的COPY . .)
#如果需要重新开始,也可以在主机上下载,但容易忘记重新执行。

FROM ruby:2.7.2-buster
MAINTAINER operations@openproject.com

# Allow platform-specific additions. Valid values are: on-prem,saas,bahn
ARG PLATFORM=on-prem
# Use OAuth token in case private gems need to be fetched
ARG GITHUB_OAUTH_TOKEN
ARG DEBIAN_FRONTEND=noninteractive

ARG PGLOADER_BINARY_DOWNLOAD_URL=https://openproject-docker-public.s3-eu-west-1.amazonaws.com/pgloader/bin/pgloader-ccl

ENV NODE_VERSION="12.18.3"
ENV BUNDLER_VERSION="2.1.4"
ENV BUNDLE_PATH__SYSTEM=false
ENV APP_USER=app
ENV APP_PATH=/app
ENV APP_DATA_PATH=/var/openproject/assets
ENV APP_DATA_PATH_LEGACY=/var/db/openproject
ENV PGDATA=/var/openproject/pgdata
ENV PGDATA_LEGACY=/var/lib/postgresql/9.6/main

ENV DATABASE_URL=postgres://openproject:openproject@127.0.0.1/openproject
ENV RAILS_ENV=production
ENV RAILS_CACHE_STORE=memcache
ENV RAILS_GROUPS=production
ENV RAILS_LOG_TO_STDOUT=1
ENV RAILS_SERVE_STATIC_FILES=1
ENV OPENPROJECT_INSTALLATION__TYPE=docker
# Valid values are: standard,bim
ENV OPENPROJECT_EDITION=standard
ENV NEW_RELIC_AGENT_ENABLED=false
ENV ATTACHMENTS_STORAGE_PATH=$APP_DATA_PATH/files
# Set a default key base, ensure to provide a secure value in production environments!
ENV SECRET_KEY_BASE=OVERWRITE_ME

RUN curl ${PGLOADER_BINARY_DOWNLOAD_URL} > /usr/local/bin/pgloader-ccl && chmod +x /usr/local/bin/pgloader-ccl

WORKDIR $APP_PATH

COPY docker/prod/setup ./docker/prod/setup
RUN ./docker/prod/setup/preinstall.sh

COPY Gemfile ./Gemfile
COPY Gemfile.* ./
COPY modules ./modules
COPY vendor ./vendor
# some gemspec files of plugins require files in there, notably OpenProject::Version
COPY lib ./lib

RUN bundle install --quiet --deployment --path vendor/bundle --no-cache \
  --with="$RAILS_GROUPS" --without="test development" --jobs=8 --retry=3 && \
  rm -rf vendor/bundle/ruby/*/cache && rm -rf vendor/bundle/ruby/*/gems/*/spec && rm -rf vendor/bundle/ruby/*/gems/*/test

# Finally, copy over the whole thing
COPY . .

# Replace gosu with gosu-arm64
RUN wget -O ./docker/prod/gosu https://github.com/tianon/gosu/releases/download/1.12/gosu-arm64

RUN ./docker/prod/setup/postinstall.sh

# Expose ports for apache and postgres
EXPOSE 8080 5432

# Expose the postgres data directory and OpenProject data directory as volumes
VOLUME ["$PGDATA", "$APP_DATA_PATH"]

# Set a custom entrypoint to allow for privilege dropping and one-off commands
ENTRYPOINT ["./docker/prod/entrypoint.sh"]

# Set default command to launch the all-in-one configuration supervised by supervisord
CMD ["./docker/prod/supervisord"]

修改docker/prod/setup/preinstall-common.sh

将指定x64版本的二进制文件的部分更改为使用arm64版本。

#!/bin/bash

set -e
set -o pipefail

# install node + npm
curl -s https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-arm64.tar.gz | tar xzf - -C /usr/local --strip-components=1

wget --quiet -O- https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
echo "deb http://apt.postgresql.org/pub/repos/apt buster-pgdg main" > /etc/apt/sources.list.d/pgdg.list

apt-get update -qq
apt-get install -y \
    apt-transport-https \
    pandoc \
    poppler-utils \
    unrtf \
    tesseract-ocr \
    catdoc \
    postgresql-9.6 \
    postgresql-client-9.6 \
    imagemagick

rm -rf "$PGDATA_LEGACY"

# Specifics for BIM edition
curl -SL -o dotnet.tar.gz https://dotnetcli.blob.core.windows.net/dotnet/Runtime/master/dotnet-runtime-latest-linux-arm64.tar.gz
mkdir -p /usr/share/dotnet
tar -zxf dotnet.tar.gz -C /usr/share/dotnet
ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet

tmpdir=$(mktemp -d)
cd $tmpdir

# Install XKT converter
npm install @xeokit/xeokit-gltf-to-xkt@0.0.3 -g

# Install COLLADA2GLTF
wget --quiet https://github.com/KhronosGroup/COLLADA2GLTF/releases/download/v2.1.5/COLLADA2GLTF-v2.1.5-linux.zip
unzip -q COLLADA2GLTF-v2.1.5-linux.zip
mv COLLADA2GLTF-bin "/usr/local/bin/COLLADA2GLTF"

# IFCconvert
wget --quiet https://s3.amazonaws.com/ifcopenshell-builds/IfcConvert-v0.6.0-9bcd932-linux64.zip
unzip -q IfcConvert-v0.6.0-9bcd932-linux64.zip
mv IfcConvert "/usr/local/bin/IfcConvert"

wget --quiet https://github.com/bimspot/xeokit-metadata/releases/download/1.0.0/xeokit-metadata-linux-arm.tar.gz
tar -zxvf xeokit-metadata-linux-arm.tar.gz
chmod +x xeokit-metadata-linux-arm/xeokit-metadata
cp -r xeokit-metadata-linux-arm/ "/usr/lib/xeokit-metadata"
ln -s /usr/lib/xeokit-metadata/xeokit-metadata /usr/local/bin/xeokit-metadata

cd /
rm -rf $tmpdir

gem install bundler --version "$BUNDLER_VERSION" --no-document

useradd -d /home/$APP_USER -m $APP_USER

建设(大约需要20分钟)

docker build -t openproject/community:11-arm64 -f docker/prod/Dockerfile ./

进行烟雾测试

docker run --rm -it -p 8080:80 -e SECRET_KEY_BASE=secret openproject/community:11-arm64

在使用docker-compose运行

获取公式的 docker-compose.yml

git clone https://github.com/opf/openproject-deploy --depth=1 --branch=stable/11 openproject
cd openproject/compose
nano docker-compose.yml

修改docker-compose.yml文件

    • コンテナネーム app のイメージを自作のイメージに変更する

volumes のホスト側のマウントポイントをカレントディレクトリにする

version: "3.7"

networks:
  frontend:
  backend:

volumes:
  pgdata:
  opdata:

x-op-restart-policy: &restart_policy
  restart: unless-stopped
x-op-image: &image
  image: openproject/community:11-arm64
x-op-app: &app
  <<: *image
  <<: *restart_policy
  environment:
    RAILS_CACHE_STORE: "memcache"
    OPENPROJECT_CACHE__MEMCACHE__SERVER: "cache:11211"
    OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
    DATABASE_URL: "postgres://postgres:p4ssw0rd@db/openproject"
    USE_PUMA: "true"
    # set to true to enable the email receiving feature. See ./docker/cron for more options
    IMAP_ENABLED: "${IMAP_ENABLED:-false}"
  volumes:
    - "./opdata:/var/openproject/assets"

services:
  db:
    image: postgres:10
    <<: *restart_policy
    stop_grace_period: "3s"
    volumes:
      - "./pgdata:/var/lib/postgresql/data"
    environment:
      POSTGRES_PASSWORD: p4ssw0rd
      POSTGRES_DB: openproject
    networks:
      - backend

  cache:
    image: memcached
    <<: *restart_policy
    networks:
      - backend

  proxy:
    <<: *image
    <<: *restart_policy
    command: "./docker/prod/proxy"
    ports:
      - "${PORT:-8080}:80"
    environment:
      APP_HOST: web
      OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
    depends_on:
      - web
    networks:
      - frontend

  web:
    <<: *app
    command: "./docker/prod/web"
    networks:
      - frontend
      - backend
    depends_on:
      - db
      - cache
      - seeder

  worker:
    <<: *app
    command: "./docker/prod/worker"
    networks:
      - backend
    depends_on:
      - db
      - cache
      - seeder

  cron:
    <<: *app
    command: "./docker/prod/cron"
    networks:
      - backend
    depends_on:
      - db
      - cache
      - seeder

  seeder:
    <<: *app
    command: "./docker/prod/seeder"
    restart: on-failure
    networks:
      - backend

执行(烟雾)测试

docker-compose up

启用HTTPS

目录结构

compose/
├── docker-compose.yml #変更する
└── proxy #作成する
    ├── Dockerfile #作成する
    └── default.conf.template #作成する

将代理更改为自制的。

FROM nginx:latest
ARG CERTBOT_EMAIL=default@default.com
ARG DOMAIN_LIST

# Expose ports.
EXPOSE 80
EXPOSE 443

RUN  apt-get update \
      && apt-get install -y cron certbot \
      && certbot certonly --dry-run --standalone --agree-tos -m "${CERTBOT_EMAIL}" -n -d ${DOMAIN_LIST} \ 
      && certbot certonly  --standalone --agree-tos -m "${CERTBOT_EMAIL}" -n -d ${DOMAIN_LIST} \
      && rm -rf /var/lib/apt/lists/* \
      && echo "@monthly /usr/bin/certbot renew --nginx >> /var/log/cron.log 2>&1" >/etc/cron.d/certbot-renew \
      && crontab /etc/cron.d/certbot-renew 
VOLUME /etc/letsencrypt

CMD [ "sh", "-c", "cron && ./docker-entrypoint.sh nginx -g 'daemon off;'" ]

修改docker-compose.yml文件


version: "3.7"

networks:
  frontend:
  backend:

volumes:
  pgdata:
  opdata:

x-op-restart-policy: &restart_policy
  restart: unless-stopped
x-op-image: &image
  image: openproject/community:11-arm64
x-op-app: &app
  <<: *image
  <<: *restart_policy
  container_name: app
  environment:
    RAILS_CACHE_STORE: "memcache"
    OPENPROJECT_CACHE__MEMCACHE__SERVER: "cache:11211"
    OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"
    DATABASE_URL: "postgres://postgres:p4ssw0rd@db/openproject"
    USE_PUMA: "true"
    # set to true to enable the email receiving feature. See ./docker/cron for more options
    IMAP_ENABLED: "${IMAP_ENABLED:-false}"
  volumes:
    - "./opdata:/var/openproject/assets"

services:
  db:
    container_name: db
    image: postgres:10
    <<: *restart_policy
    stop_grace_period: "3s"
    volumes:
      - "./pgdata:/var/lib/postgresql/data"
    environment:
      POSTGRES_PASSWORD: p4ssw0rd
      POSTGRES_DB: openproject
    networks:
      - backend

  cache:
    container_name: cache
    image: memcached
    <<: *restart_policy
    networks:
      - backend

#  proxy:
#    <<: *image
#    <<: *restart_policy
#    command: "./docker/prod/proxy"
#    ports:
#      - "${PORT:-8080}:80"
#    environment:
#      APP_HOST: web
#      OPENPROJECT_RAILS__RELATIVE__URL__ROOT: "${OPENPROJECT_RAILS__RELATIVE__URL__ROOT:-}"

  proxy:
    <<: *restart_policy
    container_name: proxy
    build:
      context: ./proxy
      network: host
      args:
        - CERTBOT_EMAIL=your-email@mail.com #replace with your own email
        - DOMAIN_LIST=your.hostname.com            #replace with your own domains
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./proxy/default.conf.template:/etc/nginx/templates/default.conf.template
    environment:
      MY_DOMAIN_NAME: your.hostname.com #replace with your own domains
    depends_on:
      - web
    networks:
      - frontend
  web:
    <<: *app
    container_name: web
    command: "./docker/prod/web"
    networks:
      - frontend
      - backend
    depends_on:
      - db
      - cache
      - seeder

  worker:
    <<: *app
    container_name: worker
    command: "./docker/prod/worker"
    networks:
      - backend
    depends_on:
      - db
      - cache
      - seeder

  cron:
    <<: *app
    container_name: cron
    command: "./docker/prod/cron"
    networks:
      - backend
    depends_on:
      - db
      - cache
      - seeder

  seeder:
    <<: *app
    container_name: seeder
    command: "./docker/prod/seeder"
    restart: on-failure
    networks:
      - backend

修改proxy/default.conf.template。

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name  localhost;

    ssl_certificate /etc/letsencrypt/live/${MY_DOMAIN_NAME}/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/${MY_DOMAIN_NAME}/privkey.pem;

    location / {
        proxy_pass http://web:8080/;
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_redirect http:// https://;
    }
    # --- For CertBot ---
    location ^~ /.well-known/acme-challenge/ {
        root /usr/share/nginx/html/;
    }

    location = /.well-known/acme-challenge/ {
        return 404;
    }
}

疏通(煙熏)測試

docker-compose up
广告
将在 10 秒后关闭
bannerAds