승인을 기다리는 워크플로 실행

Semaphore UI 2.19는 2.x 라인에서 가장 큰 릴리스입니다. 그래픽 편집기를 갖춘 워크플로를 도입하고, 러너가 Docker 컨테이너와 Kubernetes 파드에서 작업을 실행할 수 있게 하며, 실행 중인 작업에 단기 JWT 신원 토큰을 발급하도록 Semaphore를 확장하고, 암호화 키 교체를 추가하고, 러너 안정성 문제를 근본적으로 해결하며, 다수의 보안 강화 변경 사항을 함께 제공합니다.

이 라인은 v2.19.0부터 v2.19.14까지의 릴리스를 포함합니다. 패치 릴리스는 마지막에 정리되어 있습니다.

주요 내용

  • 워크플로 (Beta) — 작업 템플릿을 승인 게이트가 있는 파이프라인으로 연결합니다. 그래픽 편집기에서 그리고 같은 캔버스에서 실시간으로 지켜볼 수 있습니다.
  • Docker 및 Kubernetes 실행기 — 러너가 모든 작업을 새 컨테이너 또는 파드에서 실행할 수 있습니다 (Pro / Enterprise).
  • 작업용 JWT 신원 토큰 — 플레이북에서 Vault, AWS, GCP, Azure 및 OIDC 토큰을 받아들이는 모든 서비스에 키 없이 인증합니다.
  • 암호화 키 교체 — 핫 리로드와 vaults check 명령을 지원하는 라벨이 붙은 키링입니다.
  • 환경 변수로 전달되는 설문 변수, 그리고 int, text 및 새로 디자인된 enum 유형.
  • 작업 기록의 실제 서버 측 페이지네이션 — 수백만 개의 작업이 있는 프로젝트도 빠르게 유지됩니다.
  • 러너 안정성 — 온라인/오프라인 상태, 해시 처리된 일회용 등록 토큰, 죽은 러너에 멈춘 작업의 자동 복구.
  • SEMAPHORE_DEBUG_FILTER를 통한 선택적 디버그 로깅.
  • 보안 강화: 현재 비밀번호 확인, CSRF 출처 검증, 보안 쿠키, API 전반에 걸친 더 엄격한 입력 검증.
  • BoltDB 제거 — SQLite, MySQL, PostgreSQL만 지원합니다.

워크플로 (Beta)

워크플로는 하나의 단위로 실행되는 작업 템플릿 그래프입니다. 각 노드는 작업(템플릿을 실행), 승인(사용자가 승인하거나 거부할 때까지 실행을 일시 중지), 노트(실행되지 않는 자유 형식 주석) 중 하나입니다. 엣지는 성공 시, 실패 시, 항상 중 하나의 조건을 가집니다.

워크플로는 프로젝트 사이드바에 Beta 칩이 표시된 새로운 워크플로 항목으로 나타납니다. Pro 에디션에서 사용할 수 있으며, 베타 기간 동안에는 플랜과 관계없이 활성화됩니다.

그래픽 편집기

워크플로 편집기

편집기는 Drawflow 기반의 전체 페이지 캔버스입니다:

  • 팔레트에서 노드를 드래그하고, 노드 핸들에서 드래그하여 노드를 연결합니다;
  • 엣지를 클릭하여 조건을 변경합니다; 엣지는 색상으로 구분되며 모서리에 범례가 있습니다;
  • 노드를 클릭하여 사이드 패널에서 속성을 편집합니다: 템플릿, 수렴 모드(모든 부모 / 임의의 부모), 승인 제한 시간과 메시지, 노트 텍스트;
  • 자기 자신으로 향하는 엣지와 순환은 그리는 즉시 거부되며, 문제 패널이 서버 측 검증을 그대로 반영하므로 잘못된 그래프는 저장할 수 없습니다;
  • 노드 위치가 저장됩니다; 위치 없이 API를 통해 생성된 워크플로는 자동 위상 정렬 레이아웃을 적용받습니다;
  • 툴바 또는 Ctrl + 마우스 휠로 확대/축소하고, 캔버스를 드래그하여 이동하며, 팔레트를 접어 공간을 확보합니다;
  • 시작 버전은 실행 버전 관리(1.4.0, 1.4.1, …)의 시작점을 지정합니다; 버전은 실행이 시작하는 모든 작업에 전파됩니다.

노드 속성 패널

실시간 실행 보기

실행 보기는 같은 캔버스를 재사용합니다. 각 노드는 작업 상태를 표시하고, 활성 노드는 강조되며, 대기 중인 승인은 캔버스에 직접 승인 / 거부 버튼을 렌더링합니다. 중지 버튼은 실행의 모든 작업을 강제 중지하고, 대기 중인 승인을 거부하며, 실행을 stopped로 표시합니다.

다크 테마 실행 보기

실행 상태는 running, approval, success, failed, stopped입니다. 워크플로 진행은 서버가 주도합니다: 워크플로 작업이 하나라도 완료되면 다음 노드가 예약되므로, 자동 실행 자식이 없는 템플릿이 더 이상 실행을 멈추게 하지 않습니다.

워크플로 목록

노드별 작업 매개변수

모든 작업 노드는 수동 실행과 같은 방식으로 템플릿에 전달하는 매개변수(변수, 인벤토리, 브랜치, 인수, 버전, 메시지)를 재정의할 수 있습니다.

API

GET/POST   /api/project/{id}/workflows
GET/PUT/DELETE /api/project/{id}/workflows/{workflow_id}
POST       /api/project/{id}/workflows/{workflow_id}/run
GET        /api/project/{id}/workflows/{workflow_id}/runs
GET        /api/project/{id}/workflows/{workflow_id}/runs/{run_id}
POST       /api/project/{id}/workflows/{workflow_id}/runs/{run_id}/stop
GET        /api/project/{id}/workflows/{workflow_id}/runs/{run_id}/artifacts
GET        /api/project/{id}/workflows/{workflow_id}/runs/{run_id}/approvals
POST       /api/project/{id}/workflows/{workflow_id}/runs/{run_id}/approvals/{node_id}

베타의 알려진 제한 사항: 미니맵, 실행 취소/다시 실행, 다중 선택이 없습니다; 워크플로 아티팩트 (set_stats 값)는 아직 원격 러너에서 실행되는 작업 간에 전달되지 않습니다.


Docker 및 Kubernetes 실행기 (Pro / Enterprise)

러너는 러너 호스트에서 직접 실행하는 대신 각 작업을 격리된 환경에서 실행할 수 있습니다. 실행기는 러너 프로세스당 한 번 runner.executor.type(local, docker, k8s)으로 선택합니다.

Docker (runner.executor.docker, Pro):

옵션 환경 변수 기본값
host SEMAPHORE_RUNNER_DOCKER_HOST 로컬 소켓
tls_verify, cert_path SEMAPHORE_RUNNER_DOCKER_TLS_VERIFY, …_CERT_PATH
image SEMAPHORE_RUNNER_DOCKER_IMAGE semaphoreui/job:latest
helper_image SEMAPHORE_RUNNER_DOCKER_HELPER_IMAGE semaphoreui/helper:latest
network SEMAPHORE_RUNNER_DOCKER_NETWORK bridge
pull_policy SEMAPHORE_RUNNER_DOCKER_PULL_POLICY if-not-present
cpu_limit, memory_limit SEMAPHORE_RUNNER_DOCKER_CPU_LIMIT, …_MEMORY_LIMIT
privileged SEMAPHORE_RUNNER_DOCKER_PRIVILEGED false
poll_interval_seconds, cleanup_grace_seconds …_POLL_INTERVAL_SECONDS, …_CLEANUP_GRACE_SECONDS 2, 30

Kubernetes (runner.executor.k8s, Enterprise):

옵션 환경 변수 기본값
kubeconfig SEMAPHORE_RUNNER_K8S_KUBECONFIG 클러스터 내부
namespace SEMAPHORE_RUNNER_K8S_NAMESPACE semaphore
image SEMAPHORE_RUNNER_K8S_IMAGE semaphoreui/job:latest
helper_image SEMAPHORE_RUNNER_K8S_HELPER_IMAGE semaphoreui/helper:latest
service_account SEMAPHORE_RUNNER_K8S_SERVICE_ACCOUNT default
pull_secrets SEMAPHORE_RUNNER_K8S_PULL_SECRETS
poll_interval_seconds, cleanup_grace_seconds …_POLL_INTERVAL_SECONDS, …_CLEANUP_GRACE_SECONDS 3, 30

CI에서 두 개의 새 이미지가 게시됩니다: semaphoreui/job(Ansible, Terraform, OpenTofu, Terragrunt, paramiko)과 semaphoreui/helper. 템플릿은 템플릿 양식의 새로운 실행기 이미지 필드로 자체 작업의 이미지를 재정의할 수 있습니다(실행기 기능이 라이선스되지 않은 경우 PRO로 업그레이드 배지와 함께 표시됩니다).

러너 연결에는 runner.connection.server_ca_cert_filerunner.connection.skip_tls_verify도 추가되었습니다.


작업용 JWT 신원 토큰

Semaphore는 실행 중인 작업을 위한 OIDC 방식의 신원 공급자 역할을 할 수 있으므로, 플레이북이 장기 자격 증명 없이 Vault, 클라우드 STS 엔드포인트 또는 내부 서비스에 인증할 수 있습니다.

템플릿 양식: JWT가 포함된 고급 옵션

  • 템플릿별로 작업 러너에 JWT 발급으로 활성화합니다; 하나 이상의 **대상(audience)**과 TTL(jwt.max_ttl로 상한 제한)을 설정합니다.
  • 작업은 SEMAPHORE_JWT 환경 변수로 토큰을 받습니다.
  • 토큰은 ES256(ECDSA P‑256)으로 서명되며 ID만 포함합니다: task_id, project_id, template_id, user_id와 표준 iss, sub, aud, exp, nbf, iat, jti 클레임.
  • 공개 키는 GET /.well-known/jwks.json에 게시됩니다.

서버 설정:

"jwt": {
  "enabled": true,
  "issuer": "https://semaphore.example.com",
  "default_ttl": "1h",
  "max_ttl": "24h"
}

환경 변수: SEMAPHORE_JWT_ENABLED, SEMAPHORE_JWT_ISSUER, SEMAPHORE_JWT_DEFAULT_TTL, SEMAPHORE_JWT_MAX_TTL.


설문 변수

변수를 환경 변수로 전달

설문 변수에 이제 변수 전달 방식 설정이 있습니다: 앱별 CLI 방식(Ansible의 --extra-vars, Terraform/OpenTofu의 -var, 셸 스크립트의 CLI 인수) 또는 프로세스 환경 변수입니다. 변수 이름이 그대로 사용되므로 Terraform 사용자는 단순히 TF_VAR_region으로 이름을 지으면 됩니다. 환경 변수는 프로세스 목록에 나타나지 않으므로, 시크릿에 더 안전한 선택입니다.

환경 변수로 전달되는 설문 변수

새로운 유형

  • int — 검증이 포함된 숫자 입력;
  • text — 여러 줄 텍스트;
  • enum — 이름/값 쌍과 기본값을 갖춘 새로 디자인된 편집기.

Enum 설문 변수 편집기

작업 대화 상자는 각 유형을 그에 맞게 렌더링합니다:

유형이 지정된 설문 변수가 있는 새 작업 대화 상자

기존 survey_vars JSON에 저장됩니다 — 마이그레이션이 필요하지 않습니다.


템플릿 및 작업

  • 동적 플레이북 선택기. 플레이북 파일 경로 필드가 저장소에서 발견된 플레이북을 나열합니다(GET /api/project/{id}/repositories/{repository_id}/playbooks). 목록은 선택한 브랜치를 따르며, 저장소를 읽을 수 없을 때는 자유 텍스트로 대체됩니다.

  • Ansible Galaxy 설치 건너뛰기requirements.yml의 롤과 컬렉션 설치를 건너뛰는 템플릿별 옵션으로, 작업별로 재정의할 수도 있습니다.

  • 변수 그룹의 유형이 지정된 변수, 숫자 포함:

    유형이 지정된 변수가 있는 변수 그룹

  • 작업 기록 페이지네이션. 기록 페이지는 이전에 최신 200개의 작업을 가져와 클라이언트 측에서 페이지를 넘겼기 때문에 그보다 오래된 작업에는 접근할 수 없었습니다. 이제 백엔드는 키셋 커서 (?count=20&before=<task_id>, X-Has-Next 응답 헤더)를 사용하여 COUNT(*)OFFSET 없이 한 번에 한 페이지씩 반환합니다. 푸터에는 페이지당 행 수와 이전/다음 컨트롤이 있습니다. 같은 페이지네이션이 템플릿별 작업 목록과 대시보드에도 적용됩니다.

    서버 측 페이지네이션이 적용된 기록

  • 작업 목록은 최대 5초에 한 번만 다시 로드됩니다; 여러 중복 요청이 제거되었습니다.

  • 일정은 서버 측 cron 파서로 검증되므로, 클라이언트와 서버가 더 이상 불일치하지 않습니다.

  • 작업의 브랜치 재정의는 템플릿이 허용하는 경우에만 받아들여집니다.

  • Git 작업이 저장소 디렉터리별로 직렬화됩니다. 병렬 작업 허용이 설정된 템플릿은 하나의 작업 사본을 공유하며, 동시 git pull / git checkout이 이를 손상시킬 수 있었습니다. 이제 업데이트와 체크아웃은 로컬 및 러너 실행 모두에서, 인벤토리 저장소를 포함하여 하나의 임계 영역을 이룹니다.


러너

온라인/오프라인 상태가 표시된 러너 페이지

  • 하트비트 생존 여부에서 도출되는 러너 페이지의 온라인 / 오프라인 상태.

  • 일회용 등록 토큰. 러너를 먼저 UI에서 생성한 뒤, 한 번만 표시되고 SHA‑256 해시로만 저장되며 한 시간 후 만료되는 smrs_… 토큰으로 나중에 등록할 수 있습니다. 대화 상자에는 환경 변수, 설정 파일, Docker용으로 바로 복사할 수 있는 명령이 표시됩니다. 토큰을 다시 생성하면 이미 등록된 러너가 재설정되어 다시 등록할 수 있습니다.

    러너 등록 토큰 대화 상자

    SEMAPHORE_WEB_ROOT=https://semaphore.example.com \
    SEMAPHORE_RUNNER_REGISTRATION_TOKEN=smrs_… \
    semaphore runner register --config ./config.runner.json
    
    semaphore runner start --config ./config.runner.json
    
  • 멈춘 작업 복구. 러너는 프로세스 시작 시간(X-Runner-Started-At)을 전송합니다. 폴링을 중단한 러너는 runners.offline_timeout_sec(120초) 후 오프라인으로 표시됩니다: 새 작업을 받지 않으며 starting 상태의 작업은 재할당됩니다. runners.task_fail_timeout_sec(420초) 후에는 running 상태의 작업이 명확한 메시지와 함께 실패 처리됩니다. 재시작하여 메모리 내 작업 풀을 잃은 러너는 즉시 감지됩니다. 조정은 runners.reconcile_interval_sec(30초)마다 실행됩니다.

  • 러너에서 다른 곳으로 재할당된 작업은 이전 러너에서 종료됩니다.

  • 오래된 러너 대체 동작이 사라졌습니다: 모든 러너가 오프라인이면 작업은 최대 30분 동안 폴링하지 않은 러너로 전달되는 대신 큐에서 대기합니다.

  • 러너별 RSA 암호화 키 제거. 러너‑서버 트래픽은 TLS에 의존합니다; 이로써 등록과 setup에서 키 교환 단계가 삭제됩니다.

  • 러너 클라이언트의 TCP 연결 누수를 수정했습니다; 잘못된 등록 토큰은 400을 반환합니다.


시크릿 및 암호화

  • 암호화 키 교체. 새로운 encryption 블록은 라벨이 붙은 키링을 설명합니다: 인라인 keys (값 또는 파일), 또는 모든 파일이 파일 이름으로 명명된 키가 되는 keys_folder, 그리고 시크릿 키와 옵션 키를 가리키는 active 포인터. 암호문에 이제 키 ID가 포함되므로 한 번에 모두 재암호화하지 않고도 키를 교체할 수 있습니다. keys_poll_interval(기본값 15s)이 설정된 encryption.keys_file은 키링을 핫 리로드합니다. 새로운 CLI: semaphore vaults check; semaphore vaults rekey는 키링을 중심으로 다시 작성되었습니다. 기존의 단일 access_key_encryption은 여전히 작동합니다.
  • option_encryption — 데이터베이스에 저장된 옵션을 위한 별도의 키.
  • 자체 아이콘이 있는 OpenBao 시크릿 저장소 유형(Vault 공급자를 통해 라우팅).
  • 정적 자격 증명 없는 AWS Secrets ManagerIAM 역할 사용 체크박스.
  • Vault/OpenBao 저장소를 위한 TLS 검증 건너뛰기 옵션.
  • 동기화된 읽기 전용 시크릿 필드가 업데이트 시 더 이상 지워지지 않습니다.

관측성

  • 네임스페이스 기반 디버그 로깅. --debug-filter / SEMAPHORE_DEBUG_FILTER는 Node.js debug 방식으로 어떤 하위 시스템이 디버그 출력을 내보낼지 선택합니다: runner, runner,task_pool, task_*, *, *,-db. 사용 가능한 네임스페이스: runner, task_pool, task_runner, task_logger, git, terraform, session, ldap, schedule, db, ha. 필터는 로그 수준이 DEBUG일 때만 적용되며, 수준을 올리지는 않습니다. Syslog 훅도 같은 필터를 따릅니다.
  • 러너, 작업, 인증 전반에 걸쳐 많은 새로운 컨텍스트 디버그 구문이 추가되었습니다; 선택된 워크스페이스가 시작 시 출력됩니다.

보안

비밀번호 변경 시 현재 비밀번호 필요

  • 비밀번호를 변경하려면 이제 현재 비밀번호가 필요합니다 (CWE‑620).
  • 상태를 변경하는 요청에 대한 Origin / Referer 검증 (CSRF 강화).
  • HTTPS로 제공될 때 세션 쿠키가 Secure로 표시됩니다.
  • 러너 등록 토큰은 해시로 저장되며 만료됩니다; 러너별 암호화 키가 제거되었습니다.
  • 사용자 지정 역할 생성 시 호출자의 권한을 확인합니다 (권한 상승 수정).
  • Git URL 검증; 조작된 ref가 플래그로 읽히지 않도록 git에 --end-of-options를 전달합니다; 커밋 해시의 형식을 확인합니다; 저장소 탐색 전에 브랜치를 검증합니다; 플레이북 경로를 검증합니다.
  • 액세스 키 페이로드와 템플릿 app 필드를 검증합니다.
  • JWT 클레임은 ID만 포함합니다 — 이름이나 이메일이 외부 시스템으로 유출되지 않습니다.
  • 러너 토큰이 더 이상 프로젝트 백업에 기록되지 않습니다.
  • API는 쓰기 오류 후 부분적으로 작성된 응답을 계속 보내는 대신 즉시 반환합니다.
  • 보안 SLA가 SECURITY.md에 게시되었습니다; 릴리스 아티팩트는 [email protected] GPG 키로 서명됩니다.

UI 및 현지화

체코어가 포함된 언어 선택기

  • 체코어 번역.
  • 템플릿 양식의 JWT 및 일정 섹션을 위한 드롭다운 카드.
  • 라이트 모드에서 클립보드 복사 아이콘이 보입니다; 실행 중인 작업 스피너 수정; 템플릿 양식 여백 수정; 워크플로 Beta 라벨.
  • 통합 변수 추출이 JSON 객체와 배열을 문자열화하는 대신 그대로 유지합니다.

업그레이드 참고 사항

호환성이 깨지는 변경 및 동작 변경

  1. BoltDB가 제거되었습니다. bolt는 더 이상 유효한 dialect가 아닙니다; 서버는 “Bolt is not supported starting from version 2.19” 메시지와 함께 시작을 거부합니다. 먼저 SQLite, MySQL 또는 PostgreSQL로 마이그레이션하십시오.
  2. 러너 암호화 키가 제거되었습니다. 서버와 러너 모두 2.19여야 합니다. 러너‑서버 트래픽이 TLS로 보호되는지 확인하십시오. 키를 기대하던 스크립트 기반 러너 프로비저닝은 업데이트해야 합니다.
  3. 작업 목록 API가 페이지네이션됩니다. GET /api/project/{id}/tasks/lastcountbefore를 받습니다; limit도 여전히 받아들여지지만, 한 번의 응답에서 최신 200개 작업에 의존하던 클라이언트는 페이지를 나눠야 합니다.
  4. 오래된 러너 대체 동작이 없습니다. 모든 러너가 오프라인이면 작업은 큐에 남아 있습니다.
  5. 등록에서 러너 active 플래그가 제거되었습니다.
  6. 프로젝트 백업에 더 이상 러너 토큰이 포함되지 않습니다.
  7. SQLite: v2.19.14sessiontask 테이블을 다시 빌드하여 올바른 외래 키를 추가합니다(사용자 삭제 수정). 고아 세션은 제거됩니다. 업그레이드 전에 데이터베이스를 백업하십시오.

새로운 설정

jwt, runners, encryption, option_encryption, secrets_path, db.dialect, runner.executor.{type,docker,k8s}, runner.connection.{server_ca_cert_file,skip_tls_verify}, runner.registration_token_file, runner.token_file. 모두 선택 사항입니다; 기존 설정은 계속 작동합니다. config.schema.yaml과 설정 참조 문서가 다시 생성되었습니다.

데이터베이스 마이그레이션

v2.18.6(템플릿 jwt_params), v2.18.15(워크플로 테이블), v2.19.2(runner.started_at), v2.19.11(project__workflow_node.task_params_id), v2.19.12(project__template.executor_image), v2.19.14(SQLite session/task 재빌드). MariaDB 12.1 마이그레이션 호환성이 수정되었습니다.


패치 릴리스

버전 변경 사항
2.19.8 SQLite 마이그레이션 수정; 사용자 삭제 수정(session/task 외래 키); DB 쿼리에서 대소문자를 구분하지 않는 문자열 비교
2.19.9 SEMAPHORE_RUNNER_EXECUTOR_TYPE 환경 변수; 설정 omitempty 정리
2.19.10 실행기 설정 수정; Docker 빌드 옵션 수정
2.19.11 선택된 워크스페이스를 로그에 출력; helper 이미지의 latest 태그; DB 마이그레이션 테스트
2.19.12 러너 설정 옵션의 nil 포인터 수정; 오류 전파 수정
2.19.14 설정의 기존 secrets_path 처리 수정

의존성

Go 1.26; go-git 5.19, go-oidc 3.19, golang.org/x/crypto 0.53, go-ldap 3.4.13, modernc.org/sqlite 1.52. 문서가 git 서브모듈로 추가되었습니다; THIRD-PARTY-LICENSES.md가 다시 생성되었습니다.

You might find this interesting