はじめに
この記事では AWS Cloud Tech を通して IaC を学習して実践していく記事です。
主な内容としては実践したときのメモを中心に書きます。(忘れやすいことなど)
誤りなどがあれば書き直していく予定です。
※AWS Cloud Tech では CloudFormation が基礎から学習できます。
※今回チョットだけ扱う Terraform は AWS Cloud Tech で学習しません。(2021/02/08 Mon 現在)
※この記事で紹介する Rails 環境のパラメータ(パスワードやユーザ名、権限)などはご自身の環境に
合わせていただくようお願い致します。
また、今回は一度も使っていませんが
今後はプライベートネットワークにも何かしら置く予定です。
Terraform とは
HashiCorp 社 が提供する製品の名前
巷で話題のインフラをコード化するということが各クラウドでできます。
今回は AWS を題材に使いますが、Azure,GCP でもできます。
なぜ、Terraform なのか
私が思う Terraform を採用する理由
- インフラをコードという形で実現できる
- 他のクラウドでインフラを構築しようと思った際にコードがインフラの設計図になる
- 他のクラウドにも対応しているので構築するインフラ毎にコードを書いてハイブリッドにインフラを構築することができる
AWS なら CloudFormation があるので最初の 2 つはあまり理由になりませんが
ハイブリッドにパブリッククラウドを繋ぐときに複数のクラウドサービスのIaCにも対応している為
一気通貫でインフラの柔軟な構築が可能になる。
極端なことを言えば、AWS,Azure,GCPの環境をコマンド1行で建てられる!
Terraform のここが難しい
-
HCL という前述の HashiCorp 社が提供する言語を多少学習する必要がある
-
書こうと思って調べると古いドキュメントや記事が多くて惑わされる
今回は VPC 構築からターミナルソフトで SSH ログインするところまで作成しましたが
かなり Web 上をさまよいました。
実際に書いたコード
実際は環境変数を利用してアクセスキーを隠したりしますが
ローカル環境でコードを書いていた為
key.tf にまとめました。
アクセスキーの利用は慎重に
key.tf
variable "access_key" {
default = "アクセスキー"
}
variable "secret_key" {
default = "シークレットキー"
}
main.tf
terraform {
required_version = "0.14.0"
required_providers {
aws = {
version = ">= 3.21.0"
source = "hashicorp/aws"
}
}
}
## ユーザとリージョンの設定
variable "region" {
default = "ap-northeast-1"
}
provider "aws" {
region = var.region
access_key = var.access_key
secret_key = var.secret_key
}
## VPC領域の作成
resource "aws_vpc" "rails_vpc" {
cidr_block = "10.0.0.0/21"
instance_tenancy = "default"
enable_dns_support = "true"
enable_dns_hostnames = "true"
tags = {
Name = "VPC領域"
}
}
## パブリックサブネットの作成
resource "aws_subnet" "PublicSubnetA" {
vpc_id = aws_vpc.rails_vpc.id
cidr_block = "10.0.1.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "PublicSubnetA"
}
}
## プライベートサブネットの作成
resource "aws_subnet" "PrivateSubnetA" {
vpc_id = aws_vpc.rails_vpc.id
cidr_block = "10.0.2.0/24"
availability_zone = "ap-northeast-1a"
tags = {
Name = "PrivateSubnetA"
}
}
##ルートテーブルの追加(0.0.0.0/0)
resource "aws_route_table" "public-route" {
vpc_id = aws_vpc.rails_vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.yamada-web_GW.id
}
}
##ルートテーブルの追加(1a)
resource "aws_route_table_association" "public-a" {
subnet_id = aws_subnet.PublicSubnetA.id
route_table_id = aws_route_table.public-route.id
}
##ゲートウェイの設定
resource "aws_internet_gateway" "yamada-web_GW" {
vpc_id = aws_vpc.rails_vpc.id
}
## Rails用セキュリティグループの設定
resource "aws_security_group" "rails-web" {
name = "rails-web"
description = "rails-web"
vpc_id = aws_vpc.rails_vpc.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 3000
to_port = 3000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
ipv6_cidr_blocks = ["::/0"]
}
tags = {
Name = "rails-web"
}
}
## EC2 インスタンスの設定
variable "ami" {
default = "ami-0992fc94ca0f1415a"
}
variable "instance_type" {
default = "t2.micro"
}
variable "volume_size" {
default = 8
}
##EC2(rails-web01)
resource "aws_instance" "rails-web01" {
ami = var.ami
availability_zone = "ap-northeast-1a"
instance_type = var.instance_type
key_name = aws_key_pair.key_pair.id
private_ip = "10.0.1.10"
disable_api_termination = false
vpc_security_group_ids = [aws_security_group.rails-web.id]
subnet_id = aws_subnet.PublicSubnetA.id
root_block_device {
volume_type = "gp2"
volume_size = var.volume_size
}
tags = {
Name = "rails-web01"
}
}
##EIP(rails-web01)
resource "aws_eip" "rails-web01" {
instance = aws_instance.rails-web01.id
vpc = true
}
## EC2 key pairの名前を変数に保存
variable "key_name" {
description = "keypair name"
default = "rails-web01"
}
## EC2 key pair の生成 PATH を指定
locals {
public_key_file = "./.ssh/id_rsa.pub"
private_key_file = "./.ssh/id_rsa"
}
## 鍵の生成をRSAに設定
resource "tls_private_key" "keygen" {
algorithm = "RSA"
rsa_bits = 4096
}
## 秘密鍵ファイルを作成
resource "local_file" "private_key_pem" {
filename = local.private_key_file
content = tls_private_key.keygen.private_key_pem
directory_permission ="600"
file_permission = "0600"
}
## 公開鍵ファイルを作成
resource "local_file" "public_key_openssh" {
filename = local.public_key_file
content = tls_private_key.keygen.public_key_openssh
directory_permission ="600"
file_permission = "0600"
}
## EC2に登録するキーペア名を出力
output "key_name" {
value = var.key_name
}
## 秘密鍵ファイルPATH(このファイルを利用してサーバへアクセスする。)
output "private_key_file" {
value = local.private_key_file
}
## 秘密鍵内容
output "private_key_pem" {
value = tls_private_key.keygen.private_key_pem
}
## 公開鍵ファイルPATH
output "public_key_file" {
value = local.public_key_file
}
## 公開鍵内容(サーバの~/.ssh/authorized_keysに登録して利用する。)
output "public_key_openssh" {
value = tls_private_key.keygen.public_key_openssh
}
## EC2にキーペアを登録
resource "aws_key_pair" "key_pair" {
key_name = var.key_name
public_key = tls_private_key.keygen.public_key_openssh
}
各項目の説明
ユーザとリージョンの設定
ここで設定する情報
- VPC のアベイラビリティゾーンをどのリージョンに置くか
- アクセスキーの情報
- シークレットキーの情報
ちなみにところどころで出ている「variable」というのは変数のことです。
VPC 領域の作成
VPC 領域という名前で VPC ネットワークを作成します。
※名前はなんでも構いません
プライベートサブネットの作成
データベースなどの重要情報を配置するいわゆる内部ネットワークを構築する。
パブリックサブネットの作成
インターネットの公開しても良いサブネットを作成します。
ルートテーブルの追加(0.0.0.0/0)
インターネット向けのルーティングを追加します。
ルートテーブルの追加(1a)
パブリックサブネットを「VPC 領域」に関連付けします。(サブネットの関連付け)
ゲートウェイの設定
VPC にインターネットゲートウェイを設定します。
Rails 用セキュリティグループの設定
rails server 実行時にはデフォルト 3000 番ポートが利用されるので 3000 番を開けておきます。
インターネット上にサーバを公開できているかを確認する為、nginx 用に 80 番を開けておきます。
EC2は自 PC からSSHと秘密鍵を利用して接続します。
※AWS には AWS SSM というサービスがあるので秘密鍵が無くても EC2 にアクセスできます。(IAM 権限が必要)
EC2 インスタンスの設定
ここで AMI、インスタンスタイプ、EBS ボリュームのサイズを変数にしておきます。
アベイラビリティゾーンやプライベート IP も変数にしたほうが見栄えが良いかも。。。。
EC2(rails-web01)
ami : AMI のイメージ ID を代入
availability_zone: アベイラビリティゾーンを設定
instance_type:インスタンスタイプを設定(今回は t2.micro)
key_name:秘密鍵の名前を設定
private_ip:EC2 のプライベート IP アドレスを設定
root_block_device : EBS を設定
tags:EC2 の名前を決定
EIP(rails-web01)
たぶん、ElasticIP の設定
EC2 key pair の名前を変数に保存
SSH にログインするときに必要な秘密鍵の名前を変数に保存する。
EC2 key pair の生成 PATH を指定
SSH にログインするときに必要な秘密鍵を変数に保存する。
鍵の生成を RSA に設定
RSA アルゴリズムでキーペアを作成するように設定する。
秘密鍵ファイルを作成
EC2 に SSH 接続時に利用する秘密鍵を作成する。
公開鍵ファイルを作成
EC2 に SSH 接続時に利用する公開鍵を作成する。
EC2 にキーペアを登録
EC2 に公開鍵を登録
Ruby on Rails の環境を構築(準備)
せっかくなので Ruby on Rails の環境を構築する。
その前に nginx でパブリック IP の接続確認
まずは管理者ログインを行い 、yum update -y
sudo su -
sudo yum update -y
必要なパッケージをダウンロード
sudo yum -y install gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel libffi-devel libxml2 libxslt libxml2-devel libxslt-devel sqlite-devel git wget gem
git config の user.name と user.email を変更
git config --global user.name "じぶんのアカウント名"
git config --global user.email "じぶんのメールアドレス"
今のままだと時刻が UTC なので
時刻を JST に合わせる。
sudo cp -r /usr/share/zoneinfo/Japan /etc/localtime
vi で JST に設定できるように中身を編集
sudo vim /etc/sysconfig/clock
記載内容
ZONE="Asia/Tokyo"
UTC=true
確認がてら date コマンドを打つ
date
Sun Feb 7 15:24:27 JST 2021
日本標準時で返ってくれば OK
次に EC2 がインターネットと接続されているかを nginx で確認する。
sudo amazon-linux-extras install -y nginx1
sudo systemctl list-unit-files -t service |grep "ngin*"
sudo systemctl start nginx.service
sudo systemctl enable nginx.service
nginx を止める。
今回作成した EC2 を再起動して使いまわす場合は disable も忘れずに
sudo systemctl stop nginx.service
sudo systemctl list-unit-files -t service |grep "ngin*"
## disable 確認
sudo systemctl disable nginx.service
sudo systemctl list-unit-files -t service |grep "ngin*"
Ruby on Rails の環境を構築(構築)
rbenv を git コマンドでクローンしてくる。
sudo git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
rbenv 用にシェルファイルを作成する。
sudo vim /etc/profile.d/rbenv.sh
記載内容
export RBENV_ROOT="$HOME/.rbenv"
export PATH="${RBENV_ROOT}/bin:${PATH}"
eval "$(rbenv init -)"
ruby-build を git clone
sudo git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
ec2-user のグループを wheel グループに変更してファイルのアクセス権限を変更
※人によってはここで別のユーザを作成して環境構築に入りますが工程が増えるので興味のある方は調べてみてください。
sudo usermod -G wheel ec2-user | grep wheel /etc/group
sudo chown -R ec2-user:wheel ~/.rbenv
sudo chmod -R 775 ~/.rbenv
権限とグループの確認
ls -la
rbenv を用いて ruby をインストール
自分の PC とバージョンを合わせる為、バージョンは 2.5.8 を選択
※ちなみに Ruby on Rails を動かす為の Ruby のバージョンは 2.5 以上とのこと
source /etc/profile.d/rbenv.sh
rbenv --version
rbenv install -l
rbenv install -v 2.5.8
rbenv rehash
rbenv global 2.5.8
バージョン確認
ruby -v
ruby 2.5.8p224 (2020-03-31 revision 67882) [x86_64-linux]
次に NVM
ザックリ言えば Nodejs のバージョン管理ツールを導入
git clone https://github.com/creationix/nvm.git ~/.nvm
source ~/.nvm/nvm.sh
bash_profile の設定
vi .bash_profile
記載内容
# nvm
if [[ -s ~/.nvm/nvm.sh ]] ; then
source ~/.nvm/nvm.sh ;
fi
Node.js のバージョンは自分の PC と合わせる為
v12.19 を選択
バージョンがあるかを確認する。
nvm ls-remote | grep "v12.19.*"
Node.js のインストールとデフォルトバージョンの設定
nvm install v12.19.0
nvm use v12.19.0
Node.js のバージョン確認
node -v
Rails6 あたりから yarn というパッケージマネージャが必要になったので導入する。
実は yarn って npm より高速に動くらしい。
sudo wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo
sudo curl --silent --location https://rpm.nodesource.com/setup_12.x | bash -
sudo yum -y install -y nodejs yarn
yarn install --check-files
Rails の DB には mariadb を採用
理由としては今後、RDS で MySQL を利用する為
※gem インストールを忘れずに!!
sudo yum -y install mariadb-server
sudo systemctl start mariadb
sudo yum -y install mysql-devel
gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'
仕上げに bundler と 大本命の Rails をインストール
gem install bundler
gem install rails -v 6.0.3
そしてお待ちかねの起動コマンド
rails new app
cd app
bundle install
rails server -p 80 -b 0.0.0.0
# 起動はOK
起動はできるが開いてみるとエラーになる。
どうやらデータベース周りのようだ。
エラーの原因を探す
mariadb の生存確認
sudo systemctl status mariadb
実行結果
[root@ip-10-0-1-10 app]# sudo systemctl status mariadb
mariadb.service - MariaDB database server
Loaded: loaded (/usr/lib/systemd/system/mariadb.service; disabled; vendor preset: disabled)
Active: active (running) since Mon 2021-02-08 21:09:46 JST; 14min ago
Process: 28789 ExecStartPost=/usr/libexec/mariadb-wait-ready $MAINPID (code=exited, status=0/SUCCESS)
mariadb はダイジョブ
Rails Server で読み込まれるデータベースはどうやら SQLite が標準みたいなので
./config/database.yml を編集する。
書き間違えて戻せなくなると怖いのでコピーを取る
$ [root@ip-10-0-1-10 /app] # pwd
/root/app
$ [root@ip-10-0-1-10 /app] cp ./config/database.yml ./config/sqlite3_database.yml
$ [root@ip-10-0-1-10 /app] rm -f ./config/database.yml
$ [root@ip-10-0-1-10 /app] vi ./config/database.yml
database.yml の中身
default: &default
adapter: mysql2
encoding: utf8
pool: 5
timeout: 5000
username: root
password: test
socket: /var/lib/mysql/mysql.sock
development:
<<: *default
database: rails_todo_tutorial_development
test:
<<: *default
database: rails_todo_tutorial_test
production:
<<: *default
database: rails_todo_tutorial_production
注意点:socket のパスを間違えると mariadb が起動できなくなります。(Socket エラー)
各環境ごとにテーブルの名前をつけてください。
今回はわかりやすいように末尾に環境名を付けました。
また、当然ですが
実際にはパスワードは厳密に決める必要があります。
加えて、root 権限でアプリをデプロイすることなどしません。
適切な権限を持ったアカウントを作成してコンフィグに載せましょう。
コンフィグを変更したので起動したいところですが
この時点では gem が SQLite になっていますので mysql2 に変更
変更後の Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '2.5.8'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 6.0.3'
# Use sqlite3 as the database for Active Record
# gem 'sqlite3', '~> 1.4'
gem 'mysql2'
# Use Puma as the app server
gem 'puma', '~> 4.1'
# Use SCSS for stylesheets
gem 'sass-rails', '>= 6'
# Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
gem 'webpacker', '~> 4.0'
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5'
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
gem 'jbuilder', '~> 2.7'
# Use Redis adapter to run Action Cable in production
# gem 'redis', '~> 4.0'
# Use Active Model has_secure_password
# gem 'bcrypt', '~> 3.1.7'
# Use Active Storage variant
# gem 'image_processing', '~> 1.2'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.4.2', require: false
group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
end
group :development do
# Access an interactive console on exception pages or by calling 'console' anywhere in the code.
gem 'web-console', '>= 3.3.0'
gem 'listen', '~> 3.2'
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end
group :test do
# Adds support for Capybara system testing and selenium driver
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
そしてもう一度、bundle install
$ [root@ip-10-0-1-10 /app] bundle install
Don t run Bundler as root. Bundler can ask for sudo if it is needed, and installing your bundle as root will break this application
for all non-root users on this machine.
Fetching gem metadata from https://rubygems.org/............
Resolving dependencies...
.
.
.
Using method_source 1.0.0
Using mysql2 0.5.3
Using puma 4.3.7
.
.
.
特にバージョン指定はしませんでしたが
バンドルされましたね。
Using mysql2 0.5.3
これでようやく起動かと思いきや
今度はデータベースのパスワード決めていません。
root 権限にパスワードを設定してあげましょう。
$ [root@ip-10-0-1-10 /app] mysql
MariaDB [(none)]> show databases;
MariaDB [(none)]> use mysql;
MariaDB [(none)]> show tables;
MariaDB [(none)]> SET PASSWORD FOR root@localhost = password('test');
MariaDB [(none)]> select user,password from user;
まだまだやることがあります。
今起動するとこんなエラーが表示されるはずです。
最後にやることは 3 つ
- yarn との依存関係を解決する
- DB を作成する
- DB をマイグレーション
$ [root@ip-10-0-1-10 /app] yarn install --check-files
$ [root@ip-10-0-1-10 /app] rails db:create
$ [root@ip-10-0-1-10 /app] rails db:migrate
何度目かの正直
rails server -p 80 -b 0.0.0.0
宣伝
AWS Cloud Tech では AWS の知識を幅広く学習することが可能です。
一緒に学習できるメンバーもいるので
「一人で学習して挫折する」ということをうまく避けながら学習ができます。
一緒に勉強できる仲間を募集中です。
最近ですが、Yamada 主催のもくもく会を
AWS Cloud Tech 内で随時開催する予定ですので
もし、ご興味がございましたらAWS Cloud Techの門を叩いていただけたらと思います。
リンクはこちら
おわり
Terraform で EC2 を建てるところまでできたなら
他のデータベースのセットアップなども自動でやりたい感じがする。
まぁ、データベースに関しては素直に Amazon RDS を使えば良いんですけどね。
とりあえず、環境構築はこの手順でできるとわかったので最後に「terraform destory」
これだけ書いたけど用語がありすぎて。。。アリストテレスになりそうなので用語集を書きたいですね。
あ^全部コード化したいんじゃあ^