
작업 환경: m1 맥미니
전반적인 내용은 최주호 강사님의 aws 강의를 기반으로 한다.
freetier 사용중
구글 계정을 새로 만들고, 아마존에 가입하면 1년간 프리티어 요금제로 무료로 사용할 수 있다. EC2 인스턴스 1개, RDS 1개로 포트폴리오 1개 올리기에는 딱이다.
개인 도메인을 연결하기 위해서는 구매해야해서 약간의 지출이 필요하다.
EC2에서 인스턴스를 하나 생성하고, 클릭해보면 Connect 라는 버튼이 있다.
그럼 EC2 Instance Connect, Session Manager, SSH Client, EC2 Serial console 중에서 SSH Client 를 클릭해서 나오는 방법 그대로 따라하면 된다.
ssh -i ${.pem 파일이름} ubuntu@${Public IPv4 DNS}권한이 없어서 access denined 관련 에러가 발생한다면, root 계정으로 들어가면 된다.
su -
혹은
su root
로 root 계정으로 접속한다. 처음에 비밀번호가 없기 때문에, root 비밀번호를 만든다.
sudo passwd root 명령어를 사용해서 재설정 할 수 있다.
root 계정으로 들어가면

이렇게 바뀐다.
apt update 한번 해주고, 다시 su ubuntu 로 바꿔주고 진행하면 된다.
맥북 사용 시, 터미널 앞에 표시되기 때문에 쉽게 알 수 있지만, 혹시 현재 내가 어떤 계정으로 들어왔는지 알고 싶다면 whoami 명령어를 사용하면 된다.
리눅스에서 apt 라는 저장소에서 라이브러리들을 땡겨와서 사용하는데,
cd /etc/apt 에서 확인 가능하다.

cat sources.list 로 내용을 확인할 수 있다.
가장 최신버전을 선택했다면, ubuntu 22.x 버전으로 jammy 일 것이다. 이전 버전은 focal 인데, 이는 외부 라이브러리를 apt 에 등록하고 최신 버전을 지원하지 않으면 실행이 안된다. apt upate 이 오류가 난다.

보면 deb ${경로} 이렇게 되어있는데, 검색해보니 deb 는 Debian 의 줄임말이다. Ubuntu 서버는 Debian 의 파생이고, Debian 이 제공하는 여러 핵심 파일들을 가져다가 쓴다고 한다. Debian 은 1993년에 이안 머독이라는 사람이 퍼듀 대학교 학부생(?) 때 시작했고, 여자친구 이름을 따서 뭐 어쩌고 저쩌고.. 역사가 길다.
chmod 755, chmod 600 이런게 뭘까?
스프링부트로 간단한 프로젝트를 만들어서 배포했다고 치자. 배포는 여러 사람이 동시에 쓰기위함이다. 그리고 그 중에는 개발자 혹은 특정 집단만 접근을 허용해야하는 민감한 정보가 담긴 파일이다, 운영제 문제가 되는 파일이 분명히 존재한다.
그렇기에 리눅스를 사용하면서 권한에 관련된 지식은 매우 중요하다고 볼 수 있다.

리눅스에서 파일이나 디렉토리는 다음과 같은 규칙이 있다.
${파일은 -, 폴더면 d} ${소유자: rwx} ${소유그룹: rwx} ${누구나: rwx} ${소유자} ${소유 그룹}
예를들어,
-rw-r--r-- root root
이면,
1. 파일이고
2. 소유자인 root 는 read, write 권한이 있고
3. 소유 그룹인 root 는 read 권한이 있고
4. 다른 누구도 read 권한이 있다.
read 는 말 그대로 읽을 수만 있는 권한이고
write 는 내용을 수정할 수 있다. vi ${파일} 로 파일을 수정하려면 write 권한이 필요하다.
execute 는 해당 파일을 실행할 권한인데, 어떤 파일을 다운받아서 실행시키는데 권한오류가 뜬다면 터미널에서 실행할 권한을 부여하면 된다.
여기서 소유권과 허가권 2개로 나뉜다.
소유권은 말 그대로 소유자, 소유그룹을 변경한다.
소유권은 파일, 디렉토리의 생성 & 관리 를 담당하는 권한이다.
허가권은 파일, 디렉토리의 읽기, 쓰기, 실행을 담당하는 권한이다.
chown user-a sample.txt
chown user-a.backend sample.txt
여기서, groups 명령어를 통해서 그룹을 확인할 수 있다.
리눅스에서 그룹 정보는 /etc/group, 사용자 정보는 /etc/passwd 에서 확인할 수 있다.
cd /etc 로 이동 후, cat group, cat passwd 실행하면 안의 내용을 확인할 수 있다.


그룹을 관리하는 명령어들은 다음과 같다.
groupadd {그룹명}삭제
groupdel {그룹명}그룹 사용자 설정
gpasswd -{옵션} {사용자명} {그룹명}A: 관리자로 지정
a: 그룹에 사용자 추가
d: 그룹에서 사용자 제거
그룹 비밀번호 설정
gpasswd {그룹명}adduser {사용자명}adduser --uid {id값} {사용자명}adduser --gid {groupid값} {사용자명}adduser --home {디렉토리명} {사용자명}비밀번호 변경
passwd {사용자명}사용자 삭제
userdel {사용자명}소속된 그룹 확인
groups {사용자명}chmod 755 ... 의 의미는 간단하다.
chmod XYZ ... 로 보면,
X: 소유자
Y: 그룹
Z: 게스트
가 타겟이며
read (= r) : 4
write (= w) : 2
execute(= x) : 1
숫자를 더한 값이다.
rwx 는 4 + 2 + 1 = 7
rw- 는 4 + 2 = 6
r-x 는 4 + 1 = 5
r-- 는 4
-w- 는 2
--x 는 1
다시 chmod 755 ... 를 보면 특정한 파일, 디렉토리에
소유자는 읽고, 쓰고, 실행할 권한
그룹은 읽고, 실행할 권한
게스트는 읽고, 실행할 권한
을 부여하는 것이다.
spring-restart.sh
# .jar 실행을 위해서 알아야 하는 것
# 1. pid (process id)
SPRING_PID=$(pgrep -f v1-0.0.1-SNAPSHOT.jar)
# 2. path
SPRING_PATH="/home/ubuntu/aws-v1/build/libs/v1-0.0.1-SNAPSHOT.jar"
# 3. 해당 프로세스가 실행중인지 검사하는 조건문
if [ -z "$SPRING_PID" ]; then
echo "서버가 종료된 상태입니다."
# 현재 시각을 spring-restart.log 파일에 로그로 남긴다.
echo "서버를 재시작합니다." $(date) 1>>/home/ubuntu/cron-restart/spring-restart.log
# nohup 을 사용해서 터미널이 종료되어도 서버가 돌아가도록 한다.
# 마지막에 "&" 은 백그라운드 실행 명령어이다.
nohup java -jar $SPRING_PATH 1>log.out 2>err.out &
else
echo "서버가 구동중입니다"
fi
deploy.sh 파일을 생성해서, 위에서 생성한 spring-restart.sh 파일이 실행되도록 한다.
# 1. 배포 프로세스
echo "deploy start ..."
echo "1. JDK install"
echo "2. github project download"
echo "3. gradlew 실행권한 부여"
echo "4. project build"
echo "5. set ubuntu timezone"
echo "6. initializing springboot project with nohup"
# 2. 스프링서버 종료 시 재시작
echo "crontab 등록 - spring restart..."
crontab -l > crontab_new
echo "* * * * * /home/ubuntu/cron-restart/spring-restart.sh" 1>>crontab_new
crontab crontab_new
rm crontab_new
일단 어떻게 해야할 지 흐름을 살펴보자.
위의 과정을 스크립트 하나로 작성해두자. 이름은 redeploy.sh 정도가 좋겠다.
최초 배포시에는 deploy.sh, 두번째 부터는 reploy.sh 파일을 사용하면 될 것 같다.
ps -ef 을 이용해서 v1-0.0.1-SNAPSHOT.jar 파일의 pid 를 확인해보자.
너무 출력이 많이나오므로 pgrep -f *.jar 로 찾으면 더 빠르다.
2~7번 까지 과정에서, 서버가 멈추게 된다. 멈추지 않고 배포할 수는 없을까?
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is
SHA256:md1Fpp7nKya7ObFk0Mx4Pip/QQpBDxwBooNosTduYH4.
Please contact your system administrator.
Add correct host key in /Users/jeongjin/.ssh/known_hosts to get rid of this message.
Offending ECDSA key in /Users/jeongjin/.ssh/known_hosts:5
Host key for 15.165.99.254 has changed and you have requested strict checking.
Host key verification failed.
A 라는 인스턴스에 탄력적 IP 연결 후, A 인스턴스를 종료
B 라는 새로운 인스턴스 종료 후 탄력적 IP 연결 후 위와 같은 에러가 발생했다.
검색해보니 /root/.ssh 에 저장된 정보가 문제를 일으킨다고 한다.
.ssh 에 이전 인스턴스 정보가 담겨있는 것이다.
과연 지워버리면 잘 될까, 오히려 오류가 발생할까?
다행이도, 그냥 .ssd 디렉토리를 지워버리면 잘 동작한다.
sudo rm -f .ssd
.bashrc 파일 수정해서 간단한 테스트를 해보자.
vi ./.bashrc 으로 들어가서 작성 후,
source ./.bashrc 를 통해서 적용시킨다.
vi var.sh
리눅스는 확장자가 필요없다.
하지만 스크립트 작성 시, 관습적으로 .sh 를 붙인다. (shell script 의 줄임말인가?)
bash 를 사용한다고 첫줄에 선언한다.
#!/bin/bash
하지만.. 터미널 세션을 종료시키면 적용되지 않는다.
.bashrc 에 등록하면 된다. 하지만.. 특정 파일에서만 사용할 특정 변수를 전역적으로 등록해서 관리하는 건 좀 낭비이고 유발할 가능성이 크다.
그러니 특정 변수는, 특정 파일이 실행될 때만 사용되도록 해보자.
그러기 위해서 vi deploy.sh 파일을 만들어보자.
#!/bin/bash
source ./var.sh
echo $GITHUB_ID
위 코드는 ./var.sh 이 실행될 때만 유효한 코드이다. java 에서 전역변수, 지역변수와 동일한 개념인 듯 하다.
쉘 스크립트로 변수를 만들고, 다른 파일에서 실행하기 위한 조건은 다음과 같다.
.bashrc 에 등록되어 있다.source 로 터미널에서 적용이 된다.source 파일 코드를 작성하면 된다.settings.gradle 파일에 설정된 값이 .jar 파일의 명칭에 들어간다.
build.gradle 에 있는 버전 정보가 .jar 파일의 명칭에 들어간다.
version: '0.0.1'
작성 대기 중
#!/bin/bash
GITHUB_ID="codingspecialist"
PROJECT_NAME="aws-v2"
PROJECT_VERSION="0.0.1"
PROJECT_PID="$(pgrep -f ${PROJECT_NAME}-${PROJECT_VERSION}.jar)"
JAR_PATH="${HOME}/${PROJECT_NAME}/build/libs/${PROJECT_NAME}-${PROJECT_VERSION}.jar"
export GITHUB_ID
export PROJECT_NAME
export PROJECT_VERSION
export PROJECT_PID
java -jar -Dspring.profiles.active=prod
java -jar -Dspring.profiles.active=dev
java -jar -Dspring.profiles.active=local
등등
local 환경에서 실행 시, Boot Config 에서 vm option 으로 작성해주면 된다.
jar 파일 실행 시, 에러 로그는 보고싶지 않다면
java -jar Dspring.profiles.active=prod aws-v2.0.0.1.jar99 2>/dev/null
#!/bin/bash
# 1. env variable
source ./var.sh
echo "1. env variable setting completed"
# 2. clone delete
touch crontab_delete
crontab crontab_delete
rm crontab_delete
echo "2. cron delete completed"
# 3. server checking
# if PROJECT_PID exists
if [ -n "${PROJECT_PID}" ]; then
# re-deploy
kill -9 $PROJECT_PID
echo "3. project kill completed"
else
## init-deploy
# 3-1 apt update
# put -y to answer "yes" in response
# 1>/dev/null makes log deleted
sudo apt-get -y update 1>/dev/null
echo "3-1 apt update completed"
# 3-2 jdk install
sudo apt-get -y install openjdk-11-jdk 1>/dev/null
echo "3-2 jdk install completed"
# 3-3 timezone
sudo timedatectl set-timezone Asia/Seoul
echo "3-3 timezone set completed"
fi
# 4. project folder delete
rm -rf ${HOME}/${PROJECT_NAME}
echo "4. project folder delete completed"
# 5. git clone
git clone https://github.com/${GITHUB_ID}/${PROJECT_NAME}.git
sleep 3s
echo "5. git clone completed"
# 6. gradle +x
chmod u+x ${HOME}/${PROJECT_NAME}/gradlew
echo "6. gradlew u+x completed"
# 7. build
cd ${HOME}/${PROJECT_NAME}
./gradlew build
echo "7. gradlew build completed"
# 8. starting jar
nohup java -jar -Dspring.profiles.active=prod ${JAR_PATH} 1>log.out 2>err.out &
echo "8. starting .jar completed"
# 9. cron registration
touch crontab_new
echo "* * * * * ${HOME}/check-and-restart.sh" 1>>crontab_new
# register the other...
# append something if you use 1>>...
crontab crontab_new
rm crontab_new
echo "9. cron registration completed"
스프링부트는 각종 test 파일까지 통과해야 build 할 수 있다. Unit Test 를 만들어서 배포 전에 로컬 환경에서 잘 돌아가는 지 확인하는 건 당연하지만 JUnit5 를 비롯해서 몇 가지 알아야 될 사항이 있다.
./gradlew clean build -x test
AWS 강의 수강 후 최주호 강사님의 JUnit 강좌를 듣고 정리한다.
.jar 파일을 배포하기 위해 필요한 것들
deploy.sh 파일 생성-c tar 로 묶는다.-v 압축 할때 출력을 화면에 나타낼지 말지-f 파일 이름을 지정-x tar 로 압축을 푼다.배포할 때 필요한 파일 preset 같은 느낌으로다가 압축해서 사용한다.
# 압축 파일 생성
tar -cvf hello.tar 파일명1 파일명2 파일명3 ...
# 압축 풀기
tar -xvf hello.tar
dockerCompose.yml 파일을 만들어서 docker 로 배포하는 법은 현재 최주호 강사님 유투브 채널에 지속적으로 올라오고 있다. 무료이고, 해당 강의도 보고 있으므로 정리하면서 실제 내가 만든 프로젝트를 배포해보겠다.
sftp -i ${.pem 파일} ${ec2-user}@${ip} 로 sftp 명령어를 사용할 수 있다.
lpwd 라고 입력하면 현재 접속한 local directory 가 나온다.
get 명령어를 통해서 파일을 다운로드 받을 수 있다. 하지만 gui 프로그램을 절대 사용해서는 안되는 환경이 아니고서야, ftp 프로그램을 사용하지 않을 이유가 없다.
ftp 관련해서는 유료, 무료 다양하지만 FileZilla 무료 버전으로도 충분하다.

CLI 로 하던 작업을 드래그 앤 드롭으로 할 수 있다. deploy.tar 파일을 local 에 옮겼다가, 다시 linux 서버로 보낼 수 있다.
다시 리마인드 해보자면, deploy.sh 파일안에 스크립트는 다음과 같이 작성되어 있다.
#!/bin/bash
# 1. env variable
source ./var.sh
echo "1. env variable setting complete"
# 2. clone delete
touch crontab_delete
crontab crontab_delete
rm crontab_delete
echo "2. cron delete complete"
# 3. server checking
if [ -n "${PROJECT_PID}" ]; then
# re deploy
kill -9 $PROJECT_PID
echo "3. project kill complete"
else
# first deploy
# 3-1 apt update
sudo apt-get -y update 1>/dev/null
echo "3-1. apt-get update complete"
# 3-2 jdk install
sudo apt-get -y install openjdk-11-jdk 1>/dev/null
echo "3-2. jdk install complete"
# 3-3 timezone
sudo timedatectl set-timezone Asia/Seoul
echo "3-3. timezone setting complete"
fi
# 4. project folder delete
rm -rf ${HOME}/${PROJECT_NAME}
echo "4. project folder delete complete"
# 5. git clone
git clone https://github.com/${GITHUB_ID}/${PROJECT_NAME}.git
sleep 3s
echo "5. git clone complete"
# 6. gradlew +x
chmod u+x ${HOME}/${PROJECT_NAME}/gradlew
echo "6. gradlew u+x complete"
# 7. build
cd ${HOME}/${PROJECT_NAME}
./gradlew build
echo "7. gradlew build complete"
# 8. start jar
nohup java -jar -Dspring.profiles.active=prod ${JAR_PATH} 1>${HOME}/log.out 2>${HOME}/err.out &
echo "8. start server complete"
# 9. cron registration
touch crontab_new
echo "* * * * * ${HOME}/check-and-restart.sh" 1>>crontab_new
# register the others.... you use >> (append)
crontab crontab_new
deploy.sh 파일에서 지역변수로 호출하는 var.sh 파일은 다음과 같다.
#!/bin/bash
GITHUB_ID="codingspecialist"
PROJECT_NAME="aws-v2"
PROJECT_VERSION="0.0.1"
PROJECT_PID="$(pgrep -f ${PROJECT_NAME}-${PROJECT_VERSION}.jar)"
JAR_PATH="${HOME}/${PROJECT_NAME}/build/libs/${PROJECT_NAME}-${PROJECT_VERSION}.jar"
export GITHUB_ID
export PROJECT_NAME
export PROJECT_VERSION
export PROJECT_PID
export JAR_PATH
crontab 에 등록되어 서버의 재시작을 담당할 check-and-restart.sh 파일은 다음과 같다.
#!/bin/bash
source ./var.sh
if [ -z "$PROJECT_PID" ]; then
nohup java -jar -Dspring.profiles.active=prod ${JAR_PATH} 1>${HOME}/log.out 2>${HOME}/err.out &
fi
./deploy.sh 로 파일 실행 후, crontab -e 명령어를 통해서 확인해보자.

crontab 에 등록이 잘 되어있다. kill -9 ${pid} 로 프로세스를 종료시켜도, 1분 뒤에 다시 서버가 시작된다.