You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

301 lines
9.5 KiB

4 years ago
#!/usr/bin/env bash
###################################################################
#Script Name : check-container-ip.sh
#Description : Detect container ip addresses and dynamically update nginx upstream configuration
#Create Date : 2021-06-25
#Author : lework
#Email : lework@yeah.net
###################################################################
[[ -n $DEBUG ]] && set -x
set -o errtrace # Make sure any error trap is inherited
set -o nounset # Disallow expansion of unset variables
set -o pipefail # Use last non-zero exit code in a pipeline
######################################################################################################
# environment configuration
######################################################################################################
RETRIES="${RETRIES:-9223372036854775807}"
WAIT="${WAIT:-5}"
CONTAINER_NAME="${CONTAINER_NAME:-}"
CONFIG_FILE="${CONFIG_FILE:-}"
CONFIG_FILE_NAME="${CONFIG_FILE_NAME:-}"
UPSTREAM_NAME="${UPSTREAM_NAME:-}"
SERVICE_PORT="${SERVICE_PORT:-}"
CONTAINER_IP="${CONTAINER_IP:-}"
CHECK_IP="${CHECK_IP:-}"
NGINX_STATUS=0 # 为1时reload nginx
NOTICE_TYPE="${NOTICE_TYPE:-feishu}"
NOTICE_MESSAGE="${NOTICE_MESSAGE:-}"
NOTICE_TOKEN="${NOTICE_TOKEN:-}"
TMP_DIR="$(rm -rf /tmp/check-container-ip* && mktemp -d -t check-container-ip.XXXXXXXXXX)"
TMP_CONFIG_FILE="${TMP_DIR}/${CONFIG_FILE_NAME}_$(date +%s)_temp.conf"
NGINX_BACKUP_PATH="${NGINX_BACKUP_PATH:-/tmp}"
trap trap::info 1 2 3 15 EXIT
######################################################################################################
# function
######################################################################################################
function trap::info() {
# 信号处理
[ ${n:-0} -gt 0 ] && log::info "[stop]" "check total: ${n}"
trap '' EXIT
exit
}
function log::error() {
# 错误日志
printf "[%s]: \033[31mERROR: \033[0m%s\n" "$(date +'%Y-%m-%dT%H:%M:%S.%N%z')" "$*"
}
function log::info() {
# 基础日志
printf "[%s]: \033[32mINFO: \033[0m%s\n" "$(date +'%Y-%m-%dT%H:%M:%S.%N%z')" "$*"
}
function log::warning() {
# 警告日志
printf "[%s]: \033[33mWARNING: \033[0m%s\n" "$(date +'%Y-%m-%dT%H:%M:%S.%N%z')" "$*"
}
function container::get_ip() {
# 获取容器 ip地址
log::info "[container]" "Get container ip address."
container_id=$(docker ps -a --no-trunc --filter=status=running --format "table {{.ID}}\t{{.Names}}" | awk "/${CONTAINER_NAME}/{print \$1}")
CONTAINER_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ${container_id} 2>/dev/null | tr '\n' ' ' )
if [[ "$?" != "0" || "${CONTAINER_IP}" == "" ]]; then
log::error "[container]" "Get container ip address error."
exit 1
fi
log::info "[container]" "IP: ${CONTAINER_IP}"
}
function container::check_ip() {
# 检测容器ip地址
log::info "[container]" "Check container service status."
CHECK_IP=""
for ip in ${CONTAINER_IP}; do
curl -s --output /dev/null "http://${ip}:${SERVICE_PORT}" && CHECK_IP="${CHECK_IP} ${ip}"
done
log::info "[container]" "Check success: ${CHECK_IP}"
}
function nginx::get_upstream_ip() {
# 获取 nginx upstream ip地址
log::info "[nginx]" "Get nginx upstream ${UPSTREAM_NAME} host."
upstream_ip=$(awk '-F *|:' "
/upstream ${UPSTREAM_NAME}/ {
app = 1
next
}
/server/ {
if (app) print \$3
}
/}/ {
if (app) exit
}
" "${CONFIG_FILE}" | tr '\n' ' ')
log::info "[nginx]" "Upstream host: ${upstream_ip}"
}
function nginx::config() {
# 配置 nginx
log::info "[nginx]" "Config nginx upstream ${UPSTREAM_NAME} host."
cp "${CONFIG_FILE}" "${TMP_CONFIG_FILE}"
for ip in ${CHECK_IP}; do
if ! grep "${ip}" "${TMP_CONFIG_FILE}" >/dev/null 2>&1; then
sed -i "/upstream ${UPSTREAM_NAME} {/a\ server ${ip}:${SERVICE_PORT};" "${TMP_CONFIG_FILE}" && NGINX_STATUS=1
log::info "[nginx]" "IP: ${ip} add to upstream hosts."
NOTICE_MESSAGE="${NOTICE_MESSAGE}**Add**: ${ip} add to upstream hosts.\n"
fi
done
for ip in ${upstream_ip}; do
if [[ "$CHECK_IP" == *${ip}* ]]; then
log::info "[nginx]" "IP: ${ip} ip already exists on the upstream host."
else
log::info "[nginx]" "IP: ${ip} delete from upstream host."
NOTICE_MESSAGE="${NOTICE_MESSAGE}**Del**: ${ip} delete from upstream host.\n"
sed -i "/server ${ip:-11111111111}:${SERVICE_PORT}/d" "${TMP_CONFIG_FILE}" && NGINX_STATUS=1
fi
done
if [[ $NGINX_STATUS == 1 ]]; then
log::info "[nginx]" "Backup nginx config to ${NGINX_BACKUP_PATH}/${CONFIG_FILE_NAME}-$(date +%s)"
cp -f "$CONFIG_FILE" "${NGINX_BACKUP_PATH}/${CONFIG_FILE_NAME}-$(date +%s)"
log::info "[nginx]" "Apply nginx config file."
mv -f "${TMP_CONFIG_FILE}" "$CONFIG_FILE"
fi
}
function nginx::reload() {
# 重载 nginx
if [[ $NGINX_STATUS == 1 ]]; then
log::info "[nginx]" "Reload nginx."
local level=red
if nginx -t >/dev/null 2>&1; then
if nginx -s reload >/dev/null 2>&1; then
log::info "[nginx]" "Reload nginx success."
NOTICE_MESSAGE="${NOTICE_MESSAGE}**Reload**: nginx success.\n"
level=green
else
log::error "[nginx]" "Reload nginx error."
NOTICE_MESSAGE="${NOTICE_MESSAGE}**Reload**: nginx error.\n"
fi
else
log::error "[nginx]" "test nginx config error."
NOTICE_MESSAGE="${NOTICE_MESSAGE}**Test**: nginx config error.\n"
exit 1
fi
notice "$level" "${NOTICE_MESSAGE}"
fi
}
function notice() {
# 通知
log::info "[notice]" "select ${NOTICE_TYPE}"
if [ -z $NOTICE_TOKEN ];then
log::warning "[notice]" "please set NOTICE_TOKEN"
return
fi
notice::${NOTICE_TYPE} $@
}
function notice::feishu() {
# 飞书通知
local level="${1:-green}"
local message="${@:2}"
local now_date="$(date +'%Y-%m-%d %T')"
local title="[Upstream] check container ip"
local host="$(ip a | grep glo | awk '{print $2}' | head -1 | cut -f1 -d/)"
local template="{\"msg_type\":\"interactive\",\"card\":{\"config\":{\"wide_screen_mode\":true,\"enable_forward\":true},\"header\":{\"title\":{\"content\":\"$title\",\"tag\":\"plain_text\"},\"template\":\"${level}\"},\"elements\":[{\"tag\":\"div\",\"text\":{\"content\":\"**发生时间:**\",\"tag\":\"lark_md\"},\"fields\":[{\"is_short\":false,\"text\":{\"tag\":\"lark_md\",\"content\":\"${now_date}\"}}]},{\"tag\":\"div\",\"text\":{\"content\":\"**节点:**\",\"tag\":\"lark_md\"},\"fields\":[{\"is_short\":false,\"text\":{\"tag\":\"lark_md\",\"content\":\"${host}\"}}]},{\"tag\":\"div\",\"text\":{\"content\":\"**详细信息:**\",\"tag\":\"lark_md\"},\"fields\":[{\"is_short\":false,\"text\":{\"tag\":\"lark_md\",\"content\":\"${message}\"}}]},{\"tag\":\"hr\"},{\"tag\":\"note\",\"elements\":[{\"tag\":\"plain_text\",\"content\":\"by lework\"}]}]}}"
if curl -s -X POST -H "Content-Type: application/json" -d "${template}" "https://open.feishu.cn/open-apis/bot/v2/hook/${NOTICE_TOKEN}" | grep success >/dev/null 2>&1; then
log::info "[notice]" "send feishu success."
else
log::error "[notice]" "send feishu error."
fi
}
function check::start() {
# 检测流程
echo
log::info "[check]" "Start inspection."
container::get_ip
container::check_ip
nginx::get_upstream_ip
nginx::config
nginx::reload
NGINX_STATUS=0
NOTICE_MESSAGE=""
}
function help::usage {
# 使用帮助
cat << EOF
Detect container ip addresses and dynamically update nginx upstream configuration.
Usage:
$(basename "$0") [options]
Options:
--container-name Docker container name
--config-file Nginx conf path
--upstream-name Nginx upstream name
--service-port Service port
--retries Retries number, default:${RETRIES}
--wait Retries wait time, default:${WAIT}
-h,--help View help
Example:
$0 --container-name api-test \\
--config-file /etc/nginx/conf.d/api.conf \\
--upstream-name api \\
--service-port 8000 \\
--retries 20 \\
--wait 5
EOF
exit 1
}
######################################################################################################
# main
######################################################################################################
[ "$#" == "0" ] && help::usage
while [ "${1:-}" != "" ]; do
case $1 in
--container-name ) shift
CONTAINER_NAME=${1:-$CONTAINER_NAME}
;;
--config-file ) shift
CONFIG_FILE=${1:-$CONFIG_FILE}
CONFIG_FILE_NAME=$(basename ${CONFIG_FILE})
;;
--upstream-name ) shift
UPSTREAM_NAME=${1:-$UPSTREAM_NAME}
;;
--service-port ) shift
SERVICE_PORT=${1:-$SERVICE_PORT}
;;
--retries ) shift
RETRIES=${1:-$RETRIES}
;;
--wait ) shift
WAIT=${1:-$WAIT}
;;
-h | --help ) help::usage
;;
* ) help::usage
exit 1
esac
shift
done
n=0
while [[ $n -lt ${RETRIES} ]]
do
check::start
sleep ${WAIT}
let n++
done