虚拟化云和容器机器的发展,给我们带来了极大的方便,尤其是开发环境,开发、测试、验证环境的维护和一致化一直困扰着开发和测试人员,为了配置一个环境往往需要花费大量的精力,而且还无法解决环境一致性问题,由于环境差异导致的Bug问题也让开发人员和测试耗费大量的精力。如何解决这种问题?那就是利用神器Docker容器了。本文虫虫将实例演示如何使用将Docker来轻松地建立一个高效的本地开发环境。

开发环境架构
我们假设开发组使用了下面一个典型的多余系统架构,其中涉及了NodeJS、Python、Golang、数据还有Web前端JS等应用,架构图如下:

架构组成
构成部分由:
NJS1:NodeJS服务,监听端口7000,连接数据库Db1(Psql数据库,监听端口5433)。
Py1: Python服务,监听端口9000,连接数据库Db1。
Go1: Golang服务,监听端口5000,连接数据库Db1。
NJS2: NodeJS服务,监听端口8000,连接数据库Db2(Psql数据库,监听端口5432)。
Web服务: webpack的开发前端服务器,监听端口8080。
以上环境可以通过监听的端口互相通讯和数据交流。
问题分析
管理繁琐
要管理这些服务,需要打开大量终端窗口,然后单独运行,虽然可以使用screen和tmux这样的神器来帮助我们使用多窗口,甚至工作界面的共享等,但是随着要管理不断增长,管理会相当繁琐耗时。

不一致的依赖环境
假设服务依赖于不同开发语言的版本(比如Python 2和Python 3 )。我们就需要在运行服务之前手动切换环境中语言版本,当然可以用多语言版本管理器比如pyenv我们服务业有可能需要多个数据库服务器,那么在运行服务之前,我们需要确保数据库服务器运行无误,并且数据库连接配置都无误。还有更多的是开发应用的基础类库版本的依赖问题。
基于Docker的开发环境
Docker容器是一种完美的工具,可以轻松地一键创建,部署和运行应用程序。通过容器可以将所有需要的环境打包到一个镜像中,然后通过镜像一键生成开发容器允。
Docker容器
理想的docker容器是一个超轻量级的linux的虚拟机,在它上面运行的应用程序服务。容器中有应用代码及其所有依赖环境(系统库,工具等)。我们上面提到的架构,容器化后如下图所示:

容器管理Docker Compose
Docker Compose是一个用于管理和编排容器定义和运行的多应用程序的工具(类似的流行工具还有谷歌的K8s)。通过Compose,可以使用YAML文件来配置应用程序的服务。然后可以一键创建和运行所有的服务。Docker Compose也提供Web UI管理界面和容器监控系统等。。
Docker Compose由一下部分构成
服务
:服务是可以用Compose工具运行的各个docker容器列表。通过服务我们来定义各个容器名称、端口以及其他配置。
网络
:网络组件提供了服务之间互相连接的通道。各个容器可以将自身附加到网络,相同网络内的所有容器可以彼此通信。
卷
:默认情况下,Docker容器不包含任何类型的持久性存储。如果一个docker容器被关掉,那么其内存中的所有数据都会丢掉。为了持久性保存一些数据,我们需要数据卷来挂载到数据机硬盘上。

实战配置
docker-compose.yml配置
我们上述典型环境的docker-compose.yml如下:


其中一个服务(njs1)的部分代码如下:

服务运行
配置好docker-compose.yml后,可以通过下面命令启动
docker-compose up
如果配置无误,比如njs1成功启动后,将显示:

在浏览器通过localhost:7000分文该服务。
常见docker-compose命令
build
dockerfile的路径。注意:可以指定包含Dockerfile的文件夹,也可以指定Dockerfile本身的完整路径。
command
启动docker容器时运行的命令。
environment
需要设置的所有环境变量。
ports
指定容器内端口与主机端口的映射关系。
working_dir
这是希望运行上面指定的命令的容器内部的路径。
启动所有服务
docker-compose start
该命令会启动docker-compose文件中的所有服务并以demon方式启动,在后台运行。
停止所有服务
docker-compose start
停止所有服务
重启服务
docker-compose restart njs1
启动特定服务
docker-compose up njs1
在docker-compose.yml服务列表中只启动njs1
查看特定服务的日志
docker-compose logs -f njs1
该命令将会在终端打印njs1的日志。
ssh连接到特定容器
docker-compose exec njs1 bash
总结
本文我们利用docker容器的方式来简化开发环境的架构,解决典型开发环境中管理繁琐,环境一致性等问题,为了简洁我们只以Docker Compose为例进行了架构重建。其实基于Docker编排管理系统K8S,结合Gitlab CI/CD功能,可以实现版本管理、测试、安全扫描、验证、发布等一键自动化DevOps架构,以后我们会深入探索。