massの日記

日々の薪

年の瀬にJenkinsの訃報が届いても、悲嘆にくれないために #vgadvent2013

この記事はVOYAGE GROUP エンジニアブログ : Advent Calendar 2013 の11日目の記事になります。

adingoでDevOpsやっている@_zooです。
今年から、弊社が提供しているSSPサービスのFluctでは、AWSを利用した広告配信を開始しました。

AWSは手軽に構築できる分、いつインスタスの訃報が飛んできてもいいように準備をしておく必要があります。

今回は普段元気なJenkinsおじいちゃんが年の瀬にお亡くなりになった場合に、悲嘆にくれないためのお話です。

まずは、おじいちゃんの訃報に備えての対策

急な訃報で普段きないスーツやら、黒い靴やら、年末でぐでーと酒のんでいる時に探したくはないです。

  • Puppetによる構成管理
  • ServerSpecによるサーバの状態テスト
  • Jenkinsのジョブbackupをs3に定期的に同期

以下、JenkinsのPuppetとServerSpecのコードを抜粋します。

# Puppet

class jenkins::install {
  file {
    "/ebs1/jenkins":
      owner   => "jenkins",
      group   => "contentsuser",
      mode    => 755,
      ensure  => directory;

    "/ebs1/jenkins_backup":
      owner   => "jenkins",
      group   => "contentsuser",
      mode    => 755,
      ensure  => directory;
  }

  package {
    "jenkins":
      ensure  => installed,
      require => File["/var/lib/jenkins"];

    "java-1.6.0-openjdk":
      ensure => installed;

    "rubygem-bundler":
      ensure => installed;
  }
}

class jenkins::prepare( $jenkins_https_port = 8443,
                        $jenkins_cert_file,
                        $jenkins_key_file,
                        $jenkins_java_options ) {

  file {
      "/etc/pki/tls/certs/jenkins.ec2.crt":
        owner   => "jenkins",
        group   => "contentsuser",
        mode    => 440,
        source  => "puppet:///jenkins/etc/pki/tls/certs/dev.fluct.ec2.crt";

      "/etc/pki/tls/certs/jenkins.ec2.key":
        owner   => "jenkins",
        group   => "contentsuser",
        mode    => 400,
        source  => "puppet:///jenkins/etc/pki/tls/certs/dev.fluct.ec2.key";
    }
  }

  file {
    "/etc/sysconfig/jenkins":
      owner   => "root",
      group   => "root",
      mode    => 644,
      content => template("jenkins/etc/sysconfig/jenkins.erb");
  }
}

class jenkins( $jenkins_https_port   = 8443,
               $jenkins_cert_file    = "/etc/pki/tls/certs/jenkins.ec2.crt",
               $jenkins_key_file     = "/etc/pki/tls/certs/jenkins.ec2.key",
               $jenkins_java_options = "-Djava.awt.headless=true" ) {

  Class["jenkins::install"]
  -> Class["jenkins::prepare"]
  -> Class["jenkins"]

  include "jenkins::install"
  class {
    "jenkins::prepare":
      jenkins_https_port   => $jenkins_https_port,
      jenkins_cert_file    => $jenkins_cert_file,
      jenkins_key_file     => $jenkins_key_file,
      jenkins_java_options => $jenkins_java_options;
  }

  service {
    "jenkins":
      ensure  => running,
      enable  => true;
  }
}

# ServerSpec

require 'spec_helper'

describe package('jenkins') do
  it { should be_installed }
end

describe package('java-1.6.0-openjdk') do
  it { should be_installed }
end

describe package('rubygem-bundler') do
  it { should be_installed }
end

describe package('dejavu-sans-fonts') do
  it { should be_installed }
end

describe service('jenkins') do
  it { should be_enabled   }
  it { should be_running   }
end

describe port(8443) do
  it { should be_listening }
end

describe file('/etc/sysconfig/jenkins') do
  it { should be_file }
  it { should be_mode 644 }
  it { should be_owned_by 'root' }
  it { should be_grouped_into 'root' }
end

おじいちゃんは自分の健康状態を維持するため、このServerSpecを30分おきに実行しています。

さて、そんな日々の健康管理があっても急な病にかかることもあります。

そんな時に備えて、おじいちゃんは自分の設定ファイルをS3に置いておきます。
これでいつ倒れても、次のおじいちゃんが引き継げます。

JenkinsのバックアップはPluginで提供されているthinBackupで作成しています。
f:id:johnsmith0707:20131210224758p:plain

このバックアップそのものもJenkinsのジョブで取っています。
とはいっても、ワンライナーです。ぶっちゃけ、s3cmdのsync一行です。

s3cmd -c s3cfg sync /ebs1/jenkins_backup s3://jenkins-backup/

次に、おじいちゃんを殺してみます

やはりAWSとはいえ殺すというのは怖いですね、手が震えます。
インスタンスはCloudFormationで作成しているので、Stackを消せば終了です。

いざっ!

aws cloudformation delete-stack --stack-name JENKINS

やってしまいました。
ついカッとなって、やってしまった。怖いですね、これが人間というものです。

最後に、次のおじいちゃんを作ります

先ほど同様、CloudFormationを利用します。

aws cloudformation create-stack --stack-name JENKINS --template-body file://template_jenkins.json

次にできあがったインスタンスsshでログインしてPuppetをあてます。

sudo /etc/init.d/puppet start

次にデータをリストアします。

s3cmd -c s3cfg sync s3://jenkins-backup/ /ebs1/jenkins_backup

後はthinBackupのリストア画面から、リストアするだけです。
f:id:johnsmith0707:20131210224802p:plain

リストアしたら、元気なおじーちゃんが生まれました。良かったです。

かなりはしょりましたが、こんな感じで訃報がとんできても悲嘆にくれないようにしています。
ひよっこDevOpsエンジニアで、まだまだPuppetやServerSpecを使いこなせてないです。
そんなひよっこDevOpsエンジニアがいる弊社は絶賛DevOps系エンジニアを募集中です!

明日のアドベントカレンダー@tadasyさんです。お楽しみに!

AWSを利用してサービスの拡張環境を構築してみた

AWSを真面目に使ってみた

今月からAWSを本格的に業務で使い初めた。
環境構築の方針や導入した技術の振り返りもかねて、まとめてみた。

自分の立ち位置

昨年からアドネットワークサービスの開発業務に携わっているひよっこDevOpsエンジニア。
昨年の主な業務はデプロイシステム改善、今年の初夏からの主な業務は配信システムのインフラ部分やサーバ構成の改善。
インフラ部分は以前から興味があったので、ジェダイの騎士達(弊社インフラチームを心の中で勝手にこう呼んでいる)に教えをこいつつ日々勉強。

AWSを利用することになった背景

広告配信システムのうち、配信サーバの増設コストが課題となっていたため、より柔軟にスケールアップ・スケールダウン/スケールアウト・スケールインができる環境としてAWSを利用することにした。

利用したサービス・ツール

CI

監視

設計の方針

AWS環境の構築するにあたって、ご近所さんに相談したり、勉強会に参加して、環境構築の方針をざっくり決めた。
まず、環境構築を自動化する。
自動化として、Bootstrapping、Configuration、Orchestration、の三段階にわける。
Bootstrapingは環境構築の自動化、Configurationはサーバ構築の自動化、Orchestrationはアプリケーションのリリース作業の自動化、とそれぞれ自動化する部分での役割を分担させることにした。

Bootstrapingは何を行うか?

BootstrapingではCloudFormationとAWS CLIを利用し、JSONファイルを定義ファイルとし再現性のある環境構築を行う。
配置場所(VPC)、配置物(ELB、EC2)、出入口(SecurityGroup)、を作成する役割を持つ。

Configurationは何を行うか?

ConfigurationではPuppetを利用し、サーバのあるべき状態をManifestで定義し、定義を元に構成管理を行う。
パッケージのインストール、ディレクトリの構成、サービスの起動状態などを管理する役割を持つ。

Orchestrationは何を行うか?

OrchestrationではJenkinsを利用し、継続的なビルド&テスト、各環境へのデプロイ作業を行う。
アプリケーションのリリースを管理する役割を持つ。

結果

どこまで自動化できたか?

手動での設定が3ファイル残ってしまったが、それ以外はほぼ自動で環境構築を行えるようになった。

どの程度の柔軟性を得たか?

数十台のサーバを半日以内で、スケールアウト・スケールイン/スケールアップ・スケールダウンできる柔軟性を得た。

振り返り

一番悩んだのが、環境構築の自動化部分での役割分担。
三段階にわけるというのが果たしてよいのか、単一化した方がよいのかも、とか、色々悩んだ。
手探り状態で行ったので、運用していくと色々と問題点がみつかるかもしれないが、そこは適時対応する。

DevOpsのエンジニア業を意識した初めての業務で、中々やりがいのあるお仕事だった。
周囲の先輩エンジニアやチームメンバーに支えられつつ、今月リリースとあいなった。良かった。