포스트

[Spring Boot] Docker를 통해 Spring Boot 배포 시 로그 남기는 방법

처음으로 Spring Boot를 빌드하여 도커를 통해 EC2에 배포했을 때는 다음의 명령어로 이미지를 실행하였다.

[Spring Boot] Docker를 통해 Spring Boot 배포 시 로그 남기는 방법
  • 처음 Spring Boot를 빌드하여 도커를 통해 EC2에 배포했을 때 다음의 명령어로 이미지를 실행하였다.
    1
    
    docker run --name={project_name} -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/{project_name}:latest
    
  • 해당 명령어는 컨테이너 이름을 {project_name}로 설정하고, 호스트가 8080포트로 접속하면 해당 컨테이너가 존재하는 8080포트로 전달되게 함을 의미한다.
  • 다음과 같이 도커 이미지를 run하면 Spring Boot가 실행되며 바로 로그가 출력된다. ``` . __ _ __ _ _ /\ / __ __ _ ()_ __ __ _ \ \ \
    ( ( )_
    | ‘_ | ‘| | ‘ \/ ` | \ \ \
    \/ _)| |_)| | | | | || (_| | ) ) ) ) ‘ || .|
    | ||| |_, | / / / / =========||==============|_/=////

:: Spring Boot :: (v3.3.4)

2024-11-24T17:08:44.616+09:00 INFO 15576 — [persist] [ main] yyytir777.persist.PersistApplication : Starting PersistApplication using Java 17.0.8.1 with PID 15576 2024-11-24T17:08:44.618+09:00 INFO 15576 — [persist] [ main] yyytir777.persist.PersistApplication : The following 1 profile is active: “prod” 2024-11-24T17:08:45.217+09:00 INFO 15576 — [persist] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode. 2024-11-24T17:08:45.286+09:00 INFO 15576 — [persist] [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 63 ms. Found 3 JPA repository interfaces. 2024-11-24T17:08:45.792+09:00 INFO 15576 — [persist] [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http) …(생략)

1
2
3
4
5
- 하지만 이의 단점은 로그 출력 화면이 터미널을 독점하게 된다. 해당 화면을 나가려면 ctrl + c를 통해 나가는 방법밖에 없다. 
- 더군다나 화면을 나가게 되면 해당 컨테이너는 종료상태가 된다 즉, 해당 컨테이너의 실행 상태를 유지하려면 로그 화면을 유지하는 방법밖에 없다.
* 처음에는 백그라운드에서도 컨테이너가 실행상태를 유지하는 방법을 몰랐었다. 도커 이미지를 실행시킬 때 `-d` 옵션을 통해 백그라운드에서 컨테이너를 실행할 수 있다.

docker run –name={project_name} -d -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/{project_name}:latest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
* 이를 통해 로그 출력 화면을 보지 않고 컨테이너를 실행하는 동시에 터미널을 사용할 수 있게 된다
* 하지만 이렇게 되면 로그는 어떻게 확인해야할까? 해당 애플리케이션에 오류가 발생했을 때 로그를 확인해야하는데 확인할 수 없게 되었다.
* 구글링을 하며 백그라운드에서 컨테이너를 실행하면서 로그를 확인할 수 있는 다양한 방법을 찾아보았다.

---


### 첫 번째 방법
* `nohup`이라는 명령어를 사용하는 방법이다. HUP신호를 무시하도록 만드는 POSIX명령어라고 한다.
* 터미널로 향하는 출력을 넘겨주기 처리를 하지 않았을 경우 `nohup.out`이라는 이름의 파일로 처리된다.
* 사용법은 실행하고자 하는 명령어 앞에 `nohup`을 붙여주기만 하면 된다.
* Spring Boot의 `jar`파일을 실행하는 `Dockerfile`의 명령어에 붙여줄 수 있다.
```Dockerfile
FROM openjdk:17-jdk  
  
ARG JAR_FILE=build/libs/project_name-0.0.1-SNAPSHOT.jar  
  
COPY ${JAR_FILE} /project_name.jar  
  
ENTRYPOINT ["nohup", "java", "-jar", "-Dspring.profiles.active=prod", "/project_name.jar"]
  • 이렇게 하면 될 줄 알았다… 문제는 도커를 통해 jar파일을 실행하고 있다는 것이었다.
  • nohupshell에서 실행되는 명령어이다. Dockerfile에서의 ENTRYPOINT의 명령어는 엄밀히 말해 shell이 아닌 도커 컨테이너에서 실행되는 명령어였던 것이다.
  • nohup을 사용하려면 도커가 아닌 직접 jar파일을 실행하면 된다.
    1
    
    nohup java -jar project_name.jar
    
  • 이때는 도커를 사용하지 않으므로 java를 실행할 수 있도록 맞는 버전의 java jdk를 설치해야한다.
  • 도커와는 어울리지 않는 방법이므로 다른 방법을 또 찾아보았다.

두 번째 방법

  • 다음과 같은 방법을 찾아냈다.
    1
    2
    3
    4
    
    FROM openjdk:17-jdk 
    ARG JAR_FILE=build/libs/proejct_name-0.0.1-SNAPSHOT.jar 
    COPY ${JAR_FILE} /proejct_name.jar 
    ENTRYPOINT ["sh", "-c", "java -jar -Dspring.profiles.active=prod /proejct_name.jar > /logs/application.log 2>&1"]
    
  • > /logs/application.log는 출력을 /logs/application.log에 저장하겠다는 의미이며, 실행 중에 발생하는 메세지가 파일에 기록된다
  • 2>&1의 의미는 다음과 같다.
    • 2>는 에러의 리다이렉션,
    • &1는 출력의 파일 디스크립터의 참조
  • 즉, 에러와 출력을 같은 파일에 기록하라는 뜻이라고 한다.
  • sh - csh명령어로 뒤에 오는 문자열을 명령어로 실행하도록 지시한다.
  • "java -jar -Dspring.profiles.active=prod /proejct_name.jar > /logs/application.log 2>&1"라는 복합 명령어를 실행할 수 있도록 하는 명령으로 이해하였다.
  • 해당 Dockerfile로 도커 이미지를 실행하였더니 /logs/application.log파일에는 아무것도 기록되지 않았다.
  • 쓰기 권한이 없나 싶어 /logs폴더에 다음과 같이 권한을 수정하였지만 바뀌는건 없었다.
    1
    
    chmod -R 777 /logs
    

세 번째 방법

  • 너무나도 쉬운 방법이 있었는데 완전히 돌아갔다…
  • 도커 자체에서 컨테이너의 출력을 로그 시스템에 저장한다는 것이었다. docker Docs
  • 위의 링크에 따르면, docker logs --option [container id || container name] 를 통해 해당 컨테이너의 STDOUT과 STDERR(표준출력, 표준에러)를 확인할 수 있다.

  • 사용 가능한 옵션은 아래와 같다.

| Option | Default | Description | | ——————————————————————————- | ——- | —————————————- | | --details | | 디테일한 추가 로그를 제공 | | -f, --follow | | 실시간 로그를 보여줌 | | --since | | 특정 시간 이후의 로그를 보여줌 | | -n, --tail | all | 끝에서부터 n개의 로그를 보여줌 | | -t, --timestamps | | 각 로그 앞에 타임스탬프를 보여줌 (Spring Boot에서는 필요 X) | | --until | | 특정 시간 이전의 로그를 보여줌 |

Example

docker logs --details <container>

docker logs -f <container>

docker logs --since="1h" <container>

docker logs --since="2024-11-24T11:12:00Z" <container>

docker logs -n 100 <container>

docker logs -t <conatiner>

docker logs --until="1h" <container>

  • 이렇게 사용자의 요구사항에 맞게 다양한 형식으로 도커 컨테이너의 로그를 출력할 수 있다.
  • 이것도 모르고 자꾸 엄한곳을 파고 있었다…
  • 이렇게 되면 설정 파일은 다음과 같게 된다.
    Dockerfile

    ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "/project_name.jar"]

  • nohup을 붙이지 않아도 된다.
    gradle.yml

    docker run --name=project_name -d -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/project_name:latest

  • 백그라운드에서 실행한다는 의미의 -d를 붙여야 한다.

  • 이제 docker logs <container>를 통해 쉽게 로그 확인이 가능해졌다.
  • 공식 문서만 조금 읽어도 해결 가능했는데 왜이렇게 돌아가려고 했는지 후회된다.
  • 앞으로 어떤 기술에 대해 알고자 할 때 공식 문서를 먼저 훑어보는 버릇을 들이고자 노력해야겠다.

원문: Velog

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.