태그 보관물: Container Registry

Enabling GitLab Container Registry over HTTPS with a Custom TLS Certificate

개요

GitLab Container Registry를 자체 관리형 Omnibus GitLab에서 HTTPS로 활성화한 작업 기록입니다. WordPress를 Kubernetes로 이전하면서 커스텀 이미지를 저장할 레지스트리가 필요했는데, 로컬 환경에 docker나 podman이 없어 클러스터 안에서 Kaniko로 이미지를 빌드하고 곧바로 푸시할 대상이 있어야 했습니다. 이미 사내에서 운영 중인 GitLab이 있었으므로 별도 레지스트리를 구축하는 대신 GitLab에 내장된 Container Registry를 켜는 방향을 선택했습니다.

Omnibus GitLab의 Container Registry는 기본적으로 비활성 상태이며 /etc/gitlab/gitlab.rb에서 직접 켜야 합니다. 이 과정에서 registry용 nginx 블록이 기존 커스텀 인증서 경로를 상속하지 않는 문제로 GitLab 전체 nginx가 기동 중지되는 상황을 겪었습니다. 레지스트리만 실패한 것이 아니라 GitLab 웹 UI까지 함께 접속 불가가 되었기 때문에, 같은 구성을 계획하고 계신 분이라면 미리 알아두실 만한 지점이라 판단해 정리합니다.

환경

항목 내용
GitLab Omnibus GitLab CE (Ubuntu, 192.168.x.x)
기존 도메인 gitlab.sierracloud.dev (HTTPS 서비스 중)
기존 TLS 인증서 Let’s Encrypt *.sierracloud.dev 와일드카드, /test/cert/에 배치
Registry 목표 주소 gitlab.sierracloud.dev:5050
이미지 사용처 Kubernetes 클러스터 (Kaniko 빌드 → 파드 imagePullSecrets)

호스트 방식 선택 — 포트 분리와 서브도메인 분리

GitLab Container Registry의 주소는 두 가지 방식으로 구성할 수 있습니다. 어느 쪽을 선택하느냐에 따라 필요한 DNS 레코드와 인증서가 달라지므로 먼저 결정해야 합니다.

방식 주소 예시 DNS 인증서
단일 호스트 + 포트 gitlab.sierracloud.dev:5050 추가 불필요 기존 와일드카드 재사용
별도 서브도메인 registry.gitlab.sierracloud.dev 레코드 추가 필요 신규 발급 또는 SAN 추가 필요

이번에는 기존에 발급받아 둔 *.sierracloud.dev 와일드카드 인증서를 그대로 재사용할 수 있고 DNS 작업이 필요 없는 단일 호스트 + 포트 방식을 선택했습니다. 다만 와일드카드 인증서는 *.sierracloud.dev 형태로 한 단계 서브도메인만 포함하므로, 별도 서브도메인 방식을 택했다면 registry.gitlab.sierracloud.dev는 두 단계가 되어 기존 인증서로 커버되지 않는다는 점도 고려 대상이었습니다.

사전 확인

GitLab은 여러 사용자가 함께 쓰는 공유 시스템이고 gitlab-ctl reconfigure가 서비스 재기동을 동반하므로, 설정을 바꾸기 전에 현재 상태와 복구 지점을 먼저 확보했습니다.

# 설정 파일 백업 (가장 중요 - 되돌릴 지점 확보)
sudo cp /etc/gitlab/gitlab.rb /etc/gitlab/gitlab.rb.bak-$(date +%Y%m%d)

# 현재 registry 관련 설정 확인 (주석이 아닌 유효 설정만)
sudo grep -n -i 'registry|external_url|nginx\[.ssl' /etc/gitlab/gitlab.rb | grep -v '^\s*#'

# 레지스트리 저장 공간 확인 - 이미지가 쌓이는 경로
df -h /var/opt/gitlab

# 방화벽 상태 및 현재 리스닝 포트
sudo ufw status
sudo ss -tlnp | grep -E ':443|:80|:5050'

# 기존 인증서 위치 확인
sudo ls -la /etc/gitlab/ssl/
sudo find /etc/letsencrypt -maxdepth 3

이 단계에서 두 가지를 확인했습니다. 첫째로 /etc/gitlab/ssl/ 디렉터리에는 인증서가 없었고, GitLab이 /test/cert/의 커스텀 경로를 바라보도록 구성되어 있었습니다. 둘째로 registry 관련 설정은 모두 주석 처리된 기본 상태였습니다. 이 첫 번째 사실이 뒤에서 문제의 원인이 됩니다.

Container Registry 활성화

/etc/gitlab/gitlab.rb 하단에 registry 설정을 추가합니다. Omnibus GitLab은 이 파일을 읽어 nginx와 registry 서비스 설정을 생성하는 구조입니다.

# gitlab.rb에 추가할 내용
registry_external_url 'https://gitlab.sierracloud.dev:5050'
gitlab_rails['registry_enabled'] = true

설정을 반영합니다. reconfigure는 Chef 기반으로 설정 파일을 재생성하고 관련 서비스를 재기동하므로 GitLab 이용자에게 짧은 영향이 발생합니다.

sudo gitlab-ctl reconfigure

# 반영 후 서비스 상태 확인
sudo gitlab-ctl status | grep -iE "nginx|registry"

트러블슈팅 — reconfigure 이후 nginx 전체 기동 실패

reconfigure는 오류 없이 끝났지만 5050 포트가 열리지 않았고, 서비스 상태를 확인하니 registry는 떠 있는데 nginx가 down 상태였습니다.

$ sudo gitlab-ctl status | grep -iE "nginx|registry"
down: nginx: 0s, normally up, want up; run: log: (pid 1249167) 3169106s
run: registry: (pid 2639512) 481s; run: log: (pid 2639249) 540s

nginx가 내려갔다는 것은 레지스트리뿐 아니라 GitLab 웹 UI 전체가 접속 불가라는 의미입니다. 실제로 외부에서 gitlab.sierracloud.dev에 접속되지 않는 상태였습니다.

원인 진단

nginx 설정 파일을 검증하려 했으나 nginx 명령이 PATH에 없었습니다. Omnibus GitLab은 nginx를 자체 번들로 포함하므로 임베디드 바이너리의 전체 경로를 지정해야 합니다.

# PATH에 없으므로 임베디드 바이너리 직접 호출
sudo /opt/gitlab/embedded/sbin/nginx -t -c /var/opt/gitlab/nginx/conf/nginx.conf

검증 결과 원인이 명확히 드러났습니다.

nginx: [emerg] cannot load certificate "/etc/gitlab/ssl/gitlab.sierracloud.dev.crt":
  BIO_new_file() failed (SSL: error:02001002:system library:fopen:
  No such file or directory)
nginx: configuration file /var/opt/gitlab/nginx/conf/nginx.conf test failed

존재하지 않는 /etc/gitlab/ssl/gitlab.sierracloud.dev.crt를 찾고 있었습니다. 이 서버는 인증서를 /test/cert/에 두고 사용하도록 구성되어 있었는데, registry용 nginx가 그 경로를 알지 못한 것입니다.

근본 원인 — registry_nginx는 인증서 설정을 상속하지 않습니다

Omnibus GitLab에서 registry_nginx는 기존 GitLab 웹용 nginx별개의 설정 블록입니다. nginx['ssl_certificate']에 커스텀 경로를 지정해 두었더라도 registry_nginx는 이를 물려받지 않고, Omnibus의 기본 명명 규칙인 /etc/gitlab/ssl/<호스트명>.crt를 찾습니다.

인증서를 기본 경로에 두고 쓰는 환경에서는 이 규칙이 우연히 맞아떨어지기 때문에 문제가 드러나지 않습니다. 반면 이번처럼 인증서를 별도 경로에서 관리하는 구성에서는 파일을 찾지 못하고, nginx는 설정 검증 실패 시 부분 기동이 아니라 전체 기동을 중단합니다. 레지스트리 하나를 추가하려다 GitLab 서비스 전체가 멈추는 이유가 여기에 있습니다.

해결

registry_nginx에도 동일한 인증서 경로를 명시적으로 지정합니다.

# gitlab.rb에 추가
registry_nginx['ssl_certificate'] = "/test/cert/fullchain.pem"
registry_nginx['ssl_certificate_key'] = "/test/cert/privkey.pem"
sudo gitlab-ctl reconfigure

$ sudo gitlab-ctl status | grep -iE "nginx|registry"
run: nginx: (pid 2643061) 44s; run: log: (pid 1249167) 3170680s
run: registry: (pid 2639512) 2055s; run: log: (pid 2639249) 2114s

nginx가 정상 기동되었습니다. 커스텀 인증서 경로를 쓰는 환경이라면 registry_external_urlregistry_nginx의 인증서 설정을 처음부터 같이 추가하는 것이 안전합니다. 두 항목을 한 번에 넣고 reconfigure를 한 번만 수행하면 다운타임 없이 마칠 수 있었던 작업이었습니다.

결과 확인

외부에서 GitLab 본 사이트와 레지스트리 엔드포인트에 각각 요청해 응답 코드를 확인합니다.

$ curl -sk -o /dev/null -w "gitlab main site -> %{http_code}\n" https://gitlab.sierracloud.dev/
gitlab main site -> 302

$ curl -sk -o /dev/null -w "registry :5050/v2/ -> %{http_code}\n" https://gitlab.sierracloud.dev:5050/v2/
registry :5050/v2/ -> 401

여기서 401 Unauthorized는 오류가 아니라 정상 신호입니다. Docker Registry HTTP API V2 규격상 /v2/ 엔드포인트는 인증되지 않은 요청에 401을 반환하면서 WWW-Authenticate 헤더로 토큰 발급처를 안내합니다. 레지스트리가 살아 있고 인증 흐름이 동작한다는 뜻이므로, 200을 기대하고 401을 실패로 오인하지 않도록 주의합니다. 반대로 000이나 커넥션 거부가 나오면 포트가 열리지 않았거나 nginx가 내려간 상태입니다.

Kubernetes 파드에서도 같은 주소에 도달하는지 확인합니다. 노드에서는 되는데 파드에서 안 되는 경우가 있어 클러스터 내부 경로를 별도로 검증했습니다.

kubectl run regtest --image=curlimages/curl:8.5.0 --rm -i --restart=Never --command -- \
  sh -c "curl -sk -o /dev/null -w '%{http_code}\n' https://gitlab.sierracloud.dev:5050/v2/"

# 출력: 401  (클러스터 내부에서도 도달 확인)

Kubernetes에서 이미지 가져오기

파드가 레지스트리에서 이미지를 받으려면 인증 정보가 담긴 Secret이 필요합니다. 개인 계정 대신 프로젝트 단위 Deploy Token을 발급해 사용했습니다. GitLab 프로젝트의 Settings → Repository → Deploy tokens에서 read_registry 권한으로 생성합니다.

kubectl create secret docker-registry gitlab-registry-cred \
  -n wordpress-system \
  --docker-server=gitlab.sierracloud.dev:5050 \
  --docker-username='gitlab+deploy-token-1' \
  --docker-password='<REDACTED>'

# Deployment에서 참조
# spec:
#   template:
#     spec:
#       imagePullSecrets:
#       - name: gitlab-registry-cred

운영 시 참고 사항

  • 인증서 갱신 — registry_nginx가 기존 인증서와 같은 파일을 바라보므로, 갱신 스크립트가 해당 경로의 파일을 제자리에서 교체한다면 별도 작업이 필요 없습니다. 다만 갱신 후에는 nginx 재기동이 필요합니다.
  • 저장 공간 — 이미지는 /var/opt/gitlab/gitlab-rails/shared/registry에 누적됩니다. 태그를 계속 늘려가는 운영이라면 디스크 사용량 모니터링과 정리 정책을 함께 준비하는 편이 좋습니다.
  • 포트 개방 — 방화벽이나 앞단 프록시를 거치는 구조라면 5050 포트에 대한 규칙을 별도로 추가해야 합니다. GitLab 서버가 내부망에서 직접 라우팅되는지 먼저 확인하시기 바랍니다.
  • 변경 전 백업gitlab.rb는 3,500줄이 넘는 파일이라 수정 전 백업이 실질적인 안전장치가 됩니다. 이번에도 백업본 대비 줄 수를 비교해 설정이 실제로 반영되었는지 확인했습니다.

참고

관련 포스트:

참고 문서: GitLab — Container Registry administration · Omnibus GitLab — SSL settings · Docker Registry HTTP API V2