Docker部署实验
一、实验背景
最近对Docker很感兴趣,对他的轻量化,便于扩展性很是痴迷,所以在工作之余进行了一些探究,结合自身公司的运维场景,设计了以下实验。
说明:大多数场景都是使用的Java7或者Java8结合resin3或者resin4的Java中间件来跑服务的,所以在这里我使用Java7和resin4来作为实验环境进行讲解。为了简便演示,本应该编译打包war包至webapps文件夹后启动中间件,在这里我只使用静态页来进行测试,实际环境把index.html换成ROOT.war即可。
二、Docker与虚拟化异同
无论是什么技术交流活动,但凡讲到Docker的主题与虚拟化的对比是必不可少的一个环节,所以尽管老生常谈,我也在这里啰嗦几句。我尽管不是计算机学院的学生,但是在大学期间就对虚拟化很感兴趣,回想一下那是我大三的时候,也就是12年左右,为了做一个Esxi的实验,重新买了一台CPU支持虚拟化的笔记本,搭载了16G内存,在当年很是风光,dei谁跟谁说我是i7-16G内存,然后享受着同学们羡慕的眼光,当年的主流本还是2G内存呢,从此开启了我的虚拟化实验的探索。好像主题有些跑偏,那我们言归正传。
先放一个老图,从图上可以看出一个服务器的基本架构,从下往上是都由必不可少的硬件+操作系统组成。但是在第三层就有变化了,对于虚拟化来讲,有一个Hypervisor层,然后之上的虚拟机都有一套完整的操作系统,并和宿主机互不影响,相互隔离。而对于容器来讲,第三层不是Hypervisor,而是docker引擎,之上的docker容器没有完整的操作系统,而是只有一些库文件,真正的内核还是借用的宿主机的内核。
所以就会出现这样一种情况,在CentOS 7上运行了CentOS 6的Docker镜像,在运行时会出现下图的样子:CentOS6的发行版却拥有3.10的内核。
其实简而言之,虚拟化和容器只差了一层内核。如果理解到这个层次,那么下面这个图就不需要我再逐条分析了。
三、Docker落地实验
现在我们还在用物理机上直接跑服务的方法,最大的问题就是测试说在本地测试后没有问题,部署到线上就出问题,然后就是运维与开发的不断扯皮。其实我们也不想这样,我们也想合作愉快,但是总会出现这样那样的问题怎么办?那就使用现在比较流行的话题“不可变基础设施+拒绝SSH”方法论(http://www.iteye.com/news/30970)。这个观点是在2013年提出来的,在当时可能只是一个设想,不过在容器时代这个是一个最佳的实践,因为容器默认就是不可ssh的,并且centos的docker镜像本身居然是没有vi命令的,只有cat,所以从官方设计的理念来讲它就是不可被写的(关于日志的输出后面再讨论)。
说了这么多估计大家都在等着实验内容吧,好,那我们接下来来点干货,可以先喝口水润润嗓子。
1、配置阿里云镜像仓库
由于官方的docker hub在国内连接很慢,所以我首先在阿里云开通了容器镜像服务。在点击容器镜像服务的时候不由分说先弹出一个输入密码框,这里是一会会用到的docker hub登录密码,一定牢记。输入完密码后会跳转到镜像仓库列表,但是是空的,所以需要创建镜像仓库,这时候会引导你创建“命名空间”,然后再创建仓库,把所需信息填好就好了,最后一步选择“本地仓库”即可。
在本次实验中创建的仓库名称是dictweb,创建完成后会告诉你使用方法,然后在左侧镜像加速器中还可以配置阿里云镜像的加速地址。
等这一切先期工作都配置完成后,我们就可以开始自己创建初始镜像了,也就是Java7+resin4。
2、下载镜像并初始化环境
我使用的基础镜像为Java7,然后把resin4的二进制包传上去即可。
# docker pull Java:7 //拉取镜像
# docker images //查看镜像ID
REPOSITORY TAG IMAGE ID CREATE SIZE java 7 5dc48a6b75af 5 months ago 584MB |
# docker run --name dictweb-v0 -p 8081:8080 -itd 5dc48a6b75af /bin/bash
//运行容器命名为dictweb-v0,并将其放在后台运行,映射宿主机端口号8081到容器的8080端口
# docker cp /tmp/resin-4.0.55.tar.gz dictweb-v0:/usr/local/ //把resin的安装包复制到容器里的/usr/local/下
# docker attach dictweb-v0 //进入容器shell界面
root@17507ed2e71f:/# cd /usr/local/
root@17507ed2e71f:/usr/local# tar xzvf resin-4.0.55.tar.gz //解压
root@17507ed2e71f:/usr/local# cd resin-4.0.55
root@17507ed2e71f:/usr/local/resin-4.0.55# rm -rf webapps/ROOT/ //删掉自带文件,因为我们在做模板。
root@17507ed2e71f:/usr/local/resin-4.0.55# exit //退出容器
# docker login --username=****@163.com --password=***** registry.cn-**.aliyuncs.com
//登录你的阿里云镜像仓库
# docker commit -a "author(作者名)" -m "dickweb-v0(commit标记)" dictweb-v0(容器名) registry.cn-**.aliyuncs.com/docker-yx/dictweb:v0 //本地提交一次
# docker push registry.cn-**.aliyuncs.com/docker-yx/dictweb:v0 //提交到阿里云镜像仓库
至此,一版初始化的镜像就创建完成了,下面就要用它进行持续部署业务了。
3、下载镜像并进行第一版本的发布
假定我们现在已经编译好了v1版本的war包,叫ROOT.war,但是为了演示简便,就新建一个文件 /disk1/code/index.html 内容就是v1.0.0,所以我只需要输入以下命令:
# docker pull registry.cn-hangzhou.aliyuncs.com/docker-yx/dictweb:v0 //拉取原始镜像
# docker run --name dictweb-v1.0.0 -dit registry.cn-**.aliyuncs.com/docker-yx/dictweb:v0 /bin/bash //后台启动镜像,命名为dictweb+版本号
# docker cp /disk1/code/index.html dictweb-v1.0.0:/usr/local/resin-4.0.55/webapps/ROOT //复制网页文件至指定目录
# docker stop dictweb-v1.0.0 //先停止镜像
# docker commit -a "yx" -m "dickweb-v1.0.0" dictweb- v1.0.0 registry.cn-**.aliyuncs.com/docker-yx/dictweb:v1.0.0 //本地提交一下
# docker push registry.cn-**.aliyuncs.com/docker-yx/dictweb:v1.0.0 //提交至阿里云仓库
# docker rm dictweb-v1.0.0 //删除本地容器(可选,建议删除,否则太多了)
至此第一个版本的镜像已经提交到阿里云仓库,接下来可以在目标服务器下载并运行了。
4、在目标服务器下载并启动
注意!:不是在刚才的主机上操作的,是在另一些线上的服务器哦!
# docker login --username=***@163.com --password=** registry.cn-**.aliyuncs.com"
# docker pull registry.cn-**.aliyuncs.com/docker-yx/dictweb:v1.0.0 //拉取v1.0.0版本
# docker ps -q --filter "name=dict" |xargs docker stop //停掉上一个版本的容器
# docker run --rm --name dictweb- v1.0.0 -p 8081:8080 -dit registry.cn-**.aliyuncs.com/docker-yx/dictweb:v1.0.0 /bin/bash
//后台启动v1.0.0版本镜像,并加上重命名、端口映射和停止后删除的选项
# docker exec dictweb- v1.0.0 /usr/local/resin-4.0.55/bin/resin.sh start //启动resin
启动后就可以打开线上机器的8081端口查看了
四、自动化部署
好吧,这又是一个老生常谈的主题,运维界一直致力的目标,所以我还是来谈一下如何自动部署,做到“持续部署”。
当然说到持续部署还是离不开Jenkins,所以基本的思路就是在刚才的命令上写一些变量和shell脚本,我们把部署思路分成两份,一个是编译、打包、上线用的,另一个是线上的服务器运行用的,于是建立两个任务:DockerUpload和DockerDeploy,直接上脚本吧,大家作为局内人一看就懂,不再啰嗦。
DockerUpload
#-------------------------------------------编译--------------------------------------------------
docker pull registry.cn-**.aliyuncs.com/docker-yx/dictweb:v0
docker run --name dictweb-${version} -dit registry.cn-**.aliyuncs.com/docker-yx/dictweb:v0 /bin/bash
docker cp /disk1/code/index.html dictweb-${version}:/usr/local/resin-4.0.55/webapps/ROOT
#-------------------------------------------上传--------------------------------------------------
docker stop dictweb-${version}
docker commit -a "yx" -m "dickweb-${version}" dictweb-${version} registry.cn-**.aliyuncs.com/docker-yx/dictweb:${version}
docker push registry.cn-**.aliyuncs.com/docker-yx/dictweb:${version}
docker rm dictweb-${version}
DockerDeploy
ssh [email protected] "docker login --username=**@163.com --password=** registry.cn-*.aliyuncs.com"
ssh [email protected] "docker pull registry.cn-*.aliyuncs.com/docker-yx/dictweb:${version}"
ssh [email protected] "docker ps -q --filter "name=dict" |xargs docker stop"
ssh [email protected] "docker run --rm --name dictweb-${version} -p 8081:8080 -dit registry.cn-*.aliyuncs.com/docker-yx/dictweb:${version} /bin/bash"
ssh [email protected] "docker exec dictweb-${version} /usr/local/resin-4.0.55/bin/resin.sh start"
这样在测试测完后就可以使用DockerUpload任务把代码打包成镜像传上去,然后运维人员使用DockerDeploy任务把对应版本的镜像run起来。如果遇到问题直接写对应版本号就可以实现秒级回滚!
至此本篇文章已接近尾声,但是大家肯定还有疑问,刚才不是说有日志往哪里打吗?就目前环境来讲日志是打到容器里的,但是这肯定不是最佳方案,后面我会结合K8S和ELK等平台讲解日志的处理问题,请大家持续关注我的博客。谢谢各位!
也欢迎高手从下方留言交流,或加qq 1848473726交流沟通。