ubuntu 一键安装mysql8脚本

作者: adm 分类: mysql 发布时间: 2025-04-04
#!/bin/bash

set -e

# 颜色输出
green() { echo -e "\033[32m$1\033[0m"; }
yellow() { echo -e "\033[33m$1\033[0m"; }
red() { echo -e "\033[31m$1\033[0m"; }
blue() { echo -e "\033[34m$1\033[0m"; }

# 检查/data目录是否存在
check_data_directory() {
    if [ ! -d "/data" ]; then
        red "错误: /data 目录不存在!"
        yellow "请先创建并挂载 /data 目录"
        exit 1
    fi
    sudo chmod 755 /data
}

# 准备MySQL主目录(统一存放数据+日志)
prepare_mysql_directory() {
    local mysql_dir="/data/mysql"

    if [ -d "$mysql_dir" ]; then
        yellow "发现已存在的MySQL目录: $mysql_dir"
        printf "是否删除现有数据并重新初始化?(y/N): "
        read choice
        if [ "$choice" = "y" ] || [ "$choice" = "Y" ]; then
            sudo rm -rf "$mysql_dir"
        fi
    fi

    sudo mkdir -p "$mysql_dir"
    sudo chmod 755 "$mysql_dir"
    green "✓ MySQL主目录准备完成: $mysql_dir"
}

# 设置MySQL目录权限
setup_mysql_permissions() {
    local mysql_dir="/data/mysql"

    yellow "设置MySQL目录权限..."

    sleep 2

    if ! id "mysql" &>/dev/null; then
        red "✗ mysql用户不存在,正在创建..."
        sudo groupadd mysql 2>/dev/null || true
        sudo useradd -r -g mysql -s /bin/false mysql 2>/dev/null || true
    fi

    sudo chown -R mysql:mysql "$mysql_dir"
    sudo chmod 750 "$mysql_dir"
    green "✓ 权限设置完成"
}

# 创建MySQL配置文件(所有路径统一到 /data/mysql)
create_mysql_config() {
    yellow "创建MySQL配置文件..."

    # 创建配置目录
    sudo mkdir -p /etc/mysql/conf.d
    sudo mkdir -p /etc/mysql/mysql.conf.d
    sudo mkdir -p /var/run/mysqld

    # 设置运行时目录权限(兼容性)
    sudo chown -R mysql:mysql /var/run/mysqld
    sudo chmod 755 /var/run/mysqld

    # 主配置文件
    sudo tee /etc/mysql/my.cnf > /dev/null <<'EOF'
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
EOF

    # mysqld 配置 - 简化版本,避免语法错误
    sudo tee /etc/mysql/mysql.conf.d/mysqld.cnf > /dev/null <<'EOF'
[mysqld]
user            = mysql
datadir         = /data/mysql
socket          = /data/mysql/mysqld.sock
pid-file        = /data/mysql/mysqld.pid
log_error       = /data/mysql/error.log
bind-address    = 0.0.0.0
port            = 3306
innodb_buffer_pool_size = 128M
skip-name-resolve
default_authentication_plugin = mysql_native_password

# 字符集设置
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
EOF

    # 客户端配置 - 简化版本
    sudo tee /etc/mysql/conf.d/mysql.cnf > /dev/null <<'EOF'
[mysql]

[client]
socket = /data/mysql/mysqld.sock
EOF

    green "✓ MySQL配置文件创建完成(数据与日志均在 /data/mysql)"
}

# 修复AppArmor权限
fix_apparmor_permissions() {
    local mysql_dir="/data/mysql"

    if [ -f "/etc/apparmor.d/usr.sbin.mysqld" ]; then
        yellow "修复AppArmor权限..."

        # 备份
        sudo cp /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/usr.sbin.mysqld.backup.$(date +%Y%m%d_%H%M%S)

        # 确保 datadir 被放行
        if ! grep -q "$mysql_dir/\*\* rwk" /etc/apparmor.d/usr.sbin.mysqld; then
            sudo sed -i "/\/var\/lib\/mysql\/\*\* rwk,/a\  $mysql_dir/** rwk," /etc/apparmor.d/usr.sbin.mysqld
        fi

        # 重载
        sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld
        green "✓ AppArmor权限修复完成"
    else
        yellow "⚠ AppArmor配置文件不存在,跳过"
    fi
}

# 检查MySQL是否已安装
check_mysql_installed() {
    if command -v mysql >/dev/null 2>&1; then return 0; fi
    if [ -f /etc/init.d/mysql ] || { command -v systemctl >/dev/null 2>&1 && systemctl list-unit-files | grep -q mysql; }; then return 0; fi
    if pgrep mysqld >/dev/null 2>&1; then return 0; fi
    return 1
}

# 显示MySQL状态
show_mysql_status() {
    echo
    blue "====================================="
    blue "          MySQL 状态检查"
    blue "====================================="

    if command -v mysql >/dev/null 2>&1; then
        green "MySQL客户端: 已安装"
        mysql --version 2>/dev/null || mysql -V 2>/dev/null
    else
        yellow "MySQL客户端: 未安装"
    fi

    local mysql_dir="/data/mysql"
    if [ -d "$mysql_dir" ]; then
        local db_size=$(sudo du -sh "$mysql_dir" 2>/dev/null | cut -f1)
        local db_count=$(sudo find "$mysql_dir" -maxdepth 1 -type d -name '*' 2>/dev/null | wc -l)
        red "⚠ 发现MySQL数据目录: $mysql_dir"
        red "⚠ 数据目录大小: $db_size"
        red "⚠ 数据库数量: $((db_count - 1))"
    elif [ -d "/var/lib/mysql" ]; then
        local db_size=$(sudo du -sh /var/lib/mysql 2>/dev/null | cut -f1)
        local db_count=$(sudo find /var/lib/mysql -maxdepth 1 -type d -name '*' 2>/dev/null | wc -l)
        red "⚠ 发现MySQL数据目录: /var/lib/mysql"
        red "⚠ 数据目录大小: $db_size"
        red "⚠ 数据库数量: $((db_count - 1))"
    fi

    blue "====================================="
    echo
}

# 备份数据库
backup_databases() {
    echo
    yellow "建议在重新安装前备份数据库!"
    printf "是否要备份数据库?(y/N): "
    read backup_choice

    if [ "$backup_choice" = "y" ] || [ "$backup_choice" = "Y" ]; then
        if command -v mysqldump >/dev/null 2>&1; then
            local backup_dir="/tmp/mysql_backup_$(date +%Y%m%d_%H%M%S)"
            mkdir -p "$backup_dir"
            yellow "正在备份数据库到: $backup_dir"
            if sudo mysqldump --all-databases > "$backup_dir/all_databases.sql" 2>/dev/null; then
                green "✓ 数据库备份完成: $backup_dir/all_databases.sql"
            else
                red "✗ 数据库备份失败,可能无法连接或没有权限"
            fi
        else
            red "✗ mysqldump 不可用,无法备份"
        fi
    else
        yellow "跳过数据库备份"
    fi
}

# 清理旧版本MySQL
clean_old_mysql() {
    red "开始清理旧版本MySQL..."
    red "⚠ 警告:这将删除所有MySQL数据和配置!"

    echo
    red "将被删除的内容:"
    red "  - 所有数据库数据 (/var/lib/mysql/ 和 /data/mysql/)"
    red "  - 配置文件 (/etc/mysql/)"
    red "  - 日志文件 (/var/log/mysql*, /data/mysql/error.log)"
    red "  - 所有用户数据和权限"
    echo

    printf "确定要删除所有MySQL数据并重新安装吗?(输入 'DELETE' 确认): "
    read final_confirmation

    if [ "$final_confirmation" != "DELETE" ]; then
        green "安装已取消,数据得以保留"
        exit 0
    fi

    yellow "开始清理..."

    # 停止服务
    sudo systemctl stop mysql 2>/dev/null || true
    sudo systemctl disable mysql 2>/dev/null || true
    sudo pkill -9 mysqld 2>/dev/null || true
    sleep 2

    # 卸载包
    yellow "卸载MySQL软件包..."
    sudo apt remove --purge mysql* mariadb* -y 2>/dev/null || true
    sudo apt autoremove -y
    sudo apt autoclean

    # 删除文件
    yellow "删除MySQL相关文件..."
    sudo rm -rf /etc/mysql /var/lib/mysql /var/log/mysql* /var/run/mysqld /data/mysql

    green "旧版本MySQL清理完成"
    echo
}

# 初始化MySQL数据目录
initialize_mysql_data() {
    local mysql_dir="/data/mysql"

    yellow "准备MySQL数据目录..."

    sudo systemctl stop mysql 2>/dev/null || true
    sleep 3

    if [ -d "$mysql_dir/mysql" ]; then
        green "✓ MySQL系统数据库已存在,跳过初始化"
        return 0
    fi

    if [ -d "$mysql_dir" ]; then
        yellow "目录存在但未初始化,清理..."
        sudo rm -rf "$mysql_dir"
    fi

    sudo mkdir -p "$mysql_dir"
    sudo chown -R mysql:mysql "$mysql_dir"
    sudo chmod 750 "$mysql_dir"

    yellow "初始化MySQL系统表(不安全模式)..."

    if sudo -u mysql mysqld --initialize-insecure --datadir="$mysql_dir" --user=mysql 2>&1 | tee /tmp/mysql_init.log; then
        if [ -d "$mysql_dir/mysql" ]; then
            green "✓ MySQL数据目录初始化成功"
            return 0
        else
            red "✗ 系统表未生成"
            return 1
        fi
    else
        red "✗ MySQL初始化失败"
        cat /tmp/mysql_init.log | tail -10
        if [ -f "/data/mysql/error.log" ]; then
            red "错误日志:"
            sudo tail -10 /data/mysql/error.log
        fi
        return 1
    fi
}

# 创建systemd服务文件
create_systemd_service() {
    yellow "创建systemd服务文件..."
    
    # 检查服务文件是否存在
    if [ ! -f "/etc/systemd/system/mysql.service" ] && [ ! -f "/lib/systemd/system/mysql.service" ]; then
        sudo tee /etc/systemd/system/mysql.service > /dev/null <<'EOF'
[Unit]
Description=MySQL Community Server
After=network.target
After=syslog.target

[Install]
WantedBy=multi-user.target

[Service]
User=mysql
Group=mysql
Type=forking
PIDFile=/data/mysql/mysqld.pid
ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/data/mysql/mysqld.pid
ExecStop=/bin/kill -TERM $MAINPID
LimitNOFILE=5000
Restart=on-failure
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF
        green "✓ systemd服务文件创建完成"
    else
        # 如果服务文件存在,修复它
        if [ -f "/lib/systemd/system/mysql.service" ]; then
            sudo sed -i 's|/var/lib/mysql|/data/mysql|g' /lib/systemd/system/mysql.service
            sudo sed -i 's|PIDFile=.*|PIDFile=/data/mysql/mysqld.pid|' /lib/systemd/system/mysql.service
        fi
        green "✓ systemd服务文件已修复"
    fi
    
    # 重新加载systemd
    sudo systemctl daemon-reload
    sudo systemctl enable mysql.service 2>/dev/null || sudo systemctl enable mysql 2>/dev/null || true
}

# 修复MySQL服务启动
fix_mysql_service() {
    yellow "修复MySQL服务启动问题..."

    sudo systemctl stop mysql 2>/dev/null || true
    sudo pkill -9 mysqld 2>/dev/null || true
    sleep 2

    # 清理残留
    sudo rm -f /var/run/mysqld/mysqld.pid /var/run/mysqld/mysqld.sock
    sudo rm -f /data/mysql/mysqld.pid /data/mysql/mysqld.sock

    # 创建systemd服务
    create_systemd_service

    # 修复 systemd 启动脚本(如果存在)
    if [ -f "/usr/share/mysql/mysql-systemd-start" ]; then
        if grep -q "/var/lib/mysql" /usr/share/mysql/mysql-systemd-start; then
            yellow "修复systemd启动脚本..."
            sudo sed -i 's|/var/lib/mysql|/data/mysql|g' /usr/share/mysql/mysql-systemd-start
        fi
    fi

    # 启动服务
    yellow "启动MySQL服务..."
    if sudo systemctl start mysql; then
        sleep 5
        if sudo systemctl is-active mysql >/dev/null 2>&1; then
            green "✓ MySQL服务启动成功"
            return 0
        fi
    fi

    # 如果systemd启动失败,尝试直接启动
    yellow "systemd启动失败,尝试直接启动mysqld..."
    sudo -u mysql mysqld --daemonize --datadir=/data/mysql --pid-file=/data/mysql/mysqld.pid
    
    sleep 3
    if pgrep mysqld >/dev/null 2>&1; then
        green "✓ MySQL直接启动成功"
        return 0
    fi

    red "✗ MySQL服务启动失败"
    # 显示错误信息
    if [ -f "/data/mysql/error.log" ]; then
        red "错误日志:"
        sudo tail -20 /data/mysql/error.log
    fi
    return 1
}

# 验证安装
verify_installation() {
    echo
    green "验证MySQL安装..."

    if ! command -v mysql >/dev/null 2>&1; then
        red "✗ MySQL客户端未安装"
        return 1
    fi

    # 检查进程是否运行
    if pgrep mysqld >/dev/null 2>&1; then
        green "✓ MySQL进程正在运行"
        
        # 等待一下让MySQL完全启动
        sleep 3
        
        # 尝试连接
        if sudo mysql --socket=/data/mysql/mysqld.sock -e "SELECT @@version, @@datadir;" 2>/dev/null; then
            green "✓ MySQL连接成功"
            actual_datadir=$(sudo mysql --socket=/data/mysql/mysqld.sock -sN -e "SELECT @@datadir;" 2>/dev/null || echo "")
            if [ "$actual_datadir" = "/data/mysql/" ]; then
                green "✓ 数据目录正确: /data/mysql/"
            else
                yellow "⚠ 实际 datadir: $actual_datadir"
            fi
            return 0
        else
            yellow "⚠ MySQL进程在运行但无法连接,可能还在启动中"
            return 1
        fi
    else
        red "✗ MySQL进程未运行"
        return 1
    fi
}

# 等待用户按键
wait_for_key() {
    yellow "按 Enter 键继续或 Ctrl+C 取消..."
    read dummy
}

# 主流程
main() {
    clear
    green "====================================="
    green "        MySQL 安装管理脚本"
    green "   (数据、日志、socket 均在 /data/mysql)"
    green "====================================="
    echo

    check_data_directory

    if check_mysql_installed; then
        red "⚠ 检测到系统已安装 MySQL!"
        show_mysql_status

        printf "是否重新安装MySQL?(y/N): "
        read reinstall_choice

        if [ "$reinstall_choice" = "y" ] || [ "$reinstall_choice" = "Y" ]; then
            backup_databases

            echo
            red "=================================================="
            red "                    ⚠ 警告 ⚠"
            red "重新安装将永久删除:"
            red "  - 所有数据库和数据表"
            red "  - 所有用户账户和权限"
            red "  - 所有配置和日志"
            red "此操作不可撤销!"
            red "=================================================="
            echo

            printf "确定要继续重新安装吗?(输入 'CONFIRM' 继续): "
            read final_confirm

            if [ "$final_confirm" != "CONFIRM" ]; then
                green "安装已取消"
                exit 0
            fi

            clean_old_mysql
        else
            green "已退出安装"
            exit 0
        fi
    else
        green "未检测到MySQL,开始全新安装..."
    fi

    wait_for_key

    # 安装
    yellow "更新软件包列表..."
    sudo apt update

    yellow "安装MySQL服务器..."
    if ! sudo apt install -y mysql-server; then
        red "✗ MySQL安装失败"
        exit 1
    fi

    prepare_mysql_directory
    setup_mysql_permissions
    create_mysql_config
    fix_apparmor_permissions
    initialize_mysql_data
    fix_mysql_service

    if verify_installation; then
        green "🎉 MySQL安装成功!"
        echo
        yellow "现在可以运行安全配置:"
        yellow "sudo mysql_secure_installation --socket=/data/mysql/mysqld.sock"
        echo
        yellow "或者手动设置密码:"
        yellow "sudo mysql --socket=/data/mysql/mysqld.sock -e \"ALTER USER 'root'@'localhost' IDENTIFIED BY '你的密码';\""
    else
        red "❌ 安装失败,请检查日志"
        exit 1
    fi
}

# 执行
main

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!