From 50d866d7b025c3c7e9b3cc7f5bb641d15b92798a Mon Sep 17 00:00:00 2001 From: aqswa <69039161+aqswa@users.noreply.github.com> Date: Tue, 13 Dec 2022 18:58:50 +0900 Subject: [PATCH] chore: deploy settings --- .github/workflows/deploy.yml | 45 ++++++++++++ .gitignore | 1 + CeosVoteBack/settings/__init__.py | 1 + .../{settings.py => settings/base.py} | 29 +++----- CeosVoteBack/settings/dev.py | 14 ++++ CeosVoteBack/settings/prod.py | 15 ++++ Dockerfile.prod | 68 +++++++++++++++++++ config/docker/entrypoint.prod.sh | 7 ++ config/nginx/Dockerfile | 4 ++ config/nginx/nginx.conf | 23 +++++++ config/scripts/deploy.sh | 27 ++++++++ docker-compose.prod.yml | 36 ++++++++++ 12 files changed, 251 insertions(+), 19 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 CeosVoteBack/settings/__init__.py rename CeosVoteBack/{settings.py => settings/base.py} (84%) create mode 100644 CeosVoteBack/settings/dev.py create mode 100644 CeosVoteBack/settings/prod.py create mode 100644 Dockerfile.prod create mode 100644 config/docker/entrypoint.prod.sh create mode 100644 config/nginx/Dockerfile create mode 100644 config/nginx/nginx.conf create mode 100644 config/scripts/deploy.sh create mode 100644 docker-compose.prod.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..b88e1bb --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,45 @@ +name: Deploy to EC2 + +on: + push: + branches: + - main + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@master + + - name: create env file + run: | + touch .env + echo "${{ secrets.ENV_VARS }}" >> .env + + - name: create remote directory + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ubuntu + key: ${{ secrets.KEY }} + script: mkdir -p /home/ubuntu/srv/ubuntu + + - name: copy source via ssh key + uses: burnett01/rsync-deployments@4.1 + with: + switches: -avzr --delete + remote_path: /home/ubuntu/srv/ubuntu/ + remote_host: ${{ secrets.HOST }} + remote_user: ubuntu + remote_key: ${{ secrets.KEY }} + + - name: executing remote ssh commands using password + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST }} + username: ubuntu + key: ${{ secrets.KEY }} + script: | + sh /home/ubuntu/srv/ubuntu/config/scripts/deploy.sh diff --git a/.gitignore b/.gitignore index 3d4afb4..465810e 100644 --- a/.gitignore +++ b/.gitignore @@ -135,6 +135,7 @@ celerybeat.pid # Environments .env +.env.prod .venv env/ venv/ diff --git a/CeosVoteBack/settings/__init__.py b/CeosVoteBack/settings/__init__.py new file mode 100644 index 0000000..f89f57a --- /dev/null +++ b/CeosVoteBack/settings/__init__.py @@ -0,0 +1 @@ +from .dev import * \ No newline at end of file diff --git a/CeosVoteBack/settings.py b/CeosVoteBack/settings/base.py similarity index 84% rename from CeosVoteBack/settings.py rename to CeosVoteBack/settings/base.py index af57a69..09e2607 100644 --- a/CeosVoteBack/settings.py +++ b/CeosVoteBack/settings/base.py @@ -14,7 +14,7 @@ import environ # Build paths inside the project like this: os.path.join(BASE_DIR, ...) -BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) env = environ.Env(DEBUG=(bool, False)) environ.Env.read_env(os.path.join(BASE_DIR, '.env')) @@ -26,8 +26,6 @@ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = env('DJANGO_SECRET_KEY') -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True ALLOWED_HOSTS = [] @@ -56,6 +54,7 @@ ] MIDDLEWARE = [ + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', @@ -86,20 +85,6 @@ WSGI_APPLICATION = 'CeosVoteBack.wsgi.application' -# Database -# https://docs.djangoproject.com/en/4.0/ref/settings/#databases - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': env('DATABASE_NAME'), - 'USER': env('DATABASE_USER'), - 'PASSWORD': env('DATABASE_PASSWORD'), - 'HOST': env('DATABASE_HOST'), - 'PORT': env('DATABASE_PORT'), - } -} - # Password validation # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators @@ -135,9 +120,15 @@ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/4.0/howto/static-files/ -STATIC_URL = 'static/' + +STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'static') + +# Media files +MEDIA_URL = '/media/' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Default primary key field type # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' \ No newline at end of file diff --git a/CeosVoteBack/settings/dev.py b/CeosVoteBack/settings/dev.py new file mode 100644 index 0000000..29fe2c3 --- /dev/null +++ b/CeosVoteBack/settings/dev.py @@ -0,0 +1,14 @@ +from .base import * # noqa + +DEBUG = True + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': env('DATABASE_NAME'), + 'USER': env('DATABASE_USER'), + 'PASSWORD': env('DATABASE_PASSWORD'), + 'HOST': env('DATABASE_HOST'), + 'PORT': env('DATABASE_PORT'), + } +} \ No newline at end of file diff --git a/CeosVoteBack/settings/prod.py b/CeosVoteBack/settings/prod.py new file mode 100644 index 0000000..d2e01bb --- /dev/null +++ b/CeosVoteBack/settings/prod.py @@ -0,0 +1,15 @@ +from .base import * # noqa + +DEBUG = False +ALLOWED_HOSTS = env.list('DJANGO_ALLOWED_HOSTS') + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': env('DATABASE_NAME'), + 'USER': env('DATABASE_USER'), + 'PASSWORD': env('DATABASE_PASSWORD'), + 'HOST': env('DATABASE_HOST'), + 'PORT': env('DATABASE_PORT'), + } +} \ No newline at end of file diff --git a/Dockerfile.prod b/Dockerfile.prod new file mode 100644 index 0000000..91a205c --- /dev/null +++ b/Dockerfile.prod @@ -0,0 +1,68 @@ +# BUILDER # +########### + +# pull official base image +FROM python:3.10.2-alpine as builder + +# set work directory +WORKDIR /usr/src/app + + +# set environment variables +ENV PYTHONDONTWRITEBYTECODE 1 +ENV PYTHONUNBUFFERED 1 + +# install psycopg2 dependencies +RUN apk update && apk add python3 python3-dev mariadb-dev build-base && pip3 install mysqlclient + +# install dependencies +RUN python -m pip install --upgrade pip +COPY ./requirements.txt . +RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt + + +######### +# FINAL # +######### + +# pull official base image +FROM python:3.10.2-alpine + +# create directory for the app user +RUN mkdir -p /home/app + +# create the app user +RUN addgroup -S app && adduser -S app -G app + +# create the appropriate directories +ENV HOME=/home/app +ENV APP_HOME=/home/app/web +RUN mkdir $APP_HOME +RUN mkdir $APP_HOME/static +RUN mkdir $APP_HOME/media +WORKDIR $APP_HOME + +# install dependencies +RUN apk update && apk add libpq +RUN apk update \ + && apk add --virtual build-deps gcc python3-dev musl-dev \ + && apk add --no-cache mariadb-dev \ + && apk add libc-dev libffi-dev \ + && apk add zlib-dev jpeg-dev +COPY --from=builder /usr/src/app/wheels /wheels +COPY --from=builder /usr/src/app/requirements.txt . +RUN pip install mysqlclient +RUN pip install --no-cache /wheels/* +RUN apk del build-deps + +# copy entrypoint-prod.sh +COPY ./config/docker/entrypoint.prod.sh $APP_HOME + +# copy project +COPY . $APP_HOME + +# chown all the files to the app user +RUN chown -R app:app $APP_HOME + +# change to the app user +USER app \ No newline at end of file diff --git a/config/docker/entrypoint.prod.sh b/config/docker/entrypoint.prod.sh new file mode 100644 index 0000000..eeffd3c --- /dev/null +++ b/config/docker/entrypoint.prod.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +python manage.py collectstatic --no-input +python manage.py makemigrations +python manage.py migrate + +exec "$@" \ No newline at end of file diff --git a/config/nginx/Dockerfile b/config/nginx/Dockerfile new file mode 100644 index 0000000..8e5916e --- /dev/null +++ b/config/nginx/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:1.19.0-alpine + +RUN rm /etc/nginx/conf.d/default.conf +COPY nginx.conf /etc/nginx/conf.d \ No newline at end of file diff --git a/config/nginx/nginx.conf b/config/nginx/nginx.conf new file mode 100644 index 0000000..10008e6 --- /dev/null +++ b/config/nginx/nginx.conf @@ -0,0 +1,23 @@ +upstream ceos_vote_back { + server web:8000; +} + +server { + + listen 80; + + location / { + proxy_pass http://ceos_vote_back; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_redirect off; + } + + location /static/ { + alias /home/app/web/static/; + } + + location /media/ { + alias /home/app/web/media/; + } +} diff --git a/config/scripts/deploy.sh b/config/scripts/deploy.sh new file mode 100644 index 0000000..619e1e7 --- /dev/null +++ b/config/scripts/deploy.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Installing docker engine if not exists +if ! type docker > /dev/null +then + echo "docker does not exist" + echo "Start installing docker" + sudo apt-get update + sudo apt install -y apt-transport-https ca-certificates curl software-properties-common + 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 bionic stable" + sudo apt update + apt-cache policy docker-ce + sudo apt install -y docker-ce +fi + +# Installing docker-compose if not exists +if ! type docker-compose > /dev/null +then + echo "docker-compose does not exist" + echo "Start installing docker-compose" + sudo curl -L "https://github.com/docker/compose/releases/download/1.27.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + sudo chmod +x /usr/local/bin/docker-compose +fi + +echo "start docker-compose up: ubuntu" +sudo docker-compose -f /home/ubuntu/srv/ubuntu/docker-compose.prod.yml up --build -d \ No newline at end of file diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml new file mode 100644 index 0000000..e086ebd --- /dev/null +++ b/docker-compose.prod.yml @@ -0,0 +1,36 @@ +version: '3' +services: + + web: + container_name: web + build: + context: ./ + dockerfile: Dockerfile.prod + command: gunicorn CeosVoteBack.wsgi:application --bind 0.0.0.0:8000 + environment: + DJANGO_SETTINGS_MODULE: CeosVoteBack.settings.prod + env_file: + - .env + volumes: + - static:/home/app/web/static + - media:/home/app/web/media + expose: + - 8000 + entrypoint: + - sh + - config/docker/entrypoint.prod.sh + + nginx: + container_name: nginx + build: ./config/nginx + volumes: + - static:/home/app/web/static + - media:/home/app/web/media + ports: + - "80:80" + depends_on: + - web + +volumes: + static: + media: \ No newline at end of file