Skip to content

Instantly share code, notes, and snippets.

@marcometz
Last active August 13, 2020 17:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcometz/ddee699779d38ac01de9ec05b4cbb066 to your computer and use it in GitHub Desktop.
Save marcometz/ddee699779d38ac01de9ec05b4cbb066 to your computer and use it in GitHub Desktop.
New Rails App with Docker
DOMAIN=docker.localhost
PRODUCTION_DOMAIN=foo.bar.de
APP=appname-api
REGISTRY_URL=registry.foo.bar.de/tpwd/appname-api
NODE_ENV=development
gem "whenever", require: false
gem "unicorn"
gem "rswag-api"
gem "rswag-ui"
gem_group :development, :test do
gem "byebug", platforms: %i[mri mingw x64_mingw]
gem "pry-rails"
gem "rails-controller-testing"
gem "rspec-rails"
gem "rswag-specs"
gem "rspec_junit_formatter"
gem "simplecov", "0.17.1", require: false
gem "simplecov-json", require: false, git: "https://github.com/kevjin/simplecov-json.git", branch: "simplecov-compatibility"
end
gem_group :development do
gem "annotate"
gem "rubocop", "~> 0.81.0", require: false
gem "rubocop-rspec"
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem "web-console", ">= 3.3.0"
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem "spring"
gem "spring-watcher-listen", "~> 2.0.0"
end
gem_group :test do
gem "capybara"
gem "database_cleaner"
gem "factory_bot_rails"
gem "faker"
gem "selenium-webdriver"
gem "shoulda"
gem "timecop"
gem "vcr"
gem "webdrivers"
gem "webmock"
end
#
# CronJob Config File
#
schedule_config = <<-EOL
# Use this file to easily define all of your cron jobs.
#
# It's helpful, but not entirely necessary to understand cron before proceeding.
# http://en.wikipedia.org/wiki/Cron
# Example:
#
# set :output, "/path/to/my/cron_log.log"
#
# every 2.hours do
# command "/usr/bin/some_great_command"
# runner "MyModel.some_method"
# rake "some:great:rake:task"
# end
#
# every 4.days do
# runner "AnotherModel.prune_old_records"
# end
set :job_template, "/bin/sh -l -c ':job'"
set :environment, ENV["RAILS_ENV"]
EOL
create_file "config/schedule.rb", schedule_config
#
# Quantum Settings File
#
quantum = <<-EOL
---
version: '1.0'
compose: quantum.yml
EOL
create_file ".quantum", quantum
#
# Database Config
#
database_config = <<-EOL
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
host: postgres
username: postdb
password: postdb
development:
<<: *default
database: postdb
test:
<<: *default
database: postdb_test
production:
<<: *default
database: postdb
host: postgres
username: postdb
password: postdb
EOL
create_file "config/database.yml.tmpl", database_config, force: true
run "bundle install"
run "bundle exec rails webpacker:install"
#
# Entrypoint Dockerfile
#
entrypoint = <<-EOL
#!/bin/sh
set -e
dockerize -wait tcp://postgres:5432 -timeout 30s
if [ $RAILS_ENV == development ]; then
bundle check || bundle install --binstubs="$BUNDLE_BIN" --with development
else
bundle check || bundle install --binstubs="$BUNDLE_BIN"
fi
# bundle exec rails webpacker:install
# yarn install --check-files
bundle exec rake assets:precompile
bundle exec rake db:create db:migrate
rm -f /unicorn.pid
exec "$@"
EOL
create_file "docker/entrypoint.sh", entrypoint
chmod "docker/entrypoint.sh", "+x"
#
# CronJob Startscript
#
start_cron = <<-EOL
#!/bin/sh
bundle exec whenever --write-crontab
crond -f
EOL
create_file "bin/start-cron.sh", start_cron
chmod "bin/start-cron.sh", "+x"
#
# Unicorn configuration
#
unicorn_config = <<-EOL
app_dir = "/app"
worker_processes ENV.fetch("UNICORN_WORKER") { 2 }
working_directory app_dir
# Load app into the master before forking workers for super-fast
# worker spawn times
preload_app true
# nuke workers after 60 seconds (the default)
timeout 60
# listen on a Unix domain socket and/or a TCP port,
listen "/unicorn/socket"
pid "/unicorn.pid"
# http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
if GC.respond_to?(:copy_on_write_friendly=)
GC.copy_on_write_friendly = true
end
after_fork do |server, worker|
# Unicorn master loads the app then forks off workers - because of the way
# Unix forking works, we need to make sure we aren't using any of the parent's
# sockets, e.g. db connection
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
# Redis and Memcached would go here but their connections are established
# on demand, so the master never opens a socket
end
EOL
create_file "config/unicorn.rb", unicorn_config
#
# NginxConfig : testen ob es entfernt werden kann
#
nginx_conf = <<-EOL
upstream app {
server unix:/unicorn/socket fail_timeout=0;
}
server {
listen 80;
server_name localhost;
root /app/public;
try_files $uri/index.html $uri @app;
location @app {
proxy_pass http://app;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
}
location ^~ /uploads/ {
access_log off;
gzip_static on;
expires max;
add_header Cache-Control public;
}
location ^~ /assets/ {
access_log off;
gzip_static on;
expires max;
add_header Cache-Control public;
}
client_max_body_size 4G;
keepalive_timeout 10;
}
EOL
create_file "docker/nginx.conf", nginx_conf
#
# Dockerfile
#
dockerfile = <<-EOL
FROM registry.gitlab.tpwd.de/cmmc-systems/ruby-nginx/ruby-2.7.1
LABEL maintainer="Marco Metz <mm@tpwd.de>"
# Für RailsAdmin Importer
RUN apk add icu-dev
# Für Cronjobs
RUN apk add dcron
RUN mkdir -p /var/log/cron && mkdir -m 0644 -p /var/spool/cron/crontabs && touch /var/log/cron/cron.log && mkdir -m 0644 -p /etc/cron.d
# für DB Zugriffe aus der Console heraus
RUN apk add mysql-client
# minIO Client in der Console
RUN apk add wget
RUN wget https://dl.min.io/client/mc/release/linux-amd64/mc
RUN chmod +x mc
RUN mv mc /bin
RUN mkdir -p /unicorn
RUN mkdir -p /app
WORKDIR /app
COPY . .
COPY config/database.yml.tmpl config/database.yml
ENTRYPOINT ["/app/docker/entrypoint.sh"]
CMD ["sh", "-c", "nginx-debug ; bundle exec unicorn -c config/unicorn.rb"]
EOL
create_file "Dockerfile", dockerfile
#
# Docker Compose
#
docker_compose = <<-EOL
version: '3.7'
services:
postgres:
image: 'postgres:10.3-alpine'
hostname: postgres
restart: unless-stopped
environment:
POSTGRES_USER: postdb
POSTGRES_PASSWORT: postdb
POSTGRES_DB: postdb
networks:
public:
adminer:
aliases:
- postgres
volumes:
- postgres:/var/lib/postgresql/data
app:
image: ${REGISTRY_URL}:latest
hostname: ${APP}
environment:
RAILS_ENV: production
RAILS_LOG_TO_STDOUT: 1
container_name: ${APP}
restart: unless-stopped
depends_on:
- postgres
networks:
public:
aliases:
- ${APP}
volumes:
- bundle:/bundle
- assets:/app/public/assets
- node_modules:/app/node_modules
labels:
- traefik.enable=true
- traefik.docker.network=public
- traefik.frontend.errors.network.backend=error
- traefik.frontend.errors.network.query=/{status}.html
- traefik.frontend.errors.network.status=500-511
configs:
- source: master-key
target: /app/config/master.key
- source: production-key
target: /app/config/production.key
cron:
image: ${REGISTRY_URL}:latest
environment:
RAILS_ENV: production
RAILS_LOG_TO_STDOUT: 1
restart: unless-stopped
entrypoint: sh
command: bin/start-cron.sh
depends_on:
- postgres
configs:
- source: master-key
target: /app/config/master.key
- source: production-key
target: /app/config/production.key
networks:
public:
aliases:
- ${APP}
volumes:
- bundle:/bundle
- assets:/app/public/assets
- node_modules:/app/node_modules
volumes:
postgres:
assets:
bundle:
node_modules:
configs:
master-key:
external: true
production-key:
external: true
networks:
public:
external: true
adminer:
external: true
EOL
create_file "docker-compose.yml", docker_compose
#
# Docker Compose Overrride
#
docker_compose_override = <<-EOL
version: '3.7'
services:
app:
build:
context: .
dockerfile: Dockerfile
environment:
RAILS_ENV: development
ports:
- "3000:80"
volumes:
- .:/app:delegated
labels:
- traefik.enable=true
- traefik.docker.network=public
- traefik.frontend.rule=Host:${APP}.${DOMAIN}
EOL
create_file "docker-compose.override.yml", docker_compose_override
#
# Drone Config
#
drone_config = <<-EOL
---
kind: pipeline
type: docker
name: Test, Build and Deploy
steps:
- name: Docker compose build quantum
image: docker/compose
settings:
username:
from_secret: REGISTRY_USER
password:
from_secret: REGISTRY_PASSWORD
registry: registry.gitlab.tpwd.de
repo: registry.gitlab.tpwd.de/tpwd/PLEASE_REPLACE_ME
dockerfile: Dockerfile
tags:
- latest
commands:
- docker-compose -f docker-compose.yml -f stack.yml config > quantum.yaml
volumes:
- name: quantum_yml
path: /quantum.yaml
- name: docker_socket
path: /var/run/docker.sock
when:
branch:
- master
depends_on:
- code-analysis
- name: Build and push Docker image
image: plugins/docker
settings:
username:
from_secret: REGISTRY_USER
password:
from_secret: REGISTRY_PASSWORD
registry: registry.gitlab.tpwd.de
repo: registry.gitlab.tpwd.de/tpwd/PLEASE_REPLACE_ME
dockerfile: Dockerfile
tags:
- latest
volumes:
- name: quantum_yml
path: /quantum.yaml
- name: docker_socket
path: /var/run/docker.sock
when:
branch:
- master
depends_on:
- Docker compose build quantum
- name: Deploy
image: registry.internal.planetary-networks.de/quantum-public/cli:2
environment:
QUANTUM_USER:
from_secret: QUANTUM_USER
QUANTUM_PASSWORD:
from_secret: QUANTUM_PASSWORD
commands:
- quantum-cli stack update --create --wait --stack PLEASE_REPLACE_ME --endpoint PLEASE_REPLACE_ME --environment production
volumes:
- name: quantum_yml
path: /quantum.yaml
- name: docker_socket
path: /var/run/docker.sock
when:
branch:
- master
depends_on:
- Build and push Docker image
volumes:
- name: quantum_yml
temp: {}
- name: coverage
temp: {}
- name: docker_socket
host:
path: /var/run/docker.sock
EOL
create_file ".drone.yml", drone_config
#
# Stack Config
#
stack_config = <<-EOL
version: '3.7'
services:
app:
deploy:
labels:
traefik.enable: "true"
traefik.port: 80
traefik.docker.network: public
traefik.main.frontend.rule: Host:api.${HOST}
traefik.main.frontend.passHostHeader: "true"
traefik.main.frontend.redirect.entryPoint: https
traefik.api.frontend.rule: Host:${PRODUCTION_DOMAIN}
traefik.api.frontend.passHostHeader: "true"
traefik.api.frontend.redirect.entryPoint: https
configs:
- source: master-key
target: /app/config/master.key
- source: production-key
target: /app/config/credentials/production.key
cron:
configs:
- source: master-key
target: /app/config/master.key
- source: production-key
target: /app/config/credentials/production.key
configs:
master-key:
external: true
production-key:
external: true
EOL
create_file "stack.yml", stack_config
#
# Starte Docker Server
#
run "docker-compose up --build"
--database=postgresql
--template=~/.rails_template.rb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment