【后端开发一体化解决方案】利用Docker、Rails、Circle CI和Terraform进行基础架构的代码环境搭建及自动容器部署至ECS【上篇】
只要有流行的技术,我们就想要使用。
大家早上好。又到了一年一度的感觉如雪花般飞舞的季节。大家过得如何呢?是否和现实环境相处融洽?我与现实环境的关系正在日益紧张。没关系,毕竟人生始终都是实际的考验(?)。
现在,在工程师们之间的以工程师为目标的工程师论中,已经不再
-
- (主にサーバーサイドの)アプリをDockerを使って環境構築して
terraformでインフラの構成をコード化して
(Kubernetesなどのコンテナオーケストレーションツールを用いて)なるべく開発環境との差異が無いままコンテナデプロイをして
CircleCIでテストからデプロイまで自動化
作为服务器端工程师,经常会看到类似的意见,说如果不知道这些方面的要求,就很难继续工作下去了。
观点将在另一个时机讨论。(假设如此),对于初学者有意向成为服务器端工程师的同学们来说,以下这些
-
- Docker
-
- CircleCI
-
- Terraform
- コンテナデプロイ(今回はECS)
我认为以教程的形式迅速学习和整理自己的理解可能是最快的方式!你觉得呢?换句话说,通过这篇文章,你能接触到目前流行的服务器端技术的各个方面!我对它的广度感到有点后悔。
好吧,那我就写吧!可能会再次变得有点长…
过去的长篇文章
【针对初学者】Rails “关联” 教程过于详细【无论如何】【完美理解】
【针对初学者】使用Heroku确保可以成功部署Rails应用程序的方法【决定版】
明天(12/5)单独演唱会,如果方便的话请来参加
音乐视频
预约请点击这里
目录
阅读本文的前提条件
-
- gitとgithubを使った事がある
-
- Dockerをなんとなく理解している
-
- Railsも触ったことある(触ったことなくてもできるかも)
- AWSのコンソールでEC2やVPCを立てた事がある
当然,我也非常鼓励其他人尝试挑战。理解可以事后补救。编程是靠实践来记忆的。
能掌握的技能或能力
使用Docker可以构建Rails应用程序,并在推送时通过CircleCI自动运行,并能够自动部署到之前通过Terraform构建的AWS环境(ECS)中!
我希望你重视一件事
-
- DBはsqliteです。RDSとつないでません。
-
- Webサーバもpumaで兼用してます。nginxなどのWebサーバーとつないでません
- =>これらはまた次回やろうと思います!
##我的环境
-
- Ruby 2.6.3
-
- Rails 6.0.1
-
- Docker 19.03.4
-
- Git 2.14.1
- Terraform 0.12.8
让我们全面了解这个问题
将各种工具和服务相互连接起来,需要非常重视对整体情况的把握。
首先,让我们确认整体流程。按照时间顺序列举如下:
应用程序自动部署到容器的流程
0. 使用Terraform定义AWS上的资源
1. 将Rails应用推送到Github
2. 在CircleCI上开始CI
3. 在CI中构建Docker镜像
4. 将Docker镜像推送到ECR
5. 更新ECS的任务定义
6. 在CI中进行数据迁移
7. 应用程序部署完成!
用图像来表示的话,下面是:

大致上解释每个工具的作用
基于云计算的基础设施即代码工具
基础设施定义工具。它可以将云上的资源生成并操作为定义文件的状态。将在屏幕上点点点操作的基础设施转化为代码。
####轨道交通系统
我想要部署的应用本身。
####Github
GitHub 是一个面向开源及私有软件项目的托管平台,能够帮助开发者进行版本控制、协作和代码管理。
版本控制工具。
就像是将应用程序与CircleCI连接起来的中枢角色。
CircleCI
CI with a circular structure
可以通过自动执行的服务来进行构建/测试/部署等操作。
利用这项服务可以自动化各种繁琐的任务。
Docker是一种容器化平台。
一个包含了所有运行应用程序所需的虚拟化平台。因为Rails可以在这个平台上运行,所以我想直接将包含Rails应用程序的Docker容器部署到生产环境中。
以下翻译仅提供一种中文版本:
ECR
可以把容器映像存储在私有环境的服务中。
我想把包含Rails应用环境的Docker容器映像放在这里。
Elastic Compute Service(ECS)的原文意思是弹性计算服务。
AWS是一项能够简化在EC2上通过Docker容器进行处理和启动服务的服务。由于在EC2上的操作都由AWS负责,因此部署应该很容易(虽然事实并非完全如此)。
没问题!非常丰富!
好的,请放心,我会按照通常的顺序进行解释。
我们首先使用Docker来创建一个能够运行Rails应用的环境吧!
通过使用Docker来构建Rails环境。
暫時在本地環境中先建立一次。
由於想要將terraform和rails應用程序放在同一個目錄下,我們創建一個名為terraform_ecs_deploy的目錄(請小心不要與github存儲庫混淆!)
$ mkdir terraform_ecs_deploy
$ cd terraform_ecs_deploy
$ rails new terraform_ecs_app --skip-javascript # webpackerは今回いらないです
$ cd terraform_ecs_app
$ rails db:migrate
现在,在这个时间点上我们要将Rails应用程序进行Docker化。
我们要创建一个Dockerfile,并获取官方的Ruby镜像,然后写入以下内容。
(请谅解我们这次只准备了一个简单的Dockerfile。)
Docker的安装在这里。
# 公式のイメージから取得
FROM ruby:2.6.3
# Dockerfile内部で使える変数として定義
ARG RAILS_ENV
ARG RAILS_MASTER_KEY
# コンテナ内のルートとする変数を/appと定義
ENV APP_ROOT /app
# 環境変数化
ENV RAILS_ENV ${RAILS_ENV}
ENV RAILS_MASTER_KEY ${RAILS_MASTER_KEY}
# コンテナ内のルートとする。
WORKDIR $APP_ROOT
# ローカルのGemfile, Gemfile.lockをコンテナ内のルートへコピー
ADD Gemfile $APP_ROOT
ADD Gemfile.lock $APP_ROOT
# bundle install実行。
# (バージョンのエラーが出る為、一応bundler 2.0.2を指定)
RUN \
gem install bundler:2.0.2 && \
bundle install && \
rm -rf ~/.gem
# バンドルインストールが終わってから他のファイルもコンテナ内へコピー
ADD . $APP_ROOT
# 本番環境の場合プロダクション
RUN if ["${RAILS_ENV}" = "production"]; then bundle exec rails assets:precompile; else export RAILS_ENV=development; fi
# ポート3000番を公開
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
那么,根据这个Dockerfile构建Docker容器,并尝试启动rails。
$ docker build -t terraform_ecs_app:latest . # -tでタグを指定
$ docker run -it -p 3000:3000 terraform_ecs_app:latest # -it
在运行docker时,选项如下列所示。
-i,–interactive:与容器的STDIN连接
-t,–tty:分配伪终端
如果是这样的话,那就可以了。

当您访问 http://localhost:3000,应该会出现熟悉的界面。

※注意!如果 docker run -it -p 3000:3000 terraform_ecs_app:latest 不能运行,请留意。
在这里的话 (Moshi koko de)
docker: you are not authorized to perform this operation: server returned 401.
See 'docker run --help'.
如果出现此类错误,请在终端中输入以下命令设置环境变量。
export DOCKER_CONTENT_TRUST=0
通过这样做,可以跳过对Docker镜像是否被篡改的完整性检查,理论上应该可以执行上述命令。
关于Docker内容信任(DCT)
Rails的详细设置
那么,这次的重点不是关于Rails的讨论,所以功能上只需要简单的即可。因此,
-
- 表示用のトップ画面
ALB(Application Load Balancer)のHealthCheck用に、jsonでstatus: okが帰ってくるURL
我们只需要实现其中两个。(当然后面也会提到ALB和健康检查。)
$ rails g controller top index
$ rails g controller health_check
由于顶层控制器应该已经自动生成了动作和视图,因此只需更改health_check控制器。
class HealthCheckController < ApplicationController
# ALBにステータスokを返す為のアクション
def index
render json: '{ "status": "ok" }'
end
end
我也会更改路由器设置。
Rails.application.routes.draw do
root to: 'top#index'
resources :health_check, only: [:index]
end
只要按如下方式显示就可以。


另外,为了避免在生产环境中出现无法创建tmp/pids和tmp/sockets的情况,请在.gitignore中添加以下内容作为一种预防措施。
# See https://help.github.com/articles/ignoring-files for more about ignoring files.
#
# If you find yourself ignoring temporary files generated by your text editor
# or operating system, you probably want to add a global ignore instead:
# git config --global core.excludesfile '~/.gitignore_global'
# Ignore bundler config.
/.bundle
# Ignore the default SQLite database.
/db/*.sqlite3
/db/*.sqlite3-journal
/db/*.sqlite3-*
# Ignore all logfiles and tempfiles.
/log/*
/tmp/*
!/log/.keep
!/tmp/.keep
# ===============追加================
!/tmp/pids
!/tmp/sockets
# =============ここまで===============
# Ignore uploaded files in development.
/storage/*
!/storage/.keep
/public/assets
.byebug_history
# Ignore master key for decrypting credentials and more.
/config/master.key
我们已经实现了最低限度的功能,现在让我们将代码推送到GitHub上。
将Rails应用程序推送到GitHub上。
这次将省略对Git和GitHub的解释。
Git的安装
GitHub的使用方法
请确保当前目录是一个Rails应用程序,然后执行以下命令。
(我这里将仓库名称设置为terraform_ecs_app。)
$ git init
$ git commit -m "initial commit"
$ git remote add origin リポジトリのURL
$ git push origin master
如果GitHub上的存储库如下所示,那就可以了。

现在,我们开始进行CircleCI的CI设置吧!
在CircleCI上进行CI设置
CircleCI是一种基于Saas的CI/CD服务,它可以自动化构建、测试和部署这三个基本任务,这也是CI/CD的基础。
我們這次想要利用這個來進行測試和部署(推送至ECR)的自動化。
请参考以下链接了解关于CircleCI的特点和价格。虽然现在有点晚,但我已经开始入门CircleCI了,所以我试着把它简单明了地总结起来。
###引入CircleCI

请点击URL右上方的Login,然后按下Log in with Github进行登录。
这样就会自动与Github相关联,进入CI的设置页面。CircleCI

按下”Add Projects”之后,您的GitHub仓库会显示出来。从中选择刚刚创建的terraform_ecs_app。然后点击”Set Up Project”。

然后,我认为会进入到项目的设置页面。
操作系统是Linux,
编程语言仍然使用Ruby也没有问题。
而且,在下面直接有英语的教程。如果要翻译成中文的话,
创建名为.circleci的文件夹,并添加config.yml文件(文件路径应为.circleci/config.yml)。
将sample.yml的内容输入到config.yml中(请参考以下)。
更新sample.yml以反映项目配置。
将这些更改推送到GitHub。
开始构建!这将在CircleCI中启动项目并侦听Webhook的工作更新。
听说是这样的。实际上,在那座大厦底下,事先有CircleCI为Rails准备的配置文件sample.yml。
按照指示,只需复制粘贴这个文件,在本地环境中创建一个名为.circleci的文件夹,并在其中创建一个名为config.yml的文件,然后将其粘贴到其中即可。
然而,由于使用的Ruby版本不同以及测试框架是rspec,本次请将以下内容修改为使用Rails默认测试框架minitest的版本。
# Ruby CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
#
version: 2
# jobsの中にタスクを定義。一番下のworkflowのjobsのなかで定義したタスクを使う。
jobs:
# buildという名前のタスク定義
build:
docker:
# specify the version you desire here
- image: circleci/ruby:2.6.3-node-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/postgres:9.4
working_directory: ~/repo
# 実際の処理内容
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
# runのたびに実行
- run:
name: install dependencies
command: |
gem install bundler -v 2.0.2
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
# Database setup
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
# run tests
- run:
name: run tests
command: |
DISABLE_SPRING=true bundle exec rails test
# collect reports
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
如果你完成了这一步骤,请返回之前的CircleCI设置页面,点击右侧的第5个“开始构建”。CircleCI将开始运行。

通过这样的方式,以后每次推送时CircleCI将开始执行工作!
顺便提一下,由于改变了routes.rb文件,导致测试失败了,所以请您在推送和构建之前,将对应的测试注释掉,或者将其正确描述(path改为root_path)。

如果成功,CircleCI的JOBS选项卡将显示为以下内容(绿色的位于最上方)。

终于来到了这一步,现在让我们开始使用Terraform吧!
准备使用Terraform
安装Terraform
Terraform官方网站
通过使用Terraform,您可以将以前的基础设施流程转化成可用代码。
在中国,您可以使用Homebrew进行安装,但建议使用tfenv作为Terraform的版本管理工具。它易于跟踪版本更新。
首先,让我们安装tfenv自身。
$ brew install tfenv
$ tfenv --version
tfenv 1.0.1
你可以使用list-remote命令来确认可以安装的Terraform版本。在这里,我们选择安装0.12.8版本。
$ tfenv list-remote
.
.
0.12.8
$ tfenv install 0.12.8
$ terraform -v
Terraform v0.12.8
设置IAM用户以便在Terraform中处理AWS。
要使用terraform处理AWS,需要IAM用户的ACCESS KEY、SECRET KEY (以及DEFAULT REGION)。
因为这里无法受Terraform管控,所以请在AWS控制台上创建IAM用户并生成访问密钥和秘钥。
创建IAM用户
如果能够发布,请在终端中将访问密钥和秘密密钥设置为环境变量。
$ export AWS_ACCESS_KEY_ID=AKIxxxxxxxxx
$ export AWS_SECRET_ACCESS_KEY=wJalxxxxxxxxxxxxxxxx
$ export AWS_DEFAULT_REGION=ap-northeast-1
如果关掉电脑或者退出终端,文本会消失,所以请重新输入一遍。
这次我使用了已附加了AdministratorAccess策略的IAM用户的访问密钥,请小心处理,因为这是相当强大的权限。
(千万不要错误地在GitHub等地方公开!)
除了AdministratorAccess策略,其他权限可能不足以执行Terraform操作,如果遇到这种情况,请参考错误消息并分配所需的权限。
现在我们已经准备好开始使用Terraform了!
创建用于Terraform的GitHub存储库。
因为应用程序开发和持续集成以及基础架构的配置是两码事,所以根据理论应该将它们分开存储在不同的代码仓库中。
这次我们将创建一个名为”terraform_ecs”的Github代码仓库。
由于处理秘密情报,请注意将其放入私有库中。

嗯,关于Terraform的目录结构有很多不同的想法,您可以参考以下链接:
https://dev.classmethod.jp/devops/directory-layout-bestpractice-in-terraform/
https://qiita.com/anfangd/items/1b84f69fa2a4f8a29fbc
https://future-architect.github.io/articles/20190903/
这次为了方便起见,我们将仅在环境中分别创建目录,并将所有的tf文件都放入其中。
terraform_ecs_deploy
├──terraform_ecs_app
└──terraform_ecs
├──prod
└──stg
另外,这次不会进行分阶段环境的构建!因为几乎是在做相同的事情,所以一旦在生产环境中构建完成,马上就能做到。
那么,让我们立即创建一个用于terraform的目录吧。 , yú terraform de ba.)
$ cd .. # railsアプリにいた場合
$ pwd
/Users/matsumotokazuki/Desktop/terraform_ecs_deploy # terraform_ecs_deployにいることを確認。
$ mkdir terraform_ecs
$ mkdir terraform_ecs/prod
$ cd terraform_ecs/prod
如果目录结构像这样,那就没问题了。
terraform_ecs_deploy
├──terraform_ecs_app
└──terraform_ecs
└──prod # カレントディレクトリ
在将代码推送到GitHub之前,需要将代表Terraform基础架构配置文件的terraform.tfstate以及代表敏感信息的terraform.tfvars从GIT的版本控制中排除掉。(后面也会提到这一点)
$touch .gitignore
/.terraform/*
/terraform.tfvars
/terraform.tfstate
那么,让我们推进吧。
$ cd .. # terraform_ecsディレクトリに移動
$ git init
$ git add -A
$ git commit -m "initial commit"
$ git remote add origin リポジトリ名
$ git push origin master

前半结束于此
在各项准备工作完成后,我们将暂时停止。现在开始使用Terraform来构建基础设施,还请耐心等待。
我计划选择后半部分。