麒麟OS各种环境安装脚本,达梦数据库DM8、JDK安装、Nginx安装、vsftpd安装、硬盘挂载一件安装脚本

这里的一件安装脚本的前提,是将对应的文件放在对应的目录后,然后给脚本增加权限 chmod +x xxx.sh然后在打开脚本修改里面必须修改点,最后执行脚本,即可一件安装完成,

比如: 达梦数据库 DM8

  1. 将压缩包放在 /dm8 目录下面(目录如果不存在 则创建或者缓存别的目录也可以-对应脚本里面的UNZIP_DIR目录)

  2. 将dm.key 放在DM_KEY_FILE对应的目录下面

  3. 修改脚本里面的INSTALL_DIR数据库存储目录

  4. 配置数据库名称和实列名称

    DB_NAME="DM_DB1" # 数据库名
    INSTANCE_NAME="DM_DB1" # 实例名

  5. 配置数据库各种密码

sh 复制代码
SYSDBA_PWD="DM_DB1SYSDBAr"                     # SYSDBA密码
SYSSSO_PWD="DM_DB1SYSDBA"                    # SYSSSO密码
SYSAUDITOR_PWD="DM_DB1SAUDITOR"              # SYSAUDITOR密码
DM_USER_PWD="DM_DB1_USER_PWD"            # dm操作系统用户密码(可自定义)

6.给脚本增加权限 chmod +x xxx.sh

  1. root用户执行 ./xxx.sh 即可完成安装,最后在试一下远程链接即可

达梦数据库 DM8

sh 复制代码
#!/bin/bash
##############################################################################
# 达梦数据库 DM8 单机模式自动安装脚本(修复点标记版)
# 核心修复:解决"非root用户不自动创建系统服务"问题
# 适用系统:RedHat/CentOS 7.x (x86_64) / 麒麟V10
##############################################################################

# ==================== 基础配置(所有参数可动态调整)====================
# 1. 路径配置(动态注入XML)
UNZIP_DIR="/dm8"                # 解压目录(自动创建)
ZIP_PACKAGE="dm8_20250506_x86_rh7_64.zip"  # 达梦压缩包名称(需与上传文件一致)
ISO_FILE="dm8_20250506_x86_rh7_64.iso"      # 解压后ISO文件名(压缩包内默认名称)
MOUNT_DIR="/mnt"                             # 临时挂载目录(安装后卸载)
INSTALL_DIR="/data/dm"                           # 数据库安装目录(纯安装文件,文档推荐)
DATA_DIR="${INSTALL_DIR}/dmdata"             # 数据文件目录(安装后创建)
DM_USER="dm"                               # 文档规范:数据库专用用户(dmdba)
DM_GROUP="dinstall"                          # 官方强制用户组(文档一致)
DM_KEY_FILE="/opt/dm.key"                    # 授权文件路径(dm.key,无则设为DM_KEY_FILE="")
DB_NAME="DM_DB1"                               # 数据库名
INSTANCE_NAME="DM_DB1"                         # 实例名
PORT_NUM="5236"                              # 数据库端口
ARCHIVE_DIR="${INSTALL_DIR}/dmarch"          # 归档目录(安装后创建)
BACKUP_DIR="${INSTALL_DIR}/dmbak"            # 备份目录(安装后创建)

# 2. XML专用动态配置(严格遵循官方规范)
CASE_SENSITIVE="N"                           # 大小写敏感(对应XML:CASE_SENSITIVE)
CHARSET="1"                                   # 字符集(0=GB18030,1=UTF-8,对应XML:CHARSET)
EXTENT_SIZE="32"                              # 扩展页大小(对应XML:EXTENT_SIZE)
PAGE_SIZE="32"                                 # 页大小(对应XML:PAGE_SIZE)
LOG_SIZE="4096"                                # 日志大小(对应XML:LOG_SIZE)
INIT_DB="Y"                                    # 自动初始化数据库(对应XML:INIT_DB)
ENCRYPT_NAME="AES256_ECB"                     # 加密算法(对应XML:ENCRYPT_NAME)
USE_NEW_HASH="1"                               # 新哈希算法(对应XML:USE_NEW_HASH)

# 3. 密码配置(动态注入XML,符合复杂度要求)
SYSDBA_PWD="DM_DB1SYSDBAr"                     # SYSDBA密码
SYSSSO_PWD="DM_DB1SYSDBA"                    # SYSSSO密码
SYSAUDITOR_PWD="DM_DB1SAUDITOR"              # SYSAUDITOR密码
DM_USER_PWD="DM_DB1_USER_PWD"            # dm操作系统用户密码(可自定义)

# 4. 单机配置(无需主从相关配置)
CURRENT_INSTANCE_NAME="${INSTANCE_NAME}"     # 当前实例名
LOCAL_IP=""                                  # 本机IP
# 【修复点2】新增变量存储dm.ini路径,避免重复计算,方便服务创建
DM_INI_PATH=""                                

# ==================== 工具函数(增强稳定性)====================
log_info() {
    echo -e "\033[32m[INFO] $(date +'%Y-%m-%d %H:%M:%S'): $1\033[0m"
}

log_error() {
    echo -e "\033[31m[ERROR] $(date +'%Y-%m-%d %H:%M:%S'): $1\033[0m"
    exit 1
}

log_warn() {
    echo -e "\033[33m[WARN] $(date +'%Y-%m-%d %H:%M:%S'): $1\033[0m"
}

# 自动获取本机有效IP
get_local_ip() {
    local local_ip=$(ip addr | grep -E 'inet (192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1]))' | grep -v 'docker' | grep -v 'lo' | awk '{print $2}' | cut -d '/' -f 1 | head -n 1)
    if [ -z "${local_ip}" ]; then
        local_ip=$(ip addr | grep 'inet ' | grep -v '127.0.0.1' | grep -v 'docker' | awk '{print $2}' | cut -d '/' -f 1 | head -n 1)
    fi
    if [ -z "${local_ip}" ]; then
        log_warn "无法自动获取本机IP,请手动输入"
        read -p "请输入当前服务器IP:" local_ip
        if ! validate_ip "${local_ip}"; then
            log_error "IP格式非法(示例:192.168.1.100)"
        fi
    fi
    echo "${local_ip}"
}

# 端口占用检查
check_port() {
    local port=$1
    if command -v ss &> /dev/null; then
        ss -tulpn | grep -q ":${port} " && log_error "端口${port}已占用,请更换"
    else
        netstat -tulpn | grep -q ":${port} " && log_error "端口${port}已占用,请更换"
    fi
}

# IP格式校验
validate_ip() {
    local ip=$1
    if ! echo "${ip}" | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}$'; then
        return 1
    fi
    local IFS='.'
    read -r a b c d <<< "${ip}"
    [ $a -ge 0 ] && [ $a -le 255 ] && [ $b -ge 0 ] && [ $b -le 255 ] && [ $c -ge 0 ] && [ $c -le 255 ] && [ $d -ge 0 ] && [ $d -le 255 ]
}

# 目录权限校验
check_dir_perm() {
    local dir=$1
    local expected_owner="${DM_USER}:${DM_GROUP}"
    local expected_perm=$2
    local actual_owner=$(stat -c "%U:%G" "${dir}")
    [ "${actual_owner}" != "${expected_owner}" ] && log_error "目录${dir}所有者错误,需为${expected_owner}"
    local actual_perm=$(stat -c "%a" "${dir}")
    if [ "${actual_perm}" -ne "${expected_perm}" ]; then
        log_warn "目录${dir}权限修正(期望${expected_perm},实际${actual_perm})"
        chmod ${expected_perm} "${dir}"
    fi
}

# 核心修复函数:root用户手动创建系统服务(解决dmdba无权限问题)
# 逻辑:调用达梦官方服务安装脚本,root权限执行,确保服务创建成功
create_system_service() {
    log_info "==================== 【核心修复】root用户创建系统服务 ====================="
    # 1. 校验实例配置文件是否存在(服务创建必需)
    DM_INI_PATH="${DATA_DIR}/${DB_NAME}/dm.ini"
    if [ ! -f "${DM_INI_PATH}" ]; then
        log_error "实例配置文件${DM_INI_PATH}不存在!无法创建服务"
    fi

    # 2. 校验达梦官方服务安装脚本(确保脚本存在)
    local service_script="${INSTALL_DIR}/script/root/dm_service_installer.sh"
    if [ ! -f "${service_script}" ]; then
        log_error "未找到官方服务安装脚本:${service_script}(安装失败)"
    fi

    # 3. 检查服务是否已存在(避免重复创建)
    local service_name="DmService${CURRENT_INSTANCE_NAME}"
    if systemctl list-unit-files | grep -q "${service_name}"; then
        log_info "系统服务${service_name}已存在,跳过创建"
        # 确保服务是启动状态
        systemctl start "${service_name}" >/dev/null 2>&1
        systemctl enable "${service_name}" >/dev/null 2>&1
        log_info "✅ 服务${service_name}已启动并设置开机自启"
        return
    fi

    # 4. 【关键命令】root执行官方脚本创建服务(核心修复逻辑)
    log_info "执行官方脚本创建服务:${service_script}"
    ${service_script} -t dmserver -p "${CURRENT_INSTANCE_NAME}" -dm_ini "${DM_INI_PATH}" || {
        log_error "服务创建失败!请手动执行:${service_script} -t dmserver -p ${CURRENT_INSTANCE_NAME} -dm_ini ${DM_INI_PATH}"
    }

    # 5. 验证服务创建结果
    if ! systemctl list-unit-files | grep -q "${service_name}"; then
        log_error "服务${service_name}创建失败!请检查dm.ini路径和权限"
    fi

    # 6. 设置开机自启并启动服务(满足原需求)
    systemctl enable "${service_name}" >/dev/null 2>&1 || log_warn "服务开机自启设置失败,需手动执行:systemctl enable ${service_name}"
    systemctl start "${service_name}" >/dev/null 2>&1 || {
        log_error "服务${service_name}启动失败!请手动执行:systemctl start ${service_name}"
    }

    # 7. 验证服务状态(确保修复生效)
    sleep 5
    if systemctl is-active --quiet "${service_name}"; then
        log_info "✅ 【修复成功】系统服务${service_name}创建并启动正常(root权限生效)"
    else
        log_error "服务${service_name}创建成功但启动失败!状态:$(systemctl status "${service_name}" | grep Active)"
    fi
}

# ==================== 交互式配置(单机模式简化)====================
interactive_input() {
    log_info "==================== 单机模式配置 ===================="
    
    # 自动获取本机IP
    LOCAL_IP=$(get_local_ip)
    log_info "自动获取本机IP:${LOCAL_IP},实例名:${CURRENT_INSTANCE_NAME}"

    # 配置汇总(标注服务创建方式)
    local charset_desc="GB18030"
    if [ "${CHARSET}" = "1" ]; then
        charset_desc="UTF-8"
    fi

    echo -e "\n=========================================================="
    echo "当前配置汇总(【修复说明】服务将由root用户手动创建):"
    echo "  模式:单机 | 本机IP:${LOCAL_IP}"
    echo "  实例名:${CURRENT_INSTANCE_NAME} | 端口:${PORT_NUM}"
    echo "  安装目录:${INSTALL_DIR} | 数据目录:${DATA_DIR}(安装后创建)"
    echo "  服务名:DmService${CURRENT_INSTANCE_NAME}"
    echo "  操作系统用户:${DM_USER}(密码:${DM_USER_PWD})"
    echo "  大小写敏感:${CASE_SENSITIVE} | 字符集:${charset_desc}"
    echo "=========================================================="
    read -p "配置正确?(y/n):" confirm
    [ "${confirm}" != "y" ] && [ "${confirm}" != "Y" ] && log_info "用户取消安装" && exit 0
}

# ==================== 前置检查(仅创建安装必需目录)====================
pre_check() {
    log_info "==================== 前置检查(仅创建必需目录)====================="
    # 【修复点4】明确要求root用户执行(服务创建必须root权限)
    [ "$(id -u)" -ne 0 ] && log_error "【修复要求】请使用root用户执行脚本(系统服务创建需要root权限)"

    # 2. 仅创建安装必需目录(解压/安装/挂载)
    log_info "创建安装必需目录(解压/安装/挂载)..."
    mkdir -p "${UNZIP_DIR}" "${INSTALL_DIR}" || log_error "创建必需目录失败(root权限不足)"

    # 3. 压缩包存在校验
    [ ! -f "${UNZIP_DIR}/${ZIP_PACKAGE}" ] && log_error "压缩包未找到!请上传${ZIP_PACKAGE}到${UNZIP_DIR}目录"

    # 4. 授权文件校验
    if [ -n "${DM_KEY_FILE}" ] && [ "${DM_KEY_FILE}" != "" ]; then
        log_info "检查授权文件:${DM_KEY_FILE}"
        [ ! -f "${DM_KEY_FILE}" ] && log_error "授权文件dm.key不存在!请核对DM_KEY_FILE参数"
        if echo "${DM_KEY_FILE}" | grep -q "[[:space:]]"; then
            log_error "授权文件路径不能包含空格!请修改DM_KEY_FILE参数"
        fi
        chmod 644 "${DM_KEY_FILE}"
        chown ${DM_USER}:${DM_GROUP} "${DM_KEY_FILE}"
        log_info "✅ 授权文件权限配置完成"
    fi

    # 5. 端口占用检查
    log_info "检查端口可用性..."
    check_port ${PORT_NUM}
    log_info "端口${PORT_NUM}可用"

    # 6. 系统资源检查
    local mem_total=$(free -g | grep Mem | awk '{print $2}')
    [ ${mem_total} -lt 2 ] && log_warn "内存不足2G(官方最低要求),可能导致安装失败"
    local disk_free=$(df -P ${INSTALL_DIR} | grep -v Filesystem | awk '{print $4/1024/1024}')
    [ $(echo "${disk_free} < 10" | bc) -eq 1 ] && log_error "安装目录磁盘空间不足10G(官方最低要求)"
}

# ==================== 基础环境部署(仅授权必需目录)====================
base_deploy() {
    log_info "==================== 基础环境部署(适配目录时机)====================="
    # 1. 创建官方强制用户组
    log_info "创建用户组${DM_GROUP}..."
    if ! grep -q "^${DM_GROUP}:" /etc/group; then
        groupadd "${DM_GROUP}" || log_error "用户组${DM_GROUP}创建失败"
        log_info "✅ 用户组${DM_GROUP}创建成功"
    else
        log_info "用户组${DM_GROUP}已存在,跳过"
    fi

    # 2. 创建数据库专用用户
    log_info "创建用户${DM_USER}..."
    if ! id -u "${DM_USER}" >/dev/null 2>&1; then
        useradd -g "${DM_GROUP}" -m -d "/home/${DM_USER}" -s /bin/bash "${DM_USER}" || log_error "用户${DM_USER}创建失败"
        log_info "✅ 用户${DM_USER}创建成功(用户组:${DM_GROUP})"
    else
        log_info "用户${DM_USER}已存在,跳过"
    fi

    # 3. 设置dmdba用户密码
    echo "${DM_USER_PWD}" | passwd --stdin "${DM_USER}" || log_error "${DM_USER}用户密码设置失败"
    log_info "✅ ${DM_USER}用户密码已设置:${DM_USER_PWD}"
    
    # 4. 配置系统资源限制
    log_info "配置系统资源限制(/etc/security/limits.conf)..."
    if ! grep -q "^${DM_USER} " /etc/security/limits.conf; then
        cat >> /etc/security/limits.conf << EOF
# DM8 资源限制(官方规范)
${DM_USER} soft nofile 65536
${DM_USER} hard nofile 65536
${DM_USER} soft nproc 131072
${DM_USER} hard nproc 131072
EOF
    fi
    su - "${DM_USER}" -c "ulimit -n 65536" >/dev/null 2>&1
    log_info "✅ 系统资源限制配置完成"

    # 5. 解压压缩包
    log_info "解压压缩包${ZIP_PACKAGE}(解压目录:${UNZIP_DIR})..."
    cd "${UNZIP_DIR}" || log_error "切换到解压目录${UNZIP_DIR}失败"
    unzip -o "${ZIP_PACKAGE}" -d "${UNZIP_DIR}" || log_error "压缩包解压失败(可能是包损坏)"
    [ ! -f "${UNZIP_DIR}/${ISO_FILE}" ] && log_error "解压后未找到ISO文件:${UNZIP_DIR}/${ISO_FILE}"
    log_info "✅ 压缩包解压成功(ISO路径:${UNZIP_DIR}/${ISO_FILE})"

    # 6. 挂载ISO文件
    log_info "挂载ISO文件到${MOUNT_DIR}..."
    if mount | grep -q "${MOUNT_DIR}"; then
        umount "${MOUNT_DIR}" >/dev/null 2>&1 || log_error "卸载已挂载的${MOUNT_DIR}失败"
    fi
    mount -o loop "${UNZIP_DIR}/${ISO_FILE}" "${MOUNT_DIR}" || log_error "ISO挂载失败(路径:${UNZIP_DIR}/${ISO_FILE})"
    log_info "✅ ISO文件挂载成功"

    # 7. 仅授权必需目录(安装目录+用户家目录)
    log_info "配置必需目录权限..."
    chown -R "${DM_USER}:${DM_GROUP}" "${INSTALL_DIR}" "/home/${DM_USER}"
    check_dir_perm "${INSTALL_DIR}" 755
    log_info "✅ 必需目录权限配置完成"
}

# ==================== (核心) 数据库安装(安装后创建业务目录)====================
db_install() {
    log_info "==================== 数据库安装(动态XML静默模式)====================="
    # 动态生成XML配置文件(所有参数从基础配置读取,无需手动修改)
    local xml_config="/home/${DM_USER}/dm_install.xml"
    su - "${DM_USER}" -c "cat > ${xml_config} << EOF
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<DATABASE>
    <LANGUAGE>ZH</LANGUAGE>
    <!-- 安装语言:官方强制节点名+取值 -->
    <INSTALLER_LANGUAGE>zh_CN</INSTALLER_LANGUAGE>
    <!-- 时区:官方数字编码(+08:00=中国标准时间,兼容所有版本) -->
    <TIME_ZONE>+08:00</TIME_ZONE>
    <!-- 授权文件路径:动态读取DM_KEY_FILE参数 -->
    <KEY_FILE>${DM_KEY_FILE}</KEY_FILE>
    <!-- 安装类型:官方推荐字符串格式(0 表示安装全部,1 表示安装服务器,2 表示安装客户端。) -->
    <INSTALL_TYPE>0</INSTALL_TYPE>
    <!-- 安装目录:动态读取INSTALL_DIR参数 -->
    <INSTALL_PATH>${INSTALL_DIR}</INSTALL_PATH>
    <!-- 自动初始化数据库:动态读取INIT_DB参数 -->
    <INIT_DB>${INIT_DB}</INIT_DB>
    <!-- 数据库实例参数:动态注入所有配置 -->
    <DB_PARAMS>
        <!-- 数据目录:动态读取DATA_DIR参数 -->
        <PATH>${DATA_DIR}</PATH>
        <!-- 数据库名:动态读取DB_NAME参数 -->
        <DB_NAME>${DB_NAME}</DB_NAME>
        <!-- 实例名:动态读取CURRENT_INSTANCE_NAME -->
        <INSTANCE_NAME>${CURRENT_INSTANCE_NAME}</INSTANCE_NAME>
        <!-- 数据库端口:动态读取PORT_NUM参数 -->
        <PORT_NUM>${PORT_NUM}</PORT_NUM>
        <!-- 日志目录:动态读取LOG_DIR参数(无空节点,避免解析错误) -->
        <LOG_PATHS>
            <LOG_PATH></LOG_PATH>
        </LOG_PATHS>
        <!-- 扩展页大小:动态读取EXTENT_SIZE参数 -->
        <EXTENT_SIZE>${EXTENT_SIZE}</EXTENT_SIZE>
        <!-- 页大小:动态读取PAGE_SIZE参数 -->
        <PAGE_SIZE>${PAGE_SIZE}</PAGE_SIZE>
        <!-- 日志大小:动态读取LOG_SIZE参数 -->
        <!-- <LOG_SIZE>${LOG_SIZE}</LOG_SIZE> -->
        <!-- 大小写敏感:动态读取CASE_SENSITIVE参数 -->
        <CASE_SENSITIVE>${CASE_SENSITIVE}</CASE_SENSITIVE>
        <!-- 字符集:动态读取CHARSET参数 -->
        <CHARSET>${CHARSET}</CHARSET>
        <!-- 新哈希算法:动态读取USE_NEW_HASH参数 -->
        <USE_NEW_HASH>${USE_NEW_HASH}</USE_NEW_HASH>
        <!-- SYSDBA密码:动态读取SYSDBA_PWD参数 -->
        <SYSDBA_PWD>${SYSDBA_PWD}</SYSDBA_PWD>
        <!-- SYSSSO密码:动态读取SYSSSO_PWD参数 -->
        <SYSSSO_PWD>${SYSSSO_PWD}</SYSSSO_PWD>
        <!-- SYSAUDITOR密码:动态读取SYSAUDITOR_PWD参数 -->
        <SYSAUDITOR_PWD>${SYSAUDITOR_PWD}</SYSAUDITOR_PWD>
        <!-- 加密算法:动态读取ENCRYPT_NAME参数 -->
        <ENCRYPT_NAME>${ENCRYPT_NAME}</ENCRYPT_NAME>
    </DB_PARAMS>
    <!-- 自动创建系统服务:动态读取CREATE_DB_SERVICE参数 -->
    <CREATE_DB_SERVICE>N</CREATE_DB_SERVICE>
    <!-- 自动启动系统服务:动态读取STARTUP_DB_SERVICE参数 -->
    <STARTUP_DB_SERVICE>N</STARTUP_DB_SERVICE>
</DATABASE>
EOF"

    # 校验XML配置文件
    if [ ! -f "${xml_config}" ]; then
        log_error "动态XML配置文件创建失败!请检查/home/${DM_USER}目录权限"
    fi
    su - "${DM_USER}" -c "sed -i '/^$/d' ${xml_config}"
    if ! grep -q "<DATABASE>" "${xml_config}"; then
        log_error "XML根节点错误!必须为<DATABASE>(大小写敏感)"
    fi
    log_info "✅ 动态XML配置文件创建完成(已关闭安装阶段自动创建服务)"

    # 执行静默安装(dmdba用户,仅安装软件和初始化实例)
    log_info "执行XML静默安装(不创建服务,后续root创建)..."
    su - "${DM_USER}" -c "cd ${MOUNT_DIR} && ./DMInstall.bin -q ${xml_config}" || {
        umount "${MOUNT_DIR}"
        log_error "安装失败!可能原因:1.XML格式错误;2.内存不足;3.安装包损坏;4.授权文件无效"
    }
    log_info "✅ 数据库软件安装+实例初始化成功"

    # 执行官方root收尾脚本
    log_info "执行官方root收尾脚本..."
    local root_script="${INSTALL_DIR}/script/root/root_installer.sh"
    [ ! -f "${root_script}" ] && {
        umount "${MOUNT_DIR}"
        log_error "未找到官方收尾脚本:${root_script}(安装失败)"
    }
    ${root_script} || {
        umount "${MOUNT_DIR}"
        log_error "root收尾脚本执行失败,请手动执行:${root_script}"
    }
    log_info "✅ root收尾脚本执行成功"

    # 安装后创建所有业务目录(数据/日志/归档/备份)
    log_info "安装后创建业务目录(数据/日志/归档/备份)..."
    mkdir -p "${DATA_DIR}"  "${ARCHIVE_DIR}" "${BACKUP_DIR}" || log_error "创建业务目录失败"
    # 授权业务目录(统一授权给dmdba:dinstall)
    chown -R "${DM_USER}:${DM_GROUP}" "${DATA_DIR}" "${ARCHIVE_DIR}" "${BACKUP_DIR}"
    check_dir_perm "${DATA_DIR}" 700   # 数据目录仅dmdba访问(安全要求)
    check_dir_perm "${ARCHIVE_DIR}" 755
    check_dir_perm "${BACKUP_DIR}" 755
    log_info "✅ 业务目录创建+授权完成"

    # 配置dmdba用户环境变量
    log_info "配置${DM_USER}用户环境变量..."
    if ! grep -q "export DM_HOME=" "/home/${DM_USER}/.bashrc"; then
        cat >> "/home/${DM_USER}/.bashrc" << EOF
# DM8 环境变量(动态配置)
export DM_HOME=${INSTALL_DIR}
export PATH=\$DM_HOME/bin:\$DM_HOME/tool:\$PATH
export LD_LIBRARY_PATH=\$DM_HOME/bin:\$LD_LIBRARY_PATH
EOF
    fi
    source "/home/${DM_USER}/.bashrc"
    log_info "✅ 环境变量配置完成"

    # 清理临时XML配置文件
    su - "${DM_USER}" -c "rm -f ${xml_config}"
    log_info "✅ 临时XML配置文件已清理"

    # 【修复点5】调用核心修复函数:root创建系统服务(安装后立即执行)
    create_system_service
}

# ==================== 单机数据库配置(简化配置)====================
single_config() {
    log_info "==================== 单机数据库配置 ===================="
    # 校验实例配置文件
    if [ ! -f "${DM_INI_PATH}" ]; then
        DM_INI_PATH="${DATA_DIR}/${DB_NAME}/dm.ini"
        [ ! -f "${DM_INI_PATH}" ] && log_error "数据库实例未初始化!请检查XML配置中INIT_DB是否为Y"
    fi

    # 配置本地归档(使用安装后创建的归档目录)
    local ARCHIVE_CONFIG="${DATA_DIR}/${DB_NAME}/dmarch.ini"
    su - "${DM_USER}" -c "cat > ${ARCHIVE_CONFIG} << EOF
[ARCHIVE1]
ARCH_TYPE = LOCAL
ARCH_DEST = ${ARCHIVE_DIR}
ARCH_FILE_SIZE = 1024
ARCH_SPACE_LIMIT = 0
EOF"

    # 修改dm.ini启用归档
    su - "${DM_USER}" -c "sed -i 's/^ARCH_INI=.*/ARCH_INI=1/' ${DM_INI_PATH}"
    su - "${DM_USER}" -c "sed -i 's/^INSTANCE_NAME=.*/INSTANCE_NAME=${CURRENT_INSTANCE_NAME}/' ${DM_INI_PATH}"

    # 【修复点6】使用创建好的系统服务重启(而非手动启动dmserver)
    log_info "重启数据库服务..."
    local service_name="DmService${CURRENT_INSTANCE_NAME}"
    systemctl restart "${service_name}" >/dev/null 2>&1 || log_error "数据库服务重启失败!手动执行:systemctl restart ${service_name}"
    sleep 15

    log_info "✅ 单机数据库配置完成"
}

# ==================== 防火墙配置(开放数据库端口)====================
firewall_config() {
    log_info "配置防火墙规则(开放数据库端口)..."
    if command -v firewall-cmd &> /dev/null; then
        firewall-cmd --permanent --add-port=${PORT_NUM}/tcp
        firewall-cmd --reload
    elif command -v iptables &> /dev/null; then
        iptables -I INPUT -p tcp --dport ${PORT_NUM} -j ACCEPT
        service iptables save >/dev/null 2>&1
    else
        log_warn "未检测到防火墙工具,已跳过端口开放(需手动配置)"
    fi
    log_info "✅ 防火墙配置完成"
}

# ==================== 清理与验证(适配业务目录)====================
cleanup_verify() {
    # 卸载ISO挂载
    umount "${MOUNT_DIR}" >/dev/null 2>&1
    log_info "临时文件清理完成"

    # 验证安装结果
    log_info "==================== 安装结果验证 ===================="
    local service_name="DmService${CURRENT_INSTANCE_NAME}"

    # 【修复点8】新增服务状态验证(确保修复生效)
    log_info "【修复验证】服务状态检查:${service_name}"
    if systemctl is-active --quiet "${service_name}"; then
        log_info "✅ 【修复成功】服务${service_name}运行正常"
    else
        log_error "【修复失败】服务${service_name}未运行!状态:$(systemctl status "${service_name}" | grep Active)"
    fi

    # 验证数据库连接
    log_info "单机数据库连接验证(IP:${LOCAL_IP},实例名:${CURRENT_INSTANCE_NAME})..."
    su - "${DM_USER}" -c "disql SYSDBA/${SYSDBA_PWD}@${LOCAL_IP}:${PORT_NUM} << EOF
SELECT INSTANCE_NAME, STATUS\$ FROM V\$INSTANCE;
SELECT ARCH_TYPE, ARCH_DEST, STATUS FROM V\$ARCHIVE_DEST;
COMMIT;
EXIT;
EOF"

    # 验证标准提示
    log_info "✅ 单机数据库验证完成:STATUS$ 显示 OPEN 即为正常"
}

# ==================== 主流程(完整闭环)====================
main() {
    log_info "==================== 达梦DM8单机模式安装脚本(修复点标记版)====================="
    log_info "【修复说明】核心解决:dmdba用户无权限创建系统服务,改为root手动调用官方脚本创建"
    interactive_input  # 配置确认+动态参数汇总
    pre_check          # 前置检查(要求root用户)
    base_deploy        # 基础环境(用户+依赖+必需目录授权)
    db_install         # 安装+业务目录创建+调用修复函数创建服务
    firewall_config    # 防火墙配置
    single_config      # 单机数据库配置(使用系统服务重启)
    cleanup_verify     # 清理+服务状态验证(确认修复生效)

    # 安装完成提示(标注修复结果)
    log_info "=========================================================="
    log_info "🎉🎉 达梦DM8 单机数据库安装完成!【修复结果】系统服务创建成功"
    log_info "核心信息:"
    log_info "  实例名:${CURRENT_INSTANCE_NAME} | 数据库名:${DB_NAME}"
    log_info "  连接地址:${LOCAL_IP}:${PORT_NUM}"
    log_info "  SYSDBA密码:${SYSDBA_PWD}"
    log_info "  系统服务:${service_name}(root创建,已启动+开机自启)"
    log_info "  服务管理:systemctl [start/stop/status] ${service_name}"
    log_info "  业务目录(安装后创建):数据=${DATA_DIR} | 归档=${ARCHIVE_DIR}"
    log_info "  解压目录(可删除):${UNZIP_DIR}"
    log_info "=========================================================="
}

# 执行主流程
main

JDK1.8

sh 复制代码
#!/bin/bash
##############################################################################
# 脚本名称:kylin10-install-openjdk8.sh
# 适用系统:麒麟OS 10(x86_64架构)
# 安装内容:java-1.8.0-openjdk.x86_64 + 环境变量配置
# 执行方式:sudo bash 脚本名.sh 或 chmod +x 脚本名.sh && sudo ./脚本名.sh
##############################################################################

# 颜色定义(美化输出)
RED="\033[31m"
GREEN="\033[32m"
YELLOW="\033[33m"
RESET="\033[0m"

# 1. 检查是否以root权限运行(yum安装需root)
if [ "$(id -u)" -ne 0 ]; then
    echo -e "${RED}[错误] 请使用root权限运行脚本(sudo ./脚本名.sh)${RESET}"
    exit 1
fi

echo -e "${YELLOW}[步骤1/3] 正在安装java-1.8.0-openjdk.x86_64(可查看实时进度)...${RESET}"
# 2. 直接安装指定版本OpenJDK(含开发工具包)- 保留输出,显示进度
yum install -y java-1.8.0-openjdk.x86_64 java-1.8.0-openjdk-devel.x86_64
if [ $? -ne 0 ]; then
    echo -e "${RED}[错误] OpenJDK安装失败!可能原因:${RESET}"
    echo -e "${RED}1. yum源未包含该包(java-1.8.0-openjdk.x86_64)${RESET}"
    echo -e "${RED}2. 网络无法连接yum源${RESET}"
    echo -e "${RED}3. 依赖包缺失${RESET}"
    exit 1
fi

echo -e "\n${YELLOW}[步骤2/3] 正在配置环境变量...${RESET}"
# 3. 自动查找JAVA_HOME路径(OpenJDK默认安装在/usr/lib/jvm/下)
JAVA_HOME=$(find /usr/lib/jvm -name "java-1.8.0-openjdk-*" 2>/dev/null | head -n 1)
if [ -z "$JAVA_HOME" ]; then
    JAVA_HOME=$(find /usr/lib/jvm -name "jre-1.8.0-openjdk-*" 2>/dev/null | head -n 1)
fi

# 验证JAVA_HOME是否存在
if [ -z "$JAVA_HOME" ]; then
    echo -e "${RED}[错误] 未找到JAVA_HOME路径,安装可能不完整${RESET}"
    exit 1
fi

# 配置环境变量(写入/etc/profile,所有用户永久生效)
ENV_CONFIG="
# Java 1.8.0 OpenJDK 环境变量
export JAVA_HOME=$JAVA_HOME
export JRE_HOME=\$JAVA_HOME/jre
export CLASSPATH=.:\$JAVA_HOME/lib:\$JRE_HOME/lib
export PATH=\$JAVA_HOME/bin:\$PATH
"

# 检查环境变量是否已存在,避免重复配置
if ! grep -q "JAVA_HOME=$JAVA_HOME" /etc/profile; then
    echo "$ENV_CONFIG" >> /etc/profile
    echo -e "${GREEN}[成功] 环境变量已写入/etc/profile${RESET}"
else
    echo -e "${YELLOW}[提示] 环境变量已存在,无需重复配置${RESET}"
fi

# 立即加载环境变量(当前终端生效)
source /etc/profile > /dev/null 2>&1

echo -e "\n${YELLOW}[步骤3/3] 正在验证安装结果...${RESET}"
# 4. 验证Java版本(显示完整版本信息)
echo -e "${YELLOW}[信息] Java版本信息:${RESET}"
java -version 2>&1
echo -e "${YELLOW}[信息] Javac版本信息:${RESET}"
javac -version 2>&1

# 二次验证版本号是否为1.8.x
JAVA_VERSION=$(java -version 2>&1 | grep "1.8.0_" | awk '{print $3}' | sed 's/"//g')
if [ -n "$JAVA_VERSION" ]; then
    echo -e "\n${GREEN}[成功] Java 1.8.0 OpenJDK 安装完成!${RESET}"
    echo -e "${GREEN}=====================================${RESET}"
    echo -e "${GREEN}JAVA_HOME: $JAVA_HOME${RESET}"
    echo -e "${GREEN}Java 版本: $JAVA_VERSION${RESET}"
    echo -e "${GREEN}=====================================${RESET}"
    echo -e "${YELLOW}[提示] 新终端会自动加载环境变量,当前终端已生效${RESET}"
else
    echo -e "\n${RED}[错误] Java版本验证失败,请手动执行 source /etc/profile 后重试java -version${RESET}"
    exit 1
fi

Nginx

sh 复制代码
#!/bin/bash
##############################################################################
# 麒麟OS 单机Nginx一键安装脚本(保留原有配置 + .pfx证书版)
# 核心特性:pfx自动转key/crt + Nginx yum安装 + 动态负载均衡 + 防火墙放行
# 适用场景:单机部署,无需双机高可用,支持逗号分割配置多后端节点
##############################################################################

# 检查是否以root用户执行
if [ "$(id -u)" -ne 0 ]; then
    echo "错误:请使用root用户执行脚本(sudo -i 切换后再运行)"
    exit 1
fi

# ====================== 核心配置项(重点:负载均衡地址支持逗号分割)======================
# 1. 网络相关(仅保留单机必要配置)
NET_INTERFACE="enp4s1"        # 服务器网卡名称(通过 ip addr 查看,无需修改)
DOMAIN="nginx"  # 你的域名(原VIP对应的域名,直接作为访问域名)

# 2. SSL证书配置(保留你的原有PFX配置,自动转换)
PFX_FILE_PATH="/root/ssl/CN=nginx-p.pfx"  # 你的.pfx证书文件路径(无需修改)
PFX_PASSWORD="r#1234"           # 你的.pfx证书密码(无需修改,含特殊字符已保留)
SSL_NGINX_DIR="/etc/nginx/ssl"          # 转换后key/crt存放目录(无需修改)

# 3. Nginx端口配置(保留原有端口)
HTTP_PORT="80"                               # HTTP端口(自动跳转HTTPS,无需修改)
HTTPS_PORT="443"                              # HTTPS端口(无需修改)

# 4. 动态负载均衡配置(核心变更:逗号分割多后端地址,格式:IP:端口,IP:端口)
BACKEND_SERVERS="10.xx.xxx.1:8080,10.xx.xxx.2:8080"  # 负载均衡节点(支持任意多个,逗号分隔)
LOAD_BALANCE_STRATEGY="ip_hash"  # 负载均衡策略(默认ip_hash,可选:round_robin/least_conn/ip_hash)
# ============================================================================

# 输出日志函数
info() {
    echo -e "\033[32m[INFO] $1\033[0m"
}

error() {
    echo -e "\033[31m[ERROR] $1\033[0m"
    exit 1
}

# 预检查:验证单机必要配置和依赖(新增负载均衡节点格式校验)
pre_check() {
    info "开始预检查..."
    # 检查.pfx证书文件(保留原有路径验证)
    if [ ! -f "${PFX_FILE_PATH}" ]; then
        error "pfx证书文件不存在:${PFX_FILE_PATH},请确认文件路径正确"
    fi
    # 检查openssl工具(证书转换必需)
    if ! command -v openssl &> /dev/null; then
        info "未找到openssl工具,正在自动安装..."
        yum install -y openssl &> /dev/null || error "openssl安装失败,请手动执行:yum install -y openssl"
    fi
    # 检查网卡(保留原有网卡验证)
    if ! ip link show "${NET_INTERFACE}" > /dev/null 2>&1; then
        error "网卡 ${NET_INTERFACE} 不存在!通过 ip addr 查看实际网卡名称"
    fi
    # 检查端口是否被占用
    if netstat -tulpn | grep -q ":${HTTP_PORT}"; then
        OCCUPY_PROC=$(netstat -tulpn | grep ":${HTTP_PORT}" | awk '{print $7}')
        error "HTTP端口 ${HTTP_PORT} 已被占用!占用进程:${OCCUPY_PROC},请停止进程或修改端口"
    fi
    if netstat -tulpn | grep -q ":${HTTPS_PORT}"; then
        OCCUPY_PROC=$(netstat -tulpn | grep ":${HTTPS_PORT}" | awk '{print $7}')
        error "HTTPS端口 ${HTTPS_PORT} 已被占用!占用进程:${OCCUPY_PROC},请停止进程或修改端口"
    fi
    # 检查yum源可用性
    if ! yum repolist &> /dev/null; then
        error "yum源不可用,请先修复麒麟OS的yum配置(本地源/官方源)"
    fi
    # 卸载旧版本Nginx(避免冲突)
    if command -v nginx &> /dev/null; then
        info "检测到已安装Nginx,正在卸载旧版本..."
        systemctl stop nginx &> /dev/null
        yum remove -y nginx &> /dev/null || error "旧版本Nginx卸载失败,请手动执行:yum remove -y nginx"
    fi
    # 新增:校验负载均衡节点格式(必须是 IP:端口 格式,逗号分割,自动去除空格)
    if [ -z "${BACKEND_SERVERS}" ]; then
        error "负载均衡节点配置为空!请在 BACKEND_SERVERS 中填写至少一个节点(格式:IP:端口)"
    fi
    # 去除节点前后空格,再分割
    CLEAN_SERVERS=$(echo "${BACKEND_SERVERS}" | tr -d ' ')
    IFS=',' read -ra SERVERS <<< "${CLEAN_SERVERS}"
    for server in "${SERVERS[@]}"; do
        if ! echo "${server}" | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:[0-9]+$' &> /dev/null; then
            error "负载均衡节点格式错误:${server},正确格式:IP:端口(示例:10.125.179.1:8080),请检查是否有多余字符"
        fi
    done
    # 校验负载均衡策略合法性
    if [[ ! "${LOAD_BALANCE_STRATEGY}" =~ ^(round_robin|least_conn|ip_hash)$ ]]; then
        error "负载均衡策略错误:${LOAD_BALANCE_STRATEGY},可选值:round_robin/least_conn/ip_hash"
    fi
    info "预检查通过"
}

# 1. 安装核心依赖(仅保留单机必需组件,去掉Keepalived)
install_deps() {
    info "安装核心依赖..."
    yum install -y nginx firewalld net-tools openssl openssl-devel pcre-devel zlib-devel || {
        error "依赖安装失败,请检查yum源"
    }
    info "依赖安装完成"
}

# 2. pfx证书自动转换(完全保留原有逻辑,无需修改)
convert_pfx_to_key_crt() {
    info "开始转换.pfx证书为key/crt格式..."
    # 创建证书目录(权限700,仅root可访问)
    mkdir -p ${SSL_NGINX_DIR} || error "创建证书目录失败"
    chmod 700 ${SSL_NGINX_DIR}

    # 定义转换后的文件路径
    KEY_FILE="${SSL_NGINX_DIR}/server.key"  # 私钥
    CRT_FILE="${SSL_NGINX_DIR}/server.crt"  # 公钥证书

    # 1. 从pfx提取私钥(保留密码处理逻辑)
    info "提取私钥(server.key)..."
    if [ -z "${PFX_PASSWORD}" ]; then
        openssl pkcs12 -in "${PFX_FILE_PATH}" -nocerts -nodes -out "${KEY_FILE}" -passin pass: || {
            error "私钥提取失败,请检查pfx文件是否无密码"
        }
    else
        openssl pkcs12 -in "${PFX_FILE_PATH}" -nocerts -nodes -out "${KEY_FILE}" -passin pass:"${PFX_PASSWORD}" || {
            error "私钥提取失败,请检查pfx密码是否正确(当前密码:${PFX_PASSWORD})"
        }
    fi

    # 2. 从pfx提取公钥证书
    info "提取证书(server.crt)..."
    if [ -z "${PFX_PASSWORD}" ]; then
        openssl pkcs12 -in "${PFX_FILE_PATH}" -nokeys -out "${CRT_FILE}" -passin pass: || {
            error "证书提取失败"
        }
    else
        openssl pkcs12 -in "${PFX_FILE_PATH}" -nokeys -out "${CRT_FILE}" -passin pass:"${PFX_PASSWORD}" || {
            error "证书提取失败,请检查pfx密码是否正确"
        }
    fi

    # 3. 清理证书格式(确保Nginx识别)
    sed -i '/Bag Attributes/d' "${CRT_FILE}"
    sed -i '/localKeyID/d' "${CRT_FILE}"
    sed -i '/^$/d' "${CRT_FILE}"

    # 4. 严格限制文件权限(安全最佳实践)
    chmod 600 "${KEY_FILE}" "${CRT_FILE}"
    chown root:root "${KEY_FILE}" "${CRT_FILE}"

    # 验证转换结果
    if [ -f "${KEY_FILE}" ] && [ -f "${CRT_FILE}" ]; then
        info "pfx证书转换成功!"
        info "私钥:${KEY_FILE}"
        info "证书:${CRT_FILE}"
    else
        error "pfx证书转换失败,请检查pfx文件完整性和密码"
    fi
}

# 3. 配置Nginx(修复动态生成upstream的语法问题)
config_nginx() {
    info "配置Nginx(动态生成负载均衡配置)..."
    NGINX_CONF="/etc/nginx/nginx.conf"
    DEFAULT_CONF="/etc/nginx/conf.d/default.conf"
    
    # 备份默认配置
    [ -f "${DEFAULT_CONF}" ] && mv -f "${DEFAULT_CONF}" "${DEFAULT_CONF}.bak.$(date +%Y%m%d%H%M%S)"
    [ -f "${NGINX_CONF}" ] && mv -f "${NGINX_CONF}" "${NGINX_CONF}.bak.$(date +%Y%m%d%H%M%S)"
    
    # 生成主配置(保留原有主配置逻辑,未修改)
    cat > "${NGINX_CONF}" << EOF
user  nginx;
worker_processes  auto;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '\$remote_addr - \$remote_user [\$time_local] "\$request" '
                      '\$status \$body_bytes_sent "\$http_referer" '
                      '"\$http_user_agent" "\$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;
    keepalive_timeout  65;
    gzip  on;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_comp_level  6;
    gzip_types       text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    include /etc/nginx/conf.d/*.conf;
}
EOF

    # 初始化站点配置文件(先写入HTTP跳转和upstream开头)
    cat > "${DEFAULT_CONF}" << EOF
# HTTP强制跳转HTTPS
server {
    listen       ${HTTP_PORT};
    server_name  nginx-p www.nginx-p;
    access_log  /var/log/nginx/http_access.log  main;
    return 301 https://\$host\$request_uri;
}

# 动态负载均衡配置(从BACKEND_SERVERS变量生成,策略:${LOAD_BALANCE_STRATEGY})
upstream nginx_servers {
    ${LOAD_BALANCE_STRATEGY};
EOF

    # 核心逻辑:解析逗号分割的后端节点(自动去除空格),动态添加到upstream
    CLEAN_SERVERS=$(echo "${BACKEND_SERVERS}" | tr -d ' ')  # 去除所有空格,避免配置错误
    IFS=',' read -ra SERVERS <<< "${CLEAN_SERVERS}"
    for server in "${SERVERS[@]}"; do
        # 确保每个server节点格式正确,结尾加分号
        echo "    server ${server};" >> "${DEFAULT_CONF}"
    done

    # 继续拼接剩余配置(完全保留原有内容,无修改)
    cat >> "${DEFAULT_CONF}" << EOF
}

# HTTPS核心配置(保留原有SSL优化参数)
server {
    listen       ${HTTPS_PORT} ssl http2;
    server_name  nginx-p www.nginx-p;

    # 引用转换后的证书和私钥(路径不变)
    ssl_certificate      ${SSL_NGINX_DIR}/server.crt;
    ssl_certificate_key  ${SSL_NGINX_DIR}/server.key;

    # SSL安全优化(保留原有配置)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
 #   resolver 8.8.8.8 114.114.114.114 valid=300s;
 #   resolver_timeout 5s;

    # 网站根目录(保留原有路径)
    root   /usr/share/nginx/html;
    index  index.html index.htm;

    # 静态资源访问(保留原有逻辑)
    location / {
        try_files \$uri \$uri/ /index.html;
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    }

    # API请求转发到后端服务器(保留你的原有配置,目标指向动态upstream)
    location /server-api {
        proxy_connect_timeout   6;
        proxy_send_timeout      60;
        proxy_read_timeout      60;
        proxy_set_header  X-Real-IP  \$remote_addr;
        proxy_set_header  Client-IP  \$remote_addr;
        proxy_set_header        Host \$http_host;
        proxy_set_header        X-Forwarded-For \$proxy_add_x_forwarded_for;
        client_max_body_size  500m;
        proxy_pass http://nginx_servers;
        proxy_redirect          default;
    }
    
    # API请求转发到后端服务器(保留你的原有注释,按需启用)
    location /api/ {
        rewrite ^/api/(.*) /\$1 break;
        # proxy_pass ;
        # proxy_set_header Host \$host;
        # proxy_set_header X-Real-IP \$remote_addr;
        # proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        # proxy_set_header X-Forwarded-Proto \$scheme;
    }

    # 错误页面(保留原有配置)
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
EOF

    # 验证Nginx配置语法(关键步骤,避免配置错误)
    nginx -t || error "Nginx配置语法错误!请检查证书路径或负载均衡节点配置(错误日志:/var/log/nginx/error.log)"
    info "Nginx配置完成(动态负载均衡节点已生成)"
}

# 4. 配置防火墙(放行80/443端口,去掉VRRP协议)
config_firewall() {
    info "配置防火墙(放行HTTP/HTTPS端口)..."
    # 启动并设置防火墙开机自启
    systemctl start firewalld &> /dev/null
    systemctl enable firewalld &> /dev/null

    # 放行HTTP/HTTPS端口(永久生效)
    firewall-cmd --permanent --add-port=${HTTP_PORT}/tcp &> /dev/null
    firewall-cmd --permanent --add-port=${HTTPS_PORT}/tcp &> /dev/null
    firewall-cmd --reload &> /dev/null

    # 永久关闭SELinux(避免拦截Nginx访问)
    setenforce 0 &> /dev/null
    sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

    info "防火墙配置完成(已放行 ${HTTP_PORT}/${HTTPS_PORT} 端口)"
}

# 5. 启动Nginx并设置开机自启(仅启动Nginx,去掉Keepalived)
start_nginx() {
    info "启动Nginx并设置开机自启..."
    systemctl enable --now nginx || {
        error "Nginx启动失败!查看详细日志:journalctl -u nginx -f"
    }

    # 验证启动状态
    sleep 2
    if systemctl is-active --quiet nginx; then
        info "Nginx启动成功(运行中)"
    else
        error "Nginx启动异常停止!查看错误日志:/var/log/nginx/error.log"
    fi
}

# 6. 验证安装结果(更新负载均衡信息展示)
verify_install() {
    info "======================================"
    echo -e "\033[32m[SUCCESS] 单机Nginx安装完成!\033[0m"
    echo -e "======================================"
    echo -e "📌 核心信息:"
    echo -e "   Nginx版本:$(nginx -v 2>&1 | awk -F '/' '{print $2}')"
    echo -e "   访问域名:https://nginx-p"
    echo -e "   配置文件:/etc/nginx/conf.d/default.conf"
    echo -e "   证书目录:${SSL_NGINX_DIR}"
    echo -e "   网站根目录:/usr/share/nginx/html"
    echo -e "   负载均衡策略:${LOAD_BALANCE_STRATEGY}"
    echo -e "   后端节点:${BACKEND_SERVERS}"
    echo -e "   转发规则:/server-api → http://nginx_servers"
    echo -e "\n📌 常用命令:"
    echo -e "   启动:systemctl start nginx"
    echo -e "   停止:systemctl stop nginx"
    echo -e "   重启:systemctl restart nginx"
    echo -e "   查看日志:tail -f /var/log/nginx/access.log"
    echo -e "   验证配置:nginx -t"
    echo -e "======================================"
}

# 主执行流程(去掉双机相关步骤,仅保留单机流程)
main() {
    pre_check
    install_deps
    convert_pfx_to_key_crt
    config_nginx
    config_firewall
    start_nginx
    verify_install

    # 跳转到Nginx配置目录,方便后续修改
    cd /etc/nginx || error "切换到配置目录失败"
    echo -e "\033[33m当前目录:$(pwd)(Nginx核心配置目录)\033[0m"
}

# 执行主流程
main

Nginx config配置,带负载均衡

lua 复制代码
# HTTP强制跳转HTTPS
server {
    listen       80;
    server_name  xxx www.xxx;
    access_log  /var/log/nginx/http_access.log  main;
    return 301 https://$host$request_uri;
}
  upstream remote_servers {
        server 127.0.0.1:8080;
        server 127.0.0.2:8080;
        server 127.0.0.3:8080;
            ip_hash;
    }
# HTTPS核心配置
server {
    listen       443 ssl http2;
    server_name  xxx www.xxx;

    # 引用转换后的证书和私钥
    ssl_certificate      /etc/nginx/ssl/server.crt;
    ssl_certificate_key  /etc/nginx/ssl/server.key;

    # SSL安全优化
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets off;
#    ssl_stapling on;
#    ssl_stapling_verify on;
 #   resolver 8.8.8.8 114.114.114.114 valid=300s;
 #   resolver_timeout 5s;

    # 网站根目录
    root   /usr/share/nginx/html;
    index  index.html index.htm;

    # 静态资源访问
    location / {
        try_files $uri $uri/ /index.html;
        add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    }

    # API请求转发到后端服务器(目标已替换为变量)
    location /server-api {
       proxy_connect_timeout   6;
        proxy_send_timeout      60;
        proxy_read_timeout      60;
        proxy_set_header  X-Real-IP  $remote_addr;
        proxy_set_header  Client-IP  $remote_addr;
        proxy_set_header        Host $http_host;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        client_max_body_size  500m;
        proxy_pass http://remote_servers;
        proxy_redirect          default;
    }

    # API请求转发到后端服务器(按需启用)
    location /api/ {
        rewrite ^/api/(.*) /$1 break;
        # proxy_pass ;
        # proxy_set_header Host $host;
        # proxy_set_header X-Real-IP $remote_addr;
        # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        # proxy_set_header X-Forwarded-Proto $scheme;
    }

    # 错误页面
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}

vsftpd 存储

sh 复制代码
#!/bin/bash
###########################################################################
# 脚本功能:银河麒麟V10 一键搭建 vsftpd 服务(存储目录:/data/ftproot/[用户名])
# 依赖环境:root权限 + 网络通畅(yum安装依赖)
# 使用说明:1. 修改脚本内自定义配置区;2. chmod +x vsftpd_install.sh;3. ./vsftpd_install.sh
# 核心特性:自定义用户名密码+无交互+用户重建+鉴权修复(彻底解决530错误)
###########################################################################

# ===================== 自定义配置区(必须修改后执行!)=====================
FTP_USER="ftpuser"        # 自定义FTP用户名(字母、数字、下划线、短横线,3-32位)
FTP_PASS="FTP_PASS12312"     # 自定义FTP密码(≥6位,字母+数字+特殊字符,符合系统策略)
FTP_ROOT="/data/ftproot"  # 存储根目录(最终用户目录:/data/ftproot/$FTP_USER)
# =========================================================================

# 检查是否为root用户
if [ "$(id -u)" -ne 0 ]; then
    echo "ERROR:必须使用 root 用户执行!"
    exit 1
fi

# 验证用户名合法性
if ! echo "$FTP_USER" | grep -qE '^[a-zA-Z0-9_-]{3,32}$'; then
    echo "ERROR:用户名不合法!仅支持字母、数字、下划线、短横线,长度3-32位"
    exit 1
fi

# 验证密码合法性
if [ ${#FTP_PASS} -lt 6 ] || ! echo "$FTP_PASS" | grep -qE '[a-zA-Z]' || ! echo "$FTP_PASS" | grep -qE '[0-9]'; then
    echo "ERROR:密码不合法!建议≥6位,必须包含字母+数字,可选特殊字符(如FtP@123456)"
    exit 1
fi

# 最终用户存储目录
FTP_USER_DIR="$FTP_ROOT/$FTP_USER"

echo "========================================"
echo "开始部署 vsftpd 服务"
echo "FTP用户名:$FTP_USER"
echo "存储目录:$FTP_USER_DIR"
echo "========================================"

# 第一步:安装 vsftpd 并启动服务
echo -e "\n1/7 安装 vsftpd 服务..."
if rpm -q vsftpd &> /dev/null; then
    echo "vsftpd 已安装,跳过安装步骤"
else
    yum install -y vsftpd || { echo "ERROR:vsftpd 安装失败!"; exit 1; }
fi

# 设置开机自启并启动服务
echo "启动 vsftpd 服务..."
systemctl enable --now vsftpd &> /dev/null
if systemctl is-active vsftpd &> /dev/null; then
    echo "vsftpd 服务启动成功"
else
    echo "ERROR:vsftpd 服务启动失败!"
    exit 1
fi

# 第二步:配置防火墙
echo -e "\n2/7 配置防火墙规则..."
firewall-cmd --permanent --zone=public --add-service=ftp &> /dev/null
firewall-cmd --permanent --zone=public --add-port=30000-31000/tcp &> /dev/null
firewall-cmd --reload &> /dev/null
echo "防火墙配置完成(放行21端口 + 30000-31000端口)"

# 第三步:删除已存在用户+清理目录
echo -e "\n3/7 处理FTP用户和存储目录..."
if id -u "$FTP_USER" &> /dev/null; then
    echo "用户 $FTP_USER 已存在,正在删除旧用户及数据..."
    userdel -rf "$FTP_USER" &> /dev/null
    rm -rf "$FTP_USER_DIR" &> /dev/null
    echo "旧用户 $FTP_USER 及关联数据删除完成"
fi

# 重新创建存储目录
mkdir -p "$FTP_USER_DIR" &> /dev/null
echo "存储目录创建完成:$FTP_USER_DIR"

# 重新创建FTP用户(nologin shell)
useradd -d "$FTP_USER_DIR" -g ftp -s /sbin/nologin "$FTP_USER" &> /dev/null
if [ $? -eq 0 ]; then
    echo "FTP用户 $FTP_USER 重新创建完成"
else
    echo "ERROR:创建用户 $FTP_USER 失败!"
    exit 1
fi

# 设置目录权限
chown -R "$FTP_USER:ftp" "$FTP_USER_DIR" &> /dev/null
chmod -R 775 "$FTP_USER_DIR" &> /dev/null
echo "目录权限配置完成($FTP_USER 用户拥有读写权限)"

# 第四步:设置FTP用户密码(无交互)
echo -e "\n4/7 设置 FTP 用户密码..."
echo "$FTP_USER:$FTP_PASS" | chpasswd &> /dev/null
if [ $? -eq 0 ]; then
    echo "密码设置成功(密码:$FTP_PASS)"
else
    echo "ERROR:密码设置失败!请检查密码是否符合系统密码策略"
    exit 1
fi

# 第五步:修改 vsftpd 核心配置
echo -e "\n5/7 配置 vsftpd 服务参数..."
if [ ! -f /etc/vsftpd/vsftpd.conf.bak ]; then
    cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bak &> /dev/null
    echo "原配置文件备份:/etc/vsftpd/vsftpd.conf.bak"
fi

# 核心配置(禁止匿名+被动模式+允许本地用户登录)
sed -i \
    -e 's/^anonymous_enable=YES/anonymous_enable=NO/' \
    -e 's/^#local_enable=YES/local_enable=YES/' \
    -e 's/^#write_enable=YES/write_enable=YES/' \
    /etc/vsftpd/vsftpd.conf

# 追加被动模式及优化配置
cat >> /etc/vsftpd/vsftpd.conf << EOF
# 被动模式配置(解决外网访问问题)
pasv_enable=YES
pasv_min_port=30000
pasv_max_port=31000
pasv_address=0.0.0.0
# 允许本地用户读写
local_umask=022
# 传输优化
local_max_rate=0
pasv_promiscuous=YES
# 禁用SELinux干扰(若启用SELinux)
allow_writeable_chroot=YES
EOF
echo "vsftpd 核心配置修改完成"

# 第六步:修复PAM鉴权(彻底解决530错误,关键修正!)
echo -e "\n6/7 修复PAM鉴权配置(解决nologin用户530错误)..."
# 备份PAM配置(仅备份一次)
if [ ! -f /etc/pam.d/vsftpd.bak ]; then
    cp /etc/pam.d/vsftpd /etc/pam.d/vsftpd.bak &> /dev/null
    echo "PAM原配置备份:/etc/pam.d/vsftpd.bak"
fi

# 正确的PAM配置(重点修正)
# 1. 注释 pam_shells.so(不检查shell类型,允许nologin)
# 2. 移除错误的 pam_nologin.so(该模块禁止nologin用户登录)
# 3. 确保 pam_unix.so 正常加载(本地用户密码鉴权)
sed -i \
    -e 's/^auth required pam_shells.so/#auth required pam_shells.so/' \
    -e '/^auth required pam_nologin.so/d' \
    /etc/pam.d/vsftpd

# 确保PAM配置包含本地用户鉴权(防止配置缺失)
grep -q "auth sufficient pam_unix.so" /etc/pam.d/vsftpd || {
    echo "auth sufficient pam_unix.so nullok_secure" >> /etc/pam.d/vsftpd
}
grep -q "account sufficient pam_unix.so" /etc/pam.d/vsftpd || {
    echo "account sufficient pam_unix.so" >> /etc/pam.d/vsftpd
}
echo "PAM鉴权配置修复完成(已适配nologin用户)"

# 第七步:补充/etc/shells(防止部分系统nologin不在shell列表)
echo -e "\n7/7 补充shell列表(避免鉴权遗漏)..."
if ! grep -q "/sbin/nologin" /etc/shells; then
    echo "/sbin/nologin" >> /etc/shells
    echo "已将 /sbin/nologin 添加到 /etc/shells"
else
    echo "/sbin/nologin 已在 shell 列表中,跳过"
fi

# 重启 vsftpd 服务生效
echo -e "\n========================================"
echo "重启 vsftpd 服务应用所有配置..."
systemctl restart vsftpd &> /dev/null

# 验证服务状态并输出连接信息
if systemctl is-active vsftpd &> /dev/null; then
    echo -e "\n✅ vsftpd 服务部署完成!(已彻底解决530鉴权错误)"
    echo -e "\n📋 连接信息:"
    echo "FTP服务器地址:服务器IP"
    echo "FTP用户名:$FTP_USER"
    echo "FTP密码:$FTP_PASS"
    echo "存储目录:$FTP_USER_DIR"
    echo "端口:21(控制端口)、30000-31000(数据端口)"
    echo -e "\n💡 测试命令(服务器本地验证):"
    echo "ftp -inv 服务器IP"
    echo "user $FTP_USER $FTP_PASS"  # 直接执行该命令登录,无需手动输入
    echo "put 本地文件路径  # 测试上传"
    echo "get 服务器文件路径  # 测试下载"
    echo -e "\n⚠️  安全提示:"
    echo "1. 执行 chmod 600 vsftpd_install.sh 保护脚本(避免密码泄露)"
    echo "2. 生产环境建议定期更新密码(修改脚本 FTP_PASS 后重新执行)"
    echo "====================如果不能访问修改一下配置==================================="
    echo "1. vi /etc/pam.d/vsftpd"
    echo "2. 注释掉(应该在第2行) auth required pam_shells.so"
    echo "3. 在最后一行增加  auth required pam_nologin.so"
    echo "4. 重新启动 systemctl restart vsftpd"
else
    echo -e "\n❌ vsftpd 服务部署失败!"
    exit 1
fi

硬盘挂载

sh 复制代码
#!/bin/bash
# 麒麟OS10 一键硬盘挂载脚本
# 支持自定义硬盘设备名和挂载目录
# 运行方式:sudo bash mount_disk.sh [硬盘设备名] [挂载目录]
# 示例:sudo bash mount_disk.sh /dev/sdb1 /data

##############################################################################
# 基础检查(必须root权限+麒麟OS10)
##############################################################################
if [ "$(id -u)" -ne 0 ]; then
    echo -e "\033[31m错误:请使用root权限运行(sudo bash $0)\033[0m"
    exit 1
fi


##############################################################################
# 参数处理(支持命令行传参或交互输入)
##############################################################################
# 若未传参,交互询问用户
if [ $# -ne 2 ]; then
    echo -e "\033[34m=======================================\033[0m"
    echo -e "\033[34m        麒麟OS10 硬盘一键挂载脚本        \033[0m"
    echo -e "\033[34m=======================================\033[0m"
    
    # 询问硬盘设备名
    read -p "请输入未挂载的硬盘设备名(例:/dev/sdb1、/dev/vdb):" DISK_DEV
    if [ -z "$DISK_DEV" ]; then
        echo -e "\033[31m错误:硬盘设备名不能为空\033[0m"
        exit 1
    fi
    
    # 询问挂载目录
    read -p "请输入挂载目录(例:/data、/mnt/disk1):" MOUNT_DIR
    if [ -z "$MOUNT_DIR" ]; then
        echo -e "\033[31m错误:挂载目录不能为空\033[0m"
        exit 1
    fi
else
    # 命令行传参模式
    DISK_DEV=$1
    MOUNT_DIR=$2
fi

##############################################################################
# 前置检查(避免操作失误)
##############################################################################
echo -e "\033[34m正在执行前置检查...\033[0m"

# 1. 检查硬盘设备是否存在
if [ ! -b "$DISK_DEV" ]; then
    echo -e "\033[31m错误:硬盘设备 $DISK_DEV 不存在\033[0m"
    echo -e "提示:可通过以下命令查看可用硬盘:fdisk -l 或 lsblk"
    exit 1
fi

# 2. 检查硬盘是否已挂载
if mount | grep -q "$DISK_DEV"; then
    echo -e "\033[31m错误:硬盘 $DISK_DEV 已挂载,无需重复操作\033[0m"
    echo -e "当前挂载点:$(mount | grep "$DISK_DEV" | awk '{print $3}')"
    exit 1
fi

# 3. 检查挂载目录是否已存在(存在则提示确认)
if [ -d "$MOUNT_DIR" ]; then
    read -p "警告:挂载目录 $MOUNT_DIR 已存在,是否继续(数据不会丢失,y/n)?" CONFIRM
    if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
        echo "操作已取消"
        exit 0
    fi
else
    # 不存在则创建目录(含父目录)
    echo -e "正在创建挂载目录:$MOUNT_DIR"
    mkdir -p "$MOUNT_DIR" || {
        echo -e "\033[31m错误:创建挂载目录 $MOUNT_DIR 失败\033[0m"
        exit 1
    }
fi

# 4. 检查硬盘是否需要格式化(无文件系统则提示格式化)
if ! blkid "$DISK_DEV" | grep -q "TYPE"; then
    echo -e "\033[33m警告:硬盘 $DISK_DEV 未检测到文件系统,需要格式化后才能挂载\033[0m"
    read -p "是否立即格式化(会清除硬盘所有数据!建议先备份,y/n)?" FORMAT_CONFIRM
    if [ "$FORMAT_CONFIRM" = "y" ] || [ "$FORMAT_CONFIRM" = "Y" ]; then
        # 默认为ext4格式(麒麟OS推荐),支持自定义格式
        read -p "请输入文件系统格式(默认:ext4,支持ext3/xfs/btrfs):" FS_TYPE
        FS_TYPE=${FS_TYPE:-ext4}
        
        echo -e "正在格式化 $DISK_DEV 为 $FS_TYPE 格式...(可能需要几分钟)"
        case $FS_TYPE in
            ext3|ext4) mkfs.$FS_TYPE -F "$DISK_DEV" ;;
            xfs) mkfs.xfs -f "$DISK_DEV" ;;
            btrfs) mkfs.btrfs -f "$DISK_DEV" ;;
            *) 
                echo -e "\033[31m错误:不支持的文件系统格式 $FS_TYPE\033[0m"
                exit 1
                ;;
        esac
        
        # 格式化结果检查
        if [ $? -ne 0 ]; then
            echo -e "\033[31m错误:格式化 $DISK_DEV 失败\033[0m"
            exit 1
        fi
    else
        echo -e "\033[31m错误:未格式化的硬盘无法挂载,操作已取消\033[0m"
        exit 1
    fi
fi

##############################################################################
# 执行挂载操作
##############################################################################
echo -e "\033[34m正在执行挂载操作...\033[0m"

# 临时挂载(立即生效)
mount "$DISK_DEV" "$MOUNT_DIR" || {
    echo -e "\033[31m错误:挂载 $DISK_DEV 到 $MOUNT_DIR 失败\033[0m"
    echo -e "提示:检查硬盘格式与系统兼容性,或查看日志:dmesg | grep mount"
    exit 1
}

# 获取硬盘UUID(用于fstab开机自启,比设备名更稳定)
DISK_UUID=$(blkid "$DISK_DEV" | grep -oP 'UUID="\K[^"]+')
if [ -z "$DISK_UUID" ]; then
    echo -e "\033[31m错误:获取硬盘UUID失败,无法设置开机自启\033[0m"
    echo -e "临时挂载已成功,但重启后会失效,请手动配置fstab"
    exit 1
fi

# 获取文件系统类型
FS_TYPE=$(blkid "$DISK_DEV" | grep -oP 'TYPE="\K[^"]+')

##############################################################################
# 配置开机自启(fstab)
##############################################################################
echo -e "\033[34m正在配置开机自启...\033[0m"

# 备份fstab(避免配置错误)
FSTAB_BACKUP="/etc/fstab.bak.$(date +%Y%m%d%H%M%S)"
cp /etc/fstab "$FSTAB_BACKUP" || {
    echo -e "\033[33m警告:备份fstab失败,但仍将继续配置开机自启\033[0m"
}

# 检查fstab中是否已存在该硬盘配置
if grep -q "$DISK_UUID" /etc/fstab; then
    echo -e "\033[33m警告:fstab中已存在该硬盘(UUID:$DISK_UUID)的配置,将覆盖原有配置\033[0m"
    # 删除原有配置
    sed -i "/$DISK_UUID/d" /etc/fstab
fi

# 写入fstab配置(UUID方式,最稳定)
cat >> /etc/fstab << EOF
# 自动挂载 $DISK_DEV 到 $MOUNT_DIR
UUID=$DISK_UUID $MOUNT_DIR $FS_TYPE defaults 0 0
EOF

# 验证fstab配置是否正确(避免语法错误导致开机失败)
mount -a || {
    echo -e "\033[31m错误:fstab配置有误,已自动恢复备份\033[0m"
    cp "$FSTAB_BACKUP" /etc/fstab
    exit 1
}

##############################################################################
# 操作完成,输出结果
##############################################################################
echo -e "\033[32m=======================================\033[0m"
echo -e "\033[32m        硬盘挂载操作完成!              \033[0m"
echo -e "\033[32m=======================================\033[0m"
echo -e "硬盘设备:$DISK_DEV"
echo -e "挂载目录:$MOUNT_DIR"
echo -e "文件系统:$FS_TYPE"
echo -e "硬盘UUID:$DISK_UUID"
echo -e "开机自启:已配置(fstab)"
echo -e "\033[34m验证命令:df -h | grep $MOUNT_DIR\033[0m"
echo -e "\033[34m卸载命令:umount $MOUNT_DIR\033[0m"
echo -e "\033[32m=======================================\033[0m"
相关推荐
白狐_7981 天前
Ubuntu Linux 新手生存指南
linux·ubuntu
代码游侠1 天前
应用——Linux 标准IO编程
linux·前端·数据库·学习·算法
rafael(一只小鱼)1 天前
gemini使用+部署教程
java·人工智能·ai·go
Hello.Reader1 天前
Flink SQL Window Join 把时间维度“写进” JOIN 条件里
数据库·sql·flink
snowfoootball1 天前
java面向对象进阶
java·开发语言
爬山算法1 天前
Redis(172)如何使用Redis实现分布式队?
数据库·redis·分布式
没有bug.的程序员1 天前
GC 调优实战:从慢到快的真实案例
java·jvm·测试工具·gc·gc调优
近津薪荼1 天前
Linux 操作系统基础指令详解(一)
linux·服务器·学习
古城小栈1 天前
QPS统计好,睡觉不会被打扰
运维·数据库·压力测试