[Docker] 스프링부트에 도커 적용하기 + M1맥에서 AWS용 도커파일 빌드하기

6 분 소요

서론

이전 게시물 : [Docker] 도커란 무엇일까?

저번 게시물에서는 도커가 무엇이고 어떻게 작동하는지에 대해 알아보았습니다. **그러면 이제 실제로 도커가 실제로 어떤방식으로 사용되는지 알아보기위해서, 제가 도커를 통해 제가 개발중인 **웹사이트의 개발환경과 서버를 이미지로 제작해서 도커허브에 올리고 이를 다시 AWS 서버에서 내려받아 해당 컨테이너를 동작시켜보도록 하겠습니다.

그럼 이번에는 도커허브에 있는 이미지가 아니라, 직접 도커허브에 내 개발환경과 파일을 이미지로 만들어서 업로드 해보도록 하겠습니다.

Dockerfile이란?

우리가 이미지를 직접 만들어 도커허브에 올리기 위해서는, ‘도커 파일’이 필요합니다. 물론 컨테이너안에서 직접 설치 하고 설정한 다음 이를 이미지 파일로 만들어 쓸 수도 있겠지만,

Dockerfile을 만들어주면, 패키지, 소스코드, 명령어, 환경변수를 기록하여 빌드해주기만 하는 것으로도 바로 원하는 환경을 구성할 수 있습니다.

무슨말인지 조금 어려울 수 있겠지만, 한번 해보면 왜 개발자들이 Docker를 선호하는지 알게 될 것입니다.

Dockerfile 만들기

Dockerfile은 이미지를 빌드하기 위한 파일로, 따로 확장자가 없는 텍스트 파일입니다. 기존에 도커 컨테이너 환경을 세팅하려면 베이스 이미지 다운로드 후 컨테이너에 접속해서 수동으로 환경설정을 해주던 방식과 다르게 자동으로 Dockerfile안에 기록한 내용대로 환경을 설정해줍니다.

원하는 디렉토리에 Dockerfile을 만들어줍니다.

image1

이 때 주의할점은 Dockerfile을 빌드 할 때에는 해당 파일 ‘Dockerfile’을 포함한 디렉토리와 그 아래에 있는 서브 디렉토리를 모두 Docker로 보내 이미지화 하기 때문에 반드시 원하는 파일만 들어갈 수 있도록 디렉토리를 설정 해주셔야 합니다. 불필요한 파일이 많아지면 도커 데몬의 성능이 저하될 수 있습니다.

먼저, 간단하게 Dockerfile에서 사용할 문법에 대해서 알아봅시다.

  • FROM - 컨테이너의 Base Image를 설정합니다.
  • MAINTAINER - 이미지를 생성한 사람의 이름 및 정보
  • LABEL - 컨테이너 이미지에 컨테이너의 정보를 저장
  • RUN - 컨테이너 빌드를 위해 Base Image에서 실행할 명령
  • COPY - 컨테이너 빌드 시 호스트의 파일을 컨테이너로 복사
  • ADD - 컨테이너 빌드 시 호스트의 파일 (tar, url 포함) 을 컨테이너로 복사
  • WORKDIR - 컨테이너 빌드 시 명령어 실행될 작업 디렉터리 설정
  • ENV - 환경 변수 지정용
  • ARG - 이미지 빌드를 위해 Dockerfile 내에서 사용하기 위한 값
  • USER - 명령 및 컨테이너 실행 시 적용할 유저 설정
  • VOLUME - 파일 또는 디렉토리를 컨테이너의 디렉토리로 마운트
  • EXPOSE - 컨테이너 동작 시 외부에서 사용할 포트 지정
  • CMD - 컨테이너 동작 시 자동으로 실행할 서비스나 스크립트 지정
  • ENTRYPOINT - CMD와 함께 사용하면서 명령어 지정 시 사용

이 명령어가 Dockerfile을 사용하기 위한 전부입니다. 어렵지 않죠??

Dockerfile 작성하기

저는 현재 동아리의 웹사이트를 제작하고 있는데요, 제가 개발하는 환경을 이렇습니다.

  • 자바 8버젼
  • 스프링 부트
  • Gradle

이 정도를 개발환경으로 소개할 수 있겠네요.

자바의 버젼도 여러가지이고, 스프링부트도 마찬가지죠. Gradle도 버젼이 많습니다. 이들은 서로 버젼간의 호환도 생각해주어야하고, 안에 코드가 해당 버젼과 호환이 안될 수도 있을 것입니다.

그렇다면 AWS에서 인스턴스에 제가 만든 웹서버를 돌린다고 가정하면 결국 AWS도 컴퓨터 하나를 대여하는 것 과 마찬가지인데, 제가 개발한 환경과 동일하게 AWS를 설정해주어야 오류없이 안전하게 제가 제작한 서버를 돌릴 수 있을 것입니다.

만약, 제 컴퓨터에서는 이상이 없었는데 AWS에서만 에러가 발생한다면, 다시 작업환경을 날리고 일일히 버젼을 확인해가며 동일하게 세팅해주어야 할 것입니다.

그럼 저는 이러한 것을 개선하기 위해서, 제 개발환경을 통째로 사진을 찍어 (이미지화 하여) 업체에 넘기고 (도커 허브에 올리고), 이를 AWS에서 다시 내려받아 동일하게 세팅 하게 해보도록 하겠습니다.

제가 설정한 Dockerfile의 내용은 다음과 같습니다.

1
2
3
4
5
6
FROM openjdk:8-alpine
RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v1.1.1/zsh-in-docker.sh)"
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
EXPOSE 8080

생각보다 굉장히 간단하죠?

그러면 한 줄 씩 해석해보겠습니다.

1
FROM openjdk:8-alpine

이 Dockerfile의 베이스 이미지는 openjdk:8-alpine 을 사용합니다. 한마디로 자바 8환경을 만드는 것이죠.

1
2
RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v1.1.1/zsh-in-docker.sh)"

이 부분은 꼭 설정하지 않아도 됩니다. 저는 bash 보다는 zsh이 편해서, 컨테이너에서 zsh를 사용해주기 위해 해당 코드를 넣었습니다. RUN은 뒤의 명령어를 컨테이너에 입력합니다.

1
ARG JAR_FILE=build/libs/*.jar

build/libs/ 디렉터리에 있는 .jar 파일을 변수로 저장합니다. ARG로 선언해준 변수는 사용하면 바로 사라집니다. 따라서 밑에서 이 변수를 사용하고 또 사용하기 위해서는 한번 더 선언하고 사용해주어야 합니다.

1
COPY ${JAR_FILE} app.jar

위에서 ARG로 선언해준 변수에 해당하는 파일 .jar 파일을 app.jar 라는 이름으로 컨테이너에 복사합니다.

1
ENTRYPOINT ["java","-jar","/app.jar"]

컨테이너 실행 후 변경되지 않을 사항에 대해서는 ENTRYPOINT를 사용합니다.
해당 명령은 “java -jar ./app.jar”의 명령을 컨테이너 시작과 동시에 실행합니다.

1
Expose 8080

외부 포트에서 들어온 포트 연결을 해당 컨테이너의 포트로 연결해줍니다.
어쩌면 이 명령어 중에 가장 헷갈리는 개념일 수 있는데, 이 부분에서는 뒤에서 설명 하도록 하겠습니다.

그럼 이제 이 Dockerfile이 이해가 되셨나요?

도커는 자바8 기반으로 환경을 세팅하고, zsh을 설치 한 뒤, 빌드된 서버인 jar파일을 컨테이너로 옮겨 실행합니다.

이렇게 만들어준 파일을 그럼 빌드해서 이미지로 만들어 보겠습니다. 명령어는 다음과 같습니다.

1
docker build -t lmj938/jamong-sever .

.을 빼먹지 말도록 주의 해주세요.

-t 옵션을 붙이면, 뒤에있는 내용이 이미지의 이름이 됩니다.

이렇게 하면 이미지 파일이 생성되는데 여기서 주의 할 점이 있습니다. 도커허브에 올리기 위해서는 [자신의 도커 허브 아이디] / [원하는 파일명]의 형식으로 이름을 작성해주어야 합니다. 이는 사람들의 도커파일 이름이 중복 되지 않기 위함이니 꼭 주의 해주세요.

image1

그러면 다음과 같이 이미지가 생성 된 것을 볼 수 있습니다. 이를 Run 하여서 컨테이너로 만들어 동작시켜주기만 하면, 제가 만든 웹서버를 제가 기록한 환경 설정 그대로에서 동작 시킬 수 있는 것이죠. 그럼 해당 이미지를 Run 해보도록 하겠습니다.

image1

마찬가지로 실행할 컨테이너의 이름을 설정해주는 창이 뜨는데, 아까는 이름만 정해주고 넘어갔지만, 이번에는 포트를 설정 해주어야 합니다.

ContainerPort를 보시면 8080/tcp라고 써있는걸 볼 수 있습니다. 이것이 바로 아까 EXPOSE 명령어를 통해서 사용할 컨테이너의 포트를 8080으로 설정해준 결과입니다.

그렇다면 Local Host에 해당하는 포트는 무엇일까요? 바로 내가 지금 사용하는 컴퓨터의 포트 겠죠. 계속 설명했듯이, 컨테이너를 실행시킨다는건 컴퓨터 안에서 또다른 컴퓨터를 돌리는 것과 비슷한 일입니다. 그렇다면 컨테이너에게도 컴퓨터가 가진 65535개의 포트가 있을 것 입니다. 만약 이 컴퓨터에 접속 하고 싶은 사람이 있다면, 어떤 포트로 연결 해야 할까요??

이전 게시물에서 사용했던 그림을 가져오겠습니다.
image1

해당 작업실들에 문이 있다고 해봅시다. 실제로는 작업실에는 각각 작업실마다 65535개의 문이 있지만 편의상 간소화 하여 그리도록 하겠습니다. 만약 손님이 밖에 있는 집에 용무가 있어 온게 아니라, 안에 있는 작업실에 방문했다면, 우리는 어떻게 이 손님을 안에있는 작업실로 안내해줄 수 있을까요?

간단합니다. 통로를 이어놓으면 됩니다.

image1

위와 같이 8080 문에는 첫번째 작업실의 8080문을, 8082문에는 두번째 작업실의 8080문을 연결해주면 밖의 손님이 안에있는 작업실의 주소를 모르더라도, 그냥 큰집의 문 번호만 알려주면 알아서 원하는 작업실로 출입할 수 있습니다.

첫번째 작업실로 오라고 하고싶으면 8080번을 알려주면 되고, 두번째 작업실로 오라고 하고싶으면 8082 문을 열고 들어오라고 하면 되겠죠. 지금 우리가 설정해주는 것이 바로 바깥 포트를 안쪽 포트에 연결하여 밖에서 들어온 요청을 해당 컴퓨터로 이어주는 작업입니다. 이를 ‘포트포워딩’이라 합니다.

그럼 우리는 8080포트를 컨테이너의 8080포트와 연결 해주도록 할 것 입니다.

image1

컨테이너 이름을 Server로 해주고, 8080포트를 Local Host 포트에 입력해주면, 밖의 컴퓨터의 8080포트로 들어오는 것은 곧, 컨테이너의 8080 포트로 들어가는 것 입니다. 둘은 연결되어 있으니까요.

그럼 우리의 localhost:8080 포트로 접속 하면, 제가 만든 서버로 접속 할 수 있을 것 입니다.

image1

예상대로 안에 컨테이너에서 실행한 서버에 정상적으로 접속 할 수 있는것을 확인할 수 있습니다.

AWS에 도커를 사용해서 서버 실행하기

그럼 이제 이 서버를 AWS에도 동일하게 구성해볼까요?

도커에 다루는 게시물이니 자세한 AWS까지는 설명하지 않겠습니다.

image1

AWS의 제 인스턴스로 접속한 상태에서, 우선 도커를 먼저 설치해 주겠습니다.

1
sudo yum install docker

그럼 AWS의 인스턴스에도 도커가 설치가 완료됩니다. AWS는 리눅스 기반이기 때문에 여기서는 도커 데스크탑과 같은 GUI를 사용할 수 없기 때문에 터미널로 docker 명령어를 사용해 주어야 합니다.

그럼 이제 우리가 아까 만들었던 이미지를 불러와야합니다. 아까 만든 이미지를 도커허브에 업로드하면, 우리는 pull 명령어 만으로 해당 이미지를 AWS 인스턴스에 설치할 수 있을 것입니다.

방법은 아주 간단합니다.

image1
해당 이미지의 메뉴에서 push to hub를 눌러주면 끝입니다. 이름까지 이미 허브 업로드 양식에 맞추어 바꾸어 놓았으니 누르기만 하면,

image1
위와 같이 업로드가 완료됩니다.

물론 터미널을 사용해도 되지만, GUI에서 하는게 훨씬 간편할 것입니다.

그럼 다시 AWS로 돌아와서,

1
sudo docker pull lmj938/jamong-server

명령어를 해주면 아까 우리가 업로드 한 이미지를 다운 받습니다.

혹시 여기서 아래와 같은 오류가 뜬다면

1
Error response from daemon: pull access denied for lmj938/jamong-sever, repository does not exist or may require 'docker login': denied: requested access to the resource is denied 

아래의 명령어를 사용해서 로그인 후 다시 진행 해주세요.

1
sudo docker login 

image1

1
sudo docker images

다음 명령으로 이미지가 잘 다운로드 되었는지 확인 할 수 있습니다.
image1

그럼 컨테이너를 실행하려면 어떻게 해야 할까요? 아까는 GUI로 run 버튼을 누르고 나온 창에 포트를 설정 해주었지만, 터미널 환경에서는 다음과 같이 포트를 설정해 주어야 합니다.

1
sudo docker run -d -p 8080:8080 --name Server lmj938/jamong-server

docker container run [옵션] 이미지명[:태그명][인수] 로 사용하면 되는데,
명령어 옵션은 구글링을 통해 찾아보면 여러가지 옵션을 찾아 보실 수 있습니다. 여기서는 가장 많이 다루는 세가지만 다루겠습니다.

  • -d
    컨테이너의 로그가 뜨지 않고 백그라운드에서 작동됩니다. -d 옵션을 사용하지 않고 run하게 되면 바로 컨테이너안에 있는 로그가 나오게 됩니다. 다음 명령을 치기 위해서 저는 -d 옵션을 사용해주겠습니다.

  • -p
    아까 포트를 설정하던 부분입니다. 로컬호스트 포트:컨테이너포트를 입력해주면 됩니다.

  • –name
    컨테이너의 이름을 정하는 옵션입니다.

이렇게 컨테이너를 작동시키고 8080 포트로 가면 접속이 완료될것 입니다.

image1

M1 맥북에서 빌드하기

하지만 M1 프로세서를 사용하시는 분들은 AWS에서 컨테이너를 run하면 다음과 같은 오류가 뜨면서 아마 작동이 안될 것입니다.

image1

이것은 M1은 arm 아키텍쳐를 사용하고 있기 때문에, 이 상태에서 그냥 build 하게 되면 arm용 이미지로 빌드를 하게 되기 때문입니다. 따라서 빌드할때 amd64 버젼을 따로 build한뒤 이를 push해서, AWS에서 pull 해주어야 합니다.

1
sudo docker pull --platform=linux/amd64 lmj938/jamong-server 

다음과 같이 platform을 linux/amd64로 설정해주신 이미지를 푸쉬하고, AWS에서 내려받으면, AWS에서도 정상적으로 해당 이미지를 인식할 수 있습니다.

카테고리:

업데이트:

댓글남기기