业务背景
项目写好了,其中需要使用到一些中间件,考虑使用Docker来进行部署,但一个个初始化容器太慢了,所以考虑使用DockerCompose来指定镜像一键进行部署。
当然小编使用的是mac,但手上并没有服务器,当然愿意折腾的可以使用virtualBox安装Linux镜像,在本地搭建一个虚拟机来运行。
这篇文章主要来讲讲在mac环境下,使用 Docker DeskTop 来一键部署需要的环境
标准流程:
- idea配置ssh直接操作远程服务器,并且在服务器上安装并开发docker
- 在项目Pom文件中集成Docker插件,一键将项目镜像打包并部署到远程服务器上。
- 可以看这篇文章:Maven一键部署Springboot到Docker仓库
- docker拉取所需镜像,编写dockercompose
- dockercompose中涉及到的配置文件需要自定义(nginx)并上传到服务器上,使用dockerfile编写脚本进行上传
- 注意dockercompose中也要写挂载的配置文件地址
注意服务器需要包含基础镜像,dockercompose指定镜像才能生效,同理配置文件地址也是要有效的
DockerDeskTop
首先由于Mac的虚拟机环境太折磨人,尤其是M系列芯片之后,建议直接使用DockerDeskTop避免内耗。
首先docker desktop它其本质也是一个虚拟机,只不过它是透明的,我们在mac本地不能直接去访问或修改其下的文件,自由性就没有Linux那么高,但通常来说也不容易玩坏。
可以通过这个地址可以看到docker desktop的一些东西,它主要是用来存放镜像和容器的底层存储路径 :
- ~/Library/Containers/com.docker.docker/Data/vms/0/
DockerDeskTop挂载宿主主机配置文件
在docker中有时候是需要挂载我们编写的配置文件,在虚拟机中只需要将配置文件找到,写在指令中即可,例如:
docker run -d --name redis -p 6379:6379
-v /etc/redis/data:/data
-v /etc/redis/conf/redis.conf:/etc/redis/redis.conf
但这里DockerDeskTop显然是无法这样做的,这里就是需要将mac本地(宿主主机)的配置文件地址来进行操作,其实操作也较为简单,需要在设置中将配置文件的地址设置为共享即可,之后就可以正常的在dockercompose中写入了,这里是参考代码:
version: '3'
services:
redis:
image: redis:7
container_name: redis
command: redis-server --appendonly yes
volumes:
- /mydata/redis/data:/data #数据文件挂载
ports:
- 6379:6379
nginx:
image: nginx:1.22
container_name: nginx
volumes:
- /Users/tomsmile/Desktop/mydata/nginx/conf/nginx.conf:/etc/nginx/nginx.conf # 修改为宿主机的文件路径
- /Users/tomsmile/Desktop/mydata/nginx/html:/usr/share/nginx/html #静态资源根目录挂载
- /Users/tomsmile/Desktop/mydata/nginx/logs:/var/log/nginx #日志文件挂载
ports:
- 80:80
rabbitmq:
image: rabbitmq:3.9.11-management
container_name: rabbitmq
volumes:
- /mydata/rabbitmq/data:/var/lib/rabbitmq #数据文件挂载
ports:
- 5672:5672
- 15672:15672
elasticsearch:
image: elasticsearch:7.17.3
container_name: elasticsearch
environment:
- "cluster.name=elasticsearch" #设置集群名称为elasticsearch
- "discovery.type=single-node" #以单一节点模式启动
- "ES_JAVA_OPTS=-Xms2g -Xmx2g" #设置使用jvm内存大小
volumes:
- /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins #插件文件挂载
- /mydata/elasticsearch/data:/usr/share/elasticsearch/data #数据文件挂载
ports:
- 9200:9200
- 9300:9300
mem_limit: 4g # 设置内存限制
cpu_shares: 512 # 设置 CPU 权重
logstash:
image: logstash:7.17.3
container_name: logstash
environment:
- TZ=Asia/Shanghai
volumes:
- /Users/tomsmile/Desktop/mydata/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf # 修改为宿主机的文件路径
depends_on:
- elasticsearch #kibana在elasticsearch启动之后再启动
links:
- elasticsearch:es #可以用es这个域名访问elasticsearch服务
ports:
- 4560:4560
- 4561:4561
- 4562:4562
- 4563:4563
kibana:
image: kibana:7.17.3
container_name: kibana
links:
- elasticsearch:es #可以用es这个域名访问elasticsearch服务
depends_on:
- elasticsearch #kibana在elasticsearch启动之后再启动
environment:
- "elasticsearch.hosts=http://es:9200" #设置访问elasticsearch的地址
ports:
- 5601:5601
注意在部署es的时候,最好是能在compose文件中指明其内存,否则可能启动失败,当然也可能需要在docker-desktop中为资源添加更多空间
额外说明,如果要在docker中部署mysql5.7,可能出现镜像拉取失败的问题:
mac m2 arm64 docker安装mysql 5.7_macbook m2安装mysql5.7-CSDN博客
docker run --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql:5.7 --lower_case_table_names=1
参数顺序一定要对,–lower_case_table_names=1要加在镜像名后面,镜像名前面是参数,后面是mysql配置,不然会报错
dockercompose写法:
version: '3'
services:
mysql:
image: mysql:5.7
container_name: mysql
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
restart: always
environment:
MYSQL_ROOT_PASSWORD: root #设置root帐号密码
ports:
- 3306:3306
volumes:
- /mydata/mysql/data:/var/lib/mysql #数据文件挂载
- /mydata/mysql/conf:/etc/mysql #配置文件挂载
- /mydata/mysql/log:/var/log/mysql #日志文件挂载
宿主主机路径 /mydata/mysql/data 被映射到 容器内部路径 /var/lib/mysql。
容器内的数据将会存储在宿主主机上对应的路径上。
如果有的配置文件并不需要挂载自定义编写的配置文件,可以使用 /mydata/mysql/data 这个地址,注意 /mydata需要在DockerDeskTop 中的共享文件中进行额外添加
/mydata:实际上并不是直接对应宿主主机的物理地址,这里是DockerDeskTop自身映射的地址,需要也需要我们额外进行添加
Docker文件存放地址:
测试宿主文件挂载到docker中:Docker Desktop中安装Redis并挂载配置文件_docker desktop 挂载本地文件夹-CSDN博客
Error response from daemon: Mounts denied:
The path /mydata/rabbitmq/data is not shared from the host and is not known to Docker.
You can configure shared paths from Docker -> Preferences... -> Resources -> File Sharing.
问题描述:在Mac系统下,尝试让系统根目录下的文件与容器内文件进行映射时报错。
解决方案:在设置中,将路径设置为共享即可
继docker-maven-plugin插件的使用,下面简单讲讲在Mac上踩的坑
小编使用的是dockerdesktop来使用docker,所以一切配置都是在dockerdesktop中进行。
docker配置
在mac的idea中配备docker,并不需要额外配置什么,只需要勾选 docker for mac 即可使用
开启远程API
Windows、Mac、Linux中Docker开启远程访问API(2375端口)以及各种坑 - (App Store/公众号/小程序:分享录)
试了下在mac的dockerdesktop中直接修改deamon.json并不能开启,还会导致报错:
后续尝试将其设置修改为User,也不行,而且这种形式还需要重新指定docker的环境变量位置
后续通过下载镜像socat,并开启容器来实现开启远程API
docker pull alpine/socat
docker run -d --name=socat --restart=always \
-p 2375:2375 \
-v /var/run/docker.sock:/var/run/docker.sock \
alpine/socat \
tcp-listen:2375,fork,reuseaddr unix-connect:/var/run/docker.sock
测试(输入本机ip地址):
curl http://192.168.8.86:2375/version
至此docker的配置完成,接下来是代码插件配置
docker-maven-plugin
主要就是修改地址为指定服务器的地址,下面是一个示例文件:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>${docker.maven.plugin.version}</version>
<executions>
<!--如果想在项目打包时构建镜像添加-->
<execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- Docker 远程管理地址-->
<dockerHost>${docker.host}</dockerHost>
<images>
<image>
<!--定义镜像名称-->
<name>mall-tiny/${project.name}:${project.version}</name>
<!--定义镜像构建行为-->
<build>
<!--定义基础镜像-->
<from>openjdk:8</from>
<args>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</args>
<!--定义哪些文件拷贝到容器中-->
<assembly>
<!--定义拷贝到容器的目录-->
<targetDir>/</targetDir>
<!--只拷贝生成的jar包-->
<descriptorRef>artifact</descriptorRef>
</assembly>
<!--定义容器启动命令-->
<entryPoint>["java", "-jar","-Dspring.profiles.active=prod","/${project.build.finalName}.jar"]</entryPoint>
<!--定义维护者-->
<maintainer>macrozheng</maintainer>
</build>
</image>
</images>
</configuration>
</plugin>
</plugins>
</build>
- 相关配置说明:
- executions.execution:此处配置了在maven打包应用时构建docker镜像;
- image.name:用于指定镜像名称,mall-tiny是仓库名称, p r o j e c t . n a m e 为镜像名称, {project.name}为镜像名称, project.name为镜像名称,{project.version}为镜像标签名称;
- dockerHost:打包后上传到的docker服务器地址;
- build.from:该应用所依赖的基础镜像,此处为openjdk;
- entryPoint:docker容器启动时执行的命令,可以使用-Dspring.profiles.active=prod指定应用配置文件;
- assembly:定义哪些文件拷贝到容器中;
- assembly.targetDir:定义拷贝到容器的目录;
- assembly.descriptorRef:只拷贝生成的jar包;
- maintainer:定义项目的维护者。
- 添加application-prod.yml配置文件,只是将之前的数据库地址的localhost改为了db;
可以把docker中的容器看作独立的虚拟机,mall-tiny-docker访问localhost自然会访问不到mysql,docker容器之间可以通过指定好的服务名称db进行访问,至于db这个名称可以在运行mall-tiny-docker容器的时候指定。
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://db:3306/mall_tiny?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
其中在pom文件中配置的和dockerFile实际上是差不多的,两种方式都可以用来实现打包项目的镜像jar包,dockerFile是最为传统的方式,它是由用户首先使用maven对项目进行打包(注意使用此方式需要将pom文件关于docker-maven-plugin的代码进行注释),之后将打好的jar包放置在dockerFile的同级目录下,点击运行即可推至远程服务器上的docker。
更多介绍–docker-maven-plugin
代码仓库:GitHub - macrozheng/mall-learning at dev-v2
还在手动部署SpringBoot应用?试试这个自动化插件! | mall学习教程
docker-maven-plugin不仅可以操作镜像,还可以操作容器,甚至可以直接在pom文件中指明挂载的位置,例如下面指令:
docker run -p 8080:8080 --name mall-tiny-fabric \
--link mysql:db \
-v /etc/localtime:/etc/localtime \
-v /mydata/app/mall-tiny-fabric/logs:/var/logs \
-d 192.168.3.101:5000/mall-tiny/mall-tiny-fabric:0.0.1-SNAPSHOT
在pom文件中可以这样写:
<!--定义容器启动行为-->
<run>
<!--设置容器名,可采用通配符-->
<containerNamePattern>${project.artifactId}</containerNamePattern>
<!--设置端口映射-->
<ports>
<port>8080:8080</port>
</ports>
<!--设置容器间连接-->
<links>
<link>mysql:db</link>
</links>
<!--设置容器和宿主机目录挂载-->
<volumes>
<bind>
<volume>/etc/localtime:/etc/localtime</volume>
<volume>/mydata/app/${project.artifactId}/logs:/var/logs</volume>
</bind>
</volumes>
</run>
当然还是建议使用dockerCompose来统一制定,可读性也比较高
docker启动容器
在docker中启动打包的项目镜像,注意要指定mysql的服务要和配置文件中也要保持一致,这里是用服务名称来进行启动,db是启动的mysql容器。
- 使用docker命令启动:
docker run -p 3307:3306 --name mysql \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-v /mydata/mysql/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
- 如果遇到mysql容器无法启动的情况,可以先删除容器,再使用如下命令启动(该命令只是删除了mysql配置文件挂载的那行命令);
docker run -p 3306:3306 --name mysql \
-v /mydata/mysql/log:/var/log/mysql \
-v /mydata/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=root \
-d mysql:5.7
- 进入运行mysql的docker容器:
docker exec -it mysql /bin/bash
- 使用mysql命令打开客户端:
mysql -uroot -proot --default-character-set=utf8
- 修改root帐号的权限,使得任何ip都能访问:
grant all privileges on *.* to 'root'@'%';
- 创建mall数据库:
create database mall_tiny character set utf8;
- 将
mall_tiny.sql
文件拷贝到mysql容器的/
目录下:
docker cp /mydata/mall_tiny.sql mysql:/
- 将sql文件导入到数据库:
use mall_tiny;
source /mall_tiny.sql;
启动项目
项目推至本机的docker下之后,可以通过远程服务器的ip进行测试访问(注意需要打开80端口),这里小编使用本机测试,在同一个局域网内是能够访问的,但超出这个范围的就不能访问成功。
思考下为什么远程服务器部署好项目,就可以在任何地方进行访问,而这里我通过本机部署的项目为什么不能做到任何地方都访问呢?
其实主要涉及到 网络访问控制 和 防火墙设置 这两块
Docker 的默认网络模式–bridge,容器会连接到一个虚拟的桥接网络,容器之间可以通过虚拟网桥通信,但容器外的机器(包括同一局域网内的其他机器)默认是无法直接访问容器的端口的。
dockerFile常用语法:
FROM # 基础镜像,当前新镜像是基于哪个镜像的
MAINTAINER # 镜像维护者的姓名混合邮箱地址
RUN # 容器构建时需要运行的命令
EXPOSE # 当前容器对外保留出的端口
WORKDIR # 指定在创建容器后,终端默认登录的进来工作目录,一个落脚点
ENV # 用来在构建镜像过程中设置环境变量
ADD # 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
COPY # 类似ADD,拷贝文件和目录到镜像中!
VOLUME # 容器数据卷,用于数据保存和持久化工作
CMD # 指定一个容器启动时要运行的命令,dockerFile中可以有多个CMD指令,但只有最后一个生效!
ENTRYPOINT # 指定一个容器启动时要运行的命令!和CMD一样
ONBUILD # 当构建一个被继承的DockerFile时运行命令,父镜像在被子镜像继承后,父镜像的ONBUILD被触发
# 该镜像需要依赖的基础镜像
FROM openjdk:8
# 将当前目录下的jar包复制到docker容器的/目录下
ADD mall-tiny-docker-1.0-SNAPSHOT.jar /mall-tiny-docker-1.0-SNAPSHOT.jar
# 声明服务运行在8080端口
EXPOSE 8080
# 指定docker容器启动时运行jar包
ENTRYPOINT ["java", "-jar","-Dspring.profiles.active=prod","/mall-tiny-docker-1.0-SNAPSHOT.jar"]
# 指定维护者的名字
MAINTAINER macrozheng
ADD
用于复制文件,格式:
ADD <src> <dest>
示例:
# 将当前目录下的mall-tiny-docker.jar包复制到docker容器的/目录下
ADD mall-tiny-docker.jar /mall-tiny-docker.jar
ENTRYPOINT
指定docker容器启动时执行的命令,格式:
ENTRYPOINT ["executable", "param1","param2"...]
示例:
# 指定docker容器启动时运行jar包
ENTRYPOINT ["java", "-jar","/mall-tiny-docker.jar"]
ENV
用于设置环境变量,格式:
ENV <key> <value>
示例:
# mysql运行时设置root密码
ENV MYSQL_ROOT_PASSWORD root
EXPOSE
声明需要暴露的端口(只声明不会打开端口),格式:
EXPOSE <port1> <port2> ...
示例:
# 声明服务运行在8080端口
EXPOSE 8080
FROM
指定所需依赖的基础镜像,格式:
FROM <image>:<tag>
示例:
# 该镜像需要依赖的openjdk的镜像
FROM openjdk:8
MAINTAINER
指定维护者的名字,格式:
MAINTAINER <name>
示例:
MAINTAINER macrozheng
RUN
在容器构建过程中执行的命令,我们可以用该命令自定义容器的行为,比如安装一些软件,创建一些文件等,格式:
RUN <command>
RUN ["executable", "param1","param2"...]
示例:
# 在容器构建过程中需要在/目录下创建一个mall-tiny-docker.jar文件
RUN bash -c 'touch /mall-tiny-docker.jar'