🎀

Jenkins & GitHub WebhooksでROS 2のビルド・テストを自動化する

2023/07/26に公開

海洋ロボコンをやってた人です。

今回はJenkinsを触ってROS 2パッケージのビルド・テストを自動化してみたので、その備忘録として記載していきます。

構成図は以下です。

JenkinsとはオープンソースのCI/CDツール※1, 2 で、Javaで書かれたWebアプリケーションにてソフトウェアのビルド・テスト・デプロイメントなどのプロセスを自動化できます。
これにより、変更を本番環境に反映させるという手間の改善が期待されます。

※1 CI: Continuous Integration(継続的インテグレーション)
※2 CD: Continuous Deployment(継続的デプロイメント)

記事を書いた理由は以下です。

  • CI/CDを触りながら学びたいため
  • 後から使い方を自分で見直せるようにするため


CI/CDツールにはGitHub Actionsを始め、様々なツールがあると思います。

一方、Zenn: Github Actionsの「テンション上がるところ」と「注意点」にもあるように、Jenkinsと比べて管理しづらいところもあるそうなので、手始めにJenkinsを学ぶことにしたのが事の経緯です。

初学者なので、何かコメントあれば積極的に募集します。


使用機器と環境は以下です。

  • Jenkinsサーバー側: docker on Ubuntu 22.04 Laptop PC

    • jenkins --version 2.401.2 (直でインストールする場合)
    • Docker version 20.10.21, build 20.10.21-0ubuntu1~22.04.3
    • docker-compose version 1.29.2, build unknown
  • リモートサーバー側: Ubuntu 20.04 Jetson Nano B01

    • ROS 2 Foxy


※ 2023/8 GitHub ActionsとJenkinsどっちを使用するのが良いのか?という疑問に対しては、使用する際の環境と以下の相違点を踏まえて判断していただければと思います。

https://www.enterprisenetworkingplanet.com/management/github-actions-vs-jenkins/

Jenkinsのインストール

この章ではJenkinsのインストール方法を2種類記載しますが、表題の自動化はDockerを使用しています。

お手元の使用環境に応じて、選択してください。

Jenkinsを直でインストール

まずは、Jenkinsを直でインストールする方法を記載します。

環境の再現性、拡張性、管理の容易性の観点から考えると、Docker上でJenkinsサーバーを動かす方が良いと思いますが、設定のシンプルさでは直インストールが楽かもしれません。

  1. Jenkinsに必要なJavaをインストール
terminal
sudo apt install default-jre
java -version

sudo apt install default-jdk
sudo apt install openjdk-17-jdk
javac -version
  1. 最新リポジトリの著名鍵:signing keysを取得
terminal
curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key | sudo tee \
  /usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
  https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
  /etc/apt/sources.list.d/jenkins.list > /dev/null

https://www.jenkins.io/blog/2023/03/27/repository-signing-keys-changing/

  1. Jenkinsのインストール
terminal
sudo apt update
sudo apt install jenkins
jenkins --version
  1. Jenkinsをインストール後、JenkinsサーバーがActiveかどうかを確認
terminal
sudo systemctl status jenkins

もしstatusがActiveでなければ、sudo systemctl start jenkinsでjenkins.serviceをActiveにします。

ファイアウォール有効の確認もしておきましょう。

terminal
sudo ufw enable
  1. Jenkinsサーバーの動作確認

Active後、以下をブラウザで入力するとJenkinsサーバにアクセスすることができます。

terminal
http://localhost:8080/

もしJenkinsサーバーにアクセスできない場合は、/etc/hostsを確認します。
また、Jenkinsサーバーの初期パスワードは/var/lib/jenkins/secrets/initialAdminPasswordで確認できます。

terminal
sudo cat /etc/hosts
sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Unlock Jenkins画面で初期パスワードを入力後、Customize Jenkins画面に遷移します。

こちらはコミュニティで最も必要だと知られているプラグインの設置:Install suggested pluginsを選択で良いと思います。

※ 調査中
http://localhostでなく、マイコン等にJenkinsを直インストールし、マイコンのIPアドレスを使用してJenkinsサーバーにアクセスした際、プラグインインストールのGitでエラーになることがありました。
こちらは先にlocalhostでInstall suggested pluginsのGitHubをインストール後に、Jenkinsサーバーを再起動することで解決しましたが、原因が分かり次第追記します。

Create Admin User画面では、ユーザー名やパスワードの設定後、Save and Continueで設定を保存します。

ユーザー名やメールアドレスはあとからダッシュボード>Jenkinsの管理>Usersから変更可能です。

これでログインできれば、Jenkinsで遊ぶ準備は完了です。


もしJenkinsサーバーの停止や、Jenkinsの削除するときは、以下を実行します。

terminal
sudo systemctl stop  jenkins
sudo apt autoremove jenkins

Jenkins-dockerのインストール

続いて表題のビルド・テスト環境で使用した、Jenkins-dockerのインストール方法も記載します。

ディレクトリ構成は以下のとおりです。

directory-tree
jenkins-docker
  ├ Dockerfile
  ├ docker-compose.yaml
  1. docker-composeのインストール

複数のコンテナを使用するためにdocker-composeをインストールします。

terminal
sudo apt install docker-compose
docker-compose --version  # docker-compose version 1.29.2, build unknown
  1. docker-compose.yaml等のファイル作成

jenkins-dockerというフォルダ下にDockerfileとdocker-compose.yamlを格納します。

terminal
mkdir -p jenkins-docker && cd ~/jenkins-docker/
sudo nano Dockerfile
sudo nano docker-compose.yaml

Dockerfileにて、jenkins/jenkins:lts-jdk11のイメージをベースにDockerイメージを作成します。

Dockerfile
FROM jenkins/jenkins:lts-jdk11
RUN jenkins-plugin-cli --plugins pipeline-model-definition github-branch-source:1.8

docker-compose.yamlにてjenkinsとssh-agentというコンテナを定義します。

docker-compose.yaml
services:
  jenkins:
    image: jenkins/jenkins:lts
    ports:
      - "8080:8080"
    volumes:
      - jenkins_home:/var/jenkins_home
  ssh-agent:
    image: jenkins/ssh-agent
volumes:
  jenkins_home:

portsはホストのポート8080をコンテナのポート8080にマッピングしています。
volumesはJenkinsコンテナ内の/var/jenkins_homeディレクトリに保存されるデータを、`jenkins_homeという名前の永続ボリュームに永続的に保存します。

Dockerはデフォルトで永続化ボリュームの名前を<プロジェクト名>_<jenkins_home>として関連付けています。

  1. jenkins-dockerの起動を確認する

以下のコマンドでdocker-composeを起動します。

terminal
sudo docker-compose up

http://localhost:8080/にアクセスし、JenkinsサーバーにアクセスできればOKです。

initialAdminPasswordはdocker-compose upするとターミナルに表示されているので、そちらを見つけて使用しましょう。

  1. docker関連のコマンド

docker-composeを触る上で、使用したdockerコマンドも併記しておきます。

terminal
sudo docker-compose ps
sudo docker-compose images
sudo docker-compose down
sudo docker volume ls
sudo docker volume prune # ボリュームをすべて削除

JenkinsでROS 2のビルトとテストを試す

Jenkinsサーバーの準備ができたら、本題のROS 2ビルド&テストを試していきます。

Jenkinsジョブの設定

Jenkinsサーバー側でGitHubのpushによりシェルスクリプトを実行するジョブを作成します。

  1. フリースタイル・プロジェクトの作成

Create a job > Enter an item name と進み、build_and_testのようにジョブ名をつけてフリースタイル・プロジェクトとして作成します。

  1. GitHubの設定

ROS 2パッケージのビルドとテストを試すようにros2_topic_sampleを準備したので、このリポジトリのpushがトリガになるように以下3つを設定します。

General > GitHub project > Project urlへgitのweb URLを記入します。

ソースコード管理 > Git > リポジトリURLにもgitのweb URLを記入します。

ビルド・トリガ > GitHub hook trigger for GITScm polling にチェック

※ Jenkinsの管理 > Plugins > Available plugins > GitHub Pipeline for Blue Ocean
でGitHub用のプラグインを使用する方法もあるようです。

  1. シェルスクリプトの記入

Build Steps > シェルの実行(Execute shell)を選び、Githubのpushに応じて実行されるシェルを記入します。

GitHubと紐付けると、対象のリポジトリが/var/jenkins_home/workspace/ジョブ名(ここではbuild_and_test)下にクローンされるので、その内容に対しての処理を行います。

流れとしてはros2_wsを作成 > リモートマイコンにscpでワークスペースごとコピー > リモートマイコン上でcolcon build & colcon testを実行
という形です。

また後述しますが、Jenkins側でssh接続等する場合はパスワード入力等ができないので、事前に公開鍵などを登録しておく必要があります。

シェルの実行
#!/bin/bash
pwd
ls
mkdir -p ros2_ws/src/ros2_topic_sample

#! 現在のディレクトリのファイルを一時的に記録
files=$(ls -A)
#! 一時的なフォルダにファイルを移動
echo "file move"
for file in $files
do
  if [ "$file" != "ros2_ws" ]; then
    mv -f "$file" /var/jenkins_home/workspace/build_and_test/ros2_ws/src/ros2_topic_sample
  fi
done

#! リモートマイコンのIPアドレスとユーザー名を設定
remote_ip="192.168.11.7"
remote_user="jetson"
remote_path="/home/jetson"
#! Jenkinsホームディレクトリのパス
jenkins_home="/var/jenkins_home"

#! マイコンにros2_wsディレクトリを送信
echo "---scp shellscript---"
scp -i "$jenkins_home/.ssh/id_rsa_new" -r "$jenkins_home/workspace/build_and_test/ros2_ws" "$remote_user@$remote_ip:$remote_path"

#! リモートマイコン上でcolcon buildを実行
echo "---ssh shellscript---"
ssh -i "$jenkins_home/.ssh/id_rsa_new" "$remote_user@$remote_ip" <<EOF
source /opt/ros/foxy/setup.bash
echo "---colcon build script---"
cd $remote_path/ros2_ws && colcon build
echo "---colcon test script---"
colcon test
colcon test-result
EOF

echo "---Finish---"

Jenkinsからssh接続するための設定

上記でも記載しましたが、Jenkinsのシェル実行でsshコマンドを記載しても、パスワードを求められるので、接続できません。

そこで、Jenkinsサーバーが起動しているdocker containerに入り、リモートマイコンとの公開鍵を設定する必要があります。

terminal
sudo docker container exec -it -u 0 jenkins-docker_jenkins_1 bash

dockerコンテナ内に入ったら、以下を実行し公開鍵の生成と表示確認します。

公開鍵ですが、管理者には全権限を付与し、他ユーザーには書込+読込の権限を付与するためにchmod 766を設定しています。
公開鍵暗号方式での接続なので、書込+読込権限付与はセキュリティ面で良くないと思いますが、一旦はこちらで進めます。

Jenkinsサーバー
ssh-keygen -t rsa -b 4096 -f /var/jenkins_home/.ssh/id_rsa_new -C "Jenkins SSH Key" -N ""
chmod 766 /var/jenkins_home/.ssh/id_rsa_new
# Jenkinsサーバー上の公開鍵を表示
cat /var/jenkins_home/.ssh/id_rsa_new.pub

公開鍵を確認できたら、リモートマイコン側に接続し、公開鍵の内容をauthorized_keysにコピーしてください。ssh-copy-id 接続される側@接続するhost側を使用しても公開鍵をコピーできます。

ssh対象のサーバー(SBC)
nano ~/.ssh/authorized_keys

上記が上手く設定できていれば、Jenkinsサーバーで以下を実行した際に接続ができるようになります。

Jenkinsサーバーで確認
ssh -i /var/jenkins_home/.ssh/id_rsa_new jetson@192.168.11.7

Jenkins URLの設定

JenkinsとGitHubを連携する際にはgithub-webhookを使用します。

github-webhookですが、JenkinsサーバーのURLがhttp://localhostから始まっていると使用できません。

方法としては

・Jenkinsの管理 > Jenkinsのユーザーデータベース > Setting > APIトークンを生成

・Jenkinsの管理 > System > Jenkinsの位置 > Jenkins URLの変更

などもありますが、

Youtube: Jenkins #6 | Auto Trigger Jenkins Jobs using GitHub Webhooksを参考にngrokでhttps経由でアクセスできるようにしました。

ngrokのインストールおよび起動は以下です。

terminal
snap install ngrok 
ngrok http 8080

httpsに変換されたURLでJenkinsサーバーにアクセスできればOKです。

また、httpsに変換されたURLはgithub-webhookのPayload URLで使用するので、コピーしておいてください。

GitHub側の設定

最後にGithub側で設定をしていきます。

対象のリポジトリで Setting > Left Panel: Webhooks >> Add webhookと進み、5項目を以下のように設定します。

  1. Payload URL: ngrokで取得したhttpsのURL/github-webhook/ ※ ex: https://abcdefg/github-webhook/

  2. Content type: application/x-www-form-urlencoded

  3. SSL verification: Enable SSL verification

  4. Which events would you like to trigger this webhook?: Let me select individual events > Pushes

  5. Active: check

上手く設定できていれば、以下のように✅マークが表示されます。

もし✅マークがつかず上手く接続できない場合は、「Edit」ではなくWebhooksを一度「Delete」後に再度Webhooksを追加してみてください。


GithubのpushイベントによってJenkinsのジョブが起動すればOKです。

以上、お疲れ様でした。

Reference

  • Jenkins

How to install Jenkins on Ubuntu 22.04

The Construct: Jenkins Basics for Robotics | ROS2 Developers Open Class

TECH ART ONLINE: Jenkinsの使い方と環境構築方法、定期的にビルドする環境を作ろう!

  • Jenkins-Docker

GitHub: jenkinsci/docker

Zenn: JenkinsをDocker上で動かす

  • Jenkins-ngrok

ngrokを使ってJenkinsを試す

Discussion