Skip to content

Instantly share code, notes, and snippets.

@daliborgogic
Last active August 18, 2023 06:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save daliborgogic/f52fc76ce52c7aea8cbdd9ce99053217 to your computer and use it in GitHub Desktop.
Save daliborgogic/f52fc76ce52c7aea8cbdd9ce99053217 to your computer and use it in GitHub Desktop.
Best-Practice Docker Image and GitHub Workflow for Node.js app. [Continuous Integration/Delivery/Deployment]
ARG VERSION=12.10.0
# Development ##################################################################
FROM mhart/alpine-node:${VERSION} AS dev
WORKDIR /app
COPY package*.json .gitignore ./
ENV HOST=0.0.0.0
ENV PORT=${PORT}
RUN npm ci --prefer-offline
COPY . .
EXPOSE 3000
CMD ["node_modules/.bin/nuxt"]
# Audit ########################################################################
# The npm audit command will exit with a 0 exit code if no vulnerabilities were
# found.
FROM dev AS audit
RUN npm audit
# Lint #########################################################################
FROM audit AS lint
RUN npm run lint
# Test #########################################################################
FROM lint AS test
RUN npm test
# Build ########################################################################
FROM test AS build
RUN npm run build \
&& npm ci --prefer-offline --prod
# Release ######################################################################
FROM mhart/alpine-node:slim-${VERSION} AS release
ENV HOST=0.0.0.0
ENV PORT=${PORT}
RUN addgroup -g 1000 -S app && \
adduser -u 1000 -S app -G app && \
apk add --no-cache tini curl
USER app
COPY nuxt.config.js .
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/.nuxt ./.nuxt
COPY --from=build /app/api ./api
COPY --from=build /app/static ./static
HEALTHCHECK --interval=5s --timeout=2s --retries=12 \
CMD curl --silent --fail localhost:${PORT}/health || exit 1
ENTRYPOINT ["/sbin/tini", "--"]
EXPOSE 3000
CMD ["node_modules/.bin/nuxt-start"]
#!/bin/bash
# exit when any command fails
set -e
# Application containers should not store application data
# Check if container is running.
if docker inspect -f '{{.State.Running}}' $1; then
docker stop $1
fi
docker pull docker.pkg.github.com/$2
docker run --rm -d -p $3:$3 --name $1 docker.pkg.github.com/$2
name: DevOops Way
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v1
- name: Build Docker Image
uses: actions/docker/cli@master
with:
args: build -t docker.pkg.github.com/${{github.repository}}/cicd:latest .
- name: Dockle; Container Image Linter for Security, Helping build the Best-Practice Docker Image
uses: actions/checkout@v1
# Scan the image on host machine.
- run: |
VERSION=$(curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
grep '"tag_name":' | \
sed -E 's/.*"v([^"]+)".*/\1/' \
) && docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
-e DOCKLE_AUTH_URL=https://docker.pkg.github.com \
-e DOCKLE_USERNAME=${{github.actor}} \
-e DOCKLE_PASSWORD=${{secrets.token}} \
goodwithtech/dockle:v${VERSION} \
--exit-code 1 --exit-level fatal docker.pkg.github.com/${{github.repository}}/cicd:latest
- name: Login then Push to Docker Repository
uses: actions/docker/login@master
env:
DOCKER_USERNAME: ${{github.actor}}
DOCKER_PASSWORD: ${{secrets.token}}
DOCKER_REGISTRY_URL: docker.pkg.github.com
with:
args: push docker.pkg.github.com/${{github.repository}}/cicd:latest
- name: Pull from Docker Repository Then Run Docker Image
uses: daliborgogic/actions/ssh@master
env:
PRIVATE: ${{secrets.private_key}}
PUBLIC: ${{secrets.public_key}}
HOST: ${{secrets.host}}
USER: ${{secrets.user}}
with:
args: cd /usr/share/nginx/; . ./entrypoint.sh cicd ${{github.repository}}/cicd:latest 8080
@daliborgogic
Copy link
Author

daliborgogic commented Sep 11, 2019

According to Dockle:

$ export DOCKLE_LATEST=$(
 curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
 grep '"tag_name":' | \
 sed -E 's/.*"v([^"]+)".*/\1/' \
)
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock goodwithtech/dockle:v${DOCKLE_LATEST} <name>
PASS     - CIS-DI-0001: Create a user for the container
INFO     - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
PASS     - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
PASS     - CIS-DI-0007: Do not use update instructions alone in the Dockerfile
PASS     - CIS-DI-0008: Remove setuid and setgid permissions in the images
PASS     - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
PASS     - CIS-DI-0010: Do not store secrets in ENVIRONMENT variables
PASS     - CIS-DI-0010: Do not store secret files
PASS     - DKL-DI-0001: Avoid sudo command
PASS     - DKL-DI-0002: Avoid sensitive directory mounting
PASS     - DKL-DI-0003: Avoid apt-get/apk/dist-upgrade
PASS     - DKL-DI-0004: Use apk add with --no-cache
PASS     - DKL-DI-0005: Clear apt-get caches
PASS     - DKL-DI-0006: Avoid latest tag
PASS     - DKL-LI-0001: Avoid empty password
PASS     - DKL-LI-0002: Be unique UID
PASS     - DKL-LI-0002: Be unique GROUP

@daliborgogic
Copy link
Author

daliborgogic commented Sep 11, 2019

Healtcheck

$ docker inspect --format "{{json .State.Health }}" <name> | jsonpp
{
  "Status": "healthy",
  "FailingStreak": 0,
  "Log": [
    {
      "Start": "2019-09-11T08:37:33.9185511Z",
      "End": "2019-09-11T08:37:34.0912779Z",
      "ExitCode": 0,
      "Output": "{\"status\":200}"
    }
  ]
}

Swap jsonpp for whatever tool you use for json pretty print.

Or simple List containers > STATUS > (healthy)

$ docker ps
CONTAINER ID        IMAGE       COMMAND                  CREATED             STATUS                    PORTS                              NAMES
d13f0934ed19        app         "/sbin/tini -- node_…"   5 hours ago         Up 10 seconds (healthy)   3000/tcp, 0.0.0.0:3005->3005/tcp   app

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment