▶︎ 개요
특정 컨테이너가 서버 재기동 시 알아서 올라오지 못하는 문제를 발견
이 문제를 해결하기 위하여 셸 스크립트를 활용하여 서버가 재기동 되면서 죽어버린 컨테이너를 다시 재기동되도록 만들어 보려 함.
‣ 셸 스크립트
{% 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
위에서 작성한 스크립트를 실행해보고 로그 파일을 확인해보면 아래와 같이 정상적으로 로그가 찍혀있는 것을 볼 수 있을 것이다.
그럼 컨테이너도 잘 올라왔는지 확인해보자.
docker container ls --filter "name=nginx"
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