InfoGrab DocsInfoGrab Docs

OpenBao 문제 해결

요약

복구 키 작업과 비상용 루트 토큰은 복구 키 관리를 참고합니다. GitLab 이 Linux 패키지를 사용하더라도 OpenBao는 항상 Kubernetes에서 실행됩니다. 다음 예시는 Cloud Native 네임스페이스 gitlab을 사용합니다.

복구 키 작업과 비상용 루트 토큰은 복구 키 관리를 참고합니다. Geo 페일오버는 Geo 재해 복구를 참고합니다.

OpenBao 실행 위치#

GitLab 이 Linux 패키지를 사용하더라도 OpenBao는 항상 Kubernetes에서 실행됩니다. 네임스페이스와 Deployment 이름은 설치 방법에 따라 다릅니다.

설치 방법 네임스페이스 Deployment Pod 컨테이너
Cloud Native GitLab gitlab gitlab-openbao openbao-server
Linux 패키지 openbao openbao openbao-server

다음 예시는 Cloud Native 네임스페이스 gitlab을 사용합니다. Linux 패키지 설치에서는 kubectl 명령의 gitlab을 openbao로 바꿉니다.

OpenBao Pod에는 app.kubernetes.io/name=openbao 레이블이 붙습니다. 활성 노드에는 openbao-active=true 레이블도 붙습니다.

OpenBao 로그 찾기#

OpenBao 로그는 kubectl logs로 확인합니다. 관련된 GitLab Rails 및 Sidekiq 로그는 설치 방법에 따라 별도의 위치에 저장됩니다.

소스 Cloud Native GitLab Linux 패키지
OpenBao 서버 openbao-server 컨테이너에서 kubectl logs openbao-server 컨테이너에서 kubectl logs
GitLab Rails webservice Pod에서 kubectl logs /var/log/gitlab/gitlab-rails/production_json.log
Sidekiq sidekiq Pod에서 kubectl logs /var/log/gitlab/sidekiq/current
GitLab Runner GitLab UI의 CI/CD job 로그 GitLab UI의 CI/CD job 로그

OpenBao는 감사 이벤트를 GitLab으로 전송하고, 동시에 OpenBao Pod 로그에도 기록합니다.

OpenBao Pod 찾기#

OpenBao Pod 목록과 활성 노드를 확인하려면 다음을 실행합니다.

kubectl get pods -n gitlab -l app.kubernetes.io/name=openbao \
  --label-columns openbao-active,openbao-sealed

OPENBAO-ACTIVE가 true 인 Pod가 활성 노드입니다. 나머지는 대기 노드입니다.

OpenBao 상태 확인#

OpenBao는 요청을 처리하려면 봉인이 해제되어 있어야 합니다. 확인하려면 Pod 안에서 bao status를 실행합니다.

OPENBAO_POD=$(kubectl get pods -n gitlab -l app.kubernetes.io/name=openbao -o name | head -1)
kubectl exec -n gitlab "$OPENBAO_POD" -c openbao-server -- \
  sh -c "BAO_ADDR=http://127.0.0.1:8200 bao status"

출력에서 Sealed는 false 여야 합니다. 활성 노드는 HA Mode active를, 대기 노드는 HA Mode standby를 표시합니다.

Seal Type       static
Initialized     true
Sealed          false
Storage Type    postgresql
HA Enabled      true
HA Mode         active

sys/seal-status 엔드포인트는 같은 상태를 "sealed":false로 보고합니다.

kubectl exec -n gitlab "$OPENBAO_POD" -c openbao-server -- \
  sh -c "BAO_ADDR=http://127.0.0.1:8200 bao read sys/seal-status"
Note

bao 바이너리는 Pod 안에 있습니다. Pod 내부에서 엔드포인트를 조회할 때는 bao read를 사용합니다.

로그에서 봉인 해제에 성공한 노드는 vault is unsealed를 기록합니다. 활성 노드는 acquired lock, enabling active operation을, 대기 노드는 entering standby mode를 기록합니다.

OPENBAO_POD=$(kubectl get pods -n gitlab -l app.kubernetes.io/name=openbao -o name | head -1)
kubectl logs -n gitlab "$OPENBAO_POD" -c openbao-server \
  | grep -E "acquired lock, enabling active operation|entering standby mode"

특정 시간대의 오류 찾기#

특정 시간대의 OpenBao 로그를 읽으려면 --since를 사용합니다.

OPENBAO_POD=$(kubectl get pods -n gitlab -l app.kubernetes.io/name=openbao -o name | head -1)
kubectl logs -n gitlab "$OPENBAO_POD" -c openbao-server --since=30m \
  | grep -iE "error|warn|failed"

Linux 패키지 설치에서는 Rails 및 Sidekiq 로그 파일을 시간 기준으로 검색합니다. 로그는 JSON 형식이며 한 줄에 이벤트 하나가 기록됩니다.

Note

OpenBao는 모든 출력을 표준 오류로 내보내므로, 일부 로그 플랫폼은 모든 줄을 오류로 표시합니다. 플랫폼이 붙인 레이블이 아니라 메시지 본문의 수준([info], [warn])을 기준으로 판단합니다.

GitLab Rails 로그#

Rails 로그에는 UI와 GraphQL API를 통한 시크릿 작업, 그리고 OpenBao가 보내는 감사 콜백이 기록됩니다.

Cloud Native 설치의 경우는 다음과 같습니다.

kubectl logs -n gitlab -l app=webservice -c webservice \
  | grep -E "Projects::SecretsController|Groups::SecretsController|secrets_manager/audit_logs"

Linux 패키지 설치의 경우는 다음과 같습니다.

grep -E "Projects::SecretsController|Groups::SecretsController|secrets_manager/audit_logs" \
  /var/log/gitlab/gitlab-rails/production_json.log

GraphQL 작업은 graphql:createProjectSecret 이나 graphql:getGroupSecrets 같은 caller_id로 나타납니다. 감사 콜백은 경로 /api/v4/internal/secrets_manager/audit_logs로 나타납니다.

Sidekiq 로그#

Secrets Manager 레코드를 프로비저닝하고 디프로비저닝하며 유지 관리하는 워커는 SecretsManagement:: 네임스페이스 아래에서 실행됩니다.

Cloud Native 설치의 경우는 다음과 같습니다.

kubectl logs -n gitlab -l app=sidekiq -c sidekiq | grep "SecretsManagement::"

Linux 패키지 설치의 경우는 다음과 같습니다.

grep "SecretsManagement::" /var/log/gitlab/sidekiq/current

프로비저닝 문제는 ProvisionProjectSecretsManagerTaskWorker 또는 ProvisionGroupSecretsManagerTaskWorker로 필터링합니다.

GitLab Runner 로그#

CI/CD job 이 시크릿을 가져오지 못하면 원인이 GitLab UI의 job 로그에 나타납니다. job 로그에서 다음 문자열을 검색합니다.

문자열 의미
Resolving secrets 러너가 job의 시크릿 확인을 시작했습니다.
Using "gitlab_secrets_manager" secret resolver 러너가 GitLab Secrets Manager 리졸버를 선택했습니다.
not initialized or sealed Vault server OpenBao가 봉인되었거나 초기화되지 않았습니다.
api error: status code 403: permission denied OpenBao가 요청을 거부했습니다. 주로 audience 또는 권한 문제입니다.
inline auth JWT is required 러너가 인증 요청을 구성하지 못했습니다.

정상 시작 로그#

재시작 후 활성 노드는 다음 순서로 로그를 기록합니다. 대기 노드는 vault is unsealed에서 멈춘 뒤 entering standby mode를 기록합니다. 줄 형식은 설정에 따라 달라지므로 접두사가 아니라 메시지 본문으로 대조합니다.

로그 메시지 의미 누락 시
==> OpenBao server started! 프로세스가 시작되고 설정을 읽었습니다. Pod 시작에 실패했습니다. Pod 이벤트를 확인합니다.
vault is unsealed 자동 봉인 해제에 성공했습니다. 자동 봉인 해제에 실패했습니다. 봉인 해제 시크릿 또는 KMS를 확인합니다.
acquired lock, enabling active operation 이 노드가 활성 노드가 되었습니다. 활성 노드가 없습니다. 데이터베이스와 HA 잠금을 확인합니다.
post-unseal setup complete 활성 노드의 설정이 완료되었습니다. 설정이 끝나지 않았습니다. 데이터베이스 연결을 확인합니다.

오류 메시지#

OpenBao 메시지는 openbao-server 컨테이너에서 나옵니다. GitLab 메시지는 Rails 또는 Sidekiq 로그에서 나옵니다.

컨테이너 메시지 설명 조치
openbao-server cipher: message authentication failed 봉인 키가 저장된 데이터를 복호화하지 못합니다. 정적 봉인 해제라면 기본 사이트에서 봉인 해제 시크릿을 복사합니다. KMS 봉인이라면 KMS 키를 확인합니다. Geo 배포 문제 해결을 참고합니다.
openbao-server unknown key ID 정적 봉인 해제 키 ID가 데이터베이스의 데이터와 일치하지 않습니다. 기본 사이트에서 봉인 해제 시크릿을 복사합니다. Geo 배포 문제 해결을 참고합니다.
openbao-server failed to acquire lock 대기 노드가 읽기 전용 데이터베이스에서 HA 잠금을 획득하지 못합니다. Geo 보조 사이트에서는 정상입니다. 조치가 필요하지 않습니다.
openbao-server cannot execute INSERT in a read-only transaction 대기 노드가 읽기 복제본에 쓰기를 시도했습니다. Geo 보조 사이트에서는 정상입니다. 그 밖의 경우에는 OpenBao에 데이터베이스 쓰기 권한이 있는지 확인하고 데이터베이스 권한을 점검합니다.
openbao-server post-unseal upgrade seal keys failed: error="no recovery key found" 복구 키가 저장된 적이 없습니다. 문제가 되지 않습니다. recovery_key:store를 실행합니다.
Rails or Sidekiq [OpenBao] health check returned unhealthy OpenBao가 응답했지만 비정상 상태를 보고했습니다. bao status와 OpenBao 로그를 확인합니다.
Rails or Sidekiq [OpenBao] health check failed GitLab 이 OpenBao에 도달하지 못했습니다. 연결을 확인합니다. GitLab 이 OpenBao에 연결하지 못하는 경우를 참고합니다.
Rails or Sidekiq Failed to authenticate with OpenBao OpenBao가 JWT를 거부했습니다. audience를 확인합니다. JWT 인증 실패를 참고합니다.
Rails or Sidekiq Failed to open TCP connection to <host>:443 (execution expired) Sidekiq 이 OpenBao URL에 도달하지 못했습니다. Sidekiq Pod에서 DNS와 OpenBao URL을 확인합니다.
Rails or Sidekiq SSL_connect ... state=error: wrong version number https URL 이 http를 제공하는 OpenBao 리스너를 가리킵니다. URL 스킴을 리스너에 맞춥니다. GitLab 이 OpenBao에 연결하지 못하는 경우를 참고합니다.
Rails or Sidekiq Retrying failed secrets_manager maintenance task 프로비저닝 또는 디프로비저닝 작업을 재시도하고 있습니다. 같은 로그에서 워커 오류를 확인합니다. 재시도는 세 번 후 중단됩니다.

Secrets Manager가 프로비저닝 상태에서 멈춤#

Secrets Manager를 활성화하면 토글이 로딩 상태에 머물고 상태가 provisioning으로 남을 수 있습니다. Secrets Manager에는 failed 상태가 없으므로, 활성화 전에 실패한 단계가 있으면 레코드가 멈춘 채로 남습니다. 대체로 Sidekiq 이 OpenBao에 도달하지 못하는 것이 원인입니다.

진단하려면 다음을 수행합니다.

  1. 프로비저닝 워커에 대한 Sidekiq 로그를 확인합니다.

    kubectl logs -n gitlab -l app=sidekiq -c sidekiq \
      | grep -E "ProvisionProjectSecretsManagerTaskWorker|ProvisionGroupSecretsManagerTaskWorker"
    
  2. Sidekiq Pod 또는 노드에서 Sidekiq 이 OpenBao에 도달할 수 있는지 확인합니다.

    curl "https://openbao.example.com/v1/sys/health"
    

유지 관리 워커는 멈춘 작업을 최대 세 번까지 재시도한 뒤 중단합니다. 그 이후에는 레코드가 provisioning 상태로 남고 자동 복구가 이루어지지 않으며, 재시도 과정에서 Retrying failed secrets_manager maintenance task가 기록됩니다.

연결 문제를 해결한 뒤에는 Secrets Manager를 비활성화했다가 다시 활성화해 프로비저닝을 재시도합니다.

자체 초기화 후 인증 마운트 누락#

여러 OpenBao Pod로 구성된 새 설치에서는 자체 초기화 경합으로 인해 OpenBao의 봉인은 해제되었지만 gitlab_rails_jwt/ 인증 마운트가 없는 상태가 될 수 있습니다. Pod는 정상으로 보이지만 시크릿 작업은 permission denied로 실패합니다. 루트 토큰으로 bao auth list를 실행해 마운트가 존재하는지 확인합니다. 경합을 방지하려면 새 설치를 복제본 하나로 시작해 초기화가 끝난 것을 확인한 뒤 확장합니다.

GitLab 이 OpenBao에 연결하지 못하는 경우#

GitLab Rails와 Sidekiq는 HTTP로 OpenBao에 연결합니다. Rails는 internal_url을 사용하고, internal_url 이 설정되어 있지 않으면 url로 대체합니다. 설정을 확인하려면 Rails 콘솔에서 다음을 실행합니다.

Gitlab.config.openbao.to_h

흔한 원인은 다음과 같습니다.

  • http를 제공하는 OpenBao 리스너에 https:// URL로 접속하면 wrong version number로 실패합니다. global.openbao.https는 GitLab 이 연결할 때 쓰는 스킴을 정하며, OpenBao 리스너의 TLS를 정하지 않습니다. 리스너는 기본적으로 일반 HTTP를 제공합니다. 이에 맞춰 global.openbao.https를 설정하지 않은 채로 두거나, openbao.config.tlsDisable: false로 리스너 TLS를 활성화하고 global.openbao.https를 true로 설정합니다.
  • 신뢰할 수 없는 TLS 인증서를 사용하면 OIDC 디스커버리와 감사 로깅이 실패합니다. GitLab 이 신뢰하는 인증서를 사용합니다.
  • OpenBao 감사 항목이 남지 않는 요청은 인증 백엔드까지 도달하지 못한 것입니다. Ingress 또는 리버스 프록시를 확인합니다.

Cloud Native 설치에서 정상 동작하는 설정은 다음과 같습니다.

global:
  openbao:
    enabled: true
    url: http://gitlab-openbao-active:8200
    internal_url: http://gitlab-openbao-active:8200

Linux 패키지 설치에서 GitLab은 /etc/gitlab/gitlab.rb의 gitlab_rails['openbao']['url'] 설정으로 OpenBao에 연결합니다. 번들 NGINX 리버스 프록시는 oak['components']['openbao'] 설정으로 OpenBao에 라우팅합니다. 자세한 내용은 Linux 패키지 배포용 OpenBao 설치를 참고합니다.

JWT 인증 실패#

GitLab은 JWT로 OpenBao에 인증합니다. JWT의 aud(audience) 클레임은 OpenBao 인증 역할의 bound_audiences 값과 정확히 일치해야 합니다. 후행 슬래시, http와 https의 차이, 포트 등 어떤 차이가 있어도 인증에 실패합니다.

OpenBao는 초기화 시점에 OpenBao URL에서 도출한 bound_audiences를 저장합니다. 이후 URL을 변경해도 저장된 값은 바뀌지 않습니다. 따라서 URL을 변경하면 저장된 bound_audiences가 GitLab 이 보내는 aud와 더 이상 일치하지 않아 인증이 깨집니다. 연결 URL과 별개로 audience를 설정하려면 global.openbao.jwt_audience를 사용합니다.

GitLab 이 보내는 audience를 확인하려면 Rails 콘솔에서 다음을 실행합니다.

SecretsManagement::ProjectSecretsManager.jwt_audience

이 메서드는 설정된 jwt_audience를 반환하며, jwt_audience가 설정되어 있지 않으면 OpenBao url을 반환합니다. 저장된 값을 확인하려면 루트 토큰으로 인증 역할을 읽어 bound_audiences를 해당 audience와 비교합니다.

Warning

권한 있는 액세스 없이는 이 문제를 해결할 수 없습니다. 루트 토큰은 자체 초기화 후 폐기되며, 봉인 해제 키로 대체할 수 없습니다. 봉인 해제 시크릿에는 봉인 해제 키만 들어 있고 루트 토큰은 들어 있지 않습니다.

저장된 시크릿을 삭제하지 않고 불일치를 해결하려면 복구 키로 인증을 다시 구성합니다. 절차는 복구 키로 인증 재구성을 참고합니다.

복구 키가 없으면 OpenBao 데이터를 초기화합니다. 이렇게 하면 저장된 시크릿이 모두 삭제됩니다.

OpenBao Pod가 봉인된 상태#

시작 시 bao status가 Sealed true를 보고하면 자동 봉인 해제가 실패한 것입니다.

  • 기본값인 정적 봉인 해제에서는 대개 봉인 해제 시크릿이 없거나 잘못된 것이 원인입니다. 해당 시크릿은 Cloud Native 설치에서는 gitlab-openbao-unseal, Linux 패키지 설치에서는 openbao-static-unseal 입니다.
  • 현재 AWS KMS(awskms)를 지원하는 KMS 자동 봉인 해제에서는 대개 OpenBao가 KMS에 도달하지 못하는 것이 원인입니다.

봉인 상태를 확인하는 방법은 OpenBao 상태 확인을 참고합니다.

Warning

이전 키를 보관하지 않은 채 정적 봉인 해제 키를 교체하면 OpenBao가 기존 데이터를 복호화하지 못합니다. 새 키와 함께 이전 키를 추가하고, 모든 Pod가 새 키로 실행된 뒤에만 이전 키를 제거합니다.

데이터베이스 문제#

OpenBao에는 전용 PostgreSQL 데이터베이스가 필요합니다. 전용 데이터베이스 없이 OpenBao를 활성화하면 GitLab 차트는 설치나 업그레이드를 실패 처리합니다.

그 밖의 데이터베이스 문제는 다음과 같습니다.

  • 커넥션 풀 고갈이나 높은 지연 시간은 간헐적인 타임아웃을 유발합니다.
  • Linux 패키지 PostgreSQL 설정에서 md5_auth_cidr_addresses, sslMode, 비밀번호 값이 잘못되면 OpenBao Pod가 CrashLoopBackOff 상태가 됩니다. 올바른 설정은 Linux 패키지 배포용 OpenBao 설치를 참고합니다.

감사 이벤트 누락#

OpenBao는 감사 이벤트를 /api/v4/internal/secrets_manager/audit_logs로 GitLab에 전송합니다. GitLab 차트는 기본적으로 감사 로깅을 활성화합니다. 감사 이벤트가 도착하지 않으면 다음을 확인합니다.

  • config.audit.http.enabled를 false로 설정하면 OpenBao가 이벤트를 전송하지 않습니다. 감사 로깅이 활성화되어 있는지 확인합니다.
  • 공유 감사 토큰이 일치하지 않으면 감사 엔드포인트가 401을 반환합니다. GitLab과 OpenBao가 같은 감사 토큰을 사용하는지 확인합니다.

Geo 배포 문제 해결#

OpenBao는 기본 Geo 사이트에서는 활성 노드로, 각 보조 사이트에서는 대기 노드로 실행됩니다. 보조 노드는 읽기 전용 PostgreSQL 복제본에 연결하므로 failed to acquire lock과 cannot execute INSERT in a read-only transaction을 기록합니다. 이 메시지는 정상입니다.

보조 노드가 cipher: message authentication failed 나 unknown key ID를 기록하면 해당 노드의 봉인 키가 기본 사이트와 일치하지 않는 것입니다. 해결 방법은 봉인 방식에 따라 다릅니다.

  • 정적 봉인 해제에서는 기본 클러스터의 gitlab-openbao-unseal 시크릿을 보조 클러스터로 복사한 뒤 OpenBao Pod를 재시작합니다.

    kubectl -n gitlab get secret gitlab-openbao-unseal -o yaml
    
  • KMS 봉인에서는 두 사이트가 같은 KMS 키를 사용하도록 구성합니다.

페일오버 후 JWT 인증이 실패하면 audience가 저장된 bound_audiences와 더 이상 일치하지 않는 것입니다. 해결 방법은 도메인에 따라 다릅니다.

  • 두 사이트가 모두 기본 OpenBao URL을 사용한다면 양쪽 사이트에서 jwt_audience를 기본 OpenBao URL로 설정합니다. 보조 사이트에 OpenBao 설치를 참고합니다.
  • 보조 사이트가 다른 도메인을 사용한다면 이 구성은 지원되지 않습니다. 모든 프로젝트와 그룹 네임스페이스도 다시 프로비저닝해야 하므로, audience를 다시 구성해도 인증은 복구되지 않습니다. 승격된 보조 사이트를 기본 도메인이 가리키도록 DNS를 변경합니다. 자세한 내용은 Geo 배포를 참고합니다.

느린 시크릿 작업 진단#

CI/CD job의 시크릿 조회가 느리거나 시크릿 작업이 타임아웃되면 다음 쿼리로 원인을 찾습니다. 이 쿼리는 OpenBao 메트릭을 수집하는 Prometheus 또는 Grafana 인스턴스에서 실행합니다. 해당 메트릭을 노출하는 방법은 OpenBao 메트릭을 참고합니다.

지연 시간 상승 확인#

다음 쿼리로 평균 요청 지연 시간을 밀리초 단위로 측정합니다. 이 쿼리는 트래픽이 적은 배포를 포함해 모든 트래픽 수준에서 동작합니다.

rate(openbao_core_handle_request_sum[5m])
/
rate(openbao_core_handle_request_count[5m])

정상 부하에서 전체 요청 유형의 평균 지연 시간은 보통 3~7ms 입니다. 평균 지연 시간이 지속적으로 20ms를 넘으면 원인을 조사합니다.

OpenBao가 실제로 요청을 처리하고 있을 때는 다음 쿼리로 P99 지연 시간을 확인합니다.

openbao_core_handle_request{quantile="0.99"}

정상적인 P99는 10ms 미만입니다. OpenBao가 유휴 상태이면 요약 윈도에 최근 관측값이 없으므로 이 쿼리는 NaN을 반환합니다. 그때는 rate 기반 쿼리를 사용합니다.

잠재적 문제 파악#

잠재적 문제 확인 항목 쿼리 임계값 조치
CPU 한도 과소 설정 CFS 스로틀 비율 CPU 스로틀링 쿼리 > 25% CPU 한도를 늘립니다
수요가 CPU 용량 초과 CPU 사용률 CPU 사용률 쿼리 요청량의 > 50% 사이징 표의 다음 행으로 확장합니다
요청 급증 처리 중 요청 수 openbao_core_in_flight_requests 5 초과 지속 일시적입니다. 재발 여부를 관찰합니다.
PostgreSQL 병목 평균 PostgreSQL 읽기 지연 시간 rate(openbao_postgres_get_sum[5m]) / rate(openbao_postgres_get_count[5m]) > 5ms PostgreSQL 리소스와 커넥션 풀을 확인합니다
메모리 압박 메모리 사용률 메모리 사용률 쿼리 메모리 요청량에 근접 네임스페이스 공식으로 메모리를 늘립니다

PostgreSQL 지연 시간이 높으면 커넥션 풀이 포화 상태인지 확인합니다. 모든 커넥션이 사용 중이면 추가 요청이 큐에 쌓여 지연이 발생합니다. 커넥션 풀 설정은 데이터베이스 리소스를 참고합니다.

OpenBao 문제 해결

GitLab v19.4
Tier: Premium, Ultimate
Offering: GitLab Self-Managed
원문 보기

요약

복구 키 작업과 비상용 루트 토큰은 복구 키 관리를 참고합니다. GitLab 이 Linux 패키지를 사용하더라도 OpenBao는 항상 Kubernetes에서 실행됩니다. 다음 예시는 Cloud Native 네임스페이스 gitlab을 사용합니다.

복구 키 작업과 비상용 루트 토큰은 복구 키 관리를 참고합니다. Geo 페일오버는 Geo 재해 복구를 참고합니다.

OpenBao 실행 위치#

GitLab 이 Linux 패키지를 사용하더라도 OpenBao는 항상 Kubernetes에서 실행됩니다. 네임스페이스와 Deployment 이름은 설치 방법에 따라 다릅니다.

설치 방법 네임스페이스 Deployment Pod 컨테이너
Cloud Native GitLab gitlab gitlab-openbao openbao-server
Linux 패키지 openbao openbao openbao-server

다음 예시는 Cloud Native 네임스페이스 gitlab을 사용합니다. Linux 패키지 설치에서는 kubectl 명령의 gitlab을 openbao로 바꿉니다.

OpenBao Pod에는 app.kubernetes.io/name=openbao 레이블이 붙습니다. 활성 노드에는 openbao-active=true 레이블도 붙습니다.

OpenBao 로그 찾기#

OpenBao 로그는 kubectl logs로 확인합니다. 관련된 GitLab Rails 및 Sidekiq 로그는 설치 방법에 따라 별도의 위치에 저장됩니다.

소스 Cloud Native GitLab Linux 패키지
OpenBao 서버 openbao-server 컨테이너에서 kubectl logs openbao-server 컨테이너에서 kubectl logs
GitLab Rails webservice Pod에서 kubectl logs /var/log/gitlab/gitlab-rails/production_json.log
Sidekiq sidekiq Pod에서 kubectl logs /var/log/gitlab/sidekiq/current
GitLab Runner GitLab UI의 CI/CD job 로그 GitLab UI의 CI/CD job 로그

OpenBao는 감사 이벤트를 GitLab으로 전송하고, 동시에 OpenBao Pod 로그에도 기록합니다.

OpenBao Pod 찾기#

OpenBao Pod 목록과 활성 노드를 확인하려면 다음을 실행합니다.

kubectl get pods -n gitlab -l app.kubernetes.io/name=openbao \
  --label-columns openbao-active,openbao-sealed

OPENBAO-ACTIVE가 true 인 Pod가 활성 노드입니다. 나머지는 대기 노드입니다.

OpenBao 상태 확인#

OpenBao는 요청을 처리하려면 봉인이 해제되어 있어야 합니다. 확인하려면 Pod 안에서 bao status를 실행합니다.

OPENBAO_POD=$(kubectl get pods -n gitlab -l app.kubernetes.io/name=openbao -o name | head -1)
kubectl exec -n gitlab "$OPENBAO_POD" -c openbao-server -- \
  sh -c "BAO_ADDR=http://127.0.0.1:8200 bao status"

출력에서 Sealed는 false 여야 합니다. 활성 노드는 HA Mode active를, 대기 노드는 HA Mode standby를 표시합니다.

Seal Type       static
Initialized     true
Sealed          false
Storage Type    postgresql
HA Enabled      true
HA Mode         active

sys/seal-status 엔드포인트는 같은 상태를 "sealed":false로 보고합니다.

kubectl exec -n gitlab "$OPENBAO_POD" -c openbao-server -- \
  sh -c "BAO_ADDR=http://127.0.0.1:8200 bao read sys/seal-status"
Note

bao 바이너리는 Pod 안에 있습니다. Pod 내부에서 엔드포인트를 조회할 때는 bao read를 사용합니다.

로그에서 봉인 해제에 성공한 노드는 vault is unsealed를 기록합니다. 활성 노드는 acquired lock, enabling active operation을, 대기 노드는 entering standby mode를 기록합니다.

OPENBAO_POD=$(kubectl get pods -n gitlab -l app.kubernetes.io/name=openbao -o name | head -1)
kubectl logs -n gitlab "$OPENBAO_POD" -c openbao-server \
  | grep -E "acquired lock, enabling active operation|entering standby mode"

특정 시간대의 오류 찾기#

특정 시간대의 OpenBao 로그를 읽으려면 --since를 사용합니다.

OPENBAO_POD=$(kubectl get pods -n gitlab -l app.kubernetes.io/name=openbao -o name | head -1)
kubectl logs -n gitlab "$OPENBAO_POD" -c openbao-server --since=30m \
  | grep -iE "error|warn|failed"

Linux 패키지 설치에서는 Rails 및 Sidekiq 로그 파일을 시간 기준으로 검색합니다. 로그는 JSON 형식이며 한 줄에 이벤트 하나가 기록됩니다.

Note

OpenBao는 모든 출력을 표준 오류로 내보내므로, 일부 로그 플랫폼은 모든 줄을 오류로 표시합니다. 플랫폼이 붙인 레이블이 아니라 메시지 본문의 수준([info], [warn])을 기준으로 판단합니다.

GitLab Rails 로그#

Rails 로그에는 UI와 GraphQL API를 통한 시크릿 작업, 그리고 OpenBao가 보내는 감사 콜백이 기록됩니다.

Cloud Native 설치의 경우는 다음과 같습니다.

kubectl logs -n gitlab -l app=webservice -c webservice \
  | grep -E "Projects::SecretsController|Groups::SecretsController|secrets_manager/audit_logs"

Linux 패키지 설치의 경우는 다음과 같습니다.

grep -E "Projects::SecretsController|Groups::SecretsController|secrets_manager/audit_logs" \
  /var/log/gitlab/gitlab-rails/production_json.log

GraphQL 작업은 graphql:createProjectSecret 이나 graphql:getGroupSecrets 같은 caller_id로 나타납니다. 감사 콜백은 경로 /api/v4/internal/secrets_manager/audit_logs로 나타납니다.

Sidekiq 로그#

Secrets Manager 레코드를 프로비저닝하고 디프로비저닝하며 유지 관리하는 워커는 SecretsManagement:: 네임스페이스 아래에서 실행됩니다.

Cloud Native 설치의 경우는 다음과 같습니다.

kubectl logs -n gitlab -l app=sidekiq -c sidekiq | grep "SecretsManagement::"

Linux 패키지 설치의 경우는 다음과 같습니다.

grep "SecretsManagement::" /var/log/gitlab/sidekiq/current

프로비저닝 문제는 ProvisionProjectSecretsManagerTaskWorker 또는 ProvisionGroupSecretsManagerTaskWorker로 필터링합니다.

GitLab Runner 로그#

CI/CD job 이 시크릿을 가져오지 못하면 원인이 GitLab UI의 job 로그에 나타납니다. job 로그에서 다음 문자열을 검색합니다.

문자열 의미
Resolving secrets 러너가 job의 시크릿 확인을 시작했습니다.
Using "gitlab_secrets_manager" secret resolver 러너가 GitLab Secrets Manager 리졸버를 선택했습니다.
not initialized or sealed Vault server OpenBao가 봉인되었거나 초기화되지 않았습니다.
api error: status code 403: permission denied OpenBao가 요청을 거부했습니다. 주로 audience 또는 권한 문제입니다.
inline auth JWT is required 러너가 인증 요청을 구성하지 못했습니다.

정상 시작 로그#

재시작 후 활성 노드는 다음 순서로 로그를 기록합니다. 대기 노드는 vault is unsealed에서 멈춘 뒤 entering standby mode를 기록합니다. 줄 형식은 설정에 따라 달라지므로 접두사가 아니라 메시지 본문으로 대조합니다.

로그 메시지 의미 누락 시
==> OpenBao server started! 프로세스가 시작되고 설정을 읽었습니다. Pod 시작에 실패했습니다. Pod 이벤트를 확인합니다.
vault is unsealed 자동 봉인 해제에 성공했습니다. 자동 봉인 해제에 실패했습니다. 봉인 해제 시크릿 또는 KMS를 확인합니다.
acquired lock, enabling active operation 이 노드가 활성 노드가 되었습니다. 활성 노드가 없습니다. 데이터베이스와 HA 잠금을 확인합니다.
post-unseal setup complete 활성 노드의 설정이 완료되었습니다. 설정이 끝나지 않았습니다. 데이터베이스 연결을 확인합니다.

오류 메시지#

OpenBao 메시지는 openbao-server 컨테이너에서 나옵니다. GitLab 메시지는 Rails 또는 Sidekiq 로그에서 나옵니다.

컨테이너 메시지 설명 조치
openbao-server cipher: message authentication failed 봉인 키가 저장된 데이터를 복호화하지 못합니다. 정적 봉인 해제라면 기본 사이트에서 봉인 해제 시크릿을 복사합니다. KMS 봉인이라면 KMS 키를 확인합니다. Geo 배포 문제 해결을 참고합니다.
openbao-server unknown key ID 정적 봉인 해제 키 ID가 데이터베이스의 데이터와 일치하지 않습니다. 기본 사이트에서 봉인 해제 시크릿을 복사합니다. Geo 배포 문제 해결을 참고합니다.
openbao-server failed to acquire lock 대기 노드가 읽기 전용 데이터베이스에서 HA 잠금을 획득하지 못합니다. Geo 보조 사이트에서는 정상입니다. 조치가 필요하지 않습니다.
openbao-server cannot execute INSERT in a read-only transaction 대기 노드가 읽기 복제본에 쓰기를 시도했습니다. Geo 보조 사이트에서는 정상입니다. 그 밖의 경우에는 OpenBao에 데이터베이스 쓰기 권한이 있는지 확인하고 데이터베이스 권한을 점검합니다.
openbao-server post-unseal upgrade seal keys failed: error="no recovery key found" 복구 키가 저장된 적이 없습니다. 문제가 되지 않습니다. recovery_key:store를 실행합니다.
Rails or Sidekiq [OpenBao] health check returned unhealthy OpenBao가 응답했지만 비정상 상태를 보고했습니다. bao status와 OpenBao 로그를 확인합니다.
Rails or Sidekiq [OpenBao] health check failed GitLab 이 OpenBao에 도달하지 못했습니다. 연결을 확인합니다. GitLab 이 OpenBao에 연결하지 못하는 경우를 참고합니다.
Rails or Sidekiq Failed to authenticate with OpenBao OpenBao가 JWT를 거부했습니다. audience를 확인합니다. JWT 인증 실패를 참고합니다.
Rails or Sidekiq Failed to open TCP connection to <host>:443 (execution expired) Sidekiq 이 OpenBao URL에 도달하지 못했습니다. Sidekiq Pod에서 DNS와 OpenBao URL을 확인합니다.
Rails or Sidekiq SSL_connect ... state=error: wrong version number https URL 이 http를 제공하는 OpenBao 리스너를 가리킵니다. URL 스킴을 리스너에 맞춥니다. GitLab 이 OpenBao에 연결하지 못하는 경우를 참고합니다.
Rails or Sidekiq Retrying failed secrets_manager maintenance task 프로비저닝 또는 디프로비저닝 작업을 재시도하고 있습니다. 같은 로그에서 워커 오류를 확인합니다. 재시도는 세 번 후 중단됩니다.

Secrets Manager가 프로비저닝 상태에서 멈춤#

Secrets Manager를 활성화하면 토글이 로딩 상태에 머물고 상태가 provisioning으로 남을 수 있습니다. Secrets Manager에는 failed 상태가 없으므로, 활성화 전에 실패한 단계가 있으면 레코드가 멈춘 채로 남습니다. 대체로 Sidekiq 이 OpenBao에 도달하지 못하는 것이 원인입니다.

진단하려면 다음을 수행합니다.

  1. 프로비저닝 워커에 대한 Sidekiq 로그를 확인합니다.

    kubectl logs -n gitlab -l app=sidekiq -c sidekiq \
      | grep -E "ProvisionProjectSecretsManagerTaskWorker|ProvisionGroupSecretsManagerTaskWorker"
    
  2. Sidekiq Pod 또는 노드에서 Sidekiq 이 OpenBao에 도달할 수 있는지 확인합니다.

    curl "https://openbao.example.com/v1/sys/health"
    

유지 관리 워커는 멈춘 작업을 최대 세 번까지 재시도한 뒤 중단합니다. 그 이후에는 레코드가 provisioning 상태로 남고 자동 복구가 이루어지지 않으며, 재시도 과정에서 Retrying failed secrets_manager maintenance task가 기록됩니다.

연결 문제를 해결한 뒤에는 Secrets Manager를 비활성화했다가 다시 활성화해 프로비저닝을 재시도합니다.

자체 초기화 후 인증 마운트 누락#

여러 OpenBao Pod로 구성된 새 설치에서는 자체 초기화 경합으로 인해 OpenBao의 봉인은 해제되었지만 gitlab_rails_jwt/ 인증 마운트가 없는 상태가 될 수 있습니다. Pod는 정상으로 보이지만 시크릿 작업은 permission denied로 실패합니다. 루트 토큰으로 bao auth list를 실행해 마운트가 존재하는지 확인합니다. 경합을 방지하려면 새 설치를 복제본 하나로 시작해 초기화가 끝난 것을 확인한 뒤 확장합니다.

GitLab 이 OpenBao에 연결하지 못하는 경우#

GitLab Rails와 Sidekiq는 HTTP로 OpenBao에 연결합니다. Rails는 internal_url을 사용하고, internal_url 이 설정되어 있지 않으면 url로 대체합니다. 설정을 확인하려면 Rails 콘솔에서 다음을 실행합니다.

Gitlab.config.openbao.to_h

흔한 원인은 다음과 같습니다.

  • http를 제공하는 OpenBao 리스너에 https:// URL로 접속하면 wrong version number로 실패합니다. global.openbao.https는 GitLab 이 연결할 때 쓰는 스킴을 정하며, OpenBao 리스너의 TLS를 정하지 않습니다. 리스너는 기본적으로 일반 HTTP를 제공합니다. 이에 맞춰 global.openbao.https를 설정하지 않은 채로 두거나, openbao.config.tlsDisable: false로 리스너 TLS를 활성화하고 global.openbao.https를 true로 설정합니다.
  • 신뢰할 수 없는 TLS 인증서를 사용하면 OIDC 디스커버리와 감사 로깅이 실패합니다. GitLab 이 신뢰하는 인증서를 사용합니다.
  • OpenBao 감사 항목이 남지 않는 요청은 인증 백엔드까지 도달하지 못한 것입니다. Ingress 또는 리버스 프록시를 확인합니다.

Cloud Native 설치에서 정상 동작하는 설정은 다음과 같습니다.

global:
  openbao:
    enabled: true
    url: http://gitlab-openbao-active:8200
    internal_url: http://gitlab-openbao-active:8200

Linux 패키지 설치에서 GitLab은 /etc/gitlab/gitlab.rb의 gitlab_rails['openbao']['url'] 설정으로 OpenBao에 연결합니다. 번들 NGINX 리버스 프록시는 oak['components']['openbao'] 설정으로 OpenBao에 라우팅합니다. 자세한 내용은 Linux 패키지 배포용 OpenBao 설치를 참고합니다.

JWT 인증 실패#

GitLab은 JWT로 OpenBao에 인증합니다. JWT의 aud(audience) 클레임은 OpenBao 인증 역할의 bound_audiences 값과 정확히 일치해야 합니다. 후행 슬래시, http와 https의 차이, 포트 등 어떤 차이가 있어도 인증에 실패합니다.

OpenBao는 초기화 시점에 OpenBao URL에서 도출한 bound_audiences를 저장합니다. 이후 URL을 변경해도 저장된 값은 바뀌지 않습니다. 따라서 URL을 변경하면 저장된 bound_audiences가 GitLab 이 보내는 aud와 더 이상 일치하지 않아 인증이 깨집니다. 연결 URL과 별개로 audience를 설정하려면 global.openbao.jwt_audience를 사용합니다.

GitLab 이 보내는 audience를 확인하려면 Rails 콘솔에서 다음을 실행합니다.

SecretsManagement::ProjectSecretsManager.jwt_audience

이 메서드는 설정된 jwt_audience를 반환하며, jwt_audience가 설정되어 있지 않으면 OpenBao url을 반환합니다. 저장된 값을 확인하려면 루트 토큰으로 인증 역할을 읽어 bound_audiences를 해당 audience와 비교합니다.

Warning

권한 있는 액세스 없이는 이 문제를 해결할 수 없습니다. 루트 토큰은 자체 초기화 후 폐기되며, 봉인 해제 키로 대체할 수 없습니다. 봉인 해제 시크릿에는 봉인 해제 키만 들어 있고 루트 토큰은 들어 있지 않습니다.

저장된 시크릿을 삭제하지 않고 불일치를 해결하려면 복구 키로 인증을 다시 구성합니다. 절차는 복구 키로 인증 재구성을 참고합니다.

복구 키가 없으면 OpenBao 데이터를 초기화합니다. 이렇게 하면 저장된 시크릿이 모두 삭제됩니다.

OpenBao Pod가 봉인된 상태#

시작 시 bao status가 Sealed true를 보고하면 자동 봉인 해제가 실패한 것입니다.

  • 기본값인 정적 봉인 해제에서는 대개 봉인 해제 시크릿이 없거나 잘못된 것이 원인입니다. 해당 시크릿은 Cloud Native 설치에서는 gitlab-openbao-unseal, Linux 패키지 설치에서는 openbao-static-unseal 입니다.
  • 현재 AWS KMS(awskms)를 지원하는 KMS 자동 봉인 해제에서는 대개 OpenBao가 KMS에 도달하지 못하는 것이 원인입니다.

봉인 상태를 확인하는 방법은 OpenBao 상태 확인을 참고합니다.

Warning

이전 키를 보관하지 않은 채 정적 봉인 해제 키를 교체하면 OpenBao가 기존 데이터를 복호화하지 못합니다. 새 키와 함께 이전 키를 추가하고, 모든 Pod가 새 키로 실행된 뒤에만 이전 키를 제거합니다.

데이터베이스 문제#

OpenBao에는 전용 PostgreSQL 데이터베이스가 필요합니다. 전용 데이터베이스 없이 OpenBao를 활성화하면 GitLab 차트는 설치나 업그레이드를 실패 처리합니다.

그 밖의 데이터베이스 문제는 다음과 같습니다.

  • 커넥션 풀 고갈이나 높은 지연 시간은 간헐적인 타임아웃을 유발합니다.
  • Linux 패키지 PostgreSQL 설정에서 md5_auth_cidr_addresses, sslMode, 비밀번호 값이 잘못되면 OpenBao Pod가 CrashLoopBackOff 상태가 됩니다. 올바른 설정은 Linux 패키지 배포용 OpenBao 설치를 참고합니다.

감사 이벤트 누락#

OpenBao는 감사 이벤트를 /api/v4/internal/secrets_manager/audit_logs로 GitLab에 전송합니다. GitLab 차트는 기본적으로 감사 로깅을 활성화합니다. 감사 이벤트가 도착하지 않으면 다음을 확인합니다.

  • config.audit.http.enabled를 false로 설정하면 OpenBao가 이벤트를 전송하지 않습니다. 감사 로깅이 활성화되어 있는지 확인합니다.
  • 공유 감사 토큰이 일치하지 않으면 감사 엔드포인트가 401을 반환합니다. GitLab과 OpenBao가 같은 감사 토큰을 사용하는지 확인합니다.

Geo 배포 문제 해결#

OpenBao는 기본 Geo 사이트에서는 활성 노드로, 각 보조 사이트에서는 대기 노드로 실행됩니다. 보조 노드는 읽기 전용 PostgreSQL 복제본에 연결하므로 failed to acquire lock과 cannot execute INSERT in a read-only transaction을 기록합니다. 이 메시지는 정상입니다.

보조 노드가 cipher: message authentication failed 나 unknown key ID를 기록하면 해당 노드의 봉인 키가 기본 사이트와 일치하지 않는 것입니다. 해결 방법은 봉인 방식에 따라 다릅니다.

  • 정적 봉인 해제에서는 기본 클러스터의 gitlab-openbao-unseal 시크릿을 보조 클러스터로 복사한 뒤 OpenBao Pod를 재시작합니다.

    kubectl -n gitlab get secret gitlab-openbao-unseal -o yaml
    
  • KMS 봉인에서는 두 사이트가 같은 KMS 키를 사용하도록 구성합니다.

페일오버 후 JWT 인증이 실패하면 audience가 저장된 bound_audiences와 더 이상 일치하지 않는 것입니다. 해결 방법은 도메인에 따라 다릅니다.

  • 두 사이트가 모두 기본 OpenBao URL을 사용한다면 양쪽 사이트에서 jwt_audience를 기본 OpenBao URL로 설정합니다. 보조 사이트에 OpenBao 설치를 참고합니다.
  • 보조 사이트가 다른 도메인을 사용한다면 이 구성은 지원되지 않습니다. 모든 프로젝트와 그룹 네임스페이스도 다시 프로비저닝해야 하므로, audience를 다시 구성해도 인증은 복구되지 않습니다. 승격된 보조 사이트를 기본 도메인이 가리키도록 DNS를 변경합니다. 자세한 내용은 Geo 배포를 참고합니다.

느린 시크릿 작업 진단#

CI/CD job의 시크릿 조회가 느리거나 시크릿 작업이 타임아웃되면 다음 쿼리로 원인을 찾습니다. 이 쿼리는 OpenBao 메트릭을 수집하는 Prometheus 또는 Grafana 인스턴스에서 실행합니다. 해당 메트릭을 노출하는 방법은 OpenBao 메트릭을 참고합니다.

지연 시간 상승 확인#

다음 쿼리로 평균 요청 지연 시간을 밀리초 단위로 측정합니다. 이 쿼리는 트래픽이 적은 배포를 포함해 모든 트래픽 수준에서 동작합니다.

rate(openbao_core_handle_request_sum[5m])
/
rate(openbao_core_handle_request_count[5m])

정상 부하에서 전체 요청 유형의 평균 지연 시간은 보통 3~7ms 입니다. 평균 지연 시간이 지속적으로 20ms를 넘으면 원인을 조사합니다.

OpenBao가 실제로 요청을 처리하고 있을 때는 다음 쿼리로 P99 지연 시간을 확인합니다.

openbao_core_handle_request{quantile="0.99"}

정상적인 P99는 10ms 미만입니다. OpenBao가 유휴 상태이면 요약 윈도에 최근 관측값이 없으므로 이 쿼리는 NaN을 반환합니다. 그때는 rate 기반 쿼리를 사용합니다.

잠재적 문제 파악#

잠재적 문제 확인 항목 쿼리 임계값 조치
CPU 한도 과소 설정 CFS 스로틀 비율 CPU 스로틀링 쿼리 > 25% CPU 한도를 늘립니다
수요가 CPU 용량 초과 CPU 사용률 CPU 사용률 쿼리 요청량의 > 50% 사이징 표의 다음 행으로 확장합니다
요청 급증 처리 중 요청 수 openbao_core_in_flight_requests 5 초과 지속 일시적입니다. 재발 여부를 관찰합니다.
PostgreSQL 병목 평균 PostgreSQL 읽기 지연 시간 rate(openbao_postgres_get_sum[5m]) / rate(openbao_postgres_get_count[5m]) > 5ms PostgreSQL 리소스와 커넥션 풀을 확인합니다
메모리 압박 메모리 사용률 메모리 사용률 쿼리 메모리 요청량에 근접 네임스페이스 공식으로 메모리를 늘립니다

PostgreSQL 지연 시간이 높으면 커넥션 풀이 포화 상태인지 확인합니다. 모든 커넥션이 사용 중이면 추가 요청이 큐에 쌓여 지연이 발생합니다. 커넥션 풀 설정은 데이터베이스 리소스를 참고합니다.