Linux用30秒部署Nginx+Tomcat+Mysql+Jdk1.8环境
✨重磅!盹猫的个人小站正式上线啦~诚邀各位技术大佬前来探秘!✨
—— 专为开发者打造的宝藏基地,等你来探索!
这里有:
🔥 硬核技术干货:编程技巧、开发经验、踩坑指南,带你解锁技术新姿势!
🎉 趣味开发日常:代码背后的脑洞故事、工具测评,让技术圈不再枯燥~
💎 独家资源分享:开源项目、学习资料包,助你打怪升级快人一步!
🚀 立即访问 → 盹猫猫的个人小站 ← 点击探索
🌟 说不定这里就有你寻找已久的技术秘籍哦~
文章目录
- 脚本核心功能概述
- 服务端口
- 脚本实现详解
- 1. 变量定义与初始化
- 2. 端口冲突检查
- 3. 目录结构创建与权限设置
- 4. Docker环境检查与安装
- 5. 镜像拉取功能
- 6. 服务启动与状态检查通用函数
- 7. MySQL服务启动
- 8. Tomcat服务启动
- 9. Nginx服务启动
- 10. 主函数
- 完整源码
- 使用方法
- 测试输出
- 测试截图
- tomcat
- nignx
- mysql
- 运行容器
- 持久化问题
- 总结
- 如果文档对你有帮助,分享一下留个赞吧!
Welcome to Code Block's blog本篇文章主要介绍了
[Linux用30秒部署Nginx+Tomcat+Mysql环境]
❤博主广交技术好友,喜欢我的文章的可以关注一下❤
在日常开发和运维工作中,搭建一套包含数据库、应用服务器和Web服务器的完整环境是非常常见的需求。手动配置不仅耗时,还容易出现各种版本兼容问题。本文将详细解析一个基于Docker的一键部署脚本,该脚本能自动完成MySQL、Tomcat和Nginx的安装配置,并实现数据持久化存储,非常适合开发测试环境快速部署。
脚本核心功能概述
这个Shell脚本实现了三大服务的自动化部署,主要特点包括:
- 全自动环境准备(包括Docker安装)
- 完善的端口冲突检查机制
- 配置文件与数据的持久化存储
- 精细化的权限控制
- 服务状态自动检查与错误处理
- 支持服务重启自动恢复
通过这个脚本,即使是Docker新手也能在几分钟内搭建起一套完整的Web应用运行环境。
服务端口
端口 | 服务 |
---|---|
3306 | mysql |
80 | nginx |
8080 | tomcat + jdk1.8 |
脚本实现详解
下面我们分模块解析脚本的实现逻辑:
1. 变量定义与初始化
# 定义颜色变量,用于美化输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # 无颜色# 定义宿主机目录(使用绝对路径,避免相对路径问题)
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/docker-services"
MYSQL_DIR="${BASE_DIR}/mysql"
MYSQL_CONF="${MYSQL_DIR}/conf"
MYSQL_DATA="${MYSQL_DIR}/data"
MYSQL_PORT=3306TOMCAT_DIR="${BASE_DIR}/tomcat"
TOMCAT_CONF="${TOMCAT_DIR}/conf"
TOMCAT_WEBAPPS="${TOMCAT_DIR}/webapps"
TOMCAT_LOGS="${TOMCAT_DIR}/logs"
TOMCAT_PORT=8080NGINX_DIR="${BASE_DIR}/nginx"
NGINX_CONF="${NGINX_DIR}/conf"
NGINX_CONF_D="${NGINX_CONF}/conf.d"
NGINX_HTML="${NGINX_DIR}/html"
NGINX_LOGS="${NGINX_DIR}/logs"
NGINX_PORT=80
这部分代码主要完成:
- 定义颜色变量,用于后续输出彩色日志信息
- 定义宿主机上的目录结构,采用绝对路径避免相对路径问题
- 定义各服务使用的端口号
目录结构设计遵循"一个服务一个主目录"的原则,每个服务目录下再细分配置、数据、日志等子目录,方便管理。
2. 端口冲突检查
check_port() {local port=$1if ss -tuln | grep -q ":$port "; thenecho -e "${RED}错误:端口 $port 已被占用(可能被其他服务占用)${NC}"echo -e "${YELLOW}请关闭占用 $port 端口的程序后重试${NC}"exit 1fi
}
在启动服务前,必须检查服务所需端口是否已被占用。check_port
函数通过ss
命令检查指定端口的使用情况,如果已被占用则提示错误并退出脚本,避免服务启动失败。
3. 目录结构创建与权限设置
create_directories() {echo -e "${YELLOW}创建宿主机目录结构并设置权限...${NC}"# 创建所有目录mkdir -p ${MYSQL_CONF} ${MYSQL_DATA} \${TOMCAT_CONF} ${TOMCAT_WEBAPPS} ${TOMCAT_LOGS} \${NGINX_CONF} ${NGINX_CONF_D} ${NGINX_HTML} ${NGINX_LOGS}# MySQL容器用户UID为999,设置数据目录归属chown -R 999:999 ${MYSQL_DIR}chmod -R 755 ${MYSQL_DIR}# Tomcat容器用户UID为1000,设置目录归属chown -R 1000:1000 ${TOMCAT_DIR}chmod -R 755 ${TOMCAT_DIR}# Nginx容器用户UID为101,设置目录归属chown -R 101:101 ${NGINX_DIR}chmod -R 755 ${NGINX_DIR}# 初始化Nginx默认页面(如果不存在)if [ ! -f "${NGINX_HTML}/index.html" ]; thencat > ${NGINX_HTML}/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title><style>body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; }</style>
</head>
<body><h1>Welcome to nginx!</h1><p>This page is served by Nginx with host-mounted volumes.</p>
</body>
</html>
EOFchown 101:101 ${NGINX_HTML}/index.htmlfi# 初始化Nginx默认配置(如果不存在)if [ ! -f "${NGINX_CONF_D}/default.conf" ]; thencat > ${NGINX_CONF_D}/default.conf << 'EOF'
server {listen 80;server_name localhost;location / {root /usr/share/nginx/html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root /usr/share/nginx/html;}
}
EOFchown 101:101 ${NGINX_CONF_D}/default.conffiecho -e "${GREEN}宿主机目录结构创建完成${NC}"
}
这部分是脚本的关键之一,主要完成:
-
创建所有需要的目录结构,使用
mkdir -p
确保递归创建 -
精细化权限控制:根据各Docker容器内部使用的默认用户UID设置目录归属
- MySQL容器默认使用UID 999
- Tomcat容器默认使用UID 1000
- Nginx容器默认使用UID 101
这种权限设置方式比简单使用777权限更安全,既能保证容器内进程有权访问目录,又不会过度开放权限。
-
初始化Nginx的默认页面和配置文件,确保首次启动就有可用的基础配置
4. Docker环境检查与安装
# 检查Docker是否已安装
check_docker() {if ! command -v docker &> /dev/null; thenecho -e "${YELLOW}Docker未安装,正在安装Docker...${NC}"install_dockerelseecho -e "${GREEN}Docker已安装${NC}"fi# 检查Docker服务是否运行if ! systemctl is-active --quiet docker; thenecho -e "${YELLOW}启动Docker服务...${NC}"systemctl start docker || {echo -e "${RED}错误:Docker服务启动失败,请检查Docker是否正常安装${NC}"exit 1}systemctl enable dockerfi
}# 安装Docker(兼容Ubuntu/Debian)
# 安装 Docker
install_docker() {echo "检测到系统: $DISTRO $VERSION"case $DISTRO inubuntu|debian)echo "正在安装 Docker (Ubuntu/Debian)..."# 卸载旧版本apt-get remove -y docker docker-engine docker.io containerd runc || true# 安装依赖apt-get updateapt-get install -y \apt-transport-https \ca-certificates \curl \gnupg \lsb-release# 添加 Docker 官方 GPG 密钥mkdir -p /etc/apt/keyringscurl -fsSL https://download.docker.com/linux/$DISTRO/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg# 设置稳定版仓库echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$DISTRO \$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null# 安装 Dockerapt-get updateapt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin;;centos|rhel|fedora)echo "正在安装 Docker (CentOS/RHEL/Fedora)..."# 卸载旧版本yum remove -y docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine || true# 安装依赖yum install -y yum-utils# 添加 Docker 仓库if [ "$DISTRO" == "fedora" ]; thenyum-config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repoelseyum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repofi# 安装 Dockeryum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin;;arch|manjaro)echo "正在安装 Docker (Arch/Manjaro)..."pacman -Sy --noconfirm docker;;*)echo "不支持的 Linux 发行版: $DISTRO"echo "尝试使用通用安装脚本..."curl -fsSL https://get.docker.com -o get-docker.shsh get-docker.shrm get-docker.sh;;esac# 启动并启用 Docker 服务systemctl enable --now dockercat <<-EOF > /etc/docker/daemon.json {"registry-mirrors": ["https://docker.xuanyuan.me"]}EOFsystemctl daemon-reloadsystemctl restart docker# 验证安装docker --versionecho "Docker 安装完成!"# 添加当前用户到 docker 组(可选)read -p "是否将当前用户添加到 docker 组? (y/n): " choiceif [ "$choice" = "y" ] || [ "$choice" = "Y" ]; thenif ! grep -q docker /etc/group; thengroupadd dockerficurrent_user=$(logname)usermod -aG docker $current_userecho "已将用户 $current_user 添加到 docker 组,可能需要重新登录才能生效"fi
}
这部分实现了Docker环境的自动检查和安装:
- 检查Docker是否已安装,未安装则自动执行安装流程
- 确保Docker服务处于运行状态,并设置开机自启
- 每个安装步骤都有错误检查,确保安装过程出现问题时能及时提示
5. 镜像拉取功能
pull_image() {local image=$1echo -e "${YELLOW}拉取镜像 $image(确保最新版本)...${NC}"if ! docker pull $image; thenecho -e "${RED}错误:拉取镜像 $image 失败,请检查网络${NC}"exit 1fi
}
该函数负责拉取指定的Docker镜像,确保本地有可用的服务镜像。在启动服务前拉取镜像,能避免因本地无镜像导致的启动失败。
6. 服务启动与状态检查通用函数
start_service() {local service_name=$1local container_name=$2local image=$3local run_cmd=$4local port=$5# 检查容器是否已存在if [ "$(docker ps -aq -f name=^/${container_name}$)" ]; then# 尝试启动已存在的容器echo -e "${YELLOW}检测到已有 $service_name 容器,尝试启动...${NC}"docker start $container_name || {echo -e "${RED}错误:启动已有 $service_name 容器失败${NC}"echo -e "${YELLOW}容器日志:${NC}"docker logs $container_name 2>&1 | head -20exit 1}sleep 5else# 启动新容器echo -e "${YELLOW}启动新的 $service_name 容器...${NC}"eval $run_cmd || { # 使用eval执行动态命令echo -e "${RED}错误:启动 $service_name 容器失败${NC}"exit 1}sleep 10 # 等待服务初始化fi# 检查容器是否运行if ! docker inspect -f '{{.State.Running}}' $container_name 2>/dev/null | grep -q "true"; thenecho -e "${RED}错误:$service_name 容器未正常运行${NC}"echo -e "${YELLOW}容器日志:${NC}"docker logs $container_name 2>&1 | head -20exit 1fi# 检查端口是否监听(确认服务启动成功)if ! ss -tuln | grep -q ":$port "; thenecho -e "${RED}错误:$service_name 端口 $port 未监听(服务未启动)${NC}"echo -e "${YELLOW}容器日志:${NC}"docker logs $container_name 2>&1 | head -20exit 1fiecho -e "${GREEN}$service_name 启动成功${NC}"
}
这是一个通用的服务启动函数,实现了服务启动的核心逻辑:
- 检查容器是否已存在,存在则尝试启动,不存在则创建新容器
- 使用
eval
执行动态构建的容器启动命令 - 检查容器是否处于运行状态
- 检查服务端口是否监听,确保服务真正启动成功
- 出现错误时输出容器日志,方便问题排查
这种设计避免了为每个服务重复编写启动逻辑,提高了代码复用性。
7. MySQL服务启动
start_mysql() {echo -e "\n${YELLOW}===== 初始化并启动MySQL =====${NC}"local image="mysql:8.0"local container_name="mysql-service"local mysql_password="root123456"# 检查端口check_port $MYSQL_PORT# 拉取镜像pull_image $image# 首次运行:目录为空时,让容器自动初始化(无需临时容器)if [ -z "$(ls -A ${MYSQL_CONF})" ]; thenecho -e "${YELLOW}MySQL配置目录为空,将由容器自动初始化${NC}"fiif [ -z "$(ls -A ${MYSQL_DATA})" ]; thenecho -e "${YELLOW}MySQL数据目录为空,首次启动将自动初始化数据库${NC}"fi# 构建启动命令(直接映射目录,容器会自动处理初始化)local run_cmd="docker run -d \--name $container_name \-p $MYSQL_PORT:3306 \-e MYSQL_ROOT_PASSWORD=$mysql_password \-e MYSQL_DATABASE=default_db \-v ${MYSQL_CONF}:/etc/mysql/conf.d \-v ${MYSQL_DATA}:/var/lib/mysql \--restart always \$image \--character-set-server=utf8mb4 \--collation-server=utf8mb4_unicode_ci"# 启动并检查start_service "MySQL" $container_name $image "$run_cmd" $MYSQL_PORT# 输出连接信息echo -e "${GREEN}MySQL服务信息:${NC}"echo -e " 地址: $(hostname -I | awk '{print $1}'):$MYSQL_PORT"echo -e " 用户名: root"echo -e " 密码: $mysql_password"echo -e " 连接示例: mysql -h $(hostname -I | awk '{print $1}') -P $MYSQL_PORT -u root -p"
}
MySQL服务的启动逻辑有几个关键点:
- 利用MySQL容器的自动初始化特性,无需临时容器即可完成配置和数据目录的初始化
- 通过环境变量设置root密码和默认数据库
- 通过
-v
参数将宿主机目录映射到容器内,实现配置和数据的持久化 - 设置
--restart always
确保容器随Docker服务自动启动 - 指定字符集参数,确保数据库默认使用utf8mb4字符集
这种实现方式简洁高效,避免了复杂的初始化脚本。
8. Tomcat服务启动
start_tomcat() {echo -e "\n${YELLOW}===== 初始化并启动Tomcat =====${NC}"local image="tomcat:9.0"local container_name="tomcat-service"# 检查端口check_port $TOMCAT_PORT# 拉取镜像pull_image $image# 首次运行:从镜像直接提取默认配置和webappsif [ -z "$(ls -A ${TOMCAT_CONF})" ] || [ -z "$(ls -A ${TOMCAT_WEBAPPS})" ]; thenecho -e "${YELLOW}首次运行,从镜像提取Tomcat配置和应用...${NC}"# 创建临时容器但不运行(--rm 退出后自动删除)docker create --name tomcat-temp $image# 直接复制文件(无需启动容器)docker cp tomcat-temp:/usr/local/tomcat/conf/. ${TOMCAT_CONF}/ || {echo -e "${RED}错误:复制Tomcat配置失败${NC}"; docker rm tomcat-temp >/dev/null 2>&1;exit 1;}docker cp tomcat-temp:/usr/local/tomcat/webapps.dist/. ${TOMCAT_WEBAPPS}/ || {echo -e "${RED}错误:复制Tomcat应用失败${NC}";docker rm tomcat-temp >/dev/null 2>&1;exit 1;}# 删除临时容器docker rm tomcat-temp >/dev/null 2>&1echo -e "${GREEN}Tomcat配置和应用提取完成${NC}"elseecho -e "${YELLOW}检测到已有Tomcat配置,跳过复制${NC}"fi# 构建启动命令local run_cmd="docker run -d \--name $container_name \-p $TOMCAT_PORT:8080 \-v ${TOMCAT_CONF}:/usr/local/tomcat/conf \-v ${TOMCAT_WEBAPPS}:/usr/local/tomcat/webapps \-v ${TOMCAT_LOGS}:/usr/local/tomcat/logs \--restart always \$image"# 启动并检查start_service "Tomcat" $container_name $image "$run_cmd" $TOMCAT_PORT# 输出访问信息echo -e "${GREEN}Tomcat服务信息:${NC}"echo -e " 访问地址: http://$(hostname -I | awk '{print $1}'):$TOMCAT_PORT"echo -e " 部署应用: 将WAR包放入 ${TOMCAT_WEBAPPS} 目录即可"
}
Tomcat的启动逻辑有一个巧妙之处:
- 使用
docker create
命令创建临时容器但不启动,这样可以直接从镜像中提取文件 - 从临时容器复制配置文件和默认应用到宿主机映射目录
- 复制完成后删除临时容器
这种方式比启动容器后再复制文件更高效,也避免了容器启动可能带来的问题。同时,通过目录映射,实现了:
- 配置文件的持久化
- Web应用的持久化部署
- 日志文件的持久化存储
9. Nginx服务启动
start_nginx() {echo -e "\n${YELLOW}===== 初始化并启动Nginx =====${NC}"local image="nginx:latest"local container_name="nginx-service"# 检查端口check_port $NGINX_PORT# 拉取镜像pull_image $image# 创建必要的目录结构mkdir -p ${NGINX_CONF} ${NGINX_CONF_D} ${NGINX_HTML} ${NGINX_LOGS}# 首次运行:从镜像提取默认配置(使用docker create)if [ ! -f "${NGINX_CONF}/nginx.conf" ]; thenecho -e "${YELLOW}首次运行,提取Nginx默认配置...${NC}"# 创建但不启动临时容器docker create --name nginx-temp ${image} >/dev/null || {echo -e "${RED}错误:创建临时容器失败${NC}"; exit 1;}# 复制主配置文件docker cp nginx-temp:/etc/nginx/nginx.conf ${NGINX_CONF}/ || {echo -e "${RED}错误:复制nginx.conf失败${NC}"; docker rm nginx-temp >/dev/null 2>&1;exit 1;}# 复制conf.d配置(注意目标路径处理)docker cp nginx-temp:/etc/nginx/conf.d/. ${NGINX_CONF_D}/ || {echo -e "${RED}错误:复制conf.d配置失败${NC}";docker rm nginx-temp >/dev/null 2>&1;exit 1;}# 删除临时容器docker rm nginx-temp >/dev/null 2>&1echo -e "${GREEN}Nginx默认配置提取完成${NC}"elseecho -e "${YELLOW}检测到已有Nginx配置,跳过复制${NC}"fi# 确保存在默认HTML文件if [ ! -f "${NGINX_HTML}/index.html" ]; thenecho "<h1>Welcome to Nginx!</h1>" > ${NGINX_HTML}/index.htmlfi# 构建启动命令local run_cmd="docker run -d \--name $container_name \-p $NGINX_PORT:80 \-v ${NGINX_CONF}/nginx.conf:/etc/nginx/nginx.conf \-v ${NGINX_CONF_D}:/etc/nginx/conf.d \-v ${NGINX_HTML}:/usr/share/nginx/html \-v ${NGINX_LOGS}:/var/log/nginx \--restart always \$image"# 启动并检查start_service "Nginx" $container_name $image "$run_cmd" $NGINX_PORT# 输出访问信息echo -e "${GREEN}Nginx服务信息:${NC}"echo -e " 访问地址: http://$(hostname -I | awk '{print $1}'):$NGINX_PORT"echo -e " 配置目录: ${NGINX_CONF_D}/"echo -e " 日志目录: ${NGINX_LOGS}/"
}
Nginx的启动逻辑与Tomcat类似,也采用了docker create
创建临时容器提取配置的方式,主要映射了:
- 主配置文件nginx.conf
- 站点配置目录conf.d
- 网页根目录html
- 日志目录logs
这种映射方式使得我们可以直接在宿主机上修改Nginx配置,无需进入容器内部。
10. 主函数
main() {echo -e "${YELLOW}===== 开始部署 MySQL + Tomcat + Nginx 服务 =====${NC}"# 先检查端口(避免启动后冲突)check_port $MYSQL_PORTcheck_port $TOMCAT_PORTcheck_port $NGINX_PORT# 创建目录create_directories# 检查Dockercheck_docker# 启动服务start_mysqlstart_tomcatstart_nginxecho -e "\n${GREEN}===== 所有服务部署完成 =====${NC}"echo -e "服务状态查看:docker ps"echo -e "配置文件目录:${BASE_DIR}"
}# 执行主函数
main
主函数定义了整个部署流程的执行顺序:
- 检查所有需要的端口是否可用
- 创建目录结构并设置权限
- 检查并初始化Docker环境
- 依次启动MySQL、Tomcat和Nginx服务
- 输出部署完成信息
完整源码
以下是完整的脚本代码,可直接保存使用:
#!/bin/bash# 定义颜色变量
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # 无颜色# 定义宿主机目录(使用绝对路径,避免相对路径问题)
BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/docker-services"
MYSQL_DIR="${BASE_DIR}/mysql"
MYSQL_CONF="${MYSQL_DIR}/conf"
MYSQL_DATA="${MYSQL_DIR}/data"
MYSQL_PORT=3306TOMCAT_DIR="${BASE_DIR}/tomcat"
TOMCAT_CONF="${TOMCAT_DIR}/conf"
TOMCAT_WEBAPPS="${TOMCAT_DIR}/webapps"
TOMCAT_LOGS="${TOMCAT_DIR}/logs"
TOMCAT_PORT=8080NGINX_DIR="${BASE_DIR}/nginx"
NGINX_CONF="${NGINX_DIR}/conf"
NGINX_CONF_D="${NGINX_CONF}/conf.d"
NGINX_HTML="${NGINX_DIR}/html"
NGINX_LOGS="${NGINX_DIR}/logs"
NGINX_PORT=80# 检查端口是否被占用
check_port() {local port=$1if ss -tuln | grep -q ":$port "; thenecho -e "${RED}错误:端口 $port 已被占用(可能被其他服务占用)${NC}"echo -e "${YELLOW}请关闭占用 $port 端口的程序后重试${NC}"exit 1fi
}# 创建目录并设置精准权限(避免777过度开放)
create_directories() {echo -e "${YELLOW}创建宿主机目录结构并设置权限...${NC}"# 创建所有目录mkdir -p ${MYSQL_CONF} ${MYSQL_DATA} \${TOMCAT_CONF} ${TOMCAT_WEBAPPS} ${TOMCAT_LOGS} \${NGINX_CONF} ${NGINX_CONF_D} ${NGINX_HTML} ${NGINX_LOGS}# MySQL容器用户UID为999,设置数据目录归属chown -R 999:999 ${MYSQL_DIR}chmod -R 755 ${MYSQL_DIR}# Tomcat容器用户UID为1000,设置目录归属chown -R 1000:1000 ${TOMCAT_DIR}chmod -R 755 ${TOMCAT_DIR}# Nginx容器用户UID为101,设置目录归属chown -R 101:101 ${NGINX_DIR}chmod -R 755 ${NGINX_DIR}# 初始化Nginx默认页面(如果不存在)if [ ! -f "${NGINX_HTML}/index.html" ]; thencat > ${NGINX_HTML}/index.html << 'EOF'
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title><style>body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; }</style>
</head>
<body><h1>Welcome to nginx!</h1><p>This page is served by Nginx with host-mounted volumes.</p>
</body>
</html>
EOFchown 101:101 ${NGINX_HTML}/index.htmlfi# 初始化Nginx默认配置(如果不存在)if [ ! -f "${NGINX_CONF_D}/default.conf" ]; thencat > ${NGINX_CONF_D}/default.conf << 'EOF'
server {listen 80;server_name localhost;location / {root /usr/share/nginx/html;index index.html index.htm;}error_page 500 502 503 504 /50x.html;location = /50x.html {root /usr/share/nginx/html;}
}
EOFchown 101:101 ${NGINX_CONF_D}/default.conffiecho -e "${GREEN}宿主机目录结构创建完成${NC}"
}# 检查是否以root用户运行
if [ "$(id -u)" -ne 0 ]; thenecho -e "${RED}错误:请使用root用户运行此脚本(sudo ./script.sh)${NC}" >&2exit 1
fi# 检测系统发行版
detect_distro() {if [ -f /etc/os-release ]; then. /etc/os-releaseDISTRO=$IDVERSION=$VERSION_IDelif type lsb_release >/dev/null 2>&1; thenDISTRO=$(lsb_release -si | tr '[:upper:]' '[:lower:]')VERSION=$(lsb_release -sr)elif [ -f /etc/redhat-release ]; thenDISTRO="centos"VERSION=$(grep -oE '[0-9]+\.[0-9]+' /etc/redhat-release)elseDISTRO=$(uname -s)VERSION=$(uname -r)fi
}# 检查Docker是否已安装
check_docker() {if ! command -v docker &> /dev/null; thenecho -e "${YELLOW}Docker未安装,正在安装Docker...${NC}"install_dockerelseecho -e "${GREEN}Docker已安装${NC}"fi# 检查Docker服务是否运行if ! systemctl is-active --quiet docker; thenecho -e "${YELLOW}启动Docker服务...${NC}"systemctl start docker || {echo -e "${RED}错误:Docker服务启动失败,请检查Docker是否正常安装${NC}"exit 1}systemctl enable dockerfi
}# 安装 Docker
install_docker() {echo "检测到系统: $DISTRO $VERSION"case $DISTRO inubuntu|debian)echo "正在安装 Docker (Ubuntu/Debian)..."# 卸载旧版本apt-get remove -y docker docker-engine docker.io containerd runc || true# 安装依赖apt-get updateapt-get install -y \apt-transport-https \ca-certificates \curl \gnupg \lsb-release# 添加 Docker 官方 GPG 密钥mkdir -p /etc/apt/keyringscurl -fsSL https://download.docker.com/linux/$DISTRO/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg# 设置稳定版仓库echo \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$DISTRO \$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null# 安装 Dockerapt-get updateapt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin;;centos|rhel|fedora)echo "正在安装 Docker (CentOS/RHEL/Fedora)..."# 卸载旧版本yum remove -y docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine || true# 安装依赖yum install -y yum-utils# 添加 Docker 仓库if [ "$DISTRO" == "fedora" ]; thenyum-config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repoelseyum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repofi# 安装 Dockeryum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin;;arch|manjaro)echo "正在安装 Docker (Arch/Manjaro)..."pacman -Sy --noconfirm docker;;*)echo "不支持的 Linux 发行版: $DISTRO"echo "尝试使用通用安装脚本..."curl -fsSL https://get.docker.com -o get-docker.shsh get-docker.shrm get-docker.sh;;esac# 启动并启用 Docker 服务systemctl enable --now dockercat <<-EOF > /etc/docker/daemon.json {"registry-mirrors": ["https://docker.xuanyuan.me"]}EOFsystemctl daemon-reloadsystemctl restart docker# 验证安装docker --versionecho "Docker 安装完成!"# 添加当前用户到 docker 组(可选)read -p "是否将当前用户添加到 docker 组? (y/n): " choiceif [ "$choice" = "y" ] || [ "$choice" = "Y" ]; thenif ! grep -q docker /etc/group; thengroupadd dockerficurrent_user=$(logname)usermod -aG docker $current_userecho "已将用户 $current_user 添加到 docker 组,可能需要重新登录才能生效"fi
}# 拉取镜像(确保镜像存在)
pull_image() {local image=$1echo -e "${YELLOW}拉取镜像 $image(确保最新版本)...${NC}"if ! docker pull $image; thenecho -e "${RED}错误:拉取镜像 $image 失败,请检查网络${NC}"exit 1fi
}# 启动服务并检查状态(通用函数)
start_service() {local service_name=$1local container_name=$2local image=$3local run_cmd=$4local port=$5# 检查容器是否已存在if [ "$(docker ps -aq -f name=^/${container_name}$)" ]; then# 尝试启动已存在的容器echo -e "${YELLOW}检测到已有 $service_name 容器,尝试启动...${NC}"docker start $container_name || {echo -e "${RED}错误:启动已有 $service_name 容器失败${NC}"echo -e "${YELLOW}容器日志:${NC}"docker logs $container_name 2>&1 | head -20exit 1}sleep 5else# 启动新容器echo -e "${YELLOW}启动新的 $service_name 容器...${NC}"eval $run_cmd || { # 使用eval执行动态命令echo -e "${RED}错误:启动 $service_name 容器失败${NC}"exit 1}sleep 10 # 等待服务初始化fi# 检查容器是否运行if ! docker inspect -f '{{.State.Running}}' $container_name 2>/dev/null | grep -q "true"; thenecho -e "${RED}错误:$service_name 容器未正常运行${NC}"echo -e "${YELLOW}容器日志:${NC}"docker logs $container_name 2>&1 | head -20exit 1fi# 检查端口是否监听(确认服务启动成功)if ! ss -tuln | grep -q ":$port "; thenecho -e "${RED}错误:$service_name 端口 $port 未监听(服务未启动)${NC}"echo -e "${YELLOW}容器日志:${NC}"docker logs $container_name 2>&1 | head -20exit 1fiecho -e "${GREEN}$service_name 启动成功${NC}"
}start_mysql() {echo -e "\n${YELLOW}===== 初始化并启动MySQL =====${NC}"local image="mysql:8.0"local container_name="mysql-service"local mysql_password="root123456"# 检查端口check_port $MYSQL_PORT# 拉取镜像pull_image $image# 首次运行:目录为空时,让容器自动初始化(无需临时容器)if [ -z "$(ls -A ${MYSQL_CONF})" ]; thenecho -e "${YELLOW}MySQL配置目录为空,将由容器自动初始化${NC}"fiif [ -z "$(ls -A ${MYSQL_DATA})" ]; thenecho -e "${YELLOW}MySQL数据目录为空,首次启动将自动初始化数据库${NC}"fi# 构建启动命令(直接映射目录,容器会自动处理初始化)local run_cmd="docker run -d \--name $container_name \-p $MYSQL_PORT:3306 \-e MYSQL_ROOT_PASSWORD=$mysql_password \-e MYSQL_DATABASE=default_db \-v ${MYSQL_CONF}:/etc/mysql/conf.d \-v ${MYSQL_DATA}:/var/lib/mysql \--restart always \$image \--character-set-server=utf8mb4 \--collation-server=utf8mb4_unicode_ci"# 启动并检查start_service "MySQL" $container_name $image "$run_cmd" $MYSQL_PORT# 输出连接信息echo -e "${GREEN}MySQL服务信息:${NC}"echo -e " 地址: $(hostname -I | awk '{print $1}'):$MYSQL_PORT"echo -e " 用户名: root"echo -e " 密码: $mysql_password"echo -e " 连接示例: mysql -h $(hostname -I | awk '{print $1}') -P $MYSQL_PORT -u root -p"
}# 启动Tomcat
start_tomcat() {echo -e "\n${YELLOW}===== 初始化并启动Tomcat =====${NC}"local image="tomcat:9.0"local container_name="tomcat-service"# 检查端口check_port $TOMCAT_PORT# 拉取镜像pull_image $image# 首次运行:从镜像直接提取默认配置和webappsif [ -z "$(ls -A ${TOMCAT_CONF})" ] || [ -z "$(ls -A ${TOMCAT_WEBAPPS})" ]; thenecho -e "${YELLOW}首次运行,从镜像提取Tomcat配置和应用...${NC}"# 创建临时容器但不运行(--rm 退出后自动删除)docker create --name tomcat-temp $image# 直接复制文件(无需启动容器)docker cp tomcat-temp:/usr/local/tomcat/conf/. ${TOMCAT_CONF}/ || {echo -e "${RED}错误:复制Tomcat配置失败${NC}"; docker rm tomcat-temp >/dev/null 2>&1;exit 1;}docker cp tomcat-temp:/usr/local/tomcat/webapps.dist/. ${TOMCAT_WEBAPPS}/ || {echo -e "${RED}错误:复制Tomcat应用失败${NC}";docker rm tomcat-temp >/dev/null 2>&1;exit 1;}# 删除临时容器docker rm tomcat-temp >/dev/null 2>&1echo -e "${GREEN}Tomcat配置和应用提取完成${NC}"elseecho -e "${YELLOW}检测到已有Tomcat配置,跳过复制${NC}"fi# 构建启动命令local run_cmd="docker run -d \--name $container_name \-p $TOMCAT_PORT:8080 \-v ${TOMCAT_CONF}:/usr/local/tomcat/conf \-v ${TOMCAT_WEBAPPS}:/usr/local/tomcat/webapps \-v ${TOMCAT_LOGS}:/usr/local/tomcat/logs \--restart always \$image"# 启动并检查start_service "Tomcat" $container_name $image "$run_cmd" $TOMCAT_PORT# 输出访问信息echo -e "${GREEN}Tomcat服务信息:${NC}"echo -e " 访问地址: http://$(hostname -I | awk '{print $1}'):$TOMCAT_PORT"echo -e " 部署应用: 将WAR包放入 ${TOMCAT_WEBAPPS} 目录即可"
}# 启动Nginx
start_nginx() {echo -e "\n${YELLOW}===== 初始化并启动Nginx =====${NC}"local image="nginx:latest"local container_name="nginx-service"# 检查端口check_port $NGINX_PORT# 拉取镜像pull_image $image# 创建必要的目录结构mkdir -p ${NGINX_CONF} ${NGINX_CONF_D} ${NGINX_HTML} ${NGINX_LOGS}# 首次运行:从镜像提取默认配置(使用docker create)if [ ! -f "${NGINX_CONF}/nginx.conf" ]; thenecho -e "${YELLOW}首次运行,提取Nginx默认配置...${NC}"# 创建但不启动临时容器docker create --name nginx-temp ${image} >/dev/null || {echo -e "${RED}错误:创建临时容器失败${NC}"; exit 1;}# 复制主配置文件docker cp nginx-temp:/etc/nginx/nginx.conf ${NGINX_CONF}/ || {echo -e "${RED}错误:复制nginx.conf失败${NC}"; docker rm nginx-temp >/dev/null 2>&1;exit 1;}# 复制conf.d配置(注意目标路径处理)docker cp nginx-temp:/etc/nginx/conf.d/. ${NGINX_CONF_D}/ || {echo -e "${RED}错误:复制conf.d配置失败${NC}";docker rm nginx-temp >/dev/null 2>&1;exit 1;}# 删除临时容器docker rm nginx-temp >/dev/null 2>&1echo -e "${GREEN}Nginx默认配置提取完成${NC}"elseecho -e "${YELLOW}检测到已有Nginx配置,跳过复制${NC}"fi# 确保存在默认HTML文件if [ ! -f "${NGINX_HTML}/index.html" ]; thenecho "<h1>Welcome to Nginx!</h1>" > ${NGINX_HTML}/index.htmlfi# 构建启动命令local run_cmd="docker run -d \--name $container_name \-p $NGINX_PORT:80 \-v ${NGINX_CONF}/nginx.conf:/etc/nginx/nginx.conf \-v ${NGINX_CONF_D}:/etc/nginx/conf.d \-v ${NGINX_HTML}:/usr/share/nginx/html \-v ${NGINX_LOGS}:/var/log/nginx \--restart always \$image"# 启动并检查start_service "Nginx" $container_name $image "$run_cmd" $NGINX_PORT# 输出访问信息echo -e "${GREEN}Nginx服务信息:${NC}"echo -e " 访问地址: http://$(hostname -I | awk '{print $1}'):$NGINX_PORT"echo -e " 配置目录: ${NGINX_CONF_D}/"echo -e " 日志目录: ${NGINX_LOGS}/"
}# 主函数
main() {echo -e "${YELLOW}===== 开始部署 MySQL + Tomcat + Nginx 服务 =====${NC}"# 先检查端口(避免启动后冲突)check_port $MYSQL_PORTcheck_port $TOMCAT_PORTcheck_port $NGINX_PORTdetect_distro# 创建目录create_directories# 检查并安装dockercheck_docker# 配置加速# 启动服务start_mysqlstart_tomcatstart_nginxecho -e "\n${GREEN}===== 所有服务部署完成 =====${NC}"echo -e "服务状态查看:docker ps"echo -e "配置文件目录:${BASE_DIR}"
}# 执行主函数
main
使用方法
- 将上述代码保存为
deploy_services.sh
- 赋予执行权限:
chmod +x deploy_services.sh
- 以root权限运行:
sudo ./deploy_services.sh
脚本运行完成后,你可以通过以下方式验证服务是否正常:
- 访问Nginx:
http://服务器IP
- 访问Tomcat:
http://服务器IP:8080
- 连接MySQL:
mysql -h 服务器IP -P 3306 -u root -p
(密码:root123456)
测试输出
root@seaua-PC:/home/seaua/Desktop# ./web环境搭建.sh
===== 开始部署 MySQL + Tomcat + Nginx 服务 =====
创建宿主机目录结构并设置权限...
宿主机目录结构创建完成
Docker已安装===== 初始化并启动MySQL =====
拉取镜像 mysql:8.0(确保最新版本)...
8.0: Pulling from library/mysql
Digest: sha256:f483084b37081f1574c8d4e4485cd9028abcb022973c44c079a520eb01801d55
Status: Image is up to date for mysql:8.0
docker.io/library/mysql:8.0
MySQL配置目录为空,将由容器自动初始化
MySQL数据目录为空,首次启动将自动初始化数据库
启动新的 MySQL 容器...
df0ad8425ab5c4e623a7f93268697ac893d71c408dd17688a73e268aa4d1fb5f
MySQL 启动成功
MySQL服务信息:地址: 192.168.0.133:3306用户名: root密码: root123456连接示例: mysql -h 192.168.0.133 -P 3306 -u root -p===== 初始化并启动Tomcat =====
拉取镜像 tomcat:9.0(确保最新版本)...
9.0: Pulling from library/tomcat
Digest: sha256:ac16eda40944d9b82eda4b241847f820cc6d75c77270cec313bf7aa762cf51f2
Status: Image is up to date for tomcat:9.0
docker.io/library/tomcat:9.0
首次运行,从镜像提取Tomcat配置和应用...
410cfc0c04a66d10a572b686fa86f411d58807b4bad903fdacf2a424a4d108c0
Successfully copied 225kB to /home/seaua/Desktop/docker-services/tomcat/conf/
Successfully copied 7.06MB to /home/seaua/Desktop/docker-services/tomcat/webapps/
Tomcat配置和应用提取完成
启动新的 Tomcat 容器...
bd0cec3eaadac15f9bc9edc499f841808b86312fd21ff6b41d9e9d91a0612f4a
Tomcat 启动成功
Tomcat服务信息:访问地址: http://192.168.0.133:8080部署应用: 将WAR包放入 /home/seaua/Desktop/docker-services/tomcat/webapps 目录即可===== 初始化并启动Nginx =====
拉取镜像 nginx:latest(确保最新版本)...
latest: Pulling from library/nginx
Digest: sha256:33e0bbc7ca9ecf108140af6288c7c9d1ecc77548cbfd3952fd8466a75edefe57
Status: Image is up to date for nginx:latest
docker.io/library/nginx:latest
首次运行,提取Nginx默认配置...
Successfully copied 2.56kB to /home/seaua/Desktop/docker-services/nginx/conf/
Successfully copied 3.58kB to /home/seaua/Desktop/docker-services/nginx/conf/conf.d/
Nginx默认配置提取完成
启动新的 Nginx 容器...
c25a0dacc6ef45d8b2b1892b131542a6b81a83ec9475ec4c8a3249862e10d988
Nginx 启动成功
Nginx服务信息:访问地址: http://192.168.0.133:80配置目录: /home/seaua/Desktop/docker-services/nginx/conf/conf.d/日志目录: /home/seaua/Desktop/docker-services/nginx/logs/===== 所有服务部署完成 =====
服务状态查看:docker ps
配置文件目录:/home/seaua/Desktop/docker-services
测试截图
tomcat
nignx
mysql
运行容器
持久化问题
不必担心持久化问题,因为已经做了本地映射,映射目录如下:
docker-services/
├── mysql/ # MySQL配置和数据
│ ├── conf/ # 配置文件
│ └── data/ # 数据库数据
├── tomcat/ # Tomcat配置和应用
│ ├── conf/ # 配置文件
│ ├── webapps/ # Web应用
│ └── logs/ # 日志
└── nginx/ # Nginx配置和网页├── conf/ # 主配置├── conf.d/ # 站点配置├── html/ # 网页文件└── logs/ # 日志
示例图:
总结
这个一键部署脚本通过Docker容器化技术,实现了MySQL、Tomcat和Nginx环境的快速搭建。脚本的设计遵循了以下原则:
- 自动化:尽可能减少人工干预,实现真正的一键部署
- 可靠性:完善的错误检查和状态验证,确保服务正常启动
- 安全性:精细化的权限控制,避免过度开放权限
- 可维护性:清晰的目录结构和配置管理,便于后续维护
- 可扩展性:模块化设计,便于添加新的服务或功能
无论是开发测试环境的快速搭建,还是小型生产环境的部署,这个脚本都能大大提高工作效率,避免重复劳动。