From e945f486658e174368e313355ef44c007467e301 Mon Sep 17 00:00:00 2001
From: prichier <pascoualito@gmail.com>
Date: Mon, 19 Oct 2020 01:01:59 +0200
Subject: [PATCH] Add dockerfile and compose for production

---
 .gitlab-ci.yml                       | 123 ---------------------------
 Dockerfile                           |  11 +--
 docker/production/Dockerfile         |  62 ++++++++++++++
 docker/production/README.md          |  50 +++++++++++
 docker/production/docker-compose.yml |  44 ++++++++++
 docker/production/env                |  10 +++
 docker/production/prod.secret.exs    |  49 +++++++++++
 docker/production/start.sh           |   3 +
 8 files changed, 219 insertions(+), 133 deletions(-)
 delete mode 100644 .gitlab-ci.yml
 mode change 100644 => 120000 Dockerfile
 create mode 100644 docker/production/Dockerfile
 create mode 100644 docker/production/README.md
 create mode 100644 docker/production/docker-compose.yml
 create mode 100644 docker/production/env
 create mode 100644 docker/production/prod.secret.exs
 create mode 100755 docker/production/start.sh

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
deleted file mode 100644
index 4a6efdf2f..000000000
--- a/.gitlab-ci.yml
+++ /dev/null
@@ -1,123 +0,0 @@
-image: tcitworld/mobilizon-ci
-
-stages:
-  - check
-  - test
-  - deploy
-
-variables:
-  MIX_ENV: "test"
-  # DB Variables for Postgres / Postgis
-  POSTGRES_DB: mobilizon_test
-  POSTGRES_USER: postgres
-  POSTGRES_PASSWORD: ""
-  POSTGRES_HOST: postgres
-  # DB Variables for Mobilizon
-  MOBILIZON_DATABASE_USERNAME: $POSTGRES_USER
-  MOBILIZON_DATABASE_PASSWORD: $POSTGRES_PASSWORD
-  MOBILIZON_DATABASE_DBNAME: $POSTGRES_DB
-  MOBILIZON_DATABASE_HOST: $POSTGRES_HOST
-  GEOLITE_CITIES_PATH: "/usr/share/GeoIP/GeoLite2-City.mmdb"
-  MOBILIZON_INSTANCE_REGISTRATIONS_OPEN: "true"
-
-cache:
-  key: ${CI_COMMIT_REF_SLUG}
-  paths:
-    - ~/.cache/Cypress
-    - _build/
-    - deps/
-    - js/node_modules
-    - cache/Cypress
-
-lint:
-  stage: check
-  script:
-    - export EXITVALUE=0
-    - mix deps.get
-    - mix credo --strict -a || export EXITVALUE=1
-    - mix format --check-formatted --dry-run || export EXITVALUE=1
-    - cd js
-    - yarn install
-    #- yarn run lint || export EXITVALUE=1
-    - yarn run prettier --ignore-path="src/i18n/*" -c . || export EXITVALUE=1
-    - yarn run build
-    - cd ../
-    - exit $EXITVALUE
-  artifacts:
-    expire_in: 1 day
-    when: on_success
-    paths:
-      - priv/static
-
-deps:
-  stage: check
-  script:
-    - export EXITVALUE=0
-    - mix deps.get
-    - mix hex.outdated || export EXITVALUE=1
-    - cd js
-    - yarn outdated || export EXITVALUE=1
-    - exit $EXITVALUE
-  allow_failure: true
-
-exunit:
-  stage: test
-  services:
-    - name: mdillon/postgis:11
-      alias: postgres
-  before_script:
-    - cd js
-    - yarn install
-    - yarn run build
-    - cd ../
-    - mix deps.get
-    - MIX_ENV=test mix ecto.create
-    - MIX_ENV=test mix ecto.migrate
-  dependencies:
-    - lint
-  script:
-    - mix coveralls
-# cypress:
-#   stage: test
-#   services:
-#     - name: mdillon/postgis:11
-#       alias: postgres
-#   script:
-#     - mix deps.get
-#     - cd js
-#     - yarn install
-#     - npx cypress install # just to be sure
-#     - yarn run build
-#     - cd ../
-#     - MIX_ENV=e2e mix ecto.create
-#     - MIX_ENV=e2e mix ecto.migrate
-#     - MIX_ENV=e2e mix run priv/repo/e2e.seed.exs
-#     - MIX_ENV=e2e mix phx.server &
-#     - cd js
-#     - npx wait-on http://localhost:4000
-#     - if [ -z "$CYPRESS_KEY" ]; then npx cypress run; else npx cypress run --record --parallel --key $CYPRESS_KEY; fi
-#   artifacts:
-#     expire_in: 2 day
-#     paths:
-#       - js/tests/e2e/screenshots/**/*.png
-#       - js/tests/e2e/videos/**/*.mp4
-
-# pages:
-#   stage: deploy
-#   script:
-#     # - mkdir public
-#     # Mobilizon documentation is now on https://framagit.org/framasoft/joinmobilizon/documentation
-#     # Mix docs disabled because of https://github.com/elixir-lang/ex_doc/issues/1172
-#     # - mix deps.get
-#     # - mix docs
-#     # - mv doc public/backend
-#     #- cd js
-#     #- yarn install
-#     #- yarn run styleguide:build
-#     #- mv styleguide ../public/frontend
-#   only:
-#     - master
-#   artifacts:
-#     expire_in: 1 hour
-#     paths:
-#       - public
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 9c73fff55..000000000
--- a/Dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-FROM bitwalker/alpine-elixir:latest
-
-RUN apk add inotify-tools postgresql-client yarn file
-RUN apk add --no-cache make gcc libc-dev argon2 imagemagick
-
-RUN mix local.hex --force && mix local.rebar --force
-
-WORKDIR /app
-
-EXPOSE 4000
diff --git a/Dockerfile b/Dockerfile
new file mode 120000
index 000000000..27f3d3ac7
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1 @@
+./docker/production/Dockerfile
\ No newline at end of file
diff --git a/docker/production/Dockerfile b/docker/production/Dockerfile
new file mode 100644
index 000000000..259efd41d
--- /dev/null
+++ b/docker/production/Dockerfile
@@ -0,0 +1,62 @@
+FROM elixir:slim
+
+# 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/*
+
+# Add mobilizon user
+RUN groupadd -r mobilizon \
+    && useradd -r -g mobilizon -m mobilizon
+
+USER mobilizon
+
+# ENV
+ENV MIX_ENV=prod
+
+# PORT
+EXPOSE 4000
+
+# Copy repo
+COPY . /app
+WORKDIR /app
+
+# 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
diff --git a/docker/production/README.md b/docker/production/README.md
new file mode 100644
index 000000000..6b9c9c87c
--- /dev/null
+++ b/docker/production/README.md
@@ -0,0 +1,50 @@
+# Build and deploy Mobilizon with docker
+
+You will need to :
+- build the image
+- adapte env file
+- run docker-compose
+
+## Build the image
+
+    docker build -t mymobilizon -f docker/prod/Dockerfile .
+
+## Adapt env file
+
+    cp env .env
+
+- Edit .env content with your params.
+- Edit docker-compose file with your params (environment section for mobilizon & posgres).
+
+You can generate `MOBILIZON_INSTANCE_SECRET_KEY_BASE` and `MOBILIZON_INSTANCE_SECRET_KEY` with:
+
+    gpg --gen-random --armor 1 50
+
+## run docker-compose
+
+    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)
diff --git a/docker/production/docker-compose.yml b/docker/production/docker-compose.yml
new file mode 100644
index 000000000..db38e50c9
--- /dev/null
+++ b/docker/production/docker-compose.yml
@@ -0,0 +1,44 @@
+version: "2.1"
+
+services:
+  mobilizon:
+    image: mobilizon
+    environment:
+      - MOBILIZON_INSTANCE_NAME="My Mobilizon Instance"
+      - MOBILIZON_INSTANCE_HOST=mobilizon.lan
+      - MOBILIZON_INSTANCE_EMAIL=noreply@mobilizon.lan
+      - MOBILIZON_INSTANCE_REGISTRATIONS_OPEN=true
+      - MOBILIZON_DATABASE_USERNAME
+      - MOBILIZON_DATABASE_PASSWORD
+      - MOBILIZON_DATABASE_DBNAME=mobilizon_prod
+      - MOBILIZON_DATABASE_HOST=postgres
+      - 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
+    volumes:
+      - ./public/upload:/app/upload
+    ports:
+     - "4000:4000"
+    depends_on:
+      - postgres
+
+  postgres:
+    image: postgis/postgis
+    volumes:
+      - ./db:/var/lib/postgresql/data
+      - ./wal:/wal
+      - ./postgresql.conf:/var/lib/postgresql/data/postgresql.conf
+    environment:
+      - POSTGRES_PASSWORD
+      - PGDATA=/var/lib/postgresql/data/pgdata
+
+networks:
+  default:
+    ipam:
+      driver: default
diff --git a/docker/production/env b/docker/production/env
new file mode 100644
index 000000000..e1a7972d9
--- /dev/null
+++ b/docker/production/env
@@ -0,0 +1,10 @@
+# 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
+MOBILIZON_SMPT_PASSWORD=password
+POSTGRES_PASSWORD=postgres
\ No newline at end of file
diff --git a/docker/production/prod.secret.exs b/docker/production/prod.secret.exs
new file mode 100644
index 000000000..96cc31781
--- /dev/null
+++ b/docker/production/prod.secret.exs
@@ -0,0 +1,49 @@
+# Mobilizon instance configuration
+
+import Config
+
+config :mobilizon, Mobilizon.Web.Endpoint,
+   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")
+
+config :mobilizon, Mobilizon.Web.Auth.Guardian,
+  secret_key: System.get_env("MOBILIZON_INSTANCE_SECRET_KEY", "KsdUIvp6hQ7b97yxUZcDQyGH0g4LS3fF0OvIsIATpkKzd1MDvSS4KexWXsjXeMQZ")
+
+config :mobilizon, :instance,
+  name: System.get_env("MOBILIZON_INSTANCE_NAME", "Mobilizon"),
+  description: "Change this to a proper description of your instance",
+  hostname: System.get_env("MOBILIZON_INSTANCE_HOST", "mobilizon.lan"),
+  registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN", "false"),
+  demo: false,
+  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")
+
+config :mobilizon, Mobilizon.Storage.Repo,
+  adapter: Ecto.Adapters.Postgres,
+  username: System.get_env("MOBILIZON_DATABASE_USERNAME", "username"),
+  password: System.get_env("MOBILIZON_DATABASE_PASSWORD", "password"),
+  database: System.get_env("MOBILIZON_DATABASE_DBNAME", "mobilizon"),
+  hostname: System.get_env("MOBILIZON_DATABASE_HOST", "postgres"),
+  port: "5432",
+  pool_size: 10
+
+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`
+  tls: :if_available,
+  allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
+  # can be `true`
+  ssl: System.get_env("MOBILIZON_SMPT_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
new file mode 100755
index 000000000..25b32dd12
--- /dev/null
+++ b/docker/production/start.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+mix ecto.migrate
+mix phx.server
\ No newline at end of file