前言:
本人防止忘记知识,笔记使用,以下是我理解的,有可能不对,多多指正。这次使用docker-compose自动编排部署项目到阿里云服务器,以前都是玩虚拟机和二进制包部署项目,不要太麻烦~,部署环境都得好长时间,报个错就得卡一会大儿。老师说现在大点的公司都用k8s编排部署项目了,耐于自己没钱,只有一个服务器,只能用docker-compose这个了,k8s部署项目我就在虚拟机玩了。
部署项目说明:
JDK:8
后端:springboot 2.7.6
前端:vue
数据库:mysql 8.0.33
缓存:redis 7.0.11
修改springboot里的application.yml
spring:
redis:
host: redis #这里写成docker-compose.yml里自定义的服务名称,它会自动映射到redis地址
password: ${SPRING_REDIS_PASSWORD} #修改为这样,会自动映射
database: ${SPRING_REDIS_DATABASE} #修改为这样,会自动映射
datasource:
username: ${SPRING_MYSQL_USERNAME} #用服务器里的mysql的用户名,会自动映射
password: ${SPRING_MYSQL_PASSWORD} #用服务器里的mysql的密码,会自动映射
#这里把原来的localhost写成docker-compose.yml里自定义的服务名称,它会自动映射到mysql地址
url: jdbc:mysql://mysql:3306/${SPRING_MYSQL_DATABASE}?useSSL=false&characterEncoding=utf-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
一、安装docker
# 卸载旧版本的Docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
# 安装必要的依赖包
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# 添加Docker仓库,使用网易云的源加速安装docker,我发现网易云的比阿里云的速度更快
sudo yum-config-manager --add-repo https://mirrors.163.com/docker-ce/linux/centos/docker-ce.repo
#启用Docker CE仓库:运行以下命令启用Docker CE仓库:
yum-config-manager --enable docker-ce-nightly
# 安装Docker
sudo yum install docker-ce docker-ce-cli containerd.io
# 启动Docker并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker
安装docker-compose
#在执行该命令前,需要先安装好 curl 命令
sudo yum install curl
#下载并安装 Docker Compose 工具
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
#慢的话用
sudo curl -L "https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
#赋予可执行权限
sudo chmod +x /usr/local/bin/docker-compose
#检查版本号
docker-compose --version
编写Dockerfile文件
多次测试我发现这个文件里的执行顺序是从上往下的执行
FROM openjdk:8-jre-alpine #我只需要使用jdk8运行这个jar包,不在这里进行开发。所以用官方推出的轻量版
#VOLUME /root/javaWeb:/app #使容器/app目录挂载到主机目录/root/javaWeb,这两个目录就相当于一个目录了,哪边目录中文件多或少了都实时响应到另一个目录,也会多与少。
COPY ${PWD}/javaWeb/jar /app #${PWD}这个看着高大上其实就是当前目录的意思,也可以用.代替
COPY ./javaWeb/app_log /app/log
EXPOSE 7001/tcp #暴露给外界7001端口,因为我的springboot项目是7001,这样才可以访问到
WORKDIR /app #指定工作目录,大白话就是你cd到了/app目录
ENTRYPOINT ["java", "-jar", "shareBike-0.0.1-SNAPSHOT.jar"]#启动容器自动执行此命令
# COPY也能和VOLUME一样实时响应,区别在于VOLUME多了个数据卷,数据卷只能增不能删,主机目录和容器目录之间传递都经过数据卷进行备份一份,所以数据卷会越来越大,要定期清理数据卷:
编写docker-compose.yml
version: "3.7"
services:
nginx:
image:
nginx:1.25.0
ports:
- "80:80"
volumes:
- /root/nginx/logs:/var/log/nginx #日志
- /root/nginx/nginx.conf:/etc/nginx/nginx.conf:ro #:ro意思是nginx只有读权限,保护文件不被篡改
- /root/nginx/html/dist:/usr/share/nginx/html:ro
container_name: nginx
mysql: #服务名自定义
image:
mysql:8.0.33 #依赖的docker镜像,docker上没有就会去拉取
volumes:
- /root/mysql/data:/var/lib/mysql/ #将 MySQL 的数据目录挂载到容器中
- /root/mysql/sql:/docker-entrypoint-initdb.d/ #导入SQL文件的地方,把sql文件放进去自动导入数据库,前提是这个表有数据库创建的命令,而且只有在第一次容器启动的时候才会自动导入,如果你加了挂载,还要rm -rf /root/mysql/data/*,然后重新创建容器启动容器才能自动导入。
#- /root/mysql/conf/:/etc/mysql/conf.d/ # 将 MySQL 的配置文件挂载到容器中。
#- /root/conf/mysql/my.cnf:/etc/mysql/my.cnf # 将 MySQL 的配置文件挂载到容器中。
container_name: mysql8.0 #设置容器名字
ports: #端口映射 主机端口:容器端口,按理来说完全不用暴露此端口,我暴露是我做开发也用想用这个mysql,要连接我的sqlyog客户端,就这样设置了,额!其实为了安全最好不要暴露,mysql可以在自己电脑配置一个。
- "3306:3306"
environment:
- TZ: Asia/Shanghai #设置时区,设置不设置一个样,都是一样用
- MYSQL_ROOT_PASSWORD: "Act274352" #设置mysql的密码
#- MYSQL_DATABASE: hxh # 设置数据库名,创建一个默认的空数据库
command:
- --default-authentication-plugin=mysql_native_password #验证密码的插件用旧的mysql_native_password,因为不用这个插件我的sqlyog会密码乱码登不上去,我的sqlyog太老了,老师给的,但它免费,就继续用下去,
rabbitmq:
image: rabbitmq:management-alpine
container_name: rabbitmq
environment:
- RABBITMQ_DEFAULT_USER=admin
- RABBITMQ_DEFAULT_PASS=123456
- SET_CONTAINER_TIMEZONE=true
- CONTAINER_TIMEZONE=Asia/Shanghai
restart: always
ports:
- "15672:15672"
- "5672:5672"
- "61613:61613"
volumes:
- ./rabbitmq/data/:/var/lib/rabbitmq/
- ./rabbitmq/log/:/var/log/rabbitmq/
logging:
driver: "json-file"
options:
max-size: "200k"
networks:
- hxhshr
redis:
image:
redis:7.0.11
ports: #端口映射 主机端口:容器端口,按理来说完全不用暴露此端口,我暴露是我做开发也用想用这个redis,要连接我的redis客户端,就这样设置了,额!其实为了安全最好不要暴露,redis可以在自己虚拟机里配置一个或者取github上拉一个windows版的。
- "6379:6379"
volumes:
- /root/redis/data:/data
#volumes: 另一种配置方式
# - /root/conf/redis/redis.conf:/usr/local/etc/redis/redis.conf
#command: redis-server /usr/local/etc/redis/redis.conf
container_name: redis
command: redis-server --requirepass "274352" #设置redis的密码
sharebike:
build: /root/ #在/root目录下查找Dockerfile自动创建
image:
sharebike:1.0.0 #声明我的springboot的项目版本
container_name: sharebike
volumes:
- /root/javaWeb/jar:/app #当在Dockerfile中挂载了或者使用了COPY,则这里不用配置,挂载目录这个是1是因为我springboot里我用了logback,记录日志用的,这个很有用,哪一天部署的项目崩了,就可以看错误日志。2是因为好重新部署新版本
ports:
- "7001:7001" #映射端口,这里多提依据当你这里配置了之后,Dockerfile里的EXPOSE就不用配置了
depends_on: #我这个项目依赖于redis和mysql
- mysql
- redis
environment:
- SPRING_MYSQL_USERNAME=root #这里的环境变量对应springboot.yml修改的那些属性如${SPRING_MYSQL_USERNAME}
- SPRING_MYSQL_PASSWORD=Act274352
- SPRING_REDIS_PASSWORD=274352
- SPRING_REDIS_DATABASE=0 #自定义的环境变量,使我这个项目redis使用redis的库0,也就是第1个库.这样做的好处是部署多个项目,就可以指定多个redis库,相互redis不影响
不使用rabbitmq:latest镜像而选择rabbitmq:management-alpine镜像有几个原因:
管理界面:通过指定management-alpine,您将获得RabbitMQ的管理插件,该插件提供了Web界面,使您能够轻松地监视和管理RabbitMQ实例。这对于开发和测试环境非常有用,因为它提供了可视化和易于使用的界面。而rabbitmq:latest可能不包含这个管理界面。
轻量级和性能:management-alpine是基于Alpine Linux的,Alpine Linux是一种轻量级的Linux发行版,只有最小的基础包,因此它比其他常见的Linux发行版更小、更快。使用Alpine作为基础镜像可以减少Docker容器的体积,提高启动速度。而rabbitmq:latest可能基于一个较大的Linux发行版。
安全性:Alpine Linux经常被用作安全基准,因为它默认启用了许多安全功能,如强制使用最小权限原则和运行时完整性检查。这些安全功能可以提高RabbitMQ容器的安全性。而rabbitmq:latest可能不包含这些安全特性。
社区支持:使用官方的RabbitMQ镜像可以确保您获得来自RabbitMQ社区的支持和更新。社区经常维护和更新镜像,以确保其包含最新的安全补丁和功能。而其他非官方的latest镜像可能没有这样的社区支持。
去阿里云服务器控制台开启安全组
安全起见,只开启7001项目的端口就行,比如3306、6379不开启也行,容器之间能通过同一个默认网关相互访问
在创建文件夹
mkdir -p /root/nginx/
mkdir -p /root/nginx/html/
mkdir -p /root/mysql/sql/
mkdir -p /root/mysql/data/
mkdir -p /root/mysql/conf/
mkdir -p /root/redis/data
mkdir -p /root/javaWeb/jar
mkdir -p /root/javaWeb/app_log
导入文件
1、在/root/nginx/目录下创建nginx.conf文件
touch /root/nginx/nginx.conf
worker_processes 1;
error_log /var/log/nginx/error.log;#配置的错误日志的路径,这是容器里面的路径,不是主机路径
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#自定义的日志格式化形式,名字为main
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
keepalive_timeout 65;
server {
listen 80; # 指定 Nginx 监听的 HTTP 请求端口为 80 端口
server_name hxhshr.info 192.168.174.131; # 指定该虚拟主机的域名或 IP 地址,例如 www.example.com 或者 192.168.0.1
access_log /var/log/nginx/192.168.174.131.access.log main; #这个也是日志,记录的是请求,以main这种格式化方式记录日志,就是说每访问一次请求,这里被记录到。这个很有用,比如分析客户请求最多的是谁,然后就加redis等提高性能。
root /usr/share/nginx/html; # 指定项目静态文件的根目录
index index.html; # 指定 Nginx 处理该虚拟主机请求的默认文件名为 index.html
location / {
#try_files $uri $uri / /index.html last;
try_files $uri /index.html last; #如果客户端请求的文件不存在,将会首先检查 $uri 中指定的文件,如果文件不存在,则会尝试返回 /index.html 文件。因为这里使用了 last 参数,因此 Nginx 将会停止尝试尝试其它的配置,并将 /index.html 返回给客户端。也就是说如果客户端请求的是一个不存在的文件,则将会被转到 /index.html 页面;如果客户端请求的是一个已存在的文件,则返回该文件。
#add_header Cache-Control "no-cache"; #禁用缓存配置
#add_header Pragma "no-cache"; #禁用缓存配置
#expires 0;#禁用缓存配置之0秒过期
}
location /api/ { #nginx的反向代理功能,我认为反向代理就是做的跨域处理而已。前端nginx后端这三者任意一方做了跨域就能通了。注意如果你在前端vue.config.js文件中做的跨域,那么这个配置文件是不会被打包到dist里的,所以vue在这个文件里做的跨域只在开发阶段有效,你nginx还得做代理才行。
proxy_pass http://192.168.174.131:7001; # 代理到应用服务器端口,这里可以不做代理,因为后端跨域已经做好了
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
#https的写法 建议别用我的方法,阿里云里有相关https配置的文档,它的更牛点。
#proxy_set_header Host $http_host;
#proxy_set_header X-Real-IP $remote_addr;
#proxy_set_header X-Forwarded-Proto https;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
2、导入前端的dist.zip包
解压到/root/root/nginx/html/下yum install unzip
unzip -q dist.zip
-q参数是静默解压,不输出解压日志
3、导入后端jar包
直接导入到/root/javaWeb/jar/下
启动部署
#docker-compose up -d --build命令会重新构建、创建并启动Compose文件中定义的所有服务(services)。其中,-d参数表示将服务在后台运行,--build参数表示如果需要则构建镜像(如果服务依赖的镜像或Dockerfile有变更)。这个命令会遵循Compose文件中指定的依赖关系,而不会重建已经在运行的服务。
#注意,在每次使用docker-compose up命令启动服务时,Docker Compose都会检查本地系统以查找镜像。如果需要重新构建镜像,请使用--build参数。如果不需要重新构建镜像,请将其省略。
docker-compose up -d --build
#不指定哪个服务就是yml中的全部服务
查看日志
docker logs sharebike
这命令里的sharebike是容器名称
==================== 到这里就部署成功了===================
==================== 到这里就部署成功了===================
==================== 到这里就部署成功了===================
======================= 不用往下看了==================
下面是我的随便写的,记录我的第一次的点点滴滴
删除所有数据卷,一路y
docker volume prune
删除所有镜像
docker rmi -f $(docker images -aq)
删除所有容器
docker rm -f $(docker ps -aq)
查看启动的容器
docker ps
查看所有容器
docker ps -a
查看容器的日志,看看我的项目到底启动成功了没
docker logs 容器id/容器名称
在这里插入图片描述
还有个地方搞不懂,我这里不能这样写如:WORKDIR /app/jar 这样多级目录,启动会报错说找不到这个路经
更换项目版本
第一步
docker-compose stop sharebike
第二步
把jar包换一下
第三步
docker-compose start sharebike
新版本部署成功
前端项目换版本也一样先停nginx服务,然后更替文件,再次启动。
坑:
这个在root目录下,nginx没有权限访问,所以要
https的nginx配置
worker_processes 1;
error_log /var/log/nginx/error.log;
events {
worker_connections 1024;
}
http {
include 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"';
sendfile on;
keepalive_timeout 65;
server {
#监听http的80端口,监听https的443端口
listen 80;
listen 443 ssl;
server_name hxhshr;
access_log /var/log/nginx/192.168.174.134.access.log main;
ssl_certificate /data/ssl/hxhshr.pem;#证书
ssl_certificate_key /data/ssl/hxhshr.key;#私钥
ssl_session_cache shared:SSL:1m; #缓存
ssl_session_timeout 5m; #设置缓存5分钟过期
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;#使用SHA256加密,不使用不安全的MD5,ADH等加密
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
#开启gzip
gzip_static on;
gzip_proxied expired no-cache no-store private auth;
gzip on;
gzip_buffers 32 4K;
gzip_comp_level 6;
gzip_min_length 1k;
gzip_types application/javascript text/css text/xml;
gzip_disable "MSIE [1-6]\.";#配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本支持)
gzip_vary on;
#禁止Scrapy等工具的抓取
if ($http_user_agent ~* (PostmanRuntime|Curl|HttpClient|Java)) {
return 403;
}
#如果是http则转发更安全的https
if ($scheme = http) {
return 301 https://$host$request_uri;
}
root /usr/share/nginx/html;
index index.html index.htm;
location / {
try_files $uri $uri/ /index.html;
#add_header Cache-Control "no-cache";
#add_header Pragma "no-cache";
#expires 0;
}
location /api/ {
#这里的sharebike是docker-compose.yml里声明的服务名,能自动映射到地址;
proxy_pass http://sharebike:7001;
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_ssl_session_reuse on;
proxy_set_header X-Forwarded-Proto https;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
部署项目没必要开放3306,6379等端口,在开发阶段如果想用mysql或者redis可以先开放一下,部署时关闭就行了,只开放80和443端口就行,因为dicker-compose编排的容器能自动映射到各服务的地址。
如:docker-compose.yml这样写
version: "3.7"
services:
nginx:
image:
nginx:1.25.0
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
ports:
- "80:80"
- "443:443"
volumes:
- /root/nginx/https:/data/ssl
- /root/nginx/logs:/var/log/nginx
- /root/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- /root/nginx/html/dist:/usr/share/nginx/html:ro
container_name: nginx
mysql:
image:
mysql:8.0.33
container_name: mysql8.0
#ports:
# - "3306:3306"
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: "Act274352"
volumes:
- /root/mysql/sql/:/docker-entrypoint-initdb.d/
- /root/mysql/data/:/var/lib/mysql/
- /root/mysql/conf/:/etc/mysql/conf.d/
command:
- --default-authentication-plugin=mysql_native_password
redis:
image:
redis:7.0.11
#ports:
# - "6379:6379"
volumes:
- /root/redis/data:/data
container_name: redis
command: redis-server --requirepass "274352"
sharebike:
build: .
image:
sharebike:1.0
container_name: sharebike
volumes:
- /root/javaWeb/jar:/app
#ports:
# - "7001:7001"
depends_on:
- mysql
- redis
environment:
- SPRING_MYSQL_USERNAME=root
- SPRING_MYSQL_PASSWORD=Act274352
- SPRING_REDIS_PASSWORD=274352
- SPRING_REDIS_DATABASE=0
这里提一嘴,没有正规的https证书也可以配置https,自己给自己签发一个证书
第一步 安装可以生成证书的工具
yum install openssl openssl-devel -y第二步 生成一个 RSA 私钥
openssl genrsa -out server.key 2048
- openssl: 表示使用 OpenSSL 工具;
- genrsa: 表示生成 RSA 私钥;
- -out server.key: 指定生成的私钥文件名为 server.key,并将其保存在当前工作目录中;
- 2048: 指定 RSA 私钥的长度为 2048 比特,这是一个常见的安全长度,适合用于 SSL/TLS 加密通信等场景。
因此,执行该命令会在当前工作目录中生成一个名为 server.key 的 RSA 私钥文件,该文件的长度为 2048 比特第三步 创建一个证书签名请求,会生成一个server.csr,没啥用就是为第四步做铺垫的
openssl req -new -key server.key -out server.csr -subj “/C=CN/ST=Beijing/L=Beijing/O=power Inc./OU=Web Security/CN=power.com”
- openssl: 表示使用 OpenSSL 工具;
- req: 表示使用 X.509 证书请求管理器;
- -new: 表示创建一个新的证书请求;
- -key server.key: 指定使用之前生成的 server.key 文件作为证书请求的私钥;
- -out server.csr: 指定生成的证书请求文件名为 server.csr,并将其保存在当前工作目录中;
- -subj “/C=CN/ST=Beijing/L=Beijing/O=power Inc./OU=Web Security/CN=power.com”: 指定证书信息。其中:
/C=CN: 表示国家名称为中国 (Country Name);
/ST=Beijing: 表示省份或州的名称为北京市 (State or Province Name);
/L=Beijing: 表示城市或区域的名称为北京市 (Locality Name);
/O=power Inc.: 表示组织名称为 power Inc. (Organization Name);
/OU=Web Security: 表示组织单位名称为 Web Security (Organizational Unit Name);
/CN=power.com: 表示通用名称为 power.com (Common Name)。第四步 生成证书
openssl req -new -x509 -sha256 -key server.key -out server.crt -days 365
- openssl: 表示使用 OpenSSL 工具;
- req: 表示使用 X.509 证书请求管理器;
- -new: 表示创建一个新的证书请求;
- -x509: 表示生成自签名的 X.509 证书;自签名的 X.509 证书包含了公钥、私钥以及证书相关信息。它被直接用作 SSL/TLS 通信中的数字证书,服务器使用私钥对通信内容进行加密,客户端使用公钥对加密数据进行解密。
- -sha256: 指定使用 SHA-256 算法进行哈希处理,使用 SHA-256 哈希算法作为证书签名算法,以替代默认的 SHA-1 哈希算法。SHA-256 是一种更安全的哈希算法,具有更高的安全性和更大的哈希值长度,可以提供更可靠的保护措施来防止数字证书遭到伪造或篡改。
- -key server.key: 指定使用之前生成的 server.key 文件作为证书的私钥;
- -out server.crt: 指定生成的证书文件名为 server.crt,并将其保存在当前工作目录中;
- -days 365: 指定证书的有效期为 365 天,即 1 年。
因此,执行该命令会在当前工作目录中生成一个名为 server.crt 的自签名 X.509 证书文件,该证书的私钥与之前生成的 server.key 文件匹配,并且证书的有效期为 1 年第五步 总结 会生成三个文件也就两个有用分别是私钥server.key和证书server.crt,私钥server.key就可以代替上面nginx中配置的hxhshr.key的位置,同理,证书server.crt可以代替上面nginx中配置的hxhshr.pem的位置。注意这样配置一样用的是https协议,但是这是你给你自己签发的证书,没有可信度所以浏览器根本不相信你是安全的,所以依旧会报警告:如图
要想解除警告点击高级按钮跟着来就行了。正规的证书会绑定特定的唯一服务器地址,在浏览器访问时会对该证书中包含的域名和 IP 地址进行验证。如果证书中包含的域名和 IP 地址与用户访问的真实域名和 IP 地址不相符,则一样会提示安全警告。也就是说我用我的虚拟机盗用我阿里云服务器上的https正规证书也会出现安全警告。
这是阿里云服务器上部署的自然不会出现安全警告如图所示:
ps -ef | grep redis-server