1. volume을 이용해 Docker가 local file 참조하게 해서, 코드 변경 반영하게 하기.
https://github.community/t/docker-not-working/181304
https://devlog-wjdrbs96.tistory.com/311?category=902375
위 두 글을 참고했다.
먼저, 어제 작성한 dockerfile을 바꾼다.
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
FROM openjdk:11-jre-slim
WORKDIR /root
ADD ./build/libs/demo-0.0.1-SNAPSHOT.jar .
ENTRYPOINT ["java", "-jar", "demo-0.0.1-SNAPSHOT.jar"]
팀원이 jdk 11을 사용한다고 해서, jdk 11으로 바꾼다. 그리고, 어제 이용했던 방법은 argument로 내가 작성했던 파일로 주고, 그 파일을 container로 복사하고(COPY), java -jar /app.jar이라는 명령어를 이용해 .jar 파일을 실행(ENTRYPOINT)한 것이었다. 이걸 조금 바꾼다. 왜 바꾸었을 때 되는지는 잘 모르겠다. 위아래 내용이 크게 달라진 건, workdir을 직접 설정해 준거만 달라진 거 아닌가..? 음...
-> 왜 workdir을 해줘야 하는지 알았다. 컨테이너 상에서, root directory, 즉 workdir을 지정하지 않은 곳에서 작업을 하면 container를 만들 필요가 없어지기 때문에 mount를 하려면 무조건 하위 directory로 들어가야 하는 모양이다 .그래서 workdir을 설정해 주는데, 이게 중요한 것 같다.
- 왜 안될까?
아니 밑에거는 왜 안되는지 도무지 알 수가 없네... WORKDIR로 컨테이너 내부에서 작업 위치를 바꾸었고, ADD/COPY를 사용해서 파일 이름을 app.jar로 바꾸고, CMD에서 그 줄을 실행한 거 아닌가..? 스트레스받네 ㅋㅋ
FROM openjdk:11-jre-slim WORKDIR /root ADD ./build/libs/demo-0.0.1-SNAPSHOT.jar app.jar CMD java -jar app.jar
일단 되는 방식대로 해보자. (바로 위의 것으로) dockerfile을 수정했으니, 어제 했던 것처럼, image를 build하고 container 실행을 해 보자.
docker build -t springvoltest . docker images
springvoltest라는 image 하나가 만들어졌다. 이제 이 container가 local을 참조하도록 해 보자. mount한다라는 표현을 쓰는 것 같다.
docker run -d -p 8080:8080 -v ${PWD}/build/libs:/root springvoltest
여기서 -v 바로 뒤에는 {로컬의, 참조할 파일 경로}:{컨테이너 내부의 디렉토리}이다. 나의 경우 ${PWD}/build/libs 안에 .jar 파일이 만들어지고, dockerfile에서 WORKDIR을 /root로 설정했으니 위와 같이 작성했다. 이렇게 하면, dockerfile에서 작성했던 COPY라는 문구가 -v라는 옵션으로 인해 기능이 조금 바뀌는 것 같다. 원래대로였다면 ./build/libs/~~~.jar 이 파일을 컨테이너로 복사하는 건데, -v 옵션을 붙임으로 인해 ./build/libs/~~~.jar 파일을 참조하게 되는 것 같다. 그리고 중요한 거. ${PWD} 이거 소문자로 하니까 에러 나더라...
아무튼,
일단 작성해 두었던 hello vs code spring이 실행된다. 그러면, /src/main/java/com/example/demo/HelloController.java에 문장 뭔가를 추가해 보자. 그 이후,
./gradlew clean build
docker ps
docker restart {소스 수정한 컨테이너 ID}
이렇게 실행해 보자. 그러면,
내가 추가한 문장이 나온다! 해당 컨테이너가, 내 local에 있는 파일을 참조하고 있기 때문에 jar file을 이용해 새로 image를 만들고 container를 새로 만드는 그런 귀찮은 짓을 하지 않아도, restart 명령어 하나만으로 내가 수정한 소스가 실행되게 된다.
2. MySQL에서 사용했던 Database volume으로 저장해서, 컨테이너를 끄더라도 데이터 날아가지 않게 처리해 보기.
https://joonhwan.github.io/2018-11-14-fix-mysql-volume-share-issue/
위 글을 참조했다. 위, spring에서는 local에 있는 파일을 참조하도록 했다. 그러나 나는 내 컴퓨터에 mysql을 설치하기 귀찮기 때문에 docker에서 지원하는 volume이라는 기능을 이용해 mysql data를 docker volume 안에 저장할 수 있도록 해보고자 한다.
docker volume create mytestdb
docker volume ls
docker run --rm -d --name testdb -e MYSQL_ROOT_PASSWORD=asdf -v mytestdb:/var/lib/mysql mysql
내가 새로 mytestdb라는 volume을 만들고, volume 목록을 확인하는 것이다. 잘 생성된 것을 볼 수 있다. 그런 후 mysql image를 받아서 실행한다. 이 때 rm은 컨테이너를 종료하면 삭제하는 옵션, -d는 백그라운드 실행 옵션이다. 그리고 -v, volume 옵션에 mytestdb:/var/lib/mysql이라고 작성했는데, 바로 윗윗줄에서 작성한 mytestdb라는 volume을 참조하겠다는 것이다. 즉, 해당 container에서 /var/lib/mysql을 mytestdb라는 volume을 참조해서 사용하겠다는 말이 되는 것이다. local 참조와 같은 맥락이지 싶다. 그러면 현재 testdb라는 container가 background로 돌아가고 있다. 해당 container에 접속해 보자.
docker exec -it testdb mysql --password=asdf
mysql이 실행된다. 여기다가 대충 아무 schema, row를 삽입해 보자.
SHOW DATABASES;
CREATE SCHEMA test;
USE test;
CREATE TABLE working (ID INT(10) NOT NULL PRIMARY KEY);
INSERT INTO working VALUES(1);
SELECT * FROM working;
이러면 test라는 database 안에 working이라는 table 안에 ID가 1인 값이 들어가 있을 거다. 그러면, 지금 만든 container를 지워보자.
docker stop testdb docker ps
아까 -rm 옵션을 주었기 때문에, stop하면 testdb container가 삭제되게 된다. docker ps를 입력하면 컨테이너가 삭제된 것을 확인할 수 있다. 그러면 아까와 마찬가지로 새로 container를 만들어 보자. 이번 container 이름은 testdbtwo로 하자.
docker run --rm -d --name testdbtwo -e MYSQL_ROOT_PASSWORD=asdf -v mytestdb:/var/lib/mysql mysql docker exec -it testdb mysql --password=asdf
아까 만들었던 것처럼, mytestdb라는 volume을 참조하도록 하자. 그리고 docker exec 명령어를 이용해 container 실행.
SELECT * FROM test.working;
앞에 만들었던 schema 안에 있는 row가 출력된다.
즉, volume을 이용해서 local mysql 없이, docker volume을 이용해서 mysql data를 관리할 수 있게 되었다! 만약 이번 단계에서 작성한 mytestdb라는 volume이 아니라 다른 volume으로 들어가면 test라는 schema는 존재하지 않게 될 것이다. 또한, 2명이서 작업할 때, docker-compose만 이용하면 별도의 설치 없이 mysql을 이용할 수 있게 되고, 만약 volume만 공유할 수 있는 수단이 있다면 같은 db로 테스트 작업을 할 수 있게 될 것이다.
docker inspect "container-ID"
위 명령어를 이용해 container-ID에 해당하는 volume이 어디에 mount되어있는지 확인할 수 있다. key가 mount인 것을 찾아가면 된다.
3. docker-compose를 이용해서 mysql-spring 연동 해 보기
거의 마지막 단계다!
위 블로그 참고했다.
이번에는 mysql과 spring을 docker 위에서, docker-compose를 이용해서 진행하고자 한다. 앞 포스트에서 했던 것 처럼 VS Code spring initializr를 이용해 새 프로젝트를 만든다.
project
└ dockerex2
└ docker-compose.yml
└ backend
└ testcompose
└ build
└ gradle
└ src
└ gradlew
└ Dockerfile
...
대충 구성도이다.
project 폴더 - 제일 상위 폴더
dockerex2 폴더 - docker compose를 다뤄보기 위한 폴더.(제일 상위라고 생각하겠다)
backend 폴더 - spring 프로젝트가 있음.
링크 건 블로그에서는 React, Mysql, Spring 이렇게 했지만 나는 React 환경 모르기 때문에 react는 빼고, 일단 이렇게 구성했다. react도 그냥 spring 한 것처럼 폴더 지정하고 volume 지정하고 이거면 해주면 되지 싶다.
일단 내가 작성한 docker-compose.yml 파일과 Dockerfile을 먼저 올린다.
docker-compose.yml
version: "3"
services:
spring:
build:
context: ./backend/testcompose
dockerfile: Dockerfile
volumes:
- ./backend/testcompose/build/libs:/spring
ports:
- 8080:8080
restart: always
container_name: springcomp
depends_on:
- mysql
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysqlcomp:3306/test?autoReconnect=true&useSSL=false&useUnicode=yes&characterEncoding=UTF-8&autoReconnectForPools=true&serverTimezone=UTC
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: asdf
networks:
- backend-network
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: asdf
MYSQL_DATABASE: test
volumes:
- mytestdb:/var/lib/mysql
ports:
- 3306:3306
container_name: mysqlcomp
networks:
- backend-network
volumes:
mytestdb:
networks:
backend-network:
이 docker-compose.yml 파일을 실행함으로써 내가 작성한 spring, mysql 컨테이너가 실행된다. 옵션 하나하나 살펴보겠다.
/backend/testcompose/Dockerfile
FROM openjdk:11-jre-slim
WORKDIR /spring
ADD /build/libs/testcompose-0.0.1-SNAPSHOT.jar .
CMD [ "java", "-jar", "./testcompose-0.0.1-SNAPSHOT.jar" ]
이 파일은 2. 단계와 그렇게 달라진 게 없다. workdir만 바꾸고, jar 파일의 이름만 바꾸었다.
- services
services:
spring:
mysql:
react:
어떤 컨테이너를 만들 것인지에 대한 정의이다. 각 services 하위에 image, build, 등등의 option이 들어간다.
- spring
spring:
build:
context: ./backend/testcompose
dockerfile: Dockerfile
volumes:
- ./backend/testcompose/build/libs:/spring
ports:
- 8080:8080
restart: always
container_name: springcomp
depends_on:
- mysql
environment:
SPRING_DATASOURCE_URL: jdbc:mysql://mysqlcomp:3306/test?autoReconnect=true&useSSL=false&useUnicode=yes&characterEncoding=UTF-8&autoReconnectForPools=true&serverTimezone=UTC
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: asdf
networks:
- backend-network
build : context, dockerfile 항목이 있다. 빌드 환경을 말하는 거다.
context : 어떤 폴더에서 spring이라는 서비스가 실행되는지이다. 나는 backend/testcompose 안에 모든 소스가 있으므로 거기에서 작업했다는 뜻으로, ./backend/testcompose로 작성한다.
dockerfile : context 내에서 어디에 Dockerfile이 있는지이다. spring dockerfile은 /project/dockerex2/backend/testcompose에 있고, docker-compose.yml은 /project/dockerex2에 있고, context를 ./backend/testcompose로 작성했으니 바로 Dockerfile로 작성해 주면 된다.
volumes : 앞에서 주구장창 이야기했던 그 volume이다. build context와 같은 것이 아닌 것 같기 때문에 어디에 있는 volume을 사용할지 정한다.
docker run -d -p 8080:8080 -v ${PWD}/build/libs:/root springvoltest
위의 단계에서 /build/libs에 있는 것을 /root로 정의했는데, 이번에는 /spring 폴더에서 작업하고 싶기 때문에 위와 같이 작성했다
ports : 외부로 노출할 포트를 지정해 준다.
restart : 컨테이너 문제가 생길 경우 항상 재사용을 하게 한다. 백엔드 같은 경우에 오류 한 번 났다고 서버가 종료되 버리면 문제가 많으니 이렇게 지정한다.
container_name : 컨테이너 이름을 지정한다.
depends_on : 의존성이다. mysql이라는 service가 켜진 이후에 spring이라는 service를 실행하게 할 것이다.
environments : 환경이다. 어떤 db 링크로 접속할 것이고, username, password는 뭔지를 기재해 준다.
networks : spring service는 backend-network에 속하게 한다. 같은 network에 있는 container들끼리 통신이 가능하다. backend-database 통신을 해야 하고, frontend-backend 통신을 해야 하므로 react를 사용한다면
react network : frontend-network
spring network : frontend-network, backend-network
mysql network : backend-network
이렇게 작성해 주면 react-spring, spring-mysql 간의 통신이 가능해질 것이다.
- mysql
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: asdf
MYSQL_DATABASE: test
volumes:
- mytestdb:/var/lib/mysql
ports:
- 3306:3306
container_name: mysqlcomp
networks:
- backend-network
image : base image이다. spring같은 경우에는 내가 만든 image를 사용하기 때문에 build context, dockerfile을 지정했지만 mysql은 있는 것을 사용할 것이기에 이렇게 해 준다.
environment : 접속 환경이다. password는 asdf라고 두자.
volumes : 앞에서 했던 volume과 동일하다. mytestdb라는 docker volume을 사용할 거고, 이에 mount되는 경로는 /var/lib/mysql이라고 두자. 이후에 여기서 명시했던 mytestdb라는 volume을 사용할 거라는 언급을 해 주어야 한다.
- volumes, networks
volumes:
mytestdb:
networks:
backend-network:
volumes : 어떤 volume을 사용할 것인지에 대한 내용이다. 앞에서 mysql service volume에서 mytestdb라는 docker volume을 사용할 것이다, 라고 작성했기 때문에 이와 같은 volume을 사용할 거라고 따로 적어준다.
networks : 어떤 network를 사용했는지에 대한 선언이라 생각하면 될 것 같다.
자, 그러면 docker-compose 파일 작성이 끝났다. /project/dockerex2에서 docker compose를 실행해 보자.
docker-compose up -d
-d 옵션은 백그라운드 실행이다.
project\dockerex2> docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------------------
mysqlcomp docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp,:::3306->3306/tcp, 33060/tcp
springcomp java -jar ./testcompose-0. ... Up 0.0.0.0:8080->8080/tcp,:::8080->8080/tcp
docker-compose ps 명령어를 이용해 docker comopse 내의 container들이 돌아가고 있는 것을 확인했다. docker desktop에서 봐도 dockerex2라는 컨테이너가 잘 돌아가고 있고 image 또한 받아와졌다.
docker-compose도 특이하게 어려운 건 없다. 각 service를 잘 정의하고, service 내부의 요소들을 잘 정의하고, 명령어만 사용하면 바로 실행되도록 되어 있다. 아래 2가지는 가졌던 의문점, 해소점이다.
*** 앞에서 spring volume 이용했던 것처럼, 소스코드 수정한 거 반영하려면 어떻게 해야 함?
처음에는 아래의 방식으로 했는데 수정한 것이 반영되지 않았다.
1) spring 코드 수정
2) ./gradlew clean build
3) docker ps, docker restart {container-ID} 명령어를 이용해 spring container만 껐다 킴
이렇게 하니 해결되었다.
1) spring 코드 수정
2) ./gradlew clean build
3) docker-compose stop, docker-compose start를 이용해 compose된 것 자체를 껐다 킴
*** docker-compose 여러 곳에서 할 수 있는데 관리 어떻게 함?
command line에서 docker-compose ps 등의 명령어는, docker-compose.yml 파일이 있는 곳에서만 수행된다. 예로, 파일 구성을 아래와 같이 했다고 치자. dockerex3는 dockerex2를 복붙한 폴더이다. dockerex2, dockerex3 각각에서 docker-compose up -d를 이용해 컨테이너 총 4개를 실행했다고 가정하자.
project
└ dockerex2
└ dockerex3
만약 docker-compose.yml 파일이 없는 디렉토리에서 docker-compose ps를 입력하면 아래와 같이 나온다. 반면 docker-compose.yml 파일이 있는 곳에서는 내가 작성한 service들이 출력된다.
project> docker-compose ps
ERROR:
Can't find a suitable configuration file in this directory or any
parent. Are you in the right directory?
project\dockerex2> docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------------------
mysqlcomp docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp,:::3306->3306/tcp, 33060/tcp
springcomp java -jar ./testcompose-0. ... Up 0.0.0.0:8080->8080/tcp,:::8080->8080/tcp
물론 docker desktop에는 모든 docker compose container, docker container들이 보인다.
*** MySQL + Spring Boot on Docker에서 spring build가 안되는 문제(주로 hikari pool에서 문제가 생긴다)가 생기면...
"Docker + gradle Spring Boot with VS Code + JPA + MySQL 게시판 생성 - 1. 전체 구조 잡기" 이 글을 참고하면 될 것이다.
1) application.properties에는 localhost로 기재
2) docker-compose.yml의 spring service environment의 SPRING_DATASOURCE_URL은 mysqlcomp로 작성
3) mysql에 접속해 board database 만들기
1) 스프링 개발환경 구축하고 hello world 띄우기
2) 도커 이미지 파일로 실행시켜보기
3) docker volume 이용해서 도커에서 local 참조하게 해보기
4) docker compose 사용해서 mysql 연동해서 docker 상에서 돌려보기
5) AWS EC2 받아서 로컬, 서버 도커 각각 실행하고 소스 연동하는 거 해보기
6) 테스트 서버, 배포 서버 만들어서 각각 돌아가게 해보기
7) 이렇게 만든 local에서 spring CRUD 게시판 예제 만들고, 서버에 올리고, Docker로 실행해 보기.
- spring MVC 패턴 공부하기.
5), 6)은 kubernetes 사용하고 jenkins 사용하면 또 어떻게 될 지 모르겠어서, 일단 spring MVC 패턴 공부해서 CRUD 게시판 예제, Auth 관련 공부를 조금 한 후에 해야겠다. 그래서 다음 포스트는 spring MVC 패턴을 이용한, CRUD 게시판 예제가 되지 싶다.
'DevOps > Docker' 카테고리의 다른 글
[Docker] Redis + Docker + 간단한 명령어 (0) | 2022.10.04 |
---|---|
[Docker] Docker 데몬 run error 해결 (0) | 2022.10.04 |
[Docker] Docker - push / pull (0) | 2022.10.04 |