月別: 2016年5月

【デーモン化で解決!】WEBrickでRedmineを立ち上げるとpost時に真っ白になる

f:id:seeds-std:20161108200910p:plain

どうも、はらぐちです。

今回は、
「WEBrickでRedmineを立ち上げると、なぜかpostした時だけ真っ白になってしまう」という件について、
解決法をご紹介します。
rails力が足りなくてハマった感じですが……

経緯

シーズではプロジェクト管理ソフトのひとつにRedmineを使用しているのですが、
最近、古いサーバー上のRedmineを、最新OSを搭載した新しいサーバーに移行することになりました。

1.データをそのままごっそり持ってきて、
2.mysqldumpをリストア、
3.rbenvで該当バージョンをそろえて、
4.gemも同じバージョンを入れていったのですが、

どうしても、Passengerだけがうまく使えない……

そこで、WEBrickで起動し、apacheのリバースプロキシ経由で参照するように設定をしてみました。

おおむねこれでうまく動いていたのですが、
なぜかPostした時だけ真っ白なページになる状態に……
(※真っ白なページにはなりますが、更新自体はされている。)

解決法

いろいろ調べてみた結果、
そのままフォアグランドで起動するとうまくいくのにバックグラウンド実行するとうまくいかない事がわかりました。
そこからはWEBrickのドキュメントなどを参照して、-dオプションでデーモン化で立ち上げる事が可能と判明。
以下のような起動方法で問題なく動作しました

/path/to/redmine/script/server -d -e production

デーモン化は知りませんでしたが、バックグラウンド実行だと何故こんな動きになるんでしょうね?
ひとまずはデーモン化で解決しました!

AWSとVPN接続を張ったけど転送量はどうなの?

blog_img_4285

先日、CTO原口くんにお願いして、シーズ社内にあったWindowsサーバーをAWSに移行しました。
AWSにWindowsServerOSインスタンスを立てて、シーズからはVPN(IPsec接続)でシームレスかつセキュアに社内ネットワークを拡張したイメージです。
その話はまた別にするとして、その時、「AWSでファイルサーバーをやるのってどうなの?使えるの?」という話になったので検証をちょこっとやってみました。

ファイルサーバー自体は、EC2.linuxでsambaを立てるなり、WindowsServerでファイルサーバーをするなりして簡単に実装できますが、一番のネックは転送スピードということになりますよね。

そこを検証してみました。

環境

社内LAN環境はすべてギガビット回線
=>社内ファイルサーバーにはギガビット転送ができる(1000Mbps=125MB/s)

インターネットともギガビットの専用線を引いている。
インターネット接続はRTX1200を利用している。VPNルーターとしても利用している。
AWS VPC VPN接続にはRTX1200からIPSECで接続している。

社内、社外インターネットともギガビットのため、理論上はすべてギガで通信できる環境です。

社内ファイルサーバーの転送スピード

アップロード
inG

アップロード(パソコンから社内ファイルサーバー)スピードです。
だいたい平均的に32.5MB/s(260Mbps)
となっているので、理論値の26%ぐらいしかでていませんが、通常利用ではまったく問題のない速さだと思います。

ダウンロード
outG

ダウンロード(社内ファイルサーバーからパソコン)はさらに早くなりました。
57.2MB/s(457.6Mbps)!
これ以上求める必要はないでしょう。というレベルだと思います。

AWS上VPN接続

アップロード
in

アップロード(パソコンからAWS-VPNファイルサーバー)転送スピードは6.82MB/s(54Mbps)です。
スピードはかなり落ちましたが、実用性ではどうでしょうかね。
一昔前の100Mbps環境時代の速度というところでしょうか。
今のギガに慣れているとストレスかもしれませんが、使えなくはなさそうですね。

ダウンロード
out

ダウンロード(AWS-VPNファイルサーバーからパソコン)は逆に遅くなりました。
3.68MB/s(29.44Mbps)で止まっています。
出だしは早かったのですが、なにか制限が入っているのでしょうかね。
とはいえ、まあ実用的に全く使えないわけではなさそうです。

結論

AWS-VPNは100Mbpsで繋がっていた時代の速度ではあるが、使えないことはなさそう。
しかし、転送量でも課金されるので注意が必要。(アップロードは無料)
1000GB = $139.86
100GB = $13.86
10GB = $1.26

Terraformを使ってみました

blog_img_4238

Terraformは、あらかじめインフラ構成を設定ファイルに記述して、
クラウド環境に適用・管理するツールです。
Vagrantなどを開発しているHashiCorpのツールになります。

AWSだけではなく様々なプロバイダに対応していますが、AWSで使用してみました。

インストール

Linuxサーバに入れてみました。
ダウンロードしてきて展開するだけで使えます。

cd /opt
mkdir terraform
cd terraform
wget https://releases.hashicorp.com/terraform/0.6.16/terraform_0.6.16_linux_amd64.zip
unzip terraform_0.6.16_linux_amd64.zip

パスを通すと便利でした。

vi ~/.bashrc

export PATH=/opt/terraform:$PATH

AWS側事前準備

  • AWSアカウント作成
  • terraformで触るリソースをいじれるポリシーを付与したIAMユーザを作成

terraformの動作イメージ

必ず使うコマンドは以下の三つ

terraform plan
dry-runです。設定ファイルの記述ミスなどをチェックしてくれます

terraform apply
実行です。設定ファイル通りの構成を実装してくれます

terraform destroy
作成した構成を削除してくれます

terraform初期設定

まずはAWSアクセスキーなどの設定。
terraformは「terraform.tfvars」というファイルがカレントディレクトリにあると
そちら中を読んで変数として取り扱ってくれます。
注意点として、このファイルでは配列は設定できないようです。

このようにアクセスキーなどを変数として設定しておく事ができます。

file : terraform.tfvars

#####################################
#Variable Settings
#####################################
#AWS Settings
access_key = "xxxxxxxxxxxxxxxxxxx"
secret_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
region = "ap-northeast-1"

このファイルは.gitignoreとかでリポジトリでは管理しないようにします。

次のこの変数をtfファイル内で使用できるようにvariableで定義します。
上記のterraform.tfvarsの記述内容は環境変数に入るので、それを定義します。

file : variables.tf

#####################################
# Variable Settings
#####################################
#AWS Settings
variable "access_key" {}
variable "secret_key" {}
variable "region" {}

上記で設定した変数を使ってterraformのAWS設定を記述します

file : aws.tf

provider "aws" {
access_key = "${var.access_key}"
secret_key = "${var.secret_key}"
region = "${var.region}"
}

以上で初期設定は終わり。
terraform planやterraform applyをするとカレントにある *.tf ファイルをすべて読み込んでくれます。
また、一度applyを実行すると、terraform.tfstateというファイルが作成されます。
terraform.tfstate は、Terraform が管理しているリソースの状態が保存されているのだと思います。
おそらく次回実行時にこちらのstateファイルとの差分でどのようなAPIを投げるか決定しているのだと思います。

VPCを作成してみる

実際にいろいろ設定をしてみたので紹介です。
以下は

・VPCの作成
・サブネット二つ作成(いずれもパグリックサブネット)
・DHCPオプションの作成
・インターネットゲートウェイ作成
・ルーティングテーブル作成

を行っています。

file aws_vpc.tf

#####################################
# VPC Settings
#####################################
resource "aws_vpc" "vpc_main" {
cidr_block = "${var.root_segment}"
enable_dns_support = true
enable_dns_hostnames = true
tags {
Name = "${var.app_name}"
}
}
#####################################
# DHCP option sets
#####################################
resource "aws_vpc_dhcp_options" "vpc_main-dhcp" {
domain_name = "${var.dhcp_option_domain_name}"
domain_name_servers = ["AmazonProvidedDNS"]
tags {
Name = "${var.app_name} DHCP"
}
}
resource "aws_vpc_dhcp_options_association" "vpc_main-dhcp-association" {
vpc_id = "${aws_vpc.vpc_main.id}"
dhcp_options_id = "${aws_vpc_dhcp_options.vpc_main-dhcp.id}"
}
#####################################
# Internet Gateway Settings
#####################################
resource "aws_internet_gateway" "vpc_main-igw" {
vpc_id = "${aws_vpc.vpc_main.id}"
tags {
Name = "${var.app_name} igw"
}
}
#####################################
# Public Subnets Settings
#####################################
resource "aws_subnet" "vpc_main-public-subnet1" {
vpc_id = "${aws_vpc.vpc_main.id}"
cidr_block = "${var.public_segment1}"
availability_zone = "${var.public_segment1_az}"
tags {
Name = "${var.app_name} public-subnet1"
}
}
resource "aws_subnet" "vpc_main-public-subnet2" {
vpc_id = "${aws_vpc.vpc_main.id}"
cidr_block = "${var.public_segment2}"
availability_zone = "${var.public_segment2_az}"
tags {
Name = "${var.app_name} public-subnet2"
}
}
#####################################
# Routes Table Settings
#####################################
resource "aws_route_table" "vpc_main-public-rt" {
vpc_id = "${aws_vpc.vpc_main.id}"
route {
cidr_block = "0.0.0.0/0"
gateway_id = "${aws_internet_gateway.vpc_main-igw.id}"
}
tags {
Name = "${var.app_name} public-rt"
}
}
resource "aws_route_table_association" "vpc_main-rta1" {
subnet_id = "${aws_subnet.vpc_main-public-subnet1.id}"
route_table_id = "${aws_route_table.vpc_main-public-rt.id}"
}
resource "aws_route_table_association" "vpc_main-rta2" {
subnet_id = "${aws_subnet.vpc_main-public-subnet2.id}"
route_table_id = "${aws_route_table.vpc_main-public-rt.id}"
}

この例の通り、作成したリソースのIDなどは他のリソースで読み込む事が可能です。
一番上で作成したVPCのIDは以下のように取得できます。

${aws_vpc.vpc_main.id}

セキュリティグループを作成してみる

特筆するところはないですが、ソース元をセキュリティグループにする例も載せてみました。
ソース元を配列にする事で複数の設定ができます。
terraform.tfvarsに配列が指定できたらいいんですが・・・

resource "aws_security_group" "lb_sg" {
name = "${var.app_name} Load Balancer"
vpc_id = "${aws_vpc.vpc_main.id}"
ingress {
from_port = 80
to_port = 80
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"]
}
description = "${var.app_name} Load Balancer"
}
resource "aws_security_group" "web_sg" {
name = "${var.app_name} WEB Server"
vpc_id = "${aws_vpc.vpc_main.id}"
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["${var.root_segment}"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["${var.root_segment}"]
security_groups = ["${aws_security_group.lb_sg.id}"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
description = "${var.app_name} WEB Server"
}

RDSの作成

RDS用のサブネットグループを作成する部分が少しはまりました

resource "aws_db_instance" "default" {
allocated_storage       = 5
identifier              = "${var.rds_name}"
engine                  = "${var.rds_engine}"
engine_version          = "${var.rds_engine_version}"
instance_class          = "${var.rds_instane_type}"
name                    = "${var.rds_db_name}"
username                = "${var.rds_user}"
password                = "${var.rds_pass}"
db_subnet_group_name    = "${aws_db_subnet_group.default.id}"
parameter_group_name    = "${var.rds_parameter_group}"
vpc_security_group_ids  = ["${aws_security_group.mysql_sg.id}"]
backup_retention_period = "1"
backup_window           = "17:08-17:38"
storage_type            = "gp2"
maintenance_window      = "Sat:13:38-Sat:14:08"
multi_az                = false
apply_immediately       = true
publicly_accessible     = true
}
resource "aws_db_subnet_group" "default" {
name = "default-${aws_vpc.vpc_main.id}"
description = "Our main group of subnets"
subnet_ids = ["${aws_subnet.vpc_main-public-subnet1.id}","${aws_subnet.vpc_main-public-subnet2.id}"]
}

s3の作成

ポリシーを設定する場合は外部に用意したポリシーのjsonを読み込ませて設定してみました。

resource "aws_s3_bucket" "b" {
bucket = "${var.s3_bucket_name}"
acl = "private"
policy = "${file("s3_bucket_policy.json")}"
}

elbの作成

# Create a new load balancer
resource "aws_elb" "default" {
name = "${var.elb_name}"
security_groups = ["${aws_security_group.lb_sg.id}"]
subnets = ["${aws_subnet.vpc_main-public-subnet1.id}","${aws_subnet.vpc_main-public-subnet2.id}"]
listener {
instance_port = 80
instance_protocol = "http"
lb_port = 80
lb_protocol = "http"
}
health_check {
healthy_threshold = 2
unhealthy_threshold = 2
timeout = 5
target = "HTTP:80/index.html"
interval = 30
}
}

dynamoDBのテーブル作成

range_keyがソートキーになります

resource "aws_dynamodb_table" "users" {
name = "${var.dynamodb_prefix_name}.users"
read_capacity = 1
write_capacity = 1
hash_key = "id"
range_key = "username"
attribute {
name = "id"
type = "N"
}
attribute {
name = "username"
type = "S"
}
}

Nが数値、Sが文字列、Bがバイナリです。

elasticacheの作成

redisを作成してみた

resource "aws_elasticache_cluster" "redis" {
cluster_id = "${var.cache_cluster_name}"
engine = "redis"
node_type = "${var.cache_instane_type}"
port = 6379
num_cache_nodes = 1
parameter_group_name = "${var.cache_parameter_group}"
security_group_ids = ["${aws_security_group.redis_sg.id}"]
subnet_group_name = "${aws_elasticache_subnet_group.subnet.id}"
}
resource "aws_elasticache_subnet_group" "subnet" {
name = "${var.cache_cluster_name}"
description = "${var.cache_cluster_name}"
subnet_ids = ["${aws_subnet.vpc_main-public-subnet1.id}","${aws_subnet.vpc_main-public-subnet2.id}"]
}

IAMロールの作成

いろいろ躓きました・・・。
IAMロールの作成の場合、

  • ロールの作成
  • 管理ポリシーをアタッチ
  • インラインポリシーをアタッチ

という形なのですが、terraformではポリシーのリソース作成でIMAロールを指定します。
また、IAMロール作成時、「信頼されたエンティティ」のID プロバイダーとして
amazonを追加しないと使えないところがはまりました。(普段意識していなかったので・・・)

IAMロールの作成。
信頼されたエンティティにec2.amazonaws.comを設定しています。

resource "aws_iam_role" "sample_role" {
name = "${var.sample_iam_role_name}"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}

作成したロールに管理ポリシーをアタッチ
管理ポリシーの場合はポリシーarnを直接記述します。

resource "aws_iam_policy_attachment" "dynamodb" {
name = "AmazonDynamoDBFullAccess"
roles = ["${aws_iam_role.sample_role.id}"]
policy_arn = "arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess"
}

作成したロールにインラインポリシーをアタッチ
インラインポリシーの場合はpolicyのjsonを直接記述します

resource "aws_iam_role_policy" "sqs_policy" {
name = "AmazonSQSFullAccess"
role = "${aws_iam_role.sample_role.id}"
policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sqs:*"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}

EC2インスタンスの作成

作成したIAMロールを関連付けたインスタンスを作成します。
ついでに固定IPもつけてみました。
インスタンスに関連づける場合は「aws_iam_instance_profile」でまず
インスタンスプロフィールを作成する必要があるようです。

こちらのリソースで作成したnameの値をaws_instanceのiam_instance_profileで指定すれば
うまくいきました。

resource "aws_iam_instance_profile" "sample_role" {
name = "sample_role"
roles = ["${aws_iam_role.sample_role.name}"]
}
resource "aws_instance" "sample" {
ami = "${var.sample_ami}"
instance_type = "t2.micro"
vpc_security_group_ids = ["${aws_security_group.sample_sg.id}"]
subnet_id = "${aws_subnet.vpc_main-public-subnet1.id}"
iam_instance_profile = "sample_role"
associate_public_ip_address = true
key_name = "${var.key_pair_name}"
tags {
Name = "${var.sample_tag_name}"
}
root_block_device {
delete_on_termination = "true"
}
}
# 固定IPもつけてみる
resource "aws_eip" "lb" {
instance = "${aws_instance.sample.id}"
vpc      = true
}

AutoScalingの設定

こちらはあまりうまくいってないので使ってないですが、うまくいかなかったりうまくいったりします。

resource "aws_launch_configuration" "as_conf" {
name_prefix = "${var.launch_config_name}"
image_id = "${var.sample_ami}"
instance_type = "t2.micro"
iam_instance_profile = "api_role"
security_groups = ["${aws_security_group.sample_sg.id}"]
key_name = "${var.key_pair_name}"
associate_public_ip_address = true
lifecycle {
create_before_destroy = true
}
root_block_device {
volume_type = "gp2"
volume_size = "24"
delete_on_termination = true
}
}
resource "aws_autoscaling_group" "sample" {
name = "${var.auto_scaling_group_name}"
vpc_zone_identifier = ["${aws_subnet.vpc_main-public-subnet1.id}","${aws_subnet.vpc_main-public-subnet2.id}"]
launch_configuration = "${aws_launch_configuration.as_conf.name}"
max_size = 0
min_size = 0
desired_capacity = 0
default_cooldown = 300
health_check_grace_period = 300
health_check_type = "EC2"
load_balancers = ["${aws_elb.default.name}"]
force_delete = true
tag {
key = "Name"
value = "${var.api_tag_name}"
propagate_at_launch = true
}
}

root_block_deviceやelb_block_deviceを使うと以下のようなエラーが出てしまう為、現在はあまり使用していません。

Error refreshing state: 1 error(s) occurred:
* aws_launch_configuration.as_conf: Invalid address to set: []string{"root_block_device", "0", "encrypted"}

こちら、お分かりにな方がいたらご教授頂きたいです・・・

怖いところ

「割と軽微な修正」だと思って修正すると
インスタンスが削除されたりする可能性があります。

例えばインスタンスのIAMロールを変更はインスタンスの立ち上げ時にしか変更できないのですが
terraformでIAMロールを変更してterraform applyすると、インスタンスは削除されて新規作成される、、
という動作となります。

これはどう考えてもterraformが正しい動作をしているのですが
本番環境などでterraform applyを実行するのは怖すぎるな・・・と思いました。

個人的なTerraformの使いどころ

おなじような機能にAWS公式サービスのCloudFormationがありますが、
構成を管理してしまうところが嫌だと感じていました。

つまり初回構築だけ実行したい、、、という事ができないわけです。
もちろんTerraformにも構成管理をする機能はありますが
プロバイダ非依存である為、このあたりは柔軟に対応できます。

あと設定ファイルがjsonではないのでコメントなども書けるのもいいですね。
InteliJにはterraform用のプラグインもありますので
設定ファイル作成もしやすいです。

というわけで個人的には
「初期構築用ツール」、もしくは「開発環境構築ツール」として利用していこうと思います。
destroyでサクッと全部消えるのはとても便利

逆に構成を管理させたい場合は
WEB GUIにて変数をフォームで渡して実行、、って事ができるので
やはりCloudFormationの方がいいかなぁと感じています。
(AutoScaleはCloudFormationを使う事が多いです)

© SEEDS Co.,Ltd.