개요 — 무엇을 하나
Assets ▸ Instances(/asset/instances) 카드의 [ver] 버튼은 그 OS 인스턴스에 설치된 소프트웨어의 **버전 원장(1:N)**을 관리하는 모달을 엽니다. PHP·Node·nginx·MySQL·Docker·Jenkins 같은 런타임·서비스·툴의 버전을 한 곳에 모아두고, 각 항목을 current(최신) / outdated(구버전) / eol(지원종료) / vuln(취약) / unknown(미상) 상태로 표시해 패치 우선순위를 판단합니다.
두 개의 테이블이 협업합니다.
| 테이블 | 역할 |
|---|---|
infr_software_catalog |
버전 관리 대상 소프트웨어 마스터(49종 · 8개 분류). key → 표시명·분류·탐지 명령·아이콘 매핑. 앱이 소유(read/write). |
infr_instance_software |
인스턴스별 버전 원장(1:N). UNIQUE(instance_id, software_key) — 수동 저장·JSON import 모두 이 키로 upsert. |
갱신 경로는 두 가지입니다.
- 수동 add/edit — 모달의 "버전관리" 탭에서 카탈로그 드롭다운으로 소프트웨어를 고르고 버전·상태·메모를 입력.
- JSON import — 대상 호스트에서
scripts/infr_version_probe.cjs(Inventory ▸ Tools 에 시드됨)를 로컬로 실행 → 출력 JSON 을 "JSON import" 탭에 붙여넣어 일괄 upsert.
버전 원장은 뒤이어 서비스 카탈로그의 **[Analysis & Build]**가 데몬성 소프트웨어(nginx·mysql·redis·docker …)를
infr_service로 자동 승격하는 원천이 됩니다. 프로브가 채우는 런타임 존재성 축(아래)이 그 라이브니스 기본값을 잡습니다.
언제 쓰나
- 취약점 공지 대응 — "OpenSSL 3.0.x 취약" 공지가 나왔을 때 어느 인스턴스가 해당 버전인지 원장에서 즉시 필터.
- 패치 계획 —
eol/outdated로 표시된 항목을 모아 업그레이드 대상 목록을 만들 때. - 신규 서버 온보딩 — 프로브 한 번으로 설치 스택 전체를 원장에 등록하고 서비스 카탈로그로 서비스까지 구성.
- 형상 관리 — "이 서버에 정확히 무슨 버전이 깔려 있나"를 사람이 읽을 수 있게 정리(CMDB 소프트웨어 축).
화면 구성
[ver] 모달 (탭 토글)
| 탭 | 내용 |
|---|---|
| 버전관리 | 이 인스턴스의 원장 행 목록(분류별 정렬) + add/edit/delete 폼. 카탈로그 드롭다운(아이콘·표시명) |
| JSON import | textarea + [가져오기] — 프로브/수동 JSON 붙여넣기 일괄 upsert |
원장 행이 담는 값
infr_instance_software 한 행:
| 필드 | 의미 |
|---|---|
software_key |
카탈로그 key(php·nginx …). 정규식 ^[a-z0-9_]+$, 최대 40자. 카탈로그 밖 커스텀 키도 허용(coverage-max) |
name / category |
표시명·분류(비우면 카탈로그에서 스냅샷) |
version |
탐지/입력된 버전 문자열(최대 60자) |
raw |
탐지 원문(명령 출력 한 줄, 최대 255자) |
status |
current / outdated / eol / vuln / unknown — 수명·취약 축 |
source |
manual(수동) / script(프로브) / import(JSON) |
detected_at |
탐지·기록 시각 |
note |
자유 메모 |
런타임 존재성 축 (마이그레이션 000718)
버전(수명)과 직교하는, 데몬성 소프트웨어의 "지금 살아 있나" 축. 프로브가 채웁니다.
| 필드 | 의미 |
|---|---|
is_daemon |
데몬/서비스 성격 여부(true/false/null) |
svc_registered |
systemd 등록됨(is-enabled = enabled/static/disabled …) |
proc_up |
프로세스 가동(is-active > pgrep > 기대 포트 리슨) |
listen_ports |
리슨 중인 기대 포트 CSV(예: 80,443) |
unit |
systemd unit 명 |
행에는 이 축에서 파생된 presence 배지가 붙습니다: proc_up→running > svc_registered→registered > is_daemon=false 또는 version 존재→binary > 그 외 null.
사용법
1. 수동으로 버전 추가/수정
- Instances 카드에서 [ver] 클릭 → "버전관리" 탭.
- [추가] → 카탈로그 드롭다운에서 소프트웨어 선택(예:
nginx). version(예:1.24.0),status(예:current), 필요 시note입력.- 저장 →
POST /asset/instances/software/save. 같은(instance_id, software_key)가 있으면 update, 없으면 create(source=manual).
name·category를 비워두면 카탈로그의 표시명·분류가 자동으로 채워집니다. 버전만 입력하세요.
2. 프로브로 일괄 등록 (권장)
대상 호스트에서 프로브를 로컬로 실행합니다. 크리덴셜·루트 불필요이며 로컬 명령만 씁니다(외부 호출 없음).
# 전체 카탈로그 탐지 (JSON 한 줄)
node infr_version_probe.cjs
# 사람이 읽기 좋게 들여쓰기
node infr_version_probe.cjs --pretty
# 특정 소프트웨어만
node infr_version_probe.cjs php,nginx,node
SE 는 보통 SSH 로 감싸 원격 수집합니다(스크립트를 배치할 필요도 없음).
ssh user@203.0.113.10 'node -' < infr_version_probe.cjs
# 또는 배치 후
ssh user@203.0.113.10 node /opt/infr/infr_version_probe.cjs
출력 JSON 을 복사해 [ver] ▸ JSON import 탭에 붙여넣고 [가져오기] → POST /asset/instances/software/import → 일괄 upsert.
프로브 출력 형태:
{
"tool": "infr_version_probe",
"hostname": "glory",
"detected_at": "2026-08-13T04:00:00.000Z",
"items": [
{ "key": "nginx", "version": "1.24.0", "raw": "nginx version: nginx/1.24.0",
"is_daemon": true, "svc_registered": true, "proc_up": true,
"listen_ports": "80,443", "unit": "nginx" },
{ "key": "php", "version": "8.4.3", "raw": "8.4.3", "is_daemon": true,
"svc_registered": true, "proc_up": true, "unit": "php8.4-fpm" },
{ "key": "openssl", "version": "3.0.13", "raw": "OpenSSL 3.0.13 30 Jan 2024", "is_daemon": false }
]
}
import 응답은 imported(반영 건수)와 unknown(카탈로그에 없는 key 목록)을 돌려줍니다. unknown 이어도 행은 등록됩니다 — 다만 표시명·분류가 비고, 카탈로그에 그 key 를 추가하면 다음부터 보강됩니다.
3. import 가 받는 JSON 형식 3종
파서는 유연합니다. 아래 셋 모두 수용합니다.
// (1) items 배열 (프로브 표준)
{ "items": [ { "key": "nginx", "version": "1.24.0", "status": "current" } ] }
// (2) versions 맵
{ "versions": { "nginx": "1.24.0", "php": "8.4.3" } }
// (3) 평면 맵
{ "nginx": "1.24.0", "php": { "version": "8.4.3", "status": "outdated" } }
예약 키(hostname·detected_at·versions·items·tool·generated_by·source)는 소프트웨어로 오인하지 않고 건너뜁니다. source는 최상위 generated_by/tool/source에 probe 또는 script가 들어 있으면 script, 아니면 import로 기록됩니다.
4. 삭제
행의 삭제 버튼 → POST /asset/instances/software/delete({id, instance_id}). 되돌릴 수 없습니다.
데이터 소스
카탈로그 — infr_software_catalog (49종 · 8분류)
InfrSoftwareCatalogSeeder가 멱등 시드(updateOrCreate by key)합니다. 이 시더가 key→탐지 명령 매핑의 단일 진실이며, scripts/infr_version_probe.cjs의 CATALOG가 이와 동기됩니다.
분류(category) |
예시 key |
|---|---|
runtime (10) |
php · node · python · java · go · ruby · perl · dotnet · rust · bun |
web (7) |
nginx · apache · haproxy · caddy · traefik · tomcat · ftp |
database (8) |
mysql · postgresql · redis · mongodb · memcached · elasticsearch · rabbitmq · kafka |
ci_cd (1) |
jenkins |
container (5) |
docker · containerd · kubelet · podman · helm |
os_core (3) |
kernel · glibc · systemd |
security (7) |
openssl · openssh · sudo · certbot · fail2ban · firewalld · wireguard |
observability (8) |
node_exporter · grafana_agent · filebeat · rsync · restic · git · curl · bash |
카탈로그 컬럼: key(unique) · name · category · detect_cmd(탐지 원라이너) · version_regex · icon(mdi) · vendor_url · seq · is_active.
ci_cd분류는 마이그레이션 000721 이 enum 을 7→8값으로 확장해 추가했습니다(Jenkins 전용). enum 이라 신규 분류는 DDL ALTER 가 필요하지만, 이후infr_instance_software.category는 varchar 라 코드만으로 확장됩니다.
Jenkins 특수 탐지 (X-Jenkins)
Jenkins 는 표준 --version 명령이 없는 자바 웹앱이라 프로브가 세 방법을 순차 시도합니다(전부 로컬·무인증).
- HTTP 헤더 —
curl -sI http://127.0.0.1:{8080,8081,8443}/응답의X-Jenkins:헤더에서 버전 추출(가장 깔끔·빠름, 다른 서비스면 헤더가 없어 오탐 없음). - WAR —
java -jar /usr/share/java/jenkins.war --version(설치만 돼 있어도). - 패키지 관리자 —
rpm -q jenkins또는dpkg-query -W jenkins.
FTP 도 특수 처리합니다 — 실행 파일이 PATH 밖(/usr/sbin/vsftpd)일 수 있어 ps 로 실행 중인 데몬의 절대경로를 찾아 -v(pure-ftpd 는 -h)로 조회합니다.
버전 파싱 규칙
pickVersion()은 명령 출력의 첫 줄만 보지 않습니다.
- 잡음 라인 스킵 — deprecation·경고·usage 라인(예: MariaDB 의
mysql --version이 먼저 뱉는 "Deprecated program name…")을 건너뜁니다. - Distrib 우선 —
mysql -V출력에서 앞 토큰(Ver 14.14)은 클라이언트 버전이므로,Distrib 5.7.41이 있으면 그 뒤 토큰(서버/배포 버전)을 우선 채택. MySQL 8.0+ 는 Distrib 없이Ver 8.0.36이 곧 서버 버전이라 이 규칙은 Distrib 이 있을 때만 발동합니다.
원장 — infr_instance_software
instance_id로 infr_instance와 1:N. 조회는 인증된 사용자면 카탈로그 + 그 인스턴스의 원장 행을 함께 반환합니다.
권한
| 동작 | 엔드포인트 | 레벨 게이트 |
|---|---|---|
| 조회(카탈로그+원장) | GET /asset/instances/software/list |
infr.auth(level ≥ 1, demo) |
| 생성/수정 | POST /asset/instances/software/save |
level ≥ 2 (staff/admin) |
| 삭제 | POST /asset/instances/software/delete |
level ≥ 2 |
| JSON import | POST /asset/instances/software/import |
level ≥ 2 |
- 쓰기 게이트는 컨트롤러에서 세션
infr_user_level >= 2로 직접 검사합니다(레벨: demo=1, staff=5, admin=10). - 모든 쓰기(save/update/delete/import)는
Log::info('[audit] instance_software.*', …)로 감사 라인을 남깁니다(행위자infr_login_id·인스턴스·key·건수). 상세 열람은 감사 로그 참조. - 프로브는 로컬 실행이라 InfraGuard 가 원격에 접속하지 않습니다 — 크리덴셜이 앱을 거치지 않습니다.
문제해결 · FAQ
import 후 일부 항목이 표시명 없이 뜹니다
그 key가 infr_software_catalog에 없는 경우입니다. import 응답의 unknown[]에 나열됩니다. 카탈로그에 해당 key 를 추가(시더 또는 관리)하면 다음 import 부터 표시명·분류가 보강됩니다. 커스텀 소프트웨어라면 그대로 두어도 무방합니다.
"bad json" / "empty json" 오류
- bad json — 붙여넣은 텍스트가 유효한 JSON 이 아닙니다.
--pretty없이 실행한 한 줄 출력을 통째로 붙여넣거나, 앞뒤에 셸 프롬프트가 섞이지 않았는지 확인하세요. - empty json — JSON 은 유효하지만 소프트웨어 항목(
items/versions/평면 key)이 하나도 없습니다. 예약 키만 있는 경우입니다.
software_key 저장이 거부됩니다(422)
key 는 소문자·숫자·언더스코어만 허용(^[a-z0-9_]+$), 최대 40자입니다. 대문자·하이픈·공백을 쓰지 마세요(예: Node.js✗ → node✓). 또 편집으로 key 를 바꿨는데 그 key 가 같은 인스턴스에 이미 있으면 중복 오류가 납니다.
프로브에 특정 소프트웨어가 안 잡힙니다
프로브는 버전이 탐지된 항목만 출력합니다. 명령이 PATH 에 없거나(5초 타임아웃) 미설치면 조용히 건너뜁니다. 특정 key 만 강제로 보려면 node infr_version_probe.cjs <key>로 실행해 원문을 확인하세요.
MariaDB 버전이 클라이언트 버전으로 잡힙니다
프로브의 Distrib 우선 규칙이 이를 처리합니다. 여전히 이상하면 raw 값을 확인하고 수동으로 version을 교정하세요(source는 그대로 두면 됩니다).
버전은 있는데 서비스가 안 뜹니다
버전 원장은 소프트웨어 유무만 봅니다. 그것이 **서비스(데몬)**로 승격되려면 서비스 카탈로그에서 **[Analysis & Build]**를 실행해야 합니다. 그때 원장의 런타임 존재성 축(is_daemon·proc_up …)이 라이브니스 기본값이 됩니다. 프로브 JSON 을 나중에 넣었다면 [Analysis & Build]를 다시 돌리세요.
관련 항목
- 인스턴스 대장 — [ver] 버튼이 있는 OS 인스턴스 CMDB(
infr_instance) - 서비스 카탈로그 — 버전 원장 → [Analysis & Build] →
infr_service승격 - 인스턴스 서버 파라미터 — [param] nginx/my.cnf/sysctl 튜닝 스냅샷
- 인스턴스 변수 — [env] Tools 매시업 변수(Crypt)
- 인벤토리(주머니) — 프로브 스크립트가 시드된 Tools
- 감사 로그 — 버전 원장 쓰기 감사 확인
![카드의 [ver] 버튼](/manual-shots/ko/instance-card.webp)