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/jenkinsHTTP_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です

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

  • 管理画面トップからプラグインの管理に移動します
  • 利用可能タブから目的のプラグインを探してチェックし、プラグインをインストールします
  • 今回はAuthorize ProjectRole-based Authorization Strategyの二つのプラグインをインストールして、再起動しました

ユーザの管理

  • 管理画面トップからユーザの管理に移動します
  • ユーザ作成をクリックして、ユーザを作成します

グローバルセキュリティの設定

  • 管理画面トップからグローバルセキュリティの設定に移動します
  • セキュリティを有効化をチェック(デフォルトなので何もしなくていいですが)
  • ユーザ情報の設定します
    • 今回はJenkinsのユーザデータベースをチェックします
    • 勝手にユーザ登録されたくないので、ユーザにサインアップを許可はチェックしません
  • 権限管理の設定をします
    • 今回はRole-Based Strategyを選択します
      • 事前にRole-based Authorization Strategyプラグインのインストールが必要です
      • Authorize Projectプラグインがインストールされ設定されてないと、ワーニングが表示されます
      • 行列による権限設定はユーザやグループ単位にやれることを設定できますが、Jenkinsのユーザデータベースを利用している場合は新しいグループを追加したりできないようです。LDAPUnix user/group databaseを利用する場合、グループがそのまま利用できます。なのでユーザ単位ではなくグループ単位で権限設定を行いたい場合はRole-Based Strategyが必要になります
      • ログイン済みユーザに許可はログインユーザであれば誰でも何でもできます
      • 全員に許可はJenkinsにアクセスできれば、ログインせずとも誰でも何でもできます
  • Access Control for Buildsの設定をします
    • Buildの実行ユーザの権限を指定する設定です
    • 今回は面倒なので、全プロジェクトに適用するジブド実行の権限を指定を選択し、システムユーザーの権限で実行するにしました
      • ただしこの設定だと、SYSTEMユーザで実行しちゃってるよーとワーニングが出るのでDISMISSする必要があります
    • プロジェクト単位でコントロールしたい場合はプロジェクト設定内でビルド実行の権限を設定するを選びます
      • ちなみに、Jenkinsにおいてプロジェクトジョブは同一と考えて良さそうです
      • ビルドを起動したユーザの権限で実行する 指定したユーザの権限で実行する システムユーザの権限で実行する など、プロジェクト設定に表示する選択肢をチェックしたうえで、ジョブの権限の設定欄から、上記でチェックした選択肢からプルダウンで選択できるようになります

Roleの設定

  • 管理画面トップからManage and Assign Rolesに移動します

  • Manage Rolesで新しいRoleを追加します

    • 今回は3つのGlobal Roleを追加することにしました
      • admin : システム管理者ユーザ。なんでもできます
        • これはデフォルトで存在します。全ての付与されています
      • developer:エンジニアユーザ。ジョブやビューの作成ができます
        • 全体:Read、ジョブ:全部、ビルド:全部、ビュー:全部 にチェックします
      • executer:特定のジョブの実行だけができるユーザ。
        • 全体:Read だけをチェックします
          • ビルド権限をつけたくなりますが、Global Roleと Project RoleはOR条件なので、Project Roleの方で付けます
    • Project Roleを追加します
      • hoge-executerPattenには対象のジョブ名を正規表現で指定します
        • ジョブ:Build、Cancel、Read にチェックします
        • 正規表現でジョブ名を指定することを前提に、ジョブ名の接頭語をパターン化しておくことをおすすめします。具体的には部署名とかプロジェクト名を接頭語としてつけておくと楽です
  • Assing Rolesでユーザに対してRoleをアサインします

    • システム管理者ユーザには、Global Roleのadminを割り当てます
    • エンジニアユーザには、Global Roleのdeveloper を割り当てます
    • 特定のジョブの実行ユーザは、Global RoleのexecuterとProject Roleのhoge-executerを割り当てます
    • Anonymous(未ログインユーザ)には何もRoleを割り当てません

Git連携

  • Git連携を行うにはGitプラグインが必要です
  • ジョブ側でGitからソースを取得する設定を行います
    • ジョブの設定画面に移動します
    • ソースコードの管理Gitを選択します
    • リポジトリURLにURL(今回はgit@の方)を入力します
    • 認証情報の追加ボタンをクリックして、認証情報を作成します
      • ちなみに追加した認証情報はJenkinsトップの左側メニューの認証情報欄から変更できます
  • 認証情報を設定します
    • クレデンシャルの種類は3択だと思います
      • SSHユーザ名と秘密鍵: gitのurlをgit@にする場合で、ssh認証を行いたい場合はこれを選択します
      • ユーザ名とパスワード: gitのurlをhttps://にする場合はこれを選択します(たぶん)
      • GitLab API トークン: APIトークンによる認証をしたい場合はこれを選択します。GitLabのプラグインが必要かも。GItLab側でAPIキーの発行を行う必要があります。多分Githubプラグインを入れればGitHubの選択肢も出てくると思われます(たぶん)
    • 今回はSSH認証を行うので、SSHユーザ名と秘密鍵を選択し、必要な情報を記入していきます
      • Domain: グローバルドメイン
      • スコープ: グローバル
      • ID: Jenkins上の認証情報に対して一意になる名前をつける
      • ユーザ名: 対象のユーザ名
      • 秘密鍵: 秘密鍵(id_rsa.pubではなくid_rsaの方)の内容を貼り付け
      • パスフレーズ: 認証を行う場合は、パスフレーズを記入
  • これでジョブ実行時にワークスペース直下に対象のプロジェクトが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です

ジョブとビュー定義を別の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でジョブを作成する
  • ビューもジョブと同じように実装すれば大丈夫です

具体的には、以下のようなスクリプトになりました。このスクリプトを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_bk
    • mv /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は有効かもしれません