Spring 프로젝트 Docker로 로컬에서 실행하기
Spring 프로젝트를 Docker로 로컬에 띄우는 이유
- 여러개 프로젝트를 Intellij로 실행하면 컴퓨터 리소스를 많이 사용한다.
- 여러개 프로젝트를 Intellij로 실행하는 번거로움이 있다.
1. Dockerfile를 생성한다.
FROM adoptopenjdk/openjdk11
ARG JAR_FILE_PATH=target/*.jar
COPY build/libs/*.jar eureka.jar
ENTRYPOINT ["java","-jar","/eureka.jar"]
2. JAR 파일 생성
intellij에서 Gradle build하여 jar파일을 생성한다. 프로젝트 파일에서 build/libs에 jar파일이 생성되었는지 확인한다.
Dockerfile에서 정의한 build/libs 경로는 프로젝트 Build-Gralde설정에서 “IntelliJ IDEA”를 선택하고 build하면 생성되는 경로이다.
3. Docker Image 생성
docker build --tag eureka-server .
-
참고 Rebuild할 때 명령
docker build --no-cache --tag eureka-server .
다음 명령으로 생성된 이미지를 확인할 수 있다.
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
eureka-server latest eb8d4f9432d2 3 hours ago 486MB
provectuslabs/kafka-ui latest 14bc242097fb 2 weeks ago 289MB
wurstmeister/kafka latest a692873757c0 12 months ago 468MB
wurstmeister/zookeeper latest 3f43f72cb283 4 years ago 510MB
4. Docker Image 실행
docker run -d -p 8761:8761 --name eureka-server eureka-server
-d, –detach: 데몬, 백그라운드로 실행
-p, –publish: 호스트와 컨테이너 포트포워딩, <호스트 포트="">:<컨테이너 포트="">컨테이너>호스트>
–name: 컨테이너 이름
5. Docker Desktop 에서 확인 및 시작
3번에서 실행한 이미지를 중지하고 docker desktop에서 좌측 Containers 탭을 확인하면 랜덤으로 생성된 Name으로 eureka 이미지가 생성되어 있다. Actions에서 실행버튼을 클릭하면 eureka image가 실행된다.
Name명을 클릭하면 실행 로그가 보인다.
컨테이너 간 통신 오류
eureka와 동일하게 config-server를 docker image로 만들어 실행하였는데, 아래와 같은 오류를 뿜뿜하였다.
org.apache.http.conn.HttpHostConnectException: Connect to localhost:8761 [localhost/127.0.0.1] failed: Connection refused (Connection refused)
config-server의 application-local.yml에는 다음과 같이 eureka url를 포함하고 있는데, localhost:8761로 접속을 할 수 없는 것이다….
eureka(docker) - config-server(docker) : Connection refused
eureka(docker) - config-server(intellij에서 실행) : 성공
eureka:
client:
register-with-eureka: true
fetch-registry: true
registry-fetch-interval-seconds: 10
service-url:
defaultZone: http://localhost:8761/eureka
컨테이너간 통신은 아래와 같이 http://host.docker.internal로 해야 한다고 한다.
$ curl http://172.18.0.3:8000 # Bad
[Error!!] Connection Refused...
$
$ curl http://container1:8000 # Bad
[Error!!] Connection Refused...
$
$ curl http://host.docker.internal:8000 # Good
[Hello World] Response Data..
application-local.yml 파일을 다음과 같이 수정하고 이미지를 재빌드하여 실행하였다.
Dockerfile에 COPY build/libs/*.jar eureka.jar 같은 부분이 있기 때문에
반드시 Gradle Build를 다시하여 jar파일을 갱신한 다음 docker build를 실행해야 한다.
eureka:
client:
register-with-eureka: true
fetch-registry: true
registry-fetch-interval-seconds: 10
service-url:
defaultZone: http://host.docker.internal:8761/eureka
정상적으로 실행되었다.
Redis + Spring boot 함께 Docker에 올리기
로컬에서 테스트할 때 spring boot에 cache로 redis를 함께 사용하려면 docker-compose로 함께 docker로 올리면 됩니다.
-
spring 프로젝트에 Dockerfile을 생성 함.
FROM adoptopenjdk/openjdk11 ARG JAR_FILE_PATH=target/*.jar COPY build/libs/*.jar manager-api.jar ENTRYPOINT ["java","-jar","/manager-api.jar"]
-
docker-compose.yml을 생성합니다.
version: '3.4' services: redis: image: redis ports: - 6379:6379 manager-api: image: manager-api build: context: . dockerfile: ./Dockerfile ports: - 9091:9091 environment: - "SPRING_PROFILES_ACTIVE=localdocker" depends_on: - redis
-
Gradle build로 jar 생성 후 docker-compose 빌드
docker-compose -f "docker-compose.yml" up -d --build
-
Docker Desktop에서 확인하기
-
Redis Cli 사용해보기
Eureka Client IP
Docker에 eureka client service가 container에 배포되면 내부 IP(172.17.x.x, netmask 255.255.0.0) 를 할당 받는다. 만약 config-server를 docker로 배포하게 되면 eureka에 172.17.0.3과 같은 형태의 주소로 등록된다. 모든 service가 docker에 올라가 있다면 상관 없겠지만, 일부 서비스만 docker로 실행하고 intellij에서 특정 서비스를 기동하게 되면 localhost에서 config-server(172.17.0.3)으로 접근이 불가능하게 때문에 오류가 난다.
Fetching config from server at : http://172.17.0.3:8888/
2023-06-01 21:47:47.552 INFO 3532 --- [ restartedMain] c.c.c.ConfigServicePropertySourceLocator : Connect Timeout Exception on Url - http://172.17.0.3:8888/. Will be trying the next url if available
2023-06-01 21:47:47.552 WARN 3532 --- [ restartedMain] c.c.c.ConfigServicePropertySourceLocator : Could not locate PropertySource: I/O error on GET request for "http://172.17.0.3:8888/member-api/local": connect timed out; nested exception is java.net.SocketTimeoutException: connect timed out
Docker container들은 bridge(default, 다른 타입의 네트워크 구성도 있음) 형태로 구성되어 Docker0이라는 Virtual network interface를 통해 외부와 통신하게 되며, gateway는172.17.0.1 IP로 고정된다. 그리고 docker container간 통신도 가능하다.
Docker Container의 IP 및 상세 정보를 보려면 아래와 같은 명령을 실행한다.
docker inspect config-server
[
{
"Id": "1ae306312741c91fcabb75e093d1250c9ee86c76044684e2f3cb0efc28b9ddce",
"Created": "2023-06-02T00:30:44.3131193Z",
"Path": "java",
"Args": [
"-jar",
"/config-server.jar"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
}
...
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:03",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "31c0f9dc6bbc0fe256f31c0b62bc26994e44eff0e0155b867a546ffdd4d7e9c5",
"EndpointID": "78555d0b2e8cc8c5385c4cd2b4d68e76c67a5799b0adf74a89ee1d01d69af0f6",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:03",
"DriverOpts": null
}
}
}
]
config-server가 docker container에 실행될 때 내부 8888 -> 외부 8888로 포트포워딩 시켜두었기 때문에 config-server가 eureka에 localhost IP로 등록시키면 된다. 다만 127.0.0.1이 아닌 내 컴퓨터 IP를 넣어줘야 docker container 간에 연결도 가능해진다.
config-server의 application-local.yml
eureka:
client:
register-with-eureka: true
fetch-registry: true
registry-fetch-interval-seconds: 10
service-url:
defaultZone: http://host.docker.internal:8761/eureka
instance:
ip-address: 192.168.xxx.xxx
prefer-ip-address: true
intellij에서 gradle build한 docker build
docker build --no-cache --tag config-server .
docker image를 다시 생성하는 경우 container를 삭제하고 다시 생성하는 것이 좋다고 함. (https://stackoverflow.com/a/41323079)
docker stop <ContainerName>
docker rm <ContainerName>
docker rmi -f $(docker images -f "dangling=true" -q)
docker run -d -p 8888:8888 -e "SPRING_PROFILES_ACTIVE=localdocker" --name config-server config-server
docker run -d -p 8000:8000 -e "SPRING_PROFILES_ACTIVE=localdocker" --name api-gateway api-gateway
docker run -d -p 9093:9093 -e "SPRING_PROFILES_ACTIVE=localdocker" --name shop-api shop-api
docker run -d -p 9094:9094 -e "SPRING_PROFILES_ACTIVE=localdocker" --name member-api member-api
docker build --no-cache --tag api-gateway .
docker run -d -p 9091:9091 -e "SPRING_PROFILES_ACTIVE=localdocker" --name manager-api manager-api
Profile 인수 실행
docker run -d -p 9094:9094 -e "SPRING_PROFILES_ACTIVE=localdocker" --name member-api member-api
*위 설명은 로컬(local) 개발 환경에 관련된 내용이며, 운영(prod) 환경은 설정 방식이 다르다.
댓글남기기