#!/bin/bash
# author akim
# version 1.0
# date 2024/11/09 08:42
# 配置控制台输出颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
# 当前所在目录
CURRENT_DIR=$(
cd "$(dirname "$0")" || exit
pwd
)
# 全局静默安装等待时间变量,单位:秒
# 调整此参数以约束等待用户输入时间
WAIT_TIME=10
# 循环尝试次数
# 暂时只针对 minio
# 因为 minio 虽然容器启动了,但是从容器的运行状态和日志中分析不出是否已完全启动成功
# 导致初始化桶失败的问题,所以需要设置尝试次数,通过 curl 方式请求运行健康状态是否正常
ATTEMPT_COUNT=10
# 循环尝试等待时间,单位:秒
# 暂时只针对 minio,问题如上 ATTEMPT_COUNT 全局变量描述,curl 请求后休眠时间
ATTEMPT_SLEEP=5
# 放行端口集合
PUBLIC_PORTS=()
# 日志处理
function log() {
# 格式化日志[年-月-日 时-分-秒]:消息,$1 表示调用函数传递的第一个参数,以此类推
message="[$(date +"%Y-%m-%d %H:%M:%S")]: $1 "
case "$1" in
# 消息中含失败、错误、权限等,控制台输出 RED (红色)
*"失败"* | *"错误"* | *"权限"* | *"警告"*)
echo -e "${RED}${message}${NC}" 2>&1 | tee -a "${CURRENT_DIR}"/install.log
;;
*"成功"* | *"设置"* | *"是否"* | *"进度"* | *"选择"* | *"输入"*)
echo -e "${GREEN}${message}${NC}" 2>&1 | tee -a "${CURRENT_DIR}"/install.log
;;
*)
echo -e "${YELLOW}${message}${NC}" 2>&1 | tee -a "${CURRENT_DIR}"/install.log
;;
esac
}
echo
cat <<EOF
██████╗ ██╗ ██╗ ██████╗ ███████╗██╗███████╗
██╔══██╗██║ ██║ ██╔══██╗██╔════╝██║██╔════╝
██║ ██║███████║ ██████╔╝█████╗ ██║███████╗
██║ ██║██╔══██║ ██╔═══╝ ██╔══╝ ██║╚════██║
██████╔╝██║ ██║ ██║ ███████╗██║███████║
╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝╚══════╝
EOF
# 记录日志
log "=================== 开始安装 ==================="
# 1、检查权限
function checkRoot() {
# 校验用户有效ID
if [[ $EUID -ne 0 ]]; then
log "请使用 root 或 sudo 权限运行此脚本"
exit
fi
}
# 2、设置安装目录
function setInstallDir() {
# 默认安装目录
DEFAULT_INSTALL_DIR=/dhpeis
log "设置安装目录(默认为:${DEFAULT_INSTALL_DIR}):\c"
# 判断等待用户输入时间是否大于全局变量 $WAIT_TIME
if read -t $WAIT_TIME -r INSTALL_DIR; then
# 如果用户输入的内容不等于空
if [[ "$INSTALL_DIR" != "" ]]; then
# 如果用户输入的格式不是以 / 开头
if [[ "$INSTALL_DIR" != /* ]]; then
log "请输入目录的完整路径"
# 重新调用:3、设置安装目录 setInstallDir 函数
setInstallDir
fi
# 判断用户输入内容是否为一个目录
if [[ ! -d $INSTALL_DIR ]]; then
# 创建用户输入的目录
mkdir -p "$INSTALL_DIR"
log "安装路径为:$INSTALL_DIR"
fi
else
# 设置安装目录变量为默认目录
INSTALL_DIR=$DEFAULT_INSTALL_DIR
log "默认安装路径:$DEFAULT_INSTALL_DIR"
fi
else
# 超过等待用户输入约定时间,设置安装目录为默认目录
INSTALL_DIR=$DEFAULT_INSTALL_DIR
fi
printf "\n"
log "默认安装路径:$DEFAULT_INSTALL_DIR"
}
# 3、获取 ip
function getIp() {
# 获取网关地址
activeInterface=$(ip route get 8.8.8.8 | awk 'NR==1 {print $5}')
# 判断网关地址是否为空
if [[ -z $activeInterface ]]; then
# 如果为空,则设置为 127.0.0.1
LOCAL_IP="127.0.0.1"
else
# 否则通过正则获取当前设置ip
LOCAL_IP=$(ip -4 addr show dev "$activeInterface" | grep -oP '(?<=inet\s)\d+(\.\d+){3}')
fi
log "本地 IP:$LOCAL_IP"
# PUBLIC_IP=$(curl -s https://api64.ipify.org)
# if [[ -z "$PUBLIC_IP" ]]; then
# PUBLIC_IP="N/A"
# fi
# if echo "$PUBLIC_IP" | grep -q ":"; then
# PUBLIC_IP=[${PUBLIC_IP}]
# # listen-ip ipv6
# fi
# log "公网 IP:$PUBLIC_IP"
}
# 4、安装 docker
function installDocker() {
# 检查 docker 是否已安装
if which docker >/dev/null 2>&1; then
log "检测到 Docker 已安装,跳过安装步骤"
log "启动 Docker"
# TODO 校验下 docker 是否运行中,别动不动就 start
systemctl start docker 2>&1 | tee -a "${CURRENT_DIR}"/install.log
else
log "开始安装 Docker"
# 授权执行权限
chmod +x "${CURRENT_DIR}"/docker/bin/*
# 复制目录下所有文件到指定目录下
cp "${CURRENT_DIR}"/docker/bin/* /usr/bin/
# 复制文件到指定目录下
cp "${CURRENT_DIR}"/docker/service/docker.service /etc/systemd/system/
# 把读和运行的权限赋予群组用户,把读的权限赋予其他用户
chmod 754 /etc/systemd/system/docker.service
# 替换文本文件匹配内容,将#LOCAL_IP替换为变量$LOCAL_IP值,文本文件中所有匹配内容($LOCAL_IP)替换为 127.0.0.1
sed -i "s/#LOCAL_IP/$LOCAL_IP/g" /etc/systemd/system/docker.service
mkdir -p /etc/docker/
cp "${CURRENT_DIR}"/docker/conf/daemon.json /etc/docker/daemon.json
log "完成安装 Docker 并启动"
# 设置 docker 开机自启动
systemctl enable docker
# 重新加载镜像加速器文件
systemctl daemon-reload
# 启动 docker
systemctl start docker 2>&1 | tee -a "${CURRENT_DIR}"/install.log
fi
}
# 5、安装 unzip 包
function installUnzip() {
# 检查是否已安装 unzip rmp包(可执行程序,可以理解为 windows 的 *.exe 或 *.msi)
if rpm -q unzip &>/dev/null; then
log "检测到 Unzip 包已经安装,跳过安装步骤"
else
# 安装 unzip 包
log "开始安装 unzip 包"
# 执行 rmp 包安装且不输出结果到控制台
rpm -ivh "${CURRENT_DIR}"/tools/unzip-6.0-46.el8.x86_64.rpm &>/dev/null
log "完成安装 unzip 包"
fi
}
# 6、安装 redis
function installRedis() {
# 校验 Redis 镜像是否存在
checkInstall redis
# 选择不安装
if [[ $IS_INSTALL -eq 2 || $IS_REINSTALL -eq 2 ]]; then
# 设置IP
setIP redis
REDIS_IP=$IP
if [ "$REDIS_IP" != "$LOCAL_IP" ]; then
# 设置 port
setPort redis 6379
REDIS_PORT=$PORT
# 设置密码
setPassword redis
REDIS_PASSWORD=$PASSWORD
else
# 获取原容器内部 6379 端口对宿主机端口
getPort redis 6379
REDIS_PORT=$HOST_PORT
log "原 redis 端口为:$REDIS_PORT"
fi
else
# 选择安装
# 设置 ip
REDIS_IP=$LOCAL_IP
# 设置 port
setPort redis 6379
REDIS_PORT=$PORT
# 设置密码
setPassword redis
REDIS_PASSWORD=$PASSWORD
# 加载镜像
loadImage redis
# 创建目录
REDIS_DIR=$INSTALL_DIR/redis
if [[ ! -d "${REDIS_DIR}/conf" ]]; then
mkdir -p "$REDIS_DIR"/conf
fi
if [[ ! -d "${REDIS_DIR}/data" ]]; then
mkdir -p "$REDIS_DIR"/data
fi
cp "${CURRENT_DIR}"/redis/conf/redis.conf /"${REDIS_DIR}"/conf
# 替换配置文件密码
if [[ $REDIS_PASSWORD != "" ]]; then
sed -i "s/# requirepass #REDIS_PASSWORD/requirepass $REDIS_PASSWORD/g" /"$REDIS_DIR"/conf/redis.conf
fi
log "启动 Redis 容器"
docker run -d --restart=always \
-p "$REDIS_PORT":6379 \
-v "$REDIS_DIR"/conf:/etc/redis/redis.conf \
-v "$REDIS_DIR"/data:/data \
--name redis \
redis
fi
}
# 7、安装 minio
function installMinio() {
# 校验 Minio 镜像是否存在
checkInstall minio
# 选择不安装
if [[ $IS_INSTALL_MINIO -eq 2 || $IS_REINSTALL_MINIO -eq 2 ]]; then
# 设置 ip
setIP minio
MINIO_IP=$IP
# 判断是否本地 IP
if [ "$REDIS_IP" != "$LOCAL_IP" ]; then
# 设置 server port
setPort minio 9000
MINIO_SERVER_PORT=$PORT
# 设置 console port
setPort minio 9090
MINIO_CONSOLE_PORT=$PORT
# 设置用户名
setUserName minio minio
MINIO_USER_NAME=$USER_NAME
# 设置密码
setPassword minio minioadmin
MINIO_PASSWORD=$PASSWORD
else
# 获取原容器内部 9000 端口对宿主机端口
getPort minio 9000
MINIO_SERVER_PORT=$HOST_PORT
# 获取原容器内部 9001 端口对宿主机端口
getPort minio 9001
MINIO_CONSOLE_PORT=$HOST_PORT
# 获取原容器启动时设置的环境变量,对应用户名
MINIO_USER_NAME=$(docker exec minio printenv MINIO_ACCESS_KEY)
# 获取原容器启动时设置的环境变量,对应密码
MINIO_PASSWORD=$(docker exec minio printenv MINIO_SECRET_KEY)
fi
else
# 选择安装
# 设置 ip
MINIO_IP=$LOCAL_IP
# 设置 server port
setPort "minio 服务" 9000
MINIO_SERVER_PORT=$PORT
# 设置 console port
setPort "minio 控制台" 9090
MINIO_CONSOLE_PORT=$PORT
# 设置用户名
setUserName minio minio
MINIO_USER_NAME=$USER_NAME
# 设置密码
setPassword minio minioadmin
MINIO_PASSWORD=$PASSWORD
# 加载镜像
loadImage minio
# 创建目录
MINIO_DIR=$INSTALL_DIR/minio
if [[ ! -d "${MINIO_DIR}/conf" ]]; then
mkdir -p "$MINIO_DIR"/conf
fi
if [[ ! -d "${MINIO_DIR}/data" ]]; then
mkdir -p "$MINIO_DIR"/data
fi
log "启动 Minio 容器"
docker run -d --restart=always \
-p "$MINIO_SERVER_PORT":9000 \
-p "$MINIO_CONSOLE_PORT":9001 \
-e "MINIO_ACCESS_KEY=$MINIO_USER_NAME" \
-e "MINIO_SECRET_KEY=$MINIO_PASSWORD" \
-v "$MINIO_DIR"/conf:/root/.minio \
-v "$MINIO_DIR"/data:/data \
--name minio \
minio server /data --console-address ":$MINIO_CONSOLE_PORT" -address ":$MINIO_SERVER_PORT"
log "minio 启动成功,开始初始化 buket"
# 安装 mc
if [ ! -e "usr/local/bin/mc" ]; then
log "开始安装 mc 工具"
cp "${CURRENT_DIR}"/tools/mc /usr/local/bin
# 添加可执行权限
chmod +x /usr/local/bin/mc
log "完成安装 mc 工具"
fi
# TODO http 需要动态配置
if [[ -z $MINIO_IP ]]; then
MINIO_BASE_URL=http://${LOCAL_IP}:${MINIO_SERVER_PORT}
else
MINIO_BASE_URL=http://${MINIO_IP}:${MINIO_SERVER_PORT}
fi
# 从启动日志、容器状态中无法分析出 minio 是否已经完全启动,所以通过 curl 方式来检测
if docker ps -f "name=minio" | grep -q "minio"; then
count=0
while [ $count -le $ATTEMPT_COUNT ]; do
curl -s "${MINIO_BASE_URL}/minio/health/live" \
-u "${MINIO_USER_NAME}:${MINIO_PASSWORD}" >/dev/null
if [ $? -eq 0 ]; then
if [[ -z $MINIO_IP ]]; then
mc alias set minio "$MINIO_BASE_URL" "$MINIO_USER_NAME" "$MINIO_PASSWORD"
else
mc alias set minio "$MINIO_BASE_URL" "$MINIO_USER_NAME" "$MINIO_PASSWORD"
fi
# 创建 buket
log "开始 buket 创建"
mc mb minio/apply-charge &>/dev/null
mc mb minio/apply-delivery &>/dev/null
mc mb minio/apply-insp &>/dev/null
mc mb minio/bar-code &>/dev/null
mc mb minio/fingerprint &>/dev/null
mc mb minio/guidance &>/dev/null
mc mb minio/jmreport &>/dev/null
mc mb minio/peis &>/dev/null
mc mb minio/photo &>/dev/null
mc mb minio/print &>/dev/null
mc mb minio/report &>/dev/null
mc mb minio/result &>/dev/null
mc mb minio/system &>/dev/null
mc mb minio/temp &>/dev/null
mc mb minio/temporary &>/dev/null
mc mb minio/tool &>/dev/null
mc mb minio/uniteport &>/dev/null
log "完成 buket 创建"
# 设置 buket 为 public
log "开始设置 buket 为公开模式"
mc anonymous set public minio/apply-charge/ &>/dev/null
mc anonymous set public minio/apply-delivery/ &>/dev/null
mc anonymous set public minio/apply-insp/ &>/dev/null
mc anonymous set public minio/bar-code/ &>/dev/null
mc anonymous set public minio/fingerprint/ &>/dev/null
mc anonymous set public minio/guidance/ &>/dev/null
mc anonymous set public minio/jmreport/ &>/dev/null
mc anonymous set public minio/peis/ &>/dev/null
mc anonymous set public minio/photo/ &>/dev/null
mc anonymous set public minio/print/ &>/dev/null
mc anonymous set public minio/report/ &>/dev/null
mc anonymous set public minio/result/ &>/dev/null
mc anonymous set public minio/system/ &>/dev/null
mc anonymous set public minio/temp/ &>/dev/null
mc anonymous set public minio/temporary/ &>/dev/null
mc anonymous set public minio/tool/ &>/dev/null
mc anonymous set public minio/uniteport/ &>/dev/null
mc version enable minio/temp &>/dev/null
log "完成设置 buket 为公开模式"
break
else
((count++))
log "尝试第 ${count} 次连接到 Minio 服务"
sleep $ATTEMPT_SLEEP
fi
done
fi
fi
}
# 8、安装 rabbitmq
function installRabbitMq() {
# 校验 RabbitMQ 镜像是否存在
checkInstall rabbitmq
# 选择不安装
if [[ $IS_INSTALL -eq 2 || $IS_REINSTALL -eq 2 ]]; then
# 设置 ip
setIP rabbitmq
RABBITMQ_IP=$IP
if [ "$RABBITMQ_IP" != "$LOCAL_IP" ]; then
# 设置 AMQP 协议 port
setPort rabbitmq 5672
RABBITMQ_AMQP_PORT=$PORT
# 设置 console port
setPort rabbitmq 15672
RABBITMQ_CONSOLE_PORT=$PORT
# 设置用户名
setUserName rabbitmq rabbitmq
RABBITMQ_USER_NAME=$USER_NAME
# 设置密码
setPassword rabbitmq rabbitmq
RABBITMQ_PASSWORD=$PASSWORD
else
# 获取原容器内部 5672 端口对宿主机端口
getPort rabbitmq 5672
RABBITMQ_AMQP_PORT=$HOST_PORT
# 获取原容器内部 15672 端口对宿主机端口
getPort rabbitmq 15672
RABBITMQ_CONSOLE_PORT=$HOST_PORT
# 获取原容器启动时设置的环境变量,对应用户名
RABBITMQ_USER_NAME=$(docker exec minio printenv RABBITMQ_DEFAULT_USER)
# 获取原容器启动时设置的环境变量,对应密码
RABBITMQ_PASSWORD=$(docker exec minio printenv RABBITMQ_DEFAULT_PASS)
fi
else
# 选择安装
# 设置 ip
RABBITMQ_IP=$LOCAL_IP
# 设置 AMQP 协议 port
setPort rabbitmq 5672
RABBITMQ_AMQP_PORT=$PORT
# 设置 console port
setPort rabbitmq 15672
RABBITMQ_CONSOLE_PORT=$PORT
# 设置用户名
setUserName rabbitmq admin
RABBITMQ_USER_NAME=$USER_NAME
# 设置密码
setPassword rabbitmq rabbitmq
RABBITMQ_PASSWORD=$PASSWORD
# 加载镜像
loadImage rabbitmq
# 创建目录
RABBITMQ_DIR=$INSTALL_DIR/rabbitmq
if [[ ! -d "${RABBITMQ_DIR}/conf" ]]; then
mkdir -p "$RABBITMQ_DIR"/conf
# 开启与前端通信的插件,已在安conf目录下的 enabled_plugins 配置文件里里配置,详情可以查看:https://www.rabbitmq.com/docs/web-stomp
# 访问:http://xxx.xxx.xxx.xxx:15674,显示"找不到 xxx.xxx.xxx.xxx 的网页",表示已开启插件。也可以进入容器,开启插件:
# 指令1:rabbitmq-plugins enable rabbitmq_web_stomp # 开启该插件后,会开放15674端口,用于与前端进行通信。
# 指令2:rabbitmq-plugins enable rabbitmq_web_stomp_examples # 开启该插件后,可以提供的示例进行测试
cp -r "${CURRENT_DIR}"/rabbitmq/conf/* "$RABBITMQ_DIR"/conf
fi
if [[ ! -d "${RABBITMQ_DIR}/logs" ]]; then
mkdir -p "$RABBITMQ_DIR"/logs
chmod 777 -R "$RABBITMQ_DIR"/logs
fi
if [[ ! -d "${RABBITMQ_DIR}/data" ]]; then
mkdir -p "$RABBITMQ_DIR"/data
chmod 777 -R "$RABBITMQ_DIR"/data
fi
log "启动 RabbitMQ 容器"
docker run -d --restart=always \
-p "$RABBITMQ_AMQP_PORT":5672 \
-p "$RABBITMQ_CONSOLE_PORT":15672 \
-p 15674:15674 \
-p 15692:15692 \
-e RABBITMQ_DEFAULT_USER="${RABBITMQ_USER_NAME}" \
-e RABBITMQ_DEFAULT_PASS="${RABBITMQ_PASSWORD}" \
-v "$RABBITMQ_DIR"/conf:/etc/rabbitmq \
-v "$RABBITMQ_DIR"/logs:/var/log/rabbitmq \
-v "$RABBITMQ_DIR"/data:/var/lib/rabbitmq \
--name rabbitmq \
rabbitmq:3-management
fi
}
# 9、安装 elasticsearch
function installElasticSearch() {
# 校验 Elasticsearch 镜像是否存在
checkInstall elasticsearch
# 选择不安装
if [[ $IS_INSTALL_ELASTICSEARCH -eq 2 || $IS_REINSTALL_ELASTICSEARCH -eq 2 ]]; then
# 设置 ip
setIP elasticsearch
ELASTICSEARCH_IP=$IP
if [ "$ELASTICSEARCH_IP" != "$LOCAL_IP" ]; then
# 设置 http port
setPort elasticsearch 9200
ELASTICSEARCH_HTTP_PORT=$PORT
# 设置 node port
setPort elasticsearch 9300
ELASTICSEARCH_NODE_PORT=$PORT
else
# 获取原容器内部 9200 端口对宿主机端口
getPort elasticsearch 9200
ELASTICSEARCH_HTTP_PORT=$HOST_PORT
# 获取原容器内部 9300 端口对宿主机端口
getPort elasticsearch 9300
ELASTICSEARCH_NODE_PORT=$HOST_PORT
fi
else
# 选择安装
# 设置 ip
ELASTICSEARCH_IP=$LOCAL_IP
# 设置 http port
setPort elasticsearch 9200
ELASTICSEARCH_HTTP_PORT=$PORT
# 设置 node port
setPort elasticsearch 9300
ELASTICSEARCH_NODE_PORT=$PORT
# 加载镜像
loadImage elasticsearch
# 创建目录
ELASTICSEARCH_DIR=$INSTALL_DIR/elasticsearch
if [[ ! -d "${ELASTICSEARCH_DIR}/conf" ]]; then
mkdir -p "$ELASTICSEARCH_DIR"/conf
fi
if [[ ! -d "${ELASTICSEARCH_DIR}/data" ]]; then
mkdir -p "$ELASTICSEARCH_DIR"/data
fi
if [[ ! -d "${ELASTICSEARCH_DIR}/logs" ]]; then
mkdir -p "$ELASTICSEARCH_DIR"/logs
fi
if [[ ! -d "${ELASTICSEARCH_DIR}/plugins" ]]; then
mkdir -p "$ELASTICSEARCH_DIR"/plugins
fi
log "启动 Elasticsearch 容器"
docker run -d --restart=always \
-p "$ELASTICSEARCH_HTTP_PORT":9200 \
-p "$ELASTICSEARCH_NODE_PORT":9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms256m -Xmx2048m" \
-v "$ELASTICSEARCH_DIR"/conf:/usr/share/elasticsearch/config \
-v "$ELASTICSEARCH_DIR"/data:/usr/share/elasticsearch/data \
-v "$ELASTICSEARCH_DIR"/logs:/usr/share/elasticsearch/logs \
-v "$ELASTICSEARCH_DIR"/plugins:/usr/share/elasticsearch/plugins \
--privileged \
--name elasticsearch \
elasticsearch:7.4.2
fi
}
# 10、安装 portainer
function installPortainer() {
# 校验 portainer 镜像是否存在
checkInstall portainer
# 选择不安装
if [[ $IS_INSTALL_PORTAINER -eq 2 || $IS_REINSTALL_PORTAINER -eq 2 ]]; then
# 设置 ip
setIP portainer
PORTAINER_IP=$IP
if [ "$PORTAINER_IP" != "$LOCAL_IP" ]; then
# 设置 port
setPort portainer 9001
PORTAINER_PORT=$PORT
else
# 获取原容器内部 9001 端口对宿主机端口
getPort portainer 9001
PORTAINER_PORT=$HOST_PORT
fi
else
# 选择安装
# 设置 ip
PORTAINER_IP=$LOCAL_IP
# 设置 port
setPort portainer 9001
PORTAINER_PORT=$PORT
# 加载镜像
loadImage portainer
# 创建目录
PORTAINER_DIR=$INSTALL_DIR/portainer
if [[ ! -d "${PORTAINER_DIR}/sock" ]]; then
mkdir -p "$PORTAINER_DIR"/sock
fi
if [[ ! -d "${PORTAINER_DIR}/data" ]]; then
mkdir -p "$PORTAINER_DIR"/data
fi
if [[ ! -d "${PORTAINER_DIR}/public" ]]; then
mkdir -p "$PORTAINER_DIR"/public
fi
log "启动 portainer 容器"
docker run -d --restart=always \
-p "$PORTAINER_PORT":9000 \
-v "$PORTAINER_DIR"/sock:/var/run/docker.sock \
-v "$PORTAINER_DIR"/data:/data \
-v "$PORTAINER_DIR"/public:/public \
-m 1024m \
--privileged \
--name portainer \
portainer
fi
}
# 11、安装 oracle
function installOracle() {
# 校验 Oracle 镜像是否存在
checkInstall oracle
# 选择不安装
if [[ $IS_INSTALL -eq 2 || $IS_REINSTALL -eq 2 ]]; then
# 设置 ip
setIP oracle
ORACLE_IP=$IP
if [ "$ORACLE_IP" != "$LOCAL_IP" ]; then
# 设置 port
setPort oracle 1521
ORACLE_PORT=$PORT
# 设置密码
setPassword oracle dhpeis#
ORACLE_PASSWORD=$PASSWORD
else
# 获取原容器内部 1521 端口对宿主机端口
getPort oracle 1521
ORACLE_PORT=$HOST_PORT
# 获取原容器启动时设置的环境变量,对应密码
ORACLE_PASSWORD=$(docker exec oracle printenv ORACLE_PWD)
fi
else
# 选择安装
# 设置 ip
ORACLE_IP=$LOCAL_IP
# 设置 port
setPort oracle 1521
ORACLE_PORT=$PORT
# 设置密码
setPassword oracle oracledh123#
ORACLE_PASSWORD=$PASSWORD
loadImage oracle
# 创建目录
ORACLE_DIR=$INSTALL_DIR/oracle
if [[ ! -d "${ORACLE_DIR}/oradata" ]]; then
mkdir -p "$ORACLE_DIR"/oradata
fi
if [[ ! -d "${ORACLE_DIR}/archivelog" ]]; then
mkdir -p "$ORACLE_DIR"/archivelog
fi
if [[ ! -d "${ORACLE_DIR}/expdp" ]]; then
mkdir -p "$ORACLE_DIR"/expdp
cp "${CURRENT_DIR}"/oracle/init/* "$ORACLE_DIR"/expdp
fi
if [[ ! -d "${ORACLE_DIR}/rman" ]]; then
mkdir -p "$ORACLE_DIR"/rman
fi
# 授权所有目录
chmod 777 -R "$ORACLE_DIR"
log "启动 Oracle 容器"
docker run -d --restart=always \
-p "$ORACLE_PORT":1521 \
-p 5500:5500 \
-v "$ORACLE_DIR"/oradata:/opt/oracle/oradata \
-v "$ORACLE_DIR"/archivelog:/opt/oracle/archivelog \
-v "$ORACLE_DIR"/expdp:/opt/oracle/expdp \
-v "$ORACLE_DIR"/rman:/opt/oracle/rman \
-v /etc/localtime:/etc/localtime:ro \
-e ORACLE_SID=ORCLCDB \
-e ORACLE_PDB=ORCLPDB1 \
-e ORACLE_PWD="${ORACLE_PASSWORD}" \
-e ORACLE_EDITION=standard \
-e ORACLE_CHARACTERSET=ZHS16GBK \
-e TZ="Asia/Shanghai" \
--name oracle \
oracle:19c
# 开始监听容器初始化过程
# 创建一个命名管道
logPipe=$(mktemp -u)
mkfifo "$logPipe"
# 启动一个后台进程来读取容器启动日志,并将其输出(包括错误输出)写入管道
{
docker logs --tail 1 -f oracle >/dev/null 2>&1 >"$logPipe"
} &
# 当前执行进程 id
logReadPID=$!
# 处理管道中的日志输出
while IFS= read -r outMessage; do
# 处理日志
case "$outMessage" in
*"Prepare for db operation"*)
log "准备开始安装!"
;;
*"% complete"*)
position=$(echo "$outMessage" | grep -bo "complete" | head -n1 | cut -d: -f1)
log "安装进度:${outMessage:0:position-1}"
;;
*"Copying database files"*)
log "复制数据库文件中..."
;;
*"Creating and starting Oracle instance"*)
log "创建并启动实例中..."
;;
*" Completing Database Creation"*)
log "数据库初始化完成"
;;
*"Executing Post Configuration Actions"*)
log "开始执行配置操作"
;;
*"DATABASE IS READY TO USE!"*)
log "数据库安装成功,开始构建并导入应用平台数据!"
break
;;
*"ERROR"*)
log "数据库安装失败,失败原因:"
break
;;
esac
done <"$logPipe"
# 清理管道和后台进程
kill "$logReadPID"
rm -f "$logPipe"
# 结束监听容器初始化过程
fi
# 构建并导入应用平台数据
initOracle
}
# 12、初始化Oracle数据库
function initOracle() {
# 初始化表空间
docker exec -i oracle sqlplus sys/"${ORACLE_PASSWORD}"@localhost:"${ORACLE_PORT}"/ORCLPDB1 as sysdba <<EOF
@/opt/oracle/expdp/create_tablespace.sql
exit
EOF
# 初始化用户
docker exec -i oracle sqlplus sys/"$ORACLE_PASSWORD"@localhost:"${ORACLE_PORT}"/ORCLPDB1 as sysdba <<EOF
@/opt/oracle/expdp/create_peis_user.sql
exit
EOF
# 初始化dmp目录
docker exec -i oracle sqlplus dhpeis/dhyx2021@localhost:"${ORACLE_PORT}"/ORCLPDB1 <<EOF
@/opt/oracle/expdp/create_dump_dir.sql
exit
EOF
# 初始化数据
docker exec -it oracle impdp dhpeis/dhyx2021@localhost:"${ORACLE_PORT}"/ORCLPDB1 directory=EXPDP_DUMP_DIR dumpfile=init.dmp logfile=import.log
# 初始化函数
docker exec -i oracle sqlplus dhpeis/dhyx2021@localhost:"${ORACLE_PORT}"/ORCLPDB1 <<EOF
@/opt/oracle/expdp/create_function.sql
exit
EOF
}
# 13、安装服务端应用
function installServerApp() {
log "安装体检系统"
# 校验 Oracle 镜像是否存在
checkInstall peis-server
# 选择不安装
if [[ $IS_INSTALL -eq 2 || $IS_REINSTALL -eq 2 ]]; then
# 获取原容器内部 8888 端口对宿主机端口
getPort peis-server 8888
SERVER_APP_PORT=$HOST_PORT
else
# 选择安装
# 设置 port
setPort peis-server 8888
SERVER_APP_PORT=$PORT
loadImage peis-server
# 创建目录
SERVER_APP_DIR=$INSTALL_DIR/peis-server
if [[ ! -d "${SERVER_APP_DIR}/logs" ]]; then
mkdir -p "$SERVER_APP_DIR"/logs
fi
if [[ ! -d "${SERVER_APP_DIR}/conf" ]]; then
mkdir -p "$SERVER_APP_DIR"/conf
# 复制配置文件
cp "${CURRENT_DIR}"/peis-server/conf/* "$SERVER_APP_DIR"/conf
# 修改配置文件
sed -i "s/#LOCAL_IP/$LOCAL_IP/g" "$SERVER_APP_DIR"/conf/application-prod.yml
# 配置 oracle
sed -i "s/#ORACLE_IP/$ORACLE_IP/g" "$SERVER_APP_DIR"/conf/application-prod.yml
sed -i "s/#ORACLE_PORT/$ORACLE_PORT/g" "$SERVER_APP_DIR"/conf/application-prod.yml
# 配置 redis
sed -i "s/#REDIS_IP/$REDIS_IP/g" "$SERVER_APP_DIR"/conf/application-prod.yml
sed -i "s/#REDIS_PORT/$REDIS_PORT/g" "$SERVER_APP_DIR"/conf/application-prod.yml
# 配置 rabbitmq
sed -i "s/#RABBITMQ_IP/$RABBITMQ_IP/g" "$SERVER_APP_DIR"/conf/application-prod.yml
sed -i "s/#RABBITMQ_PORT/$RABBITMQ_PORT/g" "$SERVER_APP_DIR"/conf/application-prod.yml
sed -i "s/#RABBITMQ_USER_NAME/$RABBITMQ_USER_NAME/g" "$SERVER_APP_DIR"/conf/application-prod.yml
sed -i "s/#RABBITMQ_PASSWORD/$RABBITMQ_PASSWORD/g" "$SERVER_APP_DIR"/conf/application-prod.yml
# 配置 elasticsearch
sed -i "s/#ELASTICSEARCH_IP/$ELASTICSEARCH_IP/g" "$SERVER_APP_DIR"/conf/application-prod.yml
sed -i "s/#ELASTICSEARCH_HTTP_PORT/$ELASTICSEARCH_HTTP_PORT/g" "$SERVER_APP_DIR"/conf/application-prod.yml
# 配置 minio
sed -i "s/#MINIO_IP/$MINIO_IP/g" "$SERVER_APP_DIR"/conf/application-prod.yml
sed -i "s/#MINIO_SERVER_PORT/$MINIO_SERVER_PORT/g" "$SERVER_APP_DIR"/conf/application-prod.yml
sed -i "s/#MINIO_USER_NAME/$MINIO_USER_NAME/g" "$SERVER_APP_DIR"/conf/application-prod.yml
sed -i "s/#MINIO_PASSWORD/$MINIO_PASSWORD/g" "$SERVER_APP_DIR"/conf/application-prod.yml
fi
if [[ ! -d "${SERVER_APP_DIR}/auth" ]]; then
mkdir -p "$SERVER_APP_DIR"/auth
fi
if [[ ! -d "${SERVER_APP_DIR}/security" ]]; then
mkdir -p "$SERVER_APP_DIR"/security
fi
log "启动 peis-server 容器"
docker run -d --restart=always \
-p "${SERVER_APP_PORT}":8888 \
-v "${SERVER_APP_DIR}"/logs:/applog \
-v "${SERVER_APP_DIR}"/conf:/var/service/config \
-v "${SERVER_APP_DIR}"/auth:/var/service/auth \
-v /usr/share/fonts:/usr/share/fonts \
-v "${SERVER_APP_DIR}"/security:/opt/java/openjdk/jre/lib/security/java.security \
--name peis-server \
peis-server:1.0.0
fi
}
# 14、安装 nginx
function installNginx() {
# 校验 nginx 镜像是否存在
checkInstall nginx
# 选择安装
if [[ $IS_INSTALL -eq 1 || $IS_REINSTALL -eq 1 ]]; then
# 校验 nginx 镜像是否存在
loadImage nginx
# 创建目录
NGINX_DIR=$INSTALL_DIR/nginx
if [[ ! -d "${NGINX_DIR}/ssl" ]]; then
mkdir -p "$NGINX_DIR"/ssl
cp "${CURRENT_DIR}"/nginx/ssl/* "$NGINX_DIR"/ssl
fi
if [[ ! -d "${NGINX_DIR}/html" ]]; then
mkdir -p "$NGINX_DIR"/html
log "开始解包 peis-web "
unzip -q -o "${CURRENT_DIR}"/peis-web/web.zip -d "$NGINX_DIR"/html
log "完成解包 peis-web"
cp "${CURRENT_DIR}"/peis-web/conf/index.js "$NGINX_DIR"/html/web/config
sed -i "s/#LOCAL_IP/$LOCAL_IP/g" "$NGINX_DIR"/html/web/config/index.js
sed -i "s/#SERVER_APP_PORT/$SERVER_APP_PORT/g" "$NGINX_DIR"/html/web/config/index.js
sed -i "s/#MINIO_IP/$MINIO_IP/g" "$NGINX_DIR"/html/web/config/index.js
sed -i "s/#MINIO_SERVER_PORT/$MINIO_SERVER_PORT/g" "$NGINX_DIR"/html/web/config/index.js
sed -i "s/#RABBITMQ_IP/$RABBITMQ_IP/g" "$NGINX_DIR"/html/web/config/index.js
fi
if [[ ! -d "${NGINX_DIR}/logs" ]]; then
mkdir -p "$NGINX_DIR"/logs
fi
cp "${CURRENT_DIR}"/nginx/conf/nginx.conf "$NGINX_DIR"
log "启动 Nginx 容器"
docker run -d --restart=always \
-p 443:443 \
-p 80:80 \
-p 8080:8080 \
-p 8443:8443 \
-v "$NGINX_DIR"/ssl:/etc/nginx/ssl \
-v "$NGINX_DIR"/nginx.conf:/etc/nginx/nginx.conf \
-v "$NGINX_DIR"/html:/usr/share/nginx/html \
-v "$NGINX_DIR"/logs:/var/log/nginx \
-m 1024m \
--privileged \
--name nginx \
nginx
fi
}
# 15、防火墙放行端口
function setFirewall() {
if which firewall-cmd >/dev/null 2>&1; then
if systemctl status firewalld | grep -q "Active: active" >/dev/null 2>&1; then
for port in "${PUBLIC_PORTS[@]}"; do
log "防火墙放行 ${port} 端口"
firewall-cmd --zone=public --add-port="$port"/tcp --permanent
done
firewall-cmd --reload
else
log "防火墙未开启,忽略端口放行"
fi
fi
if which ufw >/dev/null 2>&1; then
if systemctl status ufw | grep -q "Active: active" >/dev/null 2>&1; then
for port in "${PUBLIC_PORTS[@]}"; do
log "防火墙放行 ${port} 端口"
ufw allow "$port"/tcp
done
ufw reload
else
log "防火墙未开启,忽略端口放行"
fi
fi
}
# 公共方法部分 begin
# 移除容器和镜像
function removeDocker() {
# 校验 Redis 镜像是否存在
if ! test "$(docker images -q "$1" 2>/dev/null)" = ""; then
# 校验 Redis 容器是否存在
if command -v sudo -S docker inspect "$1" >/dev/null 2>&1; then
log "检测到 $1 容器已安装,移除原容器"
# 移除容器
docker rm -f "$1" &>/dev/null
fi
# 移除 Redis 镜像
log "检测到 $1 镜像已安装,移除原镜像"
docker rmi -f "$1" &>/dev/null
fi
}
# 校验镜像和容器是否已安装
function checkInstall() {
clear
# 校验镜像是否存在
if ! test "$(docker images -q "$1" 2>/dev/null)" = "" && docker ps -q -f "name=$1" | grep -q .; then
log "检测到 $1 镜像和容器已安装,是否重新安装 ?\n1.是 2.否"
# 开启无限循环
while true; do
if read -t ${WAIT_TIME} -r IS_REINSTALL; then
if [[ ! $IS_REINSTALL =~ ^[1-2]{1}$ ]]; then
log "错误:必须指定 1 或 2"
continue
fi
else
IS_REINSTALL=2
log "默认不重新安装 $1"
fi
# 跳出循环
break
done
if [[ $IS_REINSTALL -eq 1 ]]; then
removeDocker "$1"
fi
else
log "是否安装 $1 ?\n1.是 2.否"
while true; do
if read -t ${WAIT_TIME} -r IS_INSTALL; then
if [[ ! $IS_INSTALL =~ ^[1-2]{1}$ ]]; then
log "错误:必须指定 1 或 2"
continue
fi
else
IS_INSTALL=1
log "默认安装 $1"
fi
break
done
if [[ $IS_INSTALL -eq 1 ]]; then
removeDocker "$1"
fi
fi
}
# 设置 IP
function setIP() {
# 设置指向 ip
log "设置 $1 IP 地址(默认为:${LOCAL_IP}):\c"
while true; do
if read -t ${WAIT_TIME} -r IP; then
if [[ -z $IP ]]; then
IP=$LOCAL_IP
# 校验正则表达式匹配用户输入内容是否通过
elif [[ ! "$IP" =~ ^(2[0-4][0-9]|25[0-5]|1[0-9][0-9]|[1-9]?[0-9])(\.(2[0-4][0-9]|25[0-5]|1[0-9][0-9]|[1-9]?[0-9])){3}$ ]]; then
log "错误:$1 IP 地址输入错误,范围在 0.0.0.0 到 255.255.255.255"
continue
fi
else
IP=$LOCAL_IP
fi
break
done
printf "\n"
log "$1 IP 为:$IP"
}
# 设置 Port
function setPort() {
log "设置 $1 端口号(默认为:$2):\c"
while true; do
if read -t ${WAIT_TIME} -r PORT; then
if [[ -z $PORT ]]; then
PORT=$2
elif ! [[ "$PORT" =~ ^[1-9][0-9]{0,4}$ && "$PORT" -le 65535 ]]; then
log "错误:输入的 Redis 端口号必须在 1 到 65535 之间"
continue
fi
else
PORT=$2
fi
# 如果选择安装才校验本地端口是否被占用
if [[ $IS_INSTALL -eq 1 || $IS_REINSTALL -eq 1 ]]; then
if command -v ss >/dev/null 2>&1; then
if ss -tlun | grep -q ":$PORT" >/dev/null 2>&1; then
log "端口 $PORT 被占用,请重新输入..."
continue
fi
elif command -v netstat >/dev/null 2>&1; then
if netstat -tlun | grep -q ":$PORT" >/dev/null 2>&1; then
log "端口 $PORT 被占用,请重新输入..."
continue
fi
fi
fi
break
done
printf "\n"
log "$1 端口号为:$PORT"
PUBLIC_PORTS+=("$PORT")
}
# 设置用户名
function setUserName() {
log "设置 $1 用户名(默认为:$2):\c"
while true; do
if read -t ${WAIT_TIME} -r USER_NAME; then
if [[ -z $USER_NAME ]]; then
USER_NAME=$2
elif [[ ! "$USER_NAME" =~ ^[a-zA-Z0-9_]{3,30}$ ]]; then
log "错误:$1 用户名仅支持字母、数字、下划线,长度 3-30 位"
continue
fi
else
USER_NAME=$2
fi
break
done
printf "\n"
log "$1 用户名为:$USER_NAME"
}
# 设置密码
function setPassword() {
log "设置 $1 密码(默认为:$2):\c"
while true; do
if read -t ${WAIT_TIME} -r PASSWORD; then
if [[ -z $PASSWORD ]]; then
PASSWORD=$2
elif [[ ! "$PASSWORD" =~ ^[a-zA-Z0-9_!@#$%*,.?]{8,30}$ ]]; then
log "错误:$1 密码仅支持字母、数字、特殊字符(!@#$%*_,.?),长度 8-30 位"
continue
fi
else
PASSWORD=$2
fi
break
done
printf "\n"
log "$1 密码为:$PASSWORD"
}
# 加载镜像
function loadImage() {
# 判断压缩包文件是否存在
if [ -e "${CURRENT_DIR}/$1/$1.zip" ]; then
log "开始解包 $1"
unzip -q -o "${CURRENT_DIR}/$1/$1.zip" -d "${CURRENT_DIR}/$1"
log "完成解包 $1"
# 装载镜像
log "开始加载 $1 镜像"
docker load -i "${CURRENT_DIR}/$1/$1" &>/dev/null
log "完成加载 $1 镜像"
else
log "错误:压缩包不存在"
fi
}
# 获取已安装容器端口
function getPort() {
# 获取指定容器端口列表
portMappings=$(docker port "$1")
IFS=$'\n'
# 遍历端口列表
for mapping in $portMappings; do
# 正则表达式匹配 端口/tcp内容,取端口,例如:9000/tcp,取9000,\d+匹配所有数字
HOST_PORT=$(echo "$mapping" | grep -oP '\d+(?=/tcp)')
# 正则表达式匹配 :端口内容,取端口,例如::9001或[::]9001
CONTAINER_PORT=$(echo "$mapping" | grep -oP '(?<=:)\d+$')
# 如果参数端口匹配获取端口,则跳出循环
if [ "$2" -eq "$CONTAINER_PORT" ]; then
break
fi
done
}
# 主菜单
function changeMode() {
clear
log ""
log "=================请选择安装模式=================="
log ""
log "1.全新安装\t2.平台应用\t3.数据库"
log ""
log "4.其他组件\t5.后端更新\t6.前端更新"
log ""
log "99.卸载(请确保数据安全)"
while true; do
if read -t $WAIT_TIME -r CHANGE_MODE; then
case "$CHANGE_MODE" in
1)
log "选择全新安装模式"
installDocker
installUnzip
installRedis
installMinio
installRabbitMq
installElasticSearch
installPortainer
installOracle
installServerApp
installNginx
;;
2)
log "选择平台应用模式"
installDocker
installUnzip
installRedis
installMinio
installRabbitMq
installElasticSearch
installPortainer
installServerApp
installNginx
;;
3)
log "选择数据库模式"
installOracle
;;
4)
log ""
log "=================请选择安装中间件=================="
log ""
log "1.Redis\t2.Minio\t3.RabbitMQ"
log ""
log "4.ElasticSearch\t5.Portainer"
log ""
log "6.Nginx"
while true; do
if read -t ${WAIT_TIME} -r CHANGE_COMPONET; then
case $CHANGE_COMPONET in
1)
installRedis
;;
2)
installMinio
;;
3)
installRabbitMq
;;
4)
installElasticSearch
;;
5)
installPortainer
;;
6)
installNginx
;;
*)
log "输入错误,请重新选择"
continue
;;
esac
fi
break
done
;;
99)
while true; do
log "警告:确认是否已对数据库、minio等数据进行备份,此操作不可逆,执行后无法恢复数据!!!"
log "请输入:yes 或 no"
if read -r CONFIRM_KEY; then
if [[ $CONFIRM_KEY == "yes" ]]; then
log "确认删除所有数据"
unInstallAll
elif [[ $CONFIRM_KEY == "no" ]]; then
log "取消删除所有数据"
break
else
log "请输入 yes 以确认清楚删除后果并删除所有数据"
continue
fi
fi
break
done
;;
*)
log "输入错误,请重新选择"
continue
;;
esac
else
log "默认全新安装"
installDocker
installUnzip
installRedis
installMinio
installRabbitMq
installElasticSearch
installPortainer
installOracle
installServerApp
installNginx
fi
break
done
}
# 完全卸载
function unInstallAll() {
log "移除所有容器"
for containerId in $(docker ps -aq); do
docker rm -f "$containerId" &>/dev/null
done
log "移除所有镜像"
for imageId in $(docker images -q); do
docker image rmi -f "$imageId" &>/dev/null
done
log "停止运行 Docker"
systemctl stop docker &>/dev/null
log "移除 Docker 服务"
systemctl disable docker &>/dev/null
log "移除 Docker 基础文件"
rm -f /usr/bin/containerd
rm -f /usr/bin/containerd-shim-runc-v2
rm -f /usr/bin/ctr
rm -f /usr/bin/docker
rm -f /usr/bin/dockerd
rm -f /usr/bin/docker-init
rm -f /usr/bin/docker-proxy
rm -f /usr/bin/runc
log "移除 Docker 服务文件"
rm -f /etc/systemd/system/docker.service
log "移除 Docker daemon.json"
rm -rf /etc/docker
log "卸载 unzip"
rpm -e --nodeps unzip
while true; do
log "警告:再次确认是否已对数据库、minio等数据进行备份,此操作不可逆,执行后无法恢复数据!!!"
log "请输入:yes 或 no"
if read -r CONFIRM_KEY; then
if [[ $CONFIRM_KEY == "yes" ]]; then
log "确认删除所有数据"
# 移除持久化目录下
rm -R -f /dhpeis
elif [[ $CONFIRM_KEY == "no" ]]; then
log "取消删除所有数据"
break
else
log "请输入 yes 以确认清楚删除后果并删除所有数据"
continue
fi
fi
break
done
log "移除所有已放行端口"
firewall-cmd --zone public --permanent --remove-port ALL &>/dev/null
firewall-cmd --reload &>/dev/null
log "卸载完成"
}
# 输入密码
function inputPassword() {
charcount='0'
reply=''
while :; do
char=$(
stty cbreak -echo
dd if=/dev/tty bs=1 count=1 2>/dev/null
stty -cbreak echo
)
case $char in
"$(printenv '\000')")
break
;;
"$(printf '\177')" | "$(printf '\b')")
if [ $charcount -gt 0 ]; then
printf '\b \b'
reply="${reply%?}"
charcount=$((charcount - 1))
else
printf ''
fi
;;
"$(printf '\033')") ;;
*)
printf '*'
reply="${reply}${char}"
charcount=$((charcount + 1))
;;
esac
done
printf '\n' >&2
}
# 公共方法部分 end
# 18、显示安装结果
function showResult() {
log ""
log "=================安装已经完成=================="
log ""
log "请用浏览器访问:"
# log "外网地址: http://$PUBLIC_IP"
log "内网地址: http://$LOCAL_IP"
log ""
log "如果使用的是云服务器,请至安全组开放 80 端口"
log ""
log "为了服务器安全,在离开此界面后将无法再看到所有安装过程日志(含各组件安装详情),请在离开前妥善保管好此脚本所在目录下的 install.log 日志文件。"
log ""
log "==============================================="
}
# 主函数(脚本执行入口)
function main() {
# 校验执行权限
checkRoot
# 设置安装目录
setInstallDir
# 获取 IP
getIp
# 选择安装模式
changeMode
# 卸载不执行
if [ "$CHANGE_MODE" != "99" ]; then
# 设置防火墙
setFirewall
# 显示安装结果
showResult
fi
}
# 运行
main
文章评论