EC2의 Docker 안에 Jenkins Container 올리기 (feat. Webhook CI/CD 구축 & DooD)
Keyword
1. DooD
2. docker network
Flow
1. 새로운 EC2 인스턴스 생성 -> 젠킨스만
2. docker 설치
3. docker 컨테이너 젠킨스 실행
4. 서브 도메인 세팅
5. 젠킨스 Webhook 연결
6. 파이프라인 작성 후 배포 확인
EC2 인스턴스 설정
EC2 인스턴스 설정
용량: 최소 100GB
EC2 인스턴스 생성:
- AWS 콘솔에 로그인하고 EC2 서비스를 선택합니다.
- "인스턴스 시작" 버튼을 클릭하여 새로운 인스턴스를 생성합니다.
- Ubuntu AMI를 선택합니다.
- 인스턴스 유형을 선택합니다 (t2.micro는 무료 티어로 사용할 수 있습니다). → t2.medium.
- 키 페어를 설정하고 다운로드합니다.
- 인스턴스를 시작합니다.
보안 그룹 설정:
- SSH (포트 22), HTTP (포트 80), HTTPS (포트 443) 및 애플리케이션 포트 (예: 8080)를 허용합니다.
Docker 설치
* jenkins 실행을 위해 새로 만든 EC2 인스턴스에서 진행
시스템 업데이트 및 업그레이드:
sudo apt update -y
sudo apt upgrade -y
Docker 설치:
sudo apt install -y docker.io
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER
DooD를 이용한 Jenkins 실행
* jenkins 실행을 위해 새로 만든 EC2 인스턴스에서 진행
Jenkins용 Dockerfile 만들기
Dockerfile 생성:
vi Dockerfile
작업 모드로 변경: i
Dockerfile 작성:
FROM jenkins/jenkins:2.452.3
USER root
RUN apt-get -y update && \
apt-get -y install apt-transport-https ca-certificates curl gnupg-agent software-properties-common && \
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - && \
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" && \
apt-get -y update && \
apt-get -y install docker-ce docker-ce-cli containerd.io
RUN usermod -u 1000 jenkins && \
groupmod -g 113 docker && \
usermod -aG docker jenkins
USER jenkins
저장하고 나가기: ECS + :wq
Jenkins docker 실행
Jenkins docker 이미지 빌드:
- 앞서 작성한 Dockerfile이 있는 디렉토리에서 빌드
docker build -t jenkins .
jenkins 컨테이너 실행:
docker run -d --name jenkins -p 8080:8080 -p 5000:5000 jenkins/jenkins:2.452.3
DNS 설정 (서브 도메인 설정)
도메인 등록 기관의 DNS 관리 페이지로 이동합니다.
새 CNAME 레코드를 추가합니다:
- 이름: 원하는 서브도메인 (예: jenkins)
- 값: EC2 인스턴스의 퍼블릭 DNS (예: ec2-xx-xx-xx-xx.compute-1.amazonaws.com)
변경 사항이 적용되기까지 몇 분 정도 소요될 수 있습니다.
Jenkins 접속 및 초기 세팅
Jenkins 접속
Jenkins 메인 페이지 접근 확인:
- 브라우저에서 서브 도메인 주소를 입력하여 Jenkins에 접근
- ex) http://jenkins.yourdomain.com:8080
초기 관리자 비밀번호 얻기:
* 해당 과정은 Jenkins의 EC2 인스턴스에서 진행
docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
초기 세팅
추천 플러그인 설치:
가장 간단한 방법은 "Install suggested plugins" 옵션을 선택하는 것입니다. 이는 Jenkins 커뮤니티가 유용하다고 여기는 플러그인을 자동으로 설치합니다.
- "Install suggested plugins" 클릭
Github Webhook 설정
Jenkins 관리 페이지에서 플러그인 설치
플러그인 설치 페이지로 이동:
- Jenkins 메인 페이지에서 "Manage Jenkins" -> "Manage Plugins"로 이동
플러그인 설치:
- "Available" 탭에서 "GitHub Integration Plugin"을 검색하여 설치합니다.
- "Available" 탭에서 "Docker Pipeline" 플러그인을 검색하여 설치합니다.
- 설치 후 재시작 중요!
- jenkins EC2 인스턴스에 들어가서 jenkins 컨테이너 재시작
jenkins restart
GitHub 저장소에 Webhook 추가
GitHub 저장소로 이동하여 "Settings" -> "Webhooks"로 이동합니다.
"Add webhook"을 클릭하고 다음과 같이 설정합니다:
- Payload URL: http://<YOUR_JENKINS_URL>/github-webhook/
- Content type: application/json
- 이벤트: Just the push event
사진은 아직 연결되지 않은 상태
→ 초록색 아이콘으로 변경되어야 연결 완료!
Jenkins Pipeline 생성
Pipeline 생성
Jenkins 메인 페이지에서 "New Item"을 클릭하고 새 Job을 생성
"GitHub project" 옵션을 활성화하고 GitHub 저장소 URL을 입력
"Build Triggers" 섹션에서 "GitHub hook trigger for GITScm polling"을 선택
Pipeline 스크립트 작성 : 다음 단원에서 자세하게 다룰 예정
설정 저장:
- 모든 설정이 완료되면 Save 버튼을 클릭하여 설정을 저장
Pipeline script 작성
전체적인 흐름도
- 깃클론
- gradle build
- jar 파일과 dockerfile을 특정 경로로 이동
- 환경변수 파일 작성
- dockerfile을 이용해서 이미지 빌드(태그) → 현재 미적용된 내용
- 빌드된 이미지 도커 허브에 push
- ec2 인스턴스에 접속(제일 처음 만들었던 ec2)
- 현재 동작 중인 docker-compose 중지
- 도커 허브의 이미지 pull
- 새로 받은 이미지로 docker-compose 시작
- Jenkins의 메인화면에서 방금 만들었던 pipeline을 선택
- 왼쪽 목록에서 “configure” 선택
Pipeline script 작성:
pipeline {
agent any
environment {
// Docker Hub credentials
DOCKER_HUB_CREDENTIALS = 'docker-hub-credentials'
DOCKER_HUB_REPO = 'ysy561356/today-space-back'
DOCKER_CONTAINER = 'today-space-back'
// Github credentials
GIT_CREDENTIALS_ID = 'github-token'
GIT_BRANCH = 'release'
GIT_URL = 'https://github.com/today-space/today-space-back.git'
// EC2
SSH_CREDENTIALS_ID = 'ec2-ssh-key'
EC2_USER = 'ubuntu'
EC2_DOMAIN = 'back.today-space.com'
// DB
DB_URL = 'jdbc:postgresql://postgresql.today-space.com:5432/space'
DB_USERNAME = 'youngju'
DB_PASSWORD = '4564'
// JWT
JWT_SECRET_KEY = '7Iqk7YyM66W07YOA7L2U65Sp7YG065+9c3ByaW5n7Ius7ZmU7KO87LCo7JWE7JuD7IaM7Iux7ZSE66Gc7KCd7Yq47YyM7J2067iM66CI7KCE65Oc7YyAand0c2VjcmV0a2V5'
JWT_ACCESS_TOKEN_EXPIRATION = 1800000
JWT_REFRESH_TOKEN_EXPIRATION = 1209600000
// Social Login
KAKAO_REST_API_KEY = '6cd61cbdb917a1046bf437980b1e2ac9'
NAVER_REST_API_KEY = 'mgvICujU1zvQrtz_IQOA'
NAVER_REST_API_SECRET_KEY = 'drUb2lRtGb'
GOOGLE_REST_API_KEY = '878062679505-oh34mk7fm5mpipprk9o139i8if40b3qf.apps.googleusercontent.com'
GOOGLE_REST_API_SECRET_KEY = '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'
}
stages {
stage('Clone Git Repository') {
steps {
script {
// GitHub 저장소에서 코드 클론
git credentialsId: "${GIT_CREDENTIALS_ID}", branch: "${GIT_BRANCH}", url: "${GIT_URL}"
// gradlew 파일에 실행 권한 부여
sh 'chmod +x gradlew'
// Gradle 빌드
sh './gradlew build'
// destination 디렉토리 생성
sh 'mkdir -p ../destination'
sh 'mkdir -p ../destination/build/libs'
// JAR 파일과 Dockerfile을 특정 경로로 이동
sh 'cp build/libs/*.jar ../destination/build/libs'
sh 'cp Dockerfile ../destination/'
}
}
}
stage('Prepare .env') {
steps {
script {
sh '''
touch ../destination/back.env
echo DB_URL=$DB_URL > ../destination/back.env
echo DB_USERNAME=$DB_USERNAME >> ../destination/back.env
echo DB_PASSWORD=$DB_PASSWORD >> ../destination/back.env
echo JWT_SECRET_KEY=$JWT_SECRET_KEY >> ../destination/back.env
echo JWT_ACCESS_TOKEN_EXPIRATION=$JWT_ACCESS_TOKEN_EXPIRATION >> ../destination/back.env
echo JWT_REFRESH_TOKEN_EXPIRATION=$JWT_REFRESH_TOKEN_EXPIRATION >> ../destination/back.env
echo KAKAO_REST_API_KEY=$KAKAO_REST_API_KEY >> ../destination/back.env
echo NAVER_REST_API_KEY=$NAVER_REST_API_KEY >> ../destination/back.env
echo NAVER_REST_API_SECRET_KEY=$NAVER_REST_API_SECRET_KEY >> ../destination/back.env
echo GOOGLE_REST_API_KEY=$GOOGLE_REST_API_KEY >> ../destination/back.env
echo GOOGLE_REST_API_SECRET_KEY=$GOOGLE_REST_API_SECRET_KEY >> ../destination/back.env
echo FRONT_URL=$FRONT_URL >> ../destination/back.env
echo KAKAO_PAY_SECRET_KEY=$KAKAO_PAY_SECRET_KEY >> ../destination/back.env
echo KAKAO_PAY_URL=$KAKAO_PAY_URL >> ../destination/back.env
echo KAKAO_PAY_CID=$KAKAO_PAY_CID >> ../destination/back.env
'''
}
}
}
stage('Build and Push Docker Image') {
steps {
script {
dir('../destination') { // 특정 경로로 이동
// docker 이미지 빌드
def dockerImage = docker.build("${DOCKER_HUB_REPO}:latest", "./")
// Docker Hub에 로그인
withCredentials([usernamePassword(credentialsId: DOCKER_HUB_CREDENTIALS, usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
sh 'echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin'
}
// docker 이미지 push
dockerImage.push("latest")
}
}
}
}
stage('Deploy') {
steps {
script {
withCredentials([sshUserPrivateKey(credentialsId: "${SSH_CREDENTIALS_ID}", keyFileVariable: 'SSH_KEY')]) {
def sshKey = env.SSH_KEY
// 권한 변경
sh 'chmod 400 ' + sshKey
// docker-compose down
sh 'ssh -v -o StrictHostKeyChecking=no -i ' + sshKey + ' ${EC2_USER}@${EC2_DOMAIN} "docker-compose down"'
// docker image pull
sh 'ssh -v -o StrictHostKeyChecking=no -i ' + sshKey + ' ${EC2_USER}@${EC2_DOMAIN} "docker pull ${DOCKER_HUB_REPO}:${DOCKER_IMAGE_TAG}"' // 이미지 명시
// 새로운 docker-compose 시작
sh 'ssh -v -o StrictHostKeyChecking=no -i ' + sshKey + ' ${EC2_USER}@${EC2_DOMAIN} "docker-compose up -d"'
}
}
}
}
}
post {
always {
cleanWs()
}
success {
echo 'Deployment successful!'
}
failure {
echo 'Deployment failed!'
}
}
}
Credentials 생성
Jenkins 메인 화면에서 “Manage Jenkins” > “Credentials” > “global” > “Add Credentials”
Docker Hub credentials:
- Kind : Username with password
- Username : Docker Hub ID
- Password : Docker Hub Password
- ID : Jenkins에서 변수로 사용할 ID (ex: docker-hub-credentials)
Github credentials:
- Kind : Username with password
- Username : GitHub ID
- Password : GitHub Token
- ID : Jenkins에서 변수로 사용할 ID (ex: github-token)
EC2 credentials:
- Kind : SSH Username with private key
- ID :
- Username : ubuntu
- EC2 인스턴스의 hostname(기본적으로 linux라면 기본적으로 ubuntu)
- Pricate Key : EC2 인스턴스 생성하면서 .pem 파일을 다운 받았을텐데 해당 파일을 열어보면 사진과 같이 나오는 글자들을 전부 복사 붙여넣기
- 단, 마지막에 잘못해서 공백이 들어가면 안된다.
- Mac : 터미널에서 cat [.pem 파일 마우스로 끌어서 터미널에 넣기]
- window : 메모장으로 확인하는 게 편하다
참고 자료
- Credentials 생성
- Jenkins 사용