환경

docker, spring boot, mysql 로 구동되는 application 에서 

어제만 해도 잘 동작하던 application 이 코드 수정 후 업데이트를 하자 아래와 같은 에러를 냈다.

- 에러 내역

더보기
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
	at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:836)
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:456)
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:246)
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:198)
	at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:138)
	at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:358)
	at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:206)
	at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:477)
	at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:560)
	at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:115)
	at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112)
	at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)
	at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:38)
	at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:108)
	... 50 common frames omitted
Caused by: com.mysql.cj.exceptions.CJCommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:500)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:481)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:61)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:105)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:151)
	at com.mysql.cj.exceptions.ExceptionFactory.createCommunicationsException(ExceptionFactory.java:167)
	at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:89)
	at com.mysql.cj.NativeSession.connect(NativeSession.java:144)
	at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:956)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:826)
	... 63 common frames omitted
Caused by: java.net.NoRouteToHostException: Host is unreachable
	at java.base/sun.nio.ch.Net.pollConnect(Native Method)
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:579)
	at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:549)
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:597)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:339)
	at java.base/java.net.Socket.connect(Socket.java:585)
	at com.mysql.cj.protocol.StandardSocketFactory.connect(StandardSocketFactory.java:155)
	at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:63)
	... 66 common frames omitted

찾아보니 네트워크 router 관련 문제라고 하였다.

 

확인

해당 서버에서 db 서버로 접속이 잘 되는지 command 접속하여 확인해봤더니 접속이 잘 되었다.

application 을 docker 없이 따로 구동시켰더니 잘 되었다.

따라서 docker 문제임을 알 수 있었는데, 따로 변경해주었던 사항은 없었어서

 

해결

image 를 삭제했다가 새로 말아서 하니 network를 잘 잡았는지 잘 되었다.

 

* 기타로 효과를 못봤던 작업은

docker 껐다 켜기 효과 없음

컴퓨터를 껐다 켜기 효과 없음

블로그 이미지

uchacha

개발자 일지

,

- @참고: https://blog.naver.com/alice_k106/221310477844

[root@docker-node01 ~] docker ps -a --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
 
NAMES               IMAGE               STATUS
C0                  ubuntu:14.04        Exited (0) 22 hours ago
cs

 

  1. exit code 0: 성공적으로 종료됨
  2. exit code 125: 도커 명령어 자체가 실패한 경우
  3. exit code 126: 컨테이너의 커맨드가 실패한 경우 
  4. exit code 127: 컨테이너의 커맨드가 존재하지 않는 경우
  5. exit code 128 + n: 리눅스 시그널 n에 해당하는 오류가 발생한 경우 
    1. exit code 130: (128+2) Control + C로 종료된 경우
    2. exit code 137 & 143: (128+9, 15) 컨테이너가 SIGKILL 및 SIGTERM 을 받은 경우
      • 참고로, 컨테이너의 메모리 자원 부족으로 인한 OOM 시 컨테이너의 강제 종료에도 SIGKILL이 사용되어 137로 출력된다.
    3. exit code 255: 종료 코드가 범위를 벗어나는 경우 
블로그 이미지

uchacha

개발자 일지

,

- @참고: https://docs.docker.com/compose/networking/

 

기본적으로 Compose는 당신의 앱에 단일 network를 세팅한다. 각각의 service에 대한 container는 default network에 합류하고, 각각은 다른 container와 container의 이름과 동일한 hostname으로 통신할 수 있다.

당신의 앱의 network는 "project 명"에 기반한다. 즉, 프로젝트의 directory 명이다. --project-name flag나 COMPOSE_PROJECT_NAME 환경 변수로 override 할 수 있다.

 

예를 들어, 당신의 앱이 myapp 이라는 디렉토리에 있고, docker-compose.yml 이 아래와 같다고 하자.

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres
    ports:
      - "8001:5432"

docker-compose up 을 하면, 다음과 같은 일이 벌어진다.

1. myapp_default 라는 network가 생성된다.

2. web의 configuration을 사용한 container가 생성되고, myapp_default 라는 network에 합류해서 host명은 web이 된다.

3. db의 configuration을 사용한 container가 생성되고, myapp_default라는 network에 합류해서 host명은 db가 된다.

각 컨테이너는 hostname webdb를 찾을 수 있고, 각각을 올바른 container의 IP address로 돌릴 수 있다. 예를 들어, web의 어플리케이션은 postgres://db:5432 로 연결하고 사용할 수 있다.

** HOST_PORTCONTAINER_PORT 를 구분하는게 중요하다. 위의 예에서, dbHOST_PORT8001 이고 container port는 5432(postgre의 기본값) 이다. service-to-service 네트워크는 CONTAINER_PORT를 쓴다. HOST_PORT 가 정의되면, service는 외부에서 접근 가능하다.

web container 내부에서, db에 접속하는 주소는 postgres://db:5432와 같고, host machine에서 연결 주소는 postgres://{DOCKER_IP}:8001 이다.

서비스 명 외부 접근 주소(DOCKER_IP:HOST_PORT) 외부 접근 주소와 매핑되는 컨테이너 내부 주소(hostname:CONTAINER_PORT)
web {DOCKER_IP}:8000 web:8000
db {DOCKER_IP}:8001 db:5432

 

container 업데이트 시

service에 대한 configuration을 변경하고 docker-compose up 을 하면, 예전 container는 삭제되고 새로운 container가 다른 IP 주소지만 같은 이름으로 생성된다. running container는 그 이름을 찾을 수 있고, 새로운 주소로 연결할 수 있으며, 예전 IP 주소는 동작을 멈춘다.

만약 어떤 컨테이너가 예전 컨테이너로 연결을 열면, 동작하지 않는다. 이러한 조건을 찾고, 이름을 찾고 연결하는게 컨테이너의 책임이다.

서비스 명 외부 접근 주소(DOCKER_IP:HOST_PORT) 외부 접근 주소와 매핑되는 컨테이너 내부 주소(hostname:CONTAINER_PORT)
web {DOCKER_IP}:8000 web:8000 (web에 대한 ip가 변경됨)
db {DOCKER_IP}:8001 db:5432 (db에 대한 ip가 변경됨)

 

Links

Links는 다른 서비스에서 닿을 수 있는 여분의 별칭을 정의할 수 있게 한다. 각 service는 다른 service에 service 이름으로 서로 통신할 수 있으므로, 서비스가 통신하는데 필수값은 아니다. 다음 예에서 dbweb으로부터 hostname dbdatabase로 연결가능하다.

version: "3.9"
services:

  web:
    build: .
    links:
      - "db:database"
  db:
    image: postgres

 

Multi-host networking

Docker Engine에서 Compose application을 Swarm mode enabled 하게 배포하면, multi-host communication을 가능하게 하는 내장 overlay driver를 사용할 수 있다.

Swarm cluster를 구성하는 방법을 보려면, Swarm mode section을 봐라. multi-host overlay networks를 알고 싶으면 Getting started with multi-host networking을 봐라.

 

custom network 구성하기

기본 app network 대신에, top-levelnetworks key로 자신만의 networks를 구성할 수 있다. 더 복잡한 topologies를 생성할수 있게 하고 custom network driver와 options을 구성할 수 있게 한다. Compose 에 의해 관리되지 않는 외부 생성된 network에 연결하기 위해 사용할 수도 있다.

각각의 서비스는 어떤 네트워크가 service-levelnetworks key와 연결될건지 구성할 수 있고, top-level networks key 아래에 언급된 entries의 이름들 리스트이다.

아래는 두 custom network를 정의한 예시 Compose 파일이다. proxy service는 db service와 분리되어 있고, 공통의 network를 공유하지 않는다. 오직 app만 둘다 통신이 가능하다.

version: "3.9"

services:
  proxy:
    build: ./proxy
    networks:
      - frontend
  app:
    build: ./app
    networks:
      - frontend
      - backend
  db:
    image: postgres
    networks:
      - backend

networks:
  frontend:
    # Use a custom driver
    driver: custom-driver-1
  backend:
    # Use a custom driver which takes special options
    driver: custom-driver-2
    driver_opts:
      foo: "1"
      bar: "2"

Networks는 연결된 각 네트워크에 대하여 ipv4_address 또는 ipv6_address 를 세팅함으로써 고정 IP 주소로 구성 가능하다. Networks는 주어진 custom name 으로도 가능하다.(version 3.5부터)

가능한 network configuration option에 대한 자세한 내용을 알려면, 다음을 참조해라.

 

Top-level networks key 중

external

true로 세팅되면, network가 Compose 외부에서 생성되었음을 지정한다. docker-compose up 이 생성하려 하지 않고, 존재하지 않으면 error를 뱉는다. 

3.3이나 그 아래 format에서는, external이 다른 configuration key(driver, driver_opts, ipam, internal)와 함께 사용될 수 없다. v 3.4 이상에서는 그런 제한이 없다.

아래 예시에서는, proxy가 외부와의 gateway이다. [projectname]_outside를 생성하려 시도하는 대신, Compose는 outside라 불리는 network를 찾고, proxy service container를 그 네트워크에 연결한다.

version: "3.9"

services:
  proxy:
    build: ./proxy
    networks:
      - outside
      - default
  app:
    build: ./app
    networks:
      - default

networks:
  outside:
    external: true
!! version 3.5 file format에서 deprecated 된 것
external.name 은 version3.5 file format 부터 사용되지 않고, 대신 name을 사용한다.

Compose file 내에서 언급한 이름과 별도로 network 이름을 지정할 수도 있다.

version: "3.9"
networks:
  outside:
    external:
      name: actual-name-of-network

 

default network 구성하기

따로 network를 구성하는 대신, networks 이름을 default로 두어서 앱 전반에 기본 network를 세팅할 수도 있다.

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres

networks:
  default:
    # Use a custom driver
    driver: custom-driver-1

 

기존에 존재하는 network를 사용하기

container가 기존 존재하는 network에 합류하길 원하면, external 옵션을 사용해라.

services:
  # ...
networks:
  default:
    external:
      name: my-pre-existing-network

[projectname]_default 의 네트워크를 생성하는 대신, Compose는 my-pre-existing-network를 찾고, 앱의 container와 연결한다.

블로그 이미지

uchacha

개발자 일지

,

- @참고: docs.docker.com/get-started/07_multi_container/

 

가능한 사항

DB 데이터를 local에서 가져오거나 local로 공유하기.

local src 데이터를 container와 공유해서 local src 수정 시 바로 container 에 반영하기.

 

구현방법

두 경우 모두 cli에서는 -v, docker file에서는 volume을 사용하면 된다.

 

- Dockerfile.share.src -> 소스 공유

FROM node:12-alpine
RUN apk add --no-cache python g++ make
WORKDIR /app
COPY app/package.json app/yarn.lock ./
#COPY app/spec ./spec
#COPY app/src ./src

#RUN yarn install --production
RUN yarn install

#CMD ["node", "src/index.js"]

##########################################
Build & Run Code
##########################################
#docker build -t getting-started-share-src -f Dockerfile.share.src .
#docker run -dp 3001:3000 -v "$(pwd)\app:/app" getting-started-share-src sh -c "yarn run dev"

 

- Dockerfile -> DB 데이터 공유

FROM node:12-alpine
RUN apk add --no-cache python g++ make
WORKDIR /app
COPY app/package.json app/yarn.lock ./
COPY app/src ./src
RUN yarn install --production
CMD ["node", "src/index.js"]

##########################################
Build & Run Code
##########################################
#docker build -t getting-started-prod .
#docker run -dp 3000:3000 -v "$(pwd)\todo-db:/etc/todos" getting-started-prod
블로그 이미지

uchacha

개발자 일지

,

- @참고: stackoverflow.com/questions/59526444/error-while-creating-mount-source-path-mkdir-host-mnt-d-file-exists

사용중인 docker version

Docker version 20.10.5, build 55c4c88

 

에러 메세지

Error response from daemon: error while creating mount source path '/run/desktop/mnt/host/f/vsc/getting-started/getting-started': mkdir /run/desktop/mnt/host/f: file exists

 

해결

window 오른쪽 아래 docker icon 오른쪽 클릭, Restart Docker 하니까 업데이트도 같이 되면서(Update/Restart Docker 인가 그랬다.) 해결됐다.. 뭐지..

블로그 이미지

uchacha

개발자 일지

,