Harbor를 이용해 Docker Private Registry를 구축해 봅니다.

 

 

 

1. Harbor

 

Harbor는 비교적 덜 알려진 도커 레지스트리들 중 하나입니다. 공식 문서에 따르면 Harbor는 다음과 같이 설명하고 있습니다.

 

Harbor는 컨텐츠를 저장, 서명 및 스캔하는 오픈 소스 신뢰할 수 있는 클라우드 기본 레지스트리 프로젝트입니다. Harbor는 보안, ID 및 관리와 같은 사용자가 일반적으로 필요로 하는 기능을 추가하여 오픈 소스 Docker Distribution을 확장합니다. 

빌드 및 실행 환경에 더 가까운 레지스트리를 사용하면 이미지 전송 효율성을 향상시킬 수 있습니다. Harbor는 레지스트리 간 이미지 복제를 지원하고 사용자 관리, 액세스 제어 및 활동 감사와 같은 고급 보안 기능을 제공합니다.

Harbor는 CNCF (Cloud Native Computing Foundation)에서 호스팅 합니다. 클라우드 네이티브 기술의 진화를 구체화하려는 조직인 경우 CNCF 가입을 고려하십시오. 참여자 및 Harbor의 역할에 대한 자세한 내용은 CNCF 발표를 읽으십시오.

 

 

또한 Harbor에서 제공하는 주요한 기능은 다음과 같습니다:

 

  • 클라우드 네이티브 레지스트리: 컨테이너 이미지와 Helm 차트를 모두 지원하는 Harbor는 컨테이너 런타임 및 오케스트레이션 플랫폼과 같은 클라우드 네이티브 환경의 레지스트리 역할을 합니다.
  • 역할 기반 액세스 제어: 사용자 및 리포지토리는 '프로젝트'를 통해 구성되며 사용자는 프로젝트에서 이미지 또는 Helm 차트에 대해 서로 다른 권한을 가질 수 있습니다.
  • 정책 기반 복제: 여러 필터(리포지토리, 태그 및 레이블)가 있는 정책을 기반으로 여러 레지스트리 인스턴스 간에 이미지 및 차트를 복제(동기화) 할 수 있습니다. Harbor는 오류가 발생하면 자동으로 복제를 재 시도합니다. 로드 밸런싱, 고 가용성, 다중 데이터 센터, 하이브리드 및 다중 클라우드 시나리오에 적합합니다.
  • 취약점 검색: Harbor는 이미지를 정기적으로 스캔하여 사용자에게 취약점을 경고합니다.
  • LDAP/AD 지원: Harbor는 사용자 인증 및 관리를 위해 기존 엔터프라이즈 LDAP / AD와 통합되며 LDAP 그룹을 Harbor로 가져오고 적절한 프로젝트 역할을 할당할 수 있습니다.
  • OIDC 지원 : Harbor는 OIDC(OpenID Connect)를 활용하여 외부 인증 서버 또는 자격 증명 공급자가 인증 한 사용자의 자격 증명을 확인합니다. Single sign-on을 사용하여 Harbor 포털에 로그인할 수 있습니다.
  • 이미지 삭제 및 가비지 수집: 이미지를 삭제하고 공간을 재활용할 수 있습니다.
  • 공증(Notary): 이미지 진위를 보장할 수 있습니다.
  • 그래픽 사용자 포털: 사용자는 쉽게 저장소를 탐색하고 검색하며 프로젝트를 관리할 수 ​​있습니다.
  • 감사: 리포지토리에 대한 모든 작업이 추적됩니다.
  • RESTful API: 대부분의 관리 작업을 위한 RESTful API로 외부 시스템과 쉽게 통합할 수 있습니다. API를 탐색하고 테스트하기 위해 임베디드 Swagger UI를 사용할 수 있습니다.
  • 쉬운 배포: 온라인 및 오프라인 설치 관리자를 모두 제공합니다. 또한 Helm Chart를 사용하여 Kubernetes에 Harbor를 배포할 수 있습니다.

만약 Harbor대신 다른 레지스트리들의 옵션을 알고 싶다면이 글을 참고해 주시기 바랍니다.

 

 

 

2. Harbor 설치 준비 - 요구 사양

 

Harbor는 리눅스에서 동작합니다. 이 글에서는 리눅스 서버 18.04.3 버전을 사용합니다.

 

하드웨어 요구사항은 다음과 같습니다.

리소스 최소 사양 권장 사양
CPU 2 CPU 4CPU
메모리 4 GB 8 GB
스토리지 40 GB 160 GB

 

소프트웨어 요구사항은 다음과 같습니다.

소프트웨어 버전
Docker Engine 17.06.0 CE 버전 이상
Docker Compose 1.18.0 버전 이상
OpenSSL 최신 버전 권장

 

도커 설치는 이 글을, 도커 컴포즈 설치는 이 글을 참고해주세요.

 

 

네트워크 포트 사용은 다음과 같습니다.

 

포트 프로토콜 설명      
443 HTTPS Harbor 포털 및 코어 API는이 포트에서 HTTPS 요청을 수락합니다. 구성 파일에서이 포트를 변경할 수 있습니다.
4443 HTTPS 하버 용 Docker Content Trust 서비스에 대한 연결입니다. 공증(Notary)이 활성화 된 경우에만 필요합니다. 구성 파일에서이 포트를 변경할 수 있습니다.
80 HTTP Harbor 포털 및 코어 API는이 포트에서 HTTP 요청을 수락합니다. 구성 파일에서이 포트를 변경할 수 있습니다.

 

 

 

3. Harbor 설치

 

우선 Harbor 릴리즈 페이지로 이동해 Harbor 파일을 다운로드합니다. 이 글에선 현재(20.02.14) 최신 버전인 1.10.1 버전의 오프라인 버전을 다운로드하여서 설치해 보도록 하겠습니다.

 

.asc 파일은 옵션입니다. 이 파일은 OpenPGP 키 파일로 다운로드한 번들이 정품인지 확인하는 데 사용됩니다. 다운로드한 파일을 검증하는 방법은 여기서 확인해 주시기 바랍니다.

 

다운로드한 파일이 있는 경로로 이동해서 다음 명령어를 통해 파일을 추출합니다.

 

$ tar xvf harbor-offline-installer-v1.10.1.tgz

 

./harbor/harbor.yml파일을 수정하여 설정을 변경할 수 있습니다. 설치를 위해 호스트 이름을 변경합시다.

 

$ vi harbor.yml

호스트 설정은 파일 가장 위에 있습니다.

localhost나 127.0.0.1을 사용하지 말라고 되어있네요. 적당한 호스트를 입력한 후 저장합니다. 

이 설정 파일엔 초기 접속 정보 및 DB정보도 기재되어있습니다. 우선 관리자 암호를 기억해 둡시다.

실제 사용을 원한다면 당연히 HTTPS를 사용해야 합니다만 우선 간단한 설치를 위해 HTTPS 사용을 비활성화합니다.

 

설정을 저장한 후 다음 명령어로 install.sh을 실행시킵니다.

 

$ sudo ./install.sh

 

위와 같이 설치가 정상적으로 끝나면 앞서 입력한 호스트네임으로 접속해 봅시다. 다음과 같이 Harbor admin페이지를 확인할 수 있습니다.

기본 계정과 암호로 로그인합니다. 로그인 정보는 admin/Harbor12345입니다. 만약 설정 파일에서 초기 비밀번호를 바꾸셨다면 그 암호를 입력해 로그인합니다.

 

정상적으로 로그인되는 것을 확인할 수 있습니다.

 

 

 

 

 

반응형

이 글은 "Container Registries You Might Have Missed"을 번역한 글 입니다.

 

 

 

 

1. Intro

 

레지스트리는 주로 Docker와 같은 컨테이너 작업을 수행하는데에 있어서 주요 구성 요소 중 하나이므로 대중들에게 매력적입니다. 

 

레지스트리는 컨테이너 엔진의 호스트에서 다운로드되고 실행되는 이미지를 호스팅합니다. 컨테이너는 단순히 특정 이미지가 실행중인 인스턴스입니다. 이미지는 Microsoft Windows의 MSI 또는 Red Hat Enterprise Linux의 RPM과 같이 바로 사용할 수있는 패키지로 생각하면 됩니다.

 

여기서는 레지스트리가 어떻게 작동하는지 자세히 설명하지 않겠지만 더 자세히 알고 싶다면 이 기사를 읽어 보세요. 그 대신 제가 이 글에서 하고 싶은 것은 여전히 레이더 아래에 남아있는 몇몇의 컨테이너 레지스트리를 강조하는것 입니다. 

 

널리 알려진 이름의 레지스트리는 Docker를 사용하는 대부분의 사람들에게 이미 익숙하지만 이미지를 호스팅 할 위치를 결정할 때 고려해야 할 가치가 있는 작은 레지스트리도 있습니다. 

 

잘 알려지지 않은 컨테이너 레지스트리에 대한 설명은 계속 읽으시면 됩니다.

 

 

 

2. 잘 알려진 레지스트리들

 

먼저 잘 알려진 레지스트리를 식별해 잘 알려지지 않은 레지스트리와 비교할 내용을 명확히 하겠습니다.

 

현재 모든 계정에서 가장 인기있는 레지스트리는 Docker Hub입니다. Docker Hub는 알려진 레지스트리 세계의 중심입니다. 이건 모든 Docker 설치가 참조하도록 구성된 기본 호스팅 된 레지스트리입니다. 

 

그 외의 다른 임기 있는 레지스트리는 다음과 같습니다:

 

 

 

3. 당신이 놓치고 있을지도 모르는 레지스트리들

 

이제 흥미로운 부분으로 넘어 갑니다. 덜 알려진 레지스트리의 개요는 다음과 같습니다.

  • Amazon EC2 Container Registry (ECR)
  • FlawCheck Private Registry
  • GitLab Container Registry
  • Portus by SUSE
  • Sonatype Nexus
  • VMware Harbor Registry

 

3.1. 아마존 EC2 컨테이너 레지스트리(ECR)

 


아마존이 Amazon EC2 Container Service (ECS)라는 호스트 된 컨테이너 서비스를 제공한다는 것을 이미 알고 있을겁니다. 그러나 아마존이 ECS를 완성하기 위해 제공하는 레지스트리는 덜 관심을받는 경향이 있습니다. 

 

Amazon EC2 Container Registry (ECR)라고 불리는 레지스트리이며 호스트 된 Docker 컨테이너 레지스트리입니다. ECR은 ECS와 통합됩니다. 

 

2015 년 12 월에 도입 된이 솔루션은 잘 알려진 대부분의 레지스트리보다 새로운 레지스트리 옵션으로인해  일부 사용자에게 익숙하지 않다고 설명합니다. 

 

ECS는 ECR과 호환되는 유일한 컨테이너 레지스트리가 아닙니다. ECS는 외부 레지스트리도 지원합니다. 그러나 ECR의 주요 이점은 완전히 호스트 되고 관리되는 레지스트리이므로 배포 및 관리가 간단하다는점 입니다. 또한 ECR은 다른 ECS 인프라만큼 확장 가능하므로 확장성이 매우 뛰어납니다. 

 

최고의 사용 사례는 다음과 같습니다: AWS 서비스를 많이 사용하거나 사용할 계획을 갖고 있으며 개인 이미지를 호스팅 할 장소를 찾고 있다면 ECR을 사용하는 것이 좋습니다. 대규모 레지스트리 배포가 있거나 시간이 지남에 따라 레지스트리가 크게 확장 될 것으로 예상되는 경우에도 좋은 선택입니다. 이 경우 사실상 제한없이 ECR을 확장할 수 있습니다.

 

 

 

3.2. FlawCheck Private Registry

 

 

FlawCheck Private Registry (보안 업체 Tenable이 FlawCheck의 나머지 비즈니스와 함께 최근에 인수 함)는 보안을 중점으로 한 레지스트리 옵션입니다.

 

이 레지스트리는 컨테이너 이미지에 대한 통합 취약성 검색 및 말웨어 탐지 기능을 제공합니다. 컨테이너 이미지에 악성 코드가 없어지게 하거나 레지스트리에 악성 이미지가 삽입되는 것을 막는 만능 도구는은 아니지만 FlawCheck의 스캔 기능은 위험을 줄이는데 도움이 될 수 있습니다.

 

최상의 사용 사례는 다음과 같습니다: 보안에 민감한 회사의 경우이 방법이 정말 좋습니다. 규제가 심한 산업에서는 이 레지스트리를 많이 채택 할 것으로 예상됩니다.

 

 

 

3.3. GitLab Container Registry

 


호스트 또는 온-프레미스 레지스트리로 실행할 수있는 GitLab Container Registry는 컨테이너 이미지 호스팅을위한 GitLab의 솔루션입니다. GitLab에 내장되어 있으며 나머지 GitLab의 도구들과 완벽하게 호환되므로 GitLab 딜리버르 파이프 라인에 직접 통합 될수도 있습니다. 

 

팀이 가능한 한 적은 움직이는 부품으로 완벽한 DevOps 워크 플로우를 탐색하고 있다면 이를 채택하는것이 좋습니다. 

 

최상의 사용 사례는 다음과 같습니다: 일부 개발자는 Docker 이미지를 소스 코드와 동일한 플랫폼에 저장하는 것이 편리하다는 것을 알게 될 것입니다. 소스 코드에 GitLab을 사용한다면 GitLab Container Registry가 유용 합니다. 그러나 GitLab Container Registry는 대부분의 다른 레지스트리에서 사용할 수없는 킬러 기능을 제공하지 않습니다.

 

 

 

3.4. SUSE의 Portus

 


Portus는 기술적으로 레지스트리는 아니지만 Docker Registry의 온-프레미스 배포를 위해 기본 UI를 대체하는 프런트 엔드를 제공합니다. 

 

Portus는 추가 액세스 제어 옵션을 제공하여 Docker Registry에 가치를 더하도록 설계되었습니다. 여기에는 팀 또는 레지스트리 사용자를 서로 다른 액세스 수준을 설정하여 구성하는 기능이 포함됩니다.(여러 가지면에서이 기능은 Unix 계열 시스템의 사용자 그룹과 유사합니다.) 

 

또한 Portus는 레지스트리 네임스페이스를 제공해 팀의 사용자 뿐 아니라 개인 사용자에게 다른 저장소에 대해 세부적으로 수행할 수 있는 수정 유형 구성을 만드는것을 가능하게 합니다. 

 

그리고 Portus는 레지스트리 설정 및 액세스 제어를 구성 할 수있는 사용자 친화적 인 웹 인터페이스를 제공합니다. CLI 구성 도구 인 portusctl도 사용 가능합니다. 

 

최상의 사용 사례는 다음과 같습니다: Docker Registry를 좋아하지만 추가 보안 제어가 필요하거나 세분화 된 액세스 제어를 사용해야하는 이유가있는 경우 Portus는 강력한 솔루션입니다.

 

 

 

3.5. Sonatype Nexus

 


호스트 및 온-프레미스 배포를 지원하는 Sonatype Nexus는 범용 리포지토리입니다. 이는 Docker 이미지 호스팅보다 훨씬 많은 것을 지원하지만 Docker 레지스트리로도 사용될 수 있습니다. 

 

Docker보다 훨씬 오래 되었으며 이전에 컨테이너 레지스트리를 사용하지 않았더라도 노련한 관리자에게는 익숙 할 것입니다. 

 

핵심 Nexus 플랫폼은 오픈 소스이지만 상용 옵션 또한 제공됩니다. 

 

최상의 사용 사례는 다음과 같습니다: 많은 회사에서 Nexus를 Maven의 저장소로 몇 년 동안 배포해 왔습니다. 플랫폼의 최신 릴리스로 업그레이드하기 만하면 조직에서 Docker 이미지 호스팅을 추가하여 새로운 제품에 대한 개발 또는 운영 직원을 교육하지 않고도 자체 Docker 레지스트리를 만들 수 있습니다. 또한 Docker 이미지와 함께 다른 유형의 제작물를 호스팅 할 수 있습니다.

 

 

 

3.6. VMware Harbor Registry

 

 

Docker 생태계에서 VMware를 주요 플레이어로 생각하지는 않지만 이 회사는 확실히 새로운 시도를 하고 있습니다. Harbor Registry는 Docker 이미지 호스팅에 대한 VMware의 결과입니다. 

 

이 레지스트리는 Docker Distribution을 기반으로 구축되었지만 보안 및 ID 관리 기능을 추가하였습니다. 또한 단일 호스트에서 여러 레지스트리를 지원합니다. 

 

최상의 사용 사례는 다음과 같습니다: Harbour는 보안 및 사용자 관리에 중점을두고 있기 때문에 이 옵션은 기업이 추구하는 중요한 레지스트리 기능을 제공하며 이 기능은 다른 모든 레지스트리에서 사용할 수 있는것은 아닙니다. 이는 기업에서 좋은 선택입니다. 

 

Harbor는 Docker 컨테이너로 실행되기 때문에 Docker 환경이있는 모든 서버에 쉽게 설치할 수 있으며 개발자에게 오프라인 설치 프로그램을 제공하기 때문에 보안 고려 사항 또는 기타 요인이있는 상황(공용 인터넷에 연결할 수 없는 경우을 의미합니다.)에서 유용 할 수 있습니다.

 

 

 

4. Conclusion

 

서로 다른 레지스트리 오퍼링 사이의 주요 변수에는 지원하는 배포 환경 유형 (호스트, 온 프레미스 또는 둘 다)이 포함됩니다. 액세스 제어 옵션을 얼마나 미세하게 조정했는지; 컨테이너 레지스트리에 대한 추가 보안 수준. 

 

물론 필요에 맞는 레지스트리를 선택하는 것은 이러한 기능이 우선 순위에 어떻게 부합하는지에 달려 있습니다. 그러나 선택의 폭이 넓기 때문에 특정 조직의 요구에 완벽한 균형을 제공하는 레지스트리를 찾는 것은 어렵지 않을겁니다.

 

 

 

 

 

 

 

 

반응형

 

파이썬을 이용해 RabbitMQ에 메시지를 생성하고 소비하는 방법을 알아봅니다.

 

 

 

1. 사전준비

 

RabbitMQ를 미리 준비해 둡니다. 로컬에 설치하든 따로 서버를 준비해 설치하든 상관없습니다.

 

설치 가이드는 다음 글을 참고해 주세요.

 

 

 

2. RabbitMQ 설정

 

초기 RabbitMQ를 설치하고 나면 vhost와 사용할 queue를 생성해야 합니다.

 

먼저 vhost를 생성합니다. 로그인 후 Admin > Virtual Hosts로 이동합니다.

 

 

Add new virtual host에서 적당한 이름을 입력 한 후 vhost를 생성합니다.

 

 

위와 같이 새로운 vhost가 생성된 것을 확인할 수 있습니다. 유저를 보니 아직 vhost에 유저가 하나도 없네요. 생성된 vhost를 클릭해 유저를 할당해 봅시다.

 

 

적당한 유저를 고르고 Set permission을 눌러 권한을 부여합니다. 

 

이제 queue 탭으로 이동해 새로운 큐를 생성합니다.

 

 

적당한 이름의 queue를 생성해 줍니다. 이 예제에서는 default exchange를 사용할 예정이므로 별도의 exchange는 생성하지 않습니다.

 

 

 

3. PIKA

 

파이썬으로 AMQP를 이용하기 위해 PIKA를 사용합니다. 

 

Pika는 AMQP 0-9-1 프로토콜의 순수한 Python 구현체로써 기본 네트워크 지원 라이브러리와 독립적으로 유지됩니다.

 

자세한 내용은 여기서 확인하시기 바랍니다.

 

 

 

4. Publisher 구현

 

virtualenv를 통해 가상 환경을 생성 한 후 pika를 설치합니다.

 

> pip install pika

 

그리고 publisher.py 파일을 생성해 다음과 같이 코딩합니다.

 

import pika

class Publisher:
    def __init__(self):
        self.__url = 'host or ip here'
        self.__port = 5672
        self.__vhost = 'mq_test'
        self.__cred = pika.PlainCredentials('id here', 'password here')
        self.__queue = 't_msg_q';
        return

    def main(self):
        conn = pika.BlockingConnection(pika.ConnectionParameters(self.__url, self.__port, self.__vhost, self.__cred))
        chan = conn.channel()
        chan.basic_publish(
            exchange = '',
            routing_key = self.__queue,
            body = 'Hello RabbitMQ'
        )
        conn.close()
        return

publisher = Publisher()
publisher.main()

 

init에서 필요한 변수를 미리 지정해 둡니다. '만약 로컬에 기본값으로 설치했다면 url에 localhost, port에 5672를 사용하면 됩니다. vhost와 queue는 앞에서 생성 한 대로 입력했습니다. cred는 vhost 액세스 권한이 부여된 계정을 입력하면 됩니다.

 

코드 내용은 단순합니다. BlockingConnection을 이용해 커넥션을 생성합니다. chan에 생성된 연결에서 채널을 가져 온 후 basic_publish를 이용해 메시지를 발행합니다. 

 

exchange에 빈값을 넣어 default exchange를 사용하도록 하였으며 routing_key에 타겟 큐 이름을 넣었습니다. body엔 메시지 내용을 입력하면 됩니다.

 

이렇게 한번 실행하면 t_msg_q에 Hello RabbitMQ라는 메시지가 한번 생성되고 종료하는 publisher를 만들었습니다.

 

실제로 MQ 관리자 페이지에서 queue를 눌러보면 메시지가 들어온 것을 확인할 수 있습니다.

 

 

더 아래의 Get messages에서 실제 메시지를 조회 해 볼 수도 있습니다.

 

 

 

 

 

5. Consumer 구현

 

이제 메시지를 발행했으니 소비자를 구현해 보도록 하겠습니다.

 

별도의 폴더에서 virtualenv를 통해 가상 환경을 생성한 후 pika를 설치합니다.

 

> pip install pika

 

그 후 consumer.py 파일을 만들어 다음과 같이 코딩합니다.

 

import pika

class Consumer:
    def __init__(self):
        self.__url = 'host or ip here'
        self.__port = 5672
        self.__vhost = 'mq_test'
        self.__cred = pika.PlainCredentials('id here', 'password here')
        self.__queue = 't_msg_q';
        return

    def on_message(channel, method_frame, header_frame, body):
        print('Received %s' % body)
        return

    def main(self):
        conn = pika.BlockingConnection(pika.ConnectionParameters(self.__url, self.__port, self.__vhost, self.__cred))
        chan = conn.channel()
        chan.basic_consume(
            queue = self.__queue, 
            on_message_callback = Consumer.on_message,
            auto_ack = True
        )
        print('Consumer is starting...')
        chan.start_consuming()
        return

consumer = Consumer()
consumer.main()

 

publisher와 다른점만 설명하겠습니다. 

 

on_message는 메시지를 수신했을 때 실행 될 콜백 함수입니다. 이 예제에서는 단순히 받은 메시지를 출력하는 역할을 하고있습니다.

 

채널까지 생성한 후 basic_consume를 통해 메시지를 소비하는 방법을 정의합니다. queue엔 앞서 생성한 큐의 이름을 넣어주면 되고 on_message_callback에는 앞에서 정의한 콜백 함수를 넣어줍니다. auto_ack는 메시지를 소비했을 때 자동으로 ack을 날릴지 여부입니다. ack을 mq에 전송하면 소비된 메시지는 제거됩니다.

 

그 후 start_consuming()을 실행하면 지속적으로 큐에 있는 메시지를 소비하는 상태가 됩니다. 중단하고 싶으면 Ctrl + C를 눌러 중단하면 됩니다.

 

이제 파이썬을 실행시키면 다음과 같이 메시지가 출력됩니다.

 

 

아까 발행한 메시지를 정상적으로 소비하였습니다. 관리자 페이지로 이동해 보면 auto_ack 옵션에 따라 전송된 ack에 의해 메시지가 삭제된 것을 확인할 수 있습니다.

 

 

 

 

 

반응형

 

Python의 BeautifulSoup를 사용해 만든 크롤러를 개선해봅니다.

 

 

1. 사전 준비

 

본 연습 프로젝트는 레드 마인의 이슈를 크롤링 해오는 것을 목표로 합니다.

크롤링을 목표로 하기 때문에 레드 마인의 API는 사용하지 않습니다.

 

이전 코드는 다음 글을 확인해 주세요.

 

 

 

2. 시그널 처리기 추가하기.

 

현재의 소스는 한번 크롤링해오고 종료되는 방식입니다. 이제 이 크롤러가 주기적으로 동작하도록 수정해 봅시다.

 

소스코드의 내용을 다음과 같이 수정해 주세요.

 

import requests
import datetime
import time
from bs4 import BeautifulSoup

class Crawler:
    def __init__(self):
        self.__main_url = 'https://src.smoh.kr/'
        self.__login_url = 'https://src.smoh.kr/login'
        self.__search_url = 'https://src.smoh.kr/projects/crawlerissues/issues?set_filter=1'
        self.__login_data = {
            'username': 'jd_user',
            'password': 'P@ssword'
        }
        self.__issues = {}
        self.__stop = False;
        return

    def main(self):
        while not self.__stop:
            with requests.Session() as s:
                main_req = s.get(self.__main_url)
                html = main_req.text
                obj = BeautifulSoup(html, 'html.parser')
                auth_token = obj.find('input', {'name': 'authenticity_token'})['value']
                self.__login_data = {**self.__login_data, **{'authenticity_token': auth_token}}

                login_req = s.post(self.__login_url, self.__login_data)
                if login_req.status_code == 200:
                    search_req = s.get(self.__search_url)
                    search_obj = BeautifulSoup(search_req.text, 'html.parser')
                    thead = search_obj.select('div.autoscroll > table > thead > tr > th')
                    tbody = search_obj.select('div.autoscroll > table > tbody > tr > td')
                    issue_obj = {}
                    issue_list = []
                    for i in range(1, len(tbody) +1):
                        if(i % len(thead) != 0):
                            issue_obj[thead[i % len(thead)].text] = tbody[i].text
                        else:
                            issue_list.append(issue_obj)
                            issue_obj = {}
                    self.__issues = {
                        'execution_dttm': str(datetime.datetime.now().isoformat()),
                        'issues': issue_list
                    }
                else:
                    print('Failed to login(Code: %d)' %(req.status_code))
                print(self.__issues)
                time.sleep(5)
        return

crawler = Crawler()
crawler.main()

 

별로 변경된 내용은 없습니다. init에 __stop을 추가해주고 해당 값이 False이면 크롤링 작업을 5초마다 반복합니다.

 

중지하고 싶을 땐 Ctrl + C를 눌러서 중지합니다.

 

 

종료 방법도 Ctrl + C 밖에 없을뿐더러 종료 시 위와 같은 이상한 에러가 발생합니다. 

 

시그널을 처리할 수 있도록 수정하여 깔끔하게 종료될 수 있도록 수정해 봅니다.

 

import requests
import datetime
import time
import signal
from bs4 import BeautifulSoup

class Crawler:
    def __init__(self):
        self.__main_url = 'https://src.smoh.kr/'
        self.__login_url = 'https://src.smoh.kr/login'
        self.__search_url = 'https://src.smoh.kr/projects/crawlerissues/issues?set_filter=1'
        self.__login_data = {
            'username': 'jd_user',
            'password': 'P@ssword'
        }
        self.__issues = {}
        self.__stop = False;

        signal.signal(signal.SIGINT, self.stop)
        signal.signal(signal.SIGTERM, self.stop)
        return

    def stop(self, signum, frame):
        self.__stop = True
        return

    def main(self):
        while not self.__stop:
            with requests.Session() as s:
                main_req = s.get(self.__main_url)
                html = main_req.text
                obj = BeautifulSoup(html, 'html.parser')
                auth_token = obj.find('input', {'name': 'authenticity_token'})['value']
                self.__login_data = {**self.__login_data, **{'authenticity_token': auth_token}}

                login_req = s.post(self.__login_url, self.__login_data)
                if login_req.status_code == 200:
                    search_req = s.get(self.__search_url)
                    search_obj = BeautifulSoup(search_req.text, 'html.parser')
                    thead = search_obj.select('div.autoscroll > table > thead > tr > th')
                    tbody = search_obj.select('div.autoscroll > table > tbody > tr > td')
                    issue_obj = {}
                    issue_list = []
                    for i in range(1, len(tbody) +1):
                        if(i % len(thead) != 0):
                            issue_obj[thead[i % len(thead)].text] = tbody[i].text
                        else:
                            issue_list.append(issue_obj)
                            issue_obj = {}
                    self.__issues = {
                        'execution_dttm': str(datetime.datetime.now().isoformat()),
                        'issues': issue_list
                    }
                else:
                    print('Failed to login(Code: %d)' %(req.status_code))
                print(self.__issues)
                time.sleep(5)
        return

crawler = Crawler()
crawler.main()

 

init에 signal을 추가했으며 __stop를 다루는 stop함수를 추가했습니다. 이제 Ctrl + C로 종료를 해도 에러 없이 종료되는 모습을 확인할 수 있습니다.

 

 

 

3. Logger 추가하기.

 

크롤러의 작업 내용을 기록하는 로거를 만들어 적용해 보도록 하겠습니다.

 

logger.py 파일을 새로 만든 후 다음과 같이 코딩해 주세요.

 

import logging
import time
import json
import datetime
import os

class Logger:
    def __init__(self, name, log_file):
        self.name = name
        self.log_file = log_file
        logging.basicConfig(level = logging.INFO, format = '%(message)s')
        self.logger = logging.getLogger(self.name)
        self.log_hadler = logging.FileHandler(self.log_file)
        self.logger.addHandler(self.log_hadler)
        return
    
    def log(self, event, value):
        log_obj = {
            'component': self.name,
            'dttm': str(datetime.datetime.now().isoformat()),
            'log': {
                'event': event,
                'desc': value
            }
        }
        self.logger.info(json.dumps(log_obj))
        return

 

로거는 컴포넌트 이름과 파일 경로를 받아와서 로그 파일을 남깁니다. 남겨지는 로그의 구조는 log_obj에 정의되어 있습니다.

 

현재는 info레벨의 로그만 남기도록 되어 있으므로 필요에 따라 수정해서 남기면 됩니다.

 

이제 로거를 사용해 보도록 합니다. crawler.py 파일을 다음과 같이 수정합니다.

 

import requests
import datetime
import time
import signal
import os
from bs4 import BeautifulSoup
from logger import Logger

class Crawler:
    def __init__(self):
        self.__main_url = 'https://src.smoh.kr/'
        self.__login_url = 'https://src.smoh.kr/login'
        self.__search_url = 'https://src.smoh.kr/projects/crawlerissues/issues?set_filter=1'
        self.__login_data = {
            'username': 'jd_user',
            'password': 'P@ssword'
        }
        self.__issues = {}
        self.__stop = False;
        self.__log_file = 'D:\\crawler.log'

        self.logger = Logger('crawler', self.__log_file)

        signal.signal(signal.SIGINT, self.stop)
        signal.signal(signal.SIGTERM, self.stop)
        return

    def stop(self, signum, frame):
        self.__stop = True
        return

    def main(self):
        self.logger.log('start', { 'pid': os.getpid() })
        while not self.__stop:
            with requests.Session() as s:
                main_req = s.get(self.__main_url)
                html = main_req.text
                obj = BeautifulSoup(html, 'html.parser')
                auth_token = obj.find('input', {'name': 'authenticity_token'})['value']
                self.__login_data = {**self.__login_data, **{'authenticity_token': auth_token}}

                login_req = s.post(self.__login_url, self.__login_data)
                if login_req.status_code == 200:
                    search_req = s.get(self.__search_url)
                    search_obj = BeautifulSoup(search_req.text, 'html.parser')
                    thead = search_obj.select('div.autoscroll > table > thead > tr > th')
                    tbody = search_obj.select('div.autoscroll > table > tbody > tr > td')
                    issue_obj = {}
                    issue_list = []
                    for i in range(1, len(tbody) +1):
                        if(i % len(thead) != 0):
                            issue_obj[thead[i % len(thead)].text] = tbody[i].text
                        else:
                            issue_list.append(issue_obj)
                            issue_obj = {}
                    self.__issues = {
                        'execution_dttm': str(datetime.datetime.now().isoformat()),
                        'issues': issue_list
                    }
                else:
                    self.logger.log('login_fail', { 
                        'pid': os.getpid(), 
                        'error_code':  req.status_code
                    })
                self.logger.log('get_issues', { 
                    'pid': os.getpid(),
                    'count': len(issue_list)
                })
                print(self.__issues)
                time.sleep(5)
        self.logger.log('stop', { 'pid': os.getpid() })
        return

crawler = Crawler()
crawler.main()

 

이제 코드를 실행시키면 로그 파일 경로에 다음과 같이 정상적으로 로그가 남는 것을 확인할 수 있습니다.

 

{"component": "crawler", "dttm": "2020-02-03T10:45:16.780440", "log": {"event": "start", "desc": {"pid": 30568}}}
{"component": "crawler", "dttm": "2020-02-03T10:45:17.729584", "log": {"event": "get_issues", "desc": {"pid": 30568, "count": 3}}}
{"component": "crawler", "dttm": "2020-02-03T10:45:23.683570", "log": {"event": "get_issues", "desc": {"pid": 30568, "count": 3}}}
{"component": "crawler", "dttm": "2020-02-03T10:45:28.705034", "log": {"event": "stop", "desc": {"pid": 30568}}}

 

 

 

 

 

 

반응형

 

 

 

Python의 BeautifulSoup를 사용해 크롤러를 만들어 봅니다.

 

 

 

1. 사전 준비

 

본 연습 프로젝트는 레드마인의 이슈를 크롤링 해오는것을 목표로 합니다.

 

레드마인 준비는 별도로 설명하지 않습니다. 

 

** 언제까지 올라가 있을지는 장담할 수 없으나 본문에 제 레드마인 조회용 계정을 사용하셔도 됩니다.

 

별도의 파이썬 설치방법은 설명하지 않습니다. 다만 원활한 개발을 위해 VirtualEnv만 설명합니다.

 

파이썬을 설치하고 VSCode로 프로젝트 폴더를 엽니다.

 

Ctlr + `를 눌러 터미널 창을 열고 다음 명령어로 VirtualEvn를 설치합니다.

 

> pip install virtualenv

 

 

이제 다음 명령어를 통해 VirtualEnv를 활성화시킵니다.

 

> virtualenv venv

 

 

다음과 같이 프로젝트 폴더에 venv 폴더가 생성된 것을 확인할 수 있습니다.

 

 

다른 폴더의 이름을 원하시면 venv대신 다른 이름을 지정하시면 됩니다.

 

이제 virtualenv를 활성화시킵시다. ".\venv\scripts\activate" 파일을 실행시키면 됩니다.

 

>.\venv\scripts\activate

 

 

이제 터미널 앞에 가상 환경으로 정의한 (venv)가 나타난 것을 확인할 수 있습니다.

 

작업을 종료하고 싶으시면 ".\venv\scripts\deactivate"를 입력하시면 됩니다.

 

 

 

2. BeautifulSoup 이용하기

 

BeautifulSoup는 Python 패키지중 하나로 HTML 태그를 Python 객체로 만들어줍니다.

 

문서는 다음과 같이 설명하고 있습니다.

 

"Beautiful Soup transforms a complex HTML document into a complex tree of Python objects."
"BeautifulSoup는 HTML document안에 있는 수많은 HTML 태그들을 사용하기 편한 Python 객체 형태로 만들어 줍니다."

 

이 외에도 파싱을 돕는 다양한 내장 함수가 있어 편하게 원하는 데이터를 추출해 올 수 있습니다.

 

이제 터미널에서 다음 명령어를 실행해 beautisulsoup를 설치합시다.

 

> pip install beautifulsoup4 

 

 

이 외에도 requests라는 패키지가 필요합니다. 이 패키지는 입력한 URL에 대해 반환받는 HTML을 가져오는 역할을 합니다. 다음 명령어를 실행해 requests를 설치합니다.

 

>pip install requests

 

 

이제 이 패키지들을 사용해 보겠습니다. crawler.py 파일을 만든 뒤 다음과 같이 코딩합니다.

 

import requests
from bs4 import BeautifulSoup

class Crawler:
    def __init__(self):
        self.__url = 'https://src.smoh.kr/projects/crawlerissues/issues'
        return

    def main(self):
        response = requests.get(self.__url)
        if response.status_code == 200:
            obj = BeautifulSoup(response.content, 'html.parser')
            print(obj.html)
        else:
            print('Failed(Code: %d)' %(response.status_code))
        return

crawler = Crawler()
crawler.main()

 

지정한 URL로부터 응답을 받고 코드가 200 성공인 경우 내용을 BeautifulSoup을 이용해 객체로 생성한 뒤 html을 출력하는 코드입니다.

 

HTML결괏값이 제대로 보이시나요?

 

 

 

3. 이슈 크롤링하기

 

이 URL 정보는 CrawlerIssues 프로젝트에 생성된 이슈를 보여주는 URL입니다만 우리가 원하는 이슈 정보를 보려고 하기엔 권한이 부족합니다. HTML 결과를 살펴보면 로그인을 하는 화면임을 확인할 수 있습니다.

 

실제 URL로 들어가서 개발자 도구(F12)를 연 뒤 Elements 탭에서 확인해 보겠습니다.

 

 

이제 우리는 "username"에 유저 ID를, "password"에 암호를 넣고 "login" 액션을 통해 로그인을 진행하는 것을 확인했습니다.

 

저기에 유저 정보를 넣어 로그인을 해봅시다. 크롤러가 로그인 정보를 갖기 위해서 세션을 사용합니다. 다음과 같이 코드를 수정합니다.

 

import requests
from bs4 import BeautifulSoup

class Crawler:
    def __init__(self):
        self.__login_url = 'https://src.smoh.kr/login'
        self.__seach_url = 'https://src.smoh.kr/projects/crawlerissues/issues'
        self.__login_data = {
            'username': 'jd_user',
            'password': 'P@ssword'
        }
        return

    def main(self):
        with requests.Session() as s:
            req = s.post(self.__login_url, self.__login_data)
            if req.status_code == 200:
                html = s.get(self.__seach_url)
                print(html)
            else:
                print('Failed to login(Code: %d)' %(req.status_code))
        return

crawler = Crawler()
crawler.main()

 

** 크롤링할 레드마인이 없으신 분은 저 계정을 사용하시면 됩니다만 사전 공지 없이 변경될 수 있음을 알려드립니다.

 

결과가 어떻게 나오나요? "Failed to login(Code: 422)"와 함께 로그인이 제대로 되지 않을 겁니다. 원인이 무엇인지 postman을 통해 직접 post 요청을 날리고 메시지를 확인해 봅시다.

 

 

"Invalid form authenticity token"라는 에러와 함께 로그인에 실패하네요. 혹시 위의 HTML Elements에서 authenticity token을 보셨나요? 

 

 

해당 값은 보안을 위해 발급된 토큰으로 매 로그인 시마다 다른 값을 요구합니다. 따라서 크롤러에서 이 값을 사용하기 위해 코드를 다음과 같이 수정합시다.

 

import requests
import datetime
from bs4 import BeautifulSoup

class Crawler:
    def __init__(self):
        self.__main_url = 'https://src.smoh.kr/'
        self.__login_url = 'https://src.smoh.kr/login'
        self.__search_url = 'https://src.smoh.kr/projects/crawlerissues/issues?set_filter=1'
        self.__login_data = {
            'username': 'jd_user',
            'password': 'P@ssword'
        }
        self.__issues = {}
        return

    def main(self):
        with requests.Session() as s:
            main_req = s.get(self.__main_url)
            html = main_req.text
            obj = BeautifulSoup(html, 'html.parser')
            auth_token = obj.find('input', {'name': 'authenticity_token'})['value']
            self.__login_data = {**self.__login_data, **{'authenticity_token': auth_token}}

            login_req = s.post(self.__login_url, self.__login_data)
            if login_req.status_code == 200:
                search_req = s.get(self.__search_url)
                search_obj = BeautifulSoup(search_req.text, 'html.parser')
                thead = search_obj.select('div.autoscroll > table > thead > tr > th')
                tbody = search_obj.select('div.autoscroll > table > tbody > tr > td')
                issue_obj = {}
                issue_list = []
                for i in range(1, len(tbody) +1):
                    if(i % len(thead) != 0):
                        issue_obj[thead[i % len(thead)].text] = tbody[i].text
                    else:
                        issue_list.append(issue_obj)
                        issue_obj = {}
                self.__issues = {
                    'execution_dttm': str(datetime.datetime.now().isoformat()),
                    'issues': issue_list
                }
            else:
                print('Failed to login(Code: %d)' %(req.status_code))
            print(self.__issues)
        return

crawler = Crawler()
crawler.main()

 

차근차근 코드를 살펴봅시다.

 

우선 url이 세 개로 늘었습니다. auto_token을 받아오는 메인 페이지, 로그인을 요청하는 페이지, 이슈를 조회하는 페이지가 있습니다. 로그인 시 사용하는 객체는 __login_data에 저장해 두었으며 __issues는 조회한 이슈를 정리하여 담아두기 위한 객체입니다.

 

실제 작업의 워크 플로우를 봅시다.

 

먼저 세션을 생성하고 세션 내에서 auth_token을 구해옵니다. 구해온 토큰은 __login_data에 저장해 두었습니다.

 

이제 로그인시 필요한 정보가 모두 모였으니 로그인을 시도합니다. status_code를 확인해 정상적으로 로그인이 되었다면 조회를 시작합니다. 

 

조회 결과를 search_req에 담아둔 뒤 BeautifulSoup를 이용해 객체로 변환합니다.

 

 

모든 HTML이 아닌 위에 보이는 테이블의 헤더와 row정보만 필요하므로 셀렉터를 사용해서 값을 추출해주었습니다.

 

추출한 값을 루프 문을 통해 각각의 이슈를 객체로 만든 뒤 리스트에 담아두었습니다. 모든 작업이 끝나면 시간과 함께 JSON으로 만들기 위해 __issues에 저장한 후 출력까지 해보았습니다.

 

 

값이 위와 같이 잘 나오시나요? 이로써 가장 간단하고 기초적인 크롤러를 만들어보았습니다. 다음에는 이 크롤러를 좀 더 발전시켜보도록 하겠습니다.

 

 

** 본 포스팅의 소스코드는 여기에서 확인할 수 있습니다.

 

 

 

반응형

 

 

Redmin에 LDAP 인증을 추가해 계정관리를 해봅니다.

 

 

 

1. Redmine 및 OpenLDAP 준비

 

미리 Redmine과 OpenLDAP 서버를 준비합니다.

 

Redmine은 Docker를 이용하면 매우 간편하게 설치할 수 있습니다.

 

LDAP 서버를 설치하는 방법은 다음글을 참고해 주시기 바랍니다.

 

 

 

2. LDAP 설정

 

Redmine에 들어가 설정 > LDAP 인증 > 새 인증 공급자를 눌러줍니다.

 

 

차근차근 채워봅시다.

 

  • 이름: 이 LDAP 인증 공급자의 이름입니다.
  • 호스트: LDAP 인증 서버의 호스트를 적습니다.
  • 포트: LDAP 인증 서버의 LDAP 포트를 적습니다.
  • LDAPS: 보안 프로토콜 사용 여부를 선택합니다.
  • 계정: LDAP 인증 공급자의 계정을 입력합니다.
  • 비밀번호: 위 계정의 암호를 입력합니다.
  • 기본 DN: LDAP의 BaseDN을 입력합니다.
  • LDAP 필터: 비워둡니다.
  • 타임아웃: 타임아웃 시간을 설정합니다
  • 동적 사용자 생성: Redmine에서 LDAP를 이용하기 위해 체크합니다.
  • 로그인 속성: 로그인 id로 사용될 LDAP의 타입입니다.
  • 이름 속성: 이름으로 보일 LDAP의 타입입니다.
  • 성 속성: 성으로 보여질 LDAP의 타입입니다.
  • 메일 속성: 메일로 보여질 LDAP의 타입입니다.

위와 같은 내용으로 다음과 같이 채워줍니다.

 

 

저장 후 테스트를 진행해 봅니다.

 

 

연결 성공 메시지가 보이면 제대로 설정된 것입니다.

 

 

 

3. 유저 생성하기

 

LDAPAdmin을 통해 LDAP로 접속해 유저를 생성합니다.

 

기본적으로 로그인하면 다음과 같은 화면을 확인할 수 있습니다.

 

 

홈 > 우클릭 > New > Organizational unit을 클릭해 새 조직을 생성합니다.

 

새로 생성한 조직을 우클릭한 후 New > User를 선택해 새 유저를 생성합니다.

 

 

다음과 같이 유저 정보를 입력해 줍니다.

 

이제 생성한 유저의 비밀번호를 설정해 줍시다.

 

생성한 유저 우클릭 > Set Password를 클릭한 후 암호를 설정해 저장합니다.

 

 

 

 

4. Redmin 로그인하기

 

이제 생성한 유저로 Redmine에서 로그인을 시도해 봅시다.

 

 

Set Password에서 입력한 비밀번호를 넣어주면

 

 

정상적으로 로그인 되는것을 확인할 수 있습니다.

 

 

 

 

 

 

반응형

 

 

우분투 서버 18.04.3에 OpenLDAP를 설치해 봅니다.

 

 

 

1. OpenLDAP 설치.

 

다음 명령어를 통해 OpenLDAP를 설치합니다.

 

$sudo apt-get update

$sudo apt-get install slapd ldap-utils

 

 

설치를 하다 보면 다음과 같이 관리자 패스워드를 입력하라는 창이 나옵니다.

 

 

놀라지 말고 관리자 암호를 입력해주면 됩니다. 이 암호는 추후에 변경할 수 있습니다.

 

 

 

2. 설정 재구성

 

설치하면서 설정할 수 있는 값은 관리자 암호밖에 없습니다. 따라서 패키지를 설치한 후 재설정 과정을 진행합니다.

 

다음 명령어를 통해 설정을 재구성 합니다.

 

$sudo dpkg-reconfigure slapd

 

 

"No"를 선택합니다.

 

 

LDAP 디렉터리의 base DN 값을 설정하기 위한 DNS 이름을 넣습니다.

 

 

조직 이름을 입력합니다.

 

 

관리자 암호를 입력합니다.

 

 

사용할 DB 유형을 고릅니다. MDB를 권고하고 있습니다. 

 

 

slapd를 제거했을 때 DB 정보를 지울 것인지 정합니다. "No"를 선택합니다.

 

 

해당 경로에 이미 DB가 존재할 때 기존 DB를 이동시킬지 여부입니다. "Yes"를 선택합니다.

 

 

다음 명령어를 통해 설정한 대로 반영되었는지 확인해봅시다.

 

$sudo slapcat

 

정상적으로 재설정이 끝났습니다.

 

 

 

3. 관리용 GUI툴 사용: LDAPAdmin

 

LDAP의 모든 작업은 CLI로 가능합니다만 윈도 환경에 익숙한 우리에게 GUI를 지원해주는 툴이 있는데 안쓰는건 바보같은 짓입니다.

 

윈도우 환경에서 사용할 수 있는 LDAPAdmin을 사용해 봅시다.

 

여기에서 LDAPAdmin을 다운로드합니다.

 

압축을 풀고 LDapAdmin.exe를 관리자 권한으로 실행합니다.

 

** 우클릭 후 속성 > 호환성 > 관리자 권한으로 이 프로그램 실행 체크박스를 체크해 둡시다.

 

 

Start > Connect > New connection을 선택합니다.

 

호스트 정보와 포트 정보를 입력하고 Fetch DNs버튼을 누르면 자동으로 BaseDN을 불러옵니다.

 

 

계정 정보에 "cn=admin"을 적고 BaseDN을 뒤에 적어줍니다. 초기 설정에 입력한 패스워드를 입력한 후 TestConnection 버튼을 눌러봅시다.

 

 

위와 같은 화면이 보이면 정상적으로 접속이 된 것입니다.

 

* Referenced: OpenLDAP을 활용한 기반 시스템 중앙 인증관리

 

 

 

 

반응형

 

CI/CD를 위해 Ububtu server 18.04.3에 Jenkins를 설치해 봅시다.

 

 

 

1. 저장소 등록

 

Jenkins를 Debian 계열의 리눅스에 설치하기 위해선 시스템에 키를 먼저 추가해야 합니다.

 

다음 명령어를 통해 Jenkins 패키지 저장소 키를 추가합니다.

 

$wget -q -O - https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo apt-key add -

 

 

OK를 확인한 후 명령어를 통해 /etc/apt/sources.list에 다음 항목을 추가해야 합니다.

 

deb https://pkg.jenkins.io/debian-stable binary/  

 

다음 명령어를 통해 작업을 진행합시다.

 

우선 혹시 모를 상황을 대비해서 백업 파일을 만들어둡시다.

 

$sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

 

 

이제 다음 명령어를 통해 deb를 추가해 줍니다.

 

$echo "deb https://pkg.jenkins.io/debian-stable binary/" | sudo tee -a /etc/apt/sources.list 

 

 

잘 추가되었나 파일을 확인해 봅시다.

 

$vi /etc/apt/soucres.list

 

 

맨 아래 추가된것을 확인했다면 업데이트를 진행해 줍니다.

 

$sudo apt-get update

 

 

우리가 추가한 jenins.io에 대한 내용이 보이면 제대로 완료된 겁니다.

 

 

 

2. 자바 설치

 

슬프게도 Jenkins는 모든 Java 버전을 지원하지 않습니다. 따라서 명시적으로 Java를 설치해야 하는 과정이 필요합니다. 귀찮기는 하지만 시스템이 올바르게 구성되도록 하는 가장 안전한 방법이라고 합니다. 

 

대표적인 자바 요구사항은 다음과 같습니다.

  • 2.164 (2019-02) and newer: Java 8 or Java 11
  • 2.54 (2017-04) and newer: Java 8
  • 1.612 (2015-05) and newer: Java 7

 

현 포스팅 시점을 기준으로 2.204.1 버전의 Jenkins가 최신이므로 JAVA 11을 설치합니다. 더 자세한 내용은 여기서 확인해 주시기 바랍니다.

 

다음 명령어를 통해 JAVA11을 설치합니다.

 

$sudo apt install openjdk-11-jdk 

 

 

완료 후 자바 버전을 확인해 봅시다.

 

$java -version

 

 

이제 자바도 정상적으로 설치하였으니 Jenkins를 설치해 봅시다.

 

 

 

3. Jenkins 설치.

 

다음 명령어를 통해 Jenkins를 설치합니다.

 

$sudo apt-get install jenkins

 

 

설치가 완료되면 http://server-host:8000/으로 접속해 봅시다.

 

다음과 같은 화면이 보인다면 정상적으로 설치된겁니다.

 

 

 

 

4. Jenkins 활성화

 

이제 웹 UI의 절차를 따라 Jenkins를 활성화하면 됩니다.

 

다음 명령어로 관리자 암호를 확인한 후 UI에 적어줍시다.

 

$sudo cat /var/lib/jenkins/secrets/initialAdminPassword

 

이다음엔 Jenkins를 커스텀할 수 있는 화면이 나옵니다.

 

 

미리 구상해둔 설정이 있는 게 아니므로 우선 추천 플러그인을 설치하도록 하겠습니다.

 

설치가 완료되면 관리자 계정을 만들어 줍시다.

 

 

다음은 URL을 설정하는 화면입니다.

 

 

전 기본값으로 설정한 채 넘어갔으며 만약 당장 설정하고 싶지 않다면 우측 아래의 Not now를 눌러주시면 됩니다.

 

 

드디어 Jenkins를 사용할 준비가 되었습니다.

 

 

 

 

 

반응형

 

 

우분투 서버 18.04.3에 Rabbit MQ를 설치해 봅니다.

 

 

 

1. Rabbit MQ 설치.

 

다음 명령어를 통해 Rabbit MQ 서버를 설치합니다.

 

$ sudo apt-get install rabbitmq-server

 

 

별문제 없이 설치가 진행됩니다.

 

 

 

2. Management Plugin 활성화

 

RabbitMQ에서는 여러 가지 기능을 플러그인의 형태로 제공합니다. 

 

Management Plugin은 관리 UI와 외부 모니터링 시스템을 제공해 주는 플러그인으로 사실 RabbitMQ를 설치하면 기본으로 같이 설치됩니다. 다만 기본값은 disabled상태이므로 명시적으로 enable 시켜야 합니다.

 

다음 명령어를 통해 Management Plugin을 활성화시킵니다.

 

$ sudo rabbitmq-plugins enable rabbitmq_management

 

 

정상적으로 활성화되었나 확인하기 위해 웹 UI로 들어가 봅시다.

 

http://server-host:15672/ 로 한번 접속해 보시기 바랍니다.

 

 

위와 같은 웹페이지가 보인다면 정상적으로 플러그인이 활성화된 겁니다.

 

Management Plugin에 대해 더 많은 정보는 여기서 확인해 보시기 바랍니다.

 

 

 

3. 접속

 

Rabbit MQ는 기본적으로 guest 계정을 준비해 줍니다. 한번 웹 UI를 통해 로그인을 시도해 봅시다. 

 

기본 계정 정보는 guest/guest입니다.

 

 

어떤 분은 로그인이 되었을것이고 어떤분은 위와 같이 Login failed 메시지와 함께 로그인에 실패했을 겁니다.

 

기본적으로 guest 계정은 localhost에서만 로그인이 가능합니다. 따라서 외부에서 접속을 하기 위해서는 별도의 관리자 계정을 새로 생성해주어야 합니다.

 

 

 

4. 계정 생성

 

다음 명령어를 통해 새로운 관리자 계정을 생성해 봅시다.

 

$sudo rabbitmqctl add_user admin 12345

 

 

그 후 다음 명령어를 통해 생성된 유저를 관리자로 위임해줍니다.

 

$sudo rabbitmqctl set_user_tags admin administrator

 

 

만약 유저를 잘못 생성했다면 다음 명령어로 지우시면 됩니다.

 

$sudo rabbitmqctl delete_user admin

 

 

 

 

5. 접속

 

이제 위에서 생성한 계정으로 접속해 봅시다.

 

 

큰 노력 없이도 훌륭한 관리자 페이지를 확인할 수 있습니다.

 

 

 

 

 

 

반응형

 

우분투 서버에 FTP를 활성화하고 Filezilla를 사용하기 위한 서버를 설치해 봅니다.

 

 

 

1. vsftpd 설치

 

vsftpd는 Very Secure FTP로 보안을 강화한 FTP입니다. 보안뿐 아니라 성능과 안정성도 뛰어나며 FTP를 쉽게 관리할 수 있습니다.

 

다음 명령어를 통해 vsftpd를 설치합니다.

 

$sudo apt-get install vsftpd

 

 

 

 

2. 설정 변경

 

vsftpd.conf파일의 설정을 변경해 줍니다.

 

$sudo vi /etc/vsftpd.conf

 

여러 설정중 다음 설정을 변경합니다.

 

write_enable=YES

 

 

xferlog_file=/ver/log/vsftpd.log

 

 

설정 저장 후 다음 명령으로 vsftpd를 재시작합니다.

 

$sudo service vsftpd restart

 

 

 

 

3. Filezilla 서버 설치.

 

이제 FTP 구성을 마쳤으니 Filezilla를 설치해 줍니다.

 

다음 명령어를 실행해 설치작업을 진행합니다.

 

$sudo apt-get install filezilla

 

 

 

 

4. Filezilla 클라이언트 접속

 

이제 윈도우에서 클라이언트를 통해 접속하면 정상적으로 접속 처리됩니다.

 

 

 

 

 

반응형

+ Recent posts