서버에 어디까지 파일을 제공해야 할까?
- 소스파일 전체 제공
- 서버에서 소스파일 빌드해서
.jar
파일 만들기 - Dockerfile 파일로 도커 이미지 빌드하기
- docker-compose.yml 파일로 도커 컨테이너 실행
- → ❓ 서버에 소스파일까지 제공하는 건 너무 투머치 아닐까?
- 서버에서 소스파일 빌드해서
.jar
파일과 Dockerfile 파일, docker-compose.yml 파일 제공- 로컬에서 소스파일 빌드해서
.jar
파일 만들어서 서버에게 제공 - Dockerfile 파일로 도커 이미지 빌드하기
- docker-compose.yml 파일로 도커 컨테이너 실행
- → ❓ 빌드한
.jar
파일을 서버에 깔끔하게 제공하는 방안은? github release에 업로드 하는 방안도 있긴하다!
- 로컬에서 소스파일 빌드해서
- 빌드된 도커 이미지 파일, docker-compose.yml 파일 제공
- 로컬에서 소스파일 빌드해서
.jar
파일 만들기 - 로컬에서 Dockerfile 파일로 도커 이미지 빌드해서 이미지 서버에 제공
- docker-compose.yml 파일로 도커 컨테이너 실행
- → ❗도커 이미지는 docker hub를 통해 네트워크 상으로 이미지를 서버에 제공할 수 있다!
- 로컬에서 소스파일 빌드해서
아무튼,
- 서버 머신에선 소스파일 미포함을 비롯해서 가능한 간단하게 배포를 진행하고 싶고,
- elasticsearch를 docker로 배포해본 결과, 잘 세팅하면 서버에서 올리고 내리는게 매우 간편하다.
달성하고자 하는 목적
- Dockerfile 파일을 구성하여 docker image 빌드
- 빌드한 도커 이미지를 Docker hub에 업로드
- docker-compose.yml 파일을 구성하여 서버에서는
docker-compose up
이라는 간단한 커맨드로 배포
1. application.yml
파일이 외부 변수를 받도록 설정하기
application.yml
파일엔 여러 설정 정보가 들어간다.
민감한 정보가 기록되는 경우가 많으니 ${}
placeholder를 활용해서 외부에서 환경 변수를 주입해서 사용할 수 있도록 작성한다.
spring:
data:
web:
pageable:
default-page-size: 10
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
properties:
hibernate:
default_batch_fetch_size: 100
elasticsearch:
username: ${ES_USERNAME}
password: ${ES_PASSWORD}
uris: ${ES_URIS}
2. 소스 파일 빌드하여 .jar
파일 생성하기
./gradlew bootjar
명령어로 빌드가 가능하다.
application.yml
에서 ${}
형태로 환경변수를 활용하는 설정 값을 @Value
어노테이션을 활용해 사용하기 때문에 build
명령을 통해 진행하는 테스트 코드가 동작하지 않는 이슈가 있어, bootjar
명령어를 사용한다.
→ 이 부분은 추후 해결할 수 있는 방법이 있을지 확인할 필요가 있다!
프로젝트 내부 build/libs/
디렉토리 하위에 .jar
파일이 생성된다.
2-1. .jar
파일이 정상적으로 실행되는 지 테스트 커맨드
--key=value
형태로 사용자 환경 변수를 주입할 수 있다.
이 커맨드로 application.yml
파일에 ${key}
형태로 작성한 위치에 값을 주입한다.
java -jar build/libs/[...].jar \
--DB_HOST=[db_host] --DB_PORT=[db_port] --DB_NAME=[db_name] \
--DB_USERNAME=[db_username] --DB_PASSWORD=[db_password] \
--ES_USERNAME=[es_username] --ES_PASSWORD=[es_password] \
--ES_URIS=[es_uris]
3. Dockerfile
구성하기
도커 이미지를 생성하기 위해서는 Dockerfile이라는 이미지 빌드용 DSL 파일을 사용한다.
FROM eclipse-temurin:17-jdk-alpine
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
FROM
: 생성하려는 이미지의 베이스 이미지를 지정한다. (필수)- hub.docker.com에서 제공하는 공식 이미지를 권장한다.
- 작은 크기의 이미지(slim)와 리눅스 배포판인 알파인(Alpine) 이미지를 권장한다.
- 스프링 어플리케이션이므로 베이스 이미지로는 jdk를 지정한다.
- 개발 환경의 버전과 동일하게 jdk 17 버전을 지정한다.
ARG
: docker build 시점에서 변수 값을 전달받기 위해 변수를 정의한다.docker build --build-arg JAR_FILE=[..]
같은 형태로 사용한다.- docker-compose 파일에서 변수를 사용하는 방법은 docker-compose 파일을 구성하는 파트에서 정리 예정.
COPY
: 호스트 환경의 파일, 디렉터리를 이미지 안에 복사한다.- 빌드 작업 디렉터리의 외부 파일은 COPY할 수 없다.
- 비슷한 역할을 하는 명령어로
ADD
가 있다.
명령어의 기능이 더 많은데, 이 확장된 기능이 필요없다면COPY
명령어 사용을 권장한다.
ENTRYPOINT
: 생성된 이미지를 컨테이너로 실행할 때 실행되는 명령어이다.application.yml
에 주입할 환경 변수를 넣기 위해"--DB_HOST=${DB_HOST}"
같은 값을 사용해주자.
4. 도커 이미지 빌드
Dockerfile 파일을 작성하였으니, 해당 파일로 도커 이미지를 빌드해보자.
docker build --build-arg JAR_FILE=build/libs/[jar 파일 경로] -t [docker_username]/[image_name]:[version] . --platform linux/x86_64
--build-arg
: Dockerfile에서ARG JAR_FILE
로 명시한 변수 값을 채운다.- 일반적으로
build/libs/
디렉토리 하위에 생성되는.jar
파일의 경로로 넣어준다.
- 일반적으로
-t
: 빌드한 이미지의 이름을 지정한다- docker hub에 업로드하기 위해
[docker_username]/
으로 이미지 이름을 시작한다. - 일반적으로
:[version]
을 뒤에 붙여 이미지의 버전을 명시한다.
- docker hub에 업로드하기 위해
.
: Dockerfile 파일의 경로를 지정한다.- Dockerfile 파일이 존재하는 디렉토리에서 실행 시 간단하게
.
로 지정이 가능하다.
- Dockerfile 파일이 존재하는 디렉토리에서 실행 시 간단하게
docker image ls
명령어로 도커 이미지가 정상적으로 빌드 되었는지 확인 가능하다.
4-a. [ISSUE] eclipse-temurin:17-jdk-alpine
베이스 이미지 로딩 에러
=> ERROR [internal] load metadata for docker.io/library/eclipse-temurin:17-jdk-alpine 1.9s
------
> [internal] load metadata for docker.io/library/eclipse-temurin:17-jdk-alpine:
------
Dockerfile:1
--------------------
1 | >>> FROM eclipse-temurin:17-jdk-alpine
2 |
3 | ARG JAR_FILE
--------------------
ERROR: failed to solve: eclipse-temurin:17-jdk-alpine: no match for platform in manifest sha256:fe702d6a9b2d0855f29154512358cd5c0c866b8b16544589e254a97743304d1a: not found
Mac M1에서 특정 베이스 이미지를 불러오는 경우 발생하는 이슈라고 한다.
M1 칩의 경우 linux/arm64/v8
을 베이스로 이미지를 빌드한다. 그러나 arm64의 경우 이미지 파일이 없는 경우도 있다고 하니, docker build
명령어 실행 시 --platform linux/x86_64
단락을 추가하여 플랫폼을 명시해주면 해결 가능하다.
4-b. 빌드된 도커 이미지가 정상적으로 실행되는지 docker run
으로 테스트
docker run -d --name=[지정할 컨테이너명] \
-e DB_HOST=[db_host/mysql 컨테이너명] -e DB_PORT=[db_port] -e DB_NAME=[db_name] \
-e DB_USERNAME=[db_username] -e DB_PASSWORD=[db_password] \
-e ES_USERNAME=[es_username] -e ES_PASSWORD=[es_password] \
-e ES_URIS=[es_uris (ex elastic-container:port)] \
--network [elasticsearch 컨테이너, mysql 컨테이너와 동일한 network] \
-p 8080:8080 [실행할 이미지 이름]
5. 빌드된 도커 이미지를 docker hub에 업로드
Docker 로그인
docker login
docker desktop에서 이미 로그인을 완료한 상태라 자동으로 로그인이 된다.
그렇지않다면 Username과 Password를 입력해서 로그인한다.
Docker hub에 push
docker push [image name]
그대로 push 해주자
5. docker-compose.yml
파일 구성하기
이미 elasticsearch를 docker-compose 구성해서 실행중이다.
그리고 검색 서버는 elasticsearch가 켜져있지 않으면 서버를 실행할때 엘라스틱서치와 연결이 되지 않는다는 이슈와 함께 서버가 실행되지 않는다.
따라서 elasticsearch를 실행하는 docker-compose.yml
파일에 스프링 어플리케이션 실행을 묶어서 구성하자!
https://github.com/MerseongSanghoe/sooljari-docker-elastic
docker-compose.yml
파일 수정
services:
elasticsearch:
...
networks:
- backend
search:
image: [docker hub에 업로드한 이미지]:${SEARCH_VERSION}
container_name: [사용할 container 이름]
ports:
- 8080:8080
environment:
- DB_HOST=${DB_HOST}
- DB_PORT=${DB_PORT}
- DB_NAME=${DB_NAME}
- DB_USERNAME=${DB_USERNAME}
- DB_PASSWORD=${DB_PASSWORD}
- ES_USERNAME=elastic
- ES_PASSWORD=${ELASTIC_PASSWORD}
- ES_URIS=${ELASTIC_CONTAINER_NAME}:9200
networks:
- backend
restart: unless-stopped
depends_on:
- elasticsearch
networks:
backend:
external: true
- elasticsearch와 search의 network를
backend
로 통일시켰다.external: true
옵션을 주어 해당 docker-compose와는 독립적인 도커 네트워크와 연결하도록 설정했다.- 다른 팀원의 담당 파트인 MySQL 도커 세팅시 같은 네트워크를 사용하도록 설정했다.
environment
옵션으로 환경 변수를 지정하였다.depends_on
옵션으로 elasticsearch가 먼저 실행되도록 정의하였다.depends_on
옵션이 완전히 실행되기까지 기다리는 것이 아니고 단순히 먼저 up 커맨드를 날리는 정도의 깊이라는 이야기가 있어, 추후 elasticsearch가 완전히 실행된 후 spring 서버가 실행되도록 지정할 필요가 있는지 모니터링 필요
.env
파일 수정
# elasticsearch 컨테이너 이름
ELASTIC_CONTAINER_NAME=sooljari-elasticsearch
# elasticsearch 'elastic' 유저 비밀번호
ELASTIC_PASSWORD='' # change this
## 검색 서버 환경 변수
# 검색 서버 이미지 버전
SEARCH_VERSION=0.0.1
# 검색 서버 DB 연결 정보
#
# DB HOST는 DB container 이름으로 설정 -> 같은 컨테이너 네트워크상이기 때문에 가능
DB_HOST='sooljari-mysql'
DB_PORT='3306'
DB_NAME='' # change this
DB_USERNAME='' # change this
DB_PASSWORD='' # change this
레포지토리에 올린 .env.example
기준으로 올려두었다.
elasticsearch와 db의 경우 원격 dev 서버에는 모두 docker로 배포되며 같은 네트워크를 공유하는 컨테이너들이기 때문에 컨테이너 명으로 연결이 가능하다.
마지막! 컨테이너 실행
docker-compose up -d
추후 진행하고 싶은 작업
- Github action을 도입해서 main 브랜치에 업데이트가 있을 때 자동으로 소스 빌드 및 docker image 빌드 후 Docker hub에 도커 이미지 재배포 진행하는 구조 구축하기
- 내 관할이 아닌 mysql 쪽도 묶어서 한 번의
docker-compose up
커맨드로 mysql + elasticsearch + spring application을 컨테이너에 올리기