Movatterモバイル変換


[0]ホーム

URL:


オープンソースこねこね

Webプログラミングなどについてあれこれ。

この広告は、90日以上更新していないブログに表示しています。

JenkinsとDockerでTravisっぽいCIサーバを育ててみている

最近プライベートなプロジェクトのCIにはcircleciとかが人気なんでしょうかね。

GitHub 時代のデプロイ戦略 - naoyaのはてなダイアリー

近頃のCIサーバはアプリケーションのテストだけじゃなく、インフラのテストやデプロイ、ChatOpsなどgitやチャットツールなど他のシステムと連携した自動化のための必須プラットフォームといった感じになってきてる。とはいえ、趣味で開発しているプロダクトに余計なコストはかけたくない。ああ、でもやっぱCIはしたい。

そんなわけで以前から契約だけしていて放置気味だった、さくらのVPSの1GにjenkinsをたててオレオレCIを育てているのでその辺のことを書いてみる。CIの実行環境はDockerを使って仮想化し、ジョブの内容はTravisやcircleciのようにリポジトリ側のyamlファイルに記述できるようにしてみた。構成をざっくりと図解すると以下のようになる。

f:id:kohkimakimoto:20141013212556p:plain

また参考にさせていただいたのは以下の記事。

Docker + Jenkins + travis.yml parser 作って Travis っぽいものを作った話 - from scratch

Use Docker + Jenkins to run GitHub tests

jenkinsでのunitテストは、dockerでクリーン環境を作って行う!!shinofara's Blog (*´ω`*) | shinofara's Blog (*´ω`*)

ベースとなっている環境

CentOS6.5の上に構築している。さくらのVPSのデフォルトがそうなのと、個人的に使い慣れているというのが最大の理由。ただCentOSのDockerはカーネル周りのバグでディスク領域が開放されないことがあるらしいので、今後移行するかもしれない。

Docker on CentOS 6.5 で詰んだのでメモ - sonots:blog

CentOS6系のカーネルに上記のバグフィックスがバックポートされるのも期待している。。。まあ今のところ、仕事でつかっているわけではなく、ディスク領域にも余裕があるのと環境構築はchefでなるだけ自動化しながら作っているので、ディスクが詰まったら最悪、環境を再インストールすればいいかなという判断でやっている。

Dockerのインストール

Dockerのインストール自体について特記することはあまりない。epelリポジトリを使えるようにしておいて以下の様なchefのレシピを書いた。実際にやってることはyum install docker-io/etc/init.d/docker startと同じだ。

docker/recipes/default.rb

package "docker-io" do  action :installendservice "docker" do  action [:enable, :start]end

jenkinsのインストール

CentOS上のjenkinsのインストールについては以前に記事を書いた。今回はそれをちょこちょこ修正した(yumリポジトリからインストールするなど)。詳細は以下にペーストしたレシピの内容を見てもらえばいいが、jenkinsからdockerを実行するためにjenkinsユーザのuidを明示的(uid:45678)に指定したりdockerグループの追加したりしている。Dockerコンテナ内でジョブを実行するユーザとjenkinsの実行ユーザのIDは同じにしておかないといろいろパーミッション周りでハマる。

jenkins/recipes/default.rb

group "jenkins" do  gid    45678  action [:create, :manage]enduser 'jenkins' do  comment  'Jenkins Continuous Build server'  uid      45678  group    'jenkins'  home     '/var/lib/jenkins'  shell    '/bin/false'  password nil  action   [:create, :manage]endscript "install_jenkins_yum_repo" do  interpreter "bash"  user "root"  cwd "/tmp"  code <<-EOH  wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo  rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key  EOH  not_if "test -e /etc/yum.repos.d/jenkins.repo"endpackage "jenkins" do  action :installendservice "jenkins" do  action [:enable, :start]endgroup "docker" do  action [:modify]  members ["jenkins"]  append trueend

jenkinscliプラグインのインストール

jenkinsのプラグインもchefでインストールさせるため以下のようなレシピを書いた。jenkinsは起動までに時間がかかるのでscript[install-jenkins-cli]jenkins-cli.jarをダウンロードする際に10秒待ち、HTTPレスポンス503はリトライする仕組みにしている。

jenkins/attributes/default.rb

default['jenkins']['cli_url'] = "http://127.0.0.1:8080/jnlpJars/jenkins-cli.jar"default['jenkins']['jenkins_url'] = "http://127.0.0.1:8080/"default['jenkins']['wait_for_boot'] = "10"default['jenkins']['plugins'] = [  "git",  "cloverphp",  "simple-theme-plugin",  "jquery",  "gravatar",  "disk-usage",  "envinject",  "extra-columns",  "categorized-view",  "ci-skip",  "timestamper",  "monitoring",  "view-job-filters",  "locale",  "sidebar-link",  "pegdown-formatter",  "ansicolor"]

jenkins/templates/default/jenkins.rb

#!/usr/bin/env bashjava -jar /usr/lib/jenkins/jenkins-cli.jar -s <%=node['jenkins']['jenkins_url']%> "$@"

jenkins/recipes/default.rb

template "/usr/local/bin/jenkins" do  source "jenkins.erb"  owner "root"  group "root"  mode "0755"endcli_url = node['jenkins']['cli_url']wait_for_boot = node['jenkins']['wait_for_boot']script "install-jenkins-cli" do  interpreter "bash"  user "root"  cwd "/tmp"  code <<-EOH    sleep #{wait_for_boot}    http_response_code=503    while [ $http_response_code -eq 503 ]    do      http_response_code=`curl -LI #{cli_url} -o /dev/null -w '%{http_code}' -s`      sleep 5    done    wget -t 5 --waitretry 5 -O /usr/lib/jenkins/jenkins-cli.jar #{cli_url}  EOH  not_if "test -e /usr/lib/jenkins/jenkins-cli.jar"enddirectory "/var/lib/jenkins/updates" do   owner "jenkins"   group "jenkins"   mode "0755"   action :createend# https://issues.jenkins-ci.org/browse/JENKINS-10061# https://gist.github.com/rowan-m/1026918script "update-jenkins-updatecenter" do  interpreter "bash"  user "jenkins"  cwd "/tmp"  code <<-EOH    curl -L http://updates.jenkins-ci.org/update-center.json | sed '1d;$d' > /var/lib/jenkins/updates/default.json  EOH  not_if "test -e /var/lib/jenkins/updates/default.json"endnode['jenkins']['plugins'].each do |plugin_name|  execute "install-jenkins-plugin-" + plugin_name do    user "root"    command "/usr/local/bin/jenkins install-plugin " + plugin_name    action :run    not_if "/usr/local/bin/jenkins list-plugins | awk '{print $1}' | grep ^#{plugin_name}$"    notifies :run, "execute[jenkins-safe-restart]"  endendexecute "jenkins-safe-restart" do    command "/usr/local/bin/jenkins safe-restart"    action :nothingend

yamlパーサとコンテナの起動スクリプト

CIのジョブはTravisのようにリポジトリ側のyamlで制御、設定できるようにした。yamlファイルはこんな感じ。

.jenkins.yml

container:    image: jenkins-ci-basebefore_script:    - composer install --dev --no-interactionscript:    - php vendor/bin/phpunit -c phpunit-ci.xml.dist

これをパースしてimageで指定されたDockerイメージをrunする。この辺の処理は使い慣れたPHPで実装した。ソースは公開していないが、具体的には以下のようなことをやっている。

  • リポジトリのルートにある.jenkins.ymlをパース。
  • before_scriptscriptで指定された内容からそれぞれbefore_script.shscript.shのようなスクリプトファイルを生成してJenkinsのワークスペースに出力する。
  • 出力したスクリプトファイルを順に起動するstart.shをJenkinsのワークスペースに出力する。
  • imageで指定されたdockerイメージにワークスペースをマウントしてコンテナを起動。具体的にはdocker run -v $WORKSPACE:/home/worker/workspace -w /home/worker/workspace -u worker $IMAGE /bin/bash -l start.shのようなコマンドを実行する。
  • dockerが処理を終えたあとdocker rmを実行してコンテナを削除する。

この一連の処理を行うPHPスクリプトをjenkinsのジョブ設定の「ビルド」->「シェルの実行」で起動するように設定しておく。

Dockerイメージ

Dockerイメージはあらかじめ作っておくのだが、jenkinsから起動するために以下の決められた仕様で構成している

  • uid45678workerユーザがいる(ホスト側のjenkinsユーザと同じuid)
  • /home/worker/workspaceディレクトリがある(ホスト側のjenkinsワークスペースがマウントされる)
  • workerはパスワードなしでsudoできる

この仕様を満たす基本的なDockerイメージを作成するDockerfileは以下のようになる

FROM centos:centos6# basic settingsRUN rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpmRUN rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-6.rpmRUN yum -y groupinstall "Base" "Development tools"RUN yum -y install --enablerepo=remi,epel \  sudo \  readline \  readline-devel \  compat-readline5 \  libxml2-devel \  libxslt-devel \  libyaml-devel \  git \  make \  autoconf \  automake \  bison \  libtool \  sysstat \  gettext \  traceroute \  openssl \  openssl-devel \  curl \  wget# add worker userRUN useradd -u 45678 -d /home/worker -m -s /bin/bash worker && \  mkdir /home/worker/workspace && \  chown worker:worker /home/worker/workspace && \  echo "worker    ALL=(ALL)    NOPASSWD: ALL" > /etc/sudoers.d/worker && \  sed -i 's/.*requiretty$/#Defaults requiretty/' /etc/sudoers# Enable to run sudo in the scriptRUN sed -i 's/.*requiretty$/#Defaults requiretty/' /etc/sudoers# timezoneRUN echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock && \  rm -f /etc/localtime && \  ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime################################## default behavior is to login by worker user#################################CMD ["su", "-", "worker"]

これで、dockerコンテナ内でworkerというユーザが処理を実行する。あとは必要な環境ごとにカスタマイズしたDockerイメージを用意しておけば、いろんな環境でCIができる。ちょうどTravisPHPRubyといった言語ごとのテスト環境を用意してくれるように、例えばPHPをインストールしたjenkins-ci-phpRubyをインストールしたjenkins-ci-rubyというDockerイメージを作っておいて、

container:    image: jenkins-ci-php

container:    image: jenkins-ci-ruby

などと.jenkins.ymlで指定すればいい。

まとめ

ここ数日運用してみての感想だが、これはかなりいい感じ。現在の自分の用途の範囲では、ほぼTravisでやれることがプライベートでも実現できている。CIの実行環境はDockerによって独立し常に使い捨てにされるので、サーバ設定を丸ごと書き換えるようなプロビジョニングのテストなどにも使えると思う。

あとjenkinsのテーマをアトラシアン風にするdjonsson/jenkins-atlassian-theme · GitHubがあるので、これをちょっとカスタマイズして、UIの見た目も変えてみた。

f:id:kohkimakimoto:20141014055646p:plain

やっぱし、コンソール表示は黒背景がいい!

検索
リンク

引用をストックしました

引用するにはまずログインしてください

引用をストックできませんでした。再度お試しください

限定公開記事のため引用できません。

読者です読者をやめる読者になる読者になる

[8]ページ先頭

©2009-2025 Movatter.jp