LoginSignup
105
108

More than 3 years have passed since last update.

Jenkins+Dockerで開発中のWebアプリを手軽に公開する

Last updated at Posted at 2015-02-17

Jenkinsユーザ・カンファレンス2015で @nobuoka さんが発表されていた、
開発中の機能やUI/UXの検証環境を手軽に動かしたい をJenkins+Dockerでやってみました

概要

  • http://${branchname}.feature.example.com で各コンテナにアクセスできるようにする
    • DockerホストはJenkinsのスレーブノードとして登録することでJenkinsからDockerを操作できるようにする
    • 各コンテナは3000番ポート(webrickのデフォルトポート)を公開する
      • webrickを起動するのみでsshdなど他のサービスは基本的には立ち上げない
    • サブドメインと各コンテナ(の公開ポート)の解決にはhttpdを利用する
      • 元の資料ではPlackを使っていたけど私は馴染みがないのでhttpdでやる
      • ポートフォワーディングの待ち受けポートの取得にはDockerAPIを利用する
    • AWSのRoute53を使い*.feature.example.comへのリクエストが特定のサーバ(Dockerホスト)に辿り着くようにする architecture.png

動作イメージ

やってることは単純です

  1. 【開発者】featureブランチへのgit push
    • jenkinsがpollingして変更を検知
  2. 【Jenkins】Dockerコンテナを起動
  3. 【Jenkins】httpdに起動したDockerコンテナへのProxyを追加

対象

  • アプリ
    • Railsアプリ
    • webサーバはなんでもよいのでwebrick
    • DBはsqlite3を想定します
  • 対象ブランチ
    • git-flowブランチモデル
    • targetBranch.png

環境

  • ホスト ... AmazonLinux
  • コンテナ ... AmazonLinux

作業内容

Route53(DNS)の設定

  • Route53はワイルドカードエントリをサポートするのでその設定を行います
    • 事前にドメインを取得してください
    • ドメインを持ってない方は無料で使用できるドメインもあるので調べてみてください
  • DockerホストのPublicIPなどを設定します
    • こちらなどで設定については説明されてます

Dockerホスト(Jenkinsスレーブ)の設定

  • jenkins用に jenkins アカウントを用意しているものとします

1) Dockerの準備

  • 以前書いたこの記事を御覧ください
    • local/amznイメージを用意します
  • http経由でAPIが使いたいのでこちらの記事のように /etc/sysconfig/docker に以下を追記します
other_args="-H tcp://0.0.0.0:4243 -H unix:///var/run/docker.sock"

2) baseイメージの作成

  • 都度全てをビルドすると時間がかかるので、ある程度のパッケージ/gemなどを入れたイメージを用意します
    • 事前にgithubに登録している鍵をgithub_keyという名前でカレントディレクトリに配置しておきます
    • 以下に載せるDockerfileは参考程度に見てください
  • イメージ名はloca/app_baseという名前にしておきます
Dockerfile
FROM local/amzn
MAINTAINER kmats
# githubの鍵配置
RUN mkdir -p /root/.ssh
RUN chmod 700 /root/.ssh
COPY github_key /root/.ssh/github_key
RUN chmod 600 /root/.ssh/github_key
RUN echo -e "Host github.com\n\
  User git\n\
  Hostname ssh.github.com\n\
  Port 443\n\
  IdentityFile ~/.ssh/github_key\n\
  StrictHostKeyChecking no" >> ~/.ssh/config
RUN chmod 600 /root/.ssh/config
# パッケージのインストール
RUN yum update -y
RUN yum install -y wget curl sudo net-tools git libxslt-devel libxml2-devel patch libyaml-devel libffi-devel glibc-headers autoconf gcc-c++ glibc-devel patch readline-devel zlib-devel openssl-devel automake libtool bison make bzip2 sqlite
# Ruby(rvm)のインストール
RUN gpg2 --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
RUN curl -L https://get.rvm.io | bash -s stable
RUN /bin/bash -l -c "rvm install 2.2.0"
RUN /bin/bash -l -c "rvm use 2.2.0 --default"
# アプリの配置
RUN mkdir -p /var/app/
WORKDIR /var/app/
RUN git clone git@github.com:kmats/appname.git #<=適宜設定する
RUN /bin/bash -l -c "bundle install"
RUN /bin/bash -l -c "rake db:setup"
# script.shはあとで用意する
ENTRYPOINT /bin/bash -l -c "sh script.sh"
$ sudo docker build -t local/app_base .

3) httpdの準備

$ sudo yum install -y httpd
  • 各feature毎にProxyの設定を書いたファイルを配置するので、ディレクトリを作ります
$ sudo mkdir /etc/httpd/conf.d/proxy_includes/
$ sudo chmod 777 /etc/httpd/conf.d/proxy_includes/ #<=手抜き
  • httpd.confに以下の行を追加します
$ diff /etc/httpd/conf/httpd.conf{.default,}
353a354,363
> 
> NameVirtualHost *:80
> Include conf.d/proxy_includes/*.include
> 

4) ttyなしでsudo出来るようにする

  • dockerコマンドやサービスの再起動を行う際にはsudoを利用します
  • ttyなしでsudoできるようこちらの記事に倣いましょう
$ sudo visudo
- Defaults    requiretty
+ # Defaults    requiretty
+ Defaults:jenkins !requiretty
+ jenkins ALL=(ALL) NOPASSWD:ALL

Jenkinsの設定

  • Dockerホストはスレーブとして登録しているものとします

a) featureブランチをpollingする

  • ソースコード管理>Git から対象のリポジトリを指定します
  • このときBranches to buildは */feature/* とします
  • ビルド・トリガ>SCMをポーリング からスケジュールを設定すると良いでしょう

b) ブランチ名から branchname を取得する

以下の様なシェルスクリプトでも取得できます

BRANCHNAME=${GIT_BRANCH#*/*/}
  • あとの処理でも使えるように環境変数としてインジェクトしておきます

c) Dockerfileを作成、実際に起動するコンテナイメージをビルドする

  • シェルの実行 でビルドします
### Dockerfile作成, Dockerイメージ作成
cat << _EOT_ > Dockerfile
FROM local/app_base
WORKDIR /var/app/appname/
RUN echo -e "#!/bin/bash\\n\\
git pull\\n\\
git checkout feature/${BRANCHNAME}\\n\\
bundle install\\n\\
bundle exec rake db:migrate\\n\\
bundle exec rake db:seed\\n\\
bundle exec rails s" > script.sh
_EOT_

sudo docker build -t local/app:${BRANCHNAE} .

d) Dockerコンテナの起動、httpdの設定

  • これで最後です
  • Execute Ruby Script でやります
### Dockerコンテナ起動, httpdの設定
require 'json'
require 'open-uri'
module Docker
  class AppContainer
    def initialize(branchname)
      @cid = `sudo docker run -i -t -p 3000 -d local/app:#{branchname}`.strip
    end
    def public_port(private_port)
      information['NetworkSettings']['Ports']["#{private_port}/tcp"].first['HostPort']
    end
    private
    def information
      @information ||= JSON.parse(open("http://localhost:4243/containers/#{@cid}/json").read)
    end
  end
end

RAILS_PORT = 3000
container = Docker::AppContainer.new(ENV['BRANCHNAME'])
port = container.public_port(RAILS_PORT)
File.open("/etc/httpd/conf.d/proxy_includes/#{ENV['BRANCHNAME']}.include", 'w') do |f|
  f.write <<-__EOF__
<VirtualHost *:80>
  ServerName #{ENV['BRANCHNAME']}.feature.example.com
  ProxyPreserveHost on
  ProxyPass        / http://localhost:#{port}/
  ProxyPassReverse / http://localhost:#{port}/
</VirtualHost>
  __EOF__
end
system("sudo /etc/init.d/httpd restart")
  • 以上でコンテナが立ち上がりWebアプリにアクセスできるようになっているはずです
  • お疲れ様でした!

課題

  • 今回はとりあえずデプロイまで全てDockerfileでやりましたが、chef/puppetやcapistranoなどを使って実際のサーバを構築するように作っても良いと思います
    • 更にserverspecなどで環境のテストもやってみたいですね
  • これだけだとコンテナが増え続けるのでそちらの管理も必要になります

所感

  • Dockerはとても便利!
  • Dockerfileだけで大抵のことはできちゃうけど他のツールも連携させたい

コード沢山書いたけどコピペミスってたり直し間違えたところがありそうで先にゴメンナサイしておきます、見つけた場合はコメント等でお知らせください

105
108
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
105
108