티스토리 뷰
1. 도커 이미지 자동 배포하기
지속적 통합 및 전달( CI/CD)
- CI - CONTINUOUS Integration
- CD - CONTINUOUS Delivery
개발 서비스는 빠르고 효과적으로 서비스를 출시하기 위해 지속적인 소스의 관리와 통합과 빌드 그리고 테스트를 하고 배포하는 과정이 피요하다.
CI는 테스트 및 빌드까지의 과정을 말하고 CD는 전달 및 배포까지 포함하는 과정을 말한다.
자동화
- 소스 저장소에 최신의 소스를 저장
- 전체소스를 다운로드
- 테스트
- Docker 이미지 생성
- Docker 이미지 저장
- 어플리케이션 업데이트
자동화 도구
- 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라는 언어를 사용함
- 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'){
}
}
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
생성한 이미지를 도커 허브에 저장함
- 도커 로그인 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}')
}
}
}
개선하기[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
'DevOps > Docker' 카테고리의 다른 글
[Docker] Dockerfile 명령어 사용 (0) | 2021.04.04 |
---|---|
[Docker]Dockerfile 구성 및 빌드 (0) | 2021.04.04 |
2. Docker 이미지 만들고 배포하기 (0) | 2021.04.04 |
1. Docker 컨테이너 실행 (0) | 2021.04.04 |
Docker Container 관리툴[docker cli dashboard] (0) | 2021.04.04 |
- Total
- Today
- Yesterday
- 마이바티스
- SSL인증서
- SSL
- 인증서자동갱신
- nginx
- elk8.x
- 인증서설치
- Letsencrypt wildcard
- Letsencrypt+nginx
- 마이바티스CamelCase
- Letsencrypt wildcard auto renew
- camelcase
- logstash
- letsencrypt
- #logback
- Intellj들여쓰기
- letencrypt
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |