Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better Dev Experience w/ Docker Compose #1

Merged
merged 6 commits into from
Jun 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions dev/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# For details, see https://github.com/devcontainers/images/tree/main/src/ruby
FROM mcr.microsoft.com/devcontainers/ruby:1-3.2-bullseye

# Install Rails
# RUN gem install rails webdrivers

ARG NODE_VERSION="20"
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"

# [Optional] Uncomment this section to install additional OS packages.
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
&& apt-get -y install --no-install-recommends libicu-dev libidn11-dev ffmpeg imagemagick libpam-dev

# [Optional] Uncomment this line to install additional gems.
RUN gem install foreman

# [Optional] Uncomment this line to install global node packages.
RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && corepack enable" 2>&1

# COPY welcome-message.txt /usr/local/etc/vscode-dev-containers/first-run-notice.txt
34 changes: 34 additions & 0 deletions dev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Developer Setup w/ Docker

- Install docker/docker-compose
- Edit `/etc/hosts` and add `127.0.0.1 mastodon.local`

# Running Apps

From the root of the repo run:

- `docker-compose -f dev/docker-compose.yml up`

The app takes a while to start. Patience, my friend.

## Hosts

The developer setup doesn't configure SSL and uses nginx as a reverse proxy (see: mastodon.local.conf).

- App:

- `http://mastodon.local/`
- `http://mastodon.local:3000/`
- `http://127.0.0.1:3000/`

- Streaming:

- `http://mastodon.local/api/v1/streaming`
- `http://127.0.0.1:4000/api/v1/streaming`

- Email viewer: `http://mastodon.local/letter_opener`

# Data Directories

- `postgres14/` - nuke this directory if you want to start over
- `redis/` - stores the dump.rdb
113 changes: 113 additions & 0 deletions dev/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
version: '3'

services:
app:
build:
context: ../
dockerfile: ./dev/Dockerfile
volumes:
- ../:/mastodon
working_dir: /mastodon
environment:
LOCAL_DOMAIN: mastodon.local
RAILS_ENV: development
NODE_ENV: development
BIND: 0.0.0.0
REDIS_HOST: redis
REDIS_PORT: '6379'
DB_HOST: db
DB_NAME: postgres
DB_USER: postgres
DB_PASS: postgres
DB_PORT: '5432'
# Uncomment to enable elasticsearch
# ES_ENABLED: 'true'
# ES_HOST: es
# ES_PORT: '9200'
LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000
# Overrides default command so things don't shut down after the process ends.
# command: sleep infinity
entrypoint: /mastodon/dev/entrypoint.sh
ports:
- '127.0.0.1:3000:3000'
- '127.0.0.1:3035:3035'
- '127.0.0.1:4000:4000'
networks:
- external_network
- internal_network

db:
image: postgres:14-alpine
restart: unless-stopped
volumes:
- ../postgres14:/var/lib/postgresql/data
environment:
POSTGRES_USER: postgres
POSTGRES_DB: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_HOST_AUTH_METHOD: trust
networks:
- internal_network

redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- redis-data:/data
networks:
- internal_network

# Uncomment to enable ES
# This was copied from `.devcontainer` setup and
# is untested...
# es:
# image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2
# restart: unless-stopped
# environment:
# ES_JAVA_OPTS: -Xms512m -Xmx512m
# cluster.name: es-mastodon
# discovery.type: single-node
# bootstrap.memory_lock: 'true'
# volumes:
# - es-data:/usr/share/elasticsearch/data
# networks:
# - internal_network
# ulimits:
# memlock:
# soft: -1
# hard: -1

libretranslate:
image: libretranslate/libretranslate:v1.5.5
restart: unless-stopped
volumes:
- lt-data:/home/libretranslate/.local
networks:
- external_network
- internal_network

nginx:
image: nginx:latest
ports:
- 80:80
depends_on:
- 'app'
links:
- 'app:app'
volumes:
- ../public:/mastodon/public
- ./mastodon.local.conf:/etc/nginx/conf.d/mastodon.local.conf
networks:
- external_network
- internal_network

volumes:
postgres-data:
redis-data:
es-data:
lt-data:

networks:
external_network:
internal_network:
internal: true
30 changes: 30 additions & 0 deletions dev/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/bin/bash

set -e # Fail the whole script on first error

# Fetch Ruby gem dependencies
bundle config path 'vendor/bundle'
bundle config with 'development test'
bundle install

# Make Gemfile.lock pristine again
git checkout -- Gemfile.lock

# Fetch Javascript dependencies
corepack prepare
yarn install

# # seed test
# RAILS_ENV=test ./bin/rails db:setup
# RAILS_ENV=test ./bin/rails db:migrate
# RAILS_ENV=test ./bin/rails assets:precompile

# You can comment these lines out if you need restart
# without needing to pickup new changes here...
# Run db setup/migrations
RAILS_ENV=development ./bin/rails db:setup
RAILS_ENV=development ./bin/rails db:migrate
RAILS_ENV=development ./bin/rails assets:precompile

# This runs the Procfile.dev apps
foreman start
177 changes: 177 additions & 0 deletions dev/mastodon.local.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

upstream backend {
# server 127.0.0.1:3000 fail_timeout=0;
server app:3000 fail_timeout=0;
}

upstream streaming {
# Instruct nginx to send connections to the server with the least number of connections
# to ensure load is distributed evenly.
least_conn;

# server 127.0.0.1:4000 fail_timeout=0;
server app:4000 fail_timeout=0;

# Uncomment these lines for load-balancing multiple instances of streaming for scaling,
# this assumes your running the streaming server on ports 4000, 4001, and 4002:
# server 127.0.0.1:4001 fail_timeout=0;
# server 127.0.0.1:4002 fail_timeout=0;
}

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g;

# Re-enable to test SSL
# server {
# listen 80;
# listen [::]:80;
# server_name mastodon.local;
# root /mastodon/public;
# location /.well-known/acme-challenge/ { allow all; }
# # location / { return 301 https://$host$request_uri; }
# }

server {
listen 80;
listen [::]:80;
server_name mastodon.local;

# ssl_protocols TLSv1.2 TLSv1.3;

# You can use https://ssl-config.mozilla.org/ to generate your cipher set.
# We recommend their "Intermediate" level.
# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305;
# ssl_prefer_server_ciphers on;
# ssl_session_cache shared:SSL:10m;
# ssl_session_tickets off;

# Uncomment these lines once you acquire a certificate:
# ssl_certificate /etc/letsencrypt/live/mastodon.local/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/mastodon.local/privkey.pem;

keepalive_timeout 70;
sendfile on;
client_max_body_size 99m;

root /mastodon/public;

gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml image/x-icon;

location / {
try_files $uri @proxy;
}

# If Docker is used for deployment and Rails serves static files,
# then needed must replace line `try_files $uri =404;` with `try_files $uri @proxy;`.
location = /sw.js {
add_header Cache-Control "public, max-age=604800, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}

location ~ ^/assets/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}

location ~ ^/avatars/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}

location ~ ^/emoji/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}

location ~ ^/headers/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}

location ~ ^/packs/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri @proxy;

}

location ~ ^/shortcuts/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}

location ~ ^/sounds/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}

location ~ ^/system/ {
add_header Cache-Control "public, max-age=2419200, immutable";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'none'; form-action 'none'";
try_files $uri =404;
}

location ^~ /api/v1/streaming {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Proxy "";

proxy_pass http://streaming;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";

tcp_nodelay on;
}

location @proxy {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Proxy "";
proxy_pass_header Server;

proxy_pass http://backend;
proxy_buffering on;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

proxy_cache CACHE;
proxy_cache_valid 200 7d;
proxy_cache_valid 410 24h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cached $upstream_cache_status;

tcp_nodelay on;
}

error_page 404 500 501 502 503 504 /500.html;
}
Loading