カテゴリー: インフラ Page 1 of 3

弊社コーポレートサイトリニューアルとその裏側

お久しぶりとなってしまいました。原口です。

10/1に弊社コーポレートサイトがリニューアルされました。

www.seeds-std.co.jp

今回、社長より「技術的な部分は自由にやっていいよ」という事でしたので自由にやらせていただきましたので、
このサイトにおける技術的な部分にフォーカスして紹介をしていきたいと思います。

システムコンセプト

通常コーポレートサイトといいますとwordpressなどのCMSの導入をする事がほとんどです。
弊社でもこれまではwordpressを利用していましたが運用するうちに気づいた事として管理が非常に面倒という点がありました。
サイトの更新よりもwordpressのアップグレード回数の方が多かった・・・となる月もザラにあり、
更新の簡単さと差し引いても目に見えにくい人的コストが高い状態は否めませんでした。

そのため、リニューアルにあたっては早いうちから、管理コストの少ない構成とする事は決めていました。
できる限り静的ファイルのみでの公開をし、お問い合わせフォームなどの、どうしてもプログラムの必要な部分は
サーバーレスアーキテクチャを採用するという方針を決定致しました。

バックエンド

インフラ構成は以下の通りです

f:id:cs_sonar:20181009211159p:plain

サイトの大部分はs3で公開されています。
デプロイは開発者のローカル環境より

npm run prod

といったビルドコマンドでs3に同期するような仕組みです。

大部分はs3で公開されている為、s3は静的ホスティングとしています。
前段にcloudfrontをかませていますが、これは独自ドメインにてSSLを使いたかったが為に使用しています。
そのためキャッシュ及びネガティブキャッシュのTTLは0です。
SSLはACMで取得しました。

唯一お問い合わせフォームがシステムに絡む部分となりますので
こちらはAPI gatewayとlambdaを利用しています。
この部分は一括して、リードプログラマの川勝さんに担当していただけました。

lambdaはvalidateと、SMTP-AUTHにて別のサーバーにメールリレーするだけの簡単なプログラムとなっています。
サーバレスといいつつ、唯一メールサーバーのみ弊社データセンタを利用していますが、
こちらも本来はAWS SESを利用しても問題はないかと思います。

この構成で約10日間ほど運用していますが現在で総額 $1.5ほどの費用となります。
TOPページに動画を使っている関係かこのうちの$1.2は転送量(cloudfront)です。
このままでおおよそ$5/monthほどのコストで運用できるのではないでしょうか。

この低コストに加えて、サーバー機の保守やOS、ミドルウェア、アプリケーションの脆弱性など、
まったく考えなくてもよいという見えない人的コストも大きく削減されています。

フロントエンド

フロントエンド側では弊社フロントチームに新卒で参加した寺澤さんがコーディングから実装までほぼすべてを行いました。
モバイルファーストを考え、スマフォの際はできるだけ転送量がかからないようjsやcssをminifyを行ったり、モバイル端末の際はTOP動画をgifアニメにするなどの変更を行っています。PC側の動画の圧縮なども作業いただきました。

静的ファイルを生成するという構成上、テンプレートエンジンの利用は当初より考えていてPugを使用しています。
ビルドはgulpを利用していて、ビルド時のコマンドで生成したhtml、css、画像、jsをs3へ持っていく形としています。

jsフレームワークはVue.jsです。スライダーやページの動きはこちらで実装し、一部ページ(newsや制作実績、お問い合わせページ)ではSPAっぽいもので構成しています。

1点、起こった問題として、s3静的サイトホスティングではmod_rewriteのようにURLの書き換えができない点がありました。
リダイレクトは可能なのですがリライトはできないのですね・・・。
前述の通り一部ページはSPAでの実装を行っていたのですが、
s3での運用に際し、この点はURLが少し不格好となってしまいますがクエリストリングで対応するようにしました。

デザイン

昨年、弊社に入社されたデザイナーの河野さんが全て取り仕切って行ってくれました。
モバイルファーストと呼ばれるこの時代・・・レスポンシブデザインをベースに
スマフォでいかに見やすいか、という点を注意してデザインいただけました。

TOPにて背景動画を流したり、遊び心のあるイラスト(河野さんの自作!)など、素敵なデザインになったと思います。
イラストの一部にシーズ社員数名が紛れ込んでいるので、是非探してみてください。

終わりに

今回の構成では今後できるだけ手間をかけない、可能な限り今後の運用が低コストとなるよう考えて制作しました。
通常CMSと考えるとwordpressやサーバー、そしてデータベースの準備などなど、、、様々な事がデファクトスタンダードとして存在しますが、
規模に対して過剰な設備であったり、本来注視すべきコンテンツ以上にこれらの対応で時間が取られる事もしばしばあります。
本当にそこまで必要なのか?本当にそれらを準備しないと目的が達成できないのか?
という観点は通常のシステム開発でもとても重要なものであると改めて思いました。

同様の事例でお困りの事があれば是非、ご相談下さい!

社内WindowsサーバーをAWSに移行する話1

経緯

シーズでは、見積書・請求書の発行などに社内にWindowsサーバーを立て弥生販売を使っていました。

複数拠点から複数人が同時に使うためネットワーク版5ライセンスです。

社内にサーバーを置くメリットとして、

*ギガビットLANでの高速アクセス

*社外との通信が発生しないためセキュリティーが高い

*売るほどサーバーラックがあるのでコストはあまり気にならない

といった利点がありましたが
サーバー筐体費用、Windowsサーバー、SQLライセンスなどの初期費用(トータル約40万※構築費用は自前のため無料)がかかるうえ
毎年のライセンス更新や筐体保守費用、また毎月の電気代・空調費用も馬鹿になりません。
さらに、場所も取るし、空調にも気を使うし、なにかとメンテも大変なのでクラウドに行きたいと考えておりました。

要件

*AWSで24時間365日稼働(ただし夜間は止めるかも)

*OSはWindowsサーバー2012

*弥生販売ネットワーク版5ライセンスを動かす

*シーズ社内とVPNでセキュアに常時接続

特に最後のVPNで社内とシームレスかつセキュアに接続が一番重要ですね。

これらの要件をAWSでこのように準備しました。

AWS構成

*ec2インスタンス(Windows2012) t2.small $36.60

*EBS45GB $5.40

*VPC VPN接続 $36

合計 約78ドル=約8,424円 (1ドル108円)

f:id:panmizser:20161121214003p:plain

EBSボリューム、転送量、EIP、IOなど少額の従量制がありますが、それらを含んだとして、約8,500円です。

移行してどうだったか

結論としては、最高でした。

まず費用としては

サーバー1台をデータセンターでハウジング(月50,000相当,OSライセンス諸々込)として、それが月8,500円ポッキリになりました。(83%DOWNです!)

また弥生を使わない夜間止めることで更にコストダウンも可能です。

次に、回線速度やアプリの使用感ですが、こちらも全く問題ありません。

インスタンスタイプは最初 large → medium と様子を見ていきましたが最終的にはsmallまで落としても操作感に問題ありませんでした。

(ただし弥生インストール時はlargeでやったほうが効率がいいです)

回線速度は、AWSの提供するVPNサービスを利用することで全くストレスを感じません。
(以前、WindowsインスタンスからPPTPで会社ルーターに繋げたりしたことがありましたが、その場合、一応つながりますが、結構遅かったです。)

次回は、これらAWSの設定、社内VPNルーターの設定を含め詳しく解説していきたいと思います。

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を使う事が多いです)

wifiやbluetoothってなに?

今やIT業界だけでなく、日常生活でも普通に使われているwifibluetooth
一体これらが何なのか、物理学の博士号を取り損ねた僕が、知っている範囲で説明したいと思います。

wifibluetoothとは電磁波です

これらの正体は、電磁波と呼ばれるものです。
人体に悪影響があるとかないとかよく言われている、あの電磁波です。
ちなみに、太陽や蛍光灯の光も電磁波なのですが、中でも、蛍光灯は白かったり赤かったり青かったり、
様々な色がありますよね。
これは何が違うのか?というと、波長や周波数が違うため、違った色合いで見えるのです。

可視光

一般的に「光」と呼ばれているのは、私たちが見ることのできる
波長が約380nm〜780nm(1nm=0.000000001m)の電磁波のことを指します。
これを「可視光」と言います。
この範囲以外の電磁波は、私たち人間には見ることができません。

赤い光はだいたい780nm、青い光はだいたい400nmです。
この波長は、とても短いです。

マイクロ波

一方、wifibluetoothは「マイクロ波」と呼ばれていて、
波長が約100μm〜1m(1μm=0.000001m)の電磁波のことを言います。
あと、電子レンジで使っている電磁波もマイクロ波です。
こちらの波長は、可視光に比べると断然長いです。

電磁波の特徴

電磁波は、波長が長ければ長い程、壁などの障害物に邪魔されずに透過します。

みなさん、壁の向こうって見えないですよね?僕にはそんな能力はないです。
これは、光の波長が短すぎて、壁をすり抜けられずに反射したり吸収されたりするからです。
私たちが見ることができる「可視光」の電磁波も、同じ理由で障害物をすり抜けられません。

では、マイクロ波はどうでしょうか?
先ほど説明した通り、wifibluetoothは波長が長いため、違う部屋にwifiルーターを置いていても、
壁をすり抜けてパソコンやスマホwifibluetoothの電磁波を受信することができます。
つまり、パソコンやスマホで、壁の向こうのwifiを透視しているのです。
私たちも、もしwifiなどの電磁波を見ることができたら、壁の向こうを透視できるかもしれません!

最後に

いかがでしたか?普段何気なく使っているwifibluetoothですが、仕組みが分かると面白いですね。
書きたいことはもっとあるのですが、次回のお楽しみということで、今回はこれで終わります。

td-agentにてAWSのRDS(postgres)のログをTDに送信

AWSのRDSはLinuxサーバではない為、こちらからいろいろな設定ができません。

その為、どのようにPostgresのログを送信すればよいか、いろいろ試行錯誤したのでその備忘録です。
前提として、RDSに接続できるLinuxインスタンスにtd-agentが入っている状態が必要です。

fluent-plugin-rds-pgsql-log

https://github.com/shinsaka/fluent-plugin-rds-pgsql-log
RDSのpostgresのログを取得するには上記のプラグインを利用します。

このプラグインはAWS RDSのログを取得し、td-agentのsourceとして利用できるようにするプラグインです。

ただ導入にあたり、依存関係に「AWS SDK」があるのですが、パッケージで導入したtd-agentでは
fluentd-gem fluent-plugin-rds-pgsql-log にて導入ができませんので、以下のように導入します。

まず、td-agentに入っているrubyからaws-sdkをインストール

/usr/lib64/fluent/ruby/bin/gem install aws-sdk

fluent-plugin-rds-pgsql-logのプラグインを直接プラグインディレクトリにほおりこむ

wget https://raw.githubusercontent.com/shinsaka/fluent-plugin-rds-pgsql-log/master/lib/fluent/plugin/in_rds_pgsql_log.rb -P /etc/td-agent/plugin

これでpostgresのログをTDに送信できました。

<match td.postgres.log>
type tdlog
apikey *****************************
auto_create_table
database postgres
table log
buffer_type file
buffer_path /var/log/td-agent/buffer/td
flush_interval 180s
</match>
<source>
  type rds_pgsql_log
  region ap-northeast-1
  db_instance_identifier prod-auth
  access_key_id ******************
  secret_access_key *********************************
  refresh_interval  30
  tag td.postgres.log
  pos_file /var/log/td-agent/pgsql-prod-auth-log-pos.dat
</source>

こんな感じのデータが送信されます

{"message_level"=>"LOG", "time"=>"1436510427", "ident"=>"postgres", "host"=>"10.0.0.21(22112)", "database"=>"[unknown]", "user"=>"[unknown]", "pid"=>"6080", "log_file_name"=>"error/postgresql.log.2015-07-10-00", "message"=>"  connection received: host=10.0.0.21 port=53253"}

host部分がSQL発行元IPになる

管理の上ではhost部分はRDSの名前になっているのが望ましいので、host部分を強制的に変更します。
データをリフォームする為に「fluent-plugin-record-reformer」というプラグインを利用しました。

https://github.com/sonots/fluent-plugin-record-reformer

wget https://raw.githubusercontent.com/sonots/fluent-plugin-record-reformer/master/lib/fluent/plugin/out_record_reformer.rb -P /etc/td-agent/plugin

最終的なtd-agent.confは以下のようになりました。

<match td.postgres.log>
type record_reformer
renew_record false
enable_ruby false
tag reformed.td.postgres.log
# 強制的に文言を変更する
host rds-postgres01
ident postgres
</match>
<match reformed.td.postgres.log>
  type tdlog
  apikey *****************************
  auto_create_table
  database postgres
  table log
  buffer_type file
  buffer_path /var/log/td-agent/buffer/td
flush_interval 180s
</match>
<source>
  type rds_pgsql_log
  region ap-northeast-1
  db_instance_identifier prod-auth
  access_key_id ******************
  secret_access_key *********************************
  refresh_interval  30
  tag td.postgres.log
  pos_file /var/log/td-agent/pgsql-prod-auth-log-pos.dat
</source>

mysqlのバックアップ(mysqldump)のロック問題

こんにちは、はらぐちです。

今回お話したいのは、mysqlのバックアップ方法についてのあれこれです。

バックアップ mysqldump

mysqlのバックアップといえばmysqldumpです。
以下のような形で使います。

mysqldump -u root -p -x -A > my_dumpall.db

これで全データベースのダンプができます。
特定のデータベースをダンプしたい場合は、以下のようにデータベース名を指定します。

mysqldump -u root -p -x データベース名 > dump.sql

定期的にバックアップを取りたい場合は、シェルスクリプトで以下のようなものを
cronで実行してあげるといいでしょう。
二日間のバックアップを保持するスクリプト例です。

#!/bin/bash
MPASS=パスワード
mysqldump --defaults-extra-file=<(printf '[mysqldump]\npassword=%s\n' ${MPASS}) -u root -x -A > my_dumpall_`date +%Y%m%d`.db
OLDDATE=`date "-d2 days ago" +%Y%m%d`
rmfile=my_dumpall_$OLDDATE.db
if [ -e $rmfile ]; then
sleep 5m
rm -f $rmfile
fi

ちなみにリストアは以下のような形です。

mysql -u root -p < dump.sql
mysql -u root -p データベース名 < dump.sql

バックアップ datadirのコピー

mysqlのdatadirをOS上でコピーしてしまえばバックアップになります。

この方法の最大の利点は、リストアが非常に高速な事です。
通常のリストアはダンプファイル(SQL)をmysqlに渡す事で処理をするのでIndexの再作成などの処理もかかり、
mysqlのcpuがボトルネックとなってきますが、datadirによるリストアはdatadirのファイルを
そのまま置き換えてあげるだけですので、簡単かつ高速です。

しかし、同様に整合性の問題がありますので、mysqlを停止してからでなければいけないのが欠点です。
停止となるので完全に止まってしまいます。また、データベース単位でのバックアップなどはできません。

このバックアップ方法をとるのはサーバー移行などの高速なリストアが必要な時です。

mysqldumpのロック問題

mysqldumpコマンドのオプションで「-x」というものがありますが、
これはデータベースのすべてのテーブルをリードロックする、というものです。
データベースをリードロックすると参照系のクエリは発行できますが、
更新系のクエリはロックが解除されるまで待ち状態となります。
これはデータベースの整合性を保つ為に必要です。

しかし、サービス運用中ですと、この「更新系が待ち」になる事が許容できない事があります。
たかだか1Gくらいのデータベースですと、ミッションクリティカルなサーバーでない限り
ロックがかかる時間も少ないのでまだ許容できるかもしれませんが、
これが20GBほどのサイズとなると、30分ほどかかったりする事がざらにあります。

セッションをDBで管理してたりすると、すべてのユーザーがサービスを利用できなくなるというわけです。

ロック問題の解決策 single-transaction

データベースがすべて「innodb」であればシングルトランザクションオプションを使ったダンプを行う事で
ロックせずにバックアップする事ができます。

mysqldump -u root -p --single-transaction -A > my_dumpall.db

内部的にはスナップショットをとって、そのデータをダンプする事でロックする事なく
整合性のとれたダンプを取る事が可能です。
通常のmysqldumpと異なる点は、ダンプデータが「ダンプが終了した時の状態」ではなく
「ダンプを開始した時の状態」であるという点です。

ちなみに–master-data=2を付けるとbinlogファイルと位置の情報をdumpに含めてくれるので
レプリケーションslaveを作る際に大変重宝します。
この方法の最大の欠点はすべてのデータがinnodbである必要がある点です。
myisamなどのテーブルは整合性が取れなくなる可能性があります。

ロック問題の解決策
レプリケーションslaveにてdump

サーバーがもう一台必要ですがレプリケーションしたslaveサーバーにてバックアップを行うと、
ロックをかけてもサービスには影響を与えません。

ロック問題の解決策
LVMのスナップショットでdatadirをコピー

こちらは実際には行った事がないのですが、LVMのスナップショットで
datadirのスナップショットを取ってしまえばmysqlの停止なくバックアップ可能です。
ですが、スナップショット作成中は非常に負荷が高くなるそうで、
該当時間の書き込み処理の性能がガタ落ちするそうです。

一番いいのはsingle-transaction

日々のバックアップ用途であれば今までの運用の経験から「single-transaction」オプションでの
mysqldumpが一番よかったかなぁと思います。
これからサーバーでmysqlを使う時は、意識的に「InnoDBを使う」という事を心がけていくといいと思います。

qmailの大量キューを送信元や送信先でフィルターして削除する方法

qmail!?って思われるかもしれないですが、勉強会とかいくと結構現役なサーバって多いみたいです。

以前、qmailの大量キューをすべて削除するという記事でqmailのキューをすべて削除する方法を書きましたが、実際の運用では削除すべき大量のメールキューの中に、削除してはいけないキューがあったりします。
今回はqmailの大量キューを送信元や送信先でフィルターして削除する方法を検証しました。

ツール類の準備

下記2つのキュー操作ツールを利用します。
両方サーバーに入れます。
両方、perlのスクリプトなので任意の場所において実行権限を付与したりパスを通したりして下さい。

・qmHandle
http://qmhandle.sourceforge.net/

tar zxvf qmhandle-1.3.2.tar.gz

・queue-fast
http://www.kawa.net/works/qmail/queue-fast.html

wget http://www.kawa.net/works/qmail/src/queue-fast.20050210.tar.gz
tar zxvf queue-fast.20050210.tar.gz
chmod 755 queue-fast.pl

送信先メールアドレスでフィルターして削除

queue-fastのみで対応可能です。

キューの確認

perl queue-fast.pl

user@domain.name宛のキューを確認

perl queue-fast.pl user@domain.name

user@domain.name宛のキューを削除
キューの削除の際はqmailを落としてください。(キューの整合性が取れなくなる可能性がある為)

perl queue-fast.pl -d user@domain.name

送信元メールアドレスでフィルターして削除

queue-fastとqmHandleを組み合わせて使います。
※厳密には送信元だけではなく送信先に含まれているものも削除されます

user@domain.nameというアドレスが関連したキューをすべて削除したい場合。
まず、queue-fastでuser@domain.nameに関連したキューのIDを一覧します。

perl queue-fast.pl | grep user@domain.name | cut -d" " -f1 | cut -d"/" -f2

qmHandle の引数にキューIDを渡してあげるとそのキューIDのキューを削除してくれます。

perl qmHandle -d22222

便利に使う為、適当なスクリプトを作成

qmailQueueDelete.sh

!/bin/sh
if [ $# -ne 1 ]; then
echo "\n"
echo "qmailQueueDelete.sh \n"
echo "usage : ./qmailQueueDelete.sh test@example.com \n"
exit 1
fi
QID=`perl queue-fast.pl | grep $1 | cut -d" " -f1 | cut -d"/" -f2`
for var in ${QID}
do
if [ ! -z "$var" ] ; then
perl qmHandle -d${var}
fi
done

qmailを停止してから、

./qmailQueueDelete.sh user@domain.name

で削除が実行されます。

chef12になって今までのcookbookが動かなくなった

chefでサーバーの初期構築をする時、以下のコマンドでchefをインストールして

curl -L https://www.opscode.com/chef/install.sh | bash

その後、ソースをgitから持ってきてchef-soloを実行してました

chef-solo -c solo.rb -j localhost.json

ある時、chefをインストールしたらバージョン12になって、以下のようなエラーが出るようになりました。

ERROR: Cookbook loaded at path(s) [/tmp/vagrant-chef-1/chef-solo-1/cookbooks/xxxxxx] has invalid metadata: The `name' attribute is required in cookbook metadata

とか

ERROR: Cookbook hogehoge not found. If you're loading hogehoge from another cookbook, make sure you configure the dependency in your metadata

とか。。

どうやらchef 12 から cookbookのmetadata.rb内のname attributeの記述が必須になったり、重要視されるようになったようです。
具体的に上側のエラーはmetadata.rbにnameの記述がない、というエラー。
下側のエラーはmetadata.rbのnameに書かれてる名前が実際のレシピと異なる場合に出てるみたいです。
(例えばディレクトリ名がhogeなのに name が fuga と設定されていてrecipe[hoge]と呼び出した場合に上記のエラーになります)

上記のようなエラーが出た場合は
cookbookディレクトリ内にあるmetadata.rbのname部分を見直してみてください。

name             'hoge'  ←ここが重要!
maintainer       'YOUR_COMPANY_NAME'
maintainer_email 'YOUR_EMAIL'
license          'All rights reserved'
description      'Installs/Configures hoge'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version          '0.1.0'

AWS EC2 のインスタンスにvagrant + Jenkins + chef-solo + serverspec を入れてインフラCIする

最近インフラの話題が熱いです。
chefを使ってインフラ構築がコード化(Infrastructure as Code)ができるようになった事でプログラムソースと同じく、サーバーの構築手順などもコードとしてgitなどで管理できるようになりました。

そうするとプログラマたちがJenkins等のCIツール(継続的インテグレーション)で自動テストしているのもやりたくなってきます。
インフラのCIにおいてVagrantやserverspecといったツールがこれらのCI環境の整備を後押しした事もあり、インフラCIの手順などの記事も増えて、とてもワクワクしています。

(参考)
Vagrant + Chef Solo + serverspec + Jenkins でサーバー構築を CI

CIする所をサーバーにしたい

いろんな記事では基本的にMacOSXにjenkins vagrant virtualbox chef を入れての作業になってます。
また、vagrant-awsなどを使用してもそれは同じで、あくまでもローカルのMACからawsを操作する内容になっています。

インフラがコード化されて、gitで管理されたとなるとやっぱり共同で開発していきたいと思うのですが、インフラCIの為にMAC/Windowsのそれぞれのクライアントに「vagrant」「jenkins」「chef」・・・ と入れてもらうのは敷居が高いしちょっとなんだかなぁと思ってました。
そこで、今回はAWSのEC2インスタンスにvagrant + Jenkins + chef-solo + serverspecを入れてCIする環境を構築したいと思います。

ツールのおさらい

vagrant
仮想サーバーの構築や破棄をコマンドで行える仮想環境構築ツール。CIする上で一番うれしいのは立ち上げたサーバへのSSHなどの接続まわりのすべてを面倒みてくれるところかな、と思います。

chef
サーバー構築の自動化フレームワークツールです。「冪等性を保証する」というポリシーで作成します。冪等性とは1回行っても複数回行っても結果が常に同じであるような事をいいます。冪等性を保証する事で、単なるサーバー構築を行うツールではなく、何度実行してもサーバーが設定どおりの状態となる事を保障できるツールとなります。

serverspec
サーバにApacheが入っているか、80ポートが空いているか、など、意図した通りにサーバが稼働しているかどうかをチェックできるツールです。

jenkins
CI(継続的インテグレーション)ツールです。jenkinsを使う事で「テスト」や「ビルド」、はては「デプロイ」まで自動化する事ができます。

ゴール

・gitのリポジトリの特定ブランチにpushがあると
・jenkinsが捕捉して、ビルドを実行
・vagrantにてEC2インスタンスを立ち上げてchefを流してサーバー構築
・サーバー構築が完了するとserverspecでサーバー状態をチェック
・結果をjenkinsで管理

jenkinsサーバーの構築

ともあれ、CIを行う為のサーバーが必要なのでEC2でインスタンスを立ち上げます。
今回はCentOSを使いました。
こちらのインスタンスにvagrant、Jenkins、chef-solo、serverspecをインストールしていきます。

EPELリポジトリを追加

wget http://ftp.riken.jp/Linux/fedora/epel/6/x86_64/epel-release-6-8.noarch.rpm
rpm -ivh epel-release-6-8.noarch.rpm

パッケージをインストール。いらないものもあると思うので適当に割愛してください

yum install -y make gcc gcc-c++ xinetd openssl-devel libcurl-devel zlib-devel readline-devel bzip2-devel curl-devel libmcrypt libmcrypt-devel sudo redhat-lsb yum-utils yum-plugin-priorities yum-plugin-fastestmirror yum-plugin-security
yum install -y mlocate bind-utils dstat elinks multitail nc nmap rsync traceroute tree unzip wget which zip zsh mosh uuid telnet
yum install -y autoconf automake bison bzip2 gettext-devel libtool libxml2-devel libxslt-devel libyaml-devel libffi-devel ncurses-devel patch
yum install -y git iftop tig python-pip rpm-build ImageMagick ImageMagick-devel jq
yum install -y java-1.7.0-openjdk java-1.7.0-openjdk-devel

AWS CLIのインストール(任意)

pip install awscli

chefのインストールとchefで入ったrubyにpathを通します

curl -L https://www.opscode.com/chef/install.sh | bash
echo "export PATH=\"/opt/chef/embedded/bin:\$PATH\"" &gt; /etc/profile.d/chef_embedded.sh
source /etc/profile.d/chef_embedded.sh

chefで入ったgemでserverspecなどをインストール

/opt/chef/embedded/bin/gem install rake --no-ri --no-rdoc
/opt/chef/embedded/bin/gem install serverspec --no-ri --no-rdoc
/opt/chef/embedded/bin/gem install ci_reporter --no-ri --no-rdoc
/opt/chef/embedded/bin/gem install unf --no-ri --no-rdoc

vagrantのインストール

wget https://dl.bintray.com/mitchellh/vagrant/vagrant_1.6.5_x86_64.rpm
rpm -i vagrant_1.6.5_x86_64.rpm
vagrant plugin install vagrant-aws
vagrant plugin install unf
vagrant plugin install vagrant-serverspec
vagrant plugin install vagrant-global-status
vagrant box add dummy https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box

jenkinsのインストール

sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
sudo rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
sudo yum -y install jenkins

各種設定

以下のようなディレクトリ構成とする事にします

vagrant/
Vagrantfile
spec/
spec_helper.rb
hogehoge/
httpd_spec.rb
chef/cookbooksとかいろいろ

chefのレシピを配置

テストしたいchefのレシピたちを配置します。割愛

serverspecの設定

spec_helper.rbはこんな感じです

require 'serverspec'
require 'pathname'
require 'net/ssh'
include SpecInfra::Helper::Ssh
include SpecInfra::Helper::DetectOS

httpd_spec.rbはこんな感じです
とりあえずポート80が空いてるかどうかのテスト

require '../spec_helper'
describe port(80) do
it { should be_listening }
end

vagrantの設定

mkdir /path/to/vagrant
cd /path/to/vagrant
vi Vagrantfile

以下はサンプル。
vagrant-awsの設定とprovisionとして「シェルの実行でchefをインストール」「chef-soloの実行」「serverspecの実行」を行っています。

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
# usage vagrant up --provider=aws
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "dummy"
config.vm.provider :aws do |aws, override|
aws.access_key_id = 'xxxxxxxxxxxxx'
aws.secret_access_key = 'xxxxxxxxxxxxx'
aws.region = 'ap-northeast-1'
aws.instance_type = 't2.micro'
aws.ami = 'ami-xxxxx'
aws.security_groups = ['sg-xxxxxx','sg-xxxxx']
aws.keypair_name = 'xxxx'
aws.ssh_host_attribute = :private_ip_address
override.ssh.username = 'root'
override.ssh.private_key_path = '/path/to/key'
aws.subnet_id = 'subnet-xxxxxx'
aws.associate_public_ip = 'true'
aws.tags = { 'Name' =&gt; 'CI' }
end
# shell exec
config.vm.provision :shell, :path =&gt; "curl -L https://www.opscode.com/chef/install.sh | bash"
# chef exec
config.vm.provision "chef_solo" do |chef|
chef.cookbooks_path = ["../chef/cookbooks", "../chef/site-cookbooks"]
chef.roles_path = "../chef/roles"
chef.run_list = ["role[xxxxxxxx]"]
end
# serverspec
config.vm.provision :serverspec do |spec|
spec.pattern = '../spec/hogehgoe/*_spec.rb'
end
end

vagrant-awsにおけるAWSのアクセスキーや秘密鍵。セキュリティグループの設定等は割愛。

注意点としてprovision shellを実行する時にno ttyと出る時があります。
これは立ち上げるAMIの/etc/sudoers 内のDefaults requiretty行をコメントアウトすれば解決するかもしれません。
立ち上げるインスタンス用のAMIはこれらvagrantが接続してprovisionができる環境を整えたものであるといいかと思います。(例えばchefが入ってるAMIですとchefのインストールをvagrantから行う必要がなくなります)

vagrantで仮想サーバーが立ち上がり、chefをインストールし、chefを流し、serverspecでテストまで流れるかテストします。

vagrant up --provider=aws

もう一度chefとかを流したりしたい場合は以下のコマンドで実行できます。

vagrant provision

任意のprovisionのみ実行したいときは以下のような感じになります。

vagrant provision --provision-with serverspec

確認できたら消します。ちなみにAWSでは1分でも立ちあげると1時間ぶんの料金がかかりますので注意です。

vagrant destroy

jenkinsの設定

jenkinsからはvagrantコマンドを実行したりする予定ですが、root権限が必要なのでsudo設定等を行います

visudoで以下を追記

Defaults:jenkins !requiretty
jenkins ALL=(ALL) NOPASSWD:ALL

Defaults requirettyとかあったらコメントアウトしておきます。

jenkinsの起動

/etc/init.d/jenkins start

http://サーバーIP:8080 でjenkinsさんがみえます。

jenkinsにgitプラグインを入れる

[Jenkinsの管理]-> [プラグインの管理] -> [利用可能タグ]でGit Pluginをインストールします。

新規ジョブを作成

[新規ジョブ作成]->[フリースタイル・プロジェクトのビルド]を選択してプロジェクト名を適当に入力して作成します。

プロジェクトの設定(git)

作成したプロジェクトをクリックしてプロジェクトの画面に進み[設定]をクリックします。

ソースコードの管理でgitを選択して監視を行うリポジトリとブランチを設定します。
今回はmasterブランチです

プロジェクトの設定(ビルド・トリガ)

ビルド・トリガを設定します。
通常であればgitのリポジトリ側からpushがあった場合にjenkinsに通知する事でビルドが実行されるのがよいのですが、簡単な方法として、jenkins側から定期的にgitリポジトリに更新を確認しにいくポーリングを設定しました。

この設定ですと、10分に1回更新を確認しにいき、pushされていればビルドを実行する設定になります。

プロジェクトの設定(ビルド)

最後にビルド設定です。
ビルドは「シェルの実行」を選択します。
ビルド実行でvagrantを立ち上げ、テストして落とすという処理となりますので以下のようなコードとしました。
このときのシェルはjenkinsユーザーが実行するので適宜sudo等をつけてあげる必要があります。

cd /path/to/vagrant
sudo vagrant up || RET1=$?
sudo vagrant provision --provision-with serverspec &gt; /tmp/serverspec.tmp
cat /tmp/serverspec.tmp
SPEC=`cat /tmp/serverspec.tmp | grep "0 failures" | wc -l`
RET2=0
if [ $SPEC -ne 1 ]; then RET2=1 ; fi
rm -f /tmp/serverspec.tmp
sudo vagrant destroy
if [ $RET1 -ne 0 ]; then exit $RET1 ; fi
if [ $RET2 -ne 0 ]; then exit $RET2 ; fi
exit 0

途中ややこしい事になっていますがvagrant-serverspecプラグインでのserverspecの結果は成功/失敗によらず正常終了となるので、serverspecが失敗した場合はjenkinsでFailとなるようにすこし調整しています。

最終的にはこんな感じになりました。(gitの部分はエラーになってますが)

この設定でchefのレシピをCIできるようになりました。

Page 1 of 3

© SEEDS Co.,Ltd.