Nexus3 Repository 구축

파아란곰탱이·2023년 5월 14일
0
post-thumbnail

제약 및 참고사항

  1. 이 자료는 예전에 만든 자료를 백업한 것이다. 작성 시점은 22.10.15이다.
  2. 실습 환경은 Ubuntu 20.04 LTS
  3. 모든 명령어는 적절한 권한을 가지고 실행한다고 가정한다. 즉, 필요하면 sudo를 사용한다.
  4. 본 포스트는 nexus 서버의 hostname은 nexus.kakao.com으로 설정한다.
    이건 예시일 뿐이니 본인이 원하는대로 바꿔서 쓰자.
    이 설정은 해당 서버에 접근할 다른 서버들의 hosts 파일에도 동일하게 적용한다고 가정한다.
  5. 설치 환경의 기본적인 구성은 완료되어 있다고 가정한다.
    (hostname, hosts, 시간동기화 등)
  6. 방화벽 설정은 생략한다.
    필요에 따라서 방화벽 정책을 구성하거나 비활성화 하면 된다.

서론

sonatype nexus repository는 sonatype에서 만든 저장소 소프트웨어이다.

다양한 형식을 지원하는 사설 저장소를 만들 수 있으며, 대표적으로 docker image repository, maven repository 등이 있다.

그리고 이러한 사설 저장소를 구성하고, 관리할 수 있게 도와주는 소프트웨어가 바로 nexus이다.


이러한 사설 저장소가 필요한 이유는 여러가지 이유가 있을 수 있다.

  • 보안상의 이유로 외부망에 있는 repository에 접근하지 못하는 경우
  • 각종 image, library등을 public repository에 업로드가 불가능한 경우
  • 폐쇄망 환경에서 개발을 진행해야 할 때, 망 내부에서 접근 가능한 repository가 필요한 경우
  • 개발팀에서 공통적으로 사용하는 image, library를 공유하기 위해
  • 네트워크 환경에 문제가 있거나, 한번 다운받은 이미지에 대해 캐싱이 필요한 경우
  • 기타 등등…

필자는 본 포스트에서 docker image repository를 nexus를 통해 구축하는 방법을 설명한다.

nexus 리포지토리를 구성하는 방법은 크게 2가지로 나뉜다.

  1. Linux Container 형태로 nexus 서비스를 실행하는 방법 (EX. Docker를 이용)
  2. Linux 환경에 직접 nexus를 설치하여 서비스를 실행하는 방법

본 포스트에서는 후자의 방법인 Linux 환경에 직접 nexus3를 설치하여 서비스를 실행한다.

그리고 docker image registry를 구성한다.




Nexus Repository 설치

nexus repository라는 소프트웨어는 Java의 대표적인 WAS 중 하나인 Jetty 기반 애플리케이션이다.

그렇기에 nexus repository를 구동하는 시스템은 jre 환경이 구성되어 있어야 한다. nexus는 최소 Java8 이상을 요구한다.

다음과 같은 명령을 통해 jre를 설치한다.

필자가 설치하는 jre는 openjdk-8-jre-headless인데 일반적으로 서버에서 구동하는 프로그램은 GUI가 불필요하기 때문에 관련 요소를 제거한 버전이 바로 headless이다.

apt install -y openjdk-8-jre-headless

이후, nexus repository 바이너리를 다운로드 받는다.

해당 파일은 sonatype 공식 홈페이지에서 다운로드 받을 수 있으며, 본 포스트 작성일인 2022.10.15 기준 최신 버전인 3.42.0-01 버전을 사용한다.

다음과 같은 명령을 통해 적절한 경로에 바이너리 파일을 다운로드 받고 압축을 해제한다.

wget https://download.sonatype.com/nexus/3/nexus-3.42.0-01-unix.tar.gz
tar xzf nexus-3.42.0-01-unix.tar.gz

압축을 해제하면 nexus-<버전명> 디렉터리와 sonatype-work 디렉터리가 생성된다. 각각의 디렉터리는 다음과 같은 목적으로 사용된다.

  • nexus-<버전명> : nexus repository 실행 바이너리 및 Java 라이브러리, 설정 파일 등이 존재
  • sonatye-work : repository 관리자가 저장하고 관리하는 모든 구성요소, 데이터가 존재

쉽게 말해 nexus-버전명은 실행 관련, sonatype-work는 데이터 파일 관련이다.


두 디렉터리를 적절한 위치로 옮긴다.

본 포스트에서는 /opt 하위에 이동한다.

다만, nexus-버전명 디렉터리는 nexus로 이름을 변경하여 이동한다.

mv nexus-3.42.0-01 /opt/nexus
mv sonatype-work /opt/


이후 nexus repository 서비스를 담당할 유저를 하나 생성한다. 유저명은 nexus로 칭한다.

또한, nexus 유저에게 적절한 권한을 부여한 뒤, nexus repository 관련 파일들의 소유주를 변경한다.

다음과 같은 명령을 수행하여 nexus 유저 및 nexus repository 관련 파일들의 권한을 제어한다.

useradd -s /bin/bash nexus
chown -R nexus:nexus /opt/nexus/
chown -R nexus:nexus /opt/sonatype-work/



권한 설정이 완료되었다면 nexus가 실행될 때 참조하는 설정파일을 수정하고, nexus를 systemd에 등록하여 서비스로 만들어야 한다.

이를 위해 설정파일에서 몇 가지를 수정한다.

우선 nexus가 서비스로써 실행 시 참조하는 파일인 nexus.rc를 수정해야 한다.

해당 파일의 실행 유저 옵션을 주석 해제하고 nexus 유저로 변경한다.

파일의 경로는 /opt/nexus/bin/nexus.rc 이다.

편집기로 파일을 열어 아래와 같이 수정한 뒤 저장한다.

run_as_user="nexus"

추가적으로, nexus 가 구동될 때 JVM 옵션을 변경하고 싶다면 /opt/nexus/bin/nexus.vmoptions 파일을 열어서 수정할 수 있으니 참고하길 바란다.

JVM 실행 옵션에 대한 내용은 본 포스트와는 관계 없으니 생략한다.



이번에는 nexus repository를 서비스로 등록하기 위해 nexus.service 파일을 작성한다.

/etc/systemd/system/nexus.service 파일을 생성한 뒤, 파일의 내용은 아래와 같이 작성한다.

필요에 따라 해당 파일의 내용은 적절히 수정할 수 있다.

[Unit]
Description=nexus service
After=network.target

[Service]
Type=forking
LimitNOFILE=65536 # 해당 옵션은 환경에 맞게 적절히 수정한다.
ExecStart=/opt/nexus/bin/nexus start
ExecStop=/opt/nexus/bin/nexus stop
User=nexus
Restart=on-abort

[Install]
WantedBy=multi-user.target

이후 다음과 같은 명령을 수행하여 nexus 서비스를 등록 후 실행한다.

systemctl daemon-reload
systemctl enable nexus.service
systemctl start nexus.service



nexus repository 서비스가 정상적으로 실행되면 nesux 대시보드에 접근할 수 있다.

nexus 서비스는 기본적으로 8081 포트에 바인드 되며, 다음과 같은 주소를 통해 접근할 수 있다.

http://<NEXUS-IP>:8081



좌측 상단의 Sign in 버튼을 누르면 default 계정인 admin으로 로그인할 수 있다.

해당 화면에 표시되는 경로에 초기 비밀번호가 있으니 해당 비밀번호를 확인하여 로그인 한 뒤, 절차에 맞춰 초기 계정 설정을 진행한다.

필자는 계정명/암호를 admin/admin으로 설정하였다.



nexus 서비스는 정상적으로 실행되는 것을 확인했으니, 이번에는 docker image를 위한 repository를 구성한다.

본 포스트에서는 local registry와 Docker Hub와 연결된 registry 두 가지를 구성한다.


상단의 톱니바퀴 모양을 눌러 Administration 메뉴로 진입한다.


Repository를 생성하기 위해서는 우선, Blob Store를 먼저 생성하고, 해당 Store를 Repository에 연결해야 한다.

그렇기에, Blob Store 탭을 눌러 좌측 상단의 파란색 Create Blob Store 버튼을 클릭한다.


본 포스트에서는 외부 Object Storage에 연결하지 않고, 로컬 서버의 저장소를 이용할 것이기에, Type를 File로 지정한 뒤 해당 Blob Store 이름을 적절히 지정한다.

본 포스트에서는 private-reg-local 이라는 이름을 사용한다.


아래의 Path에서 실제 서버에서의 Blob Store의 저장 위치를 지정할 수 있다.

기본 저장 위치는 /opt/sonatype-work/nexus3/blobs/<Blob Store Name> 이다.

본 포스트에서는 해당 경로를 그대로 사용한다.


추가적으로, 하단의 Soft Quota를 enable 하면 용량 제약을 설정할 수 있다.

필요에 따라 활성화 하여 사용한다.

본 포스트에서는 해당 제약을 사용하지 않는다.


동일한 방법으로 Docker Hub Proxy용 Blob Store도 생성한다.

본 포스트에서는 public-reg-proxy로 이름을 설정한다.



Blob Store 설정이 완료된다면, Repository를 생성한다.

Local 환경의 Repository와 Docker Hub와 연결되는 Repository를 생성한다.


우선 Local 환경의 Repository를 생성한다.

Administration -> Repository -> Repositories 항목을 클릭하여 좌측 상단의 Create Repository 버튼을 클릭한다.

해당 버튼을 클릭하면 Nexus에서 제공하는 Repository Recipe 리스트가 나온다.

Recipe는 해당 저장소에 대한 Tempelate라고 생각하면 된다.

Nexus는 apt 저장소부터 Maven, yum, docker 등 다양한 저장소에 대한 Recipe를 제공한다.


Local Repository를 만들기 위해 docker (hosted) Recipe를 선택한다.

본 포스트에서는 Local Repository의 이름을 private-reg-local로 설정한다.

그리고, 해당 registry를 특정 Port에 바인드 하기 위해 하단의 HTTP 설정을 체크한 뒤 Port 번호를 입력한다. 본 포스트에서는 5080 Port를 이용한다.

Allow anonymous docker pull 옵션은 docker image를 pull 할 때, 익명 사용자를 허용하겠냐는 옵션인데 본 포스트에서는 이를 비활성화하여 익명사용자를 허용하지 않는다.

필요하다면 해당 옵션을 체크하여 익명사용자를 허용하도록 한다.

또한, Docker API v1을 지원하기 위해 Enable Docker V1 API 옵션을 활성화 한다.

주의할 점
HTTPS 설정은 이후 페이지에서 설명한다. 우선 HTTP로 테스트를 진행한다.

마지막으로 생성할 Repository가 어떤 Blob Store를 사용할지 설정한다.

이전에 생성했던 private-reg-local Blob Store를 사용한다.

설정이 완료되면 하단의 파란색 Create Repository 버튼을 클릭하여 Repository를 생성한다.

기타 설정은 sonatype nexus 공식 홈페이지의 Document를 참조하여 설정하도록 한다.

본 포스트에서는 별도로 언급되지 않은 옵션은 기본 설정을 따라간다.



추가적으로, Docker Hub와의 연결을 위한 docker (proxy) Recipe를 사용하는 repository를 생성한다.

설정은 아래 그림과 같이 따라간다.





Repository가 전부 생성되었다면, Docker Login 관련 설정을 위해 Administration -> Security -> Realms 설정에서 Docker Bearer Token Realm 을 활성화 시켜준다.

해당 옵션을 활성화 한다면 다음과 같이 Active 영역에 Docker Bearer Token Realm 옵션이 존재한다.

해당 설정을 수행한 뒤, 우측 하단의 파란색 Save 버튼을 클릭하여 Docker Login 설정을 적용한다.



모든 설정이 완료되었다면, Docker로 로그인을 진행 후 테스트를 수행한다.

현재 설정한 private registry는 기본적으로 인증되지 않은 저장소이고, HTTPS 통신을 사용하는것도 아니기에, docker daemon에서 해당 서버로의 접근을 차단한다.

그렇기에 docker daemon 설정을 변경하여 해당 서버로의 통신을 허가해야 한다.

/etc/docker/daemon.json 파일에 insecure-registries 옵션을 추가하여 다음과 같이 수정한다.

이후, 다음과 같은 명령을 수행하여 docker 서비스를 재시작 하고, 로그인을 수행한다.

로그인은 nexus 계정인 admin을 이용한다.

systemctl daemon-reload
systemctl restart docker.service
docker login http://nexus.kakao.com:5080


docker login이 성공했다면 image pull & push를 테스트 한다.





Nexus Repository HTTPS 적용

위 실습에서, docker daemon 설정의 insecure-registries 옵션을 수정하여 docker 엔진이 안전하지 않은 private registry에 접근할 수 있도록 허가했다.

하지만 이 방식은 암호화 되지 않은 HTTP 통신을 사용하도록 허가하는 것이며, 이는 보안적으로 매우 취약한 행위이다.

그렇기에 개인 환경이라면 모르겠지만, 실 서비스를 준비하거나 그에 준하는 환경에서 private repository를 사용할 시, HTTPS 통신을 사용하도록 설정하여 보다 안전한 통신을 수행해야 할 것이다.


이를 위해서는 별도의 인증서를 발급 받아야 한다.

다만, Comodo, Symantec, DigiCert 등의 공식적인 SSL 인증서 발급 기관에서 발급받은 인증서라면 상관 없지만, openssl 등으로 직접 발급한 인증받지 않은 인증서를 사용한다면, 해당 인증서를 시스템에 신뢰할 수 있는 인증서로 등록하는 별도의 과정을 거쳐야 한다.


본 포스트에서는 openssl을 이용한 인증서 발급, 그리고 해당 인증서를 nexus repository에 적용한 뒤 HTTPS를 활성화하고, docker image를 사용하는 시스템에 해당 인증서를 신뢰할 수 있는 인증서로 등록하여 별도의 insecure-registries 설정 없이 private repository에 접근할 수 있도록 일련의 과정을 서술한다.


우선, 인증서 관련하여 알고 가야 할 사안이 있다.

앞서 말했듯이 nexus repository는 Java의 jetty WAS 기반으로 구동되는 소프트웨어이다.

Java에서 인증서를 활용하기 위해서는 KeyStore라고 하는 파일에 인증서와 개인키를 보관해야 한다.

즉, nexus repository에 HTTPS를 적용하기 위해서는 적절히 발급된 인증서와 개인키가 필요하고, 이를 이용하여 KeyStore를 생성하여 nexus 실행 옵션에 넣어줘야 한다는 것이다.


또 하나 알아야 하는 사안이 있다.

아마 본 포스트을 읽는 독자들 중 docker뿐 아니라 kubernetes 환경에서도 private repository를 이용할 예정인 사람도 있을 것이다.

kubernetes는 인증서를 사용할 때 해당 인증서의 정보를 확인하는데, Wild Card를 사용하는 인증서는 허가하지 않는다.

예를들어 인증서가 *.kakao.com 이라는 CN을 가진다면 해당 인증서는 kubernetes가 거부한다.

그렇기 때문에 이를 대신하여 SAN (Subject Alternative Name) 인증서를 사용해야 한다.

이는 멀티 도메인 인증서 라고도 불리며, Wild Card 인증서와는 다르게 대표 도메인 하나에 여러개의 다른 도메인을 추가할 수 있다.

즉, *.kakao.com 으로 발급된 인증서는 www.kakao.com, api.kakao.com, narak.kakao.com 등 Wild Card 위치에 어떤 값이 오든 사용할 수 있지만, SAN 인증서는 www.kakao.com이라는 인증서에 SAN으로 nexus.kakao.com만 추가되어 있다면 www와 nexus 라는 도메인만 사용할 수 있다는 것이다.

물론 완전 다른 형태인 narak.corodiak.org 같은 도메인도 추가 가능하다. 이러한 SAN 인증서는 Wild Card 문자를 허용하지 않는다.



다른 포스트를 참조하여 openssl을 이용해 인증서 파일을 생성한다.

키 길이는 2048 이상을 추천한다.


생성해야 하는 파일은 crt, key파일과 이를 기반으로 만든 p12 파일이다.

생성된 파일을 이용해 keystore 파일을 만든다.


앞서 nexus가 구동되고 있는 시스템에는 Java가 설치되어 있다.

그렇다면 keytool 이라는 Java KeyStore 기반 인증서를 관리할 수 있는 도구가 같이 설치되었을 것이다.

이걸 이용하여 nexus repository에서 사용할 KeyStore를 생성한다.

아래와 같은 명령을 통해 KeyStore를 생성한다.

keytool -importkeystore -srckeystore <p12파일> -srcstoretype pkcs12 -destkeystore keystore.jks -deststoretype pkcs12

처음 2번의 비밀번호 입력은 KeyStore의 암호를 설정하는 것이고, 3번째는 아까 생성한 p12 파일의 암호를 입력하는 것이다.


KeyStore가 생성되었다면 이 파일을 nexus의 경로에 넣어야 한다.

해당 경로는 /opt/nexus/etc/ssl 이며 다음과 같은 명령을 통해 KeyStore를 복제한다.

cp keystore.jks /opt/nexus/etc/ssl/keystore.jks

KeyStore의 복제가 끝났다면, 이제 nexus의 설정을 변경하여 HTTPS로 동작하도록 설정해야 한다.

우선 nexus의 기본 설정 파일에서 HTTPS PORT를 지정하고, HTTPS 설정을 불러오도록 지정해야 한다.

해당 파일은 /opt/nexus/etc/nexus-default.properties 파일이며, 다음과 같이 내용을 변경한다.

핵심은 application-port를 주석처리 한뒤 application-port-ssl을 설정하고, nexus-args에서 jetty-http.xml 파일 대신 jetty-https.xml을 불러오도록 설정하는 것이다.


nexus-default.properties 파일을 수정 한 뒤 KeyStore의 적용을 위해 /opt/nexus/etc/jetty/jetty-https.xml 파일을 아래와 같이 수정한다.

핵심은 KeyStore 경로와 비밀번호이다.

아마 기본적으로 keystore.jks라는 경로는 지정되어있을 것이고, password만 이전에 인증서 및 KeyStore를 생성할 때 사용했던 값으로 변경하면 될 것이다.



설정을 완료한 뒤, 다음과 같은 명령을 통해 nexus repository 서비스를 재시작 하면 HTTPS가 적용된 nexus 대시보드를 확인할 수 있다.

Chrome 브라우저 기준, ‘연결이 비공개로 설정되어 있지 않습니다.’ 와 같이 보안 경고가 뜬다면 하단의 고급 버튼을 눌러 ‘(안전하지 않음)으로 이동’ 버튼을 눌러 접속한다.



nexus 대시보드 접속 시 인증서가 활성화 된 것을 확인했다면, 앞서 설정한 repository 설정또한 변경해야 한다.

5080번 HTTP Port를 사용하도록 설정했는데 해당 설정을 HTTPS로 변경해야 한다.

nexus 대시보드에 관리자 계정으로 로그인 한 뒤, 상단 톱니바퀴 버튼을 눌러 Administration -> Repository -> Repositories 항목을 클릭한다.

그리고 이전에 생성했던 ‘private-reg-local’ 항목을 클릭한 뒤, HTTP / HTTPS 설정을 아래와 같이 변경한 뒤 저장한다.

본 포스트에서는 5443번 포트를 이용한다.

이렇게 하면 Nexus HTTPS설정은 완료된다.




Docker / Kubernetes Private Registry 사용 설정

우선 private registry 사용 설정을 하기 전에 앞서 이전에 /etc/docker/daemon.json 설정 파일에서 추가했던 insecure-registries 옵션을 제거한 뒤 docker 서비스를 재시작한다.

systemctl restart docker.service

그리고, docker logout을 다음과 같은 명령을 통해 수행하여 기존에 로그인 되어있던 정보를 삭제한다.

docker logout nexus.kakao.com:5080

insecure-registries 설정을 제거하였기에, 해당 private registry로 접근할 때, 인증서 보안 경고로 인해 접속되지 않을 것이다.

이를 해결하기 위해 이전에 발급했던 nexus.crt 파일을 신뢰할 수 있는 인증서로 시스템에 등록하는 과정을 거쳐야 한다.

해당 작업은 docker를 구동하는 시스템 뿐 아니라 kubernetes Control Plane / Worker 에 전부 적용해야 한다.



우선 시스템에 nexus.crt 파일을 복제한다.

이후, 다음과 같은 명령을 수행하여 해당 인증서를 신뢰할 수 있는 인증서로 등록한 뒤 인증서 목록을 업데이트 한 뒤, docker 서비스를 재시작한다.

cp <crt 파일> /usr/local/share/ca-certificates/nexus.crt
update-ca-certificates
systemctl restart docker.service

설정을 수행한 뒤, docker login을 수행한다.

로그인이 성공한다면 정상적으로 인증서가 등록된 것이다.



Kubernetes에서 또한 Private Registry를 사용할 수 있는데, 이를 위해서는 Private Registry 정보를 담은 별도의 Secret을 생성한 뒤, YAML 파일에 적용해야 한다.

다음과 같은 명령을 통해 우선 regcred라는 이름을 가진 docker-registry Secret을 생성한다.

kubectl create secret docker-registry regcred \
   --docker-server=https://nexus.kakao.com:5443 --docker-username=admin \
   --docker-password=admin --docker-email=admin@example.com

그리고 이미지 pull을 테스트 하기 위해 test.yaml이라는 Pod 설정 파일을 작성한다.

해당 파일의 내용은 다음과 같다.

apiVersion: v1
kind: Pod
metadata:
  name: redis-dummy
spec:
  containers:
  - image: nexus.kakao.com:5443/redis-dummy:1.0
    imagePullPolicy: Always
    name: redis-dummy
  imagePullSecrets:
- name: regcred

Pod 설정 파일을 생성한 뒤, 다음과 같은 명령을 통해 Pod를 생성하고 결과를 확인한다.

kubectl apply -f test.yaml
kubectl describe pod redis-dummy | tail

만약 ImagePullBackOff 에러가 발생하면서 x509 관련 문제가 발생한다면 아직 Kubernetes에 인증서가 반영이 되지 않은 것이니, kubelet 서비스와 containerd 서비스를 재시작 한다.

systemctl restart kubelet.service
systemctl restart containerd.service




번외 - Kubernetes에서의 insecure-registry 등록

Kubernetes는 1.24 버전을 기준으로 Docker에 대한 지원을 중단하였다.

이에 따라 기존에 Docker 런타임에 의존하던 컨테이너 생성 방식을 버리고 containerd 및 CRI-O 기반으로 전환하였다.

즉, 1.24 버전부터 컨테이너 이미지를 pull 하는 주체가 containerd이기 때문에 Docker의 설정을 변경하는 것으로는 insecure-registry를 추가할 수 없다.


1.24 버전에서 해당 설정을 적용하기 위해서는 containerd의 설정을 변경해야 한다. containerd의 파일의 경로는 /etc/containerd/config.toml 파일이다.


해당 설정 파일을 아예 제거하고 사용하는 경우도 있지만, insecure-registry 설정을 추가하기 위해서는 이를 다시 활성화 해야 한다.

다음과 같은 내용을 설정 파일에 추가한다.

[plugins.cri.registry]
[plugins.cri.registry.mirrors]
[plugins.cri.registry.mirrors."nexus.kakao.com:5443"]
  endpoint = ["https://nexus.kakao.com:5443"]
[plugins.cri.registry.configs."nexus.kakao.com:5443".tls]
insecure_skip_verify = true

커스텀 mirror를 추가하고, 해당 mirror의 인증을 제외 하도록 설정하면 시스템에 신뢰할 수 있는 인증서 추가 과정을 거치지 않더라도 private registry에 접근할 수 있다.

주의할 점
Docker를 설치하는 과정에서 같이 containerd를 설치한 뒤, 이를 Kubernetes가 사용하게 하는 경우가 있다.
이때, /etc/containerd/config.toml설정파일에서 disabled_plugins = ["cri"] 라는 옵션이 있는 경우가 있는데, 만약 이 옵션이 활성화 되어 있다면 주석 처리를 해주어야 한다.




profile
파아란곰탱이

2개의 댓글

comment-user-thumbnail
2024년 2월 12일

정말 너무 많은 도움이 되었습니다 감사합니다 ...

1개의 답글