Ubuntuにjenkinsをインストールする手順を記載します。
基本的には こちら にしたがって作業を進めます。
バージョン
- Ubuntu: 18.04
- Jenkins: 2.190
- Javaランタイム: 11
Javaランタイムのインストール
Jenkins 2.164 よりJava 11をサポートしているらしいので、Java11をインストールします。
それ以前のバージョンの場合は、Java8をインストールします。
sudo apt-get update
sudo apt-get install openjdk-11-jre
java --version
> openjdk 11.0.4 2019-07-16
Jenkinsインストール
wget -q -O - https://pkg.jenkins.io/debian/jenkins.io.key | sudo apt-key add -
sudo sh -c 'echo deb https://pkg.jenkins.io/debian-stable binary/ > /etc/apt/sources.list.d/jenkins.list'
sudo apt-get update
sudo apt-get install jenkins
これにより、以下の設定が行われます。
- Jenkinsがサーバ起動時に起動するデーモンとして設定される。詳細は
/etc/init.d/jenkins参照 - Jenkins起動用のjenkinsユーザが作成される
- コンソールのログ出力を
/var/log/jenkins/jenkins.logにリダイレクトする -
/etc/default/jenkinsを読み込む - Jenkinsが8080ポートをリッスンするよう設定する。変更したい場合は、
/etc/default/jenkinsのHTTP_PORTを変更する
起動・停止
sudo systemctl start jenkins
sudo systemctl stop jenkins
初回設定
- ブラウザで
http://{hostname}:8080にアクセスします - Administrator passwordの入力を求められますので、
cat /var/lib/jenkins/secrets/initialAdminPasswordでパスワードを確認して入力します -
Select Plugins to Installを選択し、必要そうなpluginを選んで、Nextをクリックします - これでpluginのインストールが始まります
-
Create First Admin User画面で、ユーザー名、パスワードなどを入力します - あとは流れで初回設定は完了です
Adminユーザとしての初期設定(ユーザ、権限周りの設定)
管理画面を表示
- ブラウザでJenkinsの画面(
http://{hostname}:8080)にアクセスします- 空白画面になるようでしたら、一旦
sudo systemctl restart jenkinsでjenkinsの再起動をするとなおるかもしれません
- 空白画面になるようでしたら、一旦
- ログイン画面が表示されるので、一つ前の手順で設定したAdminのユーザ名とパスワードを入力します
- メニューの
Jenkinsの管理ボタンから管理画面トップに移動します- URLは
http://{hostname}:8080/manageです
- URLは
プラグインのインストール
- 管理画面トップから
プラグインの管理に移動します -
利用可能タブから目的のプラグインを探してチェックし、プラグインをインストールします - 今回は
Authorize ProjectとRole-based Authorization Strategyの二つのプラグインをインストールして、再起動しました
ユーザの管理
- 管理画面トップから
ユーザの管理に移動します -
ユーザ作成をクリックして、ユーザを作成します
グローバルセキュリティの設定
- 管理画面トップから
グローバルセキュリティの設定に移動します -
セキュリティを有効化をチェック(デフォルトなので何もしなくていいですが) - ユーザ情報の設定します
- 今回は
Jenkinsのユーザデータベースをチェックします - 勝手にユーザ登録されたくないので、
ユーザにサインアップを許可はチェックしません
- 今回は
- 権限管理の設定をします
- 今回は
Role-Based Strategyを選択します- 事前に
Role-based Authorization Strategyプラグインのインストールが必要です -
Authorize Projectプラグインがインストールされ設定されてないと、ワーニングが表示されます -
行列による権限設定はユーザやグループ単位にやれることを設定できますが、Jenkinsのユーザデータベースを利用している場合は新しいグループを追加したりできないようです。LDAPやUnix user/group databaseを利用する場合、グループがそのまま利用できます。なのでユーザ単位ではなくグループ単位で権限設定を行いたい場合はRole-Based Strategyが必要になります -
ログイン済みユーザに許可はログインユーザであれば誰でも何でもできます -
全員に許可はJenkinsにアクセスできれば、ログインせずとも誰でも何でもできます
- 事前に
- 今回は
-
Access Control for Buildsの設定をします- Buildの実行ユーザの権限を指定する設定です
- 今回は面倒なので、
全プロジェクトに適用するジブド実行の権限を指定を選択し、システムユーザーの権限で実行するにしました- ただしこの設定だと、SYSTEMユーザで実行しちゃってるよーとワーニングが出るので
DISMISSする必要があります
- ただしこの設定だと、SYSTEMユーザで実行しちゃってるよーとワーニングが出るので
- プロジェクト単位でコントロールしたい場合は
プロジェクト設定内でビルド実行の権限を設定するを選びます- ちなみに、Jenkinsにおいて
プロジェクトとジョブは同一と考えて良さそうです -
ビルドを起動したユーザの権限で実行する指定したユーザの権限で実行するシステムユーザの権限で実行するなど、プロジェクト設定に表示する選択肢をチェックしたうえで、ジョブの権限の設定欄から、上記でチェックした選択肢からプルダウンで選択できるようになります
- ちなみに、Jenkinsにおいて
Roleの設定
-
管理画面トップから
Manage and Assign Rolesに移動します -
Manage Rolesで新しいRoleを追加します- 今回は3つのGlobal Roleを追加することにしました
-
admin: システム管理者ユーザ。なんでもできます- これはデフォルトで存在します。全ての付与されています
-
developer:エンジニアユーザ。ジョブやビューの作成ができます- 全体:Read、ジョブ:全部、ビルド:全部、ビュー:全部 にチェックします
-
executer:特定のジョブの実行だけができるユーザ。- 全体:Read だけをチェックします
- ビルド権限をつけたくなりますが、Global Roleと Project RoleはOR条件なので、Project Roleの方で付けます
- 全体:Read だけをチェックします
-
- Project Roleを追加します
-
hoge-executer:Pattenには対象のジョブ名を正規表現で指定します- ジョブ:Build、Cancel、Read にチェックします
- 正規表現でジョブ名を指定することを前提に、ジョブ名の接頭語をパターン化しておくことをおすすめします。具体的には部署名とかプロジェクト名を接頭語としてつけておくと楽です
-
- 今回は3つのGlobal Roleを追加することにしました
-
Assing Rolesでユーザに対してRoleをアサインします- システム管理者ユーザには、Global Roleの
adminを割り当てます - エンジニアユーザには、Global Roleの
developerを割り当てます - 特定のジョブの実行ユーザは、Global Roleの
executerとProject Roleのhoge-executerを割り当てます - Anonymous(未ログインユーザ)には何もRoleを割り当てません
- システム管理者ユーザには、Global Roleの
Git連携
- Git連携を行うには
Gitプラグインが必要です - ジョブ側でGitからソースを取得する設定を行います
- ジョブの設定画面に移動します
-
ソースコードの管理でGitを選択します -
リポジトリURLにURL(今回はgit@の方)を入力します - 認証情報の
追加ボタンをクリックして、認証情報を作成します- ちなみに追加した認証情報はJenkinsトップの左側メニューの
認証情報欄から変更できます
- ちなみに追加した認証情報はJenkinsトップの左側メニューの
- 認証情報を設定します
- クレデンシャルの種類は3択だと思います
- SSHユーザ名と秘密鍵: gitのurlを
git@にする場合で、ssh認証を行いたい場合はこれを選択します - ユーザ名とパスワード: gitのurlを
https://にする場合はこれを選択します(たぶん) - GitLab API トークン: APIトークンによる認証をしたい場合はこれを選択します。
GitLabのプラグインが必要かも。GItLab側でAPIキーの発行を行う必要があります。多分Githubプラグインを入れればGitHubの選択肢も出てくると思われます(たぶん)
- SSHユーザ名と秘密鍵: gitのurlを
- 今回はSSH認証を行うので、
SSHユーザ名と秘密鍵を選択し、必要な情報を記入していきます- Domain: グローバルドメイン
- スコープ: グローバル
- ID: Jenkins上の認証情報に対して一意になる名前をつける
- ユーザ名: 対象のユーザ名
- 秘密鍵: 秘密鍵(id_rsa.pubではなくid_rsaの方)の内容を貼り付け
- パスフレーズ: 認証を行う場合は、パスフレーズを記入
- クレデンシャルの種類は3択だと思います
- これでジョブ実行時にワークスペース直下に対象のプロジェクトが
git cloneされた状態になります- 直下にプロジェクトフォルダができるのではなく、直下にプロジェクトフォルダの内容が展開されます
Jenkins CLIを使えるようにする
こちらのマニュアルを参考に進めます。
今回は、sshポートを開けれないケースを想定して、CLIクライアントを利用することにします。
-
jenkins-cli.jarを入手します- 管理画面トップから
Jenkins CLIに移動します -
jenkins-cli.jarダウンロードリンクがあるので、そこからダウンロードします
- 管理画面トップから
- APIトークンを発行します
- 管理画面トップ >
ユーザの管理> CLI経由で接続するユーザ >設定でユーザ設定画面に移動します - APIトークン欄で
Add new Tokenします。その際のトークンを覚えておいてください。
- 管理画面トップ >
- CLIを実行する
java -jar jenkins-cli.jar [-s JENKINS_URL] -auth user_name:api_token help- 使えるcommandの一覧が出てきたらOKです。
- うまく行かない場合、原因調査します
- まずは標準出力ログを確認します
-
java -jar jenkins-cli.jar -logger FINE [-s JENKINS_URL] -auth user_name:api_token helpのように-loggerオプションで標準出力の内容を詳細レベルにすることができます
-
- Jenkinsサーバ側のログを確認します
- 管理画面トップから
システムログに移動します -
すべてのログからログを確認します
- 管理画面トップから
- まずは標準出力ログを確認します
-
full-duplex channel timeoutが原因の場合- ログ確認
- 標準出力に
getOrCreateProvider(EdDSA) created instance of net.i2p.crypto.eddsa.EdDSASecurityProviderと出力されます - システムログに
java.io.IOException: HTTP full-duplex channel timeout: dba62b92-ee7c-49b3-0qewkjba-dsfopiuadfqweと出力されます
- 標準出力に
- 原因
- nginxやapacheなどのリバースプロキシを経由して、jenkinsにアクセスしている場合、リバースプロキシが正しく設定されいないと全二重HTTP通信でタイムアウトが発生するようです
- 具体的には、こちらのISSUEを踏んでいる可能性があります
- 対処
- nginxを使っている場合は、proxyの設定(/etc/nginx/sites-available)に以下を追加します
server { location / { proxy_http_version 1.1; # 1.0だと以下の二つの設定が効かないので、1.1にする必要がある proxy_request_buffering off; proxy_buffering off; : - nginxを再起動(systemctl restart nginx)して、再度
java -jar jenkins-cli.jar ...を実行して、コマンド一覧が出てくればOKです
- nginxを使っている場合は、proxyの設定(/etc/nginx/sites-available)に以下を追加します
- ログ確認
ジョブとビュー定義を別のJenkinsにコピーする(staging環境からproduction環境へ)
いくつか方法があるようですが、Jenkins CLIを利用する方法で実装しました。
今回は、staging環境からproduction環境へ全ジョブを単純にコピーするケースを想定します。
- まず、上記の
Jenkins CLIを使えるようにするの手順でstatging環境とproduction環境のJenkins両方で、Jenkins CLIを使えるようにします - あとは、以下のようなスクリプトを記載するだけです
- staging環境から
list-jobsでジョブリストを取得する - ジョブ1件毎に以下の処理を行う
- staging環境から
get-jobでジョブ定義XMLを取得する - production環境から
get-jobですでにジョブが存在するか確認する- すでに存在したらproduction環境に
update-jobでジョブを上書きする -
delete-jobしてcreate-jobする手もあるが、一度削除するとビューから外れてしまうので、update-jobの方がいいと思います - 存在しなかったらproduction環境に
create-jobでジョブを作成する
- すでに存在したらproduction環境に
- staging環境から
- ビューもジョブと同じように実装すれば大丈夫です
具体的には、以下のようなスクリプトになりました。このスクリプトをstaging環境のJenkinsジョブとして登録して、実行できるようにしています。
#!/bin/bash
set -euC
# グローバル定数
readonly JAR_PATH=/usr/share/jenkins/jenkins-cli.jar
readonly STG_JENKINS_URL="https://stg.hoge.huga/"
readonly STG_USER="admin_user"
readonly STG_API_TOKEN="0245691pdjf8414g93841"
readonly STG_JENKINS_CLI="java -jar ${JAR_PATH} -s ${STG_JENKINS_URL} -auth ${STG_USER}:${STG_API_TOKEN}"
readonly PRD_JENKINS_URL="https://prd.hoge.huga/"
readonly PRD_USER="admin_user"
readonly PRD_API_TOKEN="4598fg09w745bg8dvkn2"
readonly PRD_JENKINS_CLI="java -jar ${JAR_PATH} -s ${PRD_JENKINS_URL} -auth ${PRD_USER}:${PRD_API_TOKEN}"
# ジョブをコピーする
function copy_jobs() {
# ジョブリスト取得
local jobs=($(${STG_JENKINS_CLI} list-jobs))
# ジョブ件数分繰り返し
for job in "${jobs[@]}"; do
set +e
local prd_xml="$(${PRD_JENKINS_CLI} get-job ${job} 2>&1)"
local stg_xml="$(${STG_JENKINS_CLI} get-job ${job})"
set -e
if [[ "${prd_xml}" == "" ]]; then
echo "${stg_xml}" | ${PRD_JENKINS_CLI} create-job "${job}"
else
echo "${stg_xml}" | ${PRD_JENKINS_CLI} update-job "${job}"
fi
done
}
# ビューをコピーする
function copy_views() {
# ビューリスト取得 ※CLIで直接取得できなかったので直指定
local views=("01_ビュー" "02_ビュー" "03_ビュー")
# ビュー件数分繰り返し
for view in "${views[@]}"; do
set +e
local prd_xml="$(${PRD_JENKINS_CLI} get-view ${view} 2>&1)"
local stg_xml="$(${STG_JENKINS_CLI} get-view ${view})"
set -e
if [[ "${prd_xml}" == "" ]]; then
echo "${stg_xml}" | ${PRD_JENKINS_CLI} create-view "${view}"
else
echo "${stg_xml}" | ${PRD_JENKINS_CLI} update-view "${view}"
fi
done
}
# メイン処理
copy_jobs
copy_views
Jenkins本体のバージョンUP
新しいバージョンのJenkinsが存在する場合、画面上部からjenkins.warのダウンロードができますので、それをサーバ上のwarファイルと置き換えるだけです。
- 実行中のwarファイルの場所を確認します
- 管理画面トップから
システム情報に移動します -
executable-warの値がwarファイルの場所になります(例:/usr/share/jenkins/jenkins.war)
- 管理画面トップから
- jenkins.warファイルのダウンロードします
- サーバにコピーします
scp ~/Downloads/jenkins.war hoge_server:/tmp/
- ファイルを実行中のファイルと置き換えます
mv /usr/share/jenkins/jenkins.war /usr/share/jenkins/jenkins.war_bkmv /tmp/jenkins.war /usr/share/jenkins/chown root:root /usr/share/jenkins/jenkins.war
- jenkinsを再起動します
systemctl restart jenkins
jenkinsの実行ユーザを変更
ジョブのシェルスクリプトをデフォルトのjenkinsユーザではなく、別のユーザで実行したいケースがあると思います。
コマンド単位で別ユーザにしたい場合
まず、visudo でjenkinsユーザにsudo権限を与えます。ケースに応じて与える権限を検討する必要があります。
visudo
+ # 全権限をhogeユーザに付与
+ jenkins ALL=(hoge) NOPASSWD: ALL
+ # git実行権限をhogeユーザに付与
+ jenkins ALL=(batch) NOPASSWD: /usr/bin/git *
+ # 環境変数を引き継いで(つまりsudo -E)、shを実行できる権限をhogeユーザに付与
+ jenkins ALL=(batch) NOPASSWD: SETENV: /bin/sh *
そのうえで、ジョブのシェルスクリプト内で、コマンドを個別に別ユーザで実行していきます。
sudo -u hoge git fetch
sudo -Eu hoge sh -c "echo fuga"
一律別ユーザにしたい場合
最初は頑張ってコマンド単位でsudoしていたのですが、.bashrcを読み込む必要があるケースが出てきて、sudo -iu hogeしたところ/home/hoge/にカレントディレクトリが変更されてしまうことがあり、その整合を取るために色々頑張っていたのですが、途中で馬鹿らしくなり、そもそもシェルスクリプトを別ユーザで実行する方法を探しました。
自分の調査ではシェルスクリプトだけを別ユーザで実行する方法は見つからなかったので、jenkins自体を別ユーザで起動することにしました。
jenkinsを別ユーザで起動すると、シェルスクリプトも同じように別ユーザで実行されます。
sudo -iu root
vi /etc/default/jenkins
- JENKINS_USER=$NAME
+ JENKINS_USER=hoge
chown -R hoge: /var/lib/jenkins /var/log/jenkins /var/cache/jenkins
systemctl restart jenkins
developerとしての設定
ビュー(タブ)の作成
- Jenkinsトップの
+タブをクリックして、ビュー作成画面に移動します -
リストビューを選択します - ビューに含めたいジョブを選択して保存します
ジョブの作成
- Jenkinsトップもしくは各ビューで
新規ジョブ作成をクリックしジョブ作成画面に移動します -
copy fromで既存のジョブをコピーするか、ジョブの種類を選択して作成します- 基本的には
フリースタイル・プロジェクトのビルドを利用することが多いと思います
- 基本的には
- 詳細は多岐にわたるため割愛します。
パイプライン ジョブの作成
- Jenkins Pipeline プラグインが必要です
- 公式のドキュメントはこちらです
- ジョブの一括実行の観点
- パイプラインは複数のジョブをまとめて定義することができるとても便利なジョブです
- こんな感じのスクリプトで定義します
node { stage('hoge stage') { build job: 'hoge' } stage('fuga stage') { build job: 'fuga', parameters: [ string(name: 'param1', value: 'xxx'), string(name: 'param2', value: 'yyy') ] } }
- CIの観点
- パイプラインの定義は
Jenkinsfileで定義でき、プロジェクトのGITリポジトリで管理できます(設定のコード化ができる) - その上で、commitされたら
Jenkinsfileの定義に従って、パイプラインジョブが実行されるようにできます - agentにdockerを設定することで、独立した環境でテストすることも可能ですし、複数のstageを定義して並列で実行させるようなこともできます
- GitlabCIなどと同等の機能が提供されている印象です。CIの観点でもJenkins Pipelineは有効かもしれません
- パイプラインの定義は