Overview

Docker 공식 홈페이지에 있는 Ubuntu 설치를 보고 따라하면 됩니다


1. Docker 설치

$ sudo apt-get update
$ sudo apt-get install \
    ca-certificates \
    curl \
    gnupg

2. Docker 공식 GPG 키 추가

$ sudo mkdir -m 0755 -p /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

3. Docker Repository 설치

$ echo \
  "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

4. Docker 설치

$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

5. Docker 실행 테스트

$ sudo docker run hello-world

# 실행된 도커 컨테이너 확인
$ sudo docker ps

# 이미지 확인
$ sudo docker images

테스트 용으로 hello-world 라는 이미지를 실행합니다.

docker image 를 따로 받지 않아도 없으면 자동으로 pull 을 먼저 땡깁니다.

Overview

보통 백그라운드에서 데이터를 실행할 때는 명령어 마지막에 & 를 붙여서 실행합니다.

$ java -jar my-app.jar &

하지만 위 명령어대로 하면 애플리케이션의 로그를 볼 수 없습니다.

따라서 nohup 을 사용합니다.

nohup 에 대한 자세한 설명과 & 와의 차이점은 https://joonyon.tistory.com/98 에 잘 정리되어 있습니다!


1. nohup 으로 백그라운드 실행

$ nohup java -jar my-app.jar &

[1] 97569
nohup: ignoring input and appending output to 'nohup.out'

2. nohup 로그 조회

이제 nohup.out 파일에 기록되는 로그를 확인할 수 있습니다.

# 로그 조회
$ cat nohup.out

# 로그 테일링
$ tail -f nohup.out

3. 백그라운드 Job 확인

백그라운드에서 실행되는 프로세스가 있는지 확인해볼 수 있습니다.

# 백그라운드에서 실행되는 프로세스 확인
$ bg
-bash: bg: job 1 already in background

# 출력
$ jobs
[1]+  Running                 nohup java -jar my-app.jar &

Reference

Shell Script 전체 코드

#! /usr/bin/env bash

hosts = (a.example.com b.example.com)
LOG = "access.log"
pattern = "ERROR|WARN"

for host in $hosts
do
    if [ -z $pattern ]; then
        ssh deploy@$host "tail -F $LOG" | awk -v server=$host '{print "["server"]", $0}' &
    else
        ssh deploy@$host "tail -F $LOG" | awk -v server=$host -v pattern=$pattern '$0~pattern {print "["server"]", $0}' &
    fi
done

trap "ps | grep ssh | grep $LOG | awk '{print \$1}' | xargs kill -9" INT
wait

명령어

각 명령어에 대한 자세한 설명은 생략하고 간단하게 나열합니다.


tail

ssh deploy@$host "tail -F $LOG" &

원격 서버에 명령어를 전달하려면 ssh deploy@$host "{command}" 형태로 사용합니다.

명령어의 끝에 & 을 붙여서 모든 tail 명령어를 백그라운드에서 실행시키고 출력값만 현재 쉘에서 모아서 봅니다.


awk

awk -v server=$host '{print "["server"]", $0}'

여기서 awk 명령어는 tail 로 출력되는 로그들을 특정 포맷으로 감싸서 재출력하는 기능을 합니다.

-v 옵션은 awk action 에 특정 변수를 넘겨주고 싶을 때 사용하고 $0 은 레코드 전체를 의미합니다.

awk -v server=$host '{print "["server"]", $0}' 은 로그들을 [a.example.com] 로그내용~ 으로 변환해서 출력해줍니다.

로그들이 여러개 쌓이면 어디 서버에서 발생한 건지 알수 없기 때문이죠


awk (filter)

awk -v server=$host -v pattern=$pattern '$0~pattern {print "["server"]", $0}'

ERROR 또는 WARN 로그만 보고 싶을 수도 있습니다.

정규표현식 ~ 오퍼레이터를 사용하여 레코드 전체 ($0) 에서 정규표현식 $pattern 이 포함되어 있는 경우에만 출력하도록 필터를 걸었습니다.

처음에는 grep 명령어를 사용했으나 속도가 너무 느려서 실시간 전달이 안되고 로그가 뚝뚝 끊겨서 전달되어 보기 굉장히 불편했습니다.


trap & wait

trap "ps | grep ssh | grep $LOG | awk '{print \$1}' | xargs kill -9" INT
wait

& 기호를 붙여 백그라운드에서 실행했기 때문에 로그를 그만 볼때 모든 프로세스를 한번에 종료시켜야 합니다.

trap <handler> <signal> 은 특정 시그널을 받았을 때 handler 를 실행시키는 명령어입니다.

wait 은 자식 프로세스가 종료될 때까지 대기합니다.

즉, 위 코드는 실행한 쉘 스크립트를 대기 시켰다가 Ctrl + C 시그널을 받았을 때 백그라운드 프로세스를 모두 kill 합니다.

wait 명령어를 넣지 않으면 스크립트를 실행한 후에 전달할 시그널이 애매합니다.

그리고 로그 보고 있을 거니까 부모 프로세스를 굳이 조작할 필요도 없습니다.


Reference

Overview

쉘 스크립트 파일을 보면 파일 최상단에 #!/usr/bin/env bash 가 있는 걸 많이 봤을겁니다.

잘 모를때는 그냥 주석으로 실행 환경을 의미하는 건 줄 알았는데 사실은 굉장히 중요한 문장이었습니다.


1. SheBang (#!)

우선 #! 은 주석이 아니라 이 자체가 하나의 기호입니다.

어원은 위키피디아에 의하면 어원은 Hash(#) 또는 Sharp(#) 과 Bang(!) 의 합성어에서 유래한 것 같다고 합니다.

#! 은 2 Byte 의 매직 넘버 (Magic Number) 로 스크립트의 맨 앞에서 이 파일이 어떤 명령어 해석기의 명령어 집합인지를 시스템에 알려주는 역할을 합니다.

#! 바로 뒤에 나오는 것은 경로명으로, 명령어들을 해석할 프로그램의 위치를 나타냅니다.

가장 일반적으로 사용되는건 #!/bin/bash 이며 앞으로 나올 명령어들을 주석을 제외하고 순서대로 실행시킵니다.

만약 경로가 정확하지 않다면 bad interpreter 가 발생하고 다른 인터프리터를 지정하면 문법 오류로 실패합니다.


1.1. 문법

#!<interpreter> [optional-arg]

문법은 위와 같으며 #! 뒤에 공백 (Space) 이 하나 있어도 동작합니다.

<Interpreter> 에는 프로그램의 절대경로가 입력되어야 합니다.


1.2. 예시

#!/bin/sh
#!/bin/bash
#!/usr/bin/pwsh
#!/usr/bin/env python3

2. #!/usr/bin/env 란?

위 설명에서 <interpreter> 에는 프로그램의 절대 경로가 와야 한다고 했습니다.

하지만 절대경로는 시스템에 따라 달라질 수도 있습니다.

파이썬을 예로 들면 시스템에 따라 /bin/local/python 또는 /usr/bin/python 에 위치할 수도 있고 버전 또한 python2 와 python3 둘다 설치되어 있을 수 있습니다.

이럴 때 #!/usr/bin/env 로 설정하면 절대경로에 상관 없이 인터프리터의 위치를 찾아서 실행해줍니다.

그러니 여러 환경에서 실행되야할 스크립트라면 #!/usr/bin/env 를 사용하는 게 좋습니다.


3. Test

간단하게 테스트를 해봅시다.

터미널을 켜서 vi test 로 파일 하나를 작성합니다.

#! /usr/bin/env bash

echo "Hello This is Bash"

위 파일에 실행 권한을 추가하고 실행하면 정상적으로 동작합니다.

# 권한 추가
$ chmod +x test

# 실행
$ ./test
Hello This is Bash

이번엔 다시 파일을 열어 인터프리터를 파이썬으로 지정합니다.

#! /usr/bin/env python

echo "Hello This is Bash"

권한은 아까 주었으니 다시 파일만 실행시키면 에러가 발생합니다.

파이썬 문법에 맞지 않아 오류가 발생한 겁니다.

$ ./test
  File "./test", line 3
    echo "Hello This is Bash"
                            ^
SyntaxError: invalid syntax

다시 파일을 열어 파이썬 문법으로 고쳐줍시다.

#! /usr/bin/env python

print "Hello This is Python"

파일을 실행시키면 정상적으로 실행됩니다.

$ ./test
Hello This is Python

Conclusion

SheBang(#!) 문법은 스크립트 파일에서 어떤 프로그램으로 해당 파일을 실행시킬 지 결정합니다.

#!/usr/bin/env 를 사용하면 인터프리터의 절대 경로를 지정하지 않아도 알아서 경로를 찾아주기 때문에 여러 시스템 환경에서 사용할 때 유용합니다.

테스트를 보면서 알 수 있었던 한가지 특징은 확장자를 입력하지 않아도 #! 으로 인터프리터를 지정하면 정상적으로 실행된다는 사실입니다.


Reference

+ Recent posts