574 lines
19 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
############################################################
# Decription: Script to manage stack docker
#
# Author: Gilles Mouchet (gilles.mouchet@gmail.com)
# Creation Date: 12-06-2025
# Version: 1.0
# Usage: ./manage.sh
# Changelog:
# V1.0 - dd-Mmm-2025 - GMo
# Added
# - Creation of script from scratch
#
############################################################
#-----------------------------------------------------------------
# Doesn't change anything from here
#-----------------------------------------------------------------
version="1.0-rc1"
prog_name="./$(/bin/basename $0)"
containers_to_start=(init minio minio-cli grafana loki promtail prometheus node-exporter proxy)
containers_all=("${containers_to_start[@]}" log-generator dns-tools)
# Couleurs
VERT="\e[32m"
ROUGE="\e[31m"
NORMAL="\e[0m"
#-----------------------------------------------------------------
# Functions
#-----------------------------------------------------------------
# display help
function print_usage {
/bin/cat << EOF
Usage: $prog_name [options]
Options:
config Create folder for persit. vol. and copy conf files
param Display all vars defined in .env file
list Display container (servie) list define in docker compose file
create Create container
start-stack Start stack '${COMPOSE_PROJECT_NAME}'
stop Stop stack '${COMPOSE_PROJECT_NAME}'
down Stop stack '${COMPOSE_PROJECT_NAME}' and delete containers
restart Restart stack '${COMPOSE_PROJECT_NAME}'
version,-v,--version Display script version
help,-h,--help Display this help
Examples:
To start stack '${COMPOSE_PROJECT_NAME}'
$prog_name start
To stop stack '${COMPOSE_PROJECT_NAME}'
$prog_name stop
To start a specific container and his depend
docker compose up <container_name> -d
Ex:
docker compose up minio-cli -d
To create a new stack
$prog_name config
$prog_name create
$prog_name start
EOF
}
#-----------------------------------------------------------------
# create folders for persistent volume and config file
function create_folder {
for folder_item in "${folder_to_create[@]}"; do
echo -n "Create folder '${folder_item}': "
sudo mkdir -p ${folder_item}
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR${NORMAL}"
exit $rc
fi
done
}
#-----------------------------------------------------------------
# remove folders for persistent volume and config file
function remove_folder {
for folder_item in "${folder_to_create[@]}"; do
# we doesn't delete the certs folder.
# this folder is maybe shared with other container
if [ "${folder_item}" != "${PRX_CERTS_DIR}" ]; then
echo -n "Remove folder '${folder_item}': "
sudo rm -rf ${folder_item}
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR${NORMAL}"
exit $rc
fi
fi
done
}
#-----------------------------------------------------------------
# copy config file
function copy_conf_file {
echo -n "Copy loki config file into '${LOKI_CONF_DIR}/': "
envsubst < ./config/loki/local-config.yaml.tmpl | \
sudo tee ${LOKI_CONF_DIR}/local-config.yaml > /dev/null
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
echo -n "Copy proxy (nginx) config file into '${PRX_NGINX_CONF_DIR}/': "
sudo cp ./config/nginx/default.conf ${PRX_NGINX_CONF_DIR}/. > /dev/null 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
echo -n "Copy the Prometheus configuration file into '${PROM_CONF_DIR}/': "
sudo cp ./config/prometheus/prometheus.yml ${PROM_CONF_DIR}/. > /dev/null 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
echo -n "Copy promtail config file into '${PROMTAIL_CONF_DIR}/': "
sudo cp ./config/promtail/config.yml ${PROMTAIL_CONF_DIR}/. > /dev/null 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
}
#-----------------------------------------------------------------
# create all containers
function create_container {
for cont_item in "${containers_all[@]}"; do
# check if container exist. If not create it
if [ $(docker ps -a | grep ${COMPOSE_PROJECT_NAME}-$cont_item | wc -l) -eq 0 ]; then
echo -n "Create container $cont_item: "
if docker compose create $cont_item > /dev/null 2>&1; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR${NORMAL}"
fi
# else
# echo "Container $cont_item already exist"
fi
done
}
#-----------------------------------------------------------------
# start container
function start_container {
for cont_item in "${containers_to_start[@]}"; do
echo -n "Start container $cont_item: "
if docker compose up -d $cont_item > /dev/null 2>&1; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR${NORMAL}"
fi
done
}
#-----------------------------------------------------------------
# create grafana ds
# $1 = ds name
# $2 = url
function add_gf_data_source {
curl -k -X POST "${GF_ROOT_URL}/api/datasources" \
-u "${GF_ADMIN_USER}:${GF_ADMIN_PASS}" \
-H "Content-Type: application/json" \
-d '{
"name": "'"${1^}"'",
"type": "'"$1"'",
"access": "proxy",
"url": "'"$2"'",
"basicAuth": false,
"isDefault": false,
"jsonData": {
"maxLines": 1000
}
}' > /dev/null 2>&1
}
#-----------------------------------------------------------------
# import grafana dashboard
# $1 = dashboard ID
# $2 = source
# $3 = dashboard title. Needed for check if already impoerted
function import_gf_dashboard {
db_file=/tmp/dashboard-$1.json
wr_file=/tmp/wrapped-dashboard.json
#download json
curl -s -k -o "$db_file" "https://grafana.com/api/dashboards/$1/revisions/latest/download"
if [ ! -s "$db_file" ]; then
exit 1
fi
# stage 2 - clean up and add datadource
sed -i 's/"id":[ ]*[0-9]\+/"id": null/g' "$db_file"
sed -i 's/"uid":[ ]*"[^"]*"/"uid": null/g' "$db_file"
sed -i 's/"datasource": ".*"/"datasource": "'"${2^}"'"/g' "$db_file"
# stage 3 - creating the final JSON file expected by the Grafana API
echo '{' > "$wr_file"
echo '"dashboard":' >> "$wr_file"
cat "$db_file" >> "$wr_file"
echo ',' >> "$wr_file"
echo '"overwrite": true' >> "$wr_file"
echo '}' >> "$wr_file"
# stage 4 - import dashboard into grafana
curl -s -k -X POST "$GF_ROOT_URL/api/dashboards/db" \
-u "$GF_ADMIN_USER:$GF_ADMIN_PASS" \
-H "Content-Type: application/json" \
--data-binary "@$wr_file" > /dev/null 2>&1
#rm -f $db_file $wr_file
}
#-----------------------------------------------------------------
# MAIN
#-----------------------------------------------------------------
# read .env if exist
if [ ! -f .env ]; then
echo "file '.env' doesn't exist!"
echo "See README.md"
exit
fi
set -a
source .env
set +a
# create array folder to create
folder_to_create=(${PRX_CERTS_DIR}
${PRX_NGINX_CONF_DIR}
${MINIO_DATA_ROOT_DIR}
${MINIO_CONF_CLI_DIR}
${GF_DATA_DIR}
${PROM_CONF_DIR}
${PROM_DATA_DIR}
${LOKI_CONF_DIR}
${LOKI_DATA_DIR}
${LOKI_GEN_LOG_DIR}
${PROMTAIL_CONF_DIR})
case "$1" in
param)
cat << EOF
COMPOSE_PROJECT_NAME = ${COMPOSE_PROJECT_NAME}
PRX_CERTS_DIR = ${PRX_CERTS_DIR}
PRX_NGINX_CONF_DIR = ${PRX_NGINX_CONF_DIR}
MINIO_DATA_ROOT_DIR = ${MINIO_DATA_ROOT_DIR}
MINIO_CONF_CLI_DIR = ${MINIO_CONF_CLI_DIR}
MINIO_ACCESS_KEY = ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY = ${MINIO_SECRET_KEY}
MINIO_BUCKET_NAME = ${MINIO_BUCKET_NAME}
MINIO_BUCKET_USER = ${MINIO_BUCKET_USER}
MINIO_BUCKET_PASS = ${MINIO_BUCKET_PASS}
MINIO_BUCKET_POL_NAME = ${MINIO_BUCKET_POL_NAME}
MINIO_REDIRECT_URL = ${MINIO_REDIRECT_URL}
MINIO_ALIAS = ${MINIO_ALIAS}
GF_ADMIN_USER = ${GF_ADMIN_USER}
GF_ADMIN_PASS = ${GF_ADMIN_PASS}
GF_DATA_DIR = ${GF_DATA_DIR}
GF_ROOT_URL = ${GF_ROOT_URL}
PROM_CONF_DIR = ${PROM_CONF_DIR}
PROM_DATA_DIR = ${PROM_DATA_DIR}
LOKI_CONF_DIR = ${LOKI_CONF_DIR}
LOKI_DATA_DIR = ${LOKI_DATA_DIR}
LOKI_GEN_LOG_DIR = ${LOKI_GEN_LOG_DIR}
PROMTAIL_CONF_DIR = ${PROMTAIL_CONF_DIR}
EOF
;;
start-stack)
echo -e "---- Create Folders ----"
create_folder
echo -e "\n---- Copy config file ----"
copy_conf_file
echo -e "\n---- Config minio ----"
#-----
echo -n "Start minio container: "
if docker compose up -d minio > /dev/null 2>&1; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR${NORMAL}"
fi
#----
echo -n "Wait until minio container is started: "
while ! curl -s "http://$HOSTNAME:9000/minio/health/live" >/dev/null; do
sleep 1
done
echo -e "${VERT}STARTED${NORMAL}"
#-----
echo -n "Start minio-cli container: "
docker compose up -d minio-cli > /dev/null 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
#-----
echo -n "Create minios alias: "
docker exec ${COMPOSE_PROJECT_NAME}-minio-cli-1 mc alias set ${MINIO_ALIAS} http://minio:9000 \
${MINIO_ACCESS_KEY} ${MINIO_SECRET_KEY} > /dev/null 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
#-----
echo -n "Copy mino policy config file into ${MINIO_CONF_CLI_DIR}/: "
envsubst < ./config/minio/policy.json.tmpl |
sudo tee ${MINIO_CONF_CLI_DIR}/.mc/policy.json > /dev/null
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
#-----
echo -n "Create user for bucket ${MINIO_BUCKET_NAME}: "
docker exec ${COMPOSE_PROJECT_NAME}-minio-cli-1 mc admin user add \
${MINIO_ALIAS} ${MINIO_BUCKET_USER} ${MINIO_BUCKET_PASS} >/dev/null 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
#-----
echo -n "Create policy for access to bucket ${MINIO_BUCKET_NAME}: "
docker exec ${COMPOSE_PROJECT_NAME}-minio-cli-1 mc admin policy create \
${MINIO_ALIAS} ${MINIO_BUCKET_POL_NAME} /root/.mc/policy.json >/dev/null 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
#-----
echo -n "Attach policy to user ${MINIO_BUCKET_USER}: "
docker exec ${COMPOSE_PROJECT_NAME}-minio-cli-1 mc admin policy attach \
${MINIO_ALIAS} ${MINIO_BUCKET_POL_NAME} --user=${MINIO_BUCKET_USER} >/dev/null 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc)${NORMAL}"
exit $rc
fi
#-----
echo -n "Create bucket if not exist: "
docker exec ${COMPOSE_PROJECT_NAME}-minio-cli-1 mc ls "${MINIO_ALIAS}/$MINIO_BUCKET_NAME" >/dev/null 2>&1
rc=$?
if [ $rc -ne 0 ]; then
docker exec -it ${COMPOSE_PROJECT_NAME}-minio-cli-1 /bin/sh -c \
"mc mb ${MINIO_ALIAS}/${MINIO_BUCKET_NAME}" >/dev/null 2>&1
rc1=$?
if [ $rc1 -eq 0 ]; then
echo -e "${VERT}SUCCESSFULLY CREATED${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc1)${NORMAL}"
exit $rc1
fi
else
echo -e "${VERT}ALREADY EXISTING${NORMAL}"
fi
# ----
echo -n "Set token minio metrics: "
token=$(docker exec vdg-loki-minio-cli-1 mc admin prometheus generate myminio|grep bearer_token| sed 's/^[ \t]*//')
#token=$(docker exec -it vdg-loki-minio-cli-1 /bin/sh -c "mc admin prometheus generate myminio" | grep bearer_token)
rc1=$?
if [ $rc1 -eq 0 ]; then
echo -e "${VERT}SUCCESSFULLY CREATED${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc1)${NORMAL}"
exit $rc1
fi
# ----
echo -n "Set token in prometheus config: "
sudo sed -i "s|bearer_token:\(.*\)|$token|" ${PROM_CONF_DIR}/prometheus.yml
rc1=$?
if [ $rc1 -eq 0 ]; then
echo -e "${VERT}SUCCESS${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc1)${NORMAL}"
exit $rc1
fi
# ----
echo -e "\n---- Create container ----"
create_container
echo -e "\n---- Start container ----"
start_container
echo -e "\n---- Create Grafana DS ----"
# ----
# wait until grafan is up
echo -n "Wait until grafana is starting: "
timeout=60 # Max time to wait in seconds
interval=2 # Wait time between checks
elapsed=0
while true; do
status_code=$(curl -k -s -o /dev/null -w "%{http_code}" \
-u "$GF_ADMIN_USER:$GF_ADMIN_PASS" \
"$GF_ROOT_URL/api/health")
if [ "$status_code" -eq 200 ]; then
echo -e "${VERT}STARTED${NORMAL}"
break
fi
if [ "$elapsed" -ge "$timeout" ]; then
echo -e "${ROUGE}ERROR ($status_code)${NORMAL}"
exit 1
fi
sleep "$interval"
elapsed=$((elapsed + interval))
done
# ----
echo -n "Create Loki DS: "
add_gf_data_source loki http://loki:3100
rc1=$?
if [ $rc1 -eq 0 ]; then
echo -e "${VERT}SUCCESSFULLY CREATED${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc1)${NORMAL}"
exit $rc1
fi
# ----
echo -n "Create Prometheus DS: "
add_gf_data_source prometheus http://prometheus:9090 ""
rc1=$?
if [ $rc1 -eq 0 ]; then
echo -e "${VERT}SUCCESSFULLY CREATED${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc1)${NORMAL}"
exit $rc1
fi
echo -e "\n---- Install Grafana dashboard ----"
# ----
db_name="Node Exporter Full"
echo -n "Import '$db_name' dashboard: "
# check if already imported
response=$(curl -k -s -u "$GF_ADMIN_USER:$GF_ADMIN_PASS" "$GF_ROOT_URL/api/search?query=")
if echo "$response" | grep -iq "\"title\":\"$db_name\""; then
echo -e "${VERT}ALREADY IMPORTED${NORMAL}"
else
import_gf_dashboard 1860 prometheus
rc1=$?
if [ $rc1 -eq 0 ]; then
echo -e "${VERT}SUCCESSFULLY IMPORTED${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc1)${NORMAL}"
exit $rc1
fi
fi
# ----
db_name="Logs / App"
echo -n "Import '$db_name' dashboard: "
# check if already imported
response=$(curl -k -s -u "$GF_ADMIN_USER:$GF_ADMIN_PASS" "$GF_ROOT_URL/api/search?query=")
if echo "$response" | grep -iq "\"title\":\"$db_name\""; then
echo -e "${VERT}ALREADY IMPORTED${NORMAL}"
else
import_gf_dashboard 13639 loki
rc1=$?
if [ $rc1 -eq 0 ]; then
echo -e "${VERT}SUCCESSFULLY IMPORTED${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc1)${NORMAL}"
exit $rc1
fi
fi
# ----
#db_name="Prometheus 2.0 Overview"
db_name="Prometheus All Metrics"
echo -n "Import '$db_name' dashboard: "
# check if already imported
response=$(curl -k -s -u "$GF_ADMIN_USER:$GF_ADMIN_PASS" "$GF_ROOT_URL/api/search?query=")
if echo "$response" | grep -iq "\"title\":\"$db_name\""; then
echo -e "${VERT}ALREADY IMPORTED${NORMAL}"
else
#import_gf_dashboard 3662 prometheus
import_gf_dashboard 19268 prometheus
rc1=$?
if [ $rc1 -eq 0 ]; then
echo -e "${VERT}SUCCESSFULLY IMPORTED${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc1)${NORMAL}"
exit $rc1
fi
fi
# ----
db_name="MinIO Dashboard"
echo -n "Import '$db_name' dashboard: "
# check if already imported
response=$(curl -k -s -u "$GF_ADMIN_USER:$GF_ADMIN_PASS" "$GF_ROOT_URL/api/search?query=")
if echo "$response" | grep -iq "\"title\":\"$db_name\""; then
echo -e "${VERT}ALREADY IMPORTED${NORMAL}"
else
import_gf_dashboard 13502 prometheus
rc1=$?
if [ $rc1 -eq 0 ]; then
echo -e "${VERT}SUCCESSFULLY IMPORTED${NORMAL}"
else
echo -e "${ROUGE}ERROR ($rc1)${NORMAL}"
exit $rc1
fi
fi
;;
stop-stack)
docker compose stop
;;
down-down)
docker compose down
;;
del-stack)
# confirm installation
while true; do
read -p "Are you sure you want to permanently delete the stack '${COMPOSE_PROJECT_NAME}'? [y/n] ? " yn
case $yn in
[Yy]* ) break;;
* ) exit; #echo "Please answer yes or no.";;
esac
done
echo -e "\n---- Stop and remove all container ----"
docker compose down
echo -e "\n---- Delete all folder ----"
remove_folder
;;
list)
echo "-------------------------------------"
echo " Container list"
echo "-------------------------------------"
docker compose config --services | sort
#for cont_item in "${containers_all[@]}"; do
# echo "- $cont_item"
#done
;;
-h|--help|help)
print_usage
exit 0
;;
-v|--version|version)
cat << EOF
$(basename "$0") v$version (c) 1990 - $(date +%Y) by Gilles Mouchet
Non-Commercial Use License See LICENSE for details
EOF
exit 0
;;
*)
print_usage
exit 0
;;
esac