middlemoon
AWS Bastion Host의 연결과 Jenkins 파이프라인 설계 - 프로젝트 (2) 본문
회사에서의 첫번째 과제를 성공적으로 마치고, 두번째 과제를 할당받아 프로젝트 회고를 하고자 한다.
진행했던 프로젝트의 구조의 도식화는 아래와 같다.
Point 1
[외부 Jenkins 서버(Domain: 회사도메인.ne.kr)]
│ SSH (22/tcp)
▼
[JenkinsEC2 = Bastion 역할 (Public Subnet, IP: 13.125.211.15)]
│ SSH (22/tcp, 사설 IP)
▼
[WebEC2 (Private Subnet, IP: 192.168.100.170)]
1. WEB EC2 app.jar, app.log 삭제 -> rm -rf app.jar / rm -rf app.log
2. ps -ef | grep app.jar , netstat -lntp 입력 후 포트 종료 확인
2. JenKins 들어가 빌드 실행
3.
. Java -jar로 실행 후 localhost:8080/api/health
Point 2
🌍 브라우저
↓ HTTP (80)
[Bastion EC2:13.125.211.15]
Nginx proxy_pass
↓
[Web EC2:192.168.100.170:8080]
Spring Boot → "Health Check 성공"

회사에서 사용하고 있는 Jenkins 서버는 AWS VPC를 아예 벗어난 곳이다.
Jenkins 서버에서 파이프라인으로 GitLab에 있는 소스를 가져와 Bastion EC2 부터 Web EC2 까지 불러들여온 소스를 가져와
Ping 체크에 성공하면 되는 프로젝트이다.
물론 온프렘으로 진행하였을 때 3-Tier Stacture로 진행하게 되면 Jenkins의 서버에 접속하여 Nginx Proxy 경유를 진행하여도 진행은 되지만 거의 모든 서버에 Migration 을 진행하여 AWS를 반드시 경유해야한다.
pipeline {
agent any
parameters {
string(name: 'KEY_FILE_NAME', defaultValue: 'mun-test-ec2.pem')
string(name: 'BASTION_HOST', defaultValue: '13.125.211.15')
string(name: 'WEB_HOST', defaultValue: '192.168.100.170')
string(name: 'JAR_PATH', defaultValue: './target/kong-0.0.1-SNAPSHOT.jar')
text(name: 'PEM_KEY_VALUE', defaultValue: '', description: 'Paste PEM content here (multi-line OK)')
}
environment {
APP_NAME = "kong"
WORK_DIR = "${WORKSPACE}/tmp_pem"
}
stages {
stage('Create PEM') {
steps {
sh '''
set -e
mkdir -p "$WORK_DIR"
PEM_PATH="$WORK_DIR/${KEY_FILE_NAME:-pem_key.pem}"
echo "=== Restoring PEM from parameter ==="
printf "%b" "$PEM_KEY_VALUE" > "$PEM_PATH"
chmod 600 "$PEM_PATH"
echo "[INFO] PEM created at $PEM_PATH"
ls -l "$WORK_DIR"
'''
}
}
stage('Checkout & Build') {
steps {
git branch: 'master',
credentialsId: 'gitlab-token',
url: 'https://gitlab.bizbee.ne.kr/sb/mwsun0303/test-api.git'
sh '''
chmod +x ./mvnw || true
./mvnw clean package -DskipTests
'''
}
}
stage('SCP: Office → Bastion') {
steps {
sh '''
echo "=== SFTP Office → Bastion ==="
scp -i "$WORK_DIR/$KEY_FILE_NAME" -o StrictHostKeyChecking=no \
"$JAR_PATH" ec2-user@"$BASTION_HOST":/home/ec2-user/app.jar
'''
}
}
stage('SCP: Bastion → Web EC2') {
steps {
sh '''
echo "=== SFTP Bastion → Web EC2 ==="
# Jenkins → Bastion 연결
ssh -i "$WORK_DIR/$KEY_FILE_NAME" -o StrictHostKeyChecking=no ec2-user@"$BASTION_HOST" '
echo "=== Copying app.jar to Web EC2 ==="
# Bastion 내부에서 PEM 내용을 직접 복원 (Jenkins의 PEM을 그대로 전달)
cat > /home/ec2-user/mun-temp.pem <<EOF
'"$PEM_KEY_VALUE"'
EOF
chmod 600 /home/ec2-user/mun-temp.pem
# Web EC2에 폴더 먼저 생성
ssh -i /home/ec2-user/mun-temp.pem -o StrictHostKeyChecking=no ec2-user@'"$WEB_HOST"' "mkdir -p /home/ec2-user/app"
# Bastion → Web EC2 파일 복사
scp -i /home/ec2-user/mun-temp.pem -o StrictHostKeyChecking=no \
/home/ec2-user/app.jar ec2-user@'"$WEB_HOST"':/home/ec2-user/app/app.jar
# Bastion 내 임시 PEM 삭제
rm -f /home/ec2-user/mun-temp.pem
'
'''
}
}
stage('Deploy on Web EC2') {
steps {
sh '''
echo "=== Deploy on Web EC2 ==="
ssh -i "$WORK_DIR/$KEY_FILE_NAME" -o StrictHostKeyChecking=no ec2-user@"$BASTION_HOST" '
cat > /home/ec2-user/mun-temp.pem <<EOF
'"$PEM_KEY_VALUE"'
EOF
chmod 600 /home/ec2-user/mun-temp.pem
ssh -i /home/ec2-user/mun-temp.pem -o StrictHostKeyChecking=no ec2-user@'"$WEB_HOST"' "
pkill -f 'java -jar' || true;
mkdir -p /home/ec2-user/app;
cd /home/ec2-user/app;
java -jar app.jar > app.log 2>&1 &
pgrep -f app.jar > /dev/null && echo '\''✅ App started'\'' || echo '\''❌ App start failed'\'';
"
rm -f /home/ec2-user/mun-temp.pem
'
'''
}
}
}
post {
always {
sh 'rm -rf "$WORK_DIR" || true'
echo "🧹 Cleaned up PEM"
}
success {
echo "✅ Build & Deploy complete!"
}
failure {
echo "❌ Build or Deploy failed — check logs."
}
}
}
📌 핵심 단계 요약
| 1️⃣ | Create PEM | Jenkins에서 전달받은 PEM 복원 |
| 2️⃣ | Checkout & Build | GitLab 소스 체크아웃 및 빌드 |
| 3️⃣ | SCP: Office → Bastion | 빌드된 JAR → Bastion 전송 |
| 4️⃣ | SCP: Bastion → Web EC2 | Bastion → Private Web EC2 전송 |
| 5️⃣ | Deploy on Web EC2 | Web EC2에서 JAR 실행 및 Health Check |
다음과 같이 파이프라인을 구성하였다.
Jenkins 빌드를 하게되면 아래와 같이 String_parameter로 받을 수 있도록 구성하였다.
Pem_key는 등록된 Jenkins_Credentals 에 등록된 Key로 가져온다.


등록 후 빌드된 파이프라인이 성공하게 된다.

192-168-100-10 (Bastion Private IP -> 192.168.100.170 WEB EC2 Private IP) 로 진입한다.
빌드를 성공하게 되면 app.jar로 서버가 정상실행되며 프로세스도 정상작동되는것을 확인할 수 있다.




curl로 현재 Web EC2 서비스가 제대로 돌고있는지 확인할 수 있으며,
13.125.211.15은 Bastion EC2 공인IP인데 Nginx의 url을 paramter 로 설정하여 특정 도메인으로 호출 시, 성공메세지가 나올 수 있도록 구성하였다.

Bastion EC2 의 공인IP에서 Web EC2 애플리케이션 서버가 구성될 수 있었던 이유는
Bastion 쪽에 Proxy를 경유하여 외부 서버에서도 확인할 수 있도록 구성하였다.
보통 실무에서는 보안이나, 기본 특성상 Web EC2에 있는 사설IP를 직접가져와 보는 경우는 희박하므로
공인IP로 한번 더 거쳐 서비스를 실행한다.
Proxy서버가 껴있다보니 성능, 보안, 비용 적으로 효율을 볼 수 있다는 점이 가장크다.
마무리하며..
무엇보다도 프로젝트를 진행하면서 가장 공부해보고 싶었던 AWS와 Jenkins를 위주로 학습을 하였다.
VPC생성부터, EC2 구성까지 그리고 Jenkins 서버와 연동하여 애플리케이션을 실행해보는 시간까져 가져보았다.
AWS를 배워 이번년도 까지 관련 자격증을 취득하고, CI/CD 툴을 직접적으로 다룰 수 있어 뜻깊은 프로젝트가 아니였나 싶다.
가장 많이 할애했던 부분은 파이프라인을 구성하는 부분에서 시간이 많이 걸리긴하였다.
Jenkins에서 서버를 두번이상 Traffic을 타야하니 쉽지많은 않았다.
하지만, Cloud Engineer 라면 꼭 거쳐야 하는 관문이라 생각이 되었다.
앞으로는 AWS의 더욱 다양한 기능을 다루게 될텐데 그 부분이 가장 기대가 된다.
꾸준히 발전하며 더 나은 엔지니가 될 수 있도록 노력해볼것이다 !
'DevOps > AWS' 카테고리의 다른 글
| AWS ECS( Elastic Container Service) 로 배포하기 (0) | 2026.01.09 |
|---|---|
| AWS ECR(Elastic Container Registry) 생성하기 (0) | 2026.01.06 |
| AWS Lambda, API Gateway 를 활용하여 비대칭키 발급 구현 (0) | 2025.12.04 |
| AWS 클라우드의 VPC 구성부터 EC2 생성까지 - 프로젝트(1) (1) | 2025.10.15 |
| AWS - EC2 인스턴스 생성해서 SSH에 AWS 연결하기(1) (0) | 2022.12.28 |