From c9464ad60995a05754acd22c276729539f08cd79 Mon Sep 17 00:00:00 2001 From: Alexander Katulskiy Date: Fri, 6 Sep 2019 12:33:07 +0300 Subject: [PATCH] Update docker configs. --- backend/.deploy/lib/Dockerfile | 57 ++++++++++ backend/.deploy/lib/deploy.sh | 104 ++++++++++++++++++ backend/.deploy/lib/entrypoint.sh | 11 ++ backend/.deploy/lib/nginx/Dockerfile | 18 +++ .../.deploy/lib/nginx/configs/default.conf | 27 +++++ backend/.deploy/lib/nginx/configs/nginx.conf | 33 ++++++ backend/.deploy/production/deploy.sh | 22 ++++ backend/.deploy/production/docker-compose.yml | 62 +++++++++++ backend/.deploy/production/esc-params.yml | 15 +++ backend/.deploy/staging/deploy.sh | 22 ++++ backend/.deploy/staging/docker-compose.yml | 95 ++++++++++++++++ backend/.deploy/staging/ecs-params.yml | 19 ++++ backend/.dockerdev/.psqlrc | 26 +++++ backend/.dockerdev/Dockerfile | 27 +++++ backend/.dockerdev/docker-compose.yml | 77 +++++++++++++ backend/.dockerdev/entrypoint.sh | 11 ++ backend/dip.yml | 61 ++++++++++ frontend/.deploy/lib/Dockerfile | 32 ++++++ frontend/.deploy/lib/deploy.sh | 98 +++++++++++++++++ frontend/.deploy/lib/nginx/Dockerfile | 16 +++ .../.deploy/lib/nginx/configs/default.conf | 34 ++++++ frontend/.deploy/lib/nginx/configs/nginx.conf | 33 ++++++ frontend/.deploy/production/deploy.sh | 10 ++ .../.deploy/production/docker-compose.yml | 44 ++++++++ frontend/.deploy/production/ecs-params.yml | 13 +++ frontend/.deploy/staging/deploy.sh | 10 ++ frontend/.deploy/staging/docker-compose.yml | 44 ++++++++ frontend/.deploy/staging/ecs-params.yml | 13 +++ frontend/.dockerdev/Dockerfile | 5 + frontend/.dockerdev/docker-compose.yml | 24 ++++ frontend/dip.yml | 21 ++++ 31 files changed, 1084 insertions(+) create mode 100644 backend/.deploy/lib/Dockerfile create mode 100755 backend/.deploy/lib/deploy.sh create mode 100755 backend/.deploy/lib/entrypoint.sh create mode 100644 backend/.deploy/lib/nginx/Dockerfile create mode 100644 backend/.deploy/lib/nginx/configs/default.conf create mode 100644 backend/.deploy/lib/nginx/configs/nginx.conf create mode 100644 backend/.deploy/production/deploy.sh create mode 100644 backend/.deploy/production/docker-compose.yml create mode 100644 backend/.deploy/production/esc-params.yml create mode 100755 backend/.deploy/staging/deploy.sh create mode 100644 backend/.deploy/staging/docker-compose.yml create mode 100644 backend/.deploy/staging/ecs-params.yml create mode 100644 backend/.dockerdev/.psqlrc create mode 100644 backend/.dockerdev/Dockerfile create mode 100644 backend/.dockerdev/docker-compose.yml create mode 100755 backend/.dockerdev/entrypoint.sh create mode 100644 backend/dip.yml create mode 100644 frontend/.deploy/lib/Dockerfile create mode 100644 frontend/.deploy/lib/deploy.sh create mode 100644 frontend/.deploy/lib/nginx/Dockerfile create mode 100644 frontend/.deploy/lib/nginx/configs/default.conf create mode 100644 frontend/.deploy/lib/nginx/configs/nginx.conf create mode 100644 frontend/.deploy/production/deploy.sh create mode 100644 frontend/.deploy/production/docker-compose.yml create mode 100644 frontend/.deploy/production/ecs-params.yml create mode 100755 frontend/.deploy/staging/deploy.sh create mode 100644 frontend/.deploy/staging/docker-compose.yml create mode 100644 frontend/.deploy/staging/ecs-params.yml create mode 100644 frontend/.dockerdev/Dockerfile create mode 100644 frontend/.dockerdev/docker-compose.yml create mode 100644 frontend/dip.yml diff --git a/backend/.deploy/lib/Dockerfile b/backend/.deploy/lib/Dockerfile new file mode 100644 index 0000000..6d6cdda --- /dev/null +++ b/backend/.deploy/lib/Dockerfile @@ -0,0 +1,57 @@ +# Stage: Builder +FROM ruby:2.6.3-alpine3.9 as Builder + +ARG BUNDLE_WITHOUT +ARG RAILS_ENV + +RUN apk add --update --no-cache \ + build-base \ + libxml2-dev \ + libxslt-dev \ + postgresql-dev \ + tzdata \ + bash \ + curl \ + git \ + nodejs \ + yarn + +ENV APP_HOME /home/www/backend +ENV BUNDLE_WITHOUT=${BUNDLE_WITHOUT} + +WORKDIR $APP_HOME + +COPY Gemfile* $APP_HOME/ +RUN bundle install --jobs 20 --retry 5 \ + && rm -rf /usr/local/bundle/cache/*.gem \ + && find /usr/local/bundle/gems/ -name "*.c" -delete \ + && find /usr/local/bundle/gems/ -name "*.o" -delete + +COPY . $APP_HOME + +RUN RAILS_ENV=${RAILS_ENV} bin/rake assets:precompile + +# Stage: Final +FROM ruby:2.6.3-alpine3.9 + +RUN apk add --update --no-cache \ + libxml2-dev \ + libxslt-dev \ + postgresql-dev \ + file \ + tzdata \ + curl + +ENV APP_USER app +ENV APP_HOME /home/www/backend + +RUN addgroup -g 1000 -S $APP_USER && adduser -u 1000 -S $APP_USER -G $APP_USER + +USER $APP_USER + +COPY --from=Builder /usr/local/bundle/ /usr/local/bundle/ +COPY --from=Builder --chown=app:app $APP_HOME $APP_HOME + +WORKDIR $APP_HOME + +CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"] diff --git a/backend/.deploy/lib/deploy.sh b/backend/.deploy/lib/deploy.sh new file mode 100755 index 0000000..388b9f5 --- /dev/null +++ b/backend/.deploy/lib/deploy.sh @@ -0,0 +1,104 @@ +#!/bin/bash + +# Required variables: +# - region # ECR Region +# - aws-access-key # AWS Access Id +# - aws-secret-key # AWS Secret Key +# - service # Service's name +# - cluster # Service's cluster +# - file # docker-compose file path +# - ecs_params # AWS ECS params file path +# - containers # container names for push to ECR + +set -e + +deploy() { + while [[ $# -gt 0 ]] + do + key="$1" + + case $key in + --region) + REGION="$2" + shift + shift + ;; + --aws-access-key) + AWS_ACCESS_KEY_ID="$2" + shift + shift + ;; + --aws-secret-key) + AWS_SECRET_ACCESS_KEY="$2" + shift + shift + ;; + --cluster) + CLUSTER="$2" + shift + shift + ;; + --file) + FILE="$2" + shift + shift + ;; + --service) + SERVICE="$2" + shift + shift + ;; + --ecs_params) + ECS_PARAMS="$2" + shift + shift + ;; + --containers) + CONTAINERS="$2" + shift + shift + ;; + *) + echo "Unknown option $1\n" + shift + shift + esac + done + + export TAG=$(git log -1 --format=%h) + export REGION=$REGION + + echo "🐳 Build docker image $BUILD_APP" + push_to_docker + + echo "🚀 Deploy $BUILD_APP to $CLUSTER:$SERVICE" + ecs_deploy + + echo '✅ Deploy successfully finished' +} + +push_to_docker() { + $(aws ecr get-login --region $REGION --no-include-email) + + docker-compose -f $FILE build $CONTAINERS + docker-compose -f $FILE push $CONTAINERS +} + +ecs_deploy() { + ecs-cli configure \ + --cluster $CLUSTER \ + --region $REGION \ + --default-launch-type EC2 \ + --config-name $CLUSTER + + ecs-cli compose \ + --project-name $SERVICE \ + --file $FILE \ + --ecs-params $ECS_PARAMS \ + service up \ + --cluster-config $CLUSTER \ + --force-deployment \ + --timeout 2 +} + +exec "$@" diff --git a/backend/.deploy/lib/entrypoint.sh b/backend/.deploy/lib/entrypoint.sh new file mode 100755 index 0000000..34b156a --- /dev/null +++ b/backend/.deploy/lib/entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +set -e + +rm -f $APP_HOME/tmp/pids/server.pid +rm -f $APP_HOME/tmp/pids/sidekiq.pid + +bundle exec rails db:create +bundle exec rails db:migrate + +exec "$@" diff --git a/backend/.deploy/lib/nginx/Dockerfile b/backend/.deploy/lib/nginx/Dockerfile new file mode 100644 index 0000000..1adf294 --- /dev/null +++ b/backend/.deploy/lib/nginx/Dockerfile @@ -0,0 +1,18 @@ +FROM nginx:1.16 + +USER root + +RUN rm /usr/share/nginx/html/* + +COPY configs/nginx.conf /etc/nginx/nginx.conf +COPY configs/default.conf /etc/nginx/conf.d/default.conf + +RUN touch /var/run/nginx.pid && \ + chown -R www-data:www-data /var/run/nginx.pid && \ + chown -R www-data:www-data /var/cache/nginx && \ + chown -R www-data:www-data /etc/nginx && \ + chown -R www-data:www-data /var/log + +USER www-data + +CMD ["nginx", "-g", "daemon off;"] diff --git a/backend/.deploy/lib/nginx/configs/default.conf b/backend/.deploy/lib/nginx/configs/default.conf new file mode 100644 index 0000000..262f2a1 --- /dev/null +++ b/backend/.deploy/lib/nginx/configs/default.conf @@ -0,0 +1,27 @@ +upstream rails { + server rails:8000; +} + +server { + listen 8080 default deferred; + server_name _; + + access_log off; + error_log /var/log/nginx.error.log crit; + + client_max_body_size 100M; + keepalive_timeout 30; + + if ($request_method !~ ^(GET|HEAD|PUT|PATCH|POST|DELETE|OPTIONS)$ ){ + return 405; + } + + location / { + proxy_redirect off; + proxy_set_header Client-Ip $remote_addr; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://rails; + } +} diff --git a/backend/.deploy/lib/nginx/configs/nginx.conf b/backend/.deploy/lib/nginx/configs/nginx.conf new file mode 100644 index 0000000..1056147 --- /dev/null +++ b/backend/.deploy/lib/nginx/configs/nginx.conf @@ -0,0 +1,33 @@ +worker_processes auto; + +worker_rlimit_nofile 4096; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server_tokens off; + + add_header X-Frame-Options SAMEORIGIN; + + add_header X-Content-Type-Options nosniff; + + add_header X-XSS-Protection "1; mode=block"; + server_names_hash_bucket_size 64; + server_names_hash_max_size 512; + + sendfile on; + tcp_nopush on; + + types_hash_max_size 2048; + + gzip on; + gzip_disable "msie6"; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript; + + include /etc/nginx/conf.d/default.conf; +} diff --git a/backend/.deploy/production/deploy.sh b/backend/.deploy/production/deploy.sh new file mode 100644 index 0000000..0dc3890 --- /dev/null +++ b/backend/.deploy/production/deploy.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +source '.deploy/lib/deploy.sh' + +if [ ! -f config/master.key ]; then + if [ -z "$RAILS_PRODUCTION_KEY" ]; then + echo "No rails master key" + exit 1 + fi + + echo "$RAILS_PRODUCTION_KEY" > config/master.key +fi + +deploy \ + --aws-access-key "$AWS_ACCESS_KEY_ID" \ + --aws-secret-key "$AWS_SECRET_ACCESS_KEY" \ + --region 'us-west-1' \ + --cluster 'backend-production' \ + --service 'backend' \ + --file '.deploy/production/docker-compose.yml' \ + --ecs_params '.deploy/production/ecs-params.yml' \ + --containers 'rails nginx' diff --git a/backend/.deploy/production/docker-compose.yml b/backend/.deploy/production/docker-compose.yml new file mode 100644 index 0000000..aa1c951 --- /dev/null +++ b/backend/.deploy/production/docker-compose.yml @@ -0,0 +1,62 @@ +version: '3' + +services: + nginx: + image: "${AWS_ACCOUNT_NUMBER}.dkr.ecr.${REGION}.amazonaws.com/backend/nginx:${TAG}" + build: ../lib/nginx + ports: + - 80:8080 + links: + - rails + logging: + driver: awslogs + options: + awslogs-group: backend-production + awslogs-region: ${REGION} + awslogs-stream-prefix: nginx + healthcheck: + test: ["CMD-SHELL", "service nginx status || exit 1"] + + rails: &rails + image: "${AWS_ACCOUNT_NUMBER}.dkr.ecr.${REGION}.amazonaws.com/backend/rails:${TAG}" + build: + context: ../../. + dockerfile: .deploy/lib/Dockerfile + args: + BUNDLE_WITHOUT: development test + RAILS_ENV: production + command: bundle exec puma -C config/puma.rb + entrypoint: "./.deploy/lib/entrypoint.sh" + ports: + - 8000 + environment: &rails_env + RAILS_ENV: production + PORT: 8000 + RAILS_MAX_THREADS: 25 + RAILS_LOG_TO_STDOUT: "true" + logging: + driver: awslogs + options: + awslogs-group: backend-production + awslogs-region: ${REGION} + awslogs-stream-prefix: rails + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health_check"] + + sidekiq: + <<: *rails + command: bundle exec sidekiq + entrypoint: "" + environment: + <<: *rails_env + PORT: 8001 + ports: + - 8001 + logging: + driver: awslogs + options: + awslogs-group: backend-production + awslogs-region: ${REGION} + awslogs-stream-prefix: sidekiq + healthcheck: + test: ["CMD-SHELL", "ps ax | grep -v grep | grep sidekiq || exit 1"] diff --git a/backend/.deploy/production/esc-params.yml b/backend/.deploy/production/esc-params.yml new file mode 100644 index 0000000..3f651fe --- /dev/null +++ b/backend/.deploy/production/esc-params.yml @@ -0,0 +1,15 @@ +version: 1 +task_definition: + ecs_network_mode: bridge + + task_size: + cpu_limit: 1000 + mem_limit: 3000 + + services: + nginx: + essential: true + rails: + essential: true + sidekiq: + essential: true diff --git a/backend/.deploy/staging/deploy.sh b/backend/.deploy/staging/deploy.sh new file mode 100755 index 0000000..2b4641f --- /dev/null +++ b/backend/.deploy/staging/deploy.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +source '.deploy/lib/deploy.sh' + +if [ ! -f config/credentials/staging.key ]; then + if [ -z "$RAILS_STAGING_KEY" ]; then + echo "No rails master key" + exit 1 + fi + + echo "$RAILS_STAGING_KEY" > config/credentials/staging.key +fi + +deploy \ + --aws-access-key "$AWS_ACCESS_KEY_ID" \ + --aws-secret-key "$AWS_SECRET_ACCESS_KEY" \ + --region 'us-west-1' \ + --cluster 'backend-staging' \ + --service 'backend' \ + --file '.deploy/staging/docker-compose.yml' \ + --ecs_params '.deploy/staging/ecs-params.yml' \ + --containers 'rails nginx' diff --git a/backend/.deploy/staging/docker-compose.yml b/backend/.deploy/staging/docker-compose.yml new file mode 100644 index 0000000..414a19f --- /dev/null +++ b/backend/.deploy/staging/docker-compose.yml @@ -0,0 +1,95 @@ +version: '3' + +services: + nginx: + image: "${AWS_ACCOUNT_NUMBER}.dkr.ecr.${REGION}.amazonaws.com/backend/nginx:${TAG}" + build: ../lib/nginx + ports: + - 80:8080 + links: + - rails + logging: + driver: awslogs + options: + awslogs-group: backend-staging + awslogs-region: ${REGION} + awslogs-stream-prefix: nginx + healthcheck: + test: ["CMD-SHELL", "service nginx status || exit 1"] + + rails: &rails + image: "${AWS_ACCOUNT_NUMBER}.dkr.ecr.${REGION}.amazonaws.com/backend/rails:${TAG}" + build: + context: ../../. + dockerfile: .deploy/lib/Dockerfile + args: + BUNDLE_WITHOUT: development test + RAILS_ENV: staging + command: bundle exec puma -C config/puma.rb + entrypoint: "./.deploy/lib/entrypoint.sh" + ports: + - 8000 + environment: &rails_env + RAILS_ENV: staging + PORT: 8000 + RAILS_MAX_THREADS: 25 + REDIS_URL: redis://redis:6379 + PG_HOST: postgres + PG_PASSWORD: postgres + PG_USER: postgres + RAILS_LOG_TO_STDOUT: "true" + links: + - postgres + - redis + logging: + driver: awslogs + options: + awslogs-group: backend-staging + awslogs-region: ${REGION} + awslogs-stream-prefix: rails + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/"] + + sidekiq: + <<: *rails + command: bundle exec sidekiq + entrypoint: "" + environment: + <<: *rails_env + PORT: 8001 + ports: + - 8001 + logging: + driver: awslogs + options: + awslogs-group: backend-staging + awslogs-region: ${REGION} + awslogs-stream-prefix: sidekiq + healthcheck: + test: ["CMD-SHELL", "ps ax | grep -v grep | grep sidekiq || exit 1"] + + postgres: + image: postgres:11 + volumes: + - /postgres:/var/lib/postgresql/data + logging: + driver: awslogs + options: + awslogs-group: backend-staging + awslogs-region: ${REGION} + awslogs-stream-prefix: postgres + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres"] + + redis: + image: redis:4.0 + volumes: + - /redis:/var/lib/redis/data + logging: + driver: awslogs + options: + awslogs-group: backend-staging + awslogs-region: ${REGION} + awslogs-stream-prefix: redis + healthcheck: + test: ["CMD", "redis-cli", "-h", "localhost", "ping"] diff --git a/backend/.deploy/staging/ecs-params.yml b/backend/.deploy/staging/ecs-params.yml new file mode 100644 index 0000000..3d09449 --- /dev/null +++ b/backend/.deploy/staging/ecs-params.yml @@ -0,0 +1,19 @@ +version: 1 +task_definition: + ecs_network_mode: bridge + + task_size: + cpu_limit: 900 + mem_limit: 900 + + services: + nginx: + essential: true + rails: + essential: true + sidekiq: + essential: true + postgres: + essential: true + redis: + essential: true diff --git a/backend/.dockerdev/.psqlrc b/backend/.dockerdev/.psqlrc new file mode 100644 index 0000000..efb63f5 --- /dev/null +++ b/backend/.dockerdev/.psqlrc @@ -0,0 +1,26 @@ +-- Don't display the "helpful" message on startup. +\set QUIET 1 + +-- Allow specifying the path to history file via `PSQL_HISTFILE` env variable +-- (and fallback to the defaukt $HOME/.psql_history otherwise) +\set HISTFILE `[[ -z $PSQL_HISTFILE ]] && echo $HOME/.psql_history || echo $PSQL_HISTFILE` + +-- Show how long each query takes to execute +\timing + +-- Use best available output format +\x auto + +-- Verbose error reports +\set VERBOSITY verbose + +-- If a command is run more than once in a row, +-- only store it once in the history +\set HISTCONTROL ignoredups +\set COMP_KEYWORD_CASE upper + +-- By default, NULL displays as an empty space. Is it actually an empty +-- string, or is it null? This makes that distinction visible +\pset null '[NULL]' + +\unset QUIET diff --git a/backend/.dockerdev/Dockerfile b/backend/.dockerdev/Dockerfile new file mode 100644 index 0000000..b59e422 --- /dev/null +++ b/backend/.dockerdev/Dockerfile @@ -0,0 +1,27 @@ +FROM ruby:2.6.3-alpine3.9 + +ARG BUNDLER_VERSION + +RUN apk add --update --no-cache \ + build-base \ + libxml2-dev \ + libxslt-dev \ + postgresql-dev \ + tzdata \ + bash \ + curl \ + git \ + nodejs \ + yarn + +ENV LANG=C.UTF-8 GEM_HOME=/bundle BUNDLE_JOBS=4 BUNDLE_RETRY=3 +ENV BUNDLE_PATH $GEM_HOME +ENV BUNDLE_APP_CONFIG=$BUNDLE_PATH BUNDLE_BIN=$BUNDLE_PATH/bin +ENV PATH /app/bin:$BUNDLE_BIN:$PATH + +RUN gem update --system && \ + gem install bundler:$BUNDLER_VERSION +RUN bundle config build.nokogiri --use-system-libraries + +ENV APP_HOME /home/www/backend +WORKDIR $APP_HOME diff --git a/backend/.dockerdev/docker-compose.yml b/backend/.dockerdev/docker-compose.yml new file mode 100644 index 0000000..e91daa4 --- /dev/null +++ b/backend/.dockerdev/docker-compose.yml @@ -0,0 +1,77 @@ +version: '3.4' + +x-app: &app + build: + context: ../ + dockerfile: .dockerdev/Dockerfile + args: + BUNDLER_VERSION: '2.0.2' + image: backend-dev:1.0.0 + tmpfs: + - /tmp + +x-backend: &backend + <<: *app + stdin_open: true + tty: true + volumes: + - ../:/home/www/backend:cached + - rails_cache:/home/www/backend/tmp/cache + - bundle:/bundle + - node_modules:/home/www/backend/node_modules + - packs:/home/www/backend/public/packs + - .psqlrc:/root/.psqlrc:ro + environment: + - NODE_ENV=development + - RAILS_ENV=${RAILS_ENV:-development} + - REDIS_URL=redis://redis:6379/ + - BOOTSNAP_CACHE_DIR=/bundle/bootsnap + - WEB_CONCURRENCY=1 + - HISTFILE=/home/www/backend/log/.bash_history + - PSQL_HISTFILE=/home/www/backend/log/.psql_history + - PG_HOST=postgres + - PG_PASSWORD=postgres + - PG_USER=postgres + depends_on: + - postgres + - redis + +services: + runner: + <<: *backend + command: /bin/bash + ports: + - '3000:3000' + - '3002:3002' + + rails: + <<: *backend + command: bundle exec rails server -b 0.0.0.0 + entrypoint: './.dockerdev/entrypoint.sh' + ports: + - '3000:3000' + sidekiq: + <<: *backend + command: bundle exec sidekiq -C config/sidekiq.yml + + postgres: + image: postgres:11.1 + volumes: + - .psqlrc:/root/.psqlrc:ro + - postgres:/var/lib/postgresql/data + - ../log:/root/log:cached + environment: + - PSQL_HISTFILE=/root/log/.psql_history + + redis: + image: redis:3.2-alpine + volumes: + - redis:/data + +volumes: + postgres: + redis: + bundle: + node_modules: + rails_cache: + packs: diff --git a/backend/.dockerdev/entrypoint.sh b/backend/.dockerdev/entrypoint.sh new file mode 100755 index 0000000..66ff3bf --- /dev/null +++ b/backend/.dockerdev/entrypoint.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +rm -f $APP_HOME/tmp/pids/server.pid +rm -f $APP_HOME/tmp/pids/sidekiq.pid + +bundle exec rails db:create +bundle exec rails db:migrate + +exec "$@" diff --git a/backend/dip.yml b/backend/dip.yml new file mode 100644 index 0000000..6d8687d --- /dev/null +++ b/backend/dip.yml @@ -0,0 +1,61 @@ +version: '2' + +environment: + RAILS_ENV: development + +compose: + files: + - ./.dockerdev/docker-compose.yml + project_name: backend + +interaction: + bash: + service: runner + command: /bin/bash + compose_run_options: [no-deps] + + bundle: + service: runner + command: bundle + compose_run_options: [no-deps] + + rake: + service: runner + command: bundle exec rake + + rails: + service: runner + command: bundle exec rails + subcommands: + s: + service: rails + compose_run_options: [service-ports] + + yarn: + service: runner + command: yarn + compose_run_options: [no-deps] + + rspec: + service: runner + environment: + RAILS_ENV: test + command: bundle exec rspec + + rubocop: + service: runner + command: bundle exec rubocop + compose_run_options: [no-deps] + + psql: + service: postgres + command: psql -h postgres -U postgres -d example_app_dev + + 'redis-cli': + service: redis + command: redis-cli -h redis + +provision: + - dip compose down --volumes + - dip compose up -d postgres redis + - dip bash -c ./bin/setup diff --git a/frontend/.deploy/lib/Dockerfile b/frontend/.deploy/lib/Dockerfile new file mode 100644 index 0000000..02bd1d2 --- /dev/null +++ b/frontend/.deploy/lib/Dockerfile @@ -0,0 +1,32 @@ +# Stage: Builder +FROM node:alpine as builder + +ARG API_HOST +ARG NODE_ENV +ENV API_HOST=${API_HOST} NODE_ENV=${NODE_ENV} + +ENV APP_HOME /home/www/frontend +WORKDIR $APP_HOME + +COPY package.json yarn.lock ./ +RUN yarn install --production + +COPY . . + +RUN yarn build + +# Stage: Final +FROM node:alpine + +ARG NODE_ENV +ENV NODE_ENV=${NODE_ENV} + +RUN apk add --update --no-cache curl + +ENV APP_HOME /home/www/frontend +WORKDIR $APP_HOME + +USER node +COPY --from=builder --chown=node:node $APP_HOME ./ + +CMD ["yarn", "start", "-p", "4000"] diff --git a/frontend/.deploy/lib/deploy.sh b/frontend/.deploy/lib/deploy.sh new file mode 100644 index 0000000..8219813 --- /dev/null +++ b/frontend/.deploy/lib/deploy.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# Required variables: +# - region # ECR Region +# - aws-access-key # AWS Access Id +# - aws-secret-key # AWS Secret Key +# - service # Service's name +# - cluster # Service's cluster +# - file # docker-compose file path +# - ecs_params # AWS ECS params file path + +set -e + +deploy() { + while [[ $# -gt 0 ]] + do + key="$1" + + case $key in + --region) + REGION="$2" + shift + shift + ;; + --aws-access-key) + AWS_ACCESS_KEY_ID="$2" + shift + shift + ;; + --aws-secret-key) + AWS_SECRET_ACCESS_KEY="$2" + shift + shift + ;; + --cluster) + CLUSTER="$2" + shift + shift + ;; + --ecs_params) + ECS_PARAMS="$2" + shift + shift + ;; + --file) + FILE="$2" + shift + shift + ;; + --service) + SERVICE="$2" + shift + shift + ;; + *) + echo "Unknown option $1\n" + shift + shift + esac + done + + export TAG=$(git log -1 --format=%h) + export REGION=$REGION + + echo "🐳 Build docker image $BUILD_APP" + push_to_docker + + echo "🚀 Deploy $BUILD_APP to $CLUSTER:$SERVICE" + ecs_deploy + + echo '✅ Deploy successfully finished' +} + +push_to_docker() { + $(aws ecr get-login --region $REGION --no-include-email) + + docker-compose -f $FILE build + docker-compose -f $FILE push +} + +ecs_deploy() { + ecs-cli configure \ + --cluster $CLUSTER \ + --region $REGION \ + --default-launch-type EC2 \ + --config-name $CLUSTER + + ecs-cli compose \ + --project-name $SERVICE \ + --file $FILE \ + --ecs-params $ECS_PARAMS \ + service up \ + --cluster-config $CLUSTER \ + --force-deployment \ + --timeout 2 +} + +exec "$@" diff --git a/frontend/.deploy/lib/nginx/Dockerfile b/frontend/.deploy/lib/nginx/Dockerfile new file mode 100644 index 0000000..e22c61f --- /dev/null +++ b/frontend/.deploy/lib/nginx/Dockerfile @@ -0,0 +1,16 @@ +FROM nginx:1.16 + +RUN rm /etc/nginx/conf.d/* + +COPY configs/nginx.conf /etc/nginx/nginx.conf +COPY configs/default.conf /etc/nginx/conf.d/default.conf + +RUN touch /var/run/nginx.pid && \ + chown -R www-data:www-data /var/run/nginx.pid && \ + chown -R www-data:www-data /var/cache/nginx && \ + chown -R www-data:www-data /etc/nginx && \ + chown -R www-data:www-data /var/log + +USER www-data + +CMD [ "nginx", "-g", "daemon off;" ] diff --git a/frontend/.deploy/lib/nginx/configs/default.conf b/frontend/.deploy/lib/nginx/configs/default.conf new file mode 100644 index 0000000..6c5c26e --- /dev/null +++ b/frontend/.deploy/lib/nginx/configs/default.conf @@ -0,0 +1,34 @@ +upstream nextjs { + server nextjs:4000; +} + +server { + listen 8080 default_server; + + client_max_body_size 15M; + + server_name _; + root /home/www/frontend; + + access_log off; + error_log /var/log/nginx.error.log crit; + + location ~ ^/static { + gzip off; + gzip_static on; + + expires max; + add_header Cache-Control public; + } + + location / { + proxy_redirect off; + proxy_set_header Client-Ip $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header X-Nginx-Proxy true; + proxy_set_header Host $host; + proxy_http_version 1.1; + proxy_pass http://nextjs; + } +} diff --git a/frontend/.deploy/lib/nginx/configs/nginx.conf b/frontend/.deploy/lib/nginx/configs/nginx.conf new file mode 100644 index 0000000..1056147 --- /dev/null +++ b/frontend/.deploy/lib/nginx/configs/nginx.conf @@ -0,0 +1,33 @@ +worker_processes auto; + +worker_rlimit_nofile 4096; + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + server_tokens off; + + add_header X-Frame-Options SAMEORIGIN; + + add_header X-Content-Type-Options nosniff; + + add_header X-XSS-Protection "1; mode=block"; + server_names_hash_bucket_size 64; + server_names_hash_max_size 512; + + sendfile on; + tcp_nopush on; + + types_hash_max_size 2048; + + gzip on; + gzip_disable "msie6"; + gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript; + + include /etc/nginx/conf.d/default.conf; +} diff --git a/frontend/.deploy/production/deploy.sh b/frontend/.deploy/production/deploy.sh new file mode 100644 index 0000000..5d7a453 --- /dev/null +++ b/frontend/.deploy/production/deploy.sh @@ -0,0 +1,10 @@ +source '.deploy/lib/deploy.sh' + +deploy \ + --aws-access-key "$AWS_ACCESS_KEY_ID" \ + --aws-secret-key "$AWS_SECRET_ACCESS_KEY" \ + --region 'us-west-1' \ + --cluster 'frontend-production' \ + --service 'frontend' + --file '.deploy/production/docker-compose.yml' \ + --ecs_params '.deploy/production/ecs-params.yml' diff --git a/frontend/.deploy/production/docker-compose.yml b/frontend/.deploy/production/docker-compose.yml new file mode 100644 index 0000000..881e2d6 --- /dev/null +++ b/frontend/.deploy/production/docker-compose.yml @@ -0,0 +1,44 @@ +version: '3' + +volumes: + static: + +services: + nginx: + image: "${AWS_ACCOUNT_NUMBER}.dkr.ecr.${REGION}.amazonaws.com/frontend/nginx:${TAG}" + build: ../lib/nginx + ports: + - 80:8080 + links: + - nextjs + volumes: + - static:/home/www/frontend/static + logging: + driver: awslogs + options: + awslogs-group: frontend-production + awslogs-region: ${REGION} + awslogs-stream-prefix: nginx + healthcheck: + test: ["CMD-SHELL", "service nginx status || exit 1"] + + nextjs: + image: "${AWS_ACCOUNT_NUMBER}.dkr.ecr.${REGION}.amazonaws.com/frontend/nextjs:${TAG}" + build: + context: ../../. + dockerfile: .deploy/lib/Dockerfile + args: + API_HOST: https://api.backend.com + NODE_ENV: production + ports: + - 4000 + volumes: + - static:/home/www/frontend/static + logging: + driver: awslogs + options: + awslogs-group: frontend-production + awslogs-region: ${REGION} + awslogs-stream-prefix: nextjs + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:4000/"] diff --git a/frontend/.deploy/production/ecs-params.yml b/frontend/.deploy/production/ecs-params.yml new file mode 100644 index 0000000..d09e496 --- /dev/null +++ b/frontend/.deploy/production/ecs-params.yml @@ -0,0 +1,13 @@ +version: 1 +task_definition: + ecs_network_mode: bridge + + task_size: + cpu_limit: 800 + mem_limit: 700 + + services: + nginx: + essential: true + nextjs: + essential: true diff --git a/frontend/.deploy/staging/deploy.sh b/frontend/.deploy/staging/deploy.sh new file mode 100755 index 0000000..bee7f0e --- /dev/null +++ b/frontend/.deploy/staging/deploy.sh @@ -0,0 +1,10 @@ +source '.deploy/lib/deploy.sh' + +deploy \ + --aws-access-key "$AWS_ACCESS_KEY_ID" \ + --aws-secret-key "$AWS_SECRET_ACCESS_KEY" \ + --region 'us-west-1' \ + --cluster 'frontend-staging' \ + --service 'frontend' \ + --file '.deploy/staging/docker-compose.yml' \ + --ecs_params '.deploy/staging/ecs-params.yml' diff --git a/frontend/.deploy/staging/docker-compose.yml b/frontend/.deploy/staging/docker-compose.yml new file mode 100644 index 0000000..0018f8c --- /dev/null +++ b/frontend/.deploy/staging/docker-compose.yml @@ -0,0 +1,44 @@ +version: '3' + +volumes: + static: + +services: + nginx: + image: "${AWS_ACCOUNT_NUMBER}.dkr.ecr.${REGION}.amazonaws.com/frontend/nginx:${TAG}" + build: ../lib/nginx + ports: + - 80:8080 + links: + - nextjs + volumes: + - static:/home/www/frontend/static + logging: + driver: awslogs + options: + awslogs-group: frontend-staging + awslogs-region: ${REGION} + awslogs-stream-prefix: nginx + healthcheck: + test: ["CMD-SHELL", "service nginx status || exit 1"] + + nextjs: + image: "${AWS_ACCOUNT_NUMBER}.dkr.ecr.${REGION}.amazonaws.com/frontend/nextjs:${TAG}" + build: + context: ../../. + dockerfile: .deploy/lib/Dockerfile + args: + API_HOST: https://stagingapi.backend.com + NODE_ENV: staging + ports: + - 4000 + volumes: + - static:/home/www/frontend/static + logging: + driver: awslogs + options: + awslogs-group: frontend-staging + awslogs-region: ${REGION} + awslogs-stream-prefix: nextjs + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:4000/"] diff --git a/frontend/.deploy/staging/ecs-params.yml b/frontend/.deploy/staging/ecs-params.yml new file mode 100644 index 0000000..d09e496 --- /dev/null +++ b/frontend/.deploy/staging/ecs-params.yml @@ -0,0 +1,13 @@ +version: 1 +task_definition: + ecs_network_mode: bridge + + task_size: + cpu_limit: 800 + mem_limit: 700 + + services: + nginx: + essential: true + nextjs: + essential: true diff --git a/frontend/.dockerdev/Dockerfile b/frontend/.dockerdev/Dockerfile new file mode 100644 index 0000000..3e9c1fe --- /dev/null +++ b/frontend/.dockerdev/Dockerfile @@ -0,0 +1,5 @@ +# Stage: Builder +FROM node:alpine as builder + +ENV APP_HOME /home/www/frontend +WORKDIR $APP_HOME diff --git a/frontend/.dockerdev/docker-compose.yml b/frontend/.dockerdev/docker-compose.yml new file mode 100644 index 0000000..bef9539 --- /dev/null +++ b/frontend/.dockerdev/docker-compose.yml @@ -0,0 +1,24 @@ +version: '3.4' + +x-nodejs: &nodejs + build: + context: ../ + dockerfile: .dockerdev/Dockerfile + image: frontend-dev:0.1.0 + tmpfs: + - /tmp + stdin_open: true + tty: true + +services: + nextjs: + <<: *nodejs + command: yarn dev + volumes: + - ../:/home/www/frontend:cached + - node_modules:/home/www/frontend/node_modules + ports: + - '4000:4000' + +volumes: + node_modules: diff --git a/frontend/dip.yml b/frontend/dip.yml new file mode 100644 index 0000000..c6d0c6c --- /dev/null +++ b/frontend/dip.yml @@ -0,0 +1,21 @@ +version: '2' + +compose: + files: + - ./.dockerdev/docker-compose.yml + project_name: frontend + +interaction: + yarn: + service: nextjs + command: yarn + subcommands: + dev: + command: yarn dev + compose_run_options: [service-ports] + install: + command: yarn install + +provision: + - dip compose down --volumes + - dip yarn install