컨테이너 기반 가상화 플랫폼 '도커(Docker)'의 이해포스팅 참조 정보
해당 포스팅 참고 토크ON세미나 강의 링크
https://www.youtube.com/playlist?list=PLinIyjMcdO2S_Ojp_qK7EaZpxr3M3xprT
https://tacademy.skplanet.com/live/player/onlineLectureDetail.action?seq=125
실습 Version 정보
- Ubuntu 18.04
- Docker Version: 19.03.2
4강 도커 이미지 빌드 환경 만들기
도커 이미지 자동 배포 하기
지속적 통합 및 전달(CI/CD)
- CI
Continuous Integration
- CD
Continuous Delivery
빠르고 효과적으로 제품을 출시하기 위해 지속적으로 소스를 통합하고 빌드하고 테스트하고 배포하는 과정이 필요
CI는 보통 테스트/빌드까지의 과정을 이야기하고 CD는 추가로 전달/배포까지 포함
하지만 혼용해서 쓰는 경우가 많고 CI라도 배포까지 포함하는 경우가 있음
자동화
- 소스저장소에 최신 소스를 저장(개발자는 여기까지 신경씀)
아래의 내용은 모두 자동화 대상
2. 전체 소스를 다운로드
3. 테스트
4. Docker 이미지 만들기
5. Docker 이미지 저장하기
6. 애플리케이션 업데이트
자동화 도구들
- Jenkins
- TravisCI
- CircleCI
- 그외 다양한 도구들
실습
sinatra(ruby)로 만들어진 웹 애플리케이션이며 /
또는 /hello
로 접속할 수 있음
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
라는 언어를 사용함
create new jobs
를 선택하고Pipeline
을 선택함awesome-app
이름 입력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'){
}
}
다시 Build Now
버튼을 누르면 각 스테이징별로 실행 시간이 표시되고 성공한 것을 알 수 있음
Pull
git 저장소에 저장된 소스를 가져오는 스크립트를 작성
stage('Pull') {
git 'https://github.com/subicura/docker-jenkins-workshop.git'
}
Build
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
Push
생성한 이미지를 도커 허브에 저장함
-
도커 로그인 ID와 패스워드로 로그인
-
docker push 만든 태그로 push
-
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}')
}
}
}
개선하기1(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')
}
개선하기2(소스변경 > 자동배포)
소스가 변경되면 자동으로 배포하는 설정을 추가함
사실 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)을 받을 수 없을 때 사용하며 실제로는 대부분 웹훅을 이용한 트리거를 사용함
변경사항이 생기면 바로 적용이 되는 것을 확인 할 수 있음
개선하기3(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}
'개발강의정리 > DevOps' 카테고리의 다른 글
[데브옵스를 위한 쿠버네티스 마스터] 3. 쿠버네티스 들어가기 (0) | 2020.08.22 |
---|---|
[데브옵스를 위한 쿠버네티스 마스터] 2. 쿠버네티스 소개 (0) | 2020.08.18 |
[데브옵스를 위한 쿠버네티스 마스터] 1. Docker 요약 정리 (0) | 2020.08.17 |
[도커(Docker)의 이해] 3. 이미지 만들고 배포하기 (0) | 2019.10.07 |
[도커(Docker)의 이해] 2. 컨테이너 실행하기 (0) | 2019.10.07 |
댓글