Rails开启Docker微服务之旅,时隔半年

今天我们发布了Docker CE 17.06,它包含了诸多新特性、优化和bug修复。我们在四月份的DockeCon上公布了Moby项目,Docker CE 17.06是第一个完全构建在它基础上的Docker版本。变更日志中能看到完整的更新列表,我们来看看它的一些新特性。我们也为这篇文章制作了一个视频版本。

我们谈论关于架构的话题,总离不开微服务,而微服务又会引向容器和Docker。它们都是能简化庞大而复杂应用的构建过程。本周希云和大家分享,如何在Giant Swarm上运行Docker化的Ruby on Rails应用。

Docker 1.13 在 2017 年 1 月 18 日发布了。从 2016 年 7 月 29 日发布 1.12 以来,已经过去 5 个多月了,对于活跃的 Docker 社区来说,已经很久了,让我们看看都 1.13 都新增了什么内容吧。1.13 有一千四百多个 issue/pull request,五千多个 commits,是 Docker 历史上最高的发布版本。这并不是一个简单的小版本变化,里面有大量的更新。Top 10 新增功能1、正式支持服务栈: docker stack2、正式支持插件:docker plugin3、添加在 Swarm 集群环境下对密码、密钥管理的 secret 管理服务:docker secret4、增加 docker system 命令5、可以直接使用 docker-compose.yml 进行服务部署6、添加 docker service 滚动升级出故障后回滚的功能7、增加强制再发布选项 docker service update –force8、允许 docker service create 映射宿主端口,而不是边界负载均衡网络端口9、允许 docker run 连入指定的 swarm mode 的 overlay 网络10、解决中国 GFW 墙掉 docker-engine apt/yum源的问题新增功能解读Docker 镜像构建从已有镜像取得缓存压扁 (squash) 镜像构建镜像时支持用 –network 指定网络开始允许 docker build 中定义 Dockerfile 未使用的参数安装解决 GFW 影响 Docker 安装问题增加更多的系统支持网络允许 docker run 连入指定的 swarm mode 的网络允许 docker service create 映射宿主端口,而不是边界负载均衡网络端口iptables 的转发规则将默认拒绝在 docker network inspect 里显示连入的节点允许 service VIP 可以被 ping插件插件功能正式启用命令行checkpoint 功能准备工作创建 Checkpoint 及恢复docker stats 终于可以显示容器名了给 docker ps 增加 is-task 过滤器再也不会出现客户端和服务端不同版本导致的错误了docker inspect 将可以查看任何 docker 对象运行时不在分别构建试验可执行文件,直接使用 –experimental 参数在 overlay2 存储驱动使用于 xfs 时可以添加磁盘配额增加 docker system 命令提升 overlay2 的优先级docker exec -t 自动添加 TERM 环境变量Windows 内置的运行 Windows 程序的 Docker on Windows 的改进Swarm Mode正式支持 docker stack添加 secret 管理添加负载均衡和DNS记录对新增的健康检查的支持添加滚动升级回滚的功能补充了一些 docker service create 所缺失的参数添加命令 docker service logs 以查看服务日志增加强制再发布选项 docker service update –force参考链接:时隔半年,Docker 再发重大版本 1.13!Docker 1.13 新增功能

多阶段构建

图片 1

17.06 CE最大的特性是它的多阶段构建,它最初在四月的DockerCon被公布,现在已经达到了稳定版本。多阶段构建能从一个Dockerfile中构建出更加简洁、体积更小的Docker镜像。

对于微服务,没有一个像apt-get这样的工具,不禁让人问:“我怎样安装这个新东西?” 答案是:“你安装不了微服务。” 更具体来说是,“你不可能一下子就用上它。”

多阶段构建通过构建过渡镜像并产生输出。这样就能在一个过渡镜像中编译代码,在最终的镜像中只使用它的输出。例如,Java开发者通常使用Apache Maven来构建应用,但是运行应用却不需要Maven。多阶段构建能大幅度的减小镜像的体积:

“一个系统的架构是最难改变的”,“微服务”这个新事物也不是一颗银弹,即使在今天也没有人能轻易就重构一个复杂的系统,尤其是对于像用了Rails框架构造出来的庞大系统。

REPOSITORY TAG IMAGE ID CREATED SIZEmaven latest 66091267e43d 2 weeks ago 620MBjava 8-jdk-alpine 3fd9dd82815c 3 months ago

Docker承诺的一个特性是:“减少开发、测试和生产环境之间的差异” ,然而,在生产环境运行Docker并不简单,所以我们将研究Giant Swarm这样的工具是如何简化这个部署过程的。

来看一个能创建店铺首页的AtSea示例应用:AtSea使用了多阶段构建,并包含两个过渡阶段:用一个node.js基础镜像构建ReactJS应用,用一个Maven基础镜像将Sprint Boot应用编译成单个镜像。

刚开始,我会把一个简单的应用Docker化,这个应用是一个NoSQL数据库ArangoDb的ODM。你能在Github上找到这个应用,如果你要自己尝试这个例子,你的机器上需要装好Docker,Ruby 2版本以上,还有一个用于部署的Giant Swarm账号,不需要单独安装数据库,将会在本地使用一个容器作为数据库。

FROM node:latest AS storefrontWORKDIR /usr/src/atsea/app/react-appCOPY react-app/package.json .RUN npm installCOPY . /usr/src/atsea/appRUN npm run build

☞各个击破

FROM maven:latest AS appserverWORKDIR /usr/src/atseaCOPY pom.xml .RUN mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolveCOPY . .RUN mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests

在我们制作Docker容器之前,先看一下这个应用的本身,以及我们将如何实现Docker化,我们手头上的应用是一个普通的Web工程:

FROM java:8-jdk-alpineWORKDIR /staticCOPY from=storefront /usr/src/atsea/app/react-app/build/ .WORKDIR /appCOPY from=appserver /usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar .ENTRYPOINT [java, -jar, /app/AtSea-0.0.1-SNAPSHOT.jar]CMD [spring.profiles.active=postgres]

一个前端

最终的镜像大小只有209M,其中不包含Maven或node.js。还有其他的一些构建器方面的优化,包括在FROM指令中使用构建时参数。

通过OAuth2使用GitHub登录

日志和指标

调用外部接口(GitHub)

指标

后台长期运行的任务

目前通过一个daemon的API端点提供日志的支持。可以将docker的/metrics端点暴露给插件:

主数据库

$ docker plugin install grant-all-permissions cpuguy83/docker-metrics-plugin-test:latest$ curl

任务队列

这个插件仅用于示范。它在主机的网络上运行了一个反向代理,能将请求转发给插件中的本地的指标套接字。在真实场景中,可能会将收集的指标数据发送给外部的服务,或者使它可以被一个服务如Prometheus访问并收集。注意尽管指标插件在非实验性的daemon中可以使用,指标标签仍应该被看作是实验性的,可能在Docker未来的版本中发生改动。

可以把这些功能都放到一个容器中,但是这样会得不偿失。例如,会失去应用和数据库分离带来的可扩展性。遵循在每个容器只运行一个进程的原则,把应用分成5个容器:

日志驱动插件

Nginx会作为前端代理服务器,在我们的例子中它会提供静态资源,在更复杂的应用中,它可能会作为访问控制或者为后端服务提供负载均衡。

添加了对日志驱动器插件的支持。

第二层是Rails应用,它会运行在一个简单的web服务中,这里用的是Puma。

Service日志

Sidekiq也会运行在一个独立的容器中,如果你有一个以上的队列,需要为每一个队列创建一个容器。

docker service logs从实验版本改进到了稳定版,你可以轻松地获取Swarm上运行的一个服务的完整日志。同时也添加了服务中单个任务日志的端点。

一个安装了Redis作为任务队列的容器。

网络

一个安装了ArangoDB作为主数据库的容器。

Service中节点本地的网络支持

以下这幅图帮助我们去理解这个架构,以及组件之间的通讯:

Docker支持很多的网络选择。在Docker 17.06 CE中,可以将服务追加到节点本地的网络。这包括如Host、Macvlan,IPVlan,Bridge和本地作用域的插件。例如对于一个Macvlan网络,可以在worker节点上创建一个节点特定的网络配置,然后在manager节点上创建一个加入了这些配置的网络:

图片 2

[Wrk-node1]$ docker network create —config-only —subnet=10.1.0.0/16 local-config[Wrk-node2]$ docker network create —config-only —subnet=10.2.0.0/16 local-config[Mgr-node2]$ docker network create —scope=swarm —config-from=local-config -d macvlan mynet[Mgr-node2]$ docker service create —network=mynet my_new_service

把Sidekiq放到一个单独的容器运行和微服务架构还相差很远,但这已经使这个应用有不错的隔离性,使每个服务都在各自的工作进程中。

Swarm Mode

☞ 各个组件

Swarm mode添加了很多的新特性,这里是一部分:配置对象

我们已经指定了各个容器的功能,现在就要动手创建它们了。Docker容器是基于Dockerfile构建的,这个文件描述了每一个构建步骤。前面说到我们需要五个容器,对应地需要五个Dockerfile。

swarm mode的一个新的配置对象,允许安全地像传递密码一样传递配置信息:

不过幸运的是,这些容器可以共享同一个镜像。我们不需要额外的定制镜像就可以直接用了,你能找到各种应用的镜像,当然种类最多的还是数据库。

$ echo This is a config | docker config create test_config $ docker service create name=my-srv —config=test_config …$ docker exec -it 37d7cfdff6d5 cat test_configThis is a config

☞ 数据库

证书轮换的优化

我们将使用官方的Redis和ArangoDB镜像,通过以下命令运行:

Swarm mode中内置的公钥基础设施系统使得可以安全地部署一个容器调度系统。Swarmz中的节点使用双边TLS来认证、授权和加密他们之间,以及和Swarm其他节点的通讯。因为这些都依赖于证书,所以经常轮换很重要。自从Swarm mode伴随Docker 1.12发布以来,已经能以一小时一次的频次来安排证书轮换。有了Docker CE 17.06,添加了立即强制证书轮换的一次性操作:

# 会运行Redis并把端口暴露到宿主机

docker swarm ca rotate

$ docker run --name redis-dredis

Swarm Mode事件

# 会运行ArangoDB并把端口暴露到宿主机

docker events可以用来从Docker中获取实时的信息。在编写自动化和监控应用时很有用。但是直到Dcoker CE 17.06,都不支持swarm mode的事件。现在docker events将会返回服务、节点、网络和和secret的信息。

$ docker run --name arangodb-darangodb/arangodb

专用的数据路线

这两个命令会从官方的镜像库获取到镜像,以后台方式(-d)启动,并且分配了一个名称(—name),它们都会分配到一个卷还有默认的端口。对于ArangoDB,至少应该为生产环境配置权限认证的设置。

docker init中添加了新的datapath-addr标签,可以把swarm mode的管理任务和应用传递的数据隔离开来。这能把集群从IO贪婪的应用中拯救出来。如你用这种方式初始化集群:

☞ Nginx 前端代理

docker swarm init —advertise-addr=eth0 —datapath-addr=eth1

记住,Docker容器应该是被看作不可变的,改变应该发生在构建时而不是运行时,这个要在每次更改时重新构建镜像。对于Nginx前端,需要这样一个更改:指定一个配置文件来代理Rails应用。因为Docker镜像每次构建时都使用一个已经存在的镜像,我们使用了官方的Nginx镜像作为基础镜像:

集群管理的流量将会通过eth0,而服务将会彼此通过eth1来通讯。

FROM nginx

桌面版本

RUN rm -rf /usr/share/nginx/html

Docker for Mac和Docker for Windows添加了三个新特性:

COPY public /usr/share/nginx/html

GUI中新增了重置Docker数据而不需要丢失所有的设置

COPY config/deploy/nginx.conf /etc/nginx/conf.d/default.conf

现在重置数据不会丢失设置。

Dockerfile的开头总是FROM语句,它告诉Docker要继承于哪个基础镜像。另外我们只需要COPY这个public目录和配置到镜像中,正如我前边所说,容器应该被看作不可变的,每当需要改变这些资源时,应该创建一个新的镜像。Nginx的配置如下:

为主机添加一个实验性的DNS

server {    listen80;    server_name  localhost;    location / {        root      /usr/share/nginx/html;        index     index.html index.htm;        try_files$uri/index.html$uri.html$uri@upstream;    }    location @upstream {        proxy_pass ;    }}

如果在Docker for Mac或者Docker for Windows上运行容器,并且想访问其他的容器,你可以使用一个新的实验性主机:docker.for.mac.localhost和docker.for.win.lcoalhost来访问开放的端口。例如:

还有一件事应该提一下:这个rails-app主机名从哪里来的呢?Docker将提供两种方法去连接容器(我们会解释这点),一串环境变量和/etc/hosts文件。在这个例子中,我们使用了/etc/hosts。

$ docker run -d -it -p 80:80 nginx9a41b199e86cc4730f470aba1091530cfdc26d6f956964492b0d0b06a0ab9046$ docker run -it curlubunturoot@85664afff468:/# curl docker.for.mac.localhost:80!DOCTYPE htmlhtmlheadtitleWelcome to nginx!/titlestylebody { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif;}/style/headbodyh1Welcome to nginx!/h1pIf you see this page, the nginx web server is successfully installed andworking. Further configuration is required./p

☞ Rails应用和Sidekiq Worker

pFor online documentation and support please refer toa href= rel=external nofollow target=_blanknginx.org/a.br/Commercial support is available ata href= rel=external nofollow target=_blanknginx.com/a./p

现在添加Nginx代理的后端服务:Rails应用。官方有个Rails Dockerfile,但不会用它,因为它会安装一些我们不需要的组件,更糟的是它安装bundle的时候没用—deployment参数。尽管如此,还是用它作为指引:

pemThank you for using nginx./em/p/body/htmlroot@85664afff468:/#

FROM ruby:2.1.5

用来认证注册表访问的登录证书

# 如果Gemfile被修改过则抛出错误

可以在Docker for Mac和Docker for Windows中添加证书来访问注册表,而不仅仅是使用用户名和密码。这能让访问Docker Trusted Registery和开源的注册表和其他任何的注册应用快速而简单。

RUN bundle config --global frozen1

云版本

RUN mkdir -p /usr/src/appWORKDIR /usr/src/appCOPY Gemfile /usr/src/app/COPY Gemfile.lock /usr/src/app/RUN bundle install --deploymentCOPY . /usr/src/app/ENV RAILS_ENV productionEXPOSE8080CMD ["/usr/src/app/bin/rails","server","-p","8080"]

Cloudstor卷插件可以在Docker for AWS和Docker for Azure中使用。在Docker for AWS中,对于持久化卷的支持现在是稳定的了)。并且我们能跨可用区支持EBS卷。

不用Docker我们可以部署类似Capistrano之类的应用,而现在,需要在远程服务器上操作的步骤,我们可以在构建Docker镜像时就完成了。诸如安装gem包和复制代码到服务器,通过这样,有了一个在任何地方任何时间都能启动的容器,而且它的状态和我们最初构建它的时候一模一样。

对于Docker for Azure来说,我们吓死你在支持不部署到Azure Gov. 通过Cloustor支持持久性卷现在是稳定的,可以通缉你广化寺用在Azure Public和Azure Gov中。

Sidekiq Worker的Dockerfile基本和上边的一样,比直接复制这个Dockerfile更好的方式是,定义一个公用的基础镜像,用于构建Rails应用和Sidekiq Worker。

废弃

☞ 构建容器

在dockerd的命令行中,api-enable-core标识已经被废弃很久了,而采用api-cors-header。对于api-enable-cors还没有完全去掉。

Docker期待的是一个Dockerfile而例子中已经有三个了,把每个Dockerfile加上了一个有意义的后缀,但是使用Docker命令时会重命名这些文件。如果有一个工具可以用来实现这个,那就是Rake:

Ubuntu 12.04 precise pangolin已经完结了生命周期,所以它现在不是Docker支持的操作系统。Ubuntu的后期版本仍旧是支持的。

namespace :dockerdotask :build => ['docker:build:web','docker:build:app','docker:build:worker','assets:clobber']  namespace :builddotask :web => ['assets:precompile','assets:clean']dosh'ln -snf Dockerfile.web Dockerfile'sh'sudo docker build -t "registry.giantswarm.io/yoshida/gh-recommender-web" .'sh'rm -f Dockerfile'end    task :app => ['assets:precompile','assets:clean']dosh'ln -snf Dockerfile.app Dockerfile'sh'sudo docker build -t "registry.giantswarm.io/yoshida/gh-recommender-app" .'sh'rm -f Dockerfile'end    task :workerdosh'ln -snf Dockerfile.worker Dockerfile'sh'sudo docker build -t "registry.giantswarm.io/yoshida/gh-recommender-worker" .'sh'rm -f Dockerfile'end  endend

译文转载公众号:Docker;原文链接:-docker-17-06-community-edition-ce/

web和app的构建都需要使用RAILS_ENV=production,因为要这些文件都是给生产环境而不是开发环境生成的。-t参数会指定目标镜像的仓库名称,这对下一步把镜像推到云上是必须的。

☞ 转移到云上

目前为止我们已经有了一个完整的本地环境,这很好,但是如果想真正要的是对外的环境,至少还要几步。

任何人都可以配置服务器来运行基于Docker的应用。但是这样的话就要面对各种挑战:把容器链接在一起,扩展容器,管理跨节点的容器,还有更多。幸运的是,你可以直接使用Giant Swarm,这些它都帮你考虑了。首先你需要获取一个邀请码,你注册之后就可以使用swarm命令行工具去配置你本地的机器了。第一件要做的事是创建一个swarm.json:

{  "name":"github_recommender",  "components":{    "arangodb":{      "image":"arangodb/arangodb",      "ports":["8529/tcp"],      "volumes":[        {          "path":"/data",          "size":"5 GB"}      ]},    "nginx":{      "image":"registry.giantswarm.io/yoshida/gh-recommender-web",      "ports":["80/tcp"],      "domains":{        "80/tcp":["gh-recommender.gigantic.io"]},      "links":[        {          "component":"rails-app",          "target_port":"8080/tcp"}      ]},    "rails-app":{      "image":"registry.giantswarm.io/yoshida/gh-recommender-app",      "ports":["8080/tcp"],      "env":["RAILS_ENV=production","SECRET_KEY_BASE=$secret_key_base","REDIS_URL=redis://redis:6379","GITHUB_KEY=$github_key","GITHUB_SECRET=$github_secret"],      "links":[        {          "component":"arangodb",          "target_port":"8529/tcp"},        {          "component":"redis",          "target_port":"6379/tcp"}      ]},    "redis":{      "image":"redis",      "ports":["6379/tcp"]},    "sidekiq-worker":{      "image":"registry.giantswarm.io/yoshida/gh-recommender-worker",      "env":["RAILS_ENV=production","SECRET_KEY_BASE=$secret_key_base","REDIS_URL=redis://redis:6379"],      "links":[        {          "component":"arangodb",          "target_port":"8529/tcp"},        {          "component":"redis",          "target_port":"6379/tcp"}      ]}}}

这里定义了整个应用和组件之间的关联关系,回想一下Nginx的配置,我们使用了

如果不想在swarm.json中放置敏感信息(例如Github OAuth2的token),可以单独在一个swarmvars.json文件中定义这些变量:

{"GIANT_SWARM_USER/dev": {"github_key":"GITHUB_KEY","github_secret":"GITHUB_SECRET","secret_key_base":"SECRET_KEY_BASE"}}

可以使用例如$github_key关联这些变量到swarm.json,当应用在Giant Swarm上运行时,各个容器会使用适当的—link和—env选项。为了使所有服务都能从外部访问,我们需要指定域名到至少一个组件,Nginx是我们的入口,所以把域名指定到它上。

在启动应用之前,首先需要上传镜像到Giant Swarm的镜像库上(当然你也可以推到Docker Hub上,但可能你不想你的镜像能被公开访问):

$ docker push registry.giantswarm.io/yoshida/gh-recommender-web

$ docker push registry.giantswarm.io/yoshida/gh-recommender-app

$ docker push registry.giantswarm.io/yoshida/gh-recommender-worker

网络状况会直接影响这个上传过程,一旦上传完成,就可以用这个命令启动所有容器:

$ swarm up

这个命令会从仓库中获取所有需要用到的镜像,然后以适合的参数启动各个容器,收集所有容器的日志,并且在http://gh-recommender.gigantic.io下部署好了应用。整个过程异常简洁。

如果已经到了这步,恭喜你!

☞ 扩容

现在我们为每个组件都使用了一个容器,当你的应用吸引了更多的用户,或者突然发生了不可预见的事件需要更加多的资源。传统的做法是添加更多的服务器,需要一系列的人工操作:启动机器,搭建好环境并且添加节点到负载均衡中。使用Giant Swarm的话,添加一个实例非常简单:

$ swarm scaleup github_recommender/gh-recommender/rails-app

这样减轻了很多技术负担,但是它并不能使你的应用魔法般地就支持水平扩展,当它在数据库应用上就更加复杂了,你还需要研究怎样使应用支持扩展。但是这样至少你可以专注于这块,而不需要担心基础设施的细节了。

☞ 结论

文中谈及的就是这些,在这主题下,还有更多的东西可以讨论和学习。分享的目的是希望起码可以带大家入门,如果想走得更远,这里有几点建议主题,是文中没有提及但密切关联的:

容器能在本地开发环境使用,但本文并没有涉及如何实现。

无论在本地或者生产环境下,调试容器都是一个比较大的问题。正如它的其它方面,这个也没有银弹,可能也永远不会有。这也是需要注意的地方。

在Docker世界中,安全也是一个大问题,使用Giant Swarm会有所帮助,我们需要熟悉容器和Docker可能带来的安全性问题。这里说的不是安全漏洞,而是与传统部署方式之间的不同,例如像安装或管理虚拟机那样。

此外,希云强烈建议大家自己打包镜像,不要依赖于公共镜像库。否则最终你会需要很多不同的镜像。例如,例子中的五个容器就需要三个不同的Linux分发版。

尝试在每个容器中只启动一个进程,虽然这是Docker官方的建议,但无疑这是有争议的,是否必须要这样做,应该具体问题具体分析,根据实际情况做出决定。

如了解更多Docker相关知识,请观看培训视频:https://csphere.cn/training!

如需要Docker相关产品,请访问希云官网首页:https://csphere.cn!

cSphere1.0版本已发布,正式商用!欢迎咨询 400-686-1560

图片 3

图片 4

本文由澳门威斯尼人平台登录发布于服务器&运维,转载请注明出处:Rails开启Docker微服务之旅,时隔半年

相关阅读