diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..15ad7f87 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +# items that do not need to be in a Docker image as they +# are not used by the build system +.circleci/ +.git/ +docker/Dockerfile.* +.dockerignore +.env +.gitignore +README.md +docker-* + +# items that may be built during Docker image creation +build/ +node_modules/ diff --git a/.env b/.env index 3646d14f..180489ef 100644 --- a/.env +++ b/.env @@ -1 +1,7 @@ +PUBLIC_IP_ADDRESS= +# set to development or production +NODE_ENV=development +# React requires variable name to be `PORT` in development PORT=8000 +PORT_PROD=8001 +PORT_NGINX=80 diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..180489ef --- /dev/null +++ b/.env.example @@ -0,0 +1,7 @@ +PUBLIC_IP_ADDRESS= +# set to development or production +NODE_ENV=development +# React requires variable name to be `PORT` in development +PORT=8000 +PORT_PROD=8001 +PORT_NGINX=80 diff --git a/.gitignore b/.gitignore index d9666c80..c8c50de1 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ coverage !.yarn/sdks !.yarn/versions .pnp.* +docker-dev.log diff --git a/README.md b/README.md index 92856481..e7617495 100644 --- a/README.md +++ b/README.md @@ -143,3 +143,44 @@ it also displays the user's token balance. It is included in the template alread ``` Refer to [this doc page](https://github.com/vacp2p/docs.wakuconnect.dev/blob/develop/content/docs/guides/07_reactjs_relay.md). + +## Docker + +### Development + +* Install and run [Docker](https://www.docker.com/) +* Setup environment +```bash +cp .env.example .env +``` +* Run Substrate front-end from a Docker container and follow the terminal log instructions. +```bash +./docker-dev.sh +``` + +### Production + +* Install and run [Docker](https://www.docker.com/) +* Setup environment +```bash +cp .env.example .env +``` +Note: It will fallback to automatically detecting your Public IP Address, however you may assign the value to `PUBLIC_IP_ADDRESS` in the .env file to manually override it. +* Run Substrate front-end from a Docker container and follow the terminal log instructions. +```bash +./docker-prod.sh +``` + +### Useful Docker Commands + +* List Docker containers `docker ps -a` +* List Docker images `docker images -a` +* Enter Docker container shell `docker exec -it $CONTAINER_ID /bin/sh` +* View Docker container logs `docker logs -f $CONTAINER_ID` +* Remove Docker container `docker stop $CONTAINER_ID && docker rm $CONTAINER_ID;` +* Remove Docker image `docker rmi $IMAGE_ID` + +### Useful Nginx Commands + +* Verify Nginx Config File Syntax Ok `nginx -t` +* [Reload](https://docs.nginx.com/nginx/admin-guide/basic-functionality/runtime-control/) Nginx Config File for changes to take effect `nginx -s reload` diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml new file mode 100644 index 00000000..9c1d9413 --- /dev/null +++ b/docker-compose-dev.yml @@ -0,0 +1,21 @@ +version: "3.8" +services: + dev: + container_name: ${APP_NAME}-dev + restart: always + build: + context: "." + dockerfile: ./docker/Dockerfile.dev + args: + - APP_NAME=${APP_NAME} + - PORT=${PORT} + volumes: + - ./:/app/${APP_NAME}:delegated + - ignore:/app/${APP_NAME}/node_modules + ports: + - "${PORT}:${PORT}" + env_file: + - .env + +volumes: + ignore: diff --git a/docker-compose-prod.yml b/docker-compose-prod.yml new file mode 100644 index 00000000..7ada5389 --- /dev/null +++ b/docker-compose-prod.yml @@ -0,0 +1,23 @@ +version: "3.8" +services: + prod: + container_name: ${APP_NAME}-prod + restart: always + build: + context: "." + dockerfile: ./docker/Dockerfile.prod + args: + - NODE_ENV=${NODE_ENV} + - PORT_NGINX=${PORT_NGINX} + - PORT_PROD=${PORT_PROD} + - PUBLIC_URL=${PUBLIC_URL} + volumes: + - ./:/app:delegated + - ignore:/app/node_modules + ports: + - ${PORT_PROD}:${PORT_NGINX} + env_file: + - .env + +volumes: + ignore: diff --git a/docker-dev.sh b/docker-dev.sh new file mode 100755 index 00000000..ba150ab2 --- /dev/null +++ b/docker-dev.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Copyright 2017-2022 @polkadot authors & contributors +# This software may be modified and distributed under the terms +# of the Apache-2.0 license. See the LICENSE file for details. + +trap "echo; exit" INT +trap "echo; exit" HUP + +# try to fetch public IP address if value not set in .env +PUBLIC_IP_ADDRESS_FALLBACK=$(wget http://ipecho.net/plain -O - -q ; echo) + +# assign fallback values for environment variables from .env.example incase +# not declared in .env file. alternative approach is `echo ${X:=$X_FALLBACK}` +source $(dirname "$0")/.env.example +source $(dirname "$0")/.env +${PUBLIC_IP_ADDRESS:=$PUBLIC_IP_ADDRESS_FALLBACK} +export APP_NAME=$(jq '.name' package.json | sed 's/\"//g') +export PORT +if [ "$NODE_ENV" != "development" ]; then + printf "\nError: NODE_ENV should be set to development in .env\n"; + kill "$PPID"; exit 1; +fi + +printf "\n*** Building Docker container. Please wait... \n***" +DOCKER_BUILDKIT=0 docker compose -f docker-compose-dev.yml up --build -d +if [ $? -ne 0 ]; then + kill "$PPID"; exit 1; +fi + +printf "\n*** Finished building. Please open:\n***" +printf "\n*** - http://localhost:${PORT} (local server)" +if [ "$PUBLIC_IP_ADDRESS" != "" ]; then + printf "\n*** - http://${PUBLIC_IP_ADDRESS}:${PORT} (remote server)\n***\n"; +fi diff --git a/docker-prod.sh b/docker-prod.sh new file mode 100755 index 00000000..930e5c44 --- /dev/null +++ b/docker-prod.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Copyright 2017-2022 @polkadot authors & contributors +# This software may be modified and distributed under the terms +# of the Apache-2.0 license. See the LICENSE file for details. + +trap "echo; exit" INT +trap "echo; exit" HUP + +# try to fetch public IP address if value not set in .env +PUBLIC_IP_ADDRESS_FALLBACK=$(wget http://ipecho.net/plain -O - -q ; echo) + +# assign fallback values for environment variables from .env.example incase +# not declared in .env file. alternative approach is `echo ${X:=$X_FALLBACK}` +source $(dirname "$0")/.env.example +source $(dirname "$0")/.env +export PUBLIC_IP_ADDRESS NODE_ENV PORT_NGINX PORT_PROD +export APP_NAME=$(jq '.name' package.json | sed 's/\"//g') +if [ "$NODE_ENV" != "production" ]; then + printf "\nError: NODE_ENV should be set to production in .env\n"; + kill "$PPID"; exit 1; +fi +echo ${PUBLIC_IP_ADDRESS:=$PUBLIC_IP_ADDRESS_FALLBACK} +if [ "$PUBLIC_IP_ADDRESS" = "" ]; then + printf "\nError: PUBLIC_IP_ADDRESS should be set in .env\n"; + kill "$PPID"; exit 1; +fi +export PUBLIC_URL="http://${PUBLIC_IP_ADDRESS}:${PORT_PROD}" + +printf "\n*** Building $NODE_ENV $APP_NAME. Please wait...\n***" +DOCKER_BUILDKIT=0 docker compose -f docker-compose-prod.yml up --build -d +if [ $? -ne 0 ]; then + kill "$PPID"; exit 1; +fi + +printf "\n*** Finished building ${NODE_ENV}.\n***" +printf "\n*** Please open: ${PUBLIC_URL}.\n***\n" diff --git a/docker/Dockerfile.dev b/docker/Dockerfile.dev new file mode 100644 index 00000000..221195d3 --- /dev/null +++ b/docker/Dockerfile.dev @@ -0,0 +1,25 @@ +FROM node:gallium-alpine + +ARG APP_NAME=${APP_NAME} +ARG APP_PATH=/app/${APP_NAME} +ARG PORT=${PORT} + +ENV PATH=${APP_PATH}/node_modules/.bin:$PATH + +WORKDIR ${APP_PATH} + +COPY package.json . + +RUN apk update && apk add --update git && rm -rf /var/cache/apk/* +RUN corepack enable \ + && corepack prepare yarn@stable --activate \ + && yarn set version 3.1.1 \ + && yarn plugin import interactive-tools \ + && echo -e "nodeLinker: node-modules\n\n$(cat ${APP_PATH}/.yarnrc.yml)" > ${APP_PATH}/.yarnrc.yml \ + && yarn + +COPY . . + +EXPOSE ${PORT} + +CMD ["yarn", "run", "start"] diff --git a/docker/Dockerfile.prod b/docker/Dockerfile.prod new file mode 100644 index 00000000..ec982886 --- /dev/null +++ b/docker/Dockerfile.prod @@ -0,0 +1,40 @@ +FROM node:gallium-alpine AS builder + +ARG NODE_ENV=${NODE_ENV} +ARG PORT_NGINX=${PORT_NGINX} +ARG PUBLIC_URL=${PUBLIC_URL} +ARG APP_PATH=/app +ENV PUBLIC_URL=${PUBLIC_URL} +ENV PATH=${APP_PATH}/node_modules/.bin:$PATH +WORKDIR ${APP_PATH} + +COPY package.json . + +RUN apk update && apk add --update git && rm -rf /var/cache/apk/* +RUN corepack enable \ + && corepack prepare yarn@stable --activate \ + && yarn set version 3.1.1 \ + && yarn plugin import interactive-tools \ + && echo -e "nodeLinker: node-modules\n\n$(cat ${APP_PATH}/.yarnrc.yml)" > ${APP_PATH}/.yarnrc.yml \ + && yarn + +COPY . . + +RUN NODE_ENV=${NODE_ENV} yarn build + +# =========================================================== +FROM nginx:stable-alpine + +ARG APP_PATH=/app +ARG NGINX_PATH=/usr/share/nginx/html +ARG PUBLIC_URL=${PUBLIC_URL} +ENV PUBLIC_URL=${PUBLIC_URL} +WORKDIR ${NGINX_PATH} + +COPY --from=builder ${APP_PATH}/docker/nginx.conf /etc/nginx/nginx.conf +COPY --from=builder ${APP_PATH}/build/index.html ${NGINX_PATH}/index.html +COPY --from=builder ${APP_PATH}/build ${NGINX_PATH} + +EXPOSE ${PORT_NGINX} + +CMD ["nginx", "-g", "daemon off;"] diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 00000000..30b43d38 --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Copyright 2017-2022 @polkadot/apps authors & contributors +# This software may be modified and distributed under the terms +# of the Apache-2.0 license. See the LICENSE file for details. + +# Reference: https://github.com/polkadot-js/apps/blob/master/docker/nginx.conf + +# Directives and parameters +user nginx; # Directive in the 'main' context +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + # Configuration of connection processing + worker_connections 1024; +} + +http { + # Configuration specific to HTTP and affecting all virtual servers + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + gzip on; + + # Includes server contexts for configuring each HTTP virtual server + include /etc/nginx/conf.d/*.conf; +} diff --git a/package.json b/package.json index a9985cd6..377cbd63 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "polkadot-js" ], "scripts": { - "start": "react-app-rewired start", + "start": "WATCHPACK_POLLING=true react-app-rewired start", "build": "react-app-rewired build", "test": "CI=true react-app-rewired test", "eject": "react-scripts eject",