如何使用 Docker Compose 来构建一套开发环境
作者:互联网
一个高效的团队需要拥有能简便建立开发环境的方法。下面这个案例会详细演示如何进行这个简便过程。
前面的博客文章提到过,你应该坚持使用一种简单可重复的方式,来为你的项目建立开发环境。
前面博客文章的链接:
http://danlebrero.com/2017/09/01/a-docker-compose-environment/#content
在这篇博客中,我们将深入案例的细节,这个案例来源于Akvo上的一个开源项目。
Akvo链接:
https://akvo.org/
恕我直言,拥有一种简便的建立开发环境的方法,是减少开源项目贡献者间冲突的重要的一方面。
AkvoLumen架构
我们将要研究的项目叫做AkvoLumen,这是一个“易于使用数据进行聚合、分析和发布的平台”(“easyto use data mashup, analysis and publishing platform”)。
AkvoLumen链接:
https://github.com/akvo/akvo-lumen
“easyto use data mashup, analysis and publishing platform”链接:
http://akvo.org/products/akvo-lumen/
AkvoLumen是一个用Javascript实现的独立页面的web应用程序(SPA),有一个Clojure后端,就像下面这样:
Keycloak是一个开源的单点登录的应用。它被其他的Akvo产品所共享。
Nginx服务于SPA,且向后端发送请求。
这里的后端就是后端(目测是clojure实现的所谓的后端)......
Keycloak链接:
http://www.keycloak.org/
Nginx链接:
https://www.nginx.com/
不使用dockercompose构建环境的步骤
我们管这种构建开发环境的步骤叫做“传统”的方式,即:安装Postgres,运行初始化脚本,下载构建工具,执行一系列命令等等。
但是在当下介绍的这个工具的使用下,构建步骤被划分为了三个文件目录:keycloak、backend和SPA。
Keycloak链接:
https://github.com/akvo/akvo-lumen/tree/21882586987927bc496e94c76e3faf6cbf965085/keycloak
Backend链接:
https://github.com/akvo/akvo-lumen/tree/21882586987927bc496e94c76e3faf6cbf965085/backend
SPA链接:
https://github.com/akvo/akvo-lumen/tree/21882586987927bc496e94c76e3faf6cbf965085/client
从个人来讲,当执行上述一系列命令的时候,我不清楚我需要本地运行KeyCloak以及npm依赖包中的某一个不会在我的开发环境中编译。我也不知道其原因。我也根本不想知道。
当然,存在更好的方式。
使用了dockercompose后的构建步骤
使用dockercompose后新的步骤在这里:
https://github.com/akvo/akvo-lumen/blob/d0c535166a8f276242d4de48f869c5d4ae931859/README.dev.md
可以总结为:
sudosh -c 'echo "127.0.0.1 t1.lumen.localhost t2.lumen.localhostauth.lumen.localhost" >> /etc/hosts'
docker-composeup -d && docker-compose logs -f --tail=10
第一步之所以需要,是因为Lumen是一个多租户的产品,且租户基于host配置。
第二步等同于docker-composeup,但是不会占据你的终端。
有趣的是,这次的步骤中不再有npm和mvn的安装命令了。
上述步骤完成后,下面的环境就运行起来了:
Dockercompose文件
完整的dockercompose文件可以在这里:
https://github.com/akvo/akvo-lumen/blob/develop/docker-compose.yml
Postgres和Keycloak
从观察Postgres镜像开始:
postgres:
build: postgres
ports:
-"5432:5432"
严格来说,我们不需要暴露Postgres的端口,但是在开发过程中能够使用UI工具查看数据库表是很方便的。
Dockerfile相当简单:
FROMpostgres:9.5
ADD./provision /docker-entrypoint-initdb.d/
Dockerfile链接:
https://github.com/akvo/akvo-lumen/blob/f72db9d8efe4900f8f8b3a42d5d89f4eded4b50e/postgres/Dockerfile
依照Postgresoffical image执行步骤,我们拷贝我们的initialsetup scripts,它会在容器第一次启动的时候运行。
Postgresoffical image链接:
https://hub.docker.com/_/postgres/
initialsetup scripts链接:
https://github.com/akvo/akvo-lumen/blob/f72db9d8efe4900f8f8b3a42d5d89f4eded4b50e/postgres/provision/setup.sh
这个脚本的功能只是创建一堆空的数据库。Lumen后端会创建需要的表并引用数据,这些作为数据库迁移逻辑的一部分。
Keycloakimage很简单,只是建立初始用户的集合,密码和认证,建立的方式采用Keycloakyway。
Keycloakimage链接:
https://github.com/akvo/akvo-lumen/blob/f72db9d8efe4900f8f8b3a42d5d89f4eded4b50e/keycloak/Dockerfile
Keycloakyway链接:
https://github.com/akvo/akvo-lumen/blob/f72db9d8efe4900f8f8b3a42d5d89f4eded4b50e/keycloak/akvo.json
LumenBackend
LumenBackend是一个Clojure服务。它的dockercompose配置如下:
backend:
build:
context: ./backend
dockerfile: Dockerfile-dev
volumes:
- ./backend:/app
- ~/.m2:/root/.m2
- ~/.lein:/root/.lein
links:
- keycloak:auth.lumen.localhost
ports:
- "47480:47480"
第一个有意思的地方是它使用了与生产配置不同的Dockerfile。
不同的Dockerfile链接:
https://github.com/akvo/akvo-lumen/blob/e0881e38cc1c4bf77692cbaa611427af3db288ab/backend/Dockerfile-dev
生产配置链接:
https://github.com/akvo/akvo-lumen/blob/e0881e38cc1c4bf77692cbaa611427af3db288ab/backend/Dockerfile
在开发过程中,我们需要构建工具,像我们例子中的Lein,而且我们需要好的REPL提供的快速的应答;但是在生产环境中,我们只希望能快速启动。
Lein链接:
https://leiningen.org/
REPL链接:
http://vvvvalvalval.github.io/posts/what-makes-a-good-repl.html
要注意到因为我们的构建工具已经成为了docker镜像的一部分,所以团队里的每个人都会运行Lein的同一个版本,并且是运行在相同的JVM和OS上。其他项目可能会使用不同版本的Lein,或者不同的工具,因为容器隔离了不同的项目。
每次我们对源码文件做了改动之后,我们不想都重新构建和重启我们的BackendDocker镜像,所以“volumes”的第一行(-./backend:/app)使得源码能在Docker容器中可用:任何源码的改动,会马上在容器中可见。
我们把第二个卷挂在到本地的专用的仓库目录上(-~/.m2:/root/.m2)。这在某种程度上会弄乱你的开发环境,因为删除掉Docker容器并不会同时去掉下载的依赖包,但是理论上你的本地的专用仓库目录只是一个缓存,所以不论任何时候它变得太大后,都可以删掉它,不会有什么影响。
如果你一点也不想影响到你的开发环境,那么你可以利用缓冲层(layers)的方法,只在项目文件有了改动后再去下载依赖包。
layers链接:
http://bitjudo.com/blog/2014/03/13/building-efficient-dockerfiles-node-dot-js/
最后的卷(-~/.lein:/root/.lein)使得Lein全局信息能在容器内看到。当你在意避免任何“它运行在我的机器上”的争议,可以使用它。
Lein全局信息链接:
https://jakemccrary.com/blog/2015/01/11/overview-of-my-leiningen-profiles-dot-clj/
即使Keycloak容器可以被Backend用主机名为“keycloak”所访问,我们仍然需要链接(-keycloak:auth.lumen.localhost),因为JWT验证需要这个单点登录的主机与client(浏览器)和后端相同。
最后,我们使REPL端口可用,这样就可以使用你最喜欢的IDE链接它了。你需要准确的指出lein的配置:repl-options来监听那个端口,以便可以接受来自任何主机的连接。
任何主机链接:
https://github.com/akvo/akvo-lumen/blob/e0881e38cc1c4bf77692cbaa611427af3db288ab/backend/project.clj#L74
LumenClient
LumenClient镜像是一个Nginx,运行着SPA服务,能传递请求到后端。
client:
build:
context: ./client
dockerfile: Dockerfile-dev
volumes:
- ./client:/lumen
ports:
- "3030:3030"
对于开发我们优先考虑的是快速的交互,所以在生产环境和开发环境中的Docker镜像是不同的。
在这个案例中,我们用webpackDev Server来替换Nginx,这个服务会再次编译SPA,任何时候我们对源码做出修改都会在你的浏览器上做及时的代码加载(hotcode reload)。挂载的卷会让源码为容器内部所见。
暴露的端口即为主应用的入口。
执行测试和其他的构建任务
在这个环境中,你不用安装任何的npm、lein或者其他的包,及执行任何这些工具带来的任务,只需要在Docker容器内运行他们即可。
例如,执行Backend测试:
docker-composeexec backend lein test
或者如果你准备运行几个命令,你可以启动一个bashshell:
docker-composeexec backend bash
贴士:如果你想保留bash的历史命令,只需要添加一个新的卷到DockerCompose文件中,挂在home目录。
关于启动有依赖的各个部分
DockerCompose没有提供(verylittle help)功能来支持各个容器的启动顺序。
Verylittle help链接:
https://docs.docker.com/compose/startup-order/
这部分工作有你来做,以确保对其他有依赖的容器能够等待足够长的时间来让被依赖的部分准备好,通常会设置一个等待时间的最大值。
对于这个工程,Backend同时依赖Keycloak和Postgres,但是Backend启动的时间比其他两个都长,所以我们这次就忽略掉这个问题。
如何处理其他工程的启动依赖问题可以借鉴下面两条:
检查数据库是否准备好,可以查询最后建立的表中的数据。
链接:
https://github.com/akvo/akvo-maps/blob/4aaaea55230b3eb7f704ecee73d89db115ccc3fa/end-to-end-tests/test/windshaft_test/core_test.clj#L11
检查Kafka是否准备好,可以列举所有的主题并查找到最后创建的一个。
链接:
https://github.com/dlebrero/kafka-streams-and-ktable-example/blob/master/our-service/src/our_service/util.clj#L41
环境的升级
需求产生的契机是AkvoLumen要添加新的特性,来提供一些交互的方面的功能
(interactivemaps)。
这意味着这个工程现在需要:
扩展PostGis安装到Postgres。
一个Windshaftnode.js服务器。
一个Redis数据库。
PostGis链接:
http://postgis.net/
Windshaft链接:
https://github.com/akvo/akvo-maps/tree/develop
Redis链接:
https://redis.io/
所以我们需要DockerCompose来完成下面的内容:
---a/postgres/Dockerfile
+++b/postgres/Dockerfile
-FROMpostgres:9.5
+FROMmdillon/postgis:9.6
---a/postgres/provision/helpers/create-extensions.sql
+++b/postgres/provision/helpers/create-extensions.sql
+CREATEEXTENSION IF NOT EXISTS postgis WITH SCHEMA public;
---a/docker-compose.yml
+++b/docker-compose.yml
+redis:
+ image: redis:3.2.9
+windshaft:
+ image: akvo/akvo-maps:2469ae0cb95ba090412f042fdfa8c7038273fe0e
+ environment:
+ - NODE_ENV=development
+ volumes:
+ - ./windshaft/config/dev:/config
然后执行一次docker-composedown; docker-compose up构建一下,整个团队都会享受这个构建过程。
这样岂不是很开心?
标签:akvo,Compose,lumen,github,构建,https,Docker,com,链接 来源: https://blog.51cto.com/u_15127621/2766857