diff --git a/.dockerignore b/.dockerignore index 8bad9296..7b9644ad 100644 --- a/.dockerignore +++ b/.dockerignore @@ -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 diff --git a/README.md b/README.md index f257bdca..fb3f7c38 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/deploy/docker/Dockerfile b/deploy/docker/Dockerfile new file mode 100644 index 00000000..abb00d2f --- /dev/null +++ b/deploy/docker/Dockerfile @@ -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 diff --git a/deploy/docker/prod/Dockerfile b/deploy/docker/prod/Dockerfile deleted file mode 100644 index 37a36859..00000000 --- a/deploy/docker/prod/Dockerfile +++ /dev/null @@ -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}" diff --git a/deploy/docker/sample.nginx.conf b/deploy/docker/sample.nginx.conf new file mode 100644 index 00000000..29b32fdc --- /dev/null +++ b/deploy/docker/sample.nginx.conf @@ -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; + } +} + diff --git a/deploy/docker/tester/Dockerfile b/deploy/docker/tester/Dockerfile deleted file mode 100644 index 12fa7d86..00000000 --- a/deploy/docker/tester/Dockerfile +++ /dev/null @@ -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 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..8e09a9d9 --- /dev/null +++ b/docker-compose.yml @@ -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: diff --git a/uwsgi.ini b/uwsgi.ini new file mode 100644 index 00000000..e1b9f912 --- /dev/null +++ b/uwsgi.ini @@ -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