最近在思考如何搭建这个个人开发环境。原因是我现在把所有的计算工作都搬到了私有云上,大约$20/月的样子。这样,我就不必在工作中花时间思考如何管理数千台AWS服务器。之前我花了两天时间尝试使用其他工具尝试搭建开发环境,到后面真的累死了。相比较而言,DockerCompose要简单易用很多,我很满意。于是,我把我的docker-compose经验分享给姐姐,她有点惊讶:“是啊!你也觉得DockerCompose牛逼吧!”嗯,我觉得我应该写一篇博文把过程记录下来,所以才有这篇文章你看。我们的目标:设置开发环境我目前正在编写RubyonRails服务(这是计算机“调试”游戏的后端)。在我的生产服务器上,我安装了:一个Nginx服务器一个Rails服务一个Go服务(使用Gotty代理一些SSH连接)一个Postgres数据库。只需安装Postgres和Ruby,小菜一碟)。但是我也想把匹配/proxy/*的请求发给Go服务,其他请求都发给Rails服务,所以需要用到Nginx。问题来了,在我的笔记本电脑上安装Nginx对我来说太麻烦了。是时候使用docker-compose了!docker-compose允许你运行一组Docker容器基本上DockerCompose所做的是允许你运行一组可以相互通信的Docker容器。您可以在一个名为docker-compose.yml的文件中配置所有容器。我在下面粘贴我为这项服务编写的docker-compose.yml文件(完整内容),因为我认为它非常干净直接!version:"3.3"services:db:image:postgresvolumes:-./tmp/db:/var/lib/postgresql/dataenvironment:POSTGRES_PASSWORD:password#yes我将密码设置为'password'go_server:#todo:use在某些时候一个较小的图像,我们不需要所有的ubuntu来运行一个静态的go二进制图像:ubuntu命令:/app/go_proxy/servervolumes:-.:/apprails_server:build:docker/rails命令:bash-c"rm-ftmp/pids/server.pid&&sourcesecrets.sh&&bundleexecrailss-p3000-b'0.0.0.0'"volumes:-.:/appweb:build:docker/nginxports:-"8777:80"#这会在我的笔记本电脑上公开端口8777此配置包含两个容器。对于前两个容器,我直接使用现有的图像(图像:postgres和图像:ubuntu)。对于后两个容器,我必须构建一个自定义容器镜像,其中build:docker/rails的作用是告诉DockerCompose它应该使用docker/rails/Dockerfile来构建一个自定义容器。我需要允许我的Rails服务访问一些API密钥和东西,所以我使用sourcesecrets.sh,它在环境变量中预先设置了一组秘密。如何启动所有服务:先“build”,然后“up”我总是运行docker-composebuild来构建容器,然后运行??docker-composeup来启动所有服务。您可以在yaml文件中设置depends_on以更好地控制启动容器。但是对于我的这些服务,启动顺序并不重要,所以我没有设置它。网络互通也很简单容器之间的互通也是一件很重要的事情。DockerCompose让这一切变得超级简单!假设我有一个Rails服务在端口3000上名为rails_server的容器中运行,因此我可以通过http://rails_server:3000访问它。就这么简单!以下代码片段取自我的Nginx配置文件,根据我的使用需要配置(我去掉了很多proxy_set_headers行,让它看起来更清晰):location~/proxy.*{proxy_passhttp://go_server:8080;}location@app{proxy_passhttp://rails_server:3000;或者,你可以参考下面的代码片段,它截取了我的Rails项目的数据库配置,这里我使用了数据库容器的名字(db):development:<<:*defaultdatabase:myproject_developmenthost:db#<------会“神奇地”解析为数据库容器的IP地址看起来Docker正在我的计算机上运行DNS服务来解析这些名称。下面是一些DNS查询记录,我们可以看到每个容器都有自己的IP地址:$dig+short@127.0.0.11rails_server172.18.0.2$dig+short@127.0.0.11db172.18.0.3$dig+short@127.0.0.11web172.18.0.4$dig+short@127.0.0.11go_server172.18.0.5谁在运行这个DNS服务?我(稍微)研究了这个DNS服务是如何设置的。下面的命令都是在容器外执行的,因为我容器里安装的网络工具不多。第1步:使用psaux|greppuma获取Rails服务的进程ID。找到了,是1837916!简单~第2步:找到运行在与1837916相同的网络命名空间中的UDP服务。我使用nsenter在puma进程的网络命名空间中运行netstat(理论上,我猜你也可以使用netstat-tupn来只显示UDP服务,但此时,我的手指只是习惯于输入netstat-tulpn).$sudonsenter-n-t1837916netstat-tulpn活动互联网连接(仅限服务器)ProtoRecv-QSend-QLocalAddressForeignAddressStatePID/Programnametcp00127.0.0.11:328470.0.0.0:*LISTEN1333/dockerdtcp000.0.0.0:30000.0.0.0:*LISTEN1837916/puma4.3.7udp00127.0.0.11:594260.0.0.0:*1333/dockerd可以看到里面运行了UDP服务在由dockerd运行的端口59426上!也许这是我们正在寻找的DNS服务?第3步:确定它是否是我们正在寻找的DNS服务我们可以使用dig工具向它发送DNS查询:$sudonsenter-n-t1837916dig+short@127.0.0.1159426rails_server172.18。0.2奇怪,当我们运行dig时,为什么DNS查询没有发送到端口59426,而是发送到端口53?到底是怎么回事?第四步:iptables对于类似“这个服务好像运行在X端口,但我访问的是Y端口,这是怎么回事?”这样的问题。我的第一个想法是“一定是iptables在工作”。因此,我在容器运行的网络命令空间中执行了iptables-save。果然,真相大白了:$sudonsenter-n-t1837916iptables-save....编辑了一堆输出....-ADOCKER_POSTROUTING-s127.0.0.11/32-pudp-mudp--sport59426-jSNAT--to-source:53COMMIT在输出中有一个iptables规则,将流量发送到端口53发送到59426。哈哈,多么有趣!数据库文件存储在一个临时目录中,这样做的好处是我可以直接挂载Postgres容器的数据目录./tmp/db而无需在笔记本电脑上管理Postgres环境。我喜欢这种方法,因为我真的不想一个人在笔记本电脑上管理Postgres环境(而且我真的不知道如何配置Postgres)。另外,出于习惯,我比较喜欢把开发环境的数据库和代码放在同一个目录下。只需一行命令,我就可以访问Rails控制台管理Ruby版本总是有点棘手,即使我暂时让它工作,我也总是有点担心我会搞砸我的Ruby环境和得修十年(夸张)。在搭建好这个开发环境(使用DockerCompose)之后,如果我需要访问Railsconsole控制台(一个加载我所有Rails代码的交互式环境),我只需要运行一行代码:$docker-composeexecrails_serverrails控制台通过Spring预加载器在进程597中运行加载开发环境(Rails6.0.3.4)irb(main):001:0>耶!小问题:Railsconsolehistorylost我遇到了一个问题:Railsconsolehistorylost因为我不断重启它。不过,我也找到了一个相当简单的解决方案(呵呵):我在容器中添加了一个/root/.irbrc文件,可以将IRB历史文件的保存位置指向一个没有重启影响的容器。一行代码就够了:IRB.conf[:HISTORY_FILE]="/app/tmp/irb_history"还不知道在生产环境中表现如何目前这个项目的生产环境搭建进度还在在“我做了一个DigitalOceandroplet(LCCT译注:一个Linux虚拟机服务),并手动编辑了很多文件”阶段。嗯...我确定我会在生产环境中使用docker-compose运行它。我猜这是可行的,因为该服务可能最多由两个用户使用,如果我愿意,我可以忍受它在部署期间60秒不可用。不过话又说回来,出问题的往往是自己没有想到的地方。Twitter用户提供了一些在生产中使用docker-compose的注意事项:docker-composeup只会重启那些需要重启的容器,这样重启会更快。有一个小的Bash脚本wait-for-it,您可以使用它来一直等待一个容器,直到另一个容器的服务可用为止。您可以准备两个docker-compose.yaml文件:用于开发的docker-compose.yaml和用于生产的docker-compose-prod.yaml。我想我会为Nginx指定不同的端口:8999用于开发,80用于生产。似乎有一个共识,如果你的项目是在一台电脑上运行的小型网站,那么docker-compose在生产中是没有问题的。有人提出,如果你愿意搭建一个复杂的生产环境,DockerSwarm可能是更好的选择,但我还没有尝试过(当然,如果你要这么说,为什么不使用Kubernetes?Docker的观点Compose是因为它超级简单,而Kubernetes肯定不是:))。Docker貌似还有一个功能,可以自动将你用docker-compose搭建的环境推送到ElasticContainerService(ESC),听起来很酷,但我还没有尝试过。有没有docker-compose不适用的场景?我听说docker-compose在以下场景中表现不佳:当你有很多微服务时(最好自己构建它)当你尝试从大型数据库导入数据时(比如在每个人的笔记本电脑上存储数百GB的数据)当你在Mac上运行Docker时。我听说Docker在macOS上比在Linux上慢得多(我猜是因为它需要做额外的虚拟化)。我没有Mac,所以我还没有遇到这个问题。这里的所有都是它的!在此之前,我花了一整天的时间试图用Puppet配置一个Vagrant虚拟机,然后在这个虚拟机中配置开发环境。结果发现虚拟机启动有点慢,而且,我也不喜欢写Puppet配置(哈哈,没想到)。幸运的是,我尝试了DockerCompose,它非常简单,而且可以立即运行!
