Apps: "Bash", "Docker" & etc.
В этой заметке описан один из этапов реализации поставленной в вышестоящей публикации задачи автоматизации процедур развёртывания тестовых стендов из docker-контейнеров.
Библиотека функций управления фронтальным web-прокси:
# vi /usr/local/etc/devops/lib/front.sh.snippet
#!/bin/bash
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
# Функция подготовки конфигурации контейнера фронтального web-прокси
function front-preset {
# Создаём директорию для журналов событий
mkdir -p /var/log/devops/front
# Зачищаем конфигурацию и данные
rm -rf /var/opt/devops/front
# Профилактически создаём директории, используемые в конфигурации
mkdir -p /etc/ssl/nginx
mkdir -p /etc/letsencrypt
# Создаём директории конфигурационных файлов
mkdir -p /var/opt/devops/front/etc/nginx
mkdir -p /var/opt/devops/front/etc/nginx/bunch.d
# Копируем базовую конфигурацию "Nginx" во временные директории монтирования и явно задаём ограничения прав доступа к файлам
cp -a /usr/local/etc/devops/conf/front/etc/nginx /var/opt/devops/front/etc
chown -R 33:33 /var/opt/devops/front/etc/nginx
chmod -R o-rw /var/opt/devops/front/etc/nginx
# Создаём директории данных
mkdir -p /var/opt/devops/front/var/www
# Создаём директорию для публичных журналов событий процедур запуска и остановки контейнеров
mkdir -p /var/opt/devops/front/var/www/log
# Разрешаем чтение и запись в директории данных специфичному пользователю "www-data"
setfacl --recursive --modify user:www-data:rwX /var/opt/devops/front/var/www
setfacl --recursive --modify default:user:www-data:rwX /var/opt/devops/front/var/www
return ${?}
}
# Функция запуска контейнера фронтального web-прокси
function front-nginx-start {
# Запускаем контейнер в зависимости от указанного способа приёма трафика
if [ "${FRONTINMODE}" = "dedicated" ] ; then
# Запускаем контейнер с приёмом трафика через выделенный внешний виртуальный IP-адрес
docker run --rm --name ${FRONTNAME} \
--env TZ="`cat /etc/timezone`" \
-v /etc/ssl/nginx:/etc/ssl/nginx:ro \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
-v /var/opt/devops/front/etc/nginx:/etc/nginx:ro \
-v /var/opt/devops/front/var/www:/var/www:ro \
--tmpfs /run --tmpfs /tmp \
--network frontnet \
--detach \
selfmade:nginx-1.14-auth-pam > /dev/null
else
# Запускаем контейнер с приёмом трафика путём проброса его с несущей операционной системы
docker run --rm --name ${FRONTNAME} \
--env TZ="`cat /etc/timezone`" \
-v /etc/ssl/nginx:/etc/ssl/nginx:ro \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
-v /var/opt/devops/front/etc/nginx:/etc/nginx:ro \
-v /var/opt/devops/front/var/www:/var/www:ro \
--tmpfs /run --tmpfs /tmp \
--publish 80:80 --publish 443:443 \
--detach \
selfmade:nginx-1.14-auth-pam > /dev/null
fi
# Направляем вывод подсистемы журналирования контейнера в текстовый файл
docker logs --follow ${FRONTNAME} > /var/log/devops/front/output-nginx.log 2>&1 &
# Проверяем успешность выполнения процедуры запуска
if [ `docker ps | grep -c -i "${FRONTNAME}"` -ne 0 ] ; then
echo "$(cdate): Контейнер \"${FRONTNAME}\" успешно запущен." | tee -a "${LOG}"
# Подключаем запущенный контейнер к транзитной связующей виртуальной сети
docker network connect vianet ${FRONTNAME}
if [ "${?}" -eq "0" ] ; then
echo "$(cdate): Контейнер \"${FRONTNAME}\" успешно включён в сеть \"vianet\"." | tee -a "${LOG}"
else
FORTH=false
echo "$(cdate): Сбой при включении контейнера \"${FRONTNAME}\" в сеть \"vianet\"." | tee -a "${LOG}"
fi
else
FORTH=false
echo "$(cdate): Сбой при запуске контейнера \"${FRONTNAME}\"." | tee -a "${LOG}"
fi
return ${?}
}
# Функция остановки контейнера фронтального web-прокси
function front-nginx-stop {
# Пробуем остановить контейнер
docker stop ${FRONTNAME} > /dev/null 2>&1
if [ "$(docker ps | grep -c -i ${FRONTNAME})" -eq "0" ] ; then
echo "$(cdate): Контейнер \"${FRONTNAME}\" успешно остановлен." | tee -a "${LOG}"
else
FORTH=false
echo "$(cdate): Сбой при остановке контейнера \"${FRONTNAME}\"." | tee -a "${LOG}"
fi
return ${?}
}
# Функция предоставления доступа к web-интерфейсу контейнера фронтального web-прокси
# (функциональность PAM + "Nginx HTTP Authentication" требует наличия аккаунтов соответствующих пользователей внутри контейнера)
function front-web-view-enable {
# Формируем и перебираем список локальных пользователей ОС, входящих в группу "developer"
MEMBERS=( $(members developer 2>/dev/null) )
for MEMBER in "${MEMBERS[@]}" ; do
# Выявляем пользователей с паролями и работает только с ними
MPASSWD=$(cat /etc/shadow | grep -i "^${MEMBER}:" | awk -F ':' '{print $2}')
[[ -z "${MPASSWD}" || $(echo "${MPASSWD}" | wc -c) -le "12" ]] && { continue; }
# Добавляем нового (игнорируя наличие такового, если он уже создан) пользователя (с предварительным профилактическим созданием группы)
docker exec -i ${FRONTNAME} /bin/bash -c "groupadd --force --system developer && useradd --no-create-home --home-dir /nonexistent --shell /bin/false --gid developer ${MEMBER} 2>/dev/null" 2>/dev/null
if [[ "${?}" -eq "0" || "${?}" -eq "9" ]] ; then
# Обновляем пароль у имеющегося или задаём созданному пользователю
docker exec -i ${FRONTNAME} /bin/bash -c "usermod -p '${MPASSWD}' ${MEMBER}"
# Проверяем успешность обновления пароля
if [ "${?}" -eq "0" ] ; then
echo "$(cdate): Пользователю \"${MEMBER}\" успешно предоставлен доступ к просмотру журналов событий запуска и остановки тестовых стендов." | tee -a "${LOG}"
else
FORTH=false
echo "$(cdate): Сбой предоставления пользователю \"${MEMBER}\" доступа к просмотру журналов событий запуска и остановки тестовых стендов." | tee -a "${LOG}"
fi
else
FORTH=false
echo "$(cdate): Невозможно предоставить пользователю \"${MEMBER}\" доступ к просмотру журналов событий из-за сбоя при подключении к фронтальному web-прокси." | tee -a "${LOG}"
fi
done
unset MEMBERS; unset MEMBER
return ${?}
}
# This file contains the code snippet for the shell Bash v.4 (Bourne again shell).
# Файл содержит фрагмент кода для командного интерпретатора Bash v.4 (Bourne again shell).
# Функция подготовки конфигурации контейнера фронтального web-прокси
function front-preset {
# Создаём директорию для журналов событий
mkdir -p /var/log/devops/front
# Зачищаем конфигурацию и данные
rm -rf /var/opt/devops/front
# Профилактически создаём директории, используемые в конфигурации
mkdir -p /etc/ssl/nginx
mkdir -p /etc/letsencrypt
# Создаём директории конфигурационных файлов
mkdir -p /var/opt/devops/front/etc/nginx
mkdir -p /var/opt/devops/front/etc/nginx/bunch.d
# Копируем базовую конфигурацию "Nginx" во временные директории монтирования и явно задаём ограничения прав доступа к файлам
cp -a /usr/local/etc/devops/conf/front/etc/nginx /var/opt/devops/front/etc
chown -R 33:33 /var/opt/devops/front/etc/nginx
chmod -R o-rw /var/opt/devops/front/etc/nginx
# Создаём директории данных
mkdir -p /var/opt/devops/front/var/www
# Создаём директорию для публичных журналов событий процедур запуска и остановки контейнеров
mkdir -p /var/opt/devops/front/var/www/log
# Разрешаем чтение и запись в директории данных специфичному пользователю "www-data"
setfacl --recursive --modify user:www-data:rwX /var/opt/devops/front/var/www
setfacl --recursive --modify default:user:www-data:rwX /var/opt/devops/front/var/www
return ${?}
}
# Функция запуска контейнера фронтального web-прокси
function front-nginx-start {
# Запускаем контейнер в зависимости от указанного способа приёма трафика
if [ "${FRONTINMODE}" = "dedicated" ] ; then
# Запускаем контейнер с приёмом трафика через выделенный внешний виртуальный IP-адрес
docker run --rm --name ${FRONTNAME} \
--env TZ="`cat /etc/timezone`" \
-v /etc/ssl/nginx:/etc/ssl/nginx:ro \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
-v /var/opt/devops/front/etc/nginx:/etc/nginx:ro \
-v /var/opt/devops/front/var/www:/var/www:ro \
--tmpfs /run --tmpfs /tmp \
--network frontnet \
--detach \
selfmade:nginx-1.14-auth-pam > /dev/null
else
# Запускаем контейнер с приёмом трафика путём проброса его с несущей операционной системы
docker run --rm --name ${FRONTNAME} \
--env TZ="`cat /etc/timezone`" \
-v /etc/ssl/nginx:/etc/ssl/nginx:ro \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
-v /var/opt/devops/front/etc/nginx:/etc/nginx:ro \
-v /var/opt/devops/front/var/www:/var/www:ro \
--tmpfs /run --tmpfs /tmp \
--publish 80:80 --publish 443:443 \
--detach \
selfmade:nginx-1.14-auth-pam > /dev/null
fi
# Направляем вывод подсистемы журналирования контейнера в текстовый файл
docker logs --follow ${FRONTNAME} > /var/log/devops/front/output-nginx.log 2>&1 &
# Проверяем успешность выполнения процедуры запуска
if [ `docker ps | grep -c -i "${FRONTNAME}"` -ne 0 ] ; then
echo "$(cdate): Контейнер \"${FRONTNAME}\" успешно запущен." | tee -a "${LOG}"
# Подключаем запущенный контейнер к транзитной связующей виртуальной сети
docker network connect vianet ${FRONTNAME}
if [ "${?}" -eq "0" ] ; then
echo "$(cdate): Контейнер \"${FRONTNAME}\" успешно включён в сеть \"vianet\"." | tee -a "${LOG}"
else
FORTH=false
echo "$(cdate): Сбой при включении контейнера \"${FRONTNAME}\" в сеть \"vianet\"." | tee -a "${LOG}"
fi
else
FORTH=false
echo "$(cdate): Сбой при запуске контейнера \"${FRONTNAME}\"." | tee -a "${LOG}"
fi
return ${?}
}
# Функция остановки контейнера фронтального web-прокси
function front-nginx-stop {
# Пробуем остановить контейнер
docker stop ${FRONTNAME} > /dev/null 2>&1
if [ "$(docker ps | grep -c -i ${FRONTNAME})" -eq "0" ] ; then
echo "$(cdate): Контейнер \"${FRONTNAME}\" успешно остановлен." | tee -a "${LOG}"
else
FORTH=false
echo "$(cdate): Сбой при остановке контейнера \"${FRONTNAME}\"." | tee -a "${LOG}"
fi
return ${?}
}
# Функция предоставления доступа к web-интерфейсу контейнера фронтального web-прокси
# (функциональность PAM + "Nginx HTTP Authentication" требует наличия аккаунтов соответствующих пользователей внутри контейнера)
function front-web-view-enable {
# Формируем и перебираем список локальных пользователей ОС, входящих в группу "developer"
MEMBERS=( $(members developer 2>/dev/null) )
for MEMBER in "${MEMBERS[@]}" ; do
# Выявляем пользователей с паролями и работает только с ними
MPASSWD=$(cat /etc/shadow | grep -i "^${MEMBER}:" | awk -F ':' '{print $2}')
[[ -z "${MPASSWD}" || $(echo "${MPASSWD}" | wc -c) -le "12" ]] && { continue; }
# Добавляем нового (игнорируя наличие такового, если он уже создан) пользователя (с предварительным профилактическим созданием группы)
docker exec -i ${FRONTNAME} /bin/bash -c "groupadd --force --system developer && useradd --no-create-home --home-dir /nonexistent --shell /bin/false --gid developer ${MEMBER} 2>/dev/null" 2>/dev/null
if [[ "${?}" -eq "0" || "${?}" -eq "9" ]] ; then
# Обновляем пароль у имеющегося или задаём созданному пользователю
docker exec -i ${FRONTNAME} /bin/bash -c "usermod -p '${MPASSWD}' ${MEMBER}"
# Проверяем успешность обновления пароля
if [ "${?}" -eq "0" ] ; then
echo "$(cdate): Пользователю \"${MEMBER}\" успешно предоставлен доступ к просмотру журналов событий запуска и остановки тестовых стендов." | tee -a "${LOG}"
else
FORTH=false
echo "$(cdate): Сбой предоставления пользователю \"${MEMBER}\" доступа к просмотру журналов событий запуска и остановки тестовых стендов." | tee -a "${LOG}"
fi
else
FORTH=false
echo "$(cdate): Невозможно предоставить пользователю \"${MEMBER}\" доступ к просмотру журналов событий из-за сбоя при подключении к фронтальному web-прокси." | tee -a "${LOG}"
fi
done
unset MEMBERS; unset MEMBER
return ${?}
}