摘要:本文不讲晦涩难懂的概念、不讲实现原理、不讲内核机制,仅仅从搭建一个简单web应用的场景出发,讲讲如何复用docker完成我们开发过程中的一些日常事务,达到提升研发效率的目的。
Docker自12年发布,经过几年的蛰伏,15,16年势头猛劲,在大量的企业得到的实施,尤其是在互联网、大数据相关领域更是势不可挡。然而,在一些传统行业、政府、事业机关、小企业中,Docker的使用率还是相对较低的,原因是多方面的,但其中很重要一个原因,源于对这些铺天盖地席卷而来的新技术的“恐惧”。企业发展到一定规模后,其技术架构、组织架构、研发模式基于已经稳定,新技采用势必会对现有组织、业务产生影响,而这种影响的好坏,早期是很难判断的,因此除了少数技术狂热派,一般都会比较谨慎,然而小的初创公司,又很难承担一次技术选型的失败,因此更愿意去采用生态相对成熟、实施过程能有较大容错空间的技术。但是经过这些年的市场的磨练,Docker其实已经成为了云计算领域的一员老将,技术栈、生态各方面已经得到完善。
本次以部署一个简单的web应用为例,希望能给还徘徊在Docker门口的开发者、决策者们一些启发,如何利用Docker来简化日常研发工作、提升研发效率。
本文所讲解示例如下图,是一个简化的web应用模型,包含了常用的组件,下面将详细讲解如何基于Docker快速完成整个环境的搭建和软件的部署。
搭建MySQL数据库
一行命令搞定!
docker run –dti-e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
执行docker ps|grep mysql命令,能看到mysql的容器已经启动,比起下载、安装还有各种配置,够快吧。
当然这样运行起来的MySQL,还没有办法供外部服务直接访问,,需要把容器内的服务开个通道,让外部服务通过宿主机的地址找到这个特殊通道,才能实现与外部服务的交互。
把容器内的服务映射到宿主机上
加上参数 -p 宿主机监听port:容器开放port,建立“通道”,这样当用户访问宿主机的port时,请求会自动发送到容器内对应的端口上
docker run -dti -p 3306:3306 -eMYSQL_ROOT_PASSWORD=123456 --namemysql-service mysql:5.7
对容器运行时产生的数据进行持久化
因为每次执行docker run,相当于从指定镜像重新启动一台电脑,镜像是静态不变的,那么意味着,重启后之前运行时产生的数据会全部丢失,一切回归到初始状态,这个对数据库是致命的,如果不能持久化存储数据,也就失去了其意义,Docker也就不可能走到现在。
在启动容器时,加上参数 -v 宿主机目录:容器内目录,即可将宿主机的目录mount到容器中,这样容器销毁时,宿主机目录的数据还在,重新启动容器,会继续从宿主机加载数据,借尸还魂,很机智。
docker run -dti -p 3306:3306 -eMYSQL_ROOT_PASSWORD=123456 -v /usr1/mysql_data:/var/lib/mysql --name mysql-service mysql:5.7
现在运行起来的MySQL就基本上已经能满足日常的需求了。
当然,MySQL镜像本身还提供了强大的业务参数(这个是镜像制作者提供的,并非Docker提供),通过dockerrun的 -e 参数指定容器运行的环境变量(等价于Dockerfile里的ENV参数),实现相应的mysql能力,非常有用:
MYSQL_ROOT_PASSWORD:指定root用户的密码
MYSQL_USER: 新创建一个普通用户
MYSQL_PASSWORD:新创建用户的密码
MYSQL_DATABASE:新创建一个database
MYSQL_ALLOW_EMPTY_PASSWORD:是允许无密码登陆
想要使用时只需要通过加到命令行里即可,如:
docker run -d -p 3306:3306 --namemysql-service -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_USER=visitor -eMYSQL_PASSWORD=visitor -e MYSQL_DATABASE=visitordb -eMYSQL_ALLOW_EMPTY_PASSWORD=true -v /usr1/mysql_data:/var/lib/mysql mysql:5.7
这为我们制作业务镜像提供了一些参考,把部署、运行时所需要的变量,通过容器环境变量的方式传递给业务组件,当然业务组件也需要实现从这些约定好的变量里取值。
使用定制的MySQL镜像,初始化数据
当然,仅仅把MySQL启动起来大部分场景下是不够的,还需要创建业务需要的database、tables,有些时候可能还要导入一些初始的数据。
接下来就讲一下,如何通过对官方镜像进行定制,实现在启动容器时,自动执行指定脚本,来创建表、初始化数据。
定制镜像,就是基于官方的mysql镜像,加上一些脚本、修改一些权限,再统一打包到新的镜像里,在容器启动后自动执行这些脚本,做一些我们需要的业务操作。
首先,我们以mysql:5.7作为基础镜像
FROM mysql:5.7(语法:FROM 镜像名称:镜像版本号)
然后,把需要在MySQL容器启动后执行的脚本拷贝到目录/docker-entrypoint-initdb.d下。(MySQL镜像启动后会扫描这个目录,然后自动执行里面的shell和sql脚本)
COPY ./setup.sh
/docker-entrypoint-initdb.d/setup.sh (语法:COPY 文件所在的主机路径:镜像中的绝对路径)
接着,把setup.sh会引用到的其它文件,如shell,sql,config等,放到脚本中引用的目录下。(如果有多个脚本需要执行,都放在mysql指定目录下时,mysql并不知道脚本的执行先后顺序,因此一般是在mysql指定目录下放一个执行入口文件,在该文件中去按业务逻辑调用其它文件)
COPY ./init.sql /myinfo/init.sql
最后,在执行shell脚本时,希望root用户不输入密码就能登陆MySQL,这里需要按MySQL镜像的规范,设置一个环境变量:
ENV MYSQL_ALLOW_EMPTY_PASSWORD yes(语法:ENV 环境变量名称 环境变量值)
这样就生成了我们需要的Dockerfile文件
执行 docker build . -t mysql:cust1
就能生成我们自己的镜像了,后面加上参数-t就可以给生成的镜像打上标签,以便进行版本管理。运行该镜像的命令与最开始提到的一样,只是镜像的版本需要使用最新生成的mysql:cust1,这里不再赘述。
上面MySQL的示例,涵盖了80%常用的docker制作和运行的指令和参数,这也就是我一直认为,Docker技术也许很高深,但使用确实很简单的原因。
搭建tomcat,运行后台服务
接下来按照制作MySQL镜像的方式,制作tomcat服务镜像,一个带有VisitorService应用的tomcat定制镜像,这里不再一步步讲解,直接把成品的Dockerfile拿出来给大家分享。
FROM tomcat:7-jre8
COPY ./VisitorService.war /usr/local/tomcat/webapps
就是这么简单,两行搞定,思路和写MySQL的Dockerfile是一样的:
1、先选择一个基础镜像:因为程序是基于JDK1.8开发的,所以选择了tomcat:7的jre1.8版本进行定制,具体还有哪些可用的镜像版本,可以在dockerhub官网查到
2、然后把自定义的内容塞到镜像里:把编译好的war包拷贝到tomcat的webapps目录下,就像平时在自己测试机上操作一样。如果我们不想每次编译完war包都重新构建镜像,也可以用类似于MySQL目录映射的方法,在运行tomcat镜像时,把本地存放war包的路径,映射到容器的webapps目录下。
3、设置环境变量:因为应用中会连接数据库,需要在启动容器时,把数据库信息加入到环境变量中,或在dockerfile中通过EVN参数设置,但因为值是会变的,直接写在dockerfile中,灵活性会差很多,因此我们采用了在启动时通过-e 的方式来设置,代码里已实现从环境变量获取这些信息。
最终运行业务软件镜像容器的命令参考如下,命令行完成了目录映射、环境变量设置,并指定要启动的镜像版本:
docker run -p 9999:8080 -v 本地目录/war文件:/usr/local/tomcat/webapps/xxx.war-eMYSQL_ADDR=mysql_ip:3306 -e MYSQL_DB=visitordb -e MYSQL_USER=visitor -eMYSQL_PWD=visitor -dti tomcat:7-jre8
搭建nginx,运行静态web服务
与Tomcat类似,也是有两种方法
一种是,把需要部署在Nginx上的文件,以及配置打包到镜像中:
FROM nginx:v2
COPY ./index.html /usr/visitor/index.html
COPY ./visitor.conf/etc/nginx/conf.d/visitor.conf
另一种是,从本地目录映射:
docker run -p 80:80 -v 本地目录/xx.conf:/etc/nginx/conf.d/xx.conf -v本地目录:容器目录-dti nginx
搭建并运行Redis服务
因为只是存取一些缓存数据,因此不考虑对容器内的数据做持久化,直接放在容器里,因此这里不对redis镜像做任何改造,直接使用。
docker run –p 6379:6379 –dti redis:2.8.23
大功告成
好了,一套简易的web应用环境就搭好了,下面就是最终效果
我们来对比一下Docker帮我们提升了多少效率:
说实话,以前搭环境最怕的就是像Nginx这种,下完还要编译之后才能运行的,一旦报错,那真是对耐性的考验。现在好了,一个Dockerfile,加docker build,docker run两个命令,轻轻松松搞定环境的搭建,以前这些困难全没了。
最关键的是,日常研发过程中,搭建3至5套环境是最基本的要求了,使用编写好的Dockerfile,可以让我们快速完成各种TEST/QA/STAGE环境、Demo演示环境的搭建与复制,而不需要为拷贝动辄几十G的安装包而苦恼,也不会因为各各环境操作系统版本的差异而痛苦,着实为我们省了不少时间和精力。
后记
正因为Docker这么火,所有的公有云厂商不约而同的都提供了容器服务,这进一步降低了用户的使用门槛,让企业用户无需关心日常运维、问题处理、版本升级、技术演进等纷繁复杂的事务,使用户更专注于自身业务竞争力的提升。
华为云提供的容器服务包括云容器引擎服务(CCE)和云容器实例服务(CCI),围绕着云容器,还提供了镜像管理、应用编排、应用运维管理、微服务管理、云中间件等一系列构建容器化应用生态所需的配套服务,并在兼容社区的基础上,针对游戏、电商、基因等行业,对容器的网络性能、计算性能、存储性能、安全等方面进行了优化,能满足广大用户CI/CD、自动化运维、秒级弹性伸缩、高并发低时延、高性能运算的诉求。
赶紧点击下面的链接来体验一下吧:
华为云容器引擎:https://console.huaweicloud.com/cce2.0/
华为云容器实例:https://console.huaweicloud.com/cci/