Linux Shell 파일 구성
자동화 목적의 리눅스 쉘 파일을 구성해보려고 한다.
다음과 같은 상황을 가정하고 파일을 작성해보자.
- 간단한 spring 앱을 실행하려고 한다.
- 깃헙에 커밋을 푸시하면 이를 탐지해 github actions에서 스프링 어플리케이션을 실행할 인스턴스에 ssh로 접속하여 자동화 스크립트를 실행한다.
- 스크립트 파일에는 다음과 같은 내용이 담겨 있다.
- 깃헙 레포지토리(spring-app-simple)에서 변경사항을 Pull해온다.
- gradle로 빌드한다.
- 기존 앱이 실행중인지 프로세스에서 확인한다. 실행중이면 종료한다.
- 8080번 포트로 들어오는 트래픽을 처리하도록 한다.
- nohup 명령어로 백그라운드에서 실행하도록 한다.
- 로그는 ./spring 경로에 저장한다.
#!/usr/bin/env bash
set -eo pipefail
# ------------------------
# 1. 환경 변수 설정
# ------------------------
REPO_URL="git@github.com:your-org/spring-app-simple.git"
APP_ROOT="/home/ubuntu/spring-app-simple"
BRANCH="main"
LOG_DIR="$APP_ROOT/spring"
JAR_PATTERN="build/libs/*.jar"
PORT=8080
# ------------------------
# 2. 레포 동기화
# ------------------------
if [ -d "$APP_ROOT/.git" ]; then
cd "$APP_ROOT"
# 원격 브랜치 최신 커밋 가져와 현재 브랜치에 병합
git pull origin "$BRANCH" # 기존에 클론된 repo가 있을 때만 사용[1][4]
else
# 최초 배포 시에만 repository를 클론
git clone --branch "$BRANCH" "$REPO_URL" "$APP_ROOT"
cd "$APP_ROOT"
fi
# ------------------------
# 3. 빌드 (Gradle)
# ------------------------
./gradlew clean build -x test
# ------------------------
# 4. 기존 애플리케이션 종료
# ------------------------
# build/libs 디렉터리 내 JAR 이름으로 프로세스 검색·종료
PID=$(pgrep -f "$APP_ROOT/$JAR_PATTERN" || true)
if [ -n "$PID" ]; then
kill -9 "$PID" # 강제 종료[3]
fi
# ------------------------
# 5. 로그 디렉터리 준비
# ------------------------
mkdir -p "$LOG_DIR"
# ------------------------
# 6. 새 애플리케이션 백그라운드 실행
# ------------------------
nohup java -jar $JAR_PATTERN --server.port=$PORT \
> "$LOG_DIR/app.log" 2>&1 & # nohup으로 백그라운드 실행 및 로그 파일 지정[3][5]
파일 시스템 구조 이해 및 탐색
리눅스 파일 시스템 구조와 탐색 방법에 대해 설명한다.
리눅스 파일 시스템은 모든 파일과 디렉토리가 최상위 디렉토리인 루트(/
)로부터 시작하는 계층적인 트리 구조를 가진다. 이는 C:, D: 드라이브와 같이 여러 개의 루트 포인트가 있는 윈도우와 달리, 단 하나의 루트 아래 모든 것이 연결되는 방식이다. 대부분의 리눅스 배포판은 파일 시스템 계층 구조 표준(FHS, Filesystem Hierarchy Standard)을 따르므로 기본적인 디렉토리 구조와 역할이 유사하다.
주요 디렉토리 구조 및 역할
리눅스 파일 시스템의 주요 디렉토리와 그 역할은 다음과 같다:
/
(루트 디렉토리): 파일 시스템 계층 구조의 최상위 시작점이다. 모든 파일과 디렉토리는 이 루트 아래에 위치한다./bin
: 필수적인 사용자 명령어(binary)가 위치한다.ls
,cp
,mv
등 시스템 운영에 필요한 기본 명령어들이 포함되어 있으며, 모든 사용자가 접근 가능하다./sbin
: 필수적인 시스템 관리자용 명령어(system binary)가 위치한다.ifconfig
,reboot
,fdisk
등 시스템 관리 및 유지보수에 필요한 명령어들이 포함되어 있다./etc
: 시스템 전체의 환경 설정 파일들이 위치하는 디렉토리다. 네트워크 설정, 사용자 계정 정보(passwd
), 시작 스크립트, 서비스 설정 파일 등이 이곳에 저장된다./home
: 일반 사용자들의 홈 디렉토리가 생성되는 곳이다. 사용자 계정이 생성되면 보통/home/사용자명
형태의 개인 디렉토리가 만들어져 개인 파일과 설정을 저장한다.~
기호는 현재 로그인한 사용자의 홈 디렉토리를 나타내는 약칭이다./root
: 시스템 최고 관리자인 'root' 사용자의 전용 홈 디렉토리다./lib
:/bin
과/sbin
에 있는 프로그램들이 의존하는 공유 라이브러리 파일들과 커널 모듈이 저장되는 곳이다./usr
: 일반 사용자들이 사용하는 대부분의 프로그램과 데이터가 설치되는 공간이다./usr/bin
: 일반 사용자를 위한 비필수 명령어들이 위치한다./usr/sbin
: 비필수 시스템 관리 명령어들이 위치한다./usr/lib
:/usr
내 프로그램들을 위한 라이브러리가 위치한다./usr/local
: 사용자가 소스 코드를 컴파일하여 설치하는 프로그램들이 주로 위치하는 곳이다./opt
보다 선호되기도 한다./usr/share
: 아키텍처에 독립적인 공유 데이터 (문서, 아이콘 등)가 위치한다.
/var
: 시스템 운영 중 내용이 자주 변경되는 가변적인 데이터를 저장한다. 로그 파일(/var/log
), 메일 스풀(/var/spool/mail
), 웹 서버 데이터(/var/www
), 임시 파일 등이 여기에 속한다./tmp
: 임시 파일들이 저장되는 공간으로, 모든 사용자가 쓰기 권한을 가지며 시스템 재부팅 시 내용이 삭제될 수 있다./var/tmp
는 재부팅 후에도 유지될 수 있는 임시 파일용이다./boot
: 리눅스 커널과 부트로더(GRUB 등) 관련 파일처럼 시스템 부팅에 필요한 핵심 파일들이 위치한다./dev
: 시스템에 연결된 장치(하드디스크/dev/sda
, 터미널/dev/tty
등)를 나타내는 특수 파일들이 위치한다./proc
: 실행 중인 프로세스와 커널 상태 정보를 실시간으로 보여주는 가상 파일 시스템이다. 디스크에 실제 저장되지 않고 메모리 상의 정보를 파일 형태로 제공한다./media
,/mnt
: CD-ROM, USB 드라이브 등 이동식 저장 장치를 연결(마운트)하는 지점으로 사용된다./media
는 주로 자동 마운트에,/mnt
는 수동 임시 마운트에 사용된다./lost+found
: 파일 시스템 체크(fsck
) 시 발견된, 연결이 끊어진 파일 조각(inode)들이 저장되는 곳이다. 각 파티션마다 존재할 수 있다.
파일 시스템 탐색 방법
리눅스 터미널 환경에서는 다음 명령어들을 사용하여 파일 시스템을 탐색한다.
pwd
(Print Working Directory): 현재 작업 중인 디렉토리의 전체 경로(절대 경로)를 화면에 출력한다.cd
(Change Directory): 작업 디렉토리를 변경하는 명령어다.cd 디렉토리명
: 지정한 하위 디렉토리로 이동한다.cd ..
: 현재 디렉토리의 부모(상위) 디렉토리로 이동한다. (.
은 현재 디렉토리,..
은 부모 디렉토리를 의미한다).cd ~
또는cd
: 현재 사용자의 홈 디렉토리로 이동한다.cd -
: 바로 이전에 작업했던 디렉토리로 이동한다.- 절대 경로: 루트(
/
)부터 시작하는 전체 경로이다 (예:cd /var/log
). - 상대 경로: 현재 위치를 기준으로 한 경로이다 (예: 현재
/var
에 있다면cd log
로/var/log
로 이동한다).
ls
(List): 현재 또는 지정한 디렉토리의 파일 및 하위 디렉토리 목록을 보여준다.ls -l
: 파일의 권한, 소유자, 크기, 수정일 등 상세 정보를 긴 형식으로 보여준다.ls -a
: 숨김 파일(이름이.
으로 시작하는 파일)을 포함한 모든 파일을 보여준다.ls -h
:l
옵션과 함께 사용 시 파일 크기를 사람이 읽기 쉬운 단위(KB, MB, GB)로 표시한다.ls -F
: 파일 유형을 나타내는 기호(디렉토리 '/', 실행 파일 '*', 심볼릭 링크 '@')를 파일명 뒤에 추가한다.- 옵션은 조합하여 사용할 수 있다 (예:
ls -alh
).
file
: 파일의 종류(텍스트, 이미지, 실행 파일 등)를 실제 내용을 기반으로 파악하여 알려준다. 확장자와 실제 파일 형식이 다를 수 있을 때 유용하다.
사용자 및 권한 관리
# 1. 사용자 추가
sudo adduser testuser
# 2. 그룹 추가
sudo addgroup testgroup
# 3. testuser를 testgroup에 추가
sudo usermod -aG testgroup testuser
# 4. 파일 생성 및 권한 확인
touch sample.txt
ls -l sample.txt
# 5. 권한 변경: 소유자에만 모든 권한, 그룹/기타는 읽기 권한만
chmod 744 sample.txt
# 6. 소유자와 그룹 변경
sudo chown testuser:testgroup sample.txt
# 7. 디렉토리 생성 및 하위 권한 일괄 변경
mkdir testdir
chmod -R 755 testdir
시스템 모니터링 및 성능 분석
# top으로 실시간 모니터링
top
# htop으로 시각적 모니터링 (설치 필요)
sudo apt-get install htop
htop
# sar로 5초 간격 3회 CPU 사용률 체크
sar -u 5 3
# sar로 메모리 사용률 분석
sar -r
# sar로 시스템 부하 평균 확인
sar -q
top도 무난하고 htop도 괜찮다. 특별한 필요가 아니라면 내장된 top을 보통 사용할 것 같다.
네트워크 구성 및 트러블슈팅
ifconfig
특정 포트 허용
연결 및 경로 테스트
커널 메세지 확인
Overlay2 파일시스템으로 관리하는 디렉토리 만들기
Overlay2 파일 시스템이란?
Docker가 사용하는 기본 스토리지 드라이버, 이는 리눅스의 OverlayFS라는 파일 시스템 기술을 기반으로 한다. 여러 개의 디렉터리(레이어)를 겹쳐서 마치 하나의 통합된 디렉터리처럼 보이게 만드는 유니온 파일 시스템 기술이다.
- 계층적 구조
- Lowerdir: 읽기 전용 이미지 레이어 (기본 이미지 및 상위 레이어)
- Upperdir: 쓰기 가능한 컨테이너 레이어
- Merged: 통합된 파일 시스템 뷰
- Workdir: OverlayFS 내부 작업 디렉터리
- 디렉토리
/var/lib/docker/overlay2/
├── 3fd0040...-init # Init 레이어 (컨테이너 초기화)
├── 3fd0040... # 컨테이너 쓰기 레이어
│ ├── diff # 실제 파일 저장소
│ ├── link # 암호화된 레이어 ID
│ ├── lower # 하위 레이어 경로
│ ├── merged # 통합 뷰
│ └── work # 작업 디렉터리
├── l # 레이어 ID 심볼릭 링크
│ └── 6Y5IM2XC... → ../3fd0040...
- 레이어 식별 방법
# 컨테이너와 레이어 매핑 확인
$ docker inspect <container_id> | jq '.[].GraphDriver.Data'
# 출력 예시
{
"LowerDir": "/var/lib/docker/overlay2/l/ABCDEF...:/var/lib/docker/overlay2/l/123456...",
"MergedDir": "/var/lib/docker/overlay2/3fd0040.../merged",
"UpperDir": "/var/lib/docker/overlay2/3fd0040.../diff",
"WorkDir": "/var/lib/docker/overlay2/3fd0040.../work"
}
- 성능최적화 전략
- Copy-up 최소화 기법
- 볼륨 사용 권장
bash$ docker run -v /host/path:/container/path my_image
- 대용량 파일 처리
bash*# Direct I/O 사용 (O_DIRECT 플래그)* $ mount -o remount,direct /var/lib/docker
- 레이어 최적화
# Dockerfile 최적화 예시 RUN apt-get update && apt-get install -y \ package1 \ package2 \ && rm -rf /var/lib/apt/lists/*
- 볼륨 사용 권장
- Copy-up 최소화 기법
- 레이어 손상 복구
1. Docker 서비스 정지
$ systemctl stop docker
2. 손상된 레이어 식별
$ docker system df -v
3. 수동 백업 및 제거
$ mv /var/lib/docker/overlay2 /var/lib/docker/overlay2.bak
4. Docker 재시작
$ systemctl start docker
고급 쉘스크립팅
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
#########################
# 1. 환경 설정
#########################
# 백업 대상 디렉터리 (절대경로)
BACKUP_DIRS=( "/etc" "/var/www" )
# 백업 저장 위치
BACKUP_DEST="/backup"
# 보관할 백업 개수
KEEP_BACKUPS=7
# 분석할 로그 파일
LOG_FILES=( "/var/log/syslog" "/var/log/auth.log" )
# 신규 생성할 사용자 목록 파일 (username:password)
USER_LIST_FILE="/opt/userlist.txt"
# 비활성화 기준(일): 마지막 로그인 후 경과 일수
INACTIVE_DAYS=30
# 리포트 저장 위치
REPORT_FILE="/var/log/ops_automation_report_$(date +%F).log"
#########################
# 2. 유틸리티 함수
#########################
log() {
local level="$1"; shift
echo "[$(date +'%F %T')] [$level] $*" | tee -a "$REPORT_FILE"
}
# 스크립트 종료 시 호출
cleanup() {
log INFO "스크립트 종료"
}
trap cleanup EXIT
#########################
# 3. 시스템 상태 점검
#########################
check_system() {
log INFO "=== 시스템 상태 점검 시작 ==="
log INFO "디스크 사용량:"
df -h | tee -a "$REPORT_FILE"
log INFO "메모리 사용량:"
free -h | tee -a "$REPORT_FILE"
# 주요 서비스 상태 확인 (예: ssh, nginx, docker)
for svc in ssh nginx docker; do
if systemctl is-active --quiet "$svc"; then
log INFO "서비스 $svc: ACTIVE"
else
log WARN "서비스 $svc: INACTIVE or NOT FOUND"
fi
done
log INFO "=== 시스템 상태 점검 완료 ==="
}
#########################
# 4. 디렉터리 백업 관리
#########################
backup_directories() {
log INFO "=== 백업 시작 ==="
mkdir -p "$BACKUP_DEST"
local now="$(date +'%Y%m%d_%H%M%S')"
for dir in "${BACKUP_DIRS[@]}"; do
if [[ -d "$dir" ]]; then
local target="${BACKUP_DEST}/$(basename "$dir")_$now.tar.gz"
tar czf "$target" "$dir"
log INFO "백업 생성: $target"
else
log ERROR "대상 디렉터리 미존재: $dir"
fi
done
# 오래된 백업 삭제
find "$BACKUP_DEST" -maxdepth 1 -type f -name "*.tar.gz" \
-mtime +$KEEP_BACKUPS -print -exec rm -f {} \; \
| while read old; do log INFO "오래된 백업 삭제: $old"; done
log INFO "=== 백업 완료 ==="
}
#########################
# 5. 로그 분석
#########################
analyze_logs() {
log INFO "=== 로그 분석 시작 ==="
for lf in "${LOG_FILES[@]}"; do
if [[ -f "$lf" ]]; then
local err_count warn_count
err_count=$(grep -i "error" "$lf" | wc -l)
warn_count=$(grep -i "warn" "$lf" | wc -l)
log INFO "[$lf] ERROR=$err_count, WARN=$warn_count"
else
log WARN "로그 파일 미발견: $lf"
fi
done
log INFO "=== 로그 분석 완료 ==="
}
#########################
# 6. 사용자 관리
#########################
manage_users() {
log INFO "=== 사용자 관리 시작 ==="
# 6-1. 신규 사용자 추가
if [[ -f "$USER_LIST_FILE" ]]; then
while IFS=: read -r username password; do
if id "$username" &>/dev/null; then
log INFO "이미 존재하는 사용자: $username"
else
useradd -m "$username"
echo "$username:$password" | chpasswd
log INFO "신규 사용자 추가: $username"
fi
done < "$USER_LIST_FILE"
else
log WARN "사용자 목록 파일 미발견: $USER_LIST_FILE"
fi
# 6-2. 비활성 사용자 비활성화 (로그인 못하게 잠금)
local threshold
threshold=$(date -d "-$INACTIVE_DAYS days" +%s)
for user in $(awk -F: '$3>=1000{print $1}' /etc/passwd); do
# 마지막 로그인 시간 가져오기
last=$(lastlog -u "$user" | awk 'NR==2{print $4,$5,$6}')
if [[ -n "$last" ]]; then
last_ts=$(date -d "$last" +%s)
if (( last_ts < threshold )); then
usermod -L "$user"
log INFO "비활성화 처리(잠금): $user"
fi
fi
done
log INFO "=== 사용자 관리 완료 ==="
}
#########################
# 7. 메인 실행
#########################
main() {
# 반드시 root로 실행
if (( EUID != 0 )); then
echo "ERROR: 스크립트를 root로 실행하세요." >&2
exit 1
fi
log INFO "자동화 스크립트 시작"
check_system
backup_directories
analyze_logs
manage_users
log INFO "자동화 스크립트 완료"
}
main "$@"
반응형
'Linux' 카테고리의 다른 글
리눅스 커널 모듈 (0) | 2025.05.15 |
---|---|
시스템 로그 분석 및 모니터링(logrotate) (0) | 2025.05.15 |
Shell (0) | 2025.04.23 |