티스토리 뷰
서버를 배포하기 위해 AWS EC2 인스턴스를 사용해서 배포를 진행했다.
SSH 방식으로 서버 내에서 빌드와 배포를 진행하는 방식을 선택했는데 배포 과정에서 'command not found' 에러가 발생하게 되었다.
하지만 SSH로 직접 접속했을 때는 해당 문제가 발생하지 않았었고 pnpm, pm2 패키지가 프로젝트 내에 제대로 설치되어 있다는 점을 확인한 후에, CI/CD 방식으로 배포 시 환경변수를 읽어오지 못하는 것 같다고 판단했다.
이 문제를 해결하는 과정에서 Linux Shell의 설정 파일들과 non-interactive shell, non-login shell에 대해 공부하게 되어 정리해보려 한다.
Shell이란?
Shell이란 리눅스 커널과 사용자를 연결해주는 인터페이스를 말한다.
쉘은 크게 Bourne shell 계열과 C Shell 계열로 나뉘며 각각의 계열마다 여러 개의 셸이 속해있다.
이번에 배포를 위해 사용한 셸은 Bourne Shell 계열 중 하나인 Bash(Bourne Again Shell) 이다.
참고로 사용 중인 쉘을 알아보려면 아래의 명령어를 통해 알 수 있으며 bash 셸을 사용하는 경우에는 /bin/bash가 출력된다.
echo $SHELL
Shell의 설정 파일들
bash는 아래의 설정 파일들을 가지고 있다.
- /etc/profile
- /etc/bashrc
- ~/.bash_profile
- ~/.bashrc
- ~/.bash_logout
위 파일들은 bash를 사용하는 모든 사용자에게 영향을 주는 전역적인 파일과, 사용자 개개인을 위한 설정을 담은 지역적인 파일로 나뉜다. 일반적으로 전역적인 파일은 '/etc' 으로 시작하며, 지역적인 파일은 '.'으로 시작한다. (ex. ~/.bashrc)
각각의 설정 파일이 하는 역할은 다음과 같다.
1./etc/profile
환경변수와 bash가 수행될 때 실행되는 프로그램을 제어하는 전역적인 시스템 설정과 관련된 파일이다.
2./etc/bashrc
별칭(alias)과 bash가 수행될 때 실행되는 함수를 제어하는 전역적인 시스템 설정 파일이다.
때때로 이 파일은 생략되기도 하며 그 내용은 /etc/profile에 함께 포함되기도 한다.
3. ~/.bash_profile
환경 변수와 bash가 수행될 때 실행되는 프로그램을 제어하는 지역적인 시스템 설정 파일이다.
이 환경 변수들은 해당 사용자에게만 한정되며 그 이외의 다른사람에게는 영향을 미치지 않는다.
또한 이 파일은 전역적인 설정파일인 /etc/profile이 수행된다음 바로 수행된다.
4. ~/.bashrc
별칭(alias)과 bash가 수행될 때 실행되는 함수를 제어하는 지역적인 시스템 설정 파일이다. 이 설정들도 해당 사용자에게만 한정된다. 또한 이 파일은 전역적인 설정 파일인 /etc/bashrc가 수행된 다음 바로 수행된다.
5. ~/.bash_logout
사용자가 로그아웃하기 직전에 실행하는 프로그램에 관한 bash의 지역적인 시스템 설정 파일이다.
bash 셸이 실행되는 방법들
EC2 내 pnpm 실행 파일이 /home/ubuntu/.local/share/pnpm/pnpm 경로에 설치되어 있어서 ~/.profile에 PATH를 추가하면 문제가 해결 될 것이라 생각했다.
그런데 첫 줄에 "~/.profile: executed by the command interpreter for login shells." 라는 문장이 눈에 띄었다.
해석해보면 파일이 login shell에서만 실행된다는 말인데, 반대로 생각하면 login shell이 아닌 환경에서는 실행되지 않는다는 의미이다.
그래서 login shell에 대해 찾아보게 되었는데, 찾다보니 bash shell이 여러가지 방법으로 실행된다는 것을 알게 되었다.
1. Login Shell, Non Login Shell
Login Shell
리눅스 시스템에 로그인 하면 셸은 Login Shell로 실행된다.
터미널을 통해 SSH로 리눅스 시스템에 접근하는 경우가 Login Shell에 해당한다
Login Shell은 다음과 같은 순서로 명령을 실행한다.
- /etc/profile 을 실행
- /etc/profile 은 /etc/profile.d 안에 있는 스크립트들을 실행
- ~/.bash_profile 실행 (or ~/.bash_login or ~/.profile)
- ~/.bash_profile 은 ~/.bashrc 파일 실행
- ~/.bashrc 는 /etc/bashrc 파일을 실행
즉 /etc/profile → /etc/profile.d → ~/.bash_profile → ~/.bashrc → /etc/bashrc 순으로 읽으며 root로 로그인 시 /etc/profile 을 먼저 읽고 이후 홈 디렉토리의 실행파일들을 읽는다.
Non Login Shell
Login Shell로부터 파생된 Shell이며 로그인 없이 실행하는 Shell을 의미한다.
ssh로 리눅스 시스템에 접근 후 bash shell을 실행하는 경우 Non Login Shell에 해당한다.
Non Login Shell은 다음과 같은 순서로 명령을 실행한다.
- ~/.bashrc 를 실행시킨다.
- ~/.bashrc는 /etc/bashrc 를 실행시킨다.
- /etc/bashrc 는 /etc/profile.d 내 스크립트를 실행한다.
즉 ~/.bashrc → /etc/bashrc → /etc/profile.d 순으로 실행된다.
2. Interactive Shell, Non Interactive Shell
Interactive Shell
사용자가 실시간으로 직접 CLI에 명령을 입력하는 것을 말한다.
따라서 실행하는 명령의 결과를 사용자가 판단하여 다음 명령을 실행해야 한다.
Non Interactive Shell
Shell을 실행해서 script 파일의 명령들을 수행하는 것을 말한다.
이 경우 직접 명령을 입력하는 게 아니라 script 파일에 나열된 명령어들을 순차적으로 실행하는 형태이다.
문제 원인 파악
name: express-deployment
on:
push:
branches: [develop]
jobs:
deploy:
name: deploy
runs-on: ubuntu-latest
steps:
- name: Deploy to EC2
uses: appleboy/ssh-action@v1.2.2
with:
host: ${{ secrets.AWS_EC2_HOST }}
username: ${{ secrets.AWS_EC2_USER }}
key: ${{ secrets.AWS_PRIVATE_KEY }}
port: ${{secrets.SERVER_PORT}}
script: |
set -e
cd /home/ubuntu/codeit-resources
git pull origin develop
pnpm install
pnpm --filter=api build
pm2 restart codeit-server
나는 EC2 인스턴스에 접근하기 위해 ssh를 통해 원격 서버에 연결하고 명령을 실행할 수 있도록 도와주는 appleboy/ssh-action 라는 액션을 사용했다.
위에서 정리한 내용을 바탕으로 생각해본다면 이 방식은 Login Shell이 아닌 Non Login Shell의 방식을 사용하며 동시에 Non-Interactive Shell 환경에서 실행된다는 것을 알 수 있다.
이후 Non Login Shell의 실행 순서에 ~/.bashrc 포함되어 있어 ~/.bashrc 파일을 확인했는데 파일 상단에 셸이 interactive하게 실행되지 않으면 아무것도 실행하지 말라는주석이 적혀있었다.
따라서 GitHub Actions CI/CD는 Non-Interactive Shell 환경이기 때문에 이 코드로 인해 ~/.bashrc 파일의 나머지 부분(pnpm 환경 변수 설정 포함)이 실행되지 않았던 것이다.
이후 pnpm 관련 환경 변수 설정을 인터랙티브 체크 조건 이전으로 이동시켜 문제를 해결할 수 있었다.
마치며
오류를 해결하면서 셸이 실행되는 방법에는 여러가지가 있다는 것을 알게되었다.
특히 자동화된 배포 환경에서는 직접 명령어를 입력하는 것과 다른 맥락에서 셸이 실행되기 때문에 이런 차이점을 인식하고 적절한 설정을 해주는 것이 중요하다고 느꼈다.
참고
https://unix.stackexchange.com/questions/38175/difference-between-login-shell-and-non-login-shell
https://swmobenz.tistory.com/40
https://anggeum.tistory.com/entry/Login-vs-Non-Login-Shell-etcprofile-bashrc
'Backend' 카테고리의 다른 글
[Backend/CICD] Linux의 OOM(Out of Memory) 해결하기 (0) | 2025.01.20 |
---|---|
[Backend/DB] MongoDB 해킹 당한 사건 (0) | 2024.10.20 |
[MongoDB] Mongoose 스키마와 모델, 에러해결 (OverwriteModelError: Cannot overwrite `ShortLink` model once compiled.) (0) | 2024.06.10 |
- Total
- Today
- Yesterday
- 코드잇스프린트
- 코드잇 스프린트
- javascript
- 중급 프로젝트
- Next.js
- 리액트
- 스프린트프론트엔드6기
- hydrationboundary
- CSS
- GitHub
- Target
- 객체
- rest parameter
- 동기
- tanstackquery
- Git
- map
- js
- 제어 컴포넌트
- 비동기
- react
- 배열
- arguments
- 유사배열객체
- innerhtml
- html
- 비제어 컴포넌트
- 취업까지달린다
- currentTarget
- 프론트엔드
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |