実際の問題:ドキュメント通りに設定してもJenkinsが動かない
先月、8人の開発チーム向けのCI/CDシステム構築を完了しました。会社がサーバーをRHEL互換シリーズに統一しているためCentOS Stream 9を選択したのですが、「Jenkinsのインストール」が公式ドキュメントのコピペほど簡単ではないことを痛感しました。
Jenkins公式ドキュメントに従いインストールコマンドを実行し、サービスを起動するとactiveと表示されるものの、ブラウザで開いてみると:
curl http://localhost:8080
# curl: (7) Failed to connect to localhost port 8080: Connection refused
Jenkinsのログにはこのような行が並んでいます:
Nov 08 10:23:41 server jenkins[12345]: java.io.IOException: Failed to bind to 0.0.0.0/0.0.0.0:8080
さらにNginxリバースプロキシを前段に設置しても、設定が正しいように見えてもリクエストが転送されませんでした。公式ドキュメントでは十分に言及されていない3つの問題が重なっていたのです。
原因の分析
約2時間のデバッグの末、3つの主な原因を特定しました:
1. Javaバージョンの非互換性
Jenkins LTS 2.426以降はJava 17またはJava 21が必要です。CentOS Stream 9はデフォルトでリポジトリからjava-11-openjdkを取得します。Java 17を正しくインストールしていたにもかかわらず、alternativesコマンドを実行していなかったため、システムはJava 11をデフォルトとして使い続けていました。
# 現在アクティブなJavaを確認
java -version
# openjdk version "11.0.21" — 間違い!
# インストール済みの全Javaバージョンを確認
alternatives --list | grep java
2. SELinuxがJenkinsのポートバインドとファイルの読み書きをブロック
CentOS Stream 9はデフォルトでSELinuxがenforcingモードになっています。Jenkinsはポート8080のバインドおよびホームディレクトリへの読み書きが必要ですが、contextとportタイプを正しく設定していなければすべて拒否されます。
# Jenkinsに関連するSELinuxの拒否ログを確認
grep "avc: denied" /var/log/audit/audit.log | grep jenkins | tail -20
3. Nginxがアップストリームへのプロキシ接続を許可されていない
Nginxはhttp://localhost:8080にリクエストを転送しますが、SELinuxのboolean httpd_can_network_connectがデフォルトで無効のため、同じサーバー上でもNginxはアウトバウンド接続を確立できません。
解決策の選択肢
方法1:SELinuxを無効化——手っ取り早いが危険
ネット上の多くのガイドでは/etc/selinux/configでSELINUX=permissiveを設定することで解決しています。確かに手っ取り早いのですが、これは本番環境での重大な誤りです。Jenkinsは開発者のビルドスクリプトを実行するため、SELinuxを無効化すると任意のスクリプトがサーバー上で自由に権限昇格できてしまいます。
方法2:JenkinsをDockerで動かす
ホストのSELinuxを気にせず完全に分離できます。ただし実際には、ボリュームマウントを使ったイメージビルド時のDocker-in-Docker問題に2〜3チームが悩んでいるのを目にしました。SELinuxの設定よりもデバッグが大変です。シンプルなベアメタルのJenkinsが必要なだけであれば、不要な複雑さを追加することになります。
方法3:SELinuxとNginxを最初から正しく設定する
セットアップに約30分かかりますが、安定した構成が得られます。私のサーバーは6ヶ月間、インフラ関連の問題なく安定稼働しています。
最善の方法:ステップバイステップガイド
ステップ1:システムの準備
sudo dnf update -y
sudo dnf install -y wget curl policycoreutils-python-utils
ステップ2:Java 17のインストールとデフォルト設定
このステップで最も見落とされがちなのが「デフォルト設定」の部分です——Java 17をインストールしても、この手順を省くとJenkinsは古いJava 11を使い続けます。
# Java 17をインストール
sudo dnf install -y java-17-openjdk java-17-openjdk-devel
# Java 17をデフォルトに設定(対話形式——対応する番号を選択)
sudo alternatives --config java
# 確認
java -version
# openjdk version "17.x.x" — 正しい
ステップ3:Jenkins LTSのインストール
# Jenkinsリポジトリを追加
sudo wget -O /etc/yum.repos.d/jenkins.repo \
https://pkg.jenkins.io/redhat-stable/jenkins.repo
# GPGキーをインポート
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io-2023.key
# Jenkinsをインストール
sudo dnf install -y jenkins
# 有効化して起動
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkins
ステップ4:テスト用に一時的にファイアウォールを開放
# ポート8080を一時的に開放——Nginx設定後に閉じる
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
ステップ5:JenkinsのためのSELinux設定
公式ドキュメントが完全に無視している最も重要な部分です。
# SELinuxがenforcingモードであることを確認
getenforce
# Enforcing
# Jenkinsはポート8080をバインドする必要がある——http_port_tに追加
sudo semanage port -a -t http_port_t -p tcp 8080
# "already defined"エラーが出た場合、現在のtypeを確認
sudo semanage port -l | grep 8080
# ポートが別のtypeの場合(例:webcache_port_t)、修正する
sudo semanage port -m -t http_port_t -p tcp 8080
# JenkinsホームのSELinuxコンテキストを確認——RPMで正しくインストールすれば自動設定される
ls -laZ /var/lib/jenkins
JENKINS_HOMEを別のディレクトリに変更した場合、手動でrelabelが必要です:
sudo semanage fcontext -a -t jenkins_home_t "/custom/jenkins/home(/.*)?"
sudo restorecon -Rv /custom/jenkins/home
ステップ6:Nginxリバースプロキシのインストールと設定
sudo dnf install -y nginx
sudo nano /etc/nginx/conf.d/jenkins.conf
/etc/nginx/conf.d/jenkins.confの内容:
upstream jenkins {
server 127.0.0.1:8080 fail_timeout=0;
}
server {
listen 80;
server_name jenkins.yourdomain.com;
location / {
proxy_pass http://jenkins;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host:$server_port;
# アーティファクトのアップロードサイズ制限
client_max_body_size 100m;
}
}
ステップ7:NginxプロキシのSELinux booleanを有効化
設定が正しいのにNginxがリクエストを転送できない理由——このbooleanがデフォルトで無効になっているためです:
# Nginxがアップストリームへの接続を許可する
sudo setsebool -P httpd_can_network_connect 1
# 確認
getsebool httpd_can_network_connect
# httpd_can_network_connect --> on
# Nginxを起動
sudo systemctl enable nginx
sudo systemctl start nginx
# ポート80を開放
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload
# ポート8080を閉じる——外部公開は不要
sudo firewall-cmd --permanent --remove-port=8080/tcp
sudo firewall-cmd --reload
ステップ8:JenkinsをLocalhostのみでリッスンするよう設定
Nginxを設置した後は、Jenkinsを外部に公開する必要がありません。/etc/sysconfig/jenkinsに以下を追加します:
sudo nano /etc/sysconfig/jenkins
JENKINS_ARGS="--httpListenAddress=127.0.0.1"
JAVA_OPTS="-Xmx2g -Xms512m"
sudo systemctl restart jenkins
ステップ9:Jenkinsの初回ロック解除
# 初期管理者パスワードを取得
sudo cat /var/lib/jenkins/secrets/initialAdminPassword
ブラウザでhttp://jenkins.yourdomain.comを開き、上記のパスワードを入力して推奨プラグインをインストールし、管理者ユーザーを作成します。次にManage Jenkins → System → Jenkins URLで正しいドメインを設定してください——ここを間違えるとGitHub/GitLabのWebhookが動作せず、丸一日デバッグに費やすことになります。
6ヶ月の本番運用で学んだ実践的な知見
私の会社にはまだCentOS 7のサーバーが数台残っており、AlmaLinuxへの移行は既に対応済みです。新しいJenkinsサーバーにはCentOS Stream 9を選びました——RHEL 9の直接アップストリームだからです。Stream 9で正常動作するものは、将来のアップグレード時にRHEL 9でもほぼ確実に動作し、インフラ標準化のリスクを大幅に軽減できます。
6ヶ月の運用で得た実践的なポイントをいくつか紹介します:
- JENKINS_HOMEを定期的にバックアップする:ジョブ設定、ビルド履歴、認証情報はすべて
/var/lib/jenkinsにあります。毎晩cronジョブでS3互換ストレージにrsyncしています。このディレクトリを失うことはCI/CDセットアップ全体を失うことを意味します。 - コードをデバッグする前にAuditログを確認する:ビルドジョブが謎の失敗をした場合、まず
grep "avc: denied" /var/log/audit/audit.log | tail -30を確認してください。原因がSELinuxによる小さなファイル操作の拒否だけだったのに、1時間近くデバッグに費やしたことが何度かありました。 - JVMヒープを制限する:4GB以下のRAMのサーバーでは、JenkinsがデフォルトでほぼすべてのRAMを消費する可能性があります。ステップ8の
JAVA_OPTS="-Xmx2g -Xms512m"は省略できません。 - Jenkinsのポート8080をインターネットに公開しない:前段にNginxを置きJenkinsをlocalhostのみでバインドするのが標準的なパターンです。Jenkinsには適切なレート制限がないため、8080を公開するとブルートフォース攻撃を招くことになります。
このセットアップは8人のチームで順調に稼働しており、1日平均60〜80ビルドをこなし、過去6ヶ月間ダウンタイムゼロを維持しています。SELinux enforcingとNginxを前段に置く構成は無駄なオーバーヘッドではなく、Jenkinsのような任意のコードを実行できるツールには必要不可欠な防壁です。
