Profile picture

[Shell Script] 도커 컨테이너 자동 기동

JaehyoJJAng2023년 11월 15일

▶︎ 개요

특정 컨테이너가 서버 재기동 시 알아서 올라오지 못하는 문제를 발견

이 문제를 해결하기 위하여 셸 스크립트를 활용하여 서버가 재기동 되면서 죽어버린 컨테이너를 다시 재기동되도록 만들어 보려 함.


‣ 셸 스크립트

{% include codeHeader.html name="checkContainer.sh" %}

#!/usr/bin/bash

# 명령어 실패 시 바로 스크립트 종료
set -e

now=$(date +"%Y%m%d_%H%M")
logDir="/var/log/container/init/run"
logFileName="$now.log"

exec 3>> "$logFileName"
if [[ ! -d "$logDir" ]]; then
    echo "[$now] $logDir 폴더 생성 중 ..." >&3
    mkdir -p "$logDir"

    if [[ $? != 0 ]]; then
        echo "[$now] $logDir 디렉토리 생성 실패!" >&3
        exit 1
    else
        echo "[$now] $logDir 디렉토리 생성 성공!" >&3
    fi
else
    echo "[$now] $logDir 디렉토리는 이미 존재함" >&3
fi

echo "[$now] 도커 컨테이너 상태 확인 & 컨테이너 재기동 자동화 시작" >&3
echo "[$now] 1. 도커 상태 확인" >&3
status=$(docker ps --filter "status=exited" --format "{{.Names}}")

echo "[$now] 2. 도커 컨테이너 재시작" >&3
if [[ -n "$status" ]]; then
    echo "[$now] $status 컨테이너를 재기동 합니다." >&3
    containerRestartStatus=$(docker restart "$status")
    echo "[$now] 컨테이너 재기동 상태 $containerRestartStatus" >&3

    if [[ $? != 0 ]]; then
        echo "[$now] $status - 컨테이너 재기동 실패!" >&3
        exit 1
    else
        echo "[$now] $status - 컨테이너 재기동 성공!" >&3
    fi
else
    echo "[$now] 컨테이너가 정상 구동 중입니다!" >&3
fi

echo "[$now] 도커 컨테이너 상태 확인 & 컨테이너 재기동 자동화 종료" >&3

cat "$logFileName" >| "$logDir/$logFileName"
rm -rf "$logFileName"

‣ 코드 설명

스크립트 처음에 명시된 set -e 명령어는 셸 스크립트에서 사용할 수 있는 명령어이다.

위 명령어를 설정하게 되면 어떤 명령어를 처리했을 때, 실패 시 아래 있는 내용을 처리하지 않고 바로 해당 스크립트를 종료하라는 의미이다.

즉, 종료 상태코드가 0이 아니라면 해당 스크립트는 종료 되어 버리게 하는 것!

#!/usr/bin/bash

set -e

echo "Hello, World!"
NON_FILE="/존재하지않는파일"
ls "$NON_FILE"

# 아래 echo 문은 실행되지 않는다
echo "Bye!"

만약 특정 부분에서 실패를 했을 때 계속 진행하고 싶다면 해당 부분에서 set +e 명령을 명시해주면 set -e를 무시하는 효과를 얻을 수 있다.

set -e는 스크립트에서 오류 처리를 강제하는 것이다.

스크립트에서 오류가 발생하면 스크립트가 바로 종료되기 때문에 스크립트 작성 시 예외 처리와 오류 처리에 조금 더 신경을 써줄 수 있게 된다.


if [[ ! -d "$logDir" ]]; then
    echo "[$now] $logDir 폴더 생성 중 ..." >&3
    mkdir -p "$logDir"

    if [[ $? != 0 ]]; then
        echo "[$now] $logDir 디렉토리 생성 실패!" >&3
        exit 1
    else
        echo "[$now] $logDir 디렉토리 생성 성공!" >&3
    fi
else
    echo "[$now] $logDir 디렉토리는 이미 존재함" >&3
fi

로그를 남길 디렉토리가 존재하는지 확인하고 없다면 생성하도록 하는 코드이다.


echo "[$now] 도커 컨테이너 상태 확인 & 컨테이너 재기동 자동화 시작" >&3
echo "[$now] 1. 도커 상태 확인" >&3
status=$(docker ps --filter "status=exited" --format "{{.Names}}")

echo "[$now] 2. 도커 컨테이너 재시작" >&3
if [[ -n "$status" ]]; then
    echo "[$now] $status 컨테이너를 재기동 합니다." >&3
    containerRestartStatus=$(docker restart "$status")
    echo "[$now] 컨테이너 재기동 상태 $containerRestartStatus" >&3

    if [[ $? != 0 ]]; then
        echo "[$now] $status - 컨테이너 재기동 실패!" >&3
        exit 1
    else
        echo "[$now] $status - 컨테이너 재기동 성공!" >&3
    fi
else
    echo "[$now] 컨테이너가 정상 구동 중입니다!" >&3
fi

exited된 Container의 상태를 확인하고 재기동 시키는 부분이다.

$? != 0: docker restart "$status"를 실행했을 때의 상태 코드가 0이 아니라면 명령어가 실패한 것으로 간주하여 바로 스크립트가 종료되도록 만들어줬다.


‣ 동작 테스트

스크립트를 테스트해보자.

먼저 테스트용 nginx 컨테이너를 만들어주고

docker run -d -it --name nginx -p 80:80 nginx:latest

곧바로 docker kill로 컨테이너를 잠들게하자.

docker kill nginx

위에서 작성한 스크립트를 실행해보고 로그 파일을 확인해보면 아래와 같이 정상적으로 로그가 찍혀있는 것을 볼 수 있을 것이다.
image


그럼 컨테이너도 잘 올라왔는지 확인해보자.

docker container ls --filter "name=nginx"

image

container 또한 잘 올라왔다.


‣ 자동화 (cron)

만든 셸 스크리트를 서버가 부팅되면 자동으로 실행되도록 만들어주자.


{% include codeHeader.html name="/etc/systemd/system/{임의의 서비스 이름}.service" %}

[Unit]
Description=Run Docker Container
#After=suspend.target
#After=hibernate.target
#After=hybrid-sleep.target

[Service]
ExecStart=/home/dev/github/checkContainer/script.sh

[Install]
WantedBy=multi-user.target

위와 같이 systemd를 이용하여 스크립트가 시작 프로그램에 등록될 수 있도록 작성해주었다.

각 섹션별 내용은 아래와 같다.

  • Description: 서비스에 대한 설명
  • After: 서비스가 실행되기 전 의존해야 하는 대상이 있다면 명시
    • 예를 들어 Network가 준비된 후에 실행되어야 한다면 network.target을 지정
  • ExecStart: 실행할 스크립트 경로와 스크립트 파일 이름 명시
  • WantedBy: 서비스가 활성화 되어야 하는 타겟 지정 (일반적으로는 default.target을 명시)

systemctl daemon-reload
systemctl enable docker-reload.service

그리고 systemd Daemon을 reload하고 해당 Service를 시작 프로그램에 등록해주면 된다.


▶︎ --restart

번거롭게 위처럼 스크립트를 작성할 필요 없이

docker run --restart always ...

--restart 플래그를 사용하면 서버가 재기동 되었을 때도 자동으로 컨테이너가 재시작 된다.

즉, 번거롭게 스크립트를 작성할 필요가 없다는 뜻이다.


Docker Compose에서는 아래와 같이 쓴다.
{% include codeHeader.html name="docker-compose.yaml" %}

services:
  nginx:
    image: nginx:latest
    restart: always

Loading script...