Spring Boot的容器化改造
目前最流行的容器引擎:Docker,微服务领域最热门的框架Spring Boot, 两强联手会产生什么样的化学反应呢?
最近偶然看到了纯洁的微笑
的这篇文章,是一篇不错的入门docker文章,决定自己动手实践一下,纯洁的微笑这篇文章里用的是JPA操作数据库,我这里做了改动,用的是MyBatis框架操作数据库,docker-compose 文件也做了改动,这篇文章会把自己遇到的坑记录下来。文末会给出整个项目地址。
Spring Boot
使用Spring Boot 搭建一个web服务,在数据库中记录每个访问者的ip 和访问次数,并且返回给前端展示。
依赖包
主要依赖Spring boot web框架、Mysql驱动和MyBatis框架。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
核心代码
Controller 层主要负责对接收到请求进行判断,操作数据库记录ip 和访问次数。
@RestController
public class VisitorController {
@Autowired
private VisitorMapper visitorMapper;
@RequestMapping("/")
public String index(HttpServletRequest request) {
String ip=request.getRemoteAddr();
Visitor visitor = visitorMapper.getOne(ip);
if(visitor==null){
visitor=new Visitor();
visitor.setIp(ip);
visitor.setTimes(1);
visitorMapper.insert(visitor);
}else {
visitor.setTimes(visitor.getTimes()+1);
visitorMapper.update(visitor);
}
return "I have been seen ip "+visitor.getIp()+" "+visitor.getTimes()+" times.";
}
}
MyBatis的Mapper 负责操作数据库。
@Mapper
@Component(value = "visitorMapper")
public interface VisitorMapper {
@Select("SELECT * FROM visitor WHERE ip =#{ip}")
Visitor getOne(String ip);
@Update("UPDATE visitor SET ip=#{ip} ,times=#{times} WHERE ip =#{ip}")
void update(Visitor visitor);
@Insert("INSERT INTO visitor(ip,times) VALUES(#{ip},#{times})")
void insert(Visitor visitor);
}
容器化改造
首先看一下目录结构。DockerCompose
是Spring Boot的项目工程文件,docker-compose.yaml
是描述如何构建服务。nginx
里是关于nginx的配置。mysql
是mount到容器里的文件,下面会具体介绍。
docker-compose.yaml
docker-compose.yaml 与纯洁的微笑里的文件相比做了些许改动,下面具体说明。
version: '3'
services:
nginx:
container_name: v-nginx
build: ./nginx
restart: always
ports:
- 80:80
- 443:443
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
mysql:
container_name: v-mysql
image: mysql/mysql-server:5.7
volumes:
- ./mysql/data:/var/lib/mysql
environment:
MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_HOST: '%'
ports:
- "3306:3306"
restart: always
app:
restart: always
build: ./DockerCompose
working_dir: /DockerCompose
volumes:
- ./DockerCompose:/DockerCompose
- ~/.m2:/root/.m2
expose:
- "8080"
depends_on:
- nginx
- mysql
command: mvn clean spring-boot:run -Dspring-boot.run.profiles=docker
改动一:nginx
在纯洁的微笑里nginx里并没有build: ./nginx
,而是直接用image:nginx:1.13
基础镜像。
关于build
的定义:
服务除了可以基于指定的镜像,还可以基于一份 Dockerfile,在使用 up 启动之时执行构建任务,这个构建标签就是 build,它可以指定 Dockerfile 所在文件夹的路径。
因为nginx:1.13
基础镜像并不能满足我的在容器中使用vim
命令的需求,所以在这里重新构建了一个镜像。使用apt-get
安装vim
,可以方便在nginx容器中使用vim命令。
nginx目录下的Dockerfile:
image.png
改动二:mysql
纯洁的微笑在结束运行容器之后,并没有保存数据库中的表,这是因为每次启动容器都会在宿主机中创建一个新的数据卷,所以只要一直是使用同一个数据卷,就不会有数据丢失的问题。可以具体参照这篇文章。
关于volumes
的定义:
挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER] 这样的格式
在Spring Boot 项目的同级目录下创建一个mysql/data
目录,挂载完成之后,进入data
目录,可以看到有test
目录对应到数据库中的test
。
进入到test目录中,看到visitor
命名的文件,就是对应到数据库中visitor
表,这两种后缀是InnoDB引擎的存储方式。
除了上面两处改动,其他关键字的说明如下:
-
version
: '3': 表示使用第三代语法来构建 docker-compose.yaml 文件。 -
services
: 用来表示 compose 需要启动的服务,我们可以看出此文件中有三个服务分别为:nginx、mysql、app。 -
container_name
: 容器名称 -
environment
: 此节点下的信息会当作环境变量传入容器,此示例中 mysql 服务配置了数据库、密码和权限信息。 -
ports
: 表示对外开放的端口 -
restart: always
表示如果服务启动不成功会一直尝试。 -
volumes:
加载本地目录下的配置文件到容器目标地址下 -
depends_on:
可以配置依赖服务,表示需要先启动 depends_on 下面的服务后,再启动本服务。 -
command:
mvn clean spring-boot:run -Dspring-boot.run.profiles=docker: 表示以这个命令来启动项目,-Dspring-- boot.run.profiles=docker表示使用 application-docker.properties文件配置信息进行启动。
docker compose 的书写规则可以参照Docker Compose 配置文件详解
Nginx配置
Nginx没有改动,需要注意的一点是proxy_pass http://app:8080
这块要使用app
,必须要和compose中的service 名称对应。不熟悉Nginx配置的同学可以参考Nginx配置文章。
server {
listen 80;
charset utf-8;
access_log off;
location / {
proxy_pass http://app:8080;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static {
access_log off;
expires 30d;
alias /app/static;
}
}
部署与运行
docker-compose up -d:以后端运行的方式启动容器
docker ps:查看正在运行的所有容器
docker-compose down: 关闭项目中的所有容器
image.png运行结果:
image.png
总结
使用Docker可以解决部署中的环境问题,不必再去一个个安装配置Mysql 、Nginx,安装配置完在和Spring Boot联调,非常麻烦。写个docker-compose.yaml文件,docker会帮你搞定这一切。即使是部署新的一个环境,docker也能帮你完美的避开安装配置的坑。
示例代码:GitHub地址
网友评论