用Docker创建开发环境

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 本文讲的是用Docker创建开发环境,【编者的话】鉴于还没有用Docker创建本地开发环境的先例,Jeff Nickoloff开创了一个先例,相信大家也可以。
本文讲的是用Docker创建开发环境 【编者的话】鉴于还没有用Docker创建本地开发环境的先例,Jeff Nickoloff开创了一个先例,相信大家也可以。

译者解释用Docker创建开发环境:作者在此处使用了Frankenstein一词。Frankenstein,《弗兰肯斯坦》原是英国诗人雪莱的妻子玛丽·雪莱在1818年创作的小说,被认为是世界第一部真正意义上的科幻小说。弗兰肯斯坦来自于此小说,可以理解为怪人;毁灭创造者自己之物
4.jpeg

知道一个事物和实现这个事物是完全不同的事情。从Docker诞生那天开始,我们就梦想着诸如“15秒部署一个项目”,“版本可控开发环境”,以及时髦的运维用语,如“滚动开发”,“软件定义架构”。处于浪尖的行业人士都在以前所未有的热情参与到将很多名词和工具,例如“编排”,“服务发现”等,定义,重新定义以及商品化大潮中。

我认为这股大潮的催化剂来自于Docker在应用和基础架构之间带来的美妙接口和抽象。开发者可以在不必知道底层架构情况下谈论基础架构,操作人员也不必花大量时间研究如何安装和管理软件。肯定有什么力量隐藏在看似简单的外表下使得大家生活简化,更加高效。

现实世界时残酷的,不要想当然认为采用一项新技术只会带来享受。过去几年经过一些项目的磨练,经历过奇怪的环境,我认为Docker也不例外。但是某一个经验一般可以直接应用到项目的下一阶段。要想从Docker获得功力,必须浸淫到实际项目中去磨练。

过去一年中,我全身心投入去教授我的关于Dokcer基础的书,Docker in Action。

我注意到几乎所有人开始学习Docker技术时都会纠结于如何创建开发环境,然后才能了解生态系统之内大家的关系。每个人开始都会认为使用Docker会使环境搭建变的简单,也不是完全不对,有很多“容器化”教程都涵盖了创建一个image和如何将某个工具打包到容器(Container)内,但是如何将开发环境Docker化是一个完全不同的事情。

作为一个踏坑先驱者,我可以分享一下我的经验。

我曾经是一个资深Java使用者,但这个分享的经验不是关于Java的,而是围绕着我使用Go和Node开发应用发生的。我有一定的Go开发经验,主动提高在这一领域的能力。进入一个不熟悉领域迅速上手碰到的主要问题就是如何获得正确的工作流,而且我还比较厌恶在笔记本上不断安装软件,这些都驱使我尝试用Docker做这些工作,或者有时候采用Vagrant。
1.png

我所参与的项目是用Go写一个标准的REST服务,基于gin,依赖Redis和NSQ的某些库和服务。也就是说需要import一些本地运行着的Redis和NSQ实例的库,更有趣的是我还使用了一些服务于NGINX的静态资源。

对门外汉来说,Go是一种编程语言,实际上还有一种命令行工具也叫“go”。从依赖型管理、编译、测试用例到其它各种任务都使用它。对Go项目来说,除了Git和一个好用的编辑器,剩下就是跟它打交道了。然而还是有一个问题,我不想在笔记本上安装Go,笔记本上我只想安装Git和Docker。这些问题限制了其他环境下的兼容性,并且对新手来说降低了门槛。

这个项目有运行时依赖,意味着此工具集需要为简单环境定义和编排而包括Docker Compose。 很多人会为此感到不适应,那么我们怎么办?开始创建一个Dockerfile或者docker-compose.yml?好吧,先让我告诉大家我是怎么办的,然后解释为什么这么做。
2.png

此案中,我希望我的本地包是完全自动的。我不喜欢手动逐条执行步骤,而且我的vim配置文件也很简单。我只想从“是否运行”层次控制运行环境。本地化开发环境目标被快速复制,不仅用于提高生产效率,而且用于共享Docker images。 我最终完成了Dockerfile,用来产生包含Go,Node,和我最经常使用的打包工具Gulp的images。 此Dockerfile没有嵌入代码,image也没有嵌入Gulpfile。相反的,在一个建立了的GOPATH(Go workspace的根路径)上定义了一个卷。

最终,我为此images设置了给gulp提供服务的entrypoint,设置默认命令来监控。输出images肯定不是我称为build artifact的东西,从这个意义上来讲,此环境唯一做的就是提供了一个运行实例,帮助我们判断是否代码运行。对我的场景来说,运行的非常棒。而我将“artifacts”用于称呼另外一个build。

下一步我用Compose定义本地开发环境。首先定义了在images中用到的所有Docker Hub 中定义的依赖服务,将他们连接到某一个“目标”服务。此服务引用了新Dockerfile从哪里生成,将本地源目录绑定到新image期望输出的挂载点,暴露一些可以测试的端口。然后,添加了一个服务,可以不断地向目标服务循环发起一系列集成测试。最终,我添加了NGINX服务,挂载了有很多配置文件和静态assets的卷。使用卷的好处在于重复使用配置文件和assets而不用重建image。
$ cat ./service/local.df
FROM golang:alpine
RUN apk --update add --no-cache git nodejs
RUN npm install --global gulp
ENV GOPATH=/go PATH=$PATH:/go/bin
VOLUME ["/go/src/github.com/.../myproj", "/go/pkg","/go/bin"]
WORKDIR /go/src/github.com/.../myproj
# Bring in dependencies in the image
RUN go get github.com/bitly/go-nsq && \
go get github.com/codegangsta/cli && \
go get github.com/gin-gonic/gin
CMD ["gulp"]
$ cat ./service/gulpfile.js
var gulp  = require('gulp');
var child = require('child_process');
var server = null;
gulp.task('default', ['watch']);
gulp.task('watch', function() {
gulp.watch('./**/*.go', ['fmt','build','spawn']);
});
gulp.task('fmt', function() {
return child.spawnSync('go', ['fmt']);
});
gulp.task('build', function() {
return child.spawnSync('go', ['install']);
});
gulp.task('spawn', function() {
if (server)
server.kill();
server = child.spawn('myproj');
server.stderr.on('data', function(data) {
process.stdout.write(data.toString());
});
server.stdout.on('data', function(data) {
process.stdout.write(data.toString());
});
});
$ cat docker-compose.yml
web:
image: nginx
volumes:
- ./web/assets:/var/www
- ./web/config:/etc/nginx/conf.d
integtest:
build: ./integ
links:
- service
service:
build: ./service
dockerfile: local.df
volumes:
- ./service/src/:/go/src/github.com/.../myproj
links:
- nsqd
- redis
nsqd:
image: nsqio/nsq
...
redis:
image: redis
... 

所有代码最终会在电脑上生成本地开发环境,当使用:
docker-compose up –d

时,会启动git clone,然后循环运行;不需要重建image或者重启容器。每当.go文件发生变化,Gulp就会重建,并且在运行的容器中重启我的服务。就这么简单。

创建此环境很简单吗?不尽然,但是确实实现了。难道不用容器,而在本地直接安装Go,Node,Gulp不是更简单吗?也许在这个场景是,但也只限于用Docker运行此依赖服务。我不喜欢这样。

我曾经要管理这些工具的不同版本,而产生了复杂的环境变量,到处生成artifacts。我不得不提醒同事们注意这些容易发生冲突的环境变量,他们太缺乏集中版本控制了。

也许你并不喜欢上面描述的环境,或者对项目有不同的需求。很好,确实是这样,本文并不是让所有工具都运行在Docker中,如果这样就说明并没考虑过要解决什么问题。

当我设计这个环境时,考虑过下面几个问题,顾虑,以及某些潜在答案。当开始Docker工作环境时,就会发现实际情况可能比自己的回答更糟糕。
当你考虑打包和环境时,最先考虑的因素是什么?
这个确实是最重要的问题。在此场景中,有几个选项。我可以使用go直接在容器内编程,看起来如下:
# get dependencies
$ docker run --rm -v "$(pwd)"/go/src/app golang:1.5 go get -d -v
# start the other services
# build and link
$ docker run --rm -v "$(pwd)":/go/src/app golang:1.5 go install \
github.com/allingeek/myproj
# run the program stand alone
$ docker run --rm -v "$(pwd)"/bin/myproj:/bin/myproj alpine myproj
# to iterate, make changes and repeat the last two steps 

其实这个示例中大部分bolierplate可以通过shell别名或者函数隐藏,感觉Go是安装在自己的设备中似的,还可以跟Go工作流联系,创建artifacts。这些特性对非服务项目有益处,但是对库和软件项目就不一定了。

假设你已经在使用Gulp、make、ant或者其他脚本,那么可以继续,并且使用Dokcer作为这些工具的目标。

另外一种方法,我可以通过使用Dockerbuild来定义和控制我的build,获得更多面向Docker的经验。代码如下:
$ cat Dockerfile
FROM golang:1.5-onbuild
# start the other services
# install dependencies, build, and link
$ docker build -t local/myproj .
# run the program
$ docker run --rm local/myprog
# to iterate, make changes and repeat the last two steps

使用Dokcer来控制build有若干好处。可以使用以前编译好的image,Dockerfilebuilds使用缓存方法,使得编译工作只重复最小的步骤(假设有一个很棒的Dockerfile)。最后,这些builds生成的images也可以跟其他开发者共享。

这个案例中,我使用golang资源库中的onbuildimage作为基础。其中包括一些很棒的下载依赖包逻辑。这个方法会生成可以方便用于其他非生产环境的Dockerimage。这个方法对于生产级别的image的问题在于,必须有步骤避免大image并且包括某些初始化脚本,用于启动和监控服务前验证状态。

有意思的是,Docker使用一系列脚本,Makefiles和Dockerfiles。build系统相对很健壮了,负责各种测试,linting等,以及各种操作系统和架构的artifacts。本场景中,容器是用来产生二进制的工具,然而是从一个本地build image中实现的。

扩充Docker build的选项,可以使用Compose来定义一整套开发环境。
$ cat Dockerfile
FROM golang:1.5-onbuild
$ cat docker-compose.yml
service:
build: .
links:
- redis
- nsq
redis:
image: redis
nsq:
image: nsqio/nsq
# install dependencies, build, link, launch dependency services, run
$ docker-compose up -d
# to iterate, make changes and then
$ docker-compose build && docker-compose up -d

Compose负责环境管理。如果觉得系统非常干净并不奇怪,Compose把所有事情都联系起来,优化卷管理,当images缺失时自动build,汇总日志输出。我之所以选这些开关是为了简化服务依赖,也因为它能生成我需要的artifacts。

这个示例是一个运行时容器,Compose或者Docker都有合适的工具做到这点。此场景中,也可能更需要一个分布式image,或者可能希望build可以为本机产生一个二进制文件。

如果期望获得想要的image,必须确保源码或者预编译库在build时候嵌入image中。build时候没有挂载卷,也即需要每次重复时都要重建image。

如果希望在容器内部产生某些artifacts,则需要引入挂载卷。使用Docker命令行或者Compose环境可以很容易实现。但是要注意,除非容器在运行,否则build并不工作,也就意味着不能只用dockerbuild。

汇总

目前没有Docker方式创建开发环境。Docker是一个可编排工具,不只是圣书。与其使用别人已有的dockerbuild系统,不如花一定时间学习此工具,明确自己的需求,然后创建适合自己的Docker环境。

记住,如果需要比Docker Fundamentals进阶书籍,那么Docker in Action很快就会出版。大家可以留意。
3.jpeg


原文链接:Development Environments with Docker(翻译:杨峰)

原文发布时间为:2016-01-18
本文作者:hokingyang
本文来自云栖社区合作伙伴DockerOne,了解相关信息可以关注DockerOne。
原文标题:用Docker创建开发环境
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
5月前
|
Java Shell Linux
入职必会-开发环境搭建49-Docker必会构建镜像
前面一直都是从Docker仓库中下载镜像然后使用。我们项目中可以通过Dockerfile构建自己的镜像。 Dockerfile是由一系列命令和参数构成的文本文件,Docker可以读取Dockerfile文件并根据Dockerfile文件的描述来构建镜像。Dockerfile文件内容一般分为4部分 ● 基础镜像信息 ● 维护者信息 ● 镜像操作指令 ● 容器启动时执行的指令
|
27天前
|
关系型数据库 MySQL Docker
docker环境下mysql镜像启动后权限更改问题的解决
在Docker环境下运行MySQL容器时,权限问题是一个常见的困扰。通过正确设置目录和文件的权限,可以确保MySQL容器顺利启动并正常运行。本文提供了多种解决方案,包括在主机上设置正确的权限、使用Dockerfile和Docker Compose进行配置、在容器启动后手动更改权限以及使用 `init`脚本自动更改权限。根据实际情况选择合适的方法,可以有效解决MySQL容器启动后的权限问题。希望本文对您在Docker环境下运行MySQL容器有所帮助。
68 1
|
2月前
|
网络安全 虚拟化 Docker
SSH后判断当前服务器是云主机、物理机、虚拟机、docker环境
结合上述方法,您可以对当前环境进行较为准确的判断。重要的是理解每种环境的特征,并通过系统的响应进行综合分析。如果在Docker容器内,通常会有明显的环境标志和受限的资源视图;而在云主机或虚拟机上,虽然它们也可能是虚拟化的,但通常提供更接近物理机的体验,且可通过硬件标识来识别虚拟化平台。物理机则直接反映硬件真实信息,较少有虚拟化痕迹。通过这些线索,您应该能够定位到您所处的环境类型。
38 2
|
2月前
|
存储 监控 Shell
docker的底层原理二:容器运行时环境
本文深入探讨了Docker容器运行时环境的关键技术,包括命名空间、控制组、联合文件系统、容器运行时以及分离的进程树,这些技术共同确保了容器的隔离性、资源控制和可移植性。
40 5
|
2月前
|
存储 Ubuntu JavaScript
如何使用Docker优化你的开发环境配置
如何使用Docker优化你的开发环境配置
|
2月前
|
jenkins Java 持续交付
Docker搭建jenkins环境
这篇文章详细介绍了如何利用Docker搭建Jenkins环境,包括拉取Jenkins镜像、配置端口映射及初始化设置的步骤。
170 0
Docker搭建jenkins环境
|
2月前
|
Docker 容器
利用Docker Compose优化开发环境的配置
在现代软件开发中,环境一致性至关重要。开发人员常需在不同机器间复制环境配置,而Docker Compose提供了一种简便有效的方法来定义和运行多容器Docker应用程序,确保开发、测试和生产环境一致,简化团队协作,提高开发效率。通过YAML文件配置服务、网络和卷,使用简单命令即可启动和停止服务。本文将介绍Docker Compose的核心优势、基本使用方法及高级功能,帮助你更好地管理和优化开发环境。
|
3月前
|
NoSQL 关系型数据库 Redis
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
mall在linux环境下的部署(基于Docker容器),docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongodb、minio详细教程,拉取镜像、运行容器
mall在linux环境下的部署(基于Docker容器),Docker安装mysql、redis、nginx、rabbitmq、elasticsearch、logstash、kibana、mongo
|
3月前
|
虚拟化 Docker Windows
window 10专业版部署docker环境
本文介绍了如何在Windows 10专业版上部署Docker环境,包括安装步骤、配置镜像加速以及可能遇到的错误处理。
144 2
window 10专业版部署docker环境
|
2月前
|
Linux 持续交付 iOS开发
docker的使用(环境特殊时可以考虑使用)
docker的使用(环境特殊时可以考虑使用)
15 0