CI-CD

젠킨스(Jenkins) 설치하기(with Nginx on Docker container)

iKay 2019. 12. 22. 23:08
반응형

Jenkins는 빌드, 테스트, 배포 과정을 자동화 해주는 강력한 CI(Continuous Intergreation)/CD(Coutinuous Deployment) 도구이다. 웹 프로젝트를 진행하기 위해 CI/CD 환경이 필요해서Jenkins를 설치하게 되었고 관련된 내용을 여기에 정리해본다.  

 

 

어떤 구조로 설치할까?

 

아래 그람과 같이 https://jenkins.example.com 으로 요청이 들어오면 http://jenkins:8080으로 리버스 프록시하는 구조로 설치하기로 결정했다. 이 구조를 어떻게 쉽게 설치할 수 있을까 고민하다가 docker swarm을 통해 docker stack을 deploy하는 방식을 택했다.

 

우선 직접 설치하지 않고 docker container 환경을 사용한 이유는 SSL을 사용하기 위해 ngix도 함께 설치해야 했고, 이 nginx와 jenkins 서비스를 함께 관리하는 것이 더 낫다고 판단해서 였다.

 

kubernetes 대신 swarm을 사용한 이유는 사용하기 더 쉽고 node의 수가 적을 때는 swarm으로도 제어가능하기 때문이다. 그래도 요즘 k8s가 더 많이 사용되고 있으니 방법을 정리할 예정이다.

 

로컬 환경에서 테스트

 

우선 로컬환경에서 위의 구조로 동작하는지 테스트하기 전에, 각각 이미지가 잘 동작하는지 부터 확인해봤다. 

 

각 이미지는 https://hub.docker.com/ 에서 검색한 후 적절한 컨테니어 이미지를 선택해서 pull 한 후 사용하면 된다. 

 

nginx

 

$ docker container run --name nginx -p 8081:80 nginx:1.17.6-alpine

실행 시키면 아무런 메시지가 나오지 않지만, 웹 브라우저를 열어서 http://localhost:8081 으로 이동하면 nginx가 동작하는 것을 확인할 수 있다. 

 jenkins

 

$  docker container run --name jenkins -p 8080:8080 jenkins/jenkins:lts

이번에는 http://localhost:8080으로 접속해보자. 아래 그림과 같이 Jenkins가 준비되었음을 보여준다. 

 

 

nginx와 jenkins 컨테이너 이미지에는 이상이 없는 것을 확인했다. 

 

docker-compose.test.yml 작성

 

로컬환경에서 서로 연관이 있는 컨테이너를 두 개 이상 한꺼번에 띄워서 테스트 할 때는 docker-compose.yml 을 작성해서 띄우는 것이 편리하다. 아래와 같이 docker-compose.test.yml을 작성했다. 

 

작성을 할 때, jenkins의 data가 container가 재시작해도 유지되도록 아래와 같이 path volumes 설정을 해주었다. 

 

networks는 한 개만 사용하기 때문에 생략했다.

 

# docker-compose.test.yml

version: '3.1'

services: 
  reverse-proxy:
    image: nginx:1.17.6-alpine
    volumes:
      - ./nginx/conf.d.test:/etc/nginx/conf.d
    ports: 
      - 8081:8081

  jenkins:
    image: jenkins/jenkins:lts
    volumes:
      - ./jenkins/jenkins_home:/var/jenkins_home

 

jenkins.conf 작성

conf.d 디렉토리 밑에 있는 nginx의 설정파일이다. 이 디렉토리에 있는 *.conf는 모두 nginx의 설정파일로 인식된다.

아래와 같이 http://localhost:8081로 요청이 오는 경우, http://jenkins:8080으로 reverse proxy 되도록 연결되도록 설정했다. 

server {
  listen 8081;
  server_name localhost;

  location / {
    proxy_set_header Host $host:$server_port;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_pass http://jenkins:8080;
    proxy_read_timeout  90;
  }
}

 

docker-compose를 실행시켜 보자. 

 

$ docker-compose -f docker-compose.test.yml up

 

http://localhost:8081 로 접속하면 젠킨스 서버로 접속할 수 있다. 즉, nginx의 리버스 프록시가 잘 동작함을 확인했다. 

 

 

실제 환경에서 테스트

실제환경에서는 docker swarm을 통해 docker stack을 deploy 하는 방식으로 한다.

 

참고로, docker stack에서도 docker-compose.yml 파일을 사용할 수 있다. 하지만 docker-compose, docker stack에서 지원되는 기능이 조금씩 다르다. 예를 들어, docker-compose에서만 build를 사용할 수 있고, docker stack에서만 deploy를 사용할 수 있다. 이 둘의 명령어, 사용하는 파일이 거의 비슷해서 헷갈릴 수 있는데, 쉽게 생각해서, docker-compose는 로컬 환경, docker stack은 배포 환경에서 사용되는, 염연히 다른 것이라 보면 된다. 

 

docker-compose.prod.yml 작성

 

아래와 같이 실제 환경에서 deploy하기 위한 docker-compose.prod.yml 파일을 작성한다. 

 

# docker.compose.prod.yml

version: '3.1'

services: 
  reverse-proxy:
    image: nginx:1.17.6-alpine
    volumes:
      - ./nginx/conf.d.prod:/etc/nginx/conf.d
    ports: 
      - 80:80
      - 443:443
    secrets:
      - fullchain.pem
      - privkey.pem  
      
  jenkins:       
    image: jenkins/jenkins:lts 
    volumes:
      - /usr/share/jenkins/jenkins_home:/var/jenkins_home

secrets:
    fullchain.pem:
        external: true
    privkey.pem:
        external: true
        

 

로컬 환경에서 작성했던 docker-compose.test.yml 과의 차이점을 간략히 짚고 넘어가자. 

 

실제 환경에서는 SSL 방식으로 통신할 것이기 때문에 443 port를 개방해줬고, 80 port로 요청이 오는 경우 443으로 리다이렉트할 계획이다. 

 

jenkins의 data가 저장되는 volumes의 path를 변경했다. 

 

secrets 이라는 것이 보인다. secrets는 docker swarm에서 SSH key, password, API 주소, TLS certificate 등과 같이 Dockerfile, 소스코드 등에 저장되어서는 안되거나, network를 통해 공개되기에는 민감한 정보를 저장하는 공간이다. secrets 라는 것을 통해서 민감한 정보들을 중앙관리 가능해 쉽게 폐기하거나 등록할 수 있고, node 간 전송도 비교적 안전한 편이며, service가 유지되는 동안만 유효하다. 

secrets.fullchain.pem 의 속성 으로 external을 줬는데, docker stack을 run 하기 전에 secrets를 먼저 입력하겠다는 것이다. 다음과 같이 secret를 입력할 수 있다.

 

secrets에 대해 궁금하다면 여기(https://docs.docker.com/engine/swarm/secrets/)를 더 보기 바란다.

 

$ docker secret create {key} {value}

 

 

jenkins.conf 작성

 

http://jenkins.example.com:80 으로 요청이 오는 경우 https://으로 리다이렉트되게 했다. https://jenkins.example.com으로 접속하는 경우 http://kenkins:8080으로 리버스 프록시된다. 

 

SSL certificate가 위의 fullchain.pem, privakey.pem 를 참조하기 위해 /run/secrets 를 붙었다. 

 

혹시, SSL 설정하는 법이 궁금하다면 여기 무료 SSL을 적용하는 방법에 대한 포스팅(https://kay0426.tistory.com/37?category=725077)을 참고하자.

 

server {
  listen 80;
  server_name jenkins.example.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl;
  server_name jenkins.example.com;
 
  ssl_certificate /run/secrets/fullchain.pem;
  ssl_certificate_key /run/secrets/privkey.pem;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers HIGH:!aNULL:!MD5;
 
  location / {
    proxy_set_header Host $host:$server_port;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_redirect off;
    proxy_pass http://jenkins:8080;
    # Required for new HTTP-based CLI
    proxy_http_version 1.1;
    proxy_request_buffering off;
    proxy_buffering off; # Required for HTTP-based CLI to work over SSL
    # workaround for https://issues.jenkins-ci.org/browse/JENKINS-45651
    add_header 'X-SSH-Endpoint' 'jenkins.example.com' always;
  }
}

 

deploy

 

다음과 같이 명령어를 입력해서 docker stack을 deploy 해보자. 마지막 jenkins는 deploy된 stack 이름이다.   

$ docker stack deploy -c docker-compose.prod.yml jenkins

 

다음 명령어로 jenkins라는 이름의 deploy가 2개의 service를 갖고 있음을 확인할 수 있고,

$ docker stack ls
NAME                SERVICES            ORCHESTRATOR
jenkins             2                   Swarm

 

다음 명령어로 각 서비스의 세부 사항을 확인할 수 있다. 

$ docker stack ps jenkins
ID                  NAME                      IMAGE                 NODE                DESIRED STATE       CURRENT STATE         ERROR                       PORTS
way89ws7elir        jenkins_reverse-proxy.1   nginx:1.17.6-alpine   kay                 Running             Running 3 hours ago
4muf6s4t85cj        jenkins_jenkins.1         jenkins/jenkins:lts   kay                 Running             Running 3 hours ago
mq9vaq8gsbiq        jenkins_reverse-proxy.1   nginx:1.17.6-alpine   kay                 Shutdown            Failed 3 hours ago    "task: non-zero exit (1)"

 

이제, nginx에서 설정했던 https://jenkins.example.com(각자 도메인에 맞는 주소를 입력)로 접속 가능하다. 

 

 

 

initialAdminPassword가 필요한 것 같은데 아까 docker-compose.prod.yml에서 '/usr/share/jenkins/jenkins_home:/var/jenkins_home' 를 설정한 적이 있다. 즉, 컨테이터 외부 OS의 '/usr/share/jenkins/jenkins_home'를 source, 내부 '/var/jenkins_home'를 destination으로사용하겠다는 것으로 서로 연결 된다고 보면된다.

따라서, initialAdminPassword를 확인하기 위해서는 container 내부에서 확인할 필요 없이 OS의 /usr/share/jenkins/jenkins_home에서 initialAdminPassword를 확인할 수 있다.

$ cat /usr/share/jenkins/jenkins_home/secrets/initialAdminPassword

eb**********437a977543**********

 

cat 으로 출력되는 비밀번호를 입력하고 Continue를 하면 다음 단계로 진행할 수 있다. 

 

 

 

 

결론

Docker container환경으로 Jenkins를 설치하는 법을 정리했다. jenkins만 설치하는 것은 쉬운데, docker 설정, nginx 설정 등이 들어가게 되면서 꽤 복잡한 작업이 된 것 같다. 

 

앞으로는 Jenkins, Github 등을 사용해 웹 프로젝트를 CI/CD 하는 방법을 차근차근 정리해야겠다. 

 

참고

- https://github.com/jenkinsci/docker/blob/master/README.md

- https://wiki.jenkins.io/display/JENKINS/Jenkins+behind+an+NGinX+reverse+proxy 

 

반응형

'CI-CD' 카테고리의 다른 글

젠킨스 웹훅(Jenkins webhook)으로 깃헙(Github)과 연동하기  (0) 2020.04.22