티스토리 뷰

1. 도커 이미지 자동 배포하기

지속적 통합 및 전달( CI/CD)

  • CI - CONTINUOUS Integration
  • CD - CONTINUOUS Delivery

개발 서비스는 빠르고 효과적으로 서비스를 출시하기 위해 지속적인 소스의 관리와 통합과 빌드 그리고 테스트를 하고 배포하는 과정이 피요하다.

CI는 테스트 및 빌드까지의 과정을 말하고 CD는 전달 및 배포까지 포함하는 과정을 말한다.

 

자동화

  1. 소스 저장소에 최신의 소스를 저장
  2. 전체소스를 다운로드
  3. 테스트
  4. Docker 이미지 생성
  5. Docker 이미지 저장
  6. 어플리케이션 업데이트

자동화 도구

  • Jenkins
  • GitLab CI/CD
  • TravisCI
  • CircleCI
  • 그외의 다양한 도구들

실습

샘플소스 : https://github.com/subicura/docker‐jenkins‐workshop

Jenkins

  • 2004년 썬(Sun Microsystems)의 코스케 가와구치가 Hudson을 개발
  • 빌드/테스트/코드 분석/배포/알람등 다양한 기능 제공
  • Master/Agent 구성(하나의 Master에 수십/수백개의 Agent사용가능)
  • 1400여개가 넘는 플러그인 제공
  • 깔끔한오래된UI(blueocean등장)
  • 무료

Jenkins 실행

기본 Jenkins프로젝트에 docker와 docker-compose가 설치된 도커 이미지를 사용

# mac
docker run -u root --rm -p 8080:8080 --name jenkins \
  -v $(데이터디렉토리):/var/jenkins_home \
  ## docker랑 통신할 때 사용하는 파일인데 jenkins 에 연결하여 docker가 설치되어 있지 않지만
  ## Host에 있는 docker를 jenkins가 사용할 수 있게 해줌
  ## docker for windows는 sock파일을 파일로 오픈할 수 없음
  -v /var/run/docker.sock:/var/run/docker.sock \
  subicura/jenkins:2

# windows
docker run -u root --rm -p 8080:8080 --name jenkins \
  -v $( ):/var/jenkins_home \
  subicura/jenkins:2

데이터 디렉토리는 /User/${USER}/Download/jenkins(mac) 또는 //c/jenkins(windows) 처럼 입력\

docker run -u root --rm -p 8080:8080 --name jenkins \
  -v /home/${USER}/jenkins:/var/jenkins_home \
  -v /var/run/docker.sock:/var/run/docker.sock \
  subicura/jenkins:2

http://localhost:8080 접속 후 로그에 적힌 패스워드를 입력

플러그인 설치후 계정을 생성한다음 젠킨스 및 플러그인 업데이트

 

자동 배포 스크립트 만들기

pipeline

stage 별로 작업을 만들 수 있음
groovy라는 언어를 사용함

  1. create new jobs를 선택하고 Pipeline을 선택함
  2. awesome-app 이름 입력
  3. Do not allow concurrent builds(하나의 빌드가 진행 중일 때에는 대기 했다가 빌드가 끝나면 다음 빌드가 진행됨)
    를 선택하고 GitHub Project에 https://github.com/subicura/docker-jenkins-workshop를 입력

build

Build Now 버튼을 클릭 첫번째 빌드 성공

 

stage

빈 스테이지를 구성
Configuration > Pipeline에 script를 입력함
첫번째 스테이지 부터 마지막 스테이지까지 순차적으로 실행되고 중간에 에러가 발생하면 멈춤

node {
    stage('Pull') {
    }
    stage('Unit Test'){
    }
    stage('Build') {
    }
    stage('Tag'){
    }
    stage('Push'){
    }
    stage('Deploy'){
    }
}

 

Pull

git 저장소에 저장된 소스를 가져오는 스크립트를 작성

stage('Pull') {
    git 'https://github.com/subicura/docker-jenkins-workshop.git'
}

 

Buile

unit test를 건너띄고 일단 build 스크립트를 작성
하단의 subicura부분은 자신의 docker hub ID로 변경함

stage('Build') {
  sh(script: 'docker build --force-rm=true -t subicura/ruby-app:latest .')
}

groovy에서 여러줄을 입력하고 싶으면 '''로 시작하면 됨

Windows 사용자는 Docker 데몬과 통신하기 위해 추가로 환경변수 설정이 필요

2375 포트로 docker랑 통신할 수 있는 PORT가 열려 있음

jenkins 컨테이너 안에서 Host에 있는 해당 PORT로 DOCKER_HOST 명령어를 전달하겠다고 환경변수를 설정해주면
docker for windows에서도 HOST에 있는 docker와 통신을 할 수 있음

node {
    withEnv(["DOCKER_HOST=tcp://docker.for.win.localhost:2375"]) {
        stage('Pull') {
        }
        stage('Unit Test') {
        }
        stage('Build') {
        }
        stage('Tag') {
        }
        stage('Push') {
        }
        stage('Deploy') {
        }
    } 
}

Docker hub ID와 패스워드를 Jenkins에 저장함

Credentials > Global > Add Credentials를 선택하고 정보를 입력
ID는 반드시 dockerhub로 입력함

저장한 Credentials를 사용하도록 스크립트를 변경함

java.lang.NoSuchMethodError: No such DSL method 'withCredentials' found among steps 에러가 발생하면
젠킨스 와 젠킨스 플러그인을 업데이트 하면 해결된다

node {
    withCredentials([[$class: 'UsernamePasswordMultiBinding',
      credentialsId: 'dockerhub',
      usernameVariable: 'DOCKER_USER_ID',
      passwordVariable: 'DOCKER_USER_PASSWORD']]) {
        stage('Pull') {
            git 'https://github.com/subicura/docker-jenkins-workshop.git'
        }
        stage('Unit Test') {
        }
        stage('Build') {
            sh(script: 'docker build --force-rm=true -t ${DOCKER_USER_ID}/ruby-app:latest .')
        }
        stage('Tag') {
        }
        stage('Push') {
        }
        stage('Deploy') {
        }
    }

 

Tag

생성한 이미지에 현재 빌드 번호를 태그로 달아줌
혹시 배포한 이미지에 문제가 있다면 재빠르게 이전 버전을 배포하면 됨
자동으로 빌드 버전을 넘겨 받음

stage('Tag') {
    sh(script: '''docker tag ${DOCKER_USER_ID}/ruby-app \
      ${DOCKER_USER_ID}/ruby-app:${BUILD_NUMBER}''')
}

 

Jenkins 기본 환경변수 정보

Jenkins는 다양한 환경변수를 기본으로 제공함
https://wiki.jenkins.io/display/JENKINS/Building+a+software+project

 

Building a software project - Jenkins - Jenkins Wiki

Jenkins can be used to perform the typical build server work, such as doing continuous/official/nightly builds, run tests, or perform some repetitive batch tasks. This is called "free-style software project" in Jenkins. Setting up the project Go to Jenkins

wiki.jenkins.io

 

Push

생성한 이미지를 도커 허브에 저장함

  1. 도커 로그인 ID와 패스워드로 로그인
  2. docker push 만든 태그로 push
  3. docker push latest 태그로 push
stage('Push') {
 sh(script: 'docker login -u ${DOCKER_USER_ID} -p ${DOCKER_USER_PASSWORD}')
 sh(script: 'docker push ${DOCKER_USER_ID}/ruby-app:${BUILD_NUMBER}')
 sh(script: 'docker push ${DOCKER_USER_ID}/ruby-app:latest')
}

 

Deploy

기존 컨테이너를 제거하고 새로운 컨테이너를 생성함
처음에는 앱이 구동되어있지 않아 에러가 발생하므로 try catch 로 감싸서 예외처리 함
앱이 구동중이면 정지하고 앱을 삭제 처리 함
그후 docker run 명령을 실행

stage('Deploy') { 
    try {
        sh(script: 'docker stop ruby-app')
        sh(script: 'docker rm ruby-app') 
    } catch(e) {
        echo "No ruby-app container exists"
    }
    sh(script: '''docker run -d -p 10000:4567 --name=ruby-app \
      ${DOCKER_USER_ID}/ruby-app:${BUILD_NUMBER}''')
}

정상적으로 배포되었는지 localhost:10000으로 접속해봄

 

Unit Test

작업 디렉토리를 컨테이너의 디렉토리로 연결한 다음 테스트 코드를 수행하고 결과가 정상인지 확인함

stage('Unit Test') {
    sh(script: '''docker run --rm \
      -v /var/jenkins_home/workspace/${JOB_NAME}:/app \
      -w /app \
      ruby:2.3 sh -c "bundle install && bundle exec ruby app_test.rb"''')
}

테스트를 실패하면 젠킨스는 더이상 스테이지를 진행하지 않고 멈춤

Could not locate Gemfile 오류가 발생

/var/jenkins_home/workspace/${JOB_NAME} 디렉토리에 분명 Gemfile이 있지만 찾을 수 없다는 메시지가 보임

호스트 디렉토리를 Jenkins 컨테이너가 아닌 도커가 실행중인 OS의 디렉토리 경로를 변경해줌

stage('Unit Test') {
    sh(script: '''docker run --rm \
      -v /home/freelife/jenkins/workspace/${JOB_NAME}:/app \
      -w /app \
      ruby:2.3 sh -c "bundle install && bundle exec ruby app_test.rb"''')
}

Windows의 경우 //c/jenkins와 같은 형태로 입력해줌

워크스페이스 경로는 다른 작업에서도 자주 사용하므로 환경변수로 설정함
Manage Jenkins > Configure System > Global properties > Environment variables Name WORKSPACE_PATH

Value /Users/subicura/Downloads/tmp/jenkins/workspace

워크스페이스 경로를 환경변수로 수정함

stage('Unit Test') {
    sh(script: '''docker run --rm \
      -v ${WORKSPACE_PATH}/${JOB_NAME}:/app \
      -w /app \
      ruby:2.3 sh -c "bundle install && bundle exec ruby app_test.rb"''')
}

 

테스트 / 빌드 / 배포 성공

최종적으로 개발자 개입없이 젠킨스가 스스로 배포의 모든것을 처리함.

 

기존 스트립트 완성본

node {
    withCredentials([[$class: 'UsernamePasswordMultiBinding',
      credentialsId: 'dockerhub',
      usernameVariable: 'DOCKER_USER_ID',
      passwordVariable: 'DOCKER_USER_PASSWORD']]) {
        stage('Pull') {
            git 'https://github.com/subicura/docker-jenkins-workshop.git'
        }
        stage('Unit Test') {
            sh(script: '''docker run --rm \
              -v ${WORKSPACE_PATH}/${JOB_NAME}:/app \
              -w /app \
              ruby:2.3 sh -c "bundle install && bundle exec ruby app_test.rb"''')
        }
        stage('Build') {
            sh(script: 'docker build --force-rm=true -t ${DOCKER_USER_ID}/ruby-app:latest .')
        }
        stage('Tag') {
            sh(script: 'docker tag ${DOCKER_USER_ID}/ruby-app ${DOCKER_USER_ID}/ruby-app:${BUILD_NUMBER}')
        }
        stage('Push') {
            sh(script: 'docker login -u ${DOCKER_USER_ID} -p ${DOCKER_USER_PASSWORD}')
            sh(script: 'docker push ${DOCKER_USER_ID}/ruby-app:${BUILD_NUMBER}')
            sh(script: 'docker push ${DOCKER_USER_ID}/ruby-app:latest')
        }
        stage('Deploy') { 
            try {
                sh(script: 'docker stop ruby-app')
                sh(script: 'docker rm ruby-app') 
            } catch(e) {
                echo "No ruby-app container exists"
            }
            sh(script: 'docker run -d -p 10000:4567 --name=ruby-app ${DOCKER_USER_ID}/ruby-app:${BUILD_NUMBER}')
        }
    }
}

 

개선하기[Docker Compose]

사실 docker 명령어를 그대로는 잘 사용하지 않으며, Docker Compose를 사용한다.

version: '2'

services:
  app:
    build: .
    image: ${DOCKER_USER_ID}/ruby-app
  unit:
    image: ruby:2.3
    volumes:
      - ${WORKSPACE_PATH}/${JOB_NAME}:/app
    working_dir: /app
    command: bash -c "bundle install && bundle exec ruby app_test.rb"
  production:
    image: ${DOCKER_USER_ID}/ruby-app:${BUILD_NUMBER}
    ports:
      - 10001:4567

기존에 도커 명령어를 사용하던 부분을 수정함

stage('Pull') {
    git 'https://github.com/subicura/docker-jenkins-workshop.git'
}
stage('Unit Test') {
    sh(script: 'docker-compose run --rm unit')
}
stage('Build') {
    sh(script: 'docker-compose build app')
}
stage('Tag') {
// ...
}
stage('Push') {
// ...
}
stage('Deploy') {
    sh(script: 'docker-compose up -d production')
}

 

개선하기 [소스변경 > 자동배포]

소스가 변경되면 자동으로 배포하는 설정을 추가함
사실 Build Now는 거의 사용하지 않음

주기적으로 GitHub 저장소를 체크에서 변한 부분이 있다면 Poll 해오는 스크립트 작성

Configure > Build Triggers > Poll SCM = H/5 * * * * *

* * * * * 테스트로 1분마다 한번씩 체크하도록 설정

스크립트에 변경을 체크할 git 주소를 추가함

node {
    git poll: true, url: 'https://github.com/subicura/docker-jenkins-workshop.git'
    ... 
}

poll방식은 소스 저장소에서 웹혹(webhook)을 받을 수 없을 때 사용하며 실제로는 대부분 웹훅을 이용한 트리거를 사용함

변경사항이 생기면 바로 적용이 되는 것을 확인 할 수 있음

 

개선하기[Pipeline script from SCM]

소스 폴더에 있는 jenkins 파일을 사용하는 기능

Configuration > Pipeline에서 Pipeline script from SCM을 선택하고 SCM에서 git을 선택함

Repository URL에 https://github.com/subicura/docker-jenkins-workshop.git을 입력함
(윈도우즈 사용자는 Script Path에 Jenkinsfile.win을 입력함)

이제 젠킨스 설정을 파일(코드)로 관리함
변경 이력을 관리할 수 있고 좀더 안전하게 사용할 수 있음

 

실제 사례

ChatOps

자동배포가 불안하면 이미지 생성이 완료되면 배포는 채팅 명령어로 실행

/deploy-production

 

Slack

  • message buttons
  • message menu

무중단 배포

도커는 동일한 컨테이너를 2개 만드는 것이 아주 간단함

컨테이너를 구동 시키고

Nginx 로 바라볼 컨테이너를 설정하면 무중단 배포가 가능

 

Docker Swarm

이미지를 만드는 과정은 동일하며 배포 명령어만 다름

docker service update \
  --image localhost:5000/ruby-app:${BUILD_NUMBER} \
  ruby-app


Kubernates

명령어만 입력하면 알아서 여러개의 컨테이너를 순차적으로 업데이터 해줌

kubectl set image \
  -f deploy/ruby-app.yml \
  app=localhost:5000/ruby-app:${BUILD_NUMBER}

 

참고

freedeveloper.tistory.com/59?category=808752

 

[도커(Docker)의 이해] 4. 이미지 빌드 환경 만들기

컨테이너 기반 가상화 플랫폼 '도커(Docker)'의 이해포스팅 참조 정보 해당 포스팅 참고 토크ON세미나 강의 링크 https://www.youtube.com/playlist?list=PLinIyjMcdO2S_Ojp_qK7EaZpxr3M3xprT Docker의 이해 - You..

freedeveloper.tistory.com

 

 

 

 

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함