From 1acff2dd8a8af10a7cfa778940d8ce13d2d99d93 Mon Sep 17 00:00:00 2001 From: prichier Date: Thu, 22 Oct 2020 15:19:01 +0200 Subject: [PATCH] Mutli-stage build for production --- docker-compose.yml | 72 +++++++--------- docker/production/Dockerfile | 85 +++++++------------ docker/production/README.md | 73 ++++++++-------- docker/production/docker-compose.yml | 37 ++++---- docker/production/env | 35 ++++++-- .../{prod.secret.exs => releases.exs} | 23 +++-- docker/production/start.sh | 3 - lib/mobilizon/cli.ex | 11 +++ 8 files changed, 167 insertions(+), 172 deletions(-) rename docker/production/{prod.secret.exs => releases.exs} (63%) delete mode 100755 docker/production/start.sh create mode 100644 lib/mobilizon/cli.ex diff --git a/docker-compose.yml b/docker-compose.yml index 3df763f40..25d62b4fb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,45 +1,39 @@ -version: '3' +version: "3" services: - postgres: - container_name: mobilizon_db - restart: unless-stopped - image: mdillon/postgis:11 + mobilizon: + image: mobilizon environment: - POSTGRES_PASSWORD: postgres - POSTGRES_DB: mobilizon_dev + - MOBILIZON_INSTANCE_NAME + - MOBILIZON_INSTANCE_HOST + - MOBILIZON_INSTANCE_EMAIL + - MOBILIZON_REPLY_EMAIL + - MOBILIZON_INSTANCE_REGISTRATIONS_OPEN=true + - MOBILIZON_DATABASE_USERNAME=${POSTGRES_USER} + - MOBILIZON_DATABASE_PASSWORD=${POSTGRES_PASSWORD} + - MOBILIZON_DATABASE_DBNAME=${POSTGRES_DB} + - MOBILIZON_DATABASE_HOST=db + - MOBILIZON_INSTANCE_SECRET_KEY_BASE + - MOBILIZON_INSTANCE_SECRET_KEY + - MOBILIZON_SMTP_SERVER=yoursmtpserver + - MOBILIZON_SMTP_HOSTNAME=your.smtp.domain + - MOBILIZON_SMTP_USERNAME + - MOBILIZON_SMTP_PASSWORD volumes: - - pgdata:/var/lib/postgresql/data - api: - container_name: mobilizon_api - restart: unless-stopped - build: . - volumes: - - '.:/app' + - /tmp/public/upload:/app/upload ports: - - "4000:4000" - depends_on: - - postgres + - "4000:4000" + + db: + image: postgis/postgis + volumes: + - /tmp/db:/var/lib/postgresql/data environment: - MIX_ENV: "dev" - DOCKER: "true" - MOBILIZON_INSTANCE_NAME: My Mobilizon Instance - MOBILIZON_INSTANCE_HOST: mobilizon.me - MOBILIZON_INSTANCE_EMAIL: noreply@mobilizon.me - MOBILIZON_INSTANCE_REGISTRATIONS_OPEN: "true" - MOBILIZON_DATABASE_PASSWORD: postgres - MOBILIZON_DATABASE_USERNAME: postgres - MOBILIZON_DATABASE_DBNAME: mobilizon_dev - MOBILIZON_DATABASE_HOST: postgres - command: > - sh -c "cd js && - yarn install && - cd ../ && - mix deps.get && - mix compile && - mix ecto.create && - mix ecto.migrate && - mix phx.server" -volumes: - pgdata: - .: + - POSTGRES_USER + - POSTGRES_PASSWORD + - POSTGRES_DB + +networks: + default: + ipam: + driver: default diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile index 259efd41d..71abc377b 100644 --- a/docker/production/Dockerfile +++ b/docker/production/Dockerfile @@ -1,62 +1,41 @@ -FROM elixir:slim +# First build the application assets +FROM node:alpine as assets -# Install dependencies, NodeJS, YARN & clean apt -RUN apt update \ - && apt -y dist-upgrade \ - && apt -y install build-essential \ - curl \ - wget \ - unzip \ - vim \ - openssl \ - git \ - cmake \ - imagemagick \ - webp \ - gifsicle \ - jpegoptim \ - optipng \ - pngquant \ - postgresql-client \ - && curl -sL https://deb.nodesource.com/setup_12.x | bash - \ - && apt -y install nodejs \ - && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \ - && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ - && apt -y update && apt -y install yarn \ - && apt -y clean \ - && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +RUN apk add --no-cache python build-base -# Add mobilizon user -RUN groupadd -r mobilizon \ - && useradd -r -g mobilizon -m mobilizon +COPY js . +RUN yarn install \ + && yarn run build -USER mobilizon +# Then, build the application binary +FROM elixir:alpine AS builder -# ENV +RUN apk add --no-cache build-base git cmake + +COPY mix.exs mix.lock ./ ENV MIX_ENV=prod +RUN mix local.hex --force \ + && mix local.rebar --force \ + && mix deps.get -# PORT +COPY lib ./lib +COPY priv ./priv +COPY config ./config +COPY docker/production/releases.exs ./config/ +COPY --from=assets ./priv/static ./priv/static + +RUN mix phx.digest \ + && mix release + +# Finally setup the app +FROM alpine + +RUN apk add --no-cache openssl ncurses-libs + +USER nobody EXPOSE 4000 -# Copy repo -COPY . /app -WORKDIR /app +COPY --from=builder --chown=nobody:nobody _build/prod/rel/mobilizon ./ -# Compile dependencies, Mobilizon and build front-end -RUN mix local.hex --force && mix local.rebar --force \ - && HEX_HTTP_CONCURRENCY=4 HEX_HTTP_TIMEOUT=60 mix do deps.get, compile \ - && cd js && NODE_BUILD_MEMORY=2024 yarn install && NODE_BUILD_MEMORY=2024 yarn run build \ - # free space - && rm -rf js doc deps docs support \ - && rm -rf /home/mobilizon/.cache/* \ - # copy config secret env based file - && cp docker/production/prod.secret config/ \ - # set start script mod - && chmod +x /app/docker/production/start.sh - -CMD /app/docker/production/start.sh - -## start.sh: -# !/bin/bash -# mix ecto.migrate -# mix phx.server +ENTRYPOINT ["/bin/mobilizon"] +CMD ["start"] diff --git a/docker/production/README.md b/docker/production/README.md index 6b9c9c87c..68055e518 100644 --- a/docker/production/README.md +++ b/docker/production/README.md @@ -2,49 +2,54 @@ You will need to : - build the image -- adapte env file -- run docker-compose +- tune the environment file +- use docker-compose to run the service ## Build the image - docker build -t mymobilizon -f docker/prod/Dockerfile . + git clone https://forge.tedomum.net/tedomum/mobilizon + cd mobilizon + docker build -t mobilizon -f docker/production/Dockerfile . -## Adapt env file +## Update the env file - cp env .env + cd docker/production/ + cp env.example .env -- Edit .env content with your params. -- Edit docker-compose file with your params (environment section for mobilizon & posgres). +Edit the `.env` content with your own settings. You can generate `MOBILIZON_INSTANCE_SECRET_KEY_BASE` and `MOBILIZON_INSTANCE_SECRET_KEY` with: gpg --gen-random --armor 1 50 -## run docker-compose +## Run the service - docker-compose -f docker-compose-simple.yml up - # set user for volumes - sudo chown 999:999 db public wal public/upload - # in another shell - docker-compose -f docker-compose-simple.yml exec -u 0 mobilizon bash - su - mobilizon - # backup secret - mv config/prod.secret.exs config/prod.secret.exs.env - # run config generation - MIX_ENV=prod mix mobilizon.instance gen -f - # reply anything (not used after) except for : - # - What is the name of your database? [mobilizon_prod] - # - What is the user used to connect to your database? [mobilizon] - # - What is the password used to connect to your database? [autogenerated] - # get secret env based bak - mv config/prod.secret.exs.env config/prod.secret.exs - # run the db init script as root - exit - psql -U postgres -p 5432 -h postgres -f setup_db.psql - # delete db init sript - rm setup_db.psql - # create an admin with mobilizon user - su - mobilizon - cd /app - MIX_ENV=prod mix mobilizon.users.new pascoual@tedomum.fr --password mobilizon - # exit with ctrl+d (twice times) +Start by initializing and running the database: + + docker-compose up -d db + +Instanciate required Postgres extensions: + + docker-compose exec db psql -U + # CREATE EXTENSION pg_trgm; + # CREATE EXTENSION unaccent; + + +Then run migrations: + + docker-compose run --rm mobilizon eval Mobilizon.Cli.migrate + +Finally, run the application: + + docker-compose up -d mobilizon + +## Update the service + +Pull the latest image, then run the migrations: + + docker-compose pull mobilizon + docker-compose run --rm mobilizon eval Mobilizon.Cli.migrate + +Finally, update the service: + + docker-compose up -d mobilizon diff --git a/docker/production/docker-compose.yml b/docker/production/docker-compose.yml index db38e50c9..c3b521624 100644 --- a/docker/production/docker-compose.yml +++ b/docker/production/docker-compose.yml @@ -1,42 +1,37 @@ -version: "2.1" +version: "3" services: mobilizon: image: mobilizon environment: - - MOBILIZON_INSTANCE_NAME="My Mobilizon Instance" - - MOBILIZON_INSTANCE_HOST=mobilizon.lan - - MOBILIZON_INSTANCE_EMAIL=noreply@mobilizon.lan + - MOBILIZON_INSTANCE_NAME + - MOBILIZON_INSTANCE_HOST + - MOBILIZON_INSTANCE_EMAIL + - MOBILIZON_REPLY_EMAIL - MOBILIZON_INSTANCE_REGISTRATIONS_OPEN=true - - MOBILIZON_DATABASE_USERNAME - - MOBILIZON_DATABASE_PASSWORD - - MOBILIZON_DATABASE_DBNAME=mobilizon_prod - - MOBILIZON_DATABASE_HOST=postgres + - MOBILIZON_DATABASE_USERNAME=${POSTGRES_USER} + - MOBILIZON_DATABASE_PASSWORD=${POSTGRES_PASSWORD} + - MOBILIZON_DATABASE_DBNAME=${POSTGRES_DB} + - MOBILIZON_DATABASE_HOST=db - MOBILIZON_INSTANCE_SECRET_KEY_BASE - MOBILIZON_INSTANCE_SECRET_KEY - - MOBILIZON_ADMIN_EMAIL=your@email.com - - MOBILIZON_SMPT_SERVER=yoursmtpserver - - MOBILIZON_SMPT_MOBILIZON_SMPT_HOSTNAME=your.smtp.domain - - MOBILIZON_SMPT_PORT=25 - - MOBILIZON_SMPT_USERNAME - - MOBILIZON_SMPT_PASSWORD - - MOBILIZON_SMPT_SSL=false + - MOBILIZON_SMTP_SERVER=yoursmtpserver + - MOBILIZON_SMTP_HOSTNAME=your.smtp.domain + - MOBILIZON_SMTP_USERNAME + - MOBILIZON_SMTP_PASSWORD volumes: - ./public/upload:/app/upload ports: - "4000:4000" - depends_on: - - postgres - postgres: + db: image: postgis/postgis volumes: - ./db:/var/lib/postgresql/data - - ./wal:/wal - - ./postgresql.conf:/var/lib/postgresql/data/postgresql.conf environment: + - POSTGRES_USER - POSTGRES_PASSWORD - - PGDATA=/var/lib/postgresql/data/pgdata + - POSTGRES_DB networks: default: diff --git a/docker/production/env b/docker/production/env index e1a7972d9..3b003dd38 100644 --- a/docker/production/env +++ b/docker/production/env @@ -1,10 +1,27 @@ -# You need to: -# cp env .env -# edite .env with your settings -MOBILIZON_DATABASE_PASSWORD=postgres -MOBILIZON_DATABASE_USERNAME=postgres -MOBILIZON_INSTANCE_SECRET_KEY_BASE=MmU1NWQyYWQtM2MzZC00ZTU5LTg0MmItMmY5NDZlMmNhNmEwCg -MOBILIZON_INSTANCE_SECRET_KEY=NjJhMGU5MDctZGNkOC00NGM0LWI5OWItZDEyY2FkNjRlODYyCg -MOBILIZON_SMPT_USERNAME=username +# Copy this file to env, then update it with your own settings + +# Database settings +POSTGRES_USER=mobilizon +POSTGRES_PASSWORD=changethis +POSTGRES_DB=mobilizon + +# Application config +MOBILIZON_INSTANCE_SECRET_KEY_BASE=changethis +MOBILIZON_INSTANCE_SECRET_KEY=changethis +MOBILIZON_SMTP_USERNAME=username +MOBILIZON_SMPT_PASSWORD=password +POSTGRES_PASSWORD=postgres + +# Instance configuration +MOBILIZON_INSTANCE_NAME=My Mobilizon Instance +MOBILIZON_INSTANCE_HOST=mobilizon.lan +MOBILIZON_INSTANCE_SECRET_KEY_BASE=changethis +MOBILIZON_INSTANCE_SECRET_KEY=changethis +MOBILIZON_INSTANCE_EMAIL=noreply@mobilizon.lan +MOBILIZON_REPLY_EMAIL=contact@mobilizon.lan + +# Email settings +MOBILIZON_SMPT_SERVER=localhost +MOBILIZON_SMPT_HOSTNAME=localhost +MOBILIZON_SMPT_USERNAME=noreply@mobilizon.lan MOBILIZON_SMPT_PASSWORD=password -POSTGRES_PASSWORD=postgres \ No newline at end of file diff --git a/docker/production/prod.secret.exs b/docker/production/releases.exs similarity index 63% rename from docker/production/prod.secret.exs rename to docker/production/releases.exs index 96cc31781..64a6ef993 100644 --- a/docker/production/prod.secret.exs +++ b/docker/production/releases.exs @@ -3,12 +3,13 @@ import Config config :mobilizon, Mobilizon.Web.Endpoint, + server: true, url: [host: System.get_env("MOBILIZON_INSTANCE_HOST", "mobilizon.lan")], http: [port: 4000], - secret_key_base: System.get_env("MOBILIZON_INSTANCE_SECRET_KEY_BASE", "ZcvexeC7cnwtKR8ADMBDwrYu2aYHUyjrOu4yA181Z112HNu/I5jyRleo4hoxOMqQ") + secret_key_base: System.get_env("MOBILIZON_INSTANCE_SECRET_KEY_BASE", "changethis") config :mobilizon, Mobilizon.Web.Auth.Guardian, - secret_key: System.get_env("MOBILIZON_INSTANCE_SECRET_KEY", "KsdUIvp6hQ7b97yxUZcDQyGH0g4LS3fF0OvIsIATpkKzd1MDvSS4KexWXsjXeMQZ") + secret_key: System.get_env("MOBILIZON_INSTANCE_SECRET_KEY", "changethis") config :mobilizon, :instance, name: System.get_env("MOBILIZON_INSTANCE_NAME", "Mobilizon"), @@ -19,7 +20,7 @@ config :mobilizon, :instance, allow_relay: true, federating: true, email_from: System.get_env("MOBILIZON_INSTANCE_EMAIL", "noreply@mobilizon.lan"), - email_reply_to: System.get_env("MOBILIZON_INSTANCE_EMAIL", "noreply@mobilizon.lan") + email_reply_to: System.get_env("MOBILIZON_REPLY_EMAIL", "noreply@mobilizon.lan") config :mobilizon, Mobilizon.Storage.Repo, adapter: Ecto.Adapters.Postgres, @@ -32,18 +33,14 @@ config :mobilizon, Mobilizon.Storage.Repo, config :mobilizon, Mobilizon.Web.Email.Mailer, adapter: Bamboo.SMTPAdapter, - server: System.get_env("MOBILIZON_SMPT_SERVER", "localhost"), - hostname: System.get_env("MOBILIZON_SMPT_HOSTNAME", "localhost"), - port: System.get_env("MOBILIZON_SMPT_PORT", "25"), - username: System.get_env("MOBILIZON_SMPT_USERNAME", nil), - password: System.get_env("MOBILIZON_SMPT_PASSWORD", nil), - # can be `:always` or `:never` + server: System.get_env("MOBILIZON_SMTP_SERVER", "localhost"), + hostname: System.get_env("MOBILIZON_SMTP_HOSTNAME", "localhost"), + port: System.get_env("MOBILIZON_SMTP_PORT", "25"), + username: System.get_env("MOBILIZON_SMTP_USERNAME", nil), + password: System.get_env("MOBILIZON_SMTP_PASSWORD", nil), tls: :if_available, allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], - # can be `true` - ssl: System.get_env("MOBILIZON_SMPT_SSL", "false"), + ssl: System.get_env("MOBILIZON_SMTP_SSL", "false"), retries: 1, - # can be `true` no_mx_lookups: false, - # can be `:always`. If your smtp relay requires authentication set it to `:always`. auth: :if_available diff --git a/docker/production/start.sh b/docker/production/start.sh deleted file mode 100755 index 25b32dd12..000000000 --- a/docker/production/start.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -mix ecto.migrate -mix phx.server \ No newline at end of file diff --git a/lib/mobilizon/cli.ex b/lib/mobilizon/cli.ex new file mode 100644 index 000000000..88f5bda2d --- /dev/null +++ b/lib/mobilizon/cli.ex @@ -0,0 +1,11 @@ +defmodule Mobilizon.Cli do + @app :mobilizon + + def migrate do + Application.load(@app) + + for repo <- Application.fetch_env!(@app, :ecto_repos) do + {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true)) + end + end +end \ No newline at end of file