기술 이야기/CI&CD

NCP(Naver Cloud Platform)와 Jenkins -3) CD 환경 구축

Zin0_0 2020. 11. 2. 16:24
반응형

Jenkins, Nginx, docker를 활용한 무중단 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"
    • 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
    • client

      • client는 nginx를 통해 띄워주는 것으로 결정, build를 통해 webpack을 적용

      •       deploy.sh 작성
           #!/bin/bash
        
           cd ./deploy
        
           npm install
        
           npm run build
    • compose 파일에서 port:port 이 부분은 외부 port를 도커 컨테이너 내부 port로 바인딩해준다는 뜻이다.

    • 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를 붙였다.

      •       #!/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
      • 해결되었다.

  • 컨테이너 생성하기

    • 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
              server_name $url
              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를 접근할 권한을 부여해서 해결하는 방법이다.
  • [nginx에서 세부 경로를 조회했을 때 React Router로 넘어가지 않는 오류](https://github.com/boostcamp-2020/IssueTracker-35/issues/10)
    • react는 SPA형식으로 index.html만을 사용하지만, nginx의 경우는 URL에 맞는 html파일을 찾으려고 하는 문제가 원인.
    • 설정 파일의 location 하단에 `server_name $url` 설정 파일에 url로 호스트를 설정해줘서 해결
    • 에러메세지는 사라졌는데 왜일까..? 파일을 찾는게 아닌, spa 내에서 처리하게 되는걸까??

  • 기타
  • 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

반응형