CarlosSR
CarlosSR

Reputation: 1195

How to deploy dockerized Django+uWSGI+Nginx app to Google App Engine using CircleCI

I have developed a Django dockerized web app using docker-compose. It runs in my local fine. The point is that when I define a CI pipeline, specifically CircleCI (I don't know how it works with any other alternative), to upload it to GCloud App Engine the workflow works fine but when visiting the url it returns nothing (500 error).

The code I have and that I run locally using is the following. When I set the CircleCI pipeline I have no clue on how the app.yaml file interacts and what the steps in the .circleci/config.yml should be in order to run the docker-compose. Any idea or resource I might use?

My Dockerfile:

FROM python:3.9-alpine
ENV PATH="/scripts:${PATH}"

COPY ./requirements.txt /requirements.txt
RUN apk add --update --no-cache --virtual .tmp gcc libc-dev linux-headers
RUN pip install -r /requirements.txt
RUN apk del .tmp

RUN mkdir -p /app
COPY ./app /app
WORKDIR /app
COPY ./scripts /scripts

#this allows for execute permission in all files inside /scripts/
RUN chmod +x /scripts/*

RUN mkdir -p /vol/web/media
RUN mkdir -p /vol/web/static

RUN adduser -D user
RUN chown -R user:user /vol
RUN chmod -R 755 /vol/web
USER user

CMD ["entrypoint.sh"]

My docker-compose file:

version: '3.9'

services:
  app:
    build:
      context: .
    volumes:
      - static_data:/vol/web
    environment:
      - SECRET_KEY=samplesecret123
      - ALLOWED_HOSTS=127.0.0.1,localhost

  proxy:
    build:
      context: ./proxy
    volumes:
      - static_data:/vol/static
    ports:
      - "8080:8080"
    depends_on:
      - app

volumes:
  static_data:

Nginx Dockerfile:

FROM nginxinc/nginx-unprivileged:1-alpine

COPY ./default.conf /etc/nginx/conf.d/default.conf
COPY ./uwsgi_params /etc/nginx/uwsgi_params

USER root

RUN mkdir -p /vol/static
RUN chmod 755 /vol/static

USER nginx

Nginx default.conf

server {
    listen 8080;

    location /static {
        alias /vol/static;
    }

    location / {
        uwsgi_pass app:8000;
        include /etc/nginx/uwsgi_params;
    }
}

entrypoint.sh

#!/bin/sh

set -e
python manage.py collectstatic --no-input

uwsgi --socket :8000 --master --enable-threads --module app.wsgi

.circleci/config.yml

version: 2.1

workflows:
  version: 2
  build_and_deploy_workflow:
    jobs:
      - build_and_deploy_job:
          filters:
            branches:
              only:
                - master

jobs:
  build_and_deploy_job:
    docker:
      - image: google/cloud-sdk ##based in Debian
    steps:
      - checkout
      - restore_cache:
          key: deps1-{{ .Branch }}-{{ checksum "requirements.txt" }}

      - run:
          name: Install requirements.txt
          command: |
            apt install -y python-pip
            python3 -m pip install -r requirements.txt

      - save_cache:
          key: deps1-{{ .Branch }}-{{ checksum "requirements.txt" }}
          paths:
            - "venv"

      - run:
          name: Install Docker Compose
          command: |
            curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` > ~/docker-compose
            chmod +x ~/docker-compose
            apt-get install -y sudo
            sudo mv ~/docker-compose /usr/local/bin/docker-compose
      
      - setup_remote_docker

      - run:
          name: 'Collect static'
          command: |
            docker-compose -f docker-compose-deploy.yml up --build
#            docker-compose build
#            docker-compose run --rm app
#            docker-compose run --rm app sh -c "python manage.py collectstatic"

      - run:
          name: 'Deploy to app engine'
          command: |
            echo ${GCLOUD_SERVICE_KEY} > /tmp/sa_key.json | \
            gcloud auth activate-service-account --key-file=/tmp/sa_key.json
            rm /tmp/sa_key.json
            gcloud config set project [projectname]
            gcloud config set compute/region [region]
            gcloud app deploy app.yaml

app.yaml GCloud App Engine:

runtime: python39
#entrypoint: gunicorn -b :$PORT --chdir app/ app.wsgi:application
#entrypoint: gunicorn -b :$PORT app:wsgi
entrypoint: uwsgi --socket :8000 --master --enable-threads --module app.wsgi

handlers:
  - url: /static
    static_dir: static/
  - url: /.*
    script: auto

Upvotes: 0

Views: 305

Answers (1)

Here is a link that could help you with an example of app.yaml file for a Python 3 application: https://cloud.google.com/appengine/docs/standard/python3/config/appref

Code example:

runtime: python39 # or another supported version

instance_class: F2

env_variables:
  BUCKET_NAME: "example-gcs-bucket"

handlers:
# Matches requests to /images/... to files in static/images/...
- url: /images
  static_dir: static/images

- url: /.*
  secure: always
  redirect_http_response_code: 301
  script: auto

For Python 3, the app.yaml is required to contain at least a runtime: python39 entry.

For a brief overview, see defining runtime settings: https://cloud.google.com/appengine/docs/standard/python3/configuring-your-app-with-app-yaml

To deploy to Google App Engine with CircleCi I found this article that may help you with your main issue: https://medium.com/@1555398769574/deploy-to-google-app-engine-with-circleci-or-github-actions-cb1bab15ca80

Code example:

.circleci/config.yaml
version: 2
jobs:
 build:
   working_directory: ~/workspace
   docker:
     - image: circleci/php:7.2-stretch-node-browsers
   steps:
     - checkout
     - run: |
         cp .env.example .env &&
         php artisan key:generate
    
     - persist_to_workspace:
         root: .
         paths:
           - .
 deploy:
   working_directory: ~/workspace
   docker:
     - image: google/cloud-sdk
  
   steps:
     - attach_workspace:
         at: .
     - run:
         name: Service Account Key
         command: echo ${GCLOUD_SERVICE_KEY} > ${HOME}/gcloud-service-key.json
     - run:
         name: Set gcloud command
         command: |
           gcloud auth activate-service-account --key-file=${HOME}/gcloud-service-key.json
           gcloud --quiet config set project ${GOOGLE_PROJECT_ID}
     - run:
         name: deploy to Google App Engine
         command: |
           gcloud app deploy app.yaml
workflows:
 version: 2
 build:
   jobs:
     - build
     - deploy:
       context: gcp
       requires:
         - build
       filters:
         branches:
           only: master

Adding additional documentation on how to create CI/CD pipeline for Google App Engine with CircleCI 2.0: https://runzhuoli.me/2018/12/21/ci-cd-gcp-gae-circleci.html

Upvotes: 0

Related Questions