稷然如此

  • 首页
  • 文章分类
    • AI
    • Android
    • Java
    • Shell
    • Vue
    • C#
    • Python
    • 数据库
    • 组件
    • 其他
    • Game
  • 常用命令
    • Docker
    • Git
    • Linux
  • 操作系统
    • CentOS
    • Ubuntu
    • Windows
    • Kylin
  • 工具
    • IntelliJ IDEA
    • Visual Studio Code
稷然如此
不积跬步,无以至千里
  1. 首页
  2. 操作系统
  3. CentOS
  4. 正文

编写 shell 一键安装 Docker 生产环境

2024年11月18日 428点热度 0人点赞
#!/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

 

标签: Docker shell 一键安装
最后更新:2024年11月18日

Akim

犇 骉 Java、C#、Python、Go、Android、MiniProgram、Bootstrap、Vue2

点赞
< 上一篇
下一篇 >

Copyright © 2025 aianran.com All Rights Reserved.

免责申明 | 隐私政策 | 服务条款 | 关于我们

黔ICP备2023008200号-1

贵公网安备 52010202003594号