From 257ba05f2f4bddb1b97eee7bf9b2bcdb3603a089 Mon Sep 17 00:00:00 2001 From: rishabhjain Date: Tue, 20 Nov 2018 18:35:46 -0500 Subject: [PATCH] CI/CD: Add production tooling script (#1916) --- .gitignore | 11 ++- .travis.yml | 1 + docker-compose-production.yml | 12 +-- docker-compose-staging.yml | 9 +- docker-compose.yml | 5 +- ...{docker_prod.env => docker_production.env} | 0 docker/prod/nodejs/Dockerfile | 7 +- docker/prod/nodejs/Dockerfile_staging | 26 ------ docker/prod/nodejs/nginx_production.conf | 40 +++++++++ .../nodejs/{nginx.conf => nginx_staging.conf} | 0 gulpfile.js | 2 +- scripts/deployment/deploy.sh | 88 +++++++++++++++++++ .../{deploy/deploy.sh => deployment/push.sh} | 0 13 files changed, 149 insertions(+), 52 deletions(-) rename docker/prod/{docker_prod.env => docker_production.env} (100%) delete mode 100644 docker/prod/nodejs/Dockerfile_staging create mode 100644 docker/prod/nodejs/nginx_production.conf rename docker/prod/nodejs/{nginx.conf => nginx_staging.conf} (100%) create mode 100755 scripts/deployment/deploy.sh rename scripts/{deploy/deploy.sh => deployment/push.sh} (100%) diff --git a/.gitignore b/.gitignore index d869016a58..57d1b97f9c 100644 --- a/.gitignore +++ b/.gitignore @@ -49,8 +49,13 @@ media/ # .DS_Store for OSX .DS_Store -# Encrypted files +# Docker Environment Files docker/prod/docker_staging.env +# TODO: Add docker_production.env file docker/prod/docker_prod.env -secrets.tar -scripts/docker_pull.sh + +# SSL files +ssl/ + +# Code editors +.vscode diff --git a/.travis.yml b/.travis.yml index 12b1d37376..2809b4e0ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,6 +36,7 @@ script: - py.test --cov . --cov-config .coveragerc after_success: - coveralls --rcfile=.coveragerc +- ./scripts/deployment/push.sh notifications: email: on_success: change diff --git a/docker-compose-production.yml b/docker-compose-production.yml index 58ae4898ff..473787e6e4 100644 --- a/docker-compose-production.yml +++ b/docker-compose-production.yml @@ -3,9 +3,8 @@ services: django: image: ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/evalai-production-backend:${COMMIT_ID} - container_name: django env_file: - - docker/prod/docker_prod.env + - docker/prod/docker_production.env build: context: ./ dockerfile: docker/prod/django/Dockerfile @@ -14,25 +13,22 @@ services: worker: image: ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/evalai-production-worker:${COMMIT_ID} - container_name: worker build: context: ./ dockerfile: docker/prod/worker/Dockerfile env_file: - - docker/prod/docker_prod.env + - docker/prod/docker_production.env nodejs: image: ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/evalai-production-frontend:${COMMIT_ID} - container_name: nodejs build: context: ./ dockerfile: docker/prod/nodejs/Dockerfile - environment: - NODE_ENV: production + args: + NODE_ENV: production ports: - "80:80" - "443:443" volumes: - /code/node_modules - /code/bower_components - - /etc/nginx/ssl/:/etc/nginx/ssl/ diff --git a/docker-compose-staging.yml b/docker-compose-staging.yml index 5d9d9bcdab..c68f7d38d2 100644 --- a/docker-compose-staging.yml +++ b/docker-compose-staging.yml @@ -3,7 +3,6 @@ services: django: image: ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/evalai-staging-backend:${COMMIT_ID} - container_name: django env_file: - docker/prod/docker_staging.env build: @@ -14,7 +13,6 @@ services: worker: image: ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/evalai-staging-worker:${COMMIT_ID} - container_name: worker build: context: ./ dockerfile: docker/prod/worker/Dockerfile @@ -23,12 +21,11 @@ services: nodejs: image: ${AWS_ACCOUNT_ID}.dkr.ecr.us-east-1.amazonaws.com/evalai-staging-frontend:${COMMIT_ID} - container_name: nodejs build: context: ./ - dockerfile: docker/prod/nodejs/Dockerfile_staging - environment: - NODE_ENV: production + dockerfile: docker/prod/nodejs/Dockerfile + args: + NODE_ENV: staging ports: - "80:80" volumes: diff --git a/docker-compose.yml b/docker-compose.yml index 838c2ffe84..71bd53778b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: "2" +version: "3" services: db: @@ -13,7 +13,6 @@ services: - 9324:9324 django: - container_name: django hostname: django env_file: - docker/dev/docker.env @@ -29,7 +28,6 @@ services: - .:/code worker: - container_name: worker env_file: - docker/dev/docker.env build: @@ -41,7 +39,6 @@ services: - .:/code nodejs: - container_name: nodejs hostname: nodejs build: context: ./ diff --git a/docker/prod/docker_prod.env b/docker/prod/docker_production.env similarity index 100% rename from docker/prod/docker_prod.env rename to docker/prod/docker_production.env diff --git a/docker/prod/nodejs/Dockerfile b/docker/prod/nodejs/Dockerfile index 0a1cc8f7c3..61cf502f27 100644 --- a/docker/prod/nodejs/Dockerfile +++ b/docker/prod/nodejs/Dockerfile @@ -1,5 +1,6 @@ FROM node:8.11.2 as node +ARG NODE_ENV WORKDIR /code # Add dependencies @@ -18,10 +19,8 @@ RUN npm install RUN bower install --allow-root ADD frontend /code/frontend -# TODO: Change `gulp dev` to `gulp prod` -RUN gulp prod -EXPOSE 8888 +RUN gulp ${NODE_ENV} FROM nginx:1.13-alpine -COPY docker/prod/nodejs/nginx.conf /etc/nginx/conf.d/default.conf +COPY docker/prod/nodejs/nginx_${NODE_ENV}.conf /etc/nginx/conf.d/default.conf COPY --from=node /code /code diff --git a/docker/prod/nodejs/Dockerfile_staging b/docker/prod/nodejs/Dockerfile_staging deleted file mode 100644 index 378f8bce77..0000000000 --- a/docker/prod/nodejs/Dockerfile_staging +++ /dev/null @@ -1,26 +0,0 @@ -FROM node:8.11.2 as node - -WORKDIR /code - -# Add dependencies -ADD ./package.json /code -ADD ./bower.json /code -ADD ./gulpfile.js /code -ADD ./.eslintrc /code -ADD ./karma.conf.js /code - -# Install Prerequisites -RUN npm install -g bower gulp@next -RUN npm install phantomjs-prebuilt -RUN npm link gulp -RUN npm cache clean -f -RUN npm install -RUN bower install --allow-root -ADD frontend /code/frontend - -RUN gulp staging -EXPOSE 8888 - -FROM nginx:1.13-alpine -COPY docker/prod/nodejs/nginx.conf /etc/nginx/conf.d/default.conf -COPY --from=node /code /code diff --git a/docker/prod/nodejs/nginx_production.conf b/docker/prod/nodejs/nginx_production.conf new file mode 100644 index 0000000000..868863fb9d --- /dev/null +++ b/docker/prod/nodejs/nginx_production.conf @@ -0,0 +1,40 @@ +upstream django_app { + server django:8000 fail_timeout=0; +} + +server { + server_name evalapi.cloudcv.org; + listen 80; + client_max_body_size 400M; + rewrite ^/(.*) https://evalapi.cloudcv.org/$1 permanent; + + # Access Logs + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + location / { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_pass http://django_app; + } +} + +server { + listen 80; + sendfile on; + default_type application/octet-stream; + # + server_name evalai.cloudcv.org; + client_max_body_size 200M; + gzip on; + gzip_http_version 1.1; + gzip_disable "MSIE [1-6]\."; + gzip_min_length 256; + gzip_vary on; + gzip_proxied expired no-cache no-store private auth; + gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript; + gzip_comp_level 9; + root /code/frontend; + location / { + try_files $uri $uri/ /index.html =404; + } +} diff --git a/docker/prod/nodejs/nginx.conf b/docker/prod/nodejs/nginx_staging.conf similarity index 100% rename from docker/prod/nodejs/nginx.conf rename to docker/prod/nodejs/nginx_staging.conf diff --git a/gulpfile.js b/gulpfile.js index b722632e6d..6ea57d3fb7 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -304,7 +304,7 @@ function watch() { var parallelTasks = gulp.parallel(vendorcss, vendorjs, css, js, html, images, fonts); -gulp.task('prod', gulp.series(clean, function(done) { +gulp.task('production', gulp.series(clean, function(done) { production = true; done(); }, parallelTasks, configProd, injectpaths, lint)); diff --git a/scripts/deployment/deploy.sh b/scripts/deployment/deploy.sh new file mode 100755 index 0000000000..e8a432f5c6 --- /dev/null +++ b/scripts/deployment/deploy.sh @@ -0,0 +1,88 @@ +#!/bin/bash +set -e + +opt=${1} +env=${2} + +aws_login() { + aws configure set default.region us-east-1 + eval $(aws ecr get-login --no-include-email) +} + +setup() { + export LC_ALL="en_US.UTF-8" + export LC_CTYPE="en_US.UTF-8" + sudo add-apt-repository ppa:deadsnakes/ppa + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" + sudo apt-get update + apt-cache policy docker-ce + sudo apt-get install -y docker-ce + sudo apt-get install python3.6 + sudo apt-get install python3-pip + pip3 install awscli + pip3 install docker-compose +} + +if ! python3 -c "import awscli" &> /dev/null; then + echo "Installing packages and dependencies..." + setup; +fi + +if [ -z ${AWS_ACCOUNT_ID} ]; then + echo "AWS_ACCOUNT_ID not set." + exit 0 +fi + +if [ -z ${COMMIT_ID} ]; then + export COMMIT_ID="latest" +fi + +case $opt in + pull) + aws_login; + echo "Pulling environment variables file..." + aws s3 cp s3://cloudcv-secrets/evalai/${env}/docker_${env}.env ./docker/prod/docker_${env}.env + echo "Environment varibles file successfully downloaded." + if [ ${env} == "production" ]; then + echo "Pulling ssl certificates and nginx configuration..." + aws s3 cp s3://cloudcv-secrets/evalai/${env}/ssl/ ./ssl/ + aws s3 cp s3://cloudcv-secrets/evalai/${env}/nginx_${env}.conf ./docker/prod/nodejs/nginx_${env}.conf + fi + echo "Pulling docker images from ECR..." + docker-compose -f docker-compose-${env}.yml pull + echo "Completed Pull operation." + ;; + deploy) + echo "Deploying docker container..." + docker-compose -f docker-compose-${env}.yml up -d + echo "Completed Pull operation." + ;; + scale) + service=${3} + instances=${4} + echo "Scaling the containers..." + docker-compose -f docker-compose-${env}.yml scale ${service}=${instances} + ;; + clean) + { + docker-compose -f docker-compose-${env}.yml rm -s -v -f + } || { + echo "Delete operation skipped since no container or image found!" + } + docker rmi $(docker images -a -q) + echo "Sucessfully cleaned all the images." + ;; + *) + echo "EvalAI deployment utility script" + echo " Usage: $0 {pull|deploy|scale|clean}" + echo + echo " pull : Pull docker images from ECR." + echo " Eg. ./scripts/deployment/deploy.sh pull production" + echo " deploy : Deploy containers in the respective environment." + echo " Eg. ./scripts/deployment/deploy.sh deploy production" + echo " scale : Scale particular docker service in an environment." + echo " Eg. ./scripts/deployment/deploy.sh scale production django 5" + echo " clean : Remove all docker containers and images." + echo " Eg. ./scripts/deployment/deploy.sh clean production" +esac diff --git a/scripts/deploy/deploy.sh b/scripts/deployment/push.sh similarity index 100% rename from scripts/deploy/deploy.sh rename to scripts/deployment/push.sh