TL;DR
GitBucketとJenkins、Dockerを使ってビルドやテストのためのCI環境を構築するための手順。
まだ書いていないこと
- SSH鍵の登録方法
- Jenkinsの設定は一通りinit.groovyで行うようにしてしまう
- Dockerのディレクトリは-vで適切に設定する
- Electronのサンプルソース
やりたいこと
- GitBucketの任意のグループとしてプライベートリポジトリを作成し、ビルド用のJenkinsファイルを配置する。
- Jenkins上からGitHub Organization folderを経由してGitBucketに接続し、ビルド対象リポジトリをスキャン
- GitBucketのPullRequestのタイミングでJenkinsのビルドが起動
- JenkinsのビルドはDockerPluginを通してDockerサーバーをslave起動して、ビルドを行う
- 結果をGitBucketにStatusAPIを通して通知する
イメージ
ゴール
GItBucketでPullRequestを出したら実行可能なexe(Electronアプリ)が生成される
エンドユーザー(GItBucket)からの要求を、SIer(Jenkins)が下請け(Docker)に流して働かせ、なにくわぬ顔で成果物を手に入れます。と言ったら一部の方には想像しやすいですかね?
主な登場人物
Jenkins
CIサーバー。バージョンは2.39。AmazonLinux2016.09上に生息。
Master-Slave構成を構築します。SlaveはDockerインスタンスとして稼働させるため、実行時はMasterからDockerサーバーに接続します。
GitBucketと連携するためにGitHub Organization folderを、DockerSlaveを利用するために、Docker Pluginを今回利用します。
GitBucket
GitHubクローン。バージョンは4.8。AmazonLinux2016.09上に生息。
GitHubは使えないけどGitBucketは社内で利用できる人 が今回の対象。GitHubが使える幸せな人は読む必要ありません。GitBucketのAPIをGitHub Organization Folderを利用して無理やり幸せになろうとしています。
なお、GitBucketのAPIの不足分を補うためにJekninsから直接GitBucketに接続するのではなく、
Jenkins <==> Nginx <==> GitBucketという流れを踏むことになります。
Docker
コンテナベースの仮想環境。バージョンは1.12.1(APIは1.24)。CoreOS alpha (1214.0.0)上に生息。
alphaはたまたま。
Dockerfileを利用して事前に仮想環境を構築したイメージを準備しておきます。
Jenkinsから接続され、実際のビルドやテストはこのコンテナ内部で実行されます。
環境構築
サーバー | バージョン | 同居するモジュール |
---|---|---|
Jenkins | 2.39 | Nginx |
GitBucket | 4.8 | Jetty |
Docker | 1.12.1 | (コンテナ上に) nodejs |
GitBucketサーバー
インストールから起動
-
GItBucket4.8をダウンロードします。
-
好きなアプリケーションサーバー(私はJetty)に配置して起動します。
-
http://GITBUCKET_SERVER:8080
でアクセスできることを確認してください(ここでは便宜的にGITBUCKET_SERVER
というhost名で進めていきます)。 -
ルーティング用にWebサーバーをインストールします(私はNginxを入れました)。
-
Nginxの設定を以下のように書き換えます
(参考:Multibranch PipelineによるJenkinsとGitBucketの連携)。/etc/nginx/nginx.conf# serverの定義に以下をlocation設定を追加します location ~ ^/[^/]+/[^/]+\.git.*$ { return http://GITBUCKET_SERVER:8080/git$request_uri; } location ~ ^/api/v3/users/(.*)$ { try_files $uri /api/v3/orgs/$1; } location / { proxy_pass http://GITBUCKET_SERVER:8080; client_max_body_size 10M; proxy_set_header Host $host; }
-
Nginxの設定を反映させます
nginx -s reload
-
http://GITBUCKET_SERVER/
でアクセスできることを確認します。 -
root
/root
(初期ユーザー)でログインし、任意のグループを作成します(ここではJGROUP
という名前のグループを作ります)。 -
Nginxを経由したことでGitBucketでは受け付けていなかった以下のAPIが実行できるか確認します。
# jqはお好みで $ export GITBUCKET_SERVER=127.0.0.1 $ export GROUP_NAME=JGROUP # nginxの設定をしていない場合は、{ "message": "Not Found" } が返る $ curl http://$GITBUCKET_SERVER:8080/api/v3/users/$GROUP_NAME | jq . { "message": "Not Found" } # nginxの設定をしている場合はグループの情報が返る $ curl http://$GITBUCKET_SERVER/api/v3/users/$GROUP_NAME | jq . { "login": "JGROUP", "email": "JGROUP@devnull", "type": "Organization", "site_admin": false, "created_at": "2016-01-19T00:55:00Z", "url": "http://GITBUCKET_SERVER:8080/api/v3/users/JGROUP", "html_url": "http://GITBUCKET_SERVER:8080/JGROUP", "avatar_url": "http://GITBUCKET_SERVER:8080/JGROUP/_avatar" }
ユーザー設定とリポジトリ作成
-
GitBucketにログインします。
-
Jenkins用のユーザーアカウントを追加します。
-
Jenkins用のSSH鍵を登録しておきます(TODO)。
12.http://GITBUCKET_SERVER:8080/jenkins/_ssh
へ移動
13. 秘密鍵を2つ登録します(登録の仕方は省略)。
* jenkins => jenkinsマスターから接続を行うためのssh鍵情報です。
* jenkins-slave => Docker上のインスタンスから接続を行うためのssh鍵情報です。 -
上記で作ったJGROUPに作成したjenkisユーザーを追加し、グループに任意のリポジトリを作成します。
-
テスト用のリポジトリに以下のようなJenkinsfileを登録しておきます。
Jenkinsfilenode("ubuntu-base") { stage("hello") { echo "world" } }
これでGitBucket側の設定は終了です。
Dockerサーバー
Dockerサーバーの設定
-
好きなDokcerのホストーサーバーを起動します(私の場合はCoreOSを選択)
-
tcp
プロトコルで外部から接続可能なように設定を変更します
3.docker-tcp.socket
を設定します。```ini:/etc/systemd/system/docker-tcp.socket [Unit] Description=Docker Socket for the API [Socket] ListenStream=2376 BindIPv6Only=both Service=docker.service [Install] WantedBy=sockets.target ```
注意事項 (参考:インターネットの向こう側にあるDockerを使う)
- tcpでの接続を全許可していますのでセキュリティ的に問題があります。
- 当初Protect the Docker daemon socketで設定しようとしたのですが、JenkinsのDockerPluginがtcpかunixプロトコルしか受け付けていなかったため、あきらめました(実際にはインフラレベルでIP単位でアクセス制限をかけるつもりです。まだやってないけど)。
-
dockerのプロセスを再起動します
$ sudo systemctl enable docker-tcp.socket $ sudo systemctl stop docker $ sudo systemctl start docker-tcp.socket $ sudo systemctl start docker
-
接続確認をします。Jenkinsサーバーから
curl
が通るか確認します。$ curl http://127.0.0.1:2376/images/json
ベースイメージの構築
-
Jenkinsから接続することが可能なベースとなるDockerImageを構築します。
今回はこんな感じにベースとなるDockerfileを構築しました(雑です)。FROM ubuntu:16.10 #----------------------------------------------------------- # 各種必要なライブラリのインストール # jenkinsユーザーの追加 # ssh接続を許可 # 一部後述するelectronの環境構築に必要なライブラリを含む RUN set -x \ && apt-get update \ && apt-get -y install --no-install-recommends \ sudo \ software-properties-common \ openssh-server \ curl \ wget \ git \ unzip \ zip \ apt-transport-https \ && mkdir ~/.ssh \ && mkdir /var/run/sshd \ && groupadd jenkins && useradd -d /home/jenkins -g jenkins -m jenkins \ && mkdir /home/jenkins/.ssh \ && rm -rf /var/lib/apt/lists/* # 22ポートを公開 EXPOSE 22 # jenkinsからgitbucketへ接続するための認証情報を設定 # http接続ができないのでsshで接続する必要があります COPY files/config ~/.ssh/config COPY files/config /home/jenkins/.ssh/config COPY files/id_jenkins_rsa.pub /home/jenkins/.ssh/authorized_keys COPY files/id_gitbucket_rsa /home/jenkins/.ssh/id_rsa RUN set -x \ && chown -R jenkins:jenkins /home/jenkins/.ssh \ && chmod 700 /home/jenkins/.ssh \ && chmod 600 /home/jenkins/.ssh/*
-
インストールします
$ cd ubuntu-base $ find . . ./files ./files/id_gitbucket_rsa ./files/id_jenkins_rsa ./files/config ./files/id_jenkins_rsa.pub ./files/id_gitbucket_rsa.pub ./Dockerfile ./README.md $ docker build -t ubuntu:ubuntu-base .
-
Electronのビルド環境構築のためのDockerImageを作ります
FROM ubuntu:16.10 #--------------------------- WORKDIR /usr/local ENV NDENV_ROOT /usr/local/ndenv ENV PATH "$NDENV_ROOT/bin:$PATH" # ビルド用にndenvとyarnをインストールする RUN set -x \ && git clone https://github.com/riywo/ndenv.git /usr/local/ndenv \ && mkdir /usr/local/ndenv/plugins \ && git clone https://github.com/riywo/node-build.git /usr/local/ndenv/plugins/node-build \ && (\ echo 'export NDENV_ROOT=/usr/local/ndenv'; \ echo 'export PATH="$NDENV_ROOT/bin:$PATH"'; \ echo 'eval "$(ndenv init -)"' \ ) >> /etc/profile.d/ndenv.sh \ && ndenv install v6.1.0 \ && ndenv global v6.1.0 \ && chmod +777 -R /usr/local/ndenv \ && curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - \ && echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list \ && sudo apt-get update \ && sudo apt-get install yarn -y
-
インストールします
$ cd ubuntu-electron $ find . . ./files ./files/id_gitbucket_rsa ./files/id_jenkins_rsa ./files/config ./files/id_jenkins_rsa.pub ./files/id_gitbucket_rsa.pub ./Dockerfile ./README.md $ docker build -t ubuntu:ubuntu-electron .
以上でDockerサーバーの設定は完了です。
Jenkinsサーバー
インストールから起動
-
JenkinsのLTS Releaseをダウンロードします。
-
お好きなアプリケーションサーバー(私はJetty)に配備します。ただしまだ起動はしません。
-
Jenkinsはデフォルトでは
$HOME/.jenkins
以下に必要なデータを構築しますので、事前に$HOME/.jenkins
ディレクトリを作成して、$HOME/.jenkins/init.groovy
という名前で以下のファイルを配置します。
(参考:Jenkinsの公式Dockerイメージ使ってみた)init.groovyimport hudson.model.*; import hudson.security.* import jenkins.model.*; def instance = Jenkins.instance // ログインユーザーを自動生成 // https://groups.google.com/d/msg/jenkinsci-users/Pb4QZVc2-f0/5hzCC-syNgAJ def hudsonRealm = new HudsonPrivateSecurityRealm(false) hudsonRealm.createAccount('admin','admin') instance.setSecurityRealm(hudsonRealm) def strategy = new FullControlOnceLoggedInAuthorizationStrategy() instance.setAuthorizationStrategy(strategy) instance.save() def uCenter = instance.updateCenter uCenter.updateAllSites() def checkIfPluginInstalled = { pluginid -> for(p in instance.pluginManager.plugins) { if(p.shortName == pluginid){ return true } } return false } // Installs the following plugins if not installed. [ "cloudbees-folder", "antisamy-markup-formatter", "build-timeout", "credentials-binding", "timestamper", "ws-cleanup", "gradle", "workflow-aggregator", "github-organization-folder", "pipeline-stage-view", "git", "ssh-slaves", "matrix-auth", "pam-auth", "ldap", "email-ext", "mailer", "docker-plugin", "ghprb" ].findAll({! checkIfPluginInstalled(it)}).each { uCenter.getPlugin(it).deploy() } // Set the number of executors (in master-node) to 0. instance.setNumExecutors(0) instance.reload()
-
Jenkinsを起動すると、init.groovyが読み込まれてadminユーザーと必要なプラグインがインストールされます。
プラグインの設定
必要なプラグインの設定を行っていきます。
Docker Plugin
-
http://JENKINS_SERVER:8080/configure
へ(Jenkinsの管理 => システム設定)。 -
クラウド
=>追加
=>Docker
を選択-
- Nameは任意
- Docker URLは
tcp
あるいはunix
のみが指定可能です。httpは不可 - Docker API VersionはCoreOS上で
docker version
と打つことで確認できます。
-
Images
=>Add Docker Template
で利用するDocker名を紐つけます。
-
Docker Image
=> Dockerの設定で作成したImage名を指定します。 -
Remote Filing System Root
=> Dockerfileで作成したjenkinsユーザーのhomeディレクトリを指定します -
Label
=> Jenkinsfileのnodeの指定となるslave名です。 -
用途
=>このマシーンを特定ジョブ専用にする
を選択します。 -
Lanunch method
=> Docker SSH computer launcherを選択します。認証情報は、Dockerfile内で定義した秘密鍵を使います。
-
-
Imagesの
Configuration setting...
=>Volumes
でキャッシュしたいディレクトリを永続化しておきます。
今回はyarnのキャッシュフォルダとelectronのバイナリをホスト側にマウントするために以下の設定を追加しています。/home/core/cache/yarn:/home/jenkins/.cache/yarn/ /home/core/cache/electron:/home/jenkins/.electron/
-
GitHub Organization Folder
-
http://JENKINS_SERVER:8080/configure
へ(Jenkinsの管理 => システム設定)。 -
GitHub Enterprise Servers
を設定します。
-
API endpoint
=>http://GITBUCKET_SERVER/api/v3/
-
Name
=> 適当に(AWS-GITとつけました)
-
JOB作成
-
新規ジョブ
でGitHub Organization Folderを作成します。
任意の名前をつけることが可能ですが、設定した名前がリポジトリのowner(ユーザーorグループ)になります。対象のリポジトリグループは後で変更可能です。 - 設定画面が開くので、
Project Sources
からRepository Sources
の設定を行います。- Owner --- 指定したユーザーorグループのリポジトリ以下を探索します
- API endpoint --- 接続先のサーバーをシステム設定で設定したGitBucketの名前を設定します。
- うまくいけば指定したグループ配下がスキャンされ
Jenkinsfile
が存在するリポジトリが一覧に表示されます。
ゴールを確認
やりたい事が出来ているか確認します。
- GitBucketのリポジトリ上に簡単なElectronアプリをPUSHします。Jenkinsファイルには成果物を保存するところまで記載しておきます( TODO サンプルソース)。
- Re-Scanして認識することを確認します。
- GitBucket上のリポジトリ上にブランチを切り、PullRequestを出します。
- PullRequestが出たタイミングでJenkinsビルドが走ることを確認します。
- Jenkinsビルドが完了後に成果物がダウンロードできることを確認します。
ハマリポイント(まとめ)
-
GitHub Organization folder
でスキャンしてもGitBucketに繋がらない- 上記手順にしたがってWEBサーバーでルーティングをいじる必要があります。
-
DockerPlugin
からProtect the Docker daemon socketできない。- 現在のバージョン(DockerPlugin 0.16.2)だとhttpsは設定できません。
- Slaveとして起動しているDockerのインスタンスにJenkinsから接続出来ない。
- DockerPluginの接続を確認しましょう。必要な秘密鍵はDockerに接続する認証情報ではなくて、Docker上に存在するインスタンスに接続するための秘密鍵です。
- Docker上からGitBucketのソースをclone出来ない
-
https
プロトコルやgit
プロトコルでは接続出来ません。ssh
で接続しましょう。
-
オチ
GitHubは便利です。でも会社ではGitBucket使い続けていくつもりです。