[date: 2018-10-17 01:32] [visits: 8]

docker学习之杂谈

docker,离我第一次“近距离”接触已过去两年有余,这次为追赶潮流再次学习一番,此文记录自己对docker的一些认识和见解。

我对docker的认识

docker,本质上我认为是一个虚拟化技术,与虚拟机类似,虚拟机可以在宿主机上模拟完整的硬件环境,从而可以独立安装操作系统,docker则是在宿主机上模拟一个完整软件环境,从而达到独立运行应用程序的目的。这种模拟完整软件环境用于运行特定应用程序的技术,被称为容器技术,docker是其中最闪耀的一种。

镜像与容器

一般情况下,可以设想我们如何从零开始部署一个Node.js服务:

  1. 购买虚拟主机,即在电脑上安装操作系统
  2. 安装Node
  3. 上传代码
  4. 安装npm依赖
  5. 通过Node启动服务

上述5步中的每一步,都依赖其上一步且作为下一步的依赖,docker中这每一步依赖的“实体”,称之为镜像,其中第一步的依赖是docker的镜像基础"Kernel"。5个步骤的其中任意步都产生了一个独一无二的镜像,用一个唯一ID标识,而第五步产生的镜像是一个阶段性的收获,可用于后面运行容器。当然第二步也有一个阶段性收获,可以作为其他Node环境的基础镜像。

镜像是一层叠一层且的只读实体,而容器则是在镜像的基础上再加上一个动态可写的层,镜像好比如在一台裸机上安装软件,每安装一个软件都生成一个镜像,容器则是在安装完的机器上运行软件。

docker的核心优势

docker的核心优势在于部署,特别是复杂场景下的部署,假设我们一个应用程序需要独立运行10个实例,即需要在10台独立的机器上部署。如果没有docker,我们需要分别对每一台机器安装一系列的环境软件,随后上传代码再启动服务,而在安装一系列环境软件时,有可能遇到一些奇奇怪怪的问题,然后浪费数小时,10台机器都需要重复这样的操作,甚至遇到不同的问题,这对运维来说是苦不堪言。

苦不堪言还没完,当一次部署完成后,由于预期流量将继续增加导致需要增加10台机器,运维的痛苦x2,又或者预期流量将下降,只需要5台机器,那么空出来的5台机器又希望用来部署其他业务,这对运维来说同样是一个痛苦的事。

而docker的出现能够很好应对上述场景,10台甚至更多机器只需要安装docker,然后在本地将需要部署的服务构建成一个docker镜像发布到10台机器上,继而运行即可,需要增加或减少机器也都变成分分钟的事情,运维高高兴兴,再也不需要加班。

使用docker,每台机器不会受到业务服务部署所带来的环境影响,比如A业务服务使用Node.js 6.x版本,而B业务服务使用Node.js 8.x版本,如果一台机器先部署了A服务,后面由于流量减少空闲出来用于部署B服务,环境中就遗留了A服务的Node.js 6.x版本。使用docker则完全没有这种问题。

docker的适用性

前面讲了docker的核心优势,但并不是所有公司都应该使用docker化部署技术,主要考虑以下几点:

如果没有独立运维人员,作为一个开发,也许传统的机器部署完成的还不错,但要转向docker化部署,需要一定的学习成本,甚至可能学不好

docker作为一种虚拟化技术,在其中运行应用服务肯定有性能损耗,到底损耗10%、20%又或者是30-40%,我并没有严谨的数字

边际成本主要是针对规模问题,比如公司唯一核心业务全部部署在一台2核4G的服务器上,预期未来两年都够使用,这种情况下搞docker化,那么成本就是这一个业务一台机器均摊,但如果公司业务多、机器多,那么docker化这一套,成本平均到每台机器上,就非常划算

上诉讲到三点,在现实情况中作为决策者最需要考虑的是最后一点,作为开发就无所谓。之所以写这一部分,就是因为我曾在一个创业公司中深有体会,总共就两台生产机器,分别部署两个不同业务,自己当时觉得去搞什么docker化部署,有点笑话,所以两年内都没怎么接触docker。

docker实操

由于自己是做Node.js后台,且对docker的基本概念还有记忆,按照Node.js提供的docker部署指南操作,尝试将博客项目改成docker部署。

主要分为以下几步:

按照官方文档,在centos系统上安装docker-ce,没有遇到任何问题。

使用命令docker pull node:8.12.0,下载Node镜像,由于“你懂的”的原因,可能会很慢甚至无法完成

FROM node:8.12.0

WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 8080
CMD ["npm", "start"]

- 编辑.dockerignore

.git
node_modules
log

docker build -t amsimple/blog .

docker run -d -p 8080:8080 --name blog --add-host=docker.host:172.17.0.1 amsimple/blog

实操问题

实操的过程虽不复杂,但并非一帆风顺,其中有遇到一些问题,在此写出来供大家参考。

无法访问本机mysql

经费有限,自己mysql服务与博客项目在同一台机器上,且mysql监听的是127.0.0.1地址,在初次docker build与run之后发现应用会异常退出,查明是mysql无法连接导致。

故将mysql改为监听0.0.0.0,同时修改应用配置文件将连接mysql的host改为docker.host,然后在启动容器时通过--add-host将docker.host赋值为docker在机器上的网桥地址。

另外mysql默认不允许远程访问,需要在mysql中使用GRANT ALL PRIVILEGES...进行授权。

拒绝连接

docker容器run成功后,用curl localhost:8080会提示拒绝连接,这个问题是因为容器中应用启动时监听ip为127.0.0.1,而容器外部访问容器需要通过网桥,导致无法从外部访问到服务,提示拒绝连接。

此问题,通过修改应用启动时绑定IP为0.0.0.0即可。

两年前,自己实操docker练习的时候,一直在用iptables整网络TCP包转发相关内容,不知道是当时选择的教程有问题还是现在版本的网络支持更加智能化,这次完全不需要管网络这一块,就可以很轻松的完成一次docker化部署。

日志文件

博客项目的访问日志是用写文件方式实现的,但使用docker化部署后,文件到了容器中,对后续查看和分析挺不友好。

这个问题目前暂未解决,但以目前的认知水平,觉得有以下方案:

  1. 通过docker volumes相关特性,实现容器与主机共享日志文件,这个针对规模化docker部署没有什么帮助
  2. 继续在容器内部写日志,通过脚本每天定时整理与收集
  3. 引入日志系统,采用网络方式写日志,增加复杂度但能够应对规模化的docker部署

日志文件所体现的问题,本质上是容器运行过程中的内容产出应该如何处理,比如使用docker化的mysql、redis等都有类似问题。

自动部署

单台机器在没有使用docker部署前,自己通过git-webhook + shell脚本也实现了一个不那么可靠的自动部署,但这里提到的自动部署,主要是针对多机器的自动部署,而不是单台机器自动部署。

这个问题目前暂未解决且客观条件不充分(穷),同时必要性也没那么强,但docker化部署后,后续的自动化具体应该采用什么解决方案,以及面对真正的复杂场景,方案是否仍旧适用,自己并没有经验,无法发表意见。

从这里再次看出来,docker化部署真正的价值还是需要规模化才能体现,如果只有个位数的机器,折腾学习一下还行,真正使用感觉并没有创造多少价值。

总结

自己目前不一定要用docker来部署博客,但这次docker化部署,给自己带来更多其他的思考,比如上述的日志文件与自动部署问题。这些思考可以说与docker没有关系,它是关于服务与架构设计的思考,路漫漫其修远兮,吾将上下而求索。

通过学习和简单实操,感觉docker是一个非常好的东西,我希望自己能真正用上它,用会它,用好它,而不是像现在这样,当成一个轻量级虚拟机使用,下一次再学些k8s知识,看是否能达到自己心中docker使用合理的那根线。