提示:本次部署采用centos7服务器,使用nginx进行反向代理,运行docker容器完成上线。小白看完这篇都会了!
补充说明:未经本人同意禁止转载!本文的shell代码及dockerfile代码请在linux环境下使用vim复制粘贴运行,不然会出现乱码!本文也提供了这些文件的下载地址,详情查看文末!
一、环境提前准备工作。
1.centos云服务器
国内比较出名的云服务器属阿里云、腾讯云、百度云,三家各有优劣,自行了解比较,并选择自己适合的购买就行,新用户有很大的优惠。
阿里云服务器新用户链接:https://www.aliyun.com/product/ecs?userCode=m3bbolgr
腾讯云服务器新老用户活动链接:https://cloud.tencent.com/act/cvmgift?hash=I7h4cIANzuM0Vd0J&fromSource=gwzcw.1293314.1293314.1293314&cps_key=98e3b13dc4a1659435daf62289161ee5
百度云服务器新用户链接:https://cloud.baidu.com/campaign/2021autdiscount/index.html?track=cp:nsem
2.服务器安装Docker
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
sudo yum -y install docker-ce
sudo service docker start
强烈推荐配置阿里云镜像加速器!不然好多镜像从官方仓库拉不下来!
剩下的具体配置,这个页面也教你怎么配了,不多说。
3.编写dockerfile,创建project容器。
包含前后端打包的环境,将项目放进去,包含git可以及时更新项目
FROM centos:7
MAINTAINER luoqianyi<3228077562@qq.com>
RUN yum -y update \
&& yum -y install vim \
&& yum -y install git \
&& yum -y install wget \
&& yum -y install java-1.8.0-openjdk-devel.x86_64 \
&& yum-config-manager --add-repo http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo \
&& yum-config-manager --enable epel-apache-maven\
&& yum install -y apache-maven \
&& mkdir ~/.m2
RUN echo '<?xml version="1.0" encoding="UTF-8"?><settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd"><mirrors><mirror><id>nexus-aliyun</id><mirrorOf>*</mirrorOf><name>Nexus aliyun</name><url>http://maven.aliyun.com/nexus/content/groups/public</url></mirror></mirrors></settings>' > ~/.m2/settings.xml
RUN wget https://nodejs.org/dist/v14.17.5/node-v14.17.5-linux-x64.tar.xz \
&& tar xf node-v14.17.5-linux-x64.tar.xz \
&& mv node-v14.17.5-linux-x64 /usr/local
ENV PATH $PATH:/usr/local/node-v14.17.5-linux-x64/bin
RUN npm install yarn -g
CMD echo "finish---------------------------->success!"
创建完这个project_dockerfile后,进行build生成新镜像,我这里将部署用到的dockerfile放到宿主机下自己创建的dockerfiles文件夹下。那么我需要在这个文件夹下进行build的命令就是docker build -f /dockerfiles/project_dockerfile -t luoqianyi/project .
完成构建的截图如下:
等待构建的过程中可以喝杯茶放松一下。
4.宿主机创建项目文件夹,给luoqianyi/project 创建数据卷容器
将容器里的项目与宿主机挂载,通过xftp工具方便查看代码并进行一些快捷的操作,减少繁复操作。
mkdir /datavolumes
mkdir /datavolumes/project
docker run -it -v /datavolumes/project:/project --name pro luoqianyi/project bash
cd /project
git clone https://xxxx.xxxx.git
git config --global user.email "you@example.com"
git config --global user.name "Your Name"
npm install
npm run build
mvn clean install
二、具体流程
1.编写拉取镜像的后端脚本,例如mysql镜像等
此操作简化操作,省去一个一个的自己去搜去拉,这里拉取的都是官方的镜像,可以指定版本信息。
注意:如果你买的服务器配置较高,那么连mysql的时候可以连8.0版本以上,不然还是拉取5.x版本的吧,版本太高,mysql都可以拿你服务器的cpu烤肉了。如果是5.x版本的mysql,记得看一下后端连的mysql是啥版本,需不需要进行更换。
#!/bin/bash
#OR
#!/bin/sh
#Scriptname:pull_images.sh
#Author:luoqianyi
#本脚本提供的默认后端环境镜像
DEFAULT_IMAGES=('mysql' 'redis' 'tomcat' 'rabbitmq' 'zookeeper' 'nginx')
echo -e "\e[1;33m 开始进行镜像的拉取,你只需要输入镜像的名字或版本号即可,不输入直接回车代表默认拉取最新版本镜像!\e[0m"
for image in ${DEFAULT_IMAGES[@]}
do
echo -e "\e[1;31m=============开始拉取 ${image} ============================ \e[0m"
read -p "请输入版本号:" answer
if [ "${answer}" == "" ]
then
docker pull ${image}
else
docker pull "${image}:${answer}"
fi
echo -e "\e[1;32m拉取${image}:${answer}完毕!\e[0m"
done
read -p "本脚本默认镜像已拉取完毕,是否获取其它镜像,[y/n]?" res
if [ "${res}" == "" -o "${res}" == "y" -o "${res}" == "Y" ]
then
while [ "${yn}" == "" -o "${yn}" == "y" -o "${yn}" == "Y" ]
do
echo -e "\e[1;31m请输入拉取的镜像及版本,输入参考\e[0m \e[1;33m mysql:5.7\e[0m "
read img
echo -e "\e[1;31m=============开始拉取 ${img} ============================\e[0m"
docker pull "${img}"
echo -e "\e[1;32m自定义拉取${img}完毕!\e[0m"
read -p "是否继续拉取镜像?[y/n]: " yn
done
echo "脚本运行结束!"
exit 1
else
echo "脚本运行结束!"
exit 1
fi
这个脚本你可以放在服务器的某个目录下,然后在该目录下通过bash pull_images.sh 或sh pull_images.sh 可以运行此脚本,运行过程中提供了6个后端常用的镜像,根据脚本运行的提示来就行。这个只是减轻我们重复pull的过程,简化一下操作。如果你只需要安装一个两个镜像,那么大可不必使用此脚本,直接用命令一个一个拉就行。
脚本执行界面如下:
2.详细部署
2.1.检查后端代码
设置相关服务密码,确定项目中是否用到中文字体,路径中是否存在中文,更改数据库版本问题等等。。。
这里说明一下我部署中出现的一些问题,供大家解决参考:
2.1.1编码问题:路径中是否包含中文
由于是在linux服务器上运行,可能存在中文乱码的情况,前台上传文件到服务器后,服务器返回给前台的文件列表中出现中文乱码,所有的中文文件名全部变成?,英文文件名则正常显示。
由于我们项目运行是在docker里的,我们可以更改docker容器的编码即可,这样解决了编码乱码的问题。
查看docker容器编码格式:执行locale 命令;可以看到当前编码格式为POSIX,而这种编码格式不支持中文:
那么我们可以先看一下容器已经有的所有语言,执行locale -a
ok,有en_US.utf8编码(如果切换后仍然乱码,建议参考这篇文章安装中文语言包Linux中的系统语言包及UTF-8、en_US.UTF-8和zh_CN.UTF-8的区别),我们进行切换:
LANG=en_US.UTF-8 (如果有zh_CN.UTF-8,建议切换为这个)
source /etc/profile
这样一来,当前容器的编码就可以兼容中文编码了。
2.1.2.字体问题:new Font()找不到字体
2.1.3.时区问题:应用时间和系统时间不匹配
由于服务器上的时区默认是UTC的,不是北京时间,所以后端项目启动类上需要改一下代码,以springboot项目为例:
在启动类main方法里加上即可
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
但是这不是根本解决办法,因为还有其它的情况,比如mysql时区设置,难道我们也要一点一点去改吗?当然duck不必!其实根本的解决办法就是:
启动容器的时候挂载宿主机服务器的/etc/localtime与容器的/etc/localtime,以及/etc/timezone,举个栗子:
docker run -it -v /datavolumes/project:/project -v /etc/localtime:/etc/localtime -v /etc/timezone:/etc/timezone --name pro luoqianyi/project bash
2.1.4.其它问题
仔细检查,细心推敲,不断尝试,多查百度!
2.2.后端相关容器服务启动
2.2.1.在宿主机下建立一个网桥
就Docker而言,网桥网络使用软件网桥,该软件网桥允许连接到同一网桥网络的容器进行通信,同时提供与未连接到该网桥网络的容器的隔离,所以这里就很建议使用网桥了。
docker network create project-net
2.2.2.根据镜像脚本编写启动容器的脚本
#!/bin/bash
DEFAULT_IMAGES=('mysql' 'redis' 'rabbitmq:management' 'nginx')
echo -e "\e[1;33m 开始运行容器,本脚本全自动化,如果需要个性化定制,可自行修改相关代码!\e[0m"
ROOT_DIR=/datavolumes/docker
MYSQL_CONF=/datavolumes/docker/mysql/conf
MYSQL_DATA=/datavolumes/docker/mysql/data
MYSQL_LOGS=/datavolumes/docker/mysql/logs
REDIS_DATA=/datavolumes/docker/redis/data
REDIS_CONF=/datavolumes/docker/redis/redis.conf
NGINX_CONF=/datavolumes/docker/nginx/conf
NGINX_LOGS=/datavolumes/docker/nginx/logs
MYSQL_ROOT_PASSWORD=123456
REDIS_DEFAULT_PASS=123456
RABBITMQ_DEFAULT_VHOST=my-rabbit
RABBITMQ_DEFAULT_USER=guest
RABBITMQ_DEFAULT_PASS=guest
kill_ports(){
echo "$1端口占用情况如下:"
lsof -i:$1
echo "清理占用端口中..."
sudo kill -9 $(lsof -i:$1 -t)
echo "清理完毕,检查是否还有端口占用!"
lsof -i:$1
}
run_mysql(){
echo -e "\e[1;31m=============开始启动 mysql ============================ \e[0m"
sudo killall mysqld
docker rm -f pro_mysql
mkdir $ROOT_DIR/mysql
docker run -d --name pro_mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD -v $MYSQL_CONF:/etc/mysql/conf.d -v $MYSQL_DATA:/var/lib/mysql -v $MYSQL_LOGS:/var/log mysql
echo -e "\e[1;32m mysql容器启动结束!\e[0m"
}
run_redis(){
echo -e "\e[1;31m=============开始启动 redis ============================ \e[0m"
mkdir $ROOT_DIR/redis
touch $REDIS_CONF
cat>$REDIS_CONF <<END
#注释掉这部分,这是限制redis只能本地访问
#bind 127.0.0.1
#默认yes,开启保护模式,限制为本地访问
protected-mode no
#默认no,意为是否以守护进程方式启动,可后台运行,不要改为yes 否则可能redis会启动失败
daemonize no
#redis持久化(可选)
appendonly yes
#设置redis密码
requirepass $REDIS_DEFAULT_PASS
END
port=6379
kill_ports $port
docker rm -f pro_redis
docker run -p 6379:6379 --name pro_redis -v $REDIS_CONF:/etc/redis/redis.conf -v $REDIS_DATA:/data -d redis redis-server /etc/redis/redis.conf --appendonly yes
echo -e "\e[1;32m redis容器启动结束!\e[0m"
}
run_rabbitmq(){
echo -e "\e[1;31m=============开始启动 rabbitmq:management ============================ \e[0m"
port1=5672
kill_ports $port1
port2=15672
kill_ports $port2
docker rm -f pro_rabbit
docker run -d --hostname $RABBITMQ_DEFAULT_VHOST --name pro_rabbit -e RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER -e RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS -p 15672:15672 -p 5672:5672 rabbitmq:management
echo -e "\e[1;32m rabbitmq:management容器启动结束!\e[0m"
}
run_nginx(){
echo -e "\e[1;31m=============开始启动 nginx ============================ \e[0m"
mkdir $ROOT_DIR/nginx
docker rm -f pro_nginx
docker run --net host -v $NGINX_CONF:/conf -v $NGINX_LOGS:/var/log/nginx -v /datavolumes/project:/var/www/html --name pro_nginx -d nginx
docker exec -it pro_nginx mkdir /etc/nginx/conf.d
docker exec -it pro_nginx mv /etc/nginx/conf.d /conf
docker exec -it pro_nginx ln -s /conf/conf.d /etc/nginx
docker exec -it pro_nginx mv /etc/nginx/nginx.conf /conf
docker exec -it pro_nginx ln -s /conf/nginx.conf /etc/nginx
docker exec -it pro_nginx nginx -t
docker exec -it pro_nginx nginx -s reload
docker restart pro_nginx
echo -e "\e[1;32m rabbitmq:management容器启动结束!\e[0m"
}
mkdir $ROOT_DIR
run_mysql
run_redis
run_rabbitmq
run_nginx
echo -e "\e[1;33m 本脚本默认容器已启动完成!\e[0m"
docker ps
exit 1
脚本运行完成的截图如下:
2.2.3.将已经启动相关容器绑定到一个网桥下
docker network connect project-net pro
docker network connect project-net pro_mysql
docker network connect project-net pro_redis
docker network connect project-net pro_rabbit
2.2.4.解决一些小问题
上面有些容器即使启动了,但是在提供服务的时候可能还有点问题,比如说数据库的密码更改等,这时候进入容器bash,连接mysql:
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
alter user 'root'@'%' identified by '123456';
mysql> flush privileges;
不同的容器运行遇到的情况也不一样,所以还是按照上述其它问题的解决方式来解决即可。
2.3.启动前后端服务
2.3.1.服务器防火墙相关设置
我们在前面的操作中,开启的服务端口只是在服务器内开放,但是如果想让我们通过本地去访问服务器ip获取服务,还需要在云服务器控制台那里把对应端口的防火墙打开,不然访问不到,还有一件事,服务器的防火墙与控制台的防火墙不是一回事,如果控制台那里的端口开放了,但是仍然访问不到,那么就极其可能是服务器主机防火墙没有关闭!执行命令:
2.3.2.打包运行前后端项目
在准备工作阶段,我们将我们的项目放在了pro容器里,那里有我们前后端项目打包运行的环境,我们有两种选择了:
-
第一种方式: 根据luoqianyi/project镜像运行一个专门的后端项目容器和一个专门的前端项目容器,这样似乎更符合前后端分离的思想,大型项目优选这种方案。 但是我这里推荐 -
第二种方式: 前后端在一个容器里,一起部署,前端打包成静态文件后,我们通过nginx进行代理这些静态资源可以访问到,而后端只需要打包后运行打的包即可,然后nginx代理请求访问docker网桥里的服务即可,前端访问请求也就不存在跨域问题了,axios代理可以由nginx替代。普通项目优选这种方案。
以springboot项目为例运行后端项目:
docker exec -it pro bash
cd /project/[后端项目目录]
mvn spring-boot:run
docker exec -it pro ps -ef
docker exec -it pro kill -9 [带java的进程号]
docker exec -it pro bash
nohup java -server -XX:-DisableExplicitGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -jar [jar包所在的位置] > start.log 2>&1 &
kill -9 [进程号]
以vue项目为例运行前端项目:
2.3.3.服务器端同步代码的时候自动打包运行的脚本
每次想同步部署到服务器上,难道我还要重新打包再运行吗?不用怕,我这里编写了一个自动化打包运行的脚本(本来想包含git拉取代码的操作的,但是问题可能会有点多,比如说版本回退不成功等问题),可以参考使用:
#!/bin/bash
echo -e "\e[1;33m 开始自动打包运行...\e[0m"
FRONT_PRO="/project/luoqianyi_blog/blog-vue"
ADMIN_PRO="/project/luoqianyi_blog/blog-admin"
SERVER_PRO="/project/luoqianyi_blog/blog-java"
pack_front(){
if [ $FRONT_PRO!="" ]
then
cd $FRONT_PRO
yarn install
yarn run build
echo -e "\e[1;32m front packaged! \e[0m"
fi
}
pack_admin(){
if [ $ADMIN_PRO!="" ]
then
cd $ADMIN_PRO
yarn install
yarn run build
echo -e "\e[1;32m admin packaged! \e[0m"
fi
}
pack_server(){
if [ $SERVER_PRO!="" ]
then
cd $SERVER_PRO
mvn clean package
echo -e "\e[1;32m server packaged! \e[0m"
fi
}
run_server(){
if [ $SERVER_PRO!="" ]
then
cd $SERVER_PRO
ps -ef | grep java | grep -v grep | awk '{print $2}' | xargs kill -9
nohup mvn spring-boot:run > start.log 2>&1 &
fi
}
pack_front
pack_admin
pack_server
run_server
echo -e "\e[1;33m Script Finished!!\e[0m"
exit 1
进入pro容器里下的/project/,执行bash ./package_run.sh 即可。
3.nginx反向代理服务
我们在前面启动容器的时候,准备工作都已经做好了,至于为什么启动nginx的时候需要建立软链接,详情查看这篇博客:
https://blog.csdn.net/qq_42937522/article/details/108179441。
我们只需要在宿主机修改default.conf文件即可,这里根据本文部署方案提供一种配置:
upstream myserver{
server 172.17.0.4:8000;
}
server{
listen 80;
client_max_body_size 20m;
access_log /var/log/nginx/host.access.log main;
location /portal {
alias /var/www/html/[项目前台目录]/dist;
try_files $uri $uri/ /index.html?s=$uri&$args;
index index.html index.htm index.php;
}
location / {
root /var/www/html/[项目后台目录]/dist;
try_files $uri $uri/ /index.html?s=$uri&$args;
index index.html index.htm index.php;
}
location /api {
proxy_pass http://myserver;
index index.html index.htm;
}
location /swagger-ui.html {
proxy_pass http://myserver;
index index.html index.htm;
}
location /webjars {
proxy_pass http://myserver;
index index.html index.htm;
}
location /swagger-resources {
proxy_pass http://myserver;
index index.html index.htm;
}
location /v2 {
proxy_pass http://myserver;
index index.html index.htm;
}
location /[资源所在的目录名]{
alias /var/www/html/[项目后端目录]/src/main/resources/static/[资源所在的目录名];
}
}
三、本文总结
经过一番折腾,想必终于可以理顺docker整个构建的过程了吧,其实还蛮简单,但最重要的是脚本了,这可是一劳永逸的事情。希望大家不断完善哦!
四、学习参考
五、资源下载
觉得好用欢迎一键三连哦!~
|