This commit is contained in:
root 2024-02-28 12:42:50 +02:00
commit 72c4d16448
37 changed files with 1126 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# dependencies
node_modules/
.pnp
.pnp.js
# testing
coverage/
# production
build/
# misc
.DS_Store
.env*
npm-debug.log*
yarn-debug.log*
yarn-error.log*
requests.rest
.prettierrc
CSV_FILES/
log/

266
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,266 @@
---
stages:
- preperations
- test
- build
- postbuild_test
- deploy_api
- deploy_web
variables:
ANSIBLE_IMAGE_TAG: "${CI_REGISTRY_IMAGE}/ansible:latest"
API_IMAGE_TAG: "${CI_REGISTRY_IMAGE}/api:${CI_COMMIT_SHORT_SHA}"
CLIENT_IMAGE_TAG: "${CI_REGISTRY_IMAGE}/client:${CI_COMMIT_SHORT_SHA}"
SERVER_IMAGE_TAG: "${CI_REGISTRY_IMAGE}/server:${CI_COMMIT_SHORT_SHA}"
FRONTEND_PATH: ${CI_PROJECT_DIR}/client
DEPLOY_PROJECT_NAME: "selfservice"
WEB_DOMAIN: "selfservice.example.org"
TRAEFIK_VERSION: "2.11"
.ansible-template:
before_script:
- chmod 0640 ${CI_PROJECT_DIR} -R
- chmod 0600 ${PROJECT_SSH_KEY}
build-debian-ansible:
image: docker:latest
stage: preperations
when: manual
services:
- docker:dind
script:
- cd ansible/debian
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
- docker build -t ${CI_REGISTRY_IMAGE}/debian-ansible:${CI_COMMIT_SHORT_SHA} .
- docker tag ${CI_REGISTRY_IMAGE}/debian-ansible:${CI_COMMIT_SHORT_SHA} ${CI_REGISTRY_IMAGE}/debian-ansible:latest
- docker tag ${CI_REGISTRY_IMAGE}/debian-ansible:${CI_COMMIT_SHORT_SHA} ${ANSIBLE_IMAGE_TAG}
- docker push ${CI_REGISTRY_IMAGE}/debian-ansible:${CI_COMMIT_SHORT_SHA}
- docker push ${CI_REGISTRY_IMAGE}/debian-ansible:latest
- docker push ${ANSIBLE_IMAGE_TAG}
build-alma-ansible:
image: docker:latest
stage: preperations
when: manual
services:
- docker:dind
script:
- cd ansible/alma
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
- docker build -t ${CI_REGISTRY_IMAGE}/alma-ansible:${CI_COMMIT_SHORT_SHA} .
- docker tag ${CI_REGISTRY_IMAGE}/alma-ansible:${CI_COMMIT_SHORT_SHA} ${CI_REGISTRY_IMAGE}/alma-ansible:latest
- docker tag ${CI_REGISTRY_IMAGE}/alma-ansible:${CI_COMMIT_SHORT_SHA} ${ANSIBLE_IMAGE_TAG}
- docker push ${CI_REGISTRY_IMAGE}/alma-ansible:${CI_COMMIT_SHORT_SHA}
- docker push ${CI_REGISTRY_IMAGE}/alma-ansible:latest
- docker push ${ANSIBLE_IMAGE_TAG}
build-python-ansible:
image: docker:latest
stage: preperations
when: manual
services:
- docker:dind
script:
- cd ansible/python
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
- docker build -t ${CI_REGISTRY_IMAGE}/python-ansible:${CI_COMMIT_SHORT_SHA} .
- docker tag ${CI_REGISTRY_IMAGE}/python-ansible:${CI_COMMIT_SHORT_SHA} ${CI_REGISTRY_IMAGE}/python-ansible:latest
- docker tag ${CI_REGISTRY_IMAGE}/python-ansible:${CI_COMMIT_SHORT_SHA} ${ANSIBLE_IMAGE_TAG}
- docker push ${CI_REGISTRY_IMAGE}/python-ansible:${CI_COMMIT_SHORT_SHA}
- docker push ${CI_REGISTRY_IMAGE}/python-ansible:latest
- docker push ${ANSIBLE_IMAGE_TAG}
build-api:
image: docker:latest
stage: build
when: manual
services:
- docker:dind
script:
- cd api
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
- docker build -t ${API_IMAGE_TAG} .
- docker push ${API_IMAGE_TAG}
build-server:
image: docker:latest
stage: build
when: manual
services:
- docker:dind
script:
- cd server
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
- docker build -t ${SERVER_IMAGE_TAG} .
- docker push ${SERVER_IMAGE_TAG}
build-client:
image: docker:latest
stage: build
when: manual
services:
- docker:dind
script:
- cd client
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
- docker build -t ${CLIENT_IMAGE_TAG} .
- docker push ${CLIENT_IMAGE_TAG}
test-client:
image: docker:latest
stage: test
allow_failure: true
when: manual
services:
- docker:dind
script:
- cd client
- docker build -t ${CLIENT_IMAGE_TAG} -f Dockerfile.test .
test-server:
image: docker:latest
stage: test
allow_failure: true
when: manual
services:
- docker:dind
script:
- cd server
- docker build -t ${CLIENT_IMAGE_TAG} -f Dockerfile.test .
.deploy-api:
extends: .ansible-template
image: ${ANSIBLE_IMAGE_TAG}
variables:
HOSTS_LIMIT: all
stage: deploy_api
when: manual
script:
- |
ansible-playbook --limit ${HOSTS_LIMIT} --inventory inventory deploy-api.yml --private-key ${PROJECT_SSH_KEY} \
--extra-vars project_name=${DEPLOY_PROJECT_NAME}_api --extra-vars config_dir=/etc/app-${DEPLOY_PROJECT_NAME}-api \
--extra-vars registry_url=${CI_REGISTRY} --extra-vars registry_username=${CI_REGISTRY_USER} \
--extra-vars registry_password=${CI_REGISTRY_PASSWORD} --extra-vars api_image=${API_IMAGE_TAG}
deploy-api-201:
extends: .deploy-api
variables:
HOSTS_LIMIT: 172.17.200.101
deploy-api-202:
extends: .deploy-api
variables:
HOSTS_LIMIT: 172.17.200.102
deploy-api-203:
extends: .deploy-api
variables:
HOSTS_LIMIT: 172.17.200.103
deploy-dev-api:
extends: .deploy-api
variables:
HOSTS_LIMIT: 172.17.200.104
script:
- |
ansible-playbook --limit ${HOSTS_LIMIT} --inventory inventory deploy-api.yml --private-key ${PROJECT_SSH_KEY} \
--extra-vars project_name=${DEPLOY_PROJECT_NAME}_api --extra-vars config_dir=/etc/app-${DEPLOY_PROJECT_NAME}-dev-api \
--extra-vars registry_url=${CI_REGISTRY} --extra-vars registry_username=${CI_REGISTRY_USER} \
--extra-vars registry_password=${CI_REGISTRY_PASSWORD} --extra-vars api_image=${API_IMAGE_TAG}
.deploy-web:
extends: .ansible-template
image: ${ANSIBLE_IMAGE_TAG}
stage: deploy_web
when: manual
variables:
HOSTS_LIMIT: all
script:
- |
ansible-playbook --limit ${HOSTS_LIMIT} --inventory inventory deploy-web.yml --private-key ${PROJECT_SSH_KEY} \
--extra-vars project_name=${DEPLOY_PROJECT_NAME}-web --extra-vars config_dir=/etc/app-${DEPLOY_PROJECT_NAME} \
--extra-vars registry_url=${CI_REGISTRY} --extra-vars registry_username=${CI_REGISTRY_USER} \
--extra-vars registry_password=${CI_REGISTRY_PASSWORD} --extra-vars client_image=${CLIENT_IMAGE_TAG} \
--extra-vars server_image=${SERVER_IMAGE_TAG} \
--extra-vars project_domain=${WEB_DOMAIN}
deploy-web-231:
extends: .deploy-web
variables:
HOSTS_LIMIT: 172.17.200.111
deploy-web-232:
extends: .deploy-web
variables:
HOSTS_LIMIT: 172.17.200.112
deploy-web-233:
extends: .deploy-web
variables:
HOSTS_LIMIT: 172.17.200.113
deploy-dev-web:
extends: .deploy-web
variables:
HOSTS_LIMIT: 172.17.200.114
script:
- |
ansible-playbook --limit ${HOSTS_LIMIT} --inventory inventory deploy-web.yml --private-key ${PROJECT_SSH_KEY} \
--extra-vars project_name=${DEPLOY_PROJECT_NAME}-web --extra-vars config_dir=/etc/app-${DEPLOY_PROJECT_NAME}-dev \
--extra-vars registry_url=${CI_REGISTRY} --extra-vars registry_username=${CI_REGISTRY_USER} \
--extra-vars registry_password=${CI_REGISTRY_PASSWORD} --extra-vars client_image=${CLIENT_IMAGE_TAG} \
--extra-vars server_image=${SERVER_IMAGE_TAG} \
--extra-vars project_domain=$dev-{WEB_DOMAIN}
deploy-traefik:
extends: .ansible-template
image: ${ANSIBLE_IMAGE_TAG}
stage: preperations
when: manual
script:
- |
ansible-playbook --inventory inventory deploy-traefik.yml --private-key ${PROJECT_SSH_KEY} \
--extra-vars config_dir=/etc/app-traefik
deploy-docker:
extends: .ansible-template
image: ${ANSIBLE_IMAGE_TAG}
stage: preperations
when: manual
script:
- |
ansible-playbook --inventory inventory deploy-docker-ce.yml --private-key ${PROJECT_SSH_KEY}
deploy-portainer:
extends: .ansible-template
image: ${ANSIBLE_IMAGE_TAG}
stage: preperations
when: manual
script:
- |
ansible-playbook --inventory inventory deploy-portainer.yml --private-key ${PROJECT_SSH_KEY} \
--extra-vars config_dir=/etc/app-portainer
deploy-node-exporter:
extends: .ansible-template
image: ${ANSIBLE_IMAGE_TAG}
variables:
HOSTS_LIMIT: all
stage: preperations
when: manual
script:
- |
ansible-playbook --limit ${HOSTS_LIMIT} --inventory inventory deploy-node-exporter.yml --private-key ${PROJECT_SSH_KEY}

9
ansible.cfg Normal file
View File

@ -0,0 +1,9 @@
[defaults]
host_key_checking = False
#[inventory]
#enable_plugins = yaml
[ssh_connection]
pipelining = True
control_path = /tmp/ansible-ssh-%%h-%%p-%%r

39
ansible/alma/Dockerfile Normal file
View File

@ -0,0 +1,39 @@
FROM almalinux:9
ENV container=docker
# Install requirements.
RUN yum -y install rpm dnf-plugins-core \
&& yum -y config-manager --set-enabled crb \
&& yum -y update \
&& yum -y install --allowerasing \
epel-release \
sudo \
which \
hostname \
libyaml-devel \
python3 \
python3-pip \
python3-pyyaml \
rsync \
wget \
curl \
git \
ruby \
&& yum clean all
RUN python3 -m pip install --no-cache-dir --upgrade pip \
&& pip3 install --no-cache-dir ansible
RUN ansible-galaxy collection install community.general && \
ansible-galaxy collection install ansible.posix && \
ansible-galaxy collection install community.docker && \
ansible-galaxy collection list
RUN mkdir -p /root/.ssh
# Disable requiretty.
RUN sed -i -e 's/^\(Defaults\s*requiretty\)/#--- \1/' /etc/sudoers
CMD ["ansible-playbook"]

View File

@ -0,0 +1,5 @@
---
collections:
- community.general
- ansible.posix
- community.docker

19
ansible/debian/Dockerfile Normal file
View File

@ -0,0 +1,19 @@
FROM debian:bullseye
RUN apt update && \
apt install -y gnupg2 python3 python3-pip curl wget && \
apt update && \
apt install -y ansible && \
pip3 install "ansible-lint[community,yamllint]" && \
pip3 install --upgrade docker && \
pip3 install --upgrade docker-compose && \
rm -rf /var/cache/apk/*
RUN ansible-galaxy collection install \
community.docker
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT [ "/entrypoint.sh" ]
CMD ["ansible-playbook"]

View File

@ -0,0 +1,8 @@
#!/bin/sh
## Do whatever you need with env vars here ...
chmod 0600 ${PROJECT_SSH_KEY}
# Hand off to the CMD
exec "$@"

18
ansible/python/Dockerfile Normal file
View File

@ -0,0 +1,18 @@
FROM python:3.10
RUN apt-get update && \
apt-get install sshpass rsync curl wget ruby socat netcat git -y && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
RUN pip install --upgrade pip --no-cache-dir && \
pip install ansible --no-cache-dir
RUN ansible-galaxy collection install community.general && \
ansible-galaxy collection install ansible.posix && \
ansible-galaxy collection install community.docker && \
ansible-galaxy collection list
RUN mkdir -p /root/.ssh
CMD ["ansible-playbook"]

View File

@ -0,0 +1,5 @@
---
collections:
- community.general
- ansible.posix
- community.docker

25
api/Dockerfile Normal file
View File

@ -0,0 +1,25 @@
ARG NODE_IMAGE="node"
ARG NODE_VERSION="16.17-slim"
FROM ${NODE_IMAGE}:${NODE_VERSION}
ENV TZ=Asia/Jerusalem
WORKDIR /project
COPY package.json package-lock.json /project/
RUN npm ci --no-audit --no-fund
COPY --chown=node:node . /project/
RUN chown node:node /project -R && \
find /project -type d -exec chmod 0750 {} \; && \
find /project -type f -exec chmod 0650 {} \; && \
ls -la /project && \
find /project ! -path '/project/node_modules/*'
# Run as node (user id 1000)
USER node
CMD ["node", "/project/app.js"]

15
api/app.js Normal file
View File

@ -0,0 +1,15 @@
const express = require('express');
const path = require('path');
var app = express();
app.use(express.json());
require('dotenv').config();
const port = 3002;
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
})
module.exports = app;

41
client/Dockerfile Normal file
View File

@ -0,0 +1,41 @@
ARG NGINX_IMAGE="nginx"
ARG NGINX_VERSION="alpine"
ARG NODE_IMAGE="node"
ARG NODE_VERSION="16.17.1-alpine"
## Builder image (builds the static content from JS sources)
FROM ${NODE_IMAGE}:${NODE_VERSION} AS appbuild
WORKDIR /app
COPY package.json package-lock.json /app/
RUN npm ci --no-audit --no-fund \
&& ls -la /app \
&& find /app ! -path '/app/node_modules/*'
COPY . /app/
RUN npm ci \
&& npm run build \
&& ls -la /app \
&& find /app ! -path '/app/node_modules/*'
## Client web server image
FROM ${NGINX_IMAGE}:${NGINX_VERSION}
COPY --from=appbuild app/build/ /usr/share/nginx/html/
# Copy the custom Nginx configuration
COPY default.conf /etc/nginx/conf.d/default.conf
RUN ls -la /usr/share/nginx/html/ \
&& chown root:root -R /usr/share/nginx/html/ \
&& chmod 755 -R /usr/share/nginx/html/ \
&& ls -la /usr/share/nginx/html/ \
&& find /usr/share/nginx/html/

11
client/Dockerfile.test Normal file
View File

@ -0,0 +1,11 @@
ARG NODE_IMAGE="node"
ARG NODE_VERSION="16.17.1-alpine"
FROM ${NODE_IMAGE}:${NODE_VERSION} AS appbuild
WORKDIR /app
COPY . /app/
RUN npm test

8
client/default.conf Normal file
View File

@ -0,0 +1,8 @@
server {
listen 80;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
}

4
client/dockerignore Normal file
View File

@ -0,0 +1,4 @@
Dokckerfile
Dokckerfile.*
node_modules
dockerignore

68
deploy-api.yml Normal file
View File

@ -0,0 +1,68 @@
- name: Deploy Application
hosts: api
remote_user: root
pre_tasks:
- name: "Assert project_name is provided and not empty"
assert:
that:
- project_name is not undefined and project_name != ""
tasks:
# debug section
- debug: var=inventory_hostname
- name: check resolv.conf file exists
stat:
path: /etc/resolv.conf
register: resolv_conf
- name: print a debug message when resolv.config file does not exists
debug:
msg: "The resolv.conf path doesn't exist."
when: resolv_conf.stat.exists == False
- name: "read /etc/resolv.conf"
command: /bin/cat /etc/resolv.conf
register: resolv_content
- debug: msg="{{ resolv_content.stdout_lines | quote }}"
## Login
- name: Login to registry
docker_login:
registry_url: "{{ registry_url }}"
username: "{{ registry_username }}"
password: "{{ registry_password }}"
reauthorize: true
# Deploy image
- name: Create config directory
file:
path: "{{ config_dir }}" # /etc/app-x-y
state: directory
recurse: yes
- name: Create .env file
file:
state: touch
path: "{{ config_dir }}/.env"
mode: '0600'
- name: Execute docker-compose
shell: cd "{{ config_dir }}" && docker-compose down
ignore_errors: true
- name: "Upload docker-compose template" # fills the tempalte with variables from ansile
template:
src: "templates/docker-compose-api.yml"
dest: "{{ config_dir }}/docker-compose.yml"
- name: Execute docker-compose
shell: cd "{{ config_dir }}" && docker-compose up -d
- name: Create log file
file:
path: "/var/log/{{ project_name }}"
state: directory
recurse: yes

49
deploy-docker-ce.yml Normal file
View File

@ -0,0 +1,49 @@
- name: Deploy Docker ce on Debian Based Systems
hosts: all
remote_user: root
tasks:
- meta: end_play
when: ansible_facts['os_family'] != 'Debian'
- name: "install dependencies"
apt:
pkg:
- python3-pip
- apt-transport-https
- name: "Get docker signing key"
block:
- name: "Fetch docker text signing key"
get_url:
url: "https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }}/gpg"
dest: "/usr/share/keyrings/docker.asc"
register: docker_gpg_key
- name: "De-armor docker gpg key"
shell: gpg --no-tty --yes -o /etc/apt/keyrings/docker.gpg --dearmor "/usr/share/keyrings/docker.asc"
when: docker_gpg_key.changed
- name: "Add docker repo"
apt_repository:
repo: deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/{{ ansible_facts['distribution'] | lower }} {{ ansible_distribution_release }} stable
filename: docker
update_cache: yes
- name: "Install docker packages"
apt:
update_cache: yes
pkg:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
- name: "install docker-compose"
pip:
name: docker-compose
executable: pip3
- name: "Create traefik docker network"
shell: docker network create --driver=bridge --subnet=172.28.0.0/16 --ip-range=172.28.5.0/24 --gateway=172.28.5.254 traefik_http
ignore_errors: yes

55
deploy-node-exporter.yml Normal file
View File

@ -0,0 +1,55 @@
---
- hosts: all
tasks:
- name: "Ensure group \"prometheus\" exists with correct gid"
group:
name: prometheus
state: present
gid: 2998
- name: "Add the user prometheus"
user:
name: prometheus
groups: prometheus
state: present
uid: 2998
- name: "Ensure group \"node_exporter\" exists with correct gid"
group:
name: node_exporter
state: present
gid: 2997
- name: "Add the user node_exporter"
user:
name: node_exporter
groups: node_exporter
state: present
uid: 2997
- name: Creating /opt/src
shell: |
mkdir -p /opt/src
- name: "Syncing dir to servers"
synchronize:
src: node_exportoer/node_exporter-1.7.0.linux-amd64.tar.gz
dest: /opt/src/node_exporter-1.7.0.linux-amd64.tar.gz
when:
- name: "Extracting node_exporter-1.4.0"
shell: |
tar xvf /opt/src/node_exporter-1.7.0.linux-amd64.tar.gz -C /opt/src/
cd /opt/src/node_exporter-*.linux-amd64 && cp -fv node_exporter /usr/local/bin/
- name: "Syncing service file to servers"
synchronize:
src: node_exporter/node-exporter.service
dest: /etc/systemd/system/node-exporter.service
- name: "Restart and enable the service node-exporter"
systemd:
state: restarted
daemon_reload: yes
name: node-exporter
enabled: yes

41
deploy-portainer.yml Normal file
View File

@ -0,0 +1,41 @@
- name: Deploy Portainer
hosts: all
remote_user: root
pre_tasks:
- name: "Assert config_dir is provided and not empty"
assert:
that:
- config_dir is not undefined and config_dir != ""
tasks:
##
- name: "Create config directory"
file:
path: "{{ config_dir }}"
state: directory
recurse: yes
- name: Execute docker-compose
shell: cd "{{ config_dir }}" && docker-compose down
ignore_errors: true
- name: "Upload docker-compose template"
template:
src: templates/docker-compose-portainer.yml
dest: "{{ config_dir }}/docker-compose.yml"
- name: "Upload gen-admin-passwd.sh"
copy:
src: portainer/gen-admin-passwd.sh
dest: "{{ config_dir }}/gen-admin-passwd.sh"
mode: a+x
- name: "Upload reset-admin-passwd.sh"
copy:
src: portainer/reset-admin-passwd.sh
dest: "{{ config_dir }}/reset-admin-passwd.sh"
mode: a+x
- name: Execute docker-compose
shell: cd "{{ config_dir }}" && docker-compose up -d

86
deploy-traefik.yml Normal file
View File

@ -0,0 +1,86 @@
- name: Deploy Traefik Reverse Proxy
hosts: all
remote_user: root
pre_tasks:
- name: "Assert config_dir is provided and not empty"
assert:
that:
- config_dir is not undefined and config_dir != ""
tasks:
##
- name: "Create config directory"
file:
path: "{{ config_dir }}"
state: directory
recurse: yes
- name: "Create config sub-directory certs"
file:
path: "{{ config_dir }}/certs"
state: directory
recurse: yes
- name: "Create config sub-directory traefik-config"
file:
path: "{{ config_dir }}/traefik-config"
state: directory
recurse: yes
##
- name: "Upload config file: {{ config_dir }}/traefik.toml"
copy:
src: "traefik/traefik.toml"
dest: "{{ config_dir }}/traefik.toml"
- name: "Upload config file: {{ config_dir }}/traefik-config/auth-middleware.yml"
copy:
src: "traefik/auth-middleware.yml"
dest: "{{ config_dir }}/traefik-config/auth-middleware.yml"
- name: "Upload config file: {{ config_dir }}/traefik-config/certificates.yml"
copy:
src: "traefik/certificates.yml"
dest: "{{ config_dir }}/traefik-config/certificates.yml"
##
- name: "Check if \"{{ config_dir }}/certs/cert.pem\" file exists"
stat:
path: "{{ config_dir }}/certs/cert.pem"
register: certfile
- name: "copy file: \"{{ config_dir }}/certs/cert.pem\" if it doesn't exist"
copy:
src: traefik/cert.pem
dest: "{{ config_dir }}/certs/cert.pem"
when: not certfile.stat.exists
- name: "Check if \"{{ config_dir }}/certs/key.pem\" file exists"
stat:
path: "{{ config_dir }}/certs/key.pem"
register: keyfile
- name: "copy file: \"{{ config_dir }}/certs/key.pem\" if it doesn't exist"
copy:
src: traefik/key.pem
dest: "{{ config_dir }}/certs/key.pem"
when: not keyfile.stat.exists
##
- name: Execute docker-compose
shell: cd "{{ config_dir }}" && docker-compose down
ignore_errors: true
- name: "Upload docker-compose template"
template:
src: templates/docker-compose-traefik.yml
dest: "{{ config_dir }}/docker-compose.yml"
- name: "Create traefik docker network"
shell: docker network create --driver=bridge --subnet=172.28.0.0/16 --ip-range=172.28.5.0/24 --gateway=172.28.5.254 traefik_http
ignore_errors: yes
- name: Execute docker-compose
shell: cd "{{ config_dir }}" && docker-compose up -d

54
deploy-web.yml Normal file
View File

@ -0,0 +1,54 @@
- name: Deploy Application
hosts: web
remote_user: root
pre_tasks:
- name: "Assert project_name is provided and not empty"
assert:
that:
- project_name is not undefined and project_name != ""
tasks:
## Login
- name: Login to registry
docker_login:
registry_url: "{{ registry_url }}"
username: "{{ registry_username }}"
password: "{{ registry_password }}"
reauthorize: true
# Deploy image
- name: Create config directory
file:
path: "{{ config_dir }}" # /etc/app-x-y
state: directory
recurse: yes
- name: Create .env file
file:
state: touch
path: "{{ config_dir }}/.env"
mode: '0600'
- name: Create .env.local file
file:
state: touch
path: "{{ config_dir }}/.env.local"
mode: '0600'
- name: Execute docker-compose
shell: cd "{{ config_dir }}" && docker-compose down
ignore_errors: true
- name: "Upload docker-compose template" # fills the tempalte with variables from ansile
template:
src: "templates/docker-compose-web.yml"
dest: "{{ config_dir }}/docker-compose.yml"
- name: Execute docker-compose
shell: cd "{{ config_dir }}" && docker-compose up -d
- name: Create log file
file:
path: "/var/log/{{ project_name }}"
state: directory
recurse: yes

11
inventory Normal file
View File

@ -0,0 +1,11 @@
[api]
172.17.200.101 traefik_dashboard_hostname=traefik-service101.example.org api_domain=api201-service.example.org
172.17.200.102 traefik_dashboard_hostname=traefik-service102.example.org api_domain=api202-service.example.org
172.17.200.103 traefik_dashboard_hostname=traefik-service103.example.org api_domain=api203-service.example.org
172.17.200.104 traefik_dashboard_hostname=traefik-dev-service.example.org api_domain=dev-api-service.example.org
[web]
172.17.200.111 traefik_dashboard_hostname=traefik-service111.example.org web_status_id=service231.example.org web_domain=service231.example.org
172.17.200.112 traefik_dashboard_hostname=traefik-service112.example.org web_status_id=service232.example.org web_domain=service232.example.org
172.17.200.113 traefik_dashboard_hostname=traefik-service113.example.org web_status_id=service233.example.org web_domain=service233.example.org
172.17.200.114 traefik_dashboard_hostname=traefik-dev-service.example.org web_status_id=dev-service.example.org web_domain=dev-service.example.org

View File

@ -0,0 +1,13 @@
[Unit]
Description=Prometheus Node Exporter
Wants=network-online.target
After=network-online.target
[Service]
User=node_exporter
Group=node_exporter
Type=simple
ExecStart=/usr/local/bin/node_exporter
[Install]
WantedBy=multi-user.target

Binary file not shown.

View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
USERNAME="admin"
PASSWORD="$1"
if [ -z "\${PASSWORD}" ]; then
echo -e "\\nPlease call '\$0 <password>' to run this command!\\n"
exit 1
fi
which htpasswd
if [ "$?" -gt "0" ]
then
PASS=$(docker run --entrypoint htpasswd httpd:2 -bn -B ${USERNAME} ${PASSWORD} | cut -d ":" -f 2 )
else
PASS=$(htpasswd -nb -B admin ${PASSWORD} | cut -d ":" -f 2)
fi
echo "plaintext encryption: ${PASS}"
echo "docker-compse.yml escaped: ${PASS}"|sed -e "s@\\\$@\\\$\\\$@g"

View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
docker-compose down
docker run --rm -v portainer_data:/data/ portainer/helper-reset-password
docker-compose up -d

23
server/Dockerfile Normal file
View File

@ -0,0 +1,23 @@
ARG NODE_IMAGE="node"
ARG NODE_VERSION="16.17.1-slim"
FROM ${NODE_IMAGE}:${NODE_VERSION}
WORKDIR /project
COPY package.json package-lock.json /project/
RUN npm ci --no-audit --no-fund
COPY --chown=node:node . /project/
RUN chown node:node /project -R && \
find /project -type d -exec chmod 0750 {} \; && \
find /project -type f -exec chmod 0650 {} \; && \
ls -la /project && \
find /project ! -path '/project/node_modules/*'
# Run as node (user id 1000)
USER node
CMD ["node", "/project/app.js"]

10
server/Dockerfile.test Normal file
View File

@ -0,0 +1,10 @@
ARG NODE_IMAGE="node"
ARG NODE_VERSION="16.17.1-slim"
FROM ${NODE_IMAGE}:${NODE_VERSION}
WORKDIR /project
COPY . /project/
RUN npm test

23
server/app.js Normal file
View File

@ -0,0 +1,23 @@
require('dotenv').config();
const express = require('express');
const path = require('path');
const app = express();
const port = 3001;
const session = require('express-session');
app.use(session({
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.SECURE === 'true',
}
}));
app.use(express.json());
app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
module.exports = app;

4
server/dockerignore Normal file
View File

@ -0,0 +1,4 @@
Dokckerfile
Dokckerfile.*
node_modules
dockerignore

View File

@ -0,0 +1,26 @@
version: "3.9"
services:
api:
image: "{{ api_image }}"
volumes:
- /var/log/{{ project_name }}:/var/log
env_file:
- "{{ config_dir }}/.env"
environment:
PORT: 3002
expose:
- 3002
labels:
traefik.enable: true
traefik.http.routers.{{ project_name }}-client.rule: 'Host(`{{ api_domain }}`)'
traefik.http.routers.{{ project_name }}-client.entryPoints: https
traefik.http.routers.{{ project_name }}-client.tls: true
networks:
- http
restart: always
networks:
http:
external: true
name: traefik_http

View File

@ -0,0 +1,19 @@
version: "3.9"
services:
portainer:
image: portainer/portainer-ce:latest
command: [ "--admin-password=$$2y$$05$$7pyfUY3vUdgTe/CMoV.jT.t01EU71OgqjIu.jd9pQGLEVCW.GFqbO" ]
ports:
- "9000:9000"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
labels:
traefik.enable: 'false'
restart: always
volumes:
portainer_data:
driver: local
name: portainer_data

View File

@ -0,0 +1,29 @@
version: "3.9"
services:
traefik:
image: traefik:v2.11
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- "{{ config_dir }}/traefik.toml:/etc/traefik/traefik.toml:ro"
- "{{ config_dir }}/traefik-config:/etc/traefik-config:ro"
- "{{ config_dir }}/certs:/certs:ro"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
labels:
traefik.enable: 'true'
traefik.http.routers.dashboard-secure.rule: 'Host(`{{ traefik_dashboard_hostname }}`)'
traefik.http.routers.dashboard-secure.service: 'api@internal'
traefik.http.routers.dashboard-secure.middlewares: 'auth@file'
traefik.http.routers.dashboard-secure.entrypoints: 'https'
traefik.http.routers.dashboard-secure.tls: 'true'
restart: always
networks:
- http
networks:
http:
external: true
name: traefik_http

View File

@ -0,0 +1,40 @@
version: '3.9'
services:
client:
image: "{{ client_image }}"
env_file:
- "{{ config_dir }}/.env.local"
labels:
traefik.enable: true
traefik.http.routers.{{ project_name }}-client.rule: '( Host(`{{ web_domain }}`) || Host(`{{ project_domain }}`) )'
traefik.http.routers.{{ project_name }}-client.entryPoints: https
traefik.http.routers.{{ project_name }}-client.tls: true
networks:
- http
restart: always
server:
image: "{{ server_image }}"
env_file:
- "{{ config_dir }}/.env"
environment:
PORT: 3001
CHECK_STATUS_ID: "{{ web_status_id }}"
expose:
- 3001
labels:
traefik.enable: true
traefik.http.routers.{{ project_name }}-server.rule: '( Host(`{{ web_domain }}`) || Host(`{{ project_domain }}`) ) && ( PathPrefix(`/api`) || PathPrefix(`/app`) )'
traefik.http.routers.{{ project_name }}-server.entryPoints: https
traefik.http.routers.{{ project_name }}-server.tls: true
networks:
- http
restart: always
networks:
http:
external: true
name: traefik_http

View File

@ -0,0 +1,6 @@
http:
middlewares:
auth:
basicauth:
users:
- 'admin:!!!'

26
traefik/certificates.yml Normal file
View File

@ -0,0 +1,26 @@
tls:
stores:
default:
defaultCertificate:
certFile: /certs/star_example.org/fullchain.cer
keyFile: /certs/star_example.org/star_example.org.key
certificates:
- certFile: /certs/star_example.org/fullchain.cer
keyFile: /certs/star_example.org/star_example.org.key
stores:
- default
options:
default:
minVersion: VersionTLS12
alpnProtocols:
- http/1.1
- h2
preferServerCipherSuites: true
cipherSuites:
- TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
- TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
- TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
- TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
- TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256

23
traefik/traefik.toml Normal file
View File

@ -0,0 +1,23 @@
[entryPoints.http]
address = ":80"
[entryPoints.http.http.redirections.entryPoint]
to = "https"
scheme = "https"
[entryPoints.https]
address = ":443"
[http.middleware]
[api]
dashboard = true
[providers]
[providers.docker]
useBindPortIP = true
exposedByDefault = false
[providers.file]
directory = "/etc/traefik-config/"
watch = true
debugLogGeneratedTemplate = true
[accessLog]