
상태 : RDS, S3등은 db와 직접 연결되어있음.
이번엔 docker hub없이 진행하려 함. 편리했지만. 1개의 이미지까지만 무료로 만들 수 있기 때문에, 이번엔 과금하고 싶지 않아 배제했다.
따라할 땐 트러블슈팅까지 읽고 따라하기
프리티어 방식보고 만듦

보안그룹 편집. https와 http 인스턴스 생성 시 허용하였고.
로컬에서 개발중인 프론트엔드의 접근을 위해 8080을 aniwhere ipv4로 열어주었음, 9090은 젠킨스 포트로 쓰기위해 열었음.
이후 할일:
1. 탄력적 IP 설정하기.
2. putty로 콘솔 접근하게 하기(선택)
한번 sudo apt update
# Java 17 설치
sudo apt update
sudo apt install openjdk-17-jdk -y
# 설치된 버전 확인
java -version
$ sudo apt-get update
# 패키지 인덱스 업데이트
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common
# Docker의 공식 GPG 키 추가
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# Docker 저장소 추가
$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
# 패키지 인덱스 업데이트
$ sudo apt-get update
# Docker CE 설치
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
# Docker 서비스 시작 및 자동 시작 설정
$ sudo systemctl start docker
$ sudo systemctl enable docker
# Docker 그룹에 사용자 추가
$ sudo usermod -aG docker ${USER}
# 확인
$ docker --version
하지않고 진행을 했다가, springboot를 빌드하는 과정에서 cpu 100% 찍고 죽는 경우를 여러번 겪었다. 직접적인 해결책이 된 것은 스왑 메모리였기에, 먼저 진행하자.
스왑공간은 메모리의 2배를 추천하는 듯하다.
t2.micro의 경우 RAM이 1GB밖에 없어서 스왑 공간을 2GB로 설정했음.

# 2GB 스왑 파일 생성
sudo dd if=/dev/zero of=/swapfile bs=128M count=16
# 권한 설정
sudo chmod 600 /swapfile
# 스왑 파일 포맷
sudo mkswap /swapfile
# 스왑 활성화
sudo swapon /swapfile
# 상태 확인
free -h
# 재부팅 후에도 스왑 유지
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
sudo docker pull jenkins/jenkins:lts 를 사용해서 docker 이미지로 받는 것과 비교해서 사용하자! docker in docker 문제가 있다지만 간편할 수도 있다고 함!
전 일단 호스트에서 설치 직접했음
$ curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io.key | sudo tee \
/usr/share/keyrings/jenkins-keyring.asc > /dev/null
$ echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
https://pkg.jenkins.io/debian-stable binary/ | sudo tee \
/etc/apt/sources.list.d/jenkins.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install jenkins
# 확인
$ sudo systemctl status jenkins
# jenkins 권한 주기
sudo usermod -aG docker jenkins
$ sudo systemctl start jenkins
# 초기 비밀번호 확인
$ sudo cat /var/lib/jenkins/secrets/initialAdminPassword
8080은 스프링부트 줄거라서 jenkins 포트번호 변경함
참고
# 난 vim이 좋아서
$ sudo apt-get install vim
$ sudo vi /etc/sysconfig/jenkins
들어가서 원하는 포트로 변경
JENKINS_PORT="9090"
sudo vim /usr/lib/systemd/system/jenkins.service
들어가서 포트 변경
Environment="JENKINS_PORT=9090"
재실행 하기
# 하라고 뜸
sudo systemctl daemon-reload
# 재시작
sudo systemctl restart jenkins
이제 ip주소:9090로 들어가서 초기비밀번호 입력 후
suggested plugins를 설치한 후 admin 생성하고 URL 또한 ip주소:9090으로 설정
블로그에선
[설치 플러그인]
- Generic Webhook Trigger
- Gitlab
- Gitlab API
- Gitlab Authentication
- Mattermost Notification
- Docker pipeline
설치하라고 해서 설치했음. 그런데 Docker pipeline에서 실패 떴음.
Authentication Tokens API가 실패로 뜨고 Docker Commons와 Docker Pipeline이 줄줄이 실패하였다.
앞에 두개는 Docker pipeline를 설치하려면 자동으로 오는듯한데 실패뜬다.
-> Authentication Tokens API 부터 하나씩 따로 설치하면 되는 듯하다.
(각 프로젝트마다, 각각 연결 필요함!)
프로젝트 들어감 -> Settings -> Access Tokens에서 만들기

설정함. 그리고 생성된 토큰 바로 복사해두기
Jenkins관리 -> System -> Gitlab Connections 탭에서\

+add 눌러서

설정함.
Secret token에서 Generate해서 토큰 저장

아마 근데 branch 특정은 여기서 안하고 gitlab에서 설정해도 충분함 근데 그냥햇음
^master$ 넣음 (배포 대상이 될 branch 넣기)
숨기려는 파일 업로드
Jenkins 관리 -> Credentials -> Stores scoped to Jenkins의 (glabal) ->Add Credentails
Secret file선택 후 업로드

Jenkins file에 적용된 부분:
stage('Secrets Setup') {
steps {
withCredentials([
file(credentialsId: 'prod-yaml', variable: 'prodFile'),
file(credentialsId: 'secret-yaml', variable: 'secretFile')
]) {
sh '''
cp "$prodFile" src/main/resources/application-prod.yml
cp "$secretFile" src/main/resources/application-secret.yml
chmod 644 src/main/resources/application-*.yml
'''
}
}
}
FROM openjdk:17-jdk-slim
ARG JAR_FILE=build/libs/*.jar
# jar 파일 복제
COPY ${JAR_FILE} app.jar
# 실행 명령어
ENTRYPOINT ["java", "-Dspring.profiles.active=prod,secret", "-jar", "app.jar"]
적용하고 효과는 없었지만, 굳이 메모리 스왑을 적용하고 위 사항을 제거하진 않았음.
Jenkinsfile:
pipeline {
agent any
environment {
APP_NAME = 'jenkins-test'
DOCKER_IMAGE = 'jenkins-test:latest'
GRADLE_OPTS = '-Dorg.gradle.jvmargs="-Xmx256m"' // Gradle 메모리 제한
}
stages {
stage('Prepare Environment') {
steps {
sh '''
rm -rf src/main/resources
mkdir -p src/main/resources
chmod 777 src/main/resources
# Docker 캐시 정리
docker system prune -f
'''
}
}
stage('Secrets Setup') {
steps {
withCredentials([
file(credentialsId: 'prod-yaml', variable: 'prodFile'),
file(credentialsId: 'secret-yaml', variable: 'secretFile')
]) {
sh '''
cp "$prodFile" src/main/resources/application-prod.yml
cp "$secretFile" src/main/resources/application-secret.yml
chmod 644 src/main/resources/application-*.yml
'''
}
}
}
stage('Build') {
steps {
sh '''
chmod +x gradlew
# gradle 빌드 시 테스트 스킵 (-x test)
# 메모리 제한 (256m)
# 데몬 비활성화 (--no-daemon)
./gradlew clean build -x test --no-daemon --max-workers 2 -Dorg.gradle.jvmargs="-Xmx256m"
'''
}
}
stage('Docker Build & Deploy') {
steps {
script {
sh 'docker rm -f ${APP_NAME} || true'
sh 'docker rmi ${DOCKER_IMAGE} || true'
sh 'docker build --memory=512m --memory-swap=512m -t ${DOCKER_IMAGE} .'
sh '''
docker run -d \
--name ${APP_NAME} \
--restart unless-stopped \
--memory=512m \
--memory-swap=512m \
-p 8080:8080 \
${DOCKER_IMAGE}
'''
}
}
}
}
}
docker 권한 없음
ubuntu@ip-172-31-14-202:~$ docker ps
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.47/containers/json": dial unix /var/run/docker.sock: connect: permission denied
# docker 그룹에 ubuntu 사용자 추가
sudo usermod -aG docker ubuntu
# 변경사항 적용을 위해 다음 중 하나 실행:
# 1. 새로운 그룹 적용
newgrp docker
# 또는
# 2. EC2에 재접속
# 그 후 다시 시도
docker ps
파일 복사 권한 없음 (상기 Jenkinsfile에 적용되어 있음
+ cp **** src/main/resources/application-prod.yml
cp: cannot create regular file 'src/main/resources/application-prod.yml': Permission denied
Jenkinsfile에서 Jenkins가 resources 접근하도록 Prepare Environment 작성
stage('Prepare Environment') {
steps {
sh '''
rm -rf src/main/resources
mkdir -p src/main/resources
chmod 777 src/main/resources
'''
}
}
방법
1. 테스트 스킵 (테스트가 없다면..?)
stage('Build') {
steps {
sh '''
chmod +x gradlew
./gradlew clean build -x test --no-daemon --max-workers 2 -Dorg.gradle.jvmargs="-Xmx256m"
'''
}
}
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:testdb;MODE=MySQL
username: sa
password:
테스트에 application-secret.yml이 인식 안됨
TripggApplicationTests > contextLoads() FAILED
java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:180
Caused by: org.springframework.beans.factory.BeanCreationException at AbstractBeanFactory.java:326
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException at ConstructorResolver.java:795
Caused by: org.springframework.beans.factory.BeanCreationException at ConstructorResolver.java:648
Caused by: org.springframework.beans.BeanInstantiationException at SimpleInstantiationStrategy.java:178
Caused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException at DataSourceProperties.java:186
그냥 secret file로 관리하고 싶어서, 민감변수를 application-prod.yml에 담아 credentail로 관리하였음.
본 프로젝트에서는 민감변수를 따로 처리하지 않았으나, 처리한다면 상단과 같은 방식으로 env파일 을 Jenkins Credential에서 관리하면 됨
Vue를 빌드한 결과물을 Nginx의 정적파일로 마운트하여 사용. Nginx에 https 적용, /api uri는 8080포트로 프록시하도록 적용
순서
nginx 설치 -> nginx 설정 수정 -> Https 발급(Let's Encrypt) -> nginx 중지 -> Docker로 nginx 생성 후 실행(기존 nginx설정, https 파일 마운트)
# Nginx 설치
sudo apt update
sudo apt install nginx
# Nginx 상태 확인
sudo systemctl status nginx
nginx 설정 수정
sudo vi /etc/nginx/sites-available/default
(vim 설치 안되어있으면 설치하고!)
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name 도메인(ex.tripggukgguk.site);
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
nginx 설정 문법 체크
sudo nginx -t
nginx 시작
sudo systemctl start nginx
sudo systemctl enable nginx
자동 갱신은 적용하지 않았음
# certbot 설치
sudo apt install certbot python3-certbot-nginx
# 인증서 발급 (자동으로 Nginx 설정도 수정됨)
sudo certbot --nginx -d 도메인(ex.tripggukgguk.site)
자동갱신 방법(난 적용하지 않아서 되는진 모름)
# 갱신 스크립트 생성
sudo vim /home/ubuntu/nginx/renew-cert.sh
# 아래 적용
#!/bin/bash
docker stop nginx
certbot renew --quiet
docker start nginx
# 스크립트 권한 설정
chmod +x /home/ubuntu/nginx/renew-cert.sh
# crontab 설정
sudo crontab -e
# 아래 라인 추가 (매월 1일 새벽 3시에 실행)
0 3 1 * * /home/ubuntu/nginx/renew-cert.sh
이전에 만들었던 사진 첨부
/api 로 들어오는 요청 프록시. 다른 경우 바꿀것

# 디렉토리 생성
mkdir -p /home/ubuntu/nginx/html
mkdir -p /home/ubuntu/nginx/conf
# 소유권 설정
sudo chown -R ubuntu:ubuntu /home/ubuntu/nginx
# 호스트의 nginx 중지
sudo systemctl stop nginx
sudo systemctl disable nginx
nginx 컨테이너 실행
# 이미지 받기
docker pull nginx
# Nginx 컨테이너 실행
docker run -d --name nginx \
--network host \
-v /home/ubuntu/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro \
-v /etc/letsencrypt:/etc/letsencrypt:ro \
-v /home/ubuntu/nginx/html:/usr/share/nginx/html \
nginx:stable
# 테스트
echo "<h1>HTTPS Test</h1>" > /home/ubuntu/nginx/html/index.html
Jenkins설정
# Jenkins 사용자에게 필요한 권한 부여
sudo vim /etc/sudoers.d/jenkins
# 아래 내용 추가
jenkins ALL=(ALL) NOPASSWD: /bin/rm -rf /home/ubuntu/nginx/html/*
jenkins ALL=(ALL) NOPASSWD: /bin/cp -r /var/lib/jenkins/workspace/*/dist/* /home/ubuntu/nginx/html/
jenkins ALL=(ALL) NOPASSWD: /bin/chown -R ubuntu:ubuntu /home/ubuntu/nginx/html
jenkins ALL=(ALL) NOPASSWD: /bin/chmod -R 755 /home/ubuntu/nginx/html
pipeline {
agent any
environment {
// 배포 경로 설정
DEPLOY_PATH = '/home/ubuntu/nginx/html'
}
tools {
nodejs 'node18' // Jenkins에서 설정한 NodeJS 이름과 일치해야 함
}
stages {
stage('Prepare') {
steps {
// 버전 확인
sh '''
node --version
npm --version
'''
// 기존 node_modules 제거 (캐시 클린)
sh 'rm -rf node_modules'
}
}
stage('Install Dependencies') {
steps {
// 의존성 설치
sh 'npm install'
}
}
stage('Build') {
steps {
// Vite 프로젝트 빌드
sh 'npm run build'
}
}
stage('Deploy') {
steps {
// 기존 파일 제거 및 새 파일 복사
sh '''
# 기존 파일 제거
rm -rf ${DEPLOY_PATH}/*
# 빌드 결과물 복사 (Vite는 기본적으로 dist 폴더에 생성)
cp -r dist/* ${DEPLOY_PATH}/
'''
}
}
}
post {
always {
// 작업 디렉토리 정리
cleanWs()
}
}
}
jenkinsfile에 명시한 nodejs와 동일한 버전설치.
검색해보고 따라할것. 버전은 해당 버전의 LTS버전 설치하였음(nodejs-18중 lts)
+ sudo rm -rf /home/ubuntu/nginx/html/*
+ sudo cp -r dist/assets dist/favicon.ico dist/index.html /home/ubuntu/nginx/html/
sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper
sudo: a password is required
[Pipeline] }
Jenkins에게 권한을 주고 sudo를 쓰지말아야함(현재 jenkinsfile에서는 sudo 안씀) sudo 쓰면 jenkins가 아니라 root로 실행하는데, 권한을 jenkins에게 줘서 정상 동작 x
아래 명령어로 소유자 변경(생성/수정/삭제 위해서)
sudo chown -R jenkins:jenkins /home/ubuntu/nginx/html
sudo chmod -R 755 /home/ubuntu/nginx/html
+ rm -rf /home/ubuntu/nginx/html/*
rm: cannot remove '/home/ubuntu/nginx/html/*': Permission denied
해결: 상위 디렉토리부터 jenkins에게 권한을 주도록 했음
sudo chmod 755 /home/ubuntu
sudo chmod 755 /home/ubuntu/nginx
sudo chmod -R 755 /home/ubuntu/nginx/html
# conf 디렉토리는 ubuntu 소유 유지
sudo chown -R ubuntu:ubuntu /home/ubuntu/nginx/conf
sudo chmod -R 755 /home/ubuntu/nginx/conf
sudo -u jenkins ls -la /home/ubuntu/nginx/html 이 명령어로 접근 가능한지 확인 가능
-R태그는 하위 디렉토리까지 적용
참고:
https://moon1z10.github.io/development/AWS-Ubuntu-Docker-Installation/
https://velog.io/@carrot1st/Jenkins-%ED%8F%AC%ED%8A%B8%EB%B2%88%ED%98%B8-%EB%B3%80%EA%B2%BD
규바는 신이다.