CLOVER🍀

That was when it all began.

GitLab Runnerのrun-singleコマンドを使って、単一のGitLab CI/CDジョブを実行する

これは、なにをしたくて書いたもの?

GitLab Runnerにはrun-singleというコマンドがあります。

GitLab Runner commands / Run-related commands / gitlab-runner run-single

こちらを使うと、単一GitLab CI/CDジョブを実行できるGitLab Runnerを使えるようなので試してみます。

gitlab-runner run-singleコマンド

GitLab Runnerで使えるコマンドはこちらです。

GitLab Runner commands | GitLab Docs

run-singleコマンドについてはこちらです。

GitLab Runner commands / Run-related commands / gitlab-runner run-single

単一のGitLabインスタンスから単一のビルドを実行するコマンドであるとされています。

Use this supplementary command to run a single build from a single GitLab instance.

またrunコマンドとは違い、GitLabのURLやRunnerのトークンを含め、すべてのオプションをCLIのオプションや環境変数として
指定します。

Take all options either as CLI parameters or environment variables, including the GitLab URL and Runner token. For example, a single job with all parameters specified explicitly:

ちょっと変わったコマンドだなと思うので、試してみましょう。

環境

今回の環境はこちら。

GitLab。192.168.0.7で動作しているものとします。

$ sudo gitlab-rake gitlab:env:info

System information
System:         Ubuntu 24.04
Current User:   git
Using RVM:      no
Ruby Version:   3.3.10
Gem Version:    3.7.1
Bundler Version:2.7.1
Rake Version:   13.0.6
Redis Version:  7.2.11
Sidekiq Version:7.3.9
Go Version:     unknown

GitLab information
Version:        18.10.1
Revision:       6bef35b5226
Directory:      /opt/gitlab/embedded/service/gitlab-rails
DB Adapter:     PostgreSQL
DB Version:     16.11
URL:            http://192.168.0.7
HTTP Clone URL: http://192.168.0.7/some-group/some-project.git
SSH Clone URL:  git@192.168.0.7:some-group/some-project.git
Using LDAP:     no
Using Omniauth: yes
Omniauth Providers:

GitLab Shell
Version:        14.47.0
Repository storages:
- default:      unix:/var/opt/gitlab/gitaly/gitaly.socket
GitLab Shell path:              /opt/gitlab/embedded/service/gitlab-shell

Gitaly
- default Address:      unix:/var/opt/gitlab/gitaly/gitaly.socket
- default Version:      18.10.1
- default Git Version:  2.53.gc61120c

GitLab Runner。

$ gitlab-runner --version
Version:      18.10.0
Git revision: ac71f4d8
Git branch:   18-10-stable
GO version:   go1.25.7 X:cacheprog
Built:        2026-03-16T14:23:19Z
OS/Arch:      linux/amd64

環境はTerraformで構築します。

$ terraform version
Terraform v1.14.8
on linux_amd64

準備

最初にGitLabプロジェクトとGitLab Runnerのトークンの作成を行います。

main.tf

terraform {
  required_version = "1.14.8"

  required_providers {
    gitlab = {
      source  = "gitlabhq/gitlab"
      version = "18.10.0"
    }
  }
}

variable "root_access_token" {
  type      = string
  ephemeral = true
}

provider "gitlab" {
  token    = var.root_access_token
  base_url = "http://192.168.0.7/"
}

resource "gitlab_group" "sample_group" {
  name = "sample group"
  path = "sample-group"

  visibility_level = "private"
}

resource "gitlab_project" "sample_app" {
  name         = "sample-app"
  namespace_id = gitlab_group.sample_group.id

  default_branch = "main"

  visibility_level = "private"

  auto_devops_enabled = false

  only_allow_merge_if_pipeline_succeeds            = true
  only_allow_merge_if_all_discussions_are_resolved = true
}

resource "gitlab_branch_protection" "main_branch" {
  project = gitlab_project.sample_app.id
  branch  = "main"

  allow_force_push = false

  merge_access_level     = "maintainer"
  push_access_level      = "no one"
  unprotect_access_level = "maintainer"
}

resource "gitlab_group_membership" "sample_user" {
  group_id     = gitlab_group.sample_group.id
  user_id      = gitlab_user.sample_user.id
  access_level = "owner"
}

resource "gitlab_user" "sample_user" {
  name     = "sample-user"
  username = "sample-user"
  password = "P@ssw0rd"
  email    = "sample-user@example.com"
}

resource "gitlab_personal_access_token" "sample_user_token" {
  user_id = gitlab_user.sample_user.id
  name    = "sample-user-pat"

  scopes = ["api"]
}

resource "gitlab_user_runner" "instance_runner" {
  runner_type = "instance_type"

  description = "sample instance runner"
  untagged    = true
}

resource "gitlab_user_runner" "group_runner" {
  runner_type = "group_type"

  group_id = gitlab_group.sample_group.id

  description = "sample group runner"
  untagged    = true
}
resource "gitlab_user_runner" "project_runner" {
  runner_type = "project_type"

  project_id = gitlab_project.sample_app.id

  description = "sample project runner"
  untagged    = true
}

output "user_personal_access_token" {
  value     = gitlab_personal_access_token.sample_user_token.token
  sensitive = true
}

output "instance_runner_authentication_toke" {
  value     = gitlab_user_runner.instance_runner.token
  sensitive = true
}

output "group_runner_authentication_token" {
  value     = gitlab_user_runner.group_runner.token
  sensitive = true
}

output "project_runner_authentication_toke" {
  value     = gitlab_user_runner.project_runner.token
  sensitive = true
}

グループ、プロジェクト、ユーザーを作成し、ユーザーのPersona Access TokenやGitLab Runnerのトークンも作成します。

rootアカウントのPersonal Access Tokenは.auto.tfvarsで設定することにしました。

secrets.auto.tfvars

root_access_token = "glpat-xxxxx"

リソースを作成。

$ terraform init
$ terraform apply

GitLab Runnerおよびトークンの組み合わせは全種類作っていますが、今回はグループを使うことにします。

$ terraform output group_runner_authentication_token
"glrt-xxxx"

またインストール済みのGitLab Runnerは停止しておきます。

$ sudo gitlab-runner stop

設定ファイルも削除。

$ sudo rm /etc/gitlab-runner/config.toml

これで準備は完了です。

GitLab Runnerをrun-singleコマンドで動かしてみる

では、GitLab Runnerをrun-singleコマンドで動かしてみましょう。

GitLab Runner commands / Run-related commands / gitlab-runner run-single

実行前にヘルプを見てみます。

runコマンドから。

$ gitlab-runner run --help
Runtime platform                                    arch=amd64 os=linux pid=9698 revision=ac71f4d8 version=18.10.0
NAME:
   gitlab-runner run - run multi runner service

USAGE:
   gitlab-runner run [command options] [arguments...]

OPTIONS:
   --listen-address value               Metrics / pprof server listening address [$LISTEN_ADDRESS]
   -c value, --config value             Config file (default: "/home/vagrant/.gitlab-runner/config.toml") [$CONFIG_FILE]
   -n value, --service value            Use different names for different services (default: "gitlab-runner")
   -d value, --working-directory value  Specify custom working directory
   -u value, --user value               Use specific user to execute shell scripts
   --syslog                             Log to system service logger [$LOG_SYSLOG]

run-single。runコマンドとはまったく異なる数のオプションが表示されます。

$ gitlab-runner run-single --help
Runtime platform                                    arch=amd64 os=linux pid=9707 revision=ac71f4d8 version=18.10.0
NAME:
   gitlab-runner run-single - start single runner

USAGE:
   gitlab-runner run-single [command options] [arguments...]

OPTIONS:
   --name value, --description value                                                          Runner name [$RUNNER_NAME]
   --limit value                                                                              Maximum number of builds processed by this runner (default: "0") [$RUNNER_LIMIT]
   --output-limit value                                                                       Maximum build trace size in kilobytes (default: "0") [$RUNNER_OUTPUT_LIMIT]
   --request-concurrency value                                                                Maximum concurrency for job requests (default: "0") [$RUNNER_REQUEST_CONCURRENCY]
   --strict-check-interval value                                                              When you set StrictCheckInterval to true, the runner disables the faster-than-check_interval re-polling loop that occurs when a runner receives a job. Instead, the runner waits <check_interval> seconds before it polls again, even if additional jobs are available. [$RUNNER_STRICT_CHECK_INTERVAL]
   --unhealthy-requests-limit value                                                           The number of unhealthy responses to new job requests after which a runner worker is turned off. (default: "0") [$RUNNER_UNHEALTHY_REQUESTS_LIMIT]
   --unhealthy-interval value                                                                 Duration that the runner worker is turned off after it exceeds the unhealthy requests limit. Supports syntax like '3600s' and '1h30min'.
   --job-status-final-update-retry-limit value                                                The maximum number of times GitLab Runner can retry to push the final job status to the GitLab instance. (default: "0") [$RUNNER_job_status_final_update_retry_limit]
   -u value, --url value                                                                      GitLab instance URL [$CI_SERVER_URL]
   -t value, --token value                                                                    Runner token [$CI_SERVER_TOKEN]
   --tls-ca-file value                                                                        File containing the certificates to verify the peer when using HTTPS [$CI_SERVER_TLS_CA_FILE]
   --tls-cert-file value                                                                      File containing certificate for TLS client auth when using HTTPS [$CI_SERVER_TLS_CERT_FILE]
   --tls-key-file value                                                                       File containing private key for TLS client auth when using HTTPS [$CI_SERVER_TLS_KEY_FILE]
   --executor value                                                                           Select executor, eg. shell, docker, etc. [$RUNNER_EXECUTOR]
   --builds-dir value                                                                         Directory where builds are stored [$RUNNER_BUILDS_DIR]
   --cache-dir value                                                                          Directory where build cache is stored [$RUNNER_CACHE_DIR]
   --clone-url value                                                                          Overwrite the default URL used to clone or fetch the git ref [$CLONE_URL]
   --env value                                                                                Custom environment variables injected to build environment [$RUNNER_ENV]
   --proxy-exec value                                                                         (Experimental) Proxy execution via helper binary [$RUNNER_PROXY_EXEC]
   --pre-get-sources-script value                                                             Runner-specific commands to be executed on the runner before updating the Git repository and updating submodules. [$RUNNER_PRE_GET_SOURCES_SCRIPT]
   --post-get-sources-script value                                                            Runner-specific commands to be executed on the runner after updating the Git repository and updating submodules. [$RUNNER_POST_GET_SOURCES_SCRIPT]
   --pre-build-script value                                                                   Runner-specific command script executed just before build executes [$RUNNER_PRE_BUILD_SCRIPT]
   --post-build-script value                                                                  Runner-specific command script executed just after build executes [$RUNNER_POST_BUILD_SCRIPT]

〜省略〜

やっぱり単発実行のコマンドなんですね。

トークンは変数として設定済みとします。

$ RUNNER_TOKEN=...

GitLabプロジェクトにはこんな.gitlab-ci.ymlを用意。

.gitlab-ci.yml

stages:
  - run

greeting:
  stage: run
  script: |
    echo 'Hello World'

.gitlab-ci.ymlを登録すると、パイプラインがPendingになります。

ここでGitLab Runnerをrun-singleコマンドで実行。事前のregisterコマンドの実行は不要です。

$ sudo gitlab-runner run-single \
  --url "http://192.168.0.7/" \
  --token "$RUNNER_TOKEN" \
  --executor "docker" \
  --docker-image ubuntu:24.04 \
  --docker-privileged \
  --docker-volumes "/certs/client" \
  --description "sample group runner"

そのままフォアグラウンドでGitLab Runnerが実行され、こんな感じでGitLab CI/CDジョブが実行されます。

Runtime platform                                    arch=amd64 os=linux pid=1515 revision=ac71f4d8 version=18.10.0
Starting runner for http://192.168.0.7/ with token Q4xdCJ8Z7 ...
Checking for jobs... received                       correlation_id=01KNESW8VQKJB372N8S0VTYSHW job=1 repo_url=http://192.168.0.7/sample-group/sample-app.git runner=Q4xdCJ8Z7 runner_name=sample group runner
Updating job...                                     bytesize=0 checksum= correlation_id=46408a0238f844a18d327e7fdae92774 job=1 runner=Q4xdCJ8Z7 runner_name=sample group runner
Submitting job to coordinator...ok                  bytesize=0 checksum= code=200 correlation_id=01KNESW9HY9G31W1E82HB607DK job=1 job-status=running runner=Q4xdCJ8Z7 runner_name=sample group runner update-interval=0s
Using default image                                 executor=docker gitlab_user_id=6 image=ubuntu:24.04 job=1 namespace_id=13 organization_id=1 project=6 project_full_path=sample-group/sample-app root_namespace_id=13 runner=Q4xdCJ8Z7 runner_name=sample group runner
Appending trace to coordinator...ok                 code=202 correlation_id=01KNESWCGJFR6PBXMXWVDGW0ZW job=1 job-log=0-509 job-status=running runner=Q4xdCJ8Z7 runner_name=sample group runner sent-log=0-508 status=202 Accepted update-interval=1m0s
Using default image                                 executor=docker gitlab_user_id=6 image=ubuntu:24.04 job=1 namespace_id=13 organization_id=1 project=6 project_full_path=sample-group/sample-app root_namespace_id=13 runner=Q4xdCJ8Z7 runner_name=sample group runner
Using default image                                 executor=docker gitlab_user_id=6 image=ubuntu:24.04 job=1 namespace_id=13 organization_id=1 project=6 project_full_path=sample-group/sample-app root_namespace_id=13 runner=Q4xdCJ8Z7 runner_name=sample group runner
Job succeeded                                       container_name=runner-q4xdcj8z7-project-6-concurrent-0-232d2ca0bd10075c-build duration_s=27.537055253 gitlab_user_id=6 job=1 job-status=success name=ubuntu namespace_id=13 organization_id=1 project=6 project_full_path=sample-group/sample-app root_namespace_id=13 runner=Q4xdCJ8Z7 runner_name=sample group runner
Appending trace to coordinator...ok                 code=202 correlation_id=01KNESX4EF32EM66WQ3RW6N2Y2 job=1 job-log=0-3511 job-status=running runner=Q4xdCJ8Z7 runner_name=sample group runner sent-log=509-3510 status=202 Accepted update-interval=3s
Updating job...                                     bytesize=3511 checksum=crc32:e2a49d15 correlation_id=0fe9f05a57b54a2caaee1b54a1dcc748 job=1 runner=Q4xdCJ8Z7 runner_name=sample group runner
Submitting job to coordinator...ok                  bytesize=3511 checksum=crc32:e2a49d15 code=200 correlation_id=01KNESX4K1WM878NS7X7XEXNSS job=1 job-status=success runner=Q4xdCJ8Z7 runner_name=sample group runner update-interval=0s

そしてそのまま待機します。終了したかったらCtrl-cですね。

run-singleという名前の割に、どうして終了しないかはドキュメントに書かれています。

You can use the --max-builds option to control how many builds the runner executes before exiting. The default of 0 means that the runner has no build limit and jobs run forever.

GitLab Runner commands / Run-related commands / gitlab-runner run-single

デフォルトでは実行するビルド数に制限がなく、上限を設定するには--max-buildsオプションを使います。

では、--max-buildsを1にして登録してみましょう。

$ sudo gitlab-runner run-single \
  --url "http://192.168.0.7/" \
  --token "$RUNNER_TOKEN" \
  --executor "docker" \
  --max-builds 1 \
  --docker-image ubuntu:24.04 \
  --docker-privileged \
  --docker-volumes "/certs/client" \
  --description "sample group runner"

すると、先ほどの.gitlab-ci.ymlの内容だと1回ジョブを実行するとすぐに終了します。

This runner has processed its build limit, so now exiting

ところで、こういう使い方だと--max-buildsオプションの単位が気になりますね。

ビルド=ジョブと捉えてよさそうです。パイプラインではないことに注意です。

たとえば.gitlab-ci.ymlを以下のように変更します。ジョブが3つありますね。

.gitlab-ci.yml

stages:
  - stage1
  - stage2
  - stage3

foo:
  stage: stage1
  script: |
    echo 'foo'

bar:
  stage: stage2
  script: |
    echo 'bar'

fuga:
  stage: stage3
  script: |
    echo 'fuga'

このパイプラインに対して、--max-buildsを1にして実行すると最初のジョブ(foo)を実行したところでGitLab Runnerは
終了します。

$ sudo gitlab-runner run-single \
  --url "http://192.168.0.7/" \
  --token "$RUNNER_TOKEN" \
  --executor "docker" \
  --max-builds 1 \
  --docker-image ubuntu:24.04 \
  --docker-privileged \
  --docker-volumes "/certs/client" \
  --description "sample group runner"

このパイプラインのジョブをすべて実行するには、この設定のままだと3回GitLab Runnerを実行する必要があります。

もしくは以下のように--max-buildsを3にして実行します。

$ sudo gitlab-runner run-single \
  --url "http://192.168.0.7/" \
  --token "$RUNNER_TOKEN" \
  --executor "docker" \
  --max-builds 3 \
  --docker-image ubuntu:24.04 \
  --docker-privileged \
  --docker-volumes "/certs/client" \
  --description "sample group runner"

すると、パイプラインに含まれるすべてのジョブ(foo、bar、fuga)を実行し終えたところでGitLab Runnerも終了します。

ちょっとわかりにくいですね…パイプラインを実行の単位にして欲しいところではありました…。とはいえ、難しいところ
なのでしょうね。

使い方はおよそわかりました。

おわりに

GitLab Runnerのrun-singleコマンドを使って、単一のGitLab CI/CDジョブを実行してみました。
もっとも、デフォルトは単一ではなく無制限なのですが。

個人的にはパイプライン単位で扱えると嬉しいなと思ったのですが、単位がジョブなのでちょっと扱い方が難しいですね。
自分のジョブを実行できる専用のGitLab Runnerとして使えると便利かなと思ったのですが、工夫すればなんとかなるもの
でしょうか…?

今回は使い方がわかったのでよしとしましょう。