DevOps Architecture
├── .github/
│ └── ISSUE_TEMPLATE/
├── aws/ # DevOps
├── backend/
│ ├── backend/
│ └── settings.py
│ ├── ctj_api/
│ ├── frontend_dist/
│ ├── staticfiles/
│ ├── entrypoint.sh # DevOps
│ ├── manage.py
│ ├── pyproject.toml
│ └── startServer.sh
├── dev/ # DevOps
│ ├── django.dockerfile
│ ├── vite.Dockerfile
│ └── dev.env
├── frontend/
├── mkdocs/
├── stage/ # DevOps
│ ├── Dockerfile
│ └── stage.env
├── .dockerignore # DevOps
├── .gitignore
├── CONTRIBUTING.md
├── docker-compose.docs.yml # DevOps
├── docker-compose.stage.yml # DevOps
├── docker-compose.yml # DevOps
├── LICENSE
└── README.md
Overall project structure
├── dev/
│ ├── dev.env.example
│ ├── django.dockerfile
│ └── vite.Dockerfile
├── stage/
│ ├── Dockerfile
│ └── stage.env.example
├── .dockerignore
├── docker-compose.docs.yml
├── docker-compose.stage.yml
└── docker-compose.yml
DevOps Architecture
Summary
DevOps Tech Stack: Docker, Daphne, Whitenoise, PostgreSQL
Our devops files can be thought of as a set of files needed to create an exact replica of our environments for our developers. Although the overall structure appears deceptively basic, it only represents a fraction of our devops files. Several of our files, such as the ones that construct our staging environment, contains sensitive information, and as such are not placed in our public repository. For most frontend and backend work, there is never a need to access these files.
*Note: On the rare occasions that there is a need to access the sensitive files for environments other than development, please consult the development lead of the project. They will know exactly what files you need, and what permissions you need to access them.
Overview of Directories and Files
- dev/: contains two dockerfiles and an env file.
django.dockerfile
contains information for our Django server setup.vite.Dockerfile
contains information to start our vite dev server.dev.env.example
is an example of the env file needed to configure our dev environment. - .dockerignore: This file tells Docker to ignore certain files when building the container. These are usually files that pertain to docker or git, which are not important when building the webserver.
- docker-compose.yml: Contains instructions for docker when we run "docker compose". To know more about what each line does, please consult Docker's documentation. This compose file starts the development environment.
- docker-compose.stage.yml: Docker compose file for starting the stage environment.
- docker-compose.docs.yml: Docker compose file for starting the mkdocs development server.
Docker
Docker is a platform that allows packaging and virtualizing applications within a container. This gives developers the powerful ability to collaborate in a stable, synchronized environment, and deploying web applications with the greatest of ease. We will not be going too much into Docker here, but we will explain in greater depth some of the Docker configurations we have made.
docker-compose.yml
This file contains configuration directions for docker compose. It consists of three services: pgdb
(our database), vite
(our vite bundler), and django
(our django server). The vite and django service relies on separate dockerfiles, located in the dev
directory to build the container. This separation of dockerfiles enable each container to be build with its own set of dependencies. It also makes rebuilding the container simple when dependencies are migrated to a newer version.
For those of you used to creating applications without Docker, most would run vite and django in separate terminals, so that they can both run at the same time. For the purposes of brevity, the different services can be considered to be Docker's way of running separate terminals.
One will also notice that the Django command uses a placeholder server name, 0.0.0.0:8000
. This placeholder is important, since Docker creates an isolated environment. As a result, servers that are run in Docker does not recognize a browser from outside of that environment. Without this server name, localhost:8000 will not reach the server, as the server would recognize your browser as coming from a foreign machine. Therefore, all warnings related to 0.0.0.0, should they pop-up, should be ignored.
The vite frontend and django backend dev servers are split into localhost:5175
and localhost:8000
, respectively.
*.dockerfile
Dockerfiles are files that define how a container is built. Although containerization is a deep concept, to put it briefly, you can think of containers as separate "mini-computers", each programmed to do one thing. Some containers, such as our pgdb
container does not require a dockerfile to configure it, as it works out of the box. On the otherhand, our vite
and django
containers need dockerfile to built out the files we need to run it effectively.
FROM node:latest
WORKDIR /code
# install app dependencies
COPY ./package.json ./
COPY ./package-lock.json ./
RUN npm install
# add app
COPY . .
Sample dockerfile that copies the package.json from a project and installs all dependencies.
Dockerfiles are usually named as just dockerfile
, as that is the default name that docker looks for when constructing our builds. Since our project requires multiple dockerfiles, we name them with .dockerfile
extensions, a convention that allows VSCode to detect dockerfiles and use appropriate syntax highlighting.
Do note that docker and dockerfiles can be fickle to work with, especially on old devices. Further down this documentation are various tips and commands that can be used with docker to help debug your code. But as always, consulting official documentation is the best way to get accurate, up-to-date information.
Environments
Development
Our development environment is entirely defined by our docker-compose.yml
, and the files inside of dev/
. More information about those files can be found above.
Of note, however, is the dev.env.example
file. This file is only a sample, but lists out all the environmental variables needed to run our site. While most of them are prefilled, some uses <>
to indicate placeholders, which must be filled in by the developer.
Staging
The main stage file is docker-compose.stage.yml
. This docker compose file sets up a staging environment in our local machine.
The following is a brief overview of how the stage environment is set up:
1. First build the React frontend with vite build
.
2. Copy the /frontend/dist
folder contents into /backend/frontend_dist
3. In the backend, build the django staticfiles with the command python manage.py collecstatic
4. Run the django migration commands to set up the postgres database.
5. Use daphne to start the python web server: daphne -b 0.0.0.0 -p 8000 backend.asgi:application
The stage.env.example
contains environment variables used to configure the staging environment.
The /stage/Dockerfile
sets up the Django server. It first uses a JavaScript container to build the React frontend, then copies the files into a django container. Finally, it uses the /backend/entrypoint.sh
script to build and start the server.
Our Django web server uses Whitenoise to serve the static html, css and js files. In other words, whitenoise serves the static frontend files.
AWS staging
More information on our AWS staging files can be found in the /aws
directory, and github action files. To access this information, please ask for the required permissions from the development lead.
We plan to replace some of these files with Terraform scripts.
Useful Commands
docker compose down -v
Useful to completely remove a container and related volumes. This is helpful when fiddling with database settings, which often breaks the database. This command allows the container to be restarted fresh.
docker compose -f <filename> <docker command>
Use this to specify an alternate docker-compose file to run your commands, such as docker-compose-other.yml. This is useful if you want to test out docker for yourself.
docker compose run <container> <command>
This is useful to run one time commands inside your container. Some good commands to run are:
- python manage.py makemigrations
- python manage.py migrate
- python manage.py createsuperruser
- pip install -r requirements.txt
- npm install
docker exec -it <container> sh
Use this to do heavier debugging inside of your container. What this does is open the shell inside of the container's "mini-computer". This allows you to explore the files inside the container to see if it matches what you would expect after building is finished. This command only works when a container is running, so use docker compose run -d
to run your container in the background beforehand.
docker compose build --progress=plain
Sometimes when a build is happening, the logs are too opaque to debug if a step goes wrong. This commands makes the logs a bit more verbose so that you might have an easier time debugging.
Additional Resources
Docker Documentation
How to use Django with Daphne
Making React and Django play well together - the "hybrid app" model - Fractal Ideas
Feature: Set up stage environment in docker by LoTerence · Pull Request #613 · hackforla/CivicTechJobs · GitHub