1. 简介
最近项目中有一个搜集用户feedback
的功能,用户通过回答一些问题给我们提供feedback
。feedback
的数据会以json
文件的格式通过邮件发送给我们。但是这些json
文件如何进行保存是一个问题。
于是后面我们又单独基于MongoDB
和liberity
(Web
应用服务器,相当于Tomcat
)开发了一个简单的应用,提供了文件的上传、导出、查看等功能。当我们收到这些json
文件后,通过上传功能,将json
文件中的数据,存储在MongoDB
中。同时可以从mongodb
中以文件的形式导出feedback
数据。MongoDB
是文档型数据库,非常适合用于存储JSON
数据。最终使用docker
部署应用。
2.步骤
-
将自己的应用制作成镜像
1)编写Dockerfile
基础镜像使用的是open-liberty:21.0.0.2-full-java8-openj9
。
主要做的工作是将自己编写的应用导出成war
包(Eclipse
可以直接将应用导出成war
包),然后复制到liberity
服务器的基础镜像中,并且将服务器的配置文件以及应用需要的一些jar
包也一起复制到liberity
基础镜像中。FROM open-liberty:21.0.0.2-full-java8-openj9 # Add server configuration COPY --chown=1001:0 server.xml /config/ COPY --chown=1001:0 bootstrap.properties /config/ # This script will add the requested XML snippets to enable Liberty features and grow image to be fit-for-purpose using featureUtility. # Only available in 'kernel-slim'. The 'full' tag already includes all features for convenience. # RUN features.sh # Add the application COPY --chown=1001:0 FeedbackData.war /config/ COPY --chown=1001:0 ./lib/ /config/lib/ # This script will add the requested server configurations, apply any interim fixes and populate caches to optimize runtime. RUN configure.sh
2)运行
docker build
命令,构建自己的镜像docker build -f Dockerfile -t feedbackdata:1.0 .
-
启动自己的应用容器
由于应用的端口配置的是xxxx
, 所以用-p
将其暴露在主机的xxxx
端口,这样,通过主机的ip
或者域名 + 端口号xxxx
的方式就能够访问我们的应用。
--restart unless-stopped
: 容器不正常退出的情况下,自动重启。例如直接机器重启的情况
docker run -d -p xxxx:xxxx --name feedbackdata-liberity-1.0 --restart unless-stopped feedbackdata:1.0
- 启动
MongoDB
为了安全,MongoDB
不映射主机端口。镜像使用的是dockerhub
中的mongo:4.2
版本。
--auth
, 启动mongodb
的验证功能,简单理解就是只有登录的用户才能操作数据库
docker run -d --name feedbackdata-mongo-1.0 --restart unless-stopped mongo:4.2 --auth
在启动了MongoDB
之后,通过浏览器去访问自己的应用,发现怎么也连接不上MongoDB
数据库。在log
中看见有很多连接数据库超时的信息。
经过调查,发现在连接mongodb
的时候,代码中host
地址写的是localhost
。需要改成MongoDB
容器的ip
地址。可以通过下面的命令查看容器的ip
地址:
docker inspect 容器ID或容器名
在Networks中的IPAddress字段就是该容器的IP。
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "xxxx",
"EndpointID": "xxxx",
"Gateway": "xxxxx",
"IPAddress": "xxxxx",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "xxxxx",
"DriverOpts": null
}
}
将源代码中的数据库连接地址修改成容器的ip
地址之后,确实能够正常的连接上mongodb
了,但是如果容器重启,ip
地址发生了变化怎么办?难道还要再改一次代码,重新制作应用镜像吗?
思考:
能不能通过容器名来进行连接(在应用源码中,数据库连接地址url
直接写成mongodb
容器的名字)???我们只需要保证容器名不变化就可以,即使mongodb
容器的ip
地址发生了变化也没关系。这样就不需要一直更改应用源代码中的数据库连接地址ip
了。
所以又去研究了容器互联的办法。
1)第一种办法是使用--link
。已经不推荐使用,它是直接修改的/etc/hosts
文件,将ip
地址和容器名的映射加入到了hosts
文件中。这样一来,通过容器名访问的时候,就能解析得到容器ip
,就能够访问容器了。
在启动应用容器的时候使用--link
参数,连接mongodb
容器.
docker run -d -p xxxx:xxxx --name feedbackdata-liberity-1.0 --restart unless-stopped --link feedbackdata-mongo-1.0 feedbackdata:1.0
上面这种方式也能够成功通过容器名的方式连接mongodb
,但是已经不推荐使用。
执行下面的命令进入feedbackdata-liberity-1.0
容器,cat
输出hosts
文件的内容,可以发现,hosts
文件中加入了容器名feedbackdata-mongo-1.0
和ip
地址的映射。
docker exec -it feedbackdata-liberity-1.0 /bin/bash
default@63a5d087d6a7:/logs$ cat /etc/hosts
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.3 feedbackdata-mongo-1.0 0fe311efc84f
172.17.0.2 63a5d087d6a7
2)第二种办法是可以自定义网络。推荐使用。
使用下面的命令创建一个自定义网络,将容器都加入同一个自定义网络中,这样就能够通过容器名相互访问了。
--driver bridge
: 使用的是桥接模式,docker
默认的docker0
网络,也是使用的桥接模式,但是docker0
有一些限制,例如通过容器在这里插入代码片
名不能直接访问容器。
--subnet 192.168.0.0/16
: 指定子网范围
--gateway 192.168.0.1
: 指定网关,所有的网络都从这里进出,相当于路由器
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway
192.168.0.1 feedbackdata-net-1.0
执行docker network ls
可以查看到自己创建的网络。
simple@simples-MBP FeedbackData $ docker network ls
NETWORK ID NAME DRIVER SCOPE
c99291dfd481 bridge bridge local
4f2aefc1c976 feedbackdata-net-1.0 bridge local
6cce3158d195 host host local
0945e49d3f44 none null local
执行docker network inspect feedbackdata-net-1.0
查看该网络的详细信息:
在启动容器的时候,使用--net
参数将容器加入到自定义的网络中。执行下面的命令分别启动mongodb
容器和应用容器。
docker run -d --name feedbackdata-mongo-1.0 --restart unless-stopped --net feedbackdata-net-1.0 mongo:4.2 --auth
docker run -d -p xxxx:xxxx --name feedbackdata-liberity-1.0 --restart unless-stopped --net feedbackdata-net-1.0 feedbackdata:1.0
再次运行docker network inspect feedbackdata-net-1.0
,可以看见两个容器已经被加入到自定义的网络中。
这时候,直接访问我们的应用(应用中连接mongodb
数据库的地址写的是容器名feedbackdata-mongo-1.0
),发现mongodb
也是可以直接通过容器名进行访问的。
说明了自定义网络可以帮助我们直接通过容器名的方式进行容器间的访问。
思考:
应用程序生成了一些log
,这些log
必须进入到容器里面才能进行查看,很不方便。如何解决???
如何去备份mongodb
的数据???
使用docker
的卷挂载技术,于是又去研究了容器卷相关的技术。
容器卷挂载:将容器中的目录或文件映射到宿主机的某个目录或文件。容器中的目录或文件的改变,会直接同步更新到宿主机上。反之也是一样。但是如果容器中的目录被删除,宿主机上的该目录是不会被删除的。即使mongodb
容器被删除,然后重新启动一个新的容器,也会自动同步宿主机上的数据。
最终使用下面的命令将logs
和mongoDB
的数据挂载到了宿主机的logs
目录和databackup
目录。
通过-v
进行卷挂载。
docker run -d -p xxxx:xxxx --name feedbackdata-liberity-1.0 --restart unless-stopped --net feedbackdata-net-1.0 -v /home/gqdeng/logs/:/logs/ feedbackdata:1.0
docker run -d --name feedbackdata-mongo-1.0 --restart unless-stopped --net feedbackdata-net-1.0 -v /home/gqdeng/databackup/:/data/db/ mongo:4 --auth
思考:每次应用的镜像都是自己手动build
出来的,首先要build angular
前端的工程代码,然后将build
出来的dist
目录放在后端的Webcontent
目录中,然后还需要手动从eclipse
中导出一个war
包;最后还有通过docker build
命令,将war
包构建成自己的应用镜像。非常麻烦。能不能自动化?将这些过程串起来。
经过研究,最终通过Jenkins
将这一切都串起来。只要GitHub
上代码有变化,就会自动build
一个新的镜像,替换掉老的镜像,并且停止旧的应用容器,启动一个新的应用容器。
思考:
mongodb
只起了一个容器,如果它挂掉了,我们的应用是不是也就不能正常使用了?所以又去研究了mongodb
的高可用。它有一个副本集模式。可以参考https://www.cnblogs.com/littleatp/p/8562842.html
打算配置mongodb
的副本集模式 ==> 由于项目目前不需要配置,后面如果有需要再扩展。
备注:
MongoDB
的auth
配置:
1. 进入正在运行的mongodb容器: docker exec -it mongo /bin/bash
2.执行下面的命令
1)mongo // 进入mongodb
2)use admin // 创建admin数据库
3)db.createUser({ user:'xxxxx',pwd:'xxxx',roles:[ { role:'userAdminAnyDatabase', db: 'admin'},"readWriteAnyDatabase"]}); // 创建一个admin用户,后面用来创建其它的普通用户。如果开启了权限认证,只能第一次创建admin的时候会成功创建,第二次如果再直接创建另一个admin会直接失败,报错如下:
db.createUser({ user:'xxxx',pwd:'xxxx',roles:[ { role:'userAdminAnyDatabase', db: 'admin'},"readWriteAnyDatabase"]});
2021-04-11T10:19:46.215+0000 E QUERY [js] uncaught exception: Error: couldn't add user: command createUser requires authentication :
_getErrorWithCode@src/mongo/shell/utils.js:25:13
DB.prototype.createUser@src/mongo/shell/db.js:1413:11
4)进行认证操作
db.auth("xxxxx", "xxxx") // 认证操作,只有认证之后才能操作数据库,才能进行后面的操作
5).
use feedbackdb // 创建feedbackdata数据库
db.createUser({ user: "xxxxx", pwd: "xxxxx", roles: [{ role: "readWrite", db: "feedbackdb" }] }); // 创建一个普通用户feedbackdb数据库有读写权限
// db.auth("xxxx", "xxxxx")
使用java程序连接feedbackdb,用户名是xxxx,密码是xxxx
docker exec -it feedbackdata-liberity-1.0 /bin/bash // 进入容器命令()