Skip to content

무중단 CI & CD (Jenkins & Nginx & Docker)

Zin0_0 edited this page Nov 4, 2020 · 1 revision

NCP를 이용한 Jenkins 서버 구축

  • 서버 생성
    • 이미지 타입에서 Application 선택 후, Jenkins 이미지 체크
    • image
    • 인증키 설정
      • image
      • 특이 사항은 없음
    • ACG(방화벽) 설정
      • 기존 ACG 설정이 있다면 이용해도 괜찮다.
      • 이번 서버 구축에서는 필요한 주소만 추가하면서 여는 연습을 하기 위해 새로 생성
      • image
    • Public IP 설정
      • image
    • Jenkins 상태 확인 명령어
      • # Jenkins 프로세스 기동 및 중지
        service jenkins start
        service jenkins stop
        service jenkins status
        
      • 상태 확인 ~> 실행 중인 것을 확인할 수 있음
      • image
    • 웹 브라우저로 Jenkins 접속
      • 공인IP:18080
      • image
      • 초기 접속화면으로 위와 같은 화면을 확인할 수 있다.
      • 초기 패스워드는 터미널에서 확인할 수 있다.
        • cat /var/lib/jenkins/secrets/initialAdminPassword
          
        • image
    • 초기 패스워드 이후, 젠킨스 install
      • image
      • 좌측은 Jenkins 커뮤니티에서 가장 유용하다고 알려진 플러그인들을 설치해준다.
        • Plugins 설치 오류 발생 시, Jenkins 커뮤니티에서 해결 가능하다 한다.
      • 우측은 플러그인을 직접 선택해서 설치하는 방법
        • NCP에서 제공하는 Jenkins 버전(작성 기준 2.73.2)에 호환되는 Plugins 직접 선택
      • 우선은 설정 문제가 생겼을 때, 더 빠른 해결을 위해 좌측을 선택
    • 젠킨스 계정 설정
      • 위의 설치가 끝나면, 젠킨스 계정을 설정하는 창이 나타난다.
      • image
    • 젠킨스 URL 설정
      • 계정 생성을 하고나면 URL을 설정하는 창이 나타난다
      • image
      • 따로 URL을 지정해줄 수 있지만, default 값이 권장이기 때문에 그대로 진행
    • 젠킨스 접속
      • 예시와 다르게 Empty page가 나타났다. 그래서 putty로 서버에 접속해서 jenkins를 중지했다가 다시 키니까 아래와 같은 화면이 나왔다.
      • image
      • 이후, 아이디와 패스워드를 입력해서 들어가니 초기화면이 나타났다.
      • image

Jenkins CI 환경 구축

  • Jenkins 설정
    • Jenkins는 기본적으로 보안 설정이 되어있지 않기 때문에, 보안 설정을 따로 해줘야한다.
    • Jenkins url 접속 후 Configure Global Security 클릭
      • image
    • 권한 설정
      • Security Realm은 default 설정인 Jenkins' own user db로 설정한다.
      • image
      • 인증은 Matrix-based security로 설정
        • 사용자나 그룹에 권한을 부여할 수 있는 strategy
        • 젠킨스를 공동 작업하는 팀원이 있다면, 위에서 사용자의 가입 허용을 체크하고 계정을 생성해서 권한을 부여하면 된다.
        • 하지만, 젠킨스 관련한 작업은 우선 혼자 진행할 예정이기 때문에 모든 유저에 READ만, 작업자인는 나에게 모든 권한을 부여했다.
          • 필요하다면 나중에 팀원 계정/그룹을 만들어서 권한을 추가하자
    • 플러그인 관리
      • Jenkins 관리 -> 플러그인 관리
        • Github plugin => Jenkins와 Github 통합 (처음 설치 시 권장이라 이미 포함)
        • Global Slack Notifier Plugin => Slack 연동(Job 알림 설정) ~> 추후 필요하면 사용
        • Publish Over SSH => ssh로 빌드 파일 보내기
        • Embeddable Build Status Plugin => Github 레포에 빌드 상태바 생성
        • Manage Scripts => Node.js 기반의 서버를 배포하기 위한 Script
    • Jenkins Global 설정
      • Jenkins 관리 -> 시스템 설정 -> GitHub Servers
        • image
        • git api 주소를 설정해주고, 아래 Credentials에 git access token을 선택
        • 현재 없는 상태기 때문에 옆에 Add버튼을 클릭하여 만들어준다.
        • Credential 설정을 위해, 자신의 Github -> Settings -> Developer settings -> Personal access tokens로 들어가 repo와 admin:repo_hoot이 체크된 토큰을 발행한다
        • image
        • 발행 후, 생성된 secret을 아래와 같이 Credential Secret에 입력
        • image
    • 프로젝트 생성 및 설정
      • 좌측 카테고리 네비게이션에서 새로운 Item -> Freestyle project 생성
      • image
      • 소스코드 관리에서 Git 선택 후, URL 및 Credentials, build할 branch 설정
      • image
      • user id와 pwd로 관리하는 것은 보안상 취약점 때문에 ssh키로 관리하기로 한다.
      • ~> git의 clone 주소를 https가 아니라, ssh로 변경해서 주소를 입력해줘야한다.
      • image
      • 위의 repo 주소는 사진의 주소로 입력해줘야한다.
      • image
      • ssh 설치하기
        • Jenkins 서버에 접속해서 ssh키를 생성해준다.
        • ssh-keygen -t rsa -f id_rsa
          
        • 물어보는 대답에 대해서는 엔터로 넘어가준다.
        • ls -al ~/.ssh/로 ssh파일이 생성된 것을 확인해준다고 하는데, 나의 경우에는 루프 디렉토리 아래 바로 퍼블릭키와 프라이빗 키가 생겼다.
        • 그래서 mkdir을 통해 .ssh 디렉토리를 만들어주고, mv 명령어로 생성된 public/private 키를 옮겨줬다. (ssh키 관리를 위해)
      • cat ~/.ssh/id_rsa 로 private key 확인, credentials private key에 입력(Kind에 SSH 선택)
      • image
      • BEGIN RSA 부터 END까지 모두 복사해서 붙여넣는다.
      • Username에는 현재 어떤 username이 들어가야하는지 잘 모르겠어서, 일단 임의로 넣어주었다.(CheckPoint) ~> username은 key 이름으로 나타나는 것으로 확인(별칭 같은 것)
      • cat ~/.ssh/id_rsa.pub 로 public key를 확인, Git repo의 settings -> Deploy keys에 등록
        • image
        • 이 과정까지 되면, Jenkins와 Github이 연동 되는 것까지 설정
      • push 했을 때, Jenkins가 push 이벤트를 받을 수 있도록 설정
        • git repo의 setting -> webhooks에서 jenkins 서버의 주소를 등록해준다
        • image-20201028161203187
        • 가려진 부분에 public ip를 입력해주고, 나머지 설정은 위와 같이 해준다
        • webhook 등록 후, Recent Deliveries에 연결에 실패했다고 뜬다. payload에 public ip 설정할 때, jenkins port번호까지 함께 등록해주니까 연결에 성공했다.
        • 여기까지 github repo 설정 및 연결이 끝났다.
      • 빌드 유발에서 GitHub hook trigger for GITScm polling을 체크해줘서, push가 일어나면 Jenkins 프로젝트에 빌드가 시작되는 것을 확인하자
        • image
        • image
        • 빌드가 정상적으로 완료돼서, Revision된 것을 확인하면, 여기까지 CI 설정이 끝났다!!!!!!

Jenkins CD 환경 구축

  • 보통은 Jenkins 서버와 배포하는 서버를 따로 두지만, NCP에서는 Jenkins가 탑재된 Server를 생성할 수 있고 프로젝트의 규모가 크지 않기 때문에, 하나의 서버에서 진행하는 것으로 전제한다.

  • Docker 설치

    • 패키지 저장소 추가 (도커의 공식 GPG 키와 저장소를 추가)
    • sudo apt-get update && sudo apt-get install \
          sudo apt-transport-https \
          sudo ca-certificates \
          curl \
          sudo software-properties-common
      
    • sudo: unable to resolve host라는 값이 console에 찍힌다면, /etc/hostname이 /etc/hosts에 등록되어있지 않아서 그렇다.
    • /etc/hosts에 hostname을 등록해주자
    • [/etc/hosts]
      ## ~~~
      127.0.1.1	host_name
      
    • docker 패키지가 검색되는지 확인하기
    • sudo apt-get update && sudo apt-cache search docker-ce
      
      • docker-ce - Docker: the open-source application container engine 이렇게 표시된다면 설치패키지가 검색된다는 의미
    • 도커 CE 설치 (무료버전)
    • sudo apt-get update && sudo apt-get install docker-ce
      
  • Nginx 설치

    • apt-get install nginx 명령어로 Nginx서버 설치
    • Nginx 웹 서버 수동으로 재실행하고 상태 확인하기
    • service nginx restart
      service nginx status
      
      • image
  • Dockerfile 작성

    • /home/docker-image에서 작업. server와 client를 구분하여 server만 도커 이미지화 해준다.
    • server 디렉토리 아래 작업
      • vi Dockerfile로 Dockerfile 생성 및 내부를 설정하기
      • FROM node:12.19.0
        
        MAINTAINER zin0
        
        VOLUME /deploy/issue-tracker/server
        
        RUN mkdir -p /app
        
        WORKDIR /app
        
        COPY ./deploy/ /app
        
        RUN npm install
        
        CMD npm start
        
    • docker image build -t 도커이미지이름 . 으로 이미지를 빌드한다.
      • docker image build -t issue-tracker-server .
    • .은 현재 path를 의미
    • docker-compose 작성 (blue, green)
      • docker-compose.blue.yml 작성
      • version: '2'
        
        services:
          issue-tracker-server:
            image: issue-tracker-server-docker-image
            volumes:
                - ./deploy:/deploy/issue-tracker/server
            ports:
                - "3001:3000"
        
    • docker-compose.green.yml 작성
      • version: '2'
        
        services:
          issue-tracker-server:
            image: issue-tracker-server-docker-image
            volumes:
                - ./deploy:/deploy/issue-tracker/server
            ports:
                - "3002:3000"
        
    • compose 파일에서 port:port 이 부분은 외부 port를 도커 컨테이너 내부 port로 바인딩해준다는 뜻이다.
    • deploy.sh 작성
      • #!/bin/bash
        
        DOCKER_APP_NAME=issue-tracker-server
        
        EXIST_BLUE=$(sudo docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml ps | grep Up)
        
        if [ -z "$EXIST_BLUE" ]; then
            echo "blue up"
            sudo docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml up -d
        
            sleep 10
        
            sudo docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yml down
        else
            echo "green up"
            sudo docker-compose -p ${DOCKER_APP_NAME}-green -f docker-compose.green.yml up -d
        
            sleep 10
        
            sudo docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml down
        fi
        
      • deploy.sh에 권한 추가
        • chmod 755 ./deploy.sh
      • Couldn't connect to Docker daemon at http+docker://localunixsocket - is it running? 에러
        • 검색 결과, docker가 정상적으로 실행되는지에 대한 에러라고 한다.
        • 그래서 docker socket을 이용하는 권한추가 sudo chown $USER /var/run/docker.sock나 docker 그룹에 유저를 추가하는 sudo usermod -aG docker $USER나 여러가지 방법을 해봤지만 똑같았다. 마지막으로, docker 컨테이너를 띄울 때, 권한 문제가 있지 않을까 싶어서 deploy.sh의 명령어에 sudo를 붙였다.
          • 해결되었다.
    • client
      • client는 nginx를 통해 띄워주는 것으로 결정, build를 통해 webpack을 적용
      • deploy.sh 작성
        • #!/bin/bash
          
          cd ./deploy
          
          npm install
          
          npm run build
          
  • 컨테이너 생성하기

    • docker-compose -p ${DOCKER_APP_NAME}-blue -f docker-compose.blue.yml up -d
      
    • docker-compose가 없다는 메세지가 출력돼서, apt install docker-compose를 통해 설치했다.
    • yaml.scanner.ScannerError: while scanning for the next token found character '\t' that cannot start any token 에러가 뜬다면, yaml 파일에서는 탭('\t')을 지원하지 않는다는 에러다. 따라서, tab으로 작성을 한 부분을 space(' ')로 변경해줘야한다.
    • docker ps -a로 컨테이너가 정상 작동 중인지 확인해준다.
  • Nginx 설정하기

    • Nginx로 blue와 green의 로드밸런싱을 설정
    • vi /etc/nginx/sites-available/issue-tracker-server로 서버 로드밸런싱 설정
    • # Load Balancing
      upstream issue-tracker-server {
              least_conn;
              server 127.0.0.1:3001 weight=5 max_fails=3 fail_timeout=10s;
              server 127.0.0.1:3002 weight=10 max_fails=3 fail_timeout=10s;
      }
      server {
        listen 3000;
        server_name Naver Cloud 주소;
        location / {
          proxy_pass http://issue-tracker-server;
        }
      }
      
    • vi /etc/nginx/sites-available/issue-tracker-client로 클라이언트 배포 설정
    • server {
        listen 80;
        location / {
          root /home/docker-image/client/deploy/dist;
          autoindex on;
          set $fallback_file /index.html;
          if ($http_accept !~ text/html) {
              set $fallback_file /null;
          }
          if ($uri ~ /$) {
              set $fallback_file /null;
          }
          index index.html
          try_files $uri /index.html;
        }
        error_page 404 /index.html;
      }
      
    • nginx 기본 설정 값들을 삭제해준다.
    • $ sudo rm /etc/nginx/sites-available/default
      $ sudo rm /etc/nginx/sites-enabled/default
      
    • 그런 다음 아래 명령어로 이 파일을 /etc/nginx/sites-enabled 디렉터리에 링크
    • sudo ln -fs /etc/nginx/sites-available/issue-tracker-server /etc/nginx/sites-enabled/
      sudo ln -fs /etc/nginx/sites-available/issue-tracker-client /etc/nginx/sites-enabled/
      
    • sudo nginx -t 명령어로 문법 이상 유무를 체크해주고, successful이 뜨면 됐다.
    • systemctl stop nginx, systemctl start nginx로 재실행을 해준다.
  • ACG 설정하기

    • 1024 포트(Nginx)를 열어준다.
    • Nginx는 1024포트를 통해 로드 밸런싱을 하기 때문(설정을 1024로 했음)
  • Jenkins Script 작성

    • #!/bin/sh
      ## docker container image를 만들 디렉토리를 비운다.
      sudo rm -rf /home/docker-image/server/deploy/*
      sudo rm -rf /home/docker-image/client/deploy/*
      
      ## jenkins에서 받아온 파일을 docker image 작업 디렉토리에 복사
      sudo cp -r /var/lib/jenkins/workspace/issue-tracker/server/* /home/docker-image/server/deploy/
      sudo cp -r /var/lib/jenkins/workspace/issue-tracker/client/* /home/docker-image/client/deploy/
      
      ## server 단에서, config를 구성하는 파일이 필요하기 때문에, 미리 저장해둔 파일 복사
      sudo mkdir /home/docker-image/server/deploy/config
      sudo cp /home/docker-image/server/config.json /home/docker-image/server/deploy/config/
      
      #!/bin/sh
      cd /home/docker-image/server
      sudo docker image build -t issue-tracker-server-docker-image .
      ./deploy.sh	
      
      #!/bin/sh
      cd /home/docker-image/client
      ./deploy.sh	
      exit
      EOF
      
    • 하나의 서버에서 작업하기 때문에, ssh 접속이 필요가 없었다.
    • 대신, deploy 스크립트를 실행할 때 권한 문제가 생겨서 권한을 주가해줬다.
    • chmod +x deploy.sh
    • server와 client를 각각 docker를 띄워 nginx로 로드 밸런싱해준다.
    • server에 config 디렉토리와 json 파일이 필요하기 때문에, 서버에 저장해뒀다가 docker를 image화 하는 디렉토리 아래 복사해주는 명령어를 추가했다.
  • sequilze connection error

    • GRANT ALL PRIVILEGES ON . TO root@'ip주소' IDENTIFIED BY '비밀번호'' WITH GRANT OPTION;
    • DB 접근 권한이 없기 때문에 localhost로는 접근이 가능하지만, 다른 ip로 접근했을 때 나오는 오류다. HOST에게 DB를 접근할 권한을 부여해서 해결하는 방법이다.

  • 기타
  • NodeJS 설치
    • PPA 추가
    • curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
      
    • NodeJS 설치
    • sudo apt-get install -y nodejs
      
    • build-essential 설치
    • sudo apt-get install build-essential
      
      • PPA를 통해서 NodeJS를 설치하면 npm도 함께 설치가 되는데, npm install시 에러가 발생하는 것을 방지하기 위해 build-essential을 설치해준다.
    • 설치가 완료되면 node -v로 버전을 확인해준다.
  • server forever 구동
    • forever start는 background 명령이기 때문에 도커 컨테이너가 계속 죽음 ~> forever app.js로 도커 컨테이너가 죽지 않도록 설정

  • 문제점
    • 동일 서버에서 클라이언트 배포, API 서버 배포, docker image화 및 jenkins, nginx를 한번에 돌리기 때문에 메모리 부하 및 용량이 걱정된다.
    • 하지만, 토이 프로젝트기 때문에 큰 문제가 없을 것으로 판단되고 서버당 요금이 무시할 수준은 아니기 때문에 하나에서 진행
    • 지원받는 크레딧이 있긴 하지만, 12월 까지 다른 프로젝트도 돌려야하므로 서버 한대로 진행하기로 결정했다.

Reference

📁 Documents


⚖ Rules


🖊 Records

☕️ 일일 회고 & 데일리 스크럼
🗓 주간 회고
👨‍👦‍👦 피어 세션

🤯 Tech

Clone this wiki locally