쉡 스크립트로 알아보는 리눅스 명령어

yboy·2023년 1월 4일
2

Learning Log 

목록 보기
29/41
post-thumbnail

학습동기

이전에 무중단 배포 스크립트를 짤 때 쉡 스크립트를 사용했었다. 당시에는 무중단 배포를 통해 서비스의 다운 타임을 해결하는데 급급해 쉘 스크립트 내에서 사용한 리눅스 명령어들을 잘 알지 못하고 썼었다. 이참에 사용했던 리눅스 명령어들에 대해 알아보기 위해 학습을 다짐했다.

학습내용

우선 명령어 학습을 하기 전에 무중단 배포 스크립트의 실행 흐름에 대해 다시 한번 짚고 넘어가자.

  1. idle port를 찾는다. 어느 포트에 새롭게 배포될 서버가 띄워져야 하는지 확인한다.

  2. idle port에 새로운 버전의 jar 파일을 실행한다.

  3. health check를 통해 idle port에 서버가 띄워질 때까지 기다리고 띄워지면 nginx에서 가리키고 있는 was port를 idle port로 스위칭해준다.

  4. current port에서 실행되고 있는 이전 버전의 서버를 kill 한다.

이제 각 단계 별로 차근 차근 명령어들을 살펴보자.

1. idle port를 찾는다.

# idle port 찾기 
function find_idle_port()
{
	RESPONSE_CODE=$(curl -o /dev/null -w "%{http_code}" http://{was ip주소}:8080)

    if [ ${RESPONSE_CODE} == 404 ] # 404가 아니면
    then
        IDLE_PORT=8080 
    else
        IDLE_PORT=8081
    fi

		echo "${IDLE_PORT}"
}

가장 먼저 보이는 명령어는 curl 명령어이다. 이 명령어를 통해 HTTP 요청을 보내고 HTTP response status를 확인하고 있는데 자세히 알아보자.

curl

URL로 데이터를 전송하여 서버에 데이터를 보내거나 가져올 때 사용하기 위한 명령어

java에서는 RestClient나 Webflux를 통해 http 요청을 보낼 수 있지만 SHELL에서는 curl 명령어를 사용한다. HTTP 뿐만 아니라 HTTPS/ SMTP/ SCP 등 다양한 프로토콜을 지원하는 명령어다.

다양한 옵션들을 제공해주지만 위에서는 -o(—output FILE) 옵션을 통해 HTTP 응답을 파일 형태로 받아 %{http_code} 를 파싱하고 HTTP status 값을 변수로 받아 상태를 확인하는 용도로 확인하고 있다.

-o 옵션과 비슷한 옵션으로 -O 옵션이 존재하는데 둘의 차이는 -o는 미리 정의된 파일 이름을 사용하여 파일을 저장하는 용도이고 -O는 파일을 원래 리모트에 저장되어 있던 이름으로 저장한다는 차이가 있다.

echo

다음은 linux 환경에서 가장 많이 사용하는 명령어인 echo 명령어에 대해서 알아보자.

지정한 문자열 또는 텍스트를 터미널에 출력하는 명령어

일반적으로 터미널 프롬프트 명령이나 쉘 스크립트에서 화면이나 파일로 상황을 알리는 문자열을 출력할 때 사용한다.

신기한 것은 linux 환경에서는 return 명령어가 없기 때문에 위에서 함수를 정의해 값을 return해줄 때 echo를 사용한다. 또한 쉡 스크립트를 작성하고 해당 명령어가 실제로 실행되는지 확인하기 위해 echo "> kill -15 $CURRENT_PID" 다음과 같이 명령어를 따옴표로 감싸 확인할 때도 활용될 수 있고 파이프라인을 이용해 명령어를 다음 명령어에 전달하는 용도로도 많이 사용된다.

2. idle port에 새로운 버전의 jar 파일을 실행한다.

# idle profile로 jar 파일 실행

REPOSITORY=/home/ubuntu/jar

echo "> Build 파일 복사"
echo "> cp $REPOSITORY/*.jar $REPOSITORY/"

cp $REPOSITORY/*.jar $REPOSITORY/

echo "> 새 어플리케이션 배포"
JAR_NAME=$(ls -tr $REPOSITORY/*.jar | tail -n 1)

echo "> JAR Name: $JAR_NAME"

echo "> $JAR_NAME 에 실행권한 추가"

chmod +x $JAR_NAME

echo "> $JAR_NAME 실행"

IDLE_PORT=$(find_idle_port)

echo "> $JAR_NAME 를 profile=$IDLE_PORT 로 실행합니다."
nohup java -jar -Dspring.profiles.active=prod -Dserver.port=$IDLE_PORT $JAR_NAME 1>stdout.txt 2>err.txt &

cp

파일, 디렉토리를 복사하는 명령어

위에서는 빌드 파일을 복사하고 복사된 파일을 지정된 경로에 저장하는 용도로 사용하고 있다. 사용법은 간단하다.

cp {복사하고 싶은 대상} {복사하고 싶은 대상 이름 or 경로}

chmod

파일의 권한을 변경할 때 사용하는 명령어

사용법이 좀 복잡해 보일 수 도 있지만,

위의 표를 보고 적재적소에 사용하면 큰 어려움은 없을 것이다. 위의 쉘 스크립트에서는 해당 빌드 파일에 +x 옵션을 줌으로서 파일에 실행 권한을 부여하고 있다.

ls -tr

보통 ls 명령어로 해당 경로의 파일이나 디렉토리 정보를 보거나 -al 옵션을 줘서 숨겨진 파일까지 자세히 보고 싶을 때 사용한다. 따라서 -tr 옵션은 좀 생소할 수도 있다.

해당 경로의 출력 결과를 생성된 시간 순서대로 내림차순 정렬하는 명령어이다.

tail

해당하는 파일의 마지막 부분을 확인할 수 있는 명령어

위에서는 ls -tr 명령어로 경로에 있는 모든 빌드 파일을 생성된 시간을 기준으로 내림차순 정렬하고 가장 최신에 생긴 jar 파일의 이름을 출력하기 위해 사용하고 있다.

tail 명령에도 많은 옵션들이 존재하지만 위에서는 -n 옵션으로 파일의 마지막줄 부터 지정한 라인 수 1까지의 내용을 출력하면서 가장 최신의 빌드 파일을 알아내기 위해 사용한다.

3. health check를 통해 idle port에 서버가 띄워질 때까지 기다리고 띄워지면 Nginx에서 가리키고 있는 Was port를 idle port로 스위칭해준다.

# health check
CURRENT_PORT=$(find_current_port)
IDLE_PORT=$(find_idle_port)

echo "> Health Check Start!"
echo "> IDLE_PORT: $IDLE_PORT"
echo "> curl -s http://3.39.136.191:$IDLE_PORT "
sleep 10

for RETRY_COUNT in {1..10}
do
  RESPONSE=$(curl -s http://{was ip주소}:${IDLE_PORT})
  UP_COUNT=$(echo ${RESPONSE} | grep "timestamp" | wc -l)

  if [ ${UP_COUNT} -ge 1 ]
  then # $up_count >= 1
    echo "> Health check 성공"

    # 기존 포트에서 idle port로 switch
	echo "> 전환할 Port: $IDLE_PORT"
	echo "> Port 전환"
    echo "set \$service_url http://{was ip주소}:${IDLE_PORT};" | ssh -i key-teatime.cer ubuntu@192.168.2.61 sudo tee /etc/nginx/conf.d/service-prod-url.inc

	echo "> 엔진엑스 Reload"
	ssh -i key-teatime.cer ubuntu@{ngnix1 ip주소} sudo service nginx reload
    break

  else
      echo "> Health check의 응답을 알 수 없거나 혹은 실행 상태가 아닙니다."
      echo "> Health check: ${RESPONSE}"
  fi

  if [ ${RETRY_COUNT} -eq 10 ]
  then
    echo "> Health check 실패. "
    echo "> 엔진엑스에 연결하지 않고 배포를 종료합니다."
    exit 1
  fi

  echo "> Health check 연결 실패. 재시도..."
  sleep 10
done

grep

특정 파일에서 지정한 문자열이나 정규 표현식을 포함한 행을 출력해주는 명령어

위에서는 curl 명령어를 통해 받아온 HTTP 응답 값에서 timestamp라는 문자열을 찾는 용도로 사용하고 있다. grep이란 단어 그대로 해당 파일에서 찾고자 하는 문자열이나 정규 표현식을 찾고 싶을 때 사용한다.

tee

화면에 출력된 내용을 파일로 저장하는데 사용하는 명령어

위 명령어는 설명봤을 때는 이해하기 좀 여러워서 위의 사용 예시를 보며 알아보도록 하자.

echo "set \$service_url http://{was ip주소}:${IDLE_PORT};" | ssh -i key-teatime.cer ubuntu@192.168.2.61 sudo tee /etc/nginx/conf.d/service-prod-url.inc

echo를 통해 가져온 실행문 "set \$service_url http://{was ip주소}:${IDLE_PORT};"를 ssh 명령어를 통해 해당 실행문을 다른 리눅스 서버의 /etc/nginx/conf.d/service-prod-url.inc 파일에 저장하는 구문이다.

ssh

ssh 명령어는 aws를 이용해 봤다면 모두 한번 쯤은 봤을 것이다. 우선 SSH는 Secure Shell의 약자로 클라이언트와 서버 간의 암호화된 연결에 사용되는 함호화 네트워크 프로토콜이다.

ssh 프로토콜로 다른 리눅스 서버로 접근하는 명령어

scp

위의 쉘 스크립트에서는 scp 명령어를 사용하진 않았지만 ssh와 함께 자주 사용되는 명령어이기 때문에 한 번 알아보고 넘어가자.

다른 리눅스 서버에 파일이나 디렉토리를 전달할 때 사용하는 명령어

4. current port에서 실행되고 있는 이전 버전의 서버를 kill 한다.

# 기존 포트 stop

CURRENT_PID=$(lsof -ti tcp:${CURRENT_PORT}

if [ -z ${CURRENT_PID} ]
then
  echo "> 현재 구동중인 애플리케이션이 없으므로 종료하지 않습니다."
else
  echo "> kill -15 $CURRENT_PID"
  kill -9 ${CURRENT_PID}
  sleep 5
fi

lsof -ti

lsof 명령어는 시스템에서 열려있는 파일에 대한 정보를 출력해주는 명령어이다.
보통 lsof -i:{port} 를 통해 해당 port에서 프로그램이 실행 중인지 확인하는 용도로 사용한다. 그럼 -t 옵션은 무엇을 의미할까?

PID만 출력할 때 사용한다.

아주 편리한 옵션이다. 위의 스크립트를 보면 kill 명령어로 실행 중인 PID를 죽여야 한다. 이때 -t 옵션으로 PID 값만을 확인해 스크립트 내에서 쉽게 kill 명령어를 사용할 수 있다.

마무리

무중단 배포 작업을 진행할 때 시간적으로 너무 바빠서 구글링으로 우당탕탕 사용했던 리눅스 명령어들을 제대로 숙자하지 못했었다. 이번 기회에 얕게 라도 내가 사용했던 명령어들에 대해 학습할 수 있어서 뿌듯한 학습 시간이었다. 명령어마다 다양한 옵션들이 있는데 옵션들을 모두 외우기 보단 명령어를 사용할 때 적합한 옵션을 조사해 잘 선택해서 사용해야겠다.

0개의 댓글