概述
Docker 是一个开源应用容器(当然目前也分为CE和EE版本,不完全开源化,也存在收费版本),让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
Docker 作为容器工具可以把:业务逻辑容器、数据库容器、储存容器、队列容器使得软件可以拆分成若干个标准化容器,然后像搭积木一样组合起来,让彼此通信,从而形成微服务。
因此微服务很适合用 Docker 容器实现,每个容器承载一个服务。一台计算机同时运行多个容器,从而就能很轻松地模拟出复杂的微服务架构,并能体现微服务产品架构的轻量级、高并发、大数据、智能化、易维护、动态扩展的特性。
使用Docker管理项目
本章节使用Dockerfile的方式来管理微服务项目,关于Dockerfile的指令使用或更多信息,请参照Docker操作指南
本章节所需要的必须条件:
- jre8
- ms-auth2.0:项目SVN地址:https://192.168.7.3/svn/cppt/trunk/src/micro-service/ms-auth
- mysql
- redis
制作mysql镜像
前期准备
- 创建mysql目录,该目录和jre目录平级
- 导出192.168.7.200中的ms-user数据库,将sql脚本放到mysql目录中
mysql Dockerfile编写
在mysql目录中,创建Dockerfile文件,并将以下内容复制到Dockerfile文件中
#指定使用的镜像
FROM mysql:5.6
#指定为湖人
MAINTAINER caochuanhong@sbr-info.com
#将容器启动后,执行的脚本拷贝到容器内的/docker-entrypoint-initdb.d下,该目录会自动执行sql脚本
COPY ms-user.sql /docker-entrypoint-initdb.d
#设置mysql中的参数
#服务端到数据编码
ENV character-set-server utf8mb4
#最大数据传送量
ENV max_allowed_packet 128M
#mysql密码,可以在run指令中,附带 -e MYSQL_ROOT_PASSWORD=xxx来设置mysql密码
ENV MYSQL_ROOT_PASSWORD Admin@123
#声明容器暴漏的端口,只是声明,没有其他作用
EXPOSE 3306
构建mysql镜像
将命令行定位到mysql目录,输入
docker build -t sbr/mysql:1.0.0 .
来开始进行mysql镜像的构建
说明:
- -t:给要构建的镜像定义一个名称
-
. 这个.代表着,让docker去读取当前目录下的Dockerfile文件来构建镜像
此时,mysql镜像已经制作完成,可以通过以下命令来创建并启动一个容器来启动mysql服务:
docker run -d -p 3306:3306 --name mymysql e2f0dc2544d5
说明:
- -d: 后台启动
- -p: 第一个端口是宿主机端口,第二个端口是容器端口
- --name: 为创建的容器设置一个名称
通过
docker ps
来查看正在运行的容器,可以发现,mysql已经跑起来了,如图所示:
通过数据库连接工具测试也可以连接到该服务。
制作redis镜像
redis Dockerfile编写
创建redis目录,该目录和mysql目录平级
在redis目录中,创建Dockerfile文件,在该文件中,输入以下内容:
#指定使用的镜像
FROM redis:4.0.2
#指定维护人
MAINTAINER caochuanhong@sbr-info.com
#声明容器暴漏的端口
EXPOSE 6379
构建redis镜像
将命令行定位到redis目录,输入
docker build -t sbr/redis:1.0.0 .
来开始进行redis镜像的构建
说明:
- -t:给要构建的镜像定义一个名称
- . 这个.代表着,让docker去读取当前目录下的Dockerfile文件来构建镜像
此时,redis镜像已经制作完成,可以通过以下命令来创建并启动一个容器来启动redis服务:
docker run -d -p 6379:6379 --name myredis 21690d58c82d
说明:
- -d: 后台启动
- -p: 第一个端口是宿主机端口,第二个端口是容器端口
- --name: 为创建的容器设置一个名称
- 可以在启动命令中配置 --requirepass "mypassword" 来创建redis的密码
通过
docker ps
来查看正在运行的容器,可以发现,mysql已经跑起来了,如图所示:
制作微服务项目镜像
在制作jre基础镜像一节中,已经介绍了微服务项目必须依赖的环境的镜像制作,这一节,我们来制作微服务项目的镜像,将项目打入到镜像中,通过镜像来启动我们的项目,可以提高我们项目开发完成后很多的后续操作的效率。
前期准备
- 创建app目录,该目录与jre同级
- 通过https://192.168.7.3/svn/cppt/trunk/src/micro-service/ms-auth检出项目,通过maven的clean package 对项目进行打包,打包后,会在项目目录的target中生成一个jar包,如图所示:
- 将jar包复制到app目录中
微服务项目的Dockerfile编写
在app目录中创建Dockerfile文件,并在文件中输入以下内容:
#使用sbr/jre8:1.0.0作为基础镜像
FROM sbr/jre8:1.0.0
#编写人/维护人
MAINTAINER caochuanhong#sbr-info.com
#java应用文件操作
COPY ms-auth-2.1.2051-SNAPSHOT.jar /ms-auth-2.1.2051-SNAPSHOT.jar
#声明端口
EXPOSE 9003
#容器启动后,执行的命令
ENTRYPOINT ["java", "-jar", "/ms-auth-2.1.2051-SNAPSHOT.jar"]
构建微服务项目镜像
将命令行定位到app目录下,执行以下命令:
docker build -t sbr/app:1.0.0 .
说明:
- -t:给要构建的镜像定义一个名称
-
. 这个.代表着,让docker去读取当前目录下的Dockerfile文件来构建镜像
微服务项目配置文件与启动
在启动微服务项目前,需要先看一下项目中的配置文件,有几点需要注意
在项目源码的resource目录下的application.yml文件中
spring boot类型项目中的配置项,可以通过修改环境变量的方式来赋值,spring会优先读取环境变量中的配置项和配置项目值
在docker中,访问127.0.0.1是访问的容器内部,而不是宿主机,所以,需要一种灵活的方式能够动态的配置连接信息,而通过简短的环境变量的形式来配置连接参数是一个好的选择。
无论是通过配置宿主机上的环境变量还是在启动容器的时候,来动态为容器指定环境变量的值,都可以将配置的值映射到容器内部的应用服务的配置中,从而避免连接属性写死的情况。更多的关于spring boot项目配置的信息,请参考如下:http://www.cnblogs.com/softidea/p/5759180.html
所以,在启动微服务项目的时候,附带上上图中配置的项,就可以正确的连接到mysql和redis服务中。
首先,先获取本机的IP地址,然后将本机的IP地址通过环境变量的方式配置到容器的启动命令中,比如我的IP是192.168.6.56
通过以下命令来启动微服务的应用:
docker run -p 9003:9003 -e DB_SERVER=192.168.6.56 -e DB_REDIS_HOST=192.168.6.56 --name sbr-app 5b1df7a17cff
如果redis的连接密码和项目中配置的不一致时,可以通过 -e DB_REDIS_PWD=xxx来动态修改密码。
该命令会在终端输出启动日志,如果想让docker后台启动该容器,可以在命令中添加-d参数
当容器启动后,就可以通过rest连接工具正常访问该项目提供的服务了,访问情况如下图所示:
使用Docker compose编排服务
上一章中,已经介绍了使用docker手动的方式来介绍微服务在docker环境下的部署。但是,这样去操作docker是很原始的,通过手动的一个一个的去启动服务容器,既费时又费力。
Docker 也提供了更强大的工具 Compose 来管理容器。
Docker Compose是一个用来定义和运行复杂应用的Docker工具。一个使用Docker容器的应用,通常由多个容器组成。使用Docker Compose不再需要使用shell脚本来启动容器。
Compose 通过一个配置文件来管理多个Docker容器,在配置文件中,所有的容器通过services来定义,然后使用docker-compose脚本来启动,停止和重启应用,和应用中的服务以及所有依赖服务的容器,非常适合组合使用多个容器进行开发的场景。
更多关于docker compose的内容,请参考Docker操作指南中的docker compose部分。
编写compose配置文件来管理多个容器
通过Docker操作指南中的docker compose部分我们知道,compose可以通过Dockerfile文件来创建镜像和容器。
那么下面,我们通过docker compose的方式来管理我们的镜像和容器.
创建一个docker-compose.yml配置文件,该文件与jre,mysql,redis,app目录是同级的。并将以下内容复制到docker-compose.yml文件中。
#使用的compose版本
version: '3'
#compose关键字,定义services
services:
#service的名称 sbr_redis
sbr_redis:
#service中容器的名称
container_name: sbr_redis
#使用当前目录下的redis目录下的Dockerfile来创建镜像
build: ./redis
#当前服务向外暴漏的端口
ports:
- "6379:6379"
#指定创建redis容器后,设置的密码
command:
- "--requirepass Admin@123"
#service的名称 sbr_mysql
sbr_mysql:
#service中容器的名称
container_name: sbr_mysql
#使用当前目录下的mysql目录下的Dockerfile来创建镜像
build: ./mysql
#当前服务向外暴漏的端口
ports:
- "3306:3306"
#service的名称 sbr_user_auth
sbr_user_auth:
#service中容器的名称
container_name: sbr_app
#使用当前目录下的app目录下的Dockerfile来创建镜像
build: ./app
#当前服务向外暴漏的端口
ports:
- "9003:9003"
#当前容器使用的环境变量
environment:
- DB_SERVER=sbr_mysql
- DB_REDIS_HOST=sbr_redis
#当前服务依赖depends_on配置的服务,docker compose会优先启动依赖的服务
depends_on:
- sbr_mysql
- sbr_redis
默认情况下,Compose 会为你的应用程序设置一个 网络。服务的每个容器都加入默认网络,并且该网络上的其他容器都可以访问它们,并且可以通过与容器名称相同的主机名来发现它们。所以,在设置数据库连接和redis的连接时,没有设置具体的IP地址,而是设置的服务名,这样,docker compose会自动去访问对应的服务。
输入以下命令来构建以上配置的所有镜像
docker-compose build
该命令会构建镜像,或者输入以下命令,构建镜像并启动容器
docker-compose up --build
该命令会在启动前就行构建镜像,构建完成后,再启动容器,如果不加参数 -d 的话,会在终端输入构建以及启动日志,截图如图所示:
使用rest连接工具,来验证服务是否已经成功。
至此,微服务项目通过被docker compose管理的使用场景已经整理完毕:
<font color=red>
请注意:使用compose进行启动到容器,在日志打印时,微服务项目会报连不到数据库到错误,这是正常到,因为,此时,mysql容器正在启动,而没有启动完毕,所以导致无法连接到数据库,而hibernate中有重连机制,在mysql正常启动后,就可以连接上了。关于depend_on到设置,compose只能检测到容器到启动状态,若容器启动,则compose认为这已经是成功启动了,所以,关于这一点报错到问题,请大家知悉,这是正常到。当然,也可以通过其他到方式来处理这个问题。
</font>
总结
docker 的使用过程,它分为镜像构建与容器启动。
镜像构建:即创建一个镜像,它包含安装运行所需的环境、程序代码等。这个创建过程就是使用 dockerfile 来完成的。
容器启动:容器最终运行起来是通过拉取构建好的镜像,通过一系列运行指令(如端口映射、外部数据挂载、环境变量等)来启动服务的。针对单个容器,这可以通过 docker run 来运行。
而如果涉及多个容器的运行(如服务编排)就可以通过 docker-compose 来实现,它可以轻松的将多个容器作为 service 来运行(当然也可仅运行其中的某个),并且提供了 scale (服务扩容) 的功能。
最后,docker,Dockerfile,compose的使用情况:
Dockerfile: 构建镜像
docker run: 启动容器
docker-compose: 启动服务(包含多个容器)
以上所有章节内容为微服务项目在docker环境下的使用情况,各位在实践过程中,如果出现了什么问题,可随时与我联系。
附录
<a name=jre>制作微服务所需的jre基础镜像</a>
镜像选择
在制作镜像、Docker创建容器时,基础镜像通常选择Ubuntu或Centos,不管哪个镜像的大小都在100MB以上。
我们必须要知道的一个事情是,镜像也是要占用存储空间的。那么,设计或者使用一个磁盘占用较小的镜像,是一个比较好的选择。
通过对比和资料查找后,推荐使用Alpine Linux 作为jre的基础镜像。
Alpine Linux 是一个轻量级的 Linux 发行版,基于 musl libc 及 busybox。其关注于性能及安全性,追求小(Small)、简单(Simple)及安全(Secure)。其常被应用于构建 Docker 等容器。
在使用前,我们应该知道java是需要glibc的,所以基础镜像使用的是alpine-glibc而非alpine,alpine-glibc大概是11.1M。
jre基础镜像
- 首先,需要下载jre,下载地址是https://www.java.com/en/download/manual.jsp,大概是77M。
- 然后,删除jre包中的不是必须的东西
#解压
tar xvcf jre1.8.0_191.tar.gz
#进入目录
cd jre1.8.0_191/
#删除文本文件
rm -rf COPYRIGHT LICENSE README release THIRDPARTYLICENSEREADME-JAVAFX.txtTHIRDPARTYLICENSEREADME.txt Welcome.html
#删除其他无用文件
rm -rf lib/plugin.jar \
lib/ext/jfxrt.jar \
bin/javaws \
lib/javaws.jar \
lib/desktop \
plugin \
lib/deploy* \
lib/*javafx* \
lib/*jfx* \
lib/amd64/libdecora_sse.so \
lib/amd64/libprism_*.so \
lib/amd64/libfxplugins.so \
lib/amd64/libglass.so \
lib/amd64/libgstreamer-lite.so \
lib/amd64/libjavafx*.so \
lib/amd64/libjfx*.so
- jre重新打包
tar -zcvf jre1.8.0_191.tar.gz jre1.8.0_191
基础镜像的Dockerfile的编写
新建一个jre目录,将刚才处理过的jre1.8.0_191.tar.gz包放入到jre目录中。并在jre目录中,新建一个Dockerfile文件,将以下内容复制到Dockerfile文件中
#使用alpine-glibc作为基础镜像
FROM docker.io/jeanblanchard/alpine-glibc
#编写人/维护人,这里可以写自己的信息
MAINTAINER caochuanhong@sbr-info.com
#定义jre安装到路径,可以在run 指令中,附带 -e JREDIR=/x/x/x来配置jre的安装路径
ENV JREDIR /opt/sbr/jre
#添加gz包到镜像内,gz包会自动解压
ADD jre1.8.0_191.tar.gz ${JREDIR}
#设置环境变量
ENV JAVA_HOME ${JREDIR}/jre1.8.0_191
ENV PATH ${PATH}:${JAVA_HOME}/bin
构建jre基础镜像
将命令行定位到jre目录中,输入以下命令来构建我们自己的镜像
输入
docker image ls
查看当前镜像列表,如图所示:
输入
docker build -t sbr/jre8 .
来开始进行jre基础镜像的构建
说明:
- -t:给要构建的镜像定义一个名称,也可以附带镜像的版本号,比如:sbr/jre8:1.0.0
- . 这个.代表着,让docker去读取当前目录下的Dockerfile文件来构建镜像
此时,jre基础镜像已经制作完成,我们可以通过该镜像,创建一个容器,进入内容内部执行java -version来验证。
首先,我们先复制该镜像的id:b3adba0789cd
然后,输入以下内容,创建并进入容器内部:
docker run -it --name myjre8 b3adba0789cd /bin/sh
说明:
- -i: 以交互模式运行容器,通常与 -t 同时使用;
- -t: 为容器重新分配一个伪输入终端,通常与 -i 同时使用;
- --name: 为创建的容器设置一个名称
- /bin/sh: 容器启动后,执行的脚本
最终效果如图所示:
通过输入exit来退出容器
至此,jre基础镜像的制作已经完毕,