mirror of https://github.com/lework/script
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
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
|
||
|
|