【从零搭建后端基础设施系列(九)】-- VM容器化

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


旁白:今天老大拉小黑他们开会,就公司目前机器资源紧缺进行了讨论……
老大:自从咱公司开始推行微服务化后,机器需求扶摇而上,爆发性增长啊!原来只需要几台机器,现在细分服务后,成倍成倍的长,这咋受得了,所以想听听你们有啥想法
小黑:想法还是有的,只不过想要达成这一步,需要很长一段时间啊,短期内还是得靠钱买硬件资源……
小黄:我知道小黑说的,我觉得,可以两手准备吧,首先开始立项,长期的目标是将公司的VM进行容器化,短期只能先靠增加硬件支撑。
老大:嗯,也只有如此了,那这个任务就交给你们了,这周给我一个方案初版,和一个可演示的demo。
小黑:好的,没问题


会后……


小白:小黑,什么是VM容器化?VM我知道,虚拟机嘛,容器我还没用过。
小黑:因为时间紧急,所以呢没时间给你细讲,它到底是什么。我只能说,一个物理机能运行的容器数量可以达到成百上千个,当然了,还得看机器的配置。不过你放心,等项目正式启动的时候,肯定会深入的研究一番。
小白:嗯嗯,那就好,那现在我先学会如何使用就好了。
小黑:没错,这次拿之前的那几个demo服务进行练手,将min-system-thrift-service、min-system-web-service、auto-publish-server和maven-repo-server进行容器化,原本它们需要四台机器,现在我们改成一台机器,四个容器来运行,开始吧!小黄,你负责min-system-thrift-service和auto-publish-server这两个服务,小白,你负责maven-repo-server这个服务,我今天拿min-system-web-service给你们做演示,之后照着我这个来就行了。
小黄:没问题。
小白:嘿嘿,没问题,这个服务太简单了,没有任何依赖~
小黑:嗯,是的,你们的服务依赖都比较少,web-service这个服务依赖了这三个,所以我今天就拿这个最复杂的讲,这样能防止漏掉哪些点没演示清楚。
小白:very good!
小黑:好了,现在开始吧。首先,去申请一台新的机器,之后这四个服务都会在这台机器上运行。

在这里插入图片描述
小黑:机器申请到了,接着安装必要的环境,先安装docker吧。

yum -y install docker.x86_64

在这里插入图片描述
小白:这这这也太简单了!这就算安装完了?
小黑:不然你以为要干嘛,造火箭呐?
小白:哈哈,没有没有,简单点好啊
小黑:接下来要配置一下镜像加速器
小白:这是啥?
小黑:通俗点说,docker的公共仓库在国外,你在国内访问能不慢吗?所以我们需要找国内的一个镜像服务器,通过它去下载就非常快了嘛
小白:哦~,我终于明白镜像的含义了,就是从国外的那个仓库复制一份到国内,然后别人再从国内的服务器下载,这就叫做镜像加速!妙啊妙啊!
小黑:嗯,就是这样。我之前写过一个脚本,能一键配置,给你们看看

cd /etc/docker
echo -e "{\t\"registry-mirrors\": [\t\"https://1nj0zren.mirror.aliyuncs.com\",\t\"https://docker.mirrors.ustc.edu.cn\",\t\"http://f1361db2.m.daocloud.io\",\t\"https://registry.docker-cn.com\",\t\"https://dockerhub.azk8s.cn\",\t\"https://hub-mirror.c.163.com\",\t\"https://mirror.ccs.tencentyun.com\"]}" > daemon.json
sudo systemctl daemon-reload
sudo systemctl restart docker
docker info

运行后,如图
在这里插入图片描述
小黑:配置好后,我给你们演示一下,看看有多快
在这里插入图片描述
简直快得飞起,哈哈
小白:666,到目前为止感觉都很简单啊
小黑:嗯,docker上手非常快,使用上非常方便,但是如果你想去研究它的原理,那真的是去啃石头,难啊,不过我们还是要去研究一下的。
小白:嗯嗯,这才有意思!
小黑:现在运行一个容器给你看看
在这里插入图片描述

docker run:运行一个容器,加上参数-it能以bash的交互方式进入容器中,但是如果exit退出来,那么容器就会关闭了。

小黑:现在想一下,如何让服务在容器中运行?
小白:哈哈,我知道了,可以把容器当成是一个新的系统,然后在上面安装运行服务需要的java、maven和git等环境,简单来说,按照从零开发一个后端服务(八)-- 自动化部署服务里面说的做一遍就完事了
小黑:作为一个菜鸟,你这么想是对的,给你看个例子,我在容器中创建一个文件
在这里插入图片描述
然后退出关闭容器,再重新运行,发现了吧,文件不存在了。
在这里插入图片描述
所以,如果每次运行容器的时候都用yum install重新下载安装各种环境,那得多耗时间啊?
小白:也是,那怎么办?能让文件不消失吗?
小黑:当然可以,第一种办法是在构建镜像的时候,通过Dockerfile,用命令的方式在容器内安装环境,这样,每次运行镜像的时候,这些环境都会在里面。
小白:这个方法是不错,但是我有个疑问,如果有那种需要频繁构建新镜像的场景,那不是每次都得去下载安装?
小黑:没错,所以第二种办法就是,将所需要的环境都提前下载到宿主机中,然后通过Dockerfile,用命令的方式复制到容器中安装。第二种方法只能算是对第一种的优化,并无特别之处。
小白:所以说还有更好的办法?
小黑:当然有,docker有一个volume(卷)的概念,通俗的讲就是可以将宿主机的某个目录映射到容器中。所以,如果我们有一些基础的、公共的环境,可以直接映射过去,省去了下载安装等骚操作。还有一种场景,数据volume(卷),顾名思义,就是使用宿主机的某个文件夹当做存放数据的地方,容器可以直接使用该数据卷,不用担心容器重启导致的数据丢失问题。
在这里插入图片描述
小白:明白了,说白了就是共享目录是吧,这个方法确实好!有个疑问,就是如果容器3修改了v3,那不是会影响容器4吗?
小黑:一般这种给多个容器共享的,肯定设置为只读权限,否则就会很危险了。
小白:噢噢,这倒是。
小黑:这里我把三个方法都给你演示一遍。先来第一种。
第一步,创建一个空目录,并且创建Dockerfile、run.sh和settings.xml(run.sh和settings.xml在之前的文章已经有讲了)
在这里插入图片描述
第二步,编写Dockerfile

#基于centos:7镜像(第一层镜像)
FROM centos:7

#复制运行脚本到容器中
COPY run.sh /docker/
#将私服的maven仓库配置文件复制到容器中
COPY settings.xml /docker/

#在centos7上执行下面的命令,\是转义换行,相当于在第四层镜像中同时执行下面五条命令
RUN yum -y install java-1.8.0-openjdk.x86_64 \
   && yum -y install git.x86_64 \
   && yum -y install maven.noarch \
   && sh /docker/run.sh \
   && mv /docker/settings.xml ~/.m2/settings.xml \
   && sh /docker/run.sh  #再重新运行一次,这次以才会用到私服仓库,才不会报错

#运行服务
CMD ["java","-jar","/docker/min-system-web-service/min-system-service/target/min-system-service-1.0.0-SNAPSHOT.jar"]

这里再将run.sh贴出来一次

#!/bin/bash
rm -rf /docker/min-system-web-service
echo '【INFO】正在拉取代码……'
git clone -b feature/dev https://github.com/coderxj/min-system-web-service.git /docker/min-system-web-service
echo '【INFO】拉取代码完成,目录:'  `pwd`
echo '【INFO】kill 服务'
kill -9 `cat min-system-web-service/service.pid`
echo '\n\n\n'
cd /docker/min-system-web-service/min-system-service/
echo '【INFO】正在打包……'
mvn clean install -Dmaven.test.skip=true
echo '【INFO】打包完成,目录:'  `pwd`

第三步,构建镜像

#最后的那个点是Dockerfile所在的目录
docker build -t web-server1 .

会很慢,看第四步,需要等待这5条命令执行完才能构建完成。
在这里插入图片描述
构建完成了
在这里插入图片描述
CMD命令,运行容器的时候,会执行这个命令

第四步,用构建出来的镜像,运行容器

docker run -it -p 80:8080 web-server1

如果不使用-p指定宿主机和容器的端口映射,那么是无法访问到容器的
在这里插入图片描述
第五步,测试是否调得通接口
返回这个表明接口通了,只是依赖的thrift服务没有起来而已。
在这里插入图片描述
小白:wow,厉害啊,运行起来啦。但是我有几个地方不明白,COPY这个命令不能一行复制两个文件到容器中吗?
小黑:这个是当然行,像这样

COPY run.sh settings.xml /docker/

小白:嗦嘎,好像这些问题太简单。。。关于命令的使用我还是去百度比较好,哈哈。还有第二个问题啊,为什么dockerfile里面要执行两次sh run.sh?
小黑:起初我也以为将settings直接复制到.m2下面,然后sh run.sh就行了,但是我发现第一次执行mvn install的时候会将.m2覆盖掉,所以,只能先执行一次run,sh,再复制,再执行,就能用私服仓库了
小白:原来是这样。那为什么是构建的时候去运行run.sh这个脚本?而不是启动的时候呢?这样如果想部署最新的代码,就不需要重新构建,直接运行容器,就会自动运行run.sh去拉最新的代码了。
小黑:嗯,你说的是对的,像那种不经常变的东西,构建的时候安装好,经常变的呢,就写成脚本,容器运行的时候再执行,这样是最好不过的。
小白:嗯嗯,最后一个问题是,-p后面的80:8080哪个是宿主机哪个是容器端口?
小黑:。。。这个你还问就太那个了,你不知道tomcat运行的时候默认端口是8080吗?然后我测试的时候没有加端口号直接访问,说明是80端口,所以。。。你说呢?
小白:哈哈,懂了懂了,所以是-p 宿主机端口:容器端口
小黑:嗯,没错。怎么样,是不是感觉这种操作效率极其低下,就是简单粗暴了一点而已。每次构建都得花大量时间。
小白:对啊对啊,所以赶紧讲讲剩下的两种方法吧。
小黑:第二种就不演示了,太麻烦了,需要先将安装包下载下来,然后在Dockerfile里面用命令复制到容器,然后再用命令安装,太费事了。这个也解决不了maven第一次install的时候,初始化仓库所耗费的时间。咱们直接演示第三种,最优秀的办法。

第一步,创建一个空目录,并且创建Dockerfile、run.sh和settings.xml
在这里插入图片描述
第二步,在宿主机中安装java、maven和git等环境
如果使用yum安装,映射目录的时候会比较麻烦,总感觉那不齐这不齐的,所以我干脆直接将java和maven的二进制包下下来,安装git的时候只能编译安装,因为git没有二进制包,只有源码。
安装java
直接去官网下,下下来后解压将java8目录移到/usr/local/下

mv java8/ /usr/local/

maven也是一样的

wget https://mirrors.cnnic.cn/apache/maven/maven-3/3.6.2/binaries/apache-maven-3.6.2-bin.tar.gz
tar -xf apache-maven-3.6.2-bin.tar.gz
mv maven/ /usr/local/

在这里插入图片描述
可以看到,maven报错了,看错误信息,说在这些路径上找不到java命令,OK,那我们就将java软链过去(相当于搞个快捷方式)

ln -s /usr/local/java8/bin/java /usr/bin/java

在这里插入图片描述
OK,java和maven简单搞定了。
最后安装git,这个其实也简单,只不过一开始网上搜教程安装的时候都会有些坑,我把这些踩过了,所以直接用就很简单了。

#下载git源码
wget https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.9.5.tar.gz
#解压缩
tar -xf git-2.9.5.tar.gz
mv git-2.9.5 git
#安装依赖环境,如果你想验证是否真的需要,可以先略过这一步,再回来一个个安装试试
yum -y install gcc openssl-devel curl-devel expat-devel perl-ExtUtils-MakeMaker package
#安装
cd git/
make prefix=/usr/local all
make prefix=/usr/local install

验证
在这里插入图片描述

将settings文件复制到.m2目录下(因为不是yum安装的,所以.m2目录还没有,需要手动创建,也可以先使用mvn创建一个demo,会自动生成.m2目录)
在这里插入图片描述
用mvn命令创建一个demo,目的是为了初始化仓库,下一次创建别的项目的时候,依赖包都已经下载下来了,就会快很多。

mvn archetype:generate -DgroupId=com.acme -DartifactId=demo
第三步,编写Dockerfile
#基于centos:7镜像
FROM centos:7

#这里只是声明这个目录需要共享,实际上还得run的时候再指定一次才行
VOLUME ["/root/web-server2", "/usr/local/java8/", "/usr/local/maven/","/usr/local/bin/", "/usr/bin/", "/root/.m2"]

#运行启动脚本
CMD ["sh","/root/web-server2/run.sh"]

这里再将run.sh贴出来一次

#!/bin/bash
rm -rf /root/web-server2/min-system-web-service
echo '【INFO】正在拉取代码……'
/usr/local/bin/git clone -b feature/dev git://github.com/coderxj/min-system-web-service.git /root/web-server2/min-system-web-service
echo '【INFO】拉取代码完成,目录:'  `pwd`
echo '【INFO】kill 服务'
kill -9 `cat /root/web-server2/min-system-web-service/service.pid`
echo '\n\n\n'
cd /root/web-server2/min-system-web-service/min-system-service/
echo '【INFO】正在打包……'
/usr/local/maven/bin/mvn clean install -Dmaven.test.skip=true
echo '【INFO】打包完成,目录:'  `pwd`

echo '【INFO】开始启动服务……'
/usr/local/java8/bin/java -jar ./target/min-system-service*.jar

第四步,构建镜像
在这里插入图片描述
第五步,用构建好的镜像,运行容器

docker run -it -v /root/web-server2:/root/web-server2 -v /usr/local/java8:/usr/local/java8 -v /usr/local/maven:/usr/local/maven -v /usr/local/bin/:/usr/local/bin/ -v /usr/bin:/usr/bin -v /root/.m2:/root/.m2 --privileged=true -p 80:8080 web-server2

启动完成
在这里插入图片描述
第六步,测试是否调得通接口

在这里插入图片描述
正常!

小白:wow,这个构建+运行也太快了。果然还是这种方法好,环境安装一次即可,以后所有容器都能直接共用这些环境。环境安装什么的,没啥难度,跟着来,实在不行百度,但是有个地方有疑问,为什么.m2目录也共享了?
小黑:这个问题问得好,其实不共享也是可以的,但是容器就会使用自己的.m2目录,里面没有我们的settings文件,所以会去中央仓库下载jar包,非常的慢,而且每次容器关闭后,这些数据都会消失,导致每次运行都要去下载大量的基础jar包,耗费太多时间。
小白:原来是这样,所以是不是那些想要持久化的数据都放到宿主机就好了?
小黑:大概是这么个意思吧,但是一定要注意好权限的把控,否则别人的容器也能访问你的数据那问题不是就大了。
小白:了解了解。还有问题,运行命令为什么加–privileged=true?这是个啥?
小黑:一般情况下容器是访问不了宿主机的目录的,因为没有权限,有其它更细致的办法,但是没时间去弄它,所以就加这么一个参数,使得容器对宿主机的共享目录有读写权限。
小白:哦哦,嗦嘎,了解了。最后一个问题!为什么要将那些环境包都移到/usr/local目录下?
小黑:其实你不移也行,怎样都行,只是安装的软件统一放那个目录比较规范。因为/usr/local就是存放用户自己的软件的地方。
小白:嘿嘿,好了,这下没疑问了。
小黑:好了,现在也演示完了,你们按照我这个取把其它服务也弄一下,然后下周我统一整理一下。
小白:没问题
小黄:OK
小黑:如果有什么问题,可以随时来问我,可不要耽误了进度……

未完待续……

原创文章 257 获赞 277 访问量 69万+

猜你喜欢

转载自blog.csdn.net/qq_18297675/article/details/102733817