1. 微服务镜像制作
这里就不介绍微服务了,在看这篇文章的前提是您对微服务和docker已经有一定了解了,这里对基础概念就不做太多描述了,不了解的同学可以看看博客里的关于微服务和docker的文章。本篇是基于springcloud来实现微服务,将springcloud组件和微服务部署在docker上。
首先我们要制作服务镜像,如果大家是基于springcloud架构的微服务实现,那么我们要做的事情就比较简单,springcloud对环境要求比较简单,只要环境中有jre环境就可以运行,所以我们要有一个java 1.8的镜像,如果是自己练习可以使用Docker Hub的java:8镜像,如果是公司的私服,那么就要找个linux镜像,在镜像上自己装上jdk1.8,生成一个jdk1.8的镜像方便后面使用。
制作镜像的过程可以通过docker commit也可以通过编写Dockerfile使用docker build命令来实现,这里为了方便后续Dockerfile的复用和修改,我们使用Dockerfile的方式来创建微服务镜像。
首先创建eureka的Dockerfile
FROM 10.132.33.8:5000/basejdk8
MAINTAINER feiweiwei (feiweiwei@shrbank.com)
ENV JAVA_HOME /home/jdk8
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib:$JAVA_HOME/bin:CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
COPY eureka.jar /home/eureka/eureka.jar
RUN chmod -R 777 /home/eureka/eureka.jar
WORKDIR /home
EXPOSE 8761
CMD ["java","-jar","/home/eureka/eureka.jar"]
对上面的Dockerfile做个解释,具体Dockerfile的语法命令参考附录:
- 容器镜像使用私服的10.132.33.8:5000/basejdk8,这里基础镜像的名字根据自己的jdk8镜像的名字来定
- 设置环境变量,这里主要是设置了JAVA_HOME、CLASSPATH、PATH这几个java相关的环境变量
- 将当前宿主机目录下的eureka.jar拷贝到容器的/home/eureka目录下
- 将eureka.jar的权限改为777方便测试,实际生产环境需要根据实际访问安全要求来设置(所有拷贝到容器中的文件和文件夹权限为0755,uid和gid为0,所以需要对文件设置访问权限)
- 打开容器端口,默认所有容器的端口都是关闭的
- 容器执行命令java -jar /home/eureka/eureka.jar,这里具体的可以根据自己的需求增加不同的参数
依葫芦画瓢,以此制作其它微服务的Dockerfile。
下面我们根据刚才写的Dockerfile编译生成镜像,使用docker build命令 docker build -t 10.132.33.8:5000/test_eureka:v1 .
,具体build的参数可以参考help。从执行日志,我们可以看到在编译镜像的过程中,每条命令都生成了一个中间临时镜像,从中我们也可以了解到docker镜像的分层设计原则。
[root@slave1 base]# docker build -t 10.132.33.8:5000/test_eureka:v1 .
Sending build context to Docker daemon 417.6 MB
Step 1 : FROM 10.132.33.8:5000/basejdk8
---> 1d0b8c8ee253
Step 2 : MAINTAINER feiweiwei (feiweiwei@shrbank.com)
---> Running in a74c5f0c335f
---> 15b701bd2822
... ...
Step 12 : CMD java -jar /home/eureka.jar
---> Running in d94102c5ef52
---> e57a269c0494
Removing intermediate container d94102c5ef52
Successfully built e57a269c0494
将镜像push到register服务器
docker push 10.132.33.8:5000/test_eureka:v1
根据镜像在节点生成微服务容器,docker run的参数可以参考help或者我的另一篇关于docker的文章也有介绍。
docker run --name test_eureka1 -v /home/jenkins/project/base:/usr/base -dit -p 8761:8761 10.132.33.8:5000/test_eureka:v1
2. 宿主机资源分配
镜像做好了下面插播下宿主机资源分配,网上很多创建容器的例子都没有介绍资源分配,我们都知道docker是可以实现像虚拟化技术一样的资源隔离的,上面介绍的创建容器的过程中没有对容器进行指定的cpu和内存资源的限制,下面我们介绍下如何对部署的微服务容器进行CPU和内存的限制。
docker run --help|grep cpu
通过帮助看下docker run关于cpu设置的参数有哪些:
--cpu-percent int CPU percent (Windows only)
--cpu-period int Limit CPU CFS (Completely Fair Scheduler) period
--cpu-quota int Limit CPU CFS (Completely Fair Scheduler) quota
-c, --cpu-shares int CPU shares (relative weight)
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1)
--cpuset-mems string MEMs in which to allow execution (0-3, 0,1)
cpu-percent
--cpu-percent
只适用于windows,因为没在windows上试过,感兴趣的同学可以看下帮助,试一下就知道了。
cpu-period
默认的 CPU CFS「Completely Fair Scheduler」period 是 100ms。我们可以通过 --cpu-period
值限制容器的 CPU 使用。一般 --cpu-period
配合 --cpu-quota
一起使用。
设置 cpu-period 为 100ms,cpu-quota 为 200ms,表示最多可以使用 2 个 cpu.
docker run -it --rm --cpu-period=100000 --cpu-quota=200000 test_uc:v1
cpu-shares
默认所有的容器对于 CPU 的利用占比都是一样的,-c
或者 --cpu-shares
可以设置 CPU 利用率权重,默认为 1024,可以设置权重为 2 或者更高(单个 CPU 为 1024,两个为 2048,以此类推)。如果设置选项为 0,则系统会忽略该选项并且使用默认值 1024。通过以上设置,只会在 CPU 密集(繁忙)型运行进程时体现出来。当一个 container 空闲时,其它容器都是可以占用 CPU 的。cpu-shares 值为一个相对值,实际 CPU 利用率则取决于系统上运行容器的数量。
假如一个 1core 的主机运行 3 个 container,其中一个 cpu-shares 设置为 1024,而其它 cpu-shares 被设置成 512。当 3 个容器中的进程尝试使用 100% CPU 的时候「尝试使用 100% CPU 很重要,此时才可以体现设置值」,则设置 1024 的容器会占用 50% 的 CPU 时间。如果又添加一个 cpu-shares 为 1024 的 container,那么两个设置为 1024 的容器 CPU 利用占比为 33%,而另外两个则为 16.5%。简单的算法就是,所有设置的值相加,每个容器的占比就是 CPU 的利用率,如果只有一个容器,那么此时它无论设置 512 或者 1024,CPU 利用率都将是 100%。当然,如果主机是 3core,运行 3 个容器,两个 cpu-shares 设置为 512,一个设置为 1024,则此时每个 container 都能占用其中一个 CPU 为 100%。
测试主机「4core」当只有 1 个 container 时,可以使用任意的 CPU。
cpuset-cpus
通过 --cpuset-cpus
可以绑定指定容器使用指定 CPU,设置测试容器只能使用 cpu1 和 cpu2。
docker run -it --rm --cpuset-cpus="1,2" test_uc:v1
通过命令docker run --help|grep mem
查看可以设置的内存参数:
--kernel-memory string Kernel memory limit
-m, --memory string Memory limit
--memory-reservation string Memory soft limit
--memory-swap string Swap limit equal to memory plus swap: '-1' to enable unlimited swap
--memory-swappiness int Tune container memory swappiness (0 to 100) (default -1)
kernel-memory
官方说法是该参数如果没有特殊需求不需要设置。
memory
指定容器占用内存大小。
memory-reservation
选项可以理解为内存的软限制。如果不设置 -m
选项,那么容器使用内存可以理解为是不受限的。按照官方的说法,memory reservation 设置可以确保容器不会长时间占用大量内存。
memory-swap
指定限制内存大小并且设置 memory-swap 值为 -1,表示容器程序使用内存受限,而 swap 空间使用不受限制(宿主 swap 支持使用多少则容器即可使用多少。如果 --memory-swap
设置小于 --memory
则设置不生效,使用默认设置)。
docker run -it -m 100M --memory-swap 400M test_uc:v1
指定限制内存大小并且设置 memory-swap 值为 -1,表示容器程序使用内存受限,而 swap 空间使用不受限制。
3. docker-compose
通过做好的微服务镜像,我们已经可以实现手工发布docker容器了,这里为了方便单宿主机多容器管理,我们使用docker-compose来完成多容器的部署。
docker-compose主要有3个步骤:
- 使用Dockerfile定义应用环境,以便在任何地方都可以重现该环境;
- 在docker-compose.yml中定义组成应用程序的服务,以便各个服务在一个隔离的环境中一起运行;
- 运行docker-compose up命令,启动并运行各个微服务容器;
# cat docker-compose.yml
version: '2'
services:
test_eureka:
image: 10.132.33.8:5000/test_eureka:v1
ports:
- "8761:8761"
volumes:
- .:/home
test_uc:
image: 10.132.33.8:5000/test_uc:v1
ports:
- "8084:8084"
depends_on:
- redis
redis:
image: 10.132.33.8:5000/redis
解释下上面的docker-compose文件:
- 这个compose工程定义了3个service,test_eureka、test_uc、redis
- test_eureka使用私服10.132.33.8:5000/test_eureka:v1镜像,test_uc使用10.132.33.8:5000/test_uc:v1镜像
- test_eureka容器内8761端口映射到宿主机8761端口,test_uc容器内8084端口映射到宿主机8084端口
- test_eureka容器将宿主机当前目录挂载到容器/home目录
- test_uc容器依赖于redis容器,这样compose在启动的时候会优先启动redis容器,之后再启动test_uc
执行docker-compose up
,然后去eureka console页面查看服务注册情况,本例中在其它节点中共启动了4个uc容器实例。
其它的微服务节点也是这样build镜像,发布服务容器,这里需要注意一下除了eureka,其它所有服务模块之间的访问都要求是通过在eureka中注册的serviceId来访问,而不是通过ip,这样就可以利用eureka的服务发现功能来实现动态服务路由,也可以通过links来根据compose定义的服务名来访问。在compose中可以使用networks参数设置网络,这样可以做到宿主机内部容器服务间的网络隔离,这里就不具体去讲述了,感兴趣的可以网上查下相关文档。
5. 使用docker版本发布流程
Screenshot 2017-12-07 17.51.08.png使用docker部署版本后对原有的运维流程是有比较大的改动,对于传统信息中心日常版本部署流程一般是,开发将需要部署的版本提交信息中心后,信息中心同事拿到待部署版本后,将版本传到生产服务器,按照版本部署手册对版本进行部署,一般来说都会部署多节点,那么就需要多节点版本部署,信息中心同事就需要将版本在每台机器上都用同样的方式部署一遍,最后都部署好了以后还要按照要求的顺序启动服务器。在这样的版本发布流程中运维人员的工作量还是比较大的,如果需要部署的程序和节点都很多,那么运维的工作量将是巨大的,而且很容易有人为失误。
我们看看在传统的版本发布流程中存在哪些繁琐并且容易出问题的环节,1)第一个就是版本部署的环节,我们现在很多公司都在对原系统进行微服务化,那么微服务化了以后就意味着需要部署的程序模块变多了,需要部署的节点可能也变多了,如果还是依赖人工部署的化那是非常可怕的事情,所以微服务模块在docker上部署后版本发布流程也随之改变; 2)因为微服务化后不同模块之间存在的依赖关系可能也是相对比较复杂的,这就意味着服务启动同样存在着一定的依赖关系,人工操作很容易出现错误。
Screenshot 2017-12-07 17.51.17.png使用docker部署应用后的流程会有所改变:
1)开发人员提交版本中不但要和原来一样包含执行码,还要包含用于编译镜像的Dockerfile、创建镜像的shell和用于启动容器的docker-compose.yml文件;
2)运维人员将待部署版本发送到镜像构建服务器,镜像构建服务器是新增的,原先的版本部署流程中是不存在这样的服务器的,构建镜像的动作可以和现有的持续集成整合起来,运维人员执行创建镜像的脚本,将镜像全部创建好,并且发步到私有docker register;
3)运维人员在需要部署的物理节点上去执行脚本创建容器;
4)运维人员创建好容器后在每台物理节点上执行docker-compose up命令启动服务;
从整个过程中我们可以看到所有部署的动作都简化了,运维人员核心要关注的是镜像构建,镜像构建完了以后每个节点的部署都是直接拉取镜像创建容器,自动根据依赖关系启动容器,运维人员的压力一下子小了很多。后续日常运维过程中如果要增加节点什么的也是很方便的,只需要在新的节点上创建新的容器就可以了,因为容器中的应用的微服务都是统一发布在eureka上,已经实现了统一服务注册和发现,其他微服务之间互相访问都是通过eureka来发现服务,eureka会自动根据所有注册的服务节点进行智能路由,这样就做到了动态不停机扩容。
小结
讲了在docker上如何部署微服务,有的同学会说,这只是减轻了版本发布和日常扩容的工作啊,扩容的过程还是需要有人工的干预,并且那么多物理机,每台物理机有那么多的容器,日常运维也是很痛苦的过程,后面会再写几篇关于通过k8s对容器进行管理的文章,谢谢阅读,希望能帮到大家。
附录:
Dockerfile命令
FROM
基于哪个镜像
RUN
安装软件用
MAINTAINER
镜像创建者
CMD
container启动时执行的命令,但是一个Dockerfile中只能有一条CMD命令,多条则只执行最后一条CMD.CMD主要用于container时启动指定的服务,当docker run command的命令匹配到CMD command时,会替换CMD执行的命令。
ENTRYPOINT
container启动时执行的命令,但是一个Dockerfile中只能有一条ENTRYPOINT命令,如果多条,则只执行最后一条
ENTRYPOINT没有CMD的可替换特性
USER
使用哪个用户跑container
EXPOSE
container内部服务开启的端口。主机上要用还得在启动container时,做host-container的端口映射
ENV
用来设置环境变量,key value的方式设置
ADD
将文件<src>拷贝到container的文件系统对应的路径<dest>
所有拷贝到container中的文件和文件夹权限为0755,uid和gid为0
如果文件是可识别的压缩格式,则docker会帮忙解压缩
- 如果要ADD本地文件,则本地文件必须在 docker build <PATH>,指定的<PATH>目录下
- 如果要ADD远程文件,则远程文件必须在 docker build <PATH>,指定的<PATH>目录下
VOLUME
可以将本地文件夹或者其他container的文件夹挂载到container中。
WORKDIR
切换目录用,可以多次切换(相当于cd命令),对RUN,CMD,ENTRYPOINT生效
网友评论