앞서 공부하고 CODEBOX 프로젝트에 적용해보았던
Jenkins Pipeline
에Docker
기술을 적용하여 확장해볼것이다.
Jenkins Pipeline 구축: https://velog.io/@rnqhstlr2297/Jenkins%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-Pipeline-%EA%B5%AC%EC%B6%95
Jenkins server
, Deploy(운영) server
가 각각의 AWS EC2를 구성한다.
deploy.sh
실행 명령어를 한다.deploy.sh
:DockerHub에서 이미지를 받아오고 컨테이너로 실행하는 쉘)Spring 프로젝트 코드로는 CODEBOX 프로젝트의 코드를 사용할 것이다.
FROM openjdk:11-jre
COPY build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
프로젝트 파일안에 Dockerfile을 생성한다.
# 가동중인 awsstudy 도커 중단 및 삭제
docker ps -a -q --filter "name=codebox" | grep -q . && docker stop codebox && docker rm codebox | true
# 기존 이미지 삭제
docker rmi bonsik/codebox-project-images:1.0
# 도커허브 이미지 pull
docker pull bonsik/codebox-project-images:1.0
# 도커 run
docker run -d -p 8888:8888 --name codebox bonsik/codebox-project-images:1.0
# 사용하지 않는 불필요한 이미지 삭제 -> 현재 컨테이너가 물고 있는 이미지는 삭제되지 않습니다.
docker rmi -f $(docker images -f "dangling=true" -q) || true
운영 서버에 명시해논 쉘 스크립트이다.
추후 jenkins에서 운영 서버로 ssh를 이용한 실행 명령어로 해당 셀 스크립트를 실행할것이다.
운영 EC2 서버에서 Jenkins가 dockerHub에 push한 이미지를 가져와서 컨테이너로 띄울것이다.
그러므로 Docker가 설치되어있어야된다.
또한 운영 EC2 서버의 OS가 Ubuntu 이므로 Ubuntu에 Docker를 설치하는 방식을 사용하였다.
//먼저 기본 중에 기본. 패키징 툴(apt-get)을 업데이트, 업그레이드 시켜주자.
apt update & apt upgrade
//Docker 설치에 필요한 필수 패키지를 설치해주자.
apt-transport-https ca-certificates curl gnupg-agent software-properties-common
//Docker의 GPC Key 인증을 하자.
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
// Docker Repository를 등록해보자. 이는 Docker 환경을 구축할 때 필수적인 절차이다.
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
//apt-get 패키징 툴을 통해 도커를 설치하자.
sudo apt-get update && sudo apt-get install docker-ce docker-ce-cli containerd.io
//설치된 도커 버전 확인
docker -v
//Docker를 실행해보자.
sudo systemctl enable docker
//Docker 활성화
sudo service docker start
// /var/run/docker.sock 파일의 권한을 666으로 변경하여 그룹 내 다른 사용자도 접근 가능하게 변경
sudo chmod 666 /var/run/docker.sock
//ubuntu 유저를 docker 그룹에 추가
//-> 추구 운영서버에서 "sudo"명령어 없이 "ubuntu"유저가 docker 명령어를 사용할수 있음
sudo usermod -a -G docker ubuntu
추후 jenkins EC2 server
에서 운영 EC2서버
로 쉘 시작 명령어를 할수 있게 해야한다.
그러니 SSH을 이용한 접근이 가능하도록 jenkins ec2 server ip/port
를 보안 규칙에 추가한다.
Jenkins를 EC2로 이용해 구축하는걸 찾다보니 EC2로 젠킨스 서버로 프리티어로 구축하게 되면 메모리 부족 문제가 발생한다고 한다.
젠킨스로 도커를 띄우고 실행하는것에는 문제없으나 깃허브 웹훅을 이용해 젠킨스로
Spring 어플리케이션을 clone하여 빌드과정에서 메모리 부족 문제로 EC2가 멈추는 현상이 발생한다고 한다.
그래서 하드디스크를 가상 메모리로 전환시키면 해결할수 있다고 하였다.
//스왑 파일 생성하기
sudo dd if=/dev/zero of=/swapfile bs=128M count=16
//스왑 파일에 대한 일기 쓰기 권한 업데이트하기
sudo chmod 600 /swapfile
//Linux 스왑 영역 설정하기
sudo mkswap /swapfile
//스왑 공간에 스왑 파일을 추가하여 스왑 파일을 즉시 사용할 수 있도록 하기
sudo swapon /swapfile
//확인하기
sudo swapon -s
// /etc/fstab 파일을 편집하여 부팅 시 스왑 파일을 활성화하기
sudo vi /etc/fstab
//파일 가장 마지막에 다음을 추가하고 :wq로 저장하고 종료
/swapfile swap swap defaults 0 0
//메모리 확인하기
free
Jenkins를 Docker를 이용하여 띄울것이기 때문에 마찬가지로 Docker를 설치해야된다.
Jenkins EC2 서버는 OS가 Amazon Linux 이고 해당 OS에 Docker를 설치하는 방식을 사용하였다.
//패키지 업데이트
sudo yum -y upgrade
//도커 설치
sudo yum -y install docker
//도커 설치 작업이 잘 되었는지 버전 확인
docker -v
//도커 시작
sudo service docker start
//도커 그룹에 사용자 추가 -> docker가 그룹명, ec2-user가 사용자명
sudo usermod -aG docker ec2-user
Jenkins EC2 서버에서 이미지를 push 하기전까지의 시나리오를 정리하자면
1. Docker를 이용하여 Jenkins를 실행
2. Jenkins안에서 프로젝트의 Dockerfile를 빌드하여 이미지 생성
3. 생성한 이미지를 DockerHub로 push
이러한 순서로 정리될수 있다.
이러한 순서로 하려면 도커위에서 도커를 실행시켜야된다. 이러한 방식에는 2가지 방식이 있는데 DidD(Docker in Docker
, DooD(Docker Out of Docker
이 있다.
Docker 측에서는 DooD
방식을 권장하는거 같다.
결론적으로는 컨테이너를 띄우기전 -v 옵션으로 호스트의 docker socker을 사용하도록 하면 된다고 한다. 추후 이 2가지의 차이점도 공부해야겠다.🤣
Jenkins Docker 이미지를 이용한 Jenkins 설치와 도커내 도커 설치를 Dockerfile로 만들었다. 이부분은 거의 그대로 참고하였다.🤣어렵다ㅠㅠ
//Dockerfile 생성
sudo vim Dockerfile
//Dockerfile 작성 시작 - bash를 Jenkins 이미지로
FROM jenkins/jenkins:jdk11
//도커를 실행하기 위한 root 계정으로 전환
USER root
//도커 설치(도커내 도커)
COPY docker_install.sh /docker_install.sh
RUN chmod +x /docker_install.sh
RUN /docker_install.sh
//도커 그룹에 사용자 추가
RUN usermod -aG docker jenkins
USER jenkins
아래는 도커내 도커를 설치하는 파일이다.
//docker_install.sh 파일 생성
sudo vim docker_install.sh
//docker_install.sh 파일 작성 시작
#!/bin/sh
apt-get update && \
apt-get -y install apt-transport-https \
ca-certificates \
curl \
gnupg2 \
zip \
unzip \
software-properties-common && \
curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
$(lsb_release -cs) \
stable" && \
apt-get update && \
apt-get -y install docker-ce
두파일을 같은 디렉토리 위치에 두고 Dockerfile를 빌드하기전 해야할일이 있다.
앞서 도커안에 도커는 host의 docker.sock를 빌려서 사용하는데
따라서 host의 docker.sock의 파일 권한을 변경하여 다른 사용자도 접근 가능하도록 해야된다.
//파일 권한 변경
sudo chmod 666 /var/run/docker.sock
//Dockerfile 빌드
docker build -t jenkins .
이제 마지막으로 앞서 생성한 Jenkins 이미지를 컨테이너로 띄우면 된다.
sudo docker run -d --name jenkins \
-v /var/run/docker.sock:/var/run/docker.sock \
-p 8080:8080 \
-e TZ=Asia/Seoul \
jenkins
-d: 백그라운드로 실행한다는 의미
--name: 띄운 컨테이너의 이름
-v: 이부분이 호스트의 docker socker을 사용하도록 하는 부분
-p: 포트 매핑 ->-p <호스트 시스템의 포트번호>:<컨테이너 안의 포트>
마지막(jenkins): 컨테이너로 띄울 도커 이미지 이름
앞서 포스터인 Jenkins Pipeline 에서도 GitHub Webhooks
를 사용했다.
그때는 로컬로 Jenkins 서버를 띄었기 때문에 로컬 서버 ip/port를 외부에 노출시키기 위해 ngrok
프로그램을 사용했다.
하지만 지금은 EC2로 Jenkins server를 구축했기 때문에 Github Webhooks
의
Payload URL 수정해야한다.
또한 Jenkins EC2 서버로Github Webhooks
로 부터 오는 api를 받을수 있게
EC2 인바운드 규칙을 편집해야 된다.
위 두 IP가 GitHub 서버의 IP라고 한다.
앞서 포스터인 Jenkins Pipeline에서 했던거와 같이 Jenkins에 필요한 설정, Credentials, 플러그인 설치등을 해주었다.
추가로 Jenkins에서 DockerHub로 이미지를 Push 해주어야하기 때문에 도커 계정을
Credentials에 등록해주었다.
앞서 포스터인 Jenkins Pipeline에서 작성했던 Pipeline 스크립트를 기반으로 수정하였다.
pipeline {
environment {
repository = "bonsik/codebox-project-images" //docker hub id와 repository 이름
DOCKERHUB_CREDENTIALS = credentials('dockerhub') // jenkins에 등록해 놓은 docker hub credentials 이름
}
agent any
stages {
stage('github clone') {
agent any
steps {
git branch: 'main',
credentialsId: 'github',
url: 'https://github.com/YNCB/backEnd.git'
}
post {
failure{
error "Fail Cloned Repository"
}
}
}
stage('Test project') {
agent any
steps{
withGradle{
sh '''
echo 'start Test'
chmod +x gradlew
./gradlew test
'''
}
}
post{
failure{
error 'Fail Test'
}
}
}
stage('Gradle Project Build') {
agent any
steps{
sh '''
echo 'start bootJar'
./gradlew clean bootJar
'''
}
post{
failure{
error 'Fail Gradle Build'
}
}
}
stage('Docker Image Build') {
agent any
steps{
sh '''
echo 'docker image build'
docker build -t $repository:1.0 .
'''
}
post{
failure{
error 'Fail Docker Image Build'
}
}
}
stage("Dockerhub Login") {
agent any
steps{
sh '''
echo 'dockerhub login'
echo $DOCKERHUB_CREDENTIALS_PSW | docker login -u $DOCKERHUB_CREDENTIALS_USR --password-stdin
'''
}
post{
failure{
error 'Fail Docker Login'
}
}
}
stage("Upload image to dockerHub"){
agent any
steps{
sh '''
echo 'dockerhub image deploy'
docker push $repository:1.0
'''
}
post{
failure{
error 'Fail Docker Image Deploy'
}
}
}
stage('Cleaning up'){
agent any
steps{
sh '''
echo 'docker image delete'
docker rmi $repository:1.0
'''
}
post{
failure{
error 'Fail Cleaning up'
}
}
}
stage('Execuate image'){
agent any
steps {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'deploy_server',
transfers: [
sshTransfer(cleanRemote: false,
excludes: '',
execCommand:
'''echo [dockerhub password 작성] | docker login -u [dockerhub 계정 작성] --password-stdin
sh docker_deploy.sh ''',
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '',
remoteDirectorySDF: false,
removePrefix: '',
sourceFiles: 'docker_deploy.sh')],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
]
)
}
}
}
}
DockerHub에 이미지를 Push하기 위해서 DockerHub 계정을 환경변수로 정의하였다.
Window환경에서 작업시 Gradlew의 기본권한이 644로 설정되다고 하여서 ./gradlew test
, ./gradlew build
를 위해서 chmod +x gradlew
를 통해 권한을 변경하였다.
DockerHub
에 이미지를 Push하기 위해서는 이미지의 이름이 계정/레포지토리
의 이름과 같아야 하기때문에 이미지 빌드시 docker build -t $respository:1.0
으로 도커 이미지 이름을 만들었다.
DockerHub
에 이미지를 Push 하는 저장소가 private 저장소 이므로 DockerHub
에 로그인하는 과정을 넣었다.
생성한 도커 이미지를 DockerHub
에 Push한 다음 생성한 이미지를 삭제하기 위해 삭제 과정을 넣었다.
SSH를 이용하여 운영서버로 실행할 명령어를 작성했다.
DockerHub
로 부터 이미지를 Pull하기 위해서 로그인 절차deploy.sh(도커허브로 부터 이미지 pull, 컨테이너 시작등)
실행 절차SSH란?
: Secure Shell Protocal 약자로 네트워스 상의 다른 컴퓨터에 로그인하거나 원격 시스템에서 명령을 실행하고 다른 시스템으로 파일을 복사할수 있도록 해주는 프로토콜움 sshPublisher에 execCommand에 앞서 정의한 환경변수를 사용못하는건가?..움 선언형 문법을 아직 잘몰라서 나중에 시간이 되면 공부해봐야겠다.🤣
github push시 github webhooks로 jenkins에게 알림
도움이됐습니다 고마워요