版本
- spring boot: 1.5.10
- docker engine: 18.09.4; API:1.39
- maven: 3.3.9
为何要使用容器技术
Old Way 是在主机上,通过包管理器安装应用程序来部署。这样做的缺点是,必将涉及到系统的应用程序,配置,库,以及生命周期等问题。 为了构建一个能够更新和回滚的虚拟机镜像,虚拟机会变得非常笨重。
New Way 是基于操作系统级虚拟化,而不是硬件虚拟化的容器来部署。这些容器彼此隔离:它们有自己的文件系统,也不能访问对方的程序,他们的运算和资源都是隔离的。 它们比虚拟机更容易建立,并且因为它们与底层系统没有耦合关系,所以可以很方便的在所有云服务器上分发。
由于容器是小而快,每个应用程序可以装在一个容器镜像里。是1对1的应用程序到容器镜像的关系。基于容器技术,镜像可以在编译或者发行时创建,而不是部署的时候才去创建。 因为每个应用程序不依赖于其他的程序,及系统基础环境。 在构建/发行容器的时候就保证了,开发环境和生产环境的一致性。同样,容器是远远比虚拟机更加透明化,这更加方便监控和管理。 当容器里程序的生命周期是由底层系统管理的,而不是容器内部黑盒管理。最后,管理每个单个应用程序的容器,和管理应用程序是一样的。
实战场景
mac本机上intellij创建一个maven project: java_example,远程有一台linux(48.99.190.38)开发机。机器上会安装docker及其组件。项目代码完成后打包上传到linux机器的docker容器中运行。
linux安装及配置docker
安装
官网给出了很详细的安装过程,参照官网就好;https://docs.docker.com/install/linux/docker-ce/centos/
配置-开启 Docker api 远程访问
要想远程连接docker,开启 docker api 远程操作的功能,登录远程docker宿主机器(48.99.190.38),在 /usr/lib/systemd/system/docker.service,文件中,修改 ExecStart 的参数:1
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
端口自定义设置即可。
重载所有修改过的配置文件,并且重启 docker1
2systemctl daemon-reload
systemctl restart docker.service
- 需要注意的是,由于没有密码登陆任何权限验证,外网或者生产环境需要上证书使用
intellij创建project
通过Intellij创建maven project: java_example
pom.xml引入依赖1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<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>
</dependencies>
创建类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17@SpringBootApplication
public class JavaExampleApp {
public static void main(String[] args) throws Exception {
System.out.println("app"+new Date());
SpringApplication.run(JavaExampleApp.class, args);
}
}
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/test")
public String test() {
return "success";
}
}
创建application.yml文件1
2
3
4
5
6logging:
level:
root: info
server:
port: 20282
引入docker maven插件
在pom.xml中加入 docker-maven-plugin, 网上有许多maven docker插件,这里我们选择com.spotify1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40<properties>
<docker.image.prefix>springboot_demo</docker.image.prefix>
</properties>
<build>
<plugins>
<!-- Docker maven plugin -->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<configuration>
<!-- 打包的image名称 -->
<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
<!-- Dockerfile文件的位置,以项目的 root 目录为根节点,建议到单独建立一个目录。位置非固定,随意指定,如:./src/docker/-->
<dockerDirectory>src/main/docker</dockerDirectory>
<!-- 远程docker的api地址,这里为我的linux开发机地址-->
<dockerHost>http://48.99.190.38:2375</dockerHost>
<!-- image tag,可以指定多个,不声明默认即latest-->
<imageTags>
<imageTag>latest</imageTag>
<imageTag>${project.version}}</imageTag>
</imageTags>
<!-- 执行构建docker镜像的时候需要哪些文件,即Dockerfile和项目jar和指定他们的位置-->
<resources>
<resource>
<!--这里指定的文件是target中的jar文件 -->
<!--
${project.build.directory} 表示项目项目构建输出目录,默认为 ${basedir}/target
${project.build.filename} 表示项目打包输出文件的名称,默认为 ${project.artifactId}-${project.version}
-->
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
创建Dockerfile
引入maven docker时,我们制定了Dockerfile的位置:src/main/docker,所以需要在src/main下创建文件夹docker,cd docker/,再创建文件Dockerfile1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# 基础镜像:仓库是java,tag是8
FROM java:8
#容器对外暴露8080端口,即可以通过这个端口访问容器内的服务,这个端口与容器内服务的端口存在一个对应关系,也可以在docker run时指定,本文即是这么做的,所以这里注释掉了,见后面docker run命令
#EXPOSE 8080
# 将本地/tmp目录持久化到Docker宿主主机内. 因为Spring Boot使用内嵌Tomcat容器,其默认使用/tmp作为工作目录。
# 所以效果就是将tomcat工作目录/tmp下的文件上传到远程docker主机的/var/lib/docker/**/
# 查在远程docker主机的文件:/var/lib/docker/overlay2/5e3d14ca7fedf5ee2c6ca2744d4da664f09f12affec8ed391740a8b92896f52f/diff/app.jar
VOLUME /tmp
#将打包好的程序拷贝到容器中的指定位置
ADD java_example-0.0.1-SNAPSHOT.jar app.jar
#容器启动后需要执行的命令
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
Dockerfile详细说明参见:Dockerfile指令详解
使用docker构建项目
两种方式
命令
cd ${project.dir} #如:cd ~/skyler/project/mytest/java_example
mvn clean package docker:build -DskipTests利用intellij maven插件
依次点击clean, package, docker:build
成功的效果为:
构建完可以看下本地生成的文件及目录:
简单来说,构建过程为首先把jar包和Dockerfile生成到构建目录下,然后执行Dockerfile中的指定,即构建image推到远程docker机器的/var/lib/docker/**/下,如下图
docker运行image
查看image
登录远程docker机器查看刚上传的image1
docker image ls
通过intellij输出log中的f29374d5a2d5可以找到对应的image id: f29374d5a2d5
运行image
1 | docker run -p 8081:20282 -t springboot_demo/java_example |
页面输出启动日志,可以看到和我们通过intellij main启动一样的日志内容
在浏览器中访问项目restful接口:
http://48.99.190.38:8081/user/test,接口返回success信息
也可以通过docker查看运行状态的image1
docker ps
到此基本的通过Dockerfile,springboot集成docker完事了
docker使用spring profile特性
使用spring boot 我们都知道,可以根据profile指定不同的env环境,从而加载不同环境的配置,实现多环境切换部署。使用docker也可以实现spring profile特性。具体如下
方法之一:
在Dockerfile中指定,具体为ENTRYPOINT在原有基础上增加Dspring.profiles.active=local,如下1
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-Dspring.profiles.active=local","-jar","/app.jar"]
为区分不同环境,java_example新建application-local.yml, 为容易分辨效果,指定一个新端口1
2
3
4server:
port: 20281
debug: false
重新构建在intellij上clean, package, docker:build, 去docker宿主机器上运行docker image ls
,观察CREATED字段的时间确定新的image. 运行新的image前,需要停止刚已启动的image1
2
3
4
5
6docker ps
# 找到已启动的image的containerId,docker ps -a 列出所有container
docker kill/stop containerId
docker run -p 8081:20281 -t springboot_demo/java_example
再看日志
端口已经变成20281了,spring profile已经生效了
扩展
docker 使用spring profile特性有几种方式
参考如下:https://segmentfault.com/a/1190000011367595
docker常用命令
1 | $ docker run --rm -p 8000:3000 -it demo:0.0.1 /bin/bash |
详细参见:https://www.infoq.cn/article/KBTRC719-r6GHOPS3Cr8
docker run与docker start
区别
docker run IMAGE_ID
,而docker start CONTAINER_ID
。即run runs an image
; start starts a container
。docker run = docker create + docker start
docker交互式任务
格式:docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
-i, –interactive Keep STDIN open even if not attached
-t, –tty Allocate a pseudo-TTY
如:docker exec -it 62617312c8bf /bin/bash
,这个命令是进入容器62617312c8bf内部打开shell,ll 可以看到容器的目录结构,如下图
ctop: 容器的top界面
ctop是我最近使用的一个工具,它能够提供多个容器的实时指标视图1
[root@xxxxx docker]# ctop -a
各系统安装:https://github.com/bcicen/ctop
docker-compose
意义
前面我们使用 Docker 的时候,定义 Dockerfile 文件,然后使用 docker build、docker run 等命令操作容器。然而微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而知
使用 Docker Compose 可以轻松、高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具
安装
下载稳定版本
1 | sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose |
给目录添加执行权限1
sudo chmod +x /usr/local/bin/docker-compose
可选功能:自动补全1
sudo curl -L https://raw.githubusercontent.com/docker/compose/1.24.0/contrib/completion/bash/docker-compose -o /etc/bash_completion.d/docker-compose
查看安装是否成功1
2docker-compose -v
# output:docker-compose version 1.20.0, build ca8d3c6
组装文件
docker-compose执行需要项目jar和Dockerfile
回溯下intellij使用maven clean, package, docker:build操作吗,现在只需要maven clean, package操作,然后将target/docker下的Dockerfile和java_example-0.0.1-SNAPSHOT.jar上传到docker宿主机器
1 | $ cd **/target/docker |
运行docker-compose
在 docker-compose.yml 所在路径下执行该命令 Compose 就会自动构建镜像并使用镜像启动容器
1 | docker-compose up |
进阶
这是docker和springboot集成的实战例子,下篇我们再进一步,实战springboot+mysql服务集成docker实例,链接:springboot+mysql服务集成docker实例