Single Dockerfile for building ATAT.

Dockerfile is now a single multi-stage build that relies on a Python 3.7
base image.

Notes:
- This builds uWSGI with a `pip install` because the Alpine vendored
  uWSGI is built against Python 3.6.
- Adds a docker-compose file that can be used for testing that the build
  works. It is not usable for development purposes because it creates a
  static copy of the application.
This commit is contained in:
dandds 2019-06-14 13:55:22 -04:00
parent 1c63a64bb8
commit 6f8ef27bf1
8 changed files with 176 additions and 91 deletions

View File

@ -1,9 +1,11 @@
# Files to exclude from COPY and ADD commands when
# Files to exclude from COPY and ADD commands when
# building a docker image from this directory
# Exclude Docker build related files
Dockerfile
Dockerfile.nginx
.dockerignore
docker-compose.yml
# Exclude the git directory and gitignore file
.git
@ -27,3 +29,7 @@ requirements.yml
# Skip kubernetes and Docker config stuff
deploy
# exclude build artifacts and docker directories
.venv
node_modules

View File

@ -231,6 +231,20 @@ SVG markup should be cleaned an minified, [Svgsus](http://www.svgs.us/) works we
## Deployment
### Docker build
For testing the Docker build, the repo includes a `docker-compose.yml` that will run the app container with an NGINX server in front of it. To run it, you will need `docker` and `docker-compose` installed, then:
```
docker-compose up
```
The app will be available on http://localhost:8080.
The build assumes that you have redis and postgres running on their usual ports on your host machine; it does not pull images for those services. The docker-compose build is not suitable for development because it does not mount or reload working files.
### Dev login
The `/login-dev` endpoint is protected by HTTP basic auth when deployed. This can be configured for NGINX following the instructions [here](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/). The following config should added within the main server block for the site:
```nginx

95
deploy/docker/Dockerfile Normal file
View File

@ -0,0 +1,95 @@
FROM python:3.7.3-alpine3.9 AS builder
RUN mkdir -p /install/.venv
WORKDIR /install
# Install basic Alpine packages
RUN apk update && \
apk --no-cache add \
build-base \
curl \
ca-certificates \
docker \
git \
gzip \
libffi \
libffi-dev \
libsass \
libsass-dev \
linux-headers \
nodejs \
openssh-client \
openssl \
openssl-dev \
pcre-dev \
postgresql-dev \
rsync \
sudo \
tar \
util-linux \
yarn
COPY . .
# Install app dependencies
RUN pip install pipenv uwsgi && \
PIPENV_VENV_IN_PROJECT=1 pipenv install && \
yarn install && \
yarn build
## NEW IMAGE
FROM python:3.7.3-alpine3.9
### Very low chance of changing
###############################
# Overridable default config
ARG APP_DIR=/opt/atat/atst
# Environment variables
ENV APP_DIR "${APP_DIR}"
ENV TZ UTC
# Create application directory
RUN set -x ; \
mkdir -p ${APP_DIR}
# Set working dir
WORKDIR ${APP_DIR}
# Add group
RUN addgroup -g 8000 -S "atat" && \
adduser -u 8010 -D -S -G "atat" "atst"
# Install basic Alpine packages
RUN apk update && \
apk --no-cache add \
dumb-init \
postgresql-client \
postgresql-dev \
postgresql-libs \
uwsgi-logfile
COPY --from=builder /install/.venv/ ./.venv/
COPY --from=builder /install/alembic/ ./alembic/
COPY --from=builder /install/alembic.ini .
COPY --from=builder /install/app.py .
COPY --from=builder /install/atst/ ./atst/
COPY --from=builder /install/config/ ./config/
COPY --from=builder /install/templates/ ./templates/
COPY --from=builder /install/translations.yaml .
COPY --from=builder /install/static/ ./static/
COPY --from=builder /install/uwsgi.ini .
COPY --from=builder /usr/local/bin/uwsgi /usr/local/bin/uwsgi
# Use dumb-init for proper signal handling
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# Default command is to launch the server
CMD ["uwsgi", "--ini", "uwsgi.ini"]
RUN mkdir /var/run/uwsgi && \
chown -R atst:atat /var/run/uwsgi && \
chown -R atst:atat "${APP_DIR}"
# Run as the unprivileged APP user
USER atst

View File

@ -1,49 +0,0 @@
FROM alpine:3.8
### Very low chance of changing
###############################
# Overridable default config
ARG APP_USER=atst
ARG APP_GROUP=atat
ARG APP_DIR=/opt/atat/atst
ARG APP_PORT=8000
ARG LOCAL_BIN_DIR=/usr/bin
ARG SITE_PACKAGES_DIR=/usr/lib/python3.6/site-packages
ENV APP_USER "${APP_USER}"
ENV APP_GROUP "${APP_GROUP}"
ENV APP_DIR "${APP_DIR}"
# Set port to open
EXPOSE "${APP_PORT}"
# Use dumb-init for proper signal handling
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# Default command is to launch the server
CMD ["bash", "-c", "${APP_DIR}/script/uwsgi_server"]
### Items that will change almost every build
#############################################
# Copy installed python packages from the tester image
COPY --from=atst-tester:latest "${SITE_PACKAGES_DIR}" "${SITE_PACKAGES_DIR}"
# Copy local bin directory (contains python system package wrappers)
COPY --from=atst-tester:latest "${LOCAL_BIN_DIR}" "${LOCAL_BIN_DIR}"
# Copy the app directory contents from the tester image (includes node modules)
COPY --from=atst-tester:latest "${APP_DIR}" "${APP_DIR}"
# Set working dir
WORKDIR ${APP_DIR}
# Add required system packages and app user
RUN set -x ; \
script/alpine_setup "${APP_USER}" "${APP_GROUP}"
# Update file ownership
RUN set -x ; \
for subdir in $(find . -type d -maxdepth 1 | grep -Ee '.[^/]' | grep -Fve 'node_modules'); do chown atst:atat -R ${subdir}; done
# Run as the unprivileged APP user
USER "${APP_USER}"

View File

@ -0,0 +1,24 @@
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
try_files $uri @app;
}
location @app {
include uwsgi_params;
uwsgi_pass unix:///var/run/uwsgi/uwsgi.socket;
uwsgi_param HTTP_X_SSL_CLIENT_VERIFY $ssl_client_verify;
uwsgi_param HTTP_X_SSL_CLIENT_CERT $ssl_client_raw_cert;
uwsgi_param HTTP_X_SSL_CLIENT_S_DN $ssl_client_s_dn;
uwsgi_param HTTP_X_SSL_CLIENT_S_DN_LEGACY $ssl_client_s_dn_legacy;
uwsgi_param HTTP_X_SSL_CLIENT_I_DN $ssl_client_i_dn;
uwsgi_param HTTP_X_SSL_CLIENT_I_DN_LEGACY $ssl_client_i_dn_legacy;
uwsgi_param HTTP_X_REQUEST_ID $request_id;
}
}

View File

@ -1,41 +0,0 @@
FROM registry.atat.code.mil:443/atat-app-builder:latest
### Very low chance of changing
###############################
ARG APP_USER=atst
ARG APP_GROUP=atat
ARG APP_DIR=/opt/atat/atst
ARG CIBUILD=true
ENV APP_DIR "${APP_DIR}"
ENV FLASK_ENV ci
ENV SKIP_PIPENV true
# Use dumb-init for proper signal handling
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# Default command is to run all the tests
CMD ["bash", "-c", "${APP_DIR}/script/cibuild"]
# Create application directory
RUN set -x ; \
mkdir -p ${APP_DIR}
# Set working dir
WORKDIR ${APP_DIR}
# Copy over setup scripts
COPY script/ ./script/
# Add required system packages and app user
RUN set -x ; \
script/alpine_setup
### Items that will change almost every build
#############################################
# Copy over the rest of the app source
COPY . .
# Install app dependencies
RUN set -x ; \
script/setup

27
docker-compose.yml Normal file
View File

@ -0,0 +1,27 @@
version: '3.7'
services:
backend:
build:
context: .
dockerfile: ./deploy/docker/Dockerfile
volumes:
- ./ssl:/opt/atat/atst/ssl
- ./config:/opt/atst/atst/config
- sockets:/var/run/uwsgi
environment:
PGHOST: host.docker.internal
REDIS_URI: "redis://host.docker.internal:6379"
frontend:
image: nginx:1.13-alpine
volumes:
- ./deploy/docker/sample.nginx.conf:/etc/nginx/conf.d/default.conf:ro
- sockets:/var/run/uwsgi
depends_on:
- backend
ports:
- 8080:80
volumes:
sockets:

9
uwsgi.ini Normal file
View File

@ -0,0 +1,9 @@
[uwsgi]
callable = app
module = app
socket = /var/run/uwsgi/uwsgi.socket
plugin = python3
plugin = logfile
virtualenv = /opt/atat/atst/.venv
chmod-socket = 666
chown-socket = atst:atat