From cfc86cb54326925eee27c71b3a8db5f90e15f9aa Mon Sep 17 00:00:00 2001 From: Carl Kittelberger Date: Fri, 13 Jul 2018 14:21:24 +0200 Subject: [PATCH] Set up demo deployment. --- .dockerignore | 39 ++++++++++++ Caddyfile.dist | 3 + Dockerfile.dist | 5 ++ Jenkinsfile | 114 ++++++++++++++++++++++++++++++++++ deployment/.gitignore | 1 + deployment/docker-compose.yml | 43 +++++++++++++ 6 files changed, 205 insertions(+) create mode 100644 .dockerignore create mode 100644 Caddyfile.dist create mode 100644 Dockerfile.dist create mode 100644 deployment/.gitignore create mode 100644 deployment/docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7606e0e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,39 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# Deployed apps should consider commenting this line out: +# see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git +node_modules + +# webpack output +dist + +# checkstyle results +checkstyle_*.xml + +### + +# only pass pre-built files to Docker +.* +* +!Caddyfile.dist +!dist/ diff --git a/Caddyfile.dist b/Caddyfile.dist new file mode 100644 index 0000000..0ab0cc6 --- /dev/null +++ b/Caddyfile.dist @@ -0,0 +1,3 @@ +http://:80 + +root /htdocs/ diff --git a/Dockerfile.dist b/Dockerfile.dist new file mode 100644 index 0000000..c916083 --- /dev/null +++ b/Dockerfile.dist @@ -0,0 +1,5 @@ +FROM icedream/caddy + +WORKDIR /data/ +COPY Caddyfile.dist /data/Caddyfile +COPY dist/ /htdocs/ diff --git a/Jenkinsfile b/Jenkinsfile index fe8866a..fd804fd 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,7 +1,37 @@ def gitBranchSlugUnderscore = env.BRANCH_NAME.replaceAll(/[^A-Za-z0-9]/, "_") def gitBranchSlugDash = env.BRANCH_NAME.replaceAll(/[^A-Za-z0-9]/, "-") +// Stack name under which to deploy the stack, must not conflict with other +// projects! +def dockerStackName = "rekttheme_${gitBranchSlugUnderscore}" + +// Hostnames +/* +| Component | master | develop | other branches | +| --- | --- | --- | --- | +| Frontend | https://rekt-theme.icedream.tech | https://develop.review.rekt-theme.icedream.tech | https://${gitBranchSlugDash}.review.rekt-theme.icedream.tech | + */ +def projectDomainBase = 'rekt-theme.icedream.tech' +def projectReviewDomainBase = "review.${projectDomainBase}" + +def projectTag = "latest" +def projectFrontendHostname = projectDomainBase +if (env.BRANCH_NAME != "master") { + projectTag = gitBranchSlugDash + projectFrontendHostname = "${gitBranchSlugDash}.${projectReviewDomainBase}" +} + +// How to access the swarm manager of the deployment infrastructure +def dockerSwarmManagerUrl = 'tcp://prod-docker-mgr.dmz.dreamnetwork.oss:2375' +def dockerSwarmManagerCredentials = 'docker_socket_project_gitea_icedream' + +// Docker image version tags +def dockerVersion = '18.03' +def dockerComposeVersion = '1.21.2' + // Fully qualified docker images to pull +def dockerImage = "docker:${dockerVersion}" +def dockerComposeImage = "docker/compose:${dockerComposeVersion}" def nodeImage = "node:8" // Abort build under some conditions @@ -37,6 +67,9 @@ pipeline { options { ansiColor('xterm') } + environment { + REKTTHEME_FRONTEND_HOSTNAME = "${projectFrontendHostname}" + } stages { stage('yarn:install') { agent { @@ -50,6 +83,7 @@ pipeline { stash name: 'node_modules', includes: 'node_modules/**' } } + stage('code-quality:lint') { parallel { stage('code-quality:lint:eslint') { @@ -86,6 +120,7 @@ pipeline { } } } + stage('code-quality:lint-publish') { agent { docker { @@ -99,6 +134,7 @@ pipeline { checkstyle canRunOnFailed: true, pattern: 'checkstyle_*.xml' } } + stage('build') { agent { docker { @@ -114,5 +150,83 @@ pipeline { } } } + + stage('build:image') { + agent { + docker { + label 'docker && linux && amd64' + image dockerImage + // pass through to host Docker instance + args '-v /var/run/docker.sock:/var/run/docker.sock' + } + } + environment { + REKTTHEME_FRONTEND_IMAGE = "${dockerProjectFrontendImageName}:${projectTag}" + } + steps { + sh """ + apk add --no-cache python3 py3-pip + pip3 install docker-compose==${dockerComposeVersion} + """ + sh """ + export PATH="$PATH:/var/tmp/deps/docker-compose" + cd deployment + docker-compose build \"frontend\" + docker-compose push \"frontend\" + """ + sh "docker inspect -f \"REKTTHEME_FRONTEND_IMAGE={{index .RepoDigests 0}}\" \"${dockerProjectFrontendImageName}:${projectTag}\" > deployment/frontend.env" + stash includes: 'deployment/frontend.env', name: 'deploymentFrontendEnv' + } + } + + stage('predeploy') { + parallel { + stage('predeploy:stack-config') { + agent { + docker { + label 'docker' + image dockerImage + } + } + steps { + sh """ + apk add --no-cache python3 py3-pip + pip3 install docker-compose==${dockerComposeVersion} + """ + unstash "deploymentFrontendEnv" + sh """ + export PATH="$PATH:/var/tmp/deps/docker-compose" + cd deployment + for envfile in ./*.env; do + source \"\${envfile}\" + done + export REKTTHEME_FRONTEND_IMAGE + docker-compose config > .docker-compose.yml + """ + archive "deployment/.docker-compose.yml" + stash includes: 'deployment/.docker-compose.yml', name: 'finalStackConfig' + } + } + } + } + + stage('deploy') { + agent { + label 'docker' + } + steps { + unstash "finalStackConfig" + script { + docker.withServer(dockerSwarmManagerUrl, dockerSwarmManagerCredentials) { + sh "docker stack rm ${dockerStackName}" + sleep 10 + sh "cd deployment && docker stack deploy -c .docker-compose.yml ${dockerStackName}" + } + currentBuild.description = """ +- Frontend web server:\thttps://${env.REKTTHEME_FRONTEND_HOSTNAME} +""" + } + } + } } } diff --git a/deployment/.gitignore b/deployment/.gitignore new file mode 100644 index 0000000..03bd412 --- /dev/null +++ b/deployment/.gitignore @@ -0,0 +1 @@ +*.env diff --git a/deployment/docker-compose.yml b/deployment/docker-compose.yml new file mode 100644 index 0000000..6b9d326 --- /dev/null +++ b/deployment/docker-compose.yml @@ -0,0 +1,43 @@ +# Docker Compose project stack +# This will be used by Jenkins to fire up the project on the server. + +version: "3.5" + +networks: + # Private network with all containers that will be published via HTTP/HTTPS + overlay_web_front: + external: true + +x-security-headers: &security_headers + traefik.frontend.headers.SSLRedirect: "true" + traefik.frontend.headers.ForceSTSHeader: "true" + traefik.frontend.headers.STSSeconds: "315360000" + traefik.frontend.headers.referrerPolicy: "same-origin" + traefik.frontend.headers.contentTypeNosniff: "true" + traefik.frontend.headers.browserXSSFilter: "true" + traefik.frontend.headers.frameDeny: "true" + #traefik.frontend.headers.contentSecurityPolicy: "default-src 'none'; base-uri 'none'; form-action 'self'; script-src 'self'; object-src 'none'; style-src 'self' 'unsafe-inline'; img-src 'self'; media-src 'none'; frame-src 'none'; frame-ancestors 'none'; font-src 'self'; connect-src 'self'" + +services: + # Single page frontend application + frontend: + image: ${REKTTHEME_FRONTEND_IMAGE:-docker.dreamnetwork.oss:5000/rekt-theme/rekt-theme-frontend} + build: + context: .. + dockerfile: Dockerfile.dist + env_file: + - frontend.env + deploy: + mode: global + placement: + constraints: + - node.labels.deployment.production == 1 + - node.platform.os == linux + - node.platform.arch == x86_64 + labels: + <<: *security_headers + traefik.port: "80" + traefik.frontend.rule: "Host:${REKTTHEME_FRONTEND_HOSTNAME:-rekt-theme.icedream.tech}" + traefik.docker.network: overlay_web_front + networks: + overlay_web_front: