프롤로그
여기서부터 공식 설명서 docs 들이 없다. 그래서
docker-java/docker-java-api/src/main/java/com/github/dockerjava/api at main · docker-java/docker-java
Java Docker API Client. Contribute to docker-java/docker-java development by creating an account on GitHub.
github.com
여기 직접 돌아다니면서 어떤 명령어들이 무엇인지 확인을 해보고, 해당 명령어가 어디서 부터 왔는지, 어디랑 연결되어있는지 조금씩 파악하면서 하느라 (물론 전지전능하신 GPT 형님 없었으면 못했음) 작성하기 어려웠다.
총 3편으로 나눠서 컨테이너 생성 글을 작성할 계획인데
1편에서는 딱 컨테이너 생성하는 명령어만
2편에서는 컨테이너 실행과 포트 바인딩, 이미지 없을 때 불러오기
3편에서는 추가적인 생성시에 필요한 디테일
로 계획하고 있다.
docker-java를 사용하기 위해서는 반드시
2025.02.13 - [웹 개발] - [SpringBoot] docker-java 사용하여 스프링부트로 도커 조작하기 - 사전준비
[SpringBoot] docker-java 사용하여 스프링부트로 도커 조작하기 - 사전준비
프롤로그스프링부트 공부한지 벌써 3개월째...회원가입 로그인 이런 것도 적을까말까 고민했는데 이미 노션에다가 정리하면서 적어두어서.... 졸업작품을 만들고 있는데, 내가 노션에다가 실패
taesan-smj.tistory.com
사전 세팅을 먼저 진행을 해라.
추가적으로 이 프로젝트를 진행하기 위해 현재 준비되어 있어야하는 것은 스프링부트 작성을 위한 인텔리제이(스프링 준비 과정은 매우 많이 나와 있다. 그래서 따로 작성은 블로그에서 안 했다), 도커, POSTMAN 이다. 이 세 개는 반드시 필연적으로 준비가 되어야 한다.
시나리오
현재는 사용자로부터 운영체제, 버전, 사용자이메일(아이디) 3개의 값을 프론트엔드로부터 입력받으면 해당 컨테이너를 생성을 하고, 사용자에게 생성된 컨테이너 ID와 해당 컨테이너에 접근할 수 있는 Port번호를 return해준다. 만약 생성이 되지 않으면 오류 메시지를 리턴해준다.
그렇기에, REST API 작성을 진행한다.
CreateContainerRequestDto 작성
방금 시나리오에서 사용자로부터 운영체제, 버전, 사용자 이메일 3개의 값을 프론트엔드로부터 값을 입력받는다고 하였다.
사용자의 입력을 받고 오는 그릇을 만들기 위하여, 즉 사용자로부터 오는 입력을 포함하기 위하여 CreateContainerRequestDto라는 파일을 하나 생성하고 아래와 같이 작성한다.
//CreateContainerRequestDto
package com.hanbat.dotcar.container;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CreateContainerRequestDto {
private String os; //운영체제
private String version; //버전
private String userEmail; //사용자 이메일(ID)
}
ContainerInfoDto 작성
시나리오에서 생성된 컨테이너의 컨테이너 ID, 해당 컨테이너에 접근할 수 있는 포트번호를 리턴해서 주어야 하기 떄문에 그거에 맞춰서 ContainerInfoDto를 작성해준다. (물론 이것은 직접적으로 사용자에게 주는 것이 아닌 임시저인 DTO일 뿐이다. 나중에 CreateContainerResponseDto로 똑같은 것을 만들어서 그 값을 전달해준다)
package com.hanbat.dotcar.container;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class ContainerInfoDto {
private String containerId; //컨테이너 아이디
private String port; //포트 번호
}
ContainerService 작성
ContainerService 부분은 docker-java 연결을 위한 초기 설정
2025.02.13 - [웹 개발] - [SpringBoot] docker-java 사용하여 스프링부트로 도커 조작하기 - 사전준비
[SpringBoot] docker-java 사용하여 스프링부트로 도커 조작하기 - 사전준비
프롤로그스프링부트 공부한지 벌써 3개월째...회원가입 로그인 이런 것도 적을까말까 고민했는데 이미 노션에다가 정리하면서 적어두어서.... 졸업작품을 만들고 있는데, 내가 노션에다가 실패
taesan-smj.tistory.com
여기서
package com.hanbat.dotcar.container;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.PullImageResultCallback;
import com.github.dockerjava.api.exception.NotFoundException;
import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.Ports;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import com.github.dockerjava.zerodep.ZerodepDockerHttpClient;
import org.apache.commons.io.LineIterator;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import javax.print.Doc;
import java.time.Duration;
@Service
public class ContainerService {
/**************************/
/** 공식 문서 사용 도커 연결 **/
// DockerClientConfig 인스턴스 생성 -> Docker 데몬에 접근할 수 있도록 환경 설정(예: DOCKER_HOST, 인증 관련 정보 등)을 제공하는 객체를 생성
private final DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
// .withDockerHost("unix:///var/run/docker.sock")
.build();
private final DockerHttpClient httpClient = new ZerodepDockerHttpClient.Builder()
.dockerHost(config.getDockerHost()) // DockerClientConfig에서 Docker 호스트 정보 가져오기
.sslConfig(config.getSSLConfig()) // SSL 구성 (TLS 인증서 등)
.maxConnections(100) // 최대 연결 수 설정
.connectionTimeout(Duration.ofSeconds(30)) // 연결 타임아웃 설정
.responseTimeout(Duration.ofSeconds(45)) // 응답 타임아웃 설정
.build();
// DockerClient 인스턴스 생성 -> DockerClientConfig와 DockerHttpClient를 결합하여 Docker 데몬에 명령을 전달할 수 있는 DockerClient 객체를 생성
private final DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
/**************************/
public ContainerInfoDto createContainer(CreateContainerRequestDto createContainerRequestDto){
}
}
여기까진 작성이 되어있다. 이제부터 작성할 부분은 맨 마지막줄에 이쓴 createContainer 함수 내부를 작성할 것이다.
도커 실행 확인하기
우선 도커를 먼저 실행시킨다. 도커가 실행되고 있는지 아닌지 확인하기 위해서는
docker ps
또는
docker info
를 입력한다.
위와 같이 뜬다면 도커가 실행중이라는 것을 암시한다.
도커 이미지 준비하기(ubuntu:22.04)
원래라면 사용자가 이미지와 버전을 입력했을 때 그에 맞는 이미지를 도커 허브에서 자동으로 다운 받고, 그 이미지로 컨테이너를 만들어서 실행을 시켜주는 게 목표이나, 현재로서는 개발 초기이기 때문에 되는지 안 되는지 부터 테스트를 해야한다.
그렇기에 사용자의 입력도
{
"os": "ubuntu",
"version": "22.04",
"userEmail": "hey_minj@naver.com"
}
라고 고정을 해줄 것이고, 만들어지는 것도 무조건 ubuntu:22.04 고정을 시킬 것이다.
그렇게 하기 위해서 사전에 로컬에서도 ubuntu:22.04 도커 이미지가 준비가 되어있어야 하기에 터미널에서
docker pull ubuntu:22.04
를 입력해서 이미지를 준비해준다.
이렇게 실행을 하고, docker desktop -> images 부분에 내가 방금 설치한
ubuntu:22.04가 저장이 된 것을 확인할 수 있다.
사용자의 입력(넘어온 Dto 값) 확인하기
사용자의 입력값을 확인한다. 후에 버전별로 나누어야 하지만, 지금은 앞서 말했듯이 ubuntu:22.04버전만 취급할 것이다.
//TODO : 운영체제별, 버전별로 나누기
if(!("ubuntu").equals(createContainerRequestDto.getOs()) ||
!("22.04").equals(createContainerRequestDto.getVersion())){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "현재는 Ubuntu 22.04만 지원합니다.");
}
//Version이 비어있을 경우
String os = createContainerRequestDto.getOs();
String version = (createContainerRequestDto.getVersion()) == null || createContainerRequestDto.getVersion().isEmpty()
? "latest" : createContainerRequestDto.getVersion();
//TODO : 후에 도커 이미지를 따로 경량화 시켰을 경우 해당 이미지로 변경
String imageName = os + ":" + version;
이렇게 사용자의 입력이 os : ubuntu, version:22.04인 경우에만 처리하도록 설정을 하였고
후에 있을 상황을 대비하여, 버전이 비어있거나 null이 들어오면 가장 최근의 버전인 latest로 설정되게 하였다.
그리고 이미지 이름을 os:version 즉, ubuntu:22.04로 되게 설정하여서 imageName 변수에 들어가게 하였다.
이미지를 토대로 컨테이너 생성하기
컨테이너를 생성하는 명령어는 createContainerCmd이다. 이는
docker-java/docker-java-api/src/main/java/com/github/dockerjava/api/DockerClient.java at main · docker-java/docker-java
Java Docker API Client. Contribute to docker-java/docker-java development by creating an account on GitHub.
github.com
여기에서 하나하나 찾아가면서 확인하였다
조금 세세하게 보면
163번째 줄에 createContainerCmd가 String image을 매개변수 입력받는 메서드가 하나 보이고, 저것도 import 했기 때문에 해당 모듈로 넘어가서 보면
docker-java/docker-java-api/src/main/java/com/github/dockerjava/api/command/CreateContainerCmd.java at main · docker-java/docke
Java Docker API Client. Contribute to docker-java/docker-java development by creating an account on GitHub.
github.com
CreateContainerCmd.java 여기서 1021번째 줄에
.exec()로 CreateContainerResponse를 받는 것을 알 수 있다.
저것을 토대로 코드를 작성하면
CreateContainerResponse containerResponse = dockerClient.createContainerCmd(imageName)
.exec();
이렇게 하면 컨테이너 생성을 완료할 수 있다.
생성된 컨테이너의 ID값 가져오고 Return하기
저 컨테이너 Id값은 어떻게 확인하냐?
CreateContainerResponse 페이지를 보면
docker-java/docker-java-api/src/main/java/com/github/dockerjava/api/command/CreateContainerResponse.java at main · docker-java/
Java Docker API Client. Contribute to docker-java/docker-java development by creating an account on GitHub.
github.com
23번째 줄에 getId가 존재하는데, 저것으로 생성된 컨테이너의 아이디를 가져올 수 있다.
포트는 아직 아무설정을 하기 않았기에 (2편에서 설정할 예정) N/A값을 넣어준다.
System.out.println("컨텐 아이디 : " + containerResponse.getId());
//컨테이너 아이디 가져오기
String containerId = containerResponse.getId();
//컨테이너 아이디, 포트번호 return
ContainerInfoDto containerInfoDto = ContainerInfoDto.builder()
.containerId(containerId)
.port("N/A") //TODO : 포트 나온 거 넣어두기
.build();
//TODO : 데이터베이스 저장
return containerInfoDto;
//TODO : 예외 처리
이렇게 설정해주면 값을 뽑아올 수 있다.
ContainerService 전체 코드
package com.hanbat.dotcar.container;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.CreateContainerResponse;
import com.github.dockerjava.api.command.PullImageResultCallback;
import com.github.dockerjava.api.exception.NotFoundException;
import com.github.dockerjava.api.model.ExposedPort;
import com.github.dockerjava.api.model.HostConfig;
import com.github.dockerjava.api.model.Ports;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.transport.DockerHttpClient;
import com.github.dockerjava.zerodep.ZerodepDockerHttpClient;
import org.apache.commons.io.LineIterator;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import javax.print.Doc;
import java.time.Duration;
@Service
public class ContainerService {
/**************************/
/** 공식 문서 사용 도커 연결 **/
// DockerClientConfig 인스턴스 생성 -> Docker 데몬에 접근할 수 있도록 환경 설정(예: DOCKER_HOST, 인증 관련 정보 등)을 제공하는 객체를 생성
private final DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
// .withDockerHost("unix:///var/run/docker.sock")
.build();
private final DockerHttpClient httpClient = new ZerodepDockerHttpClient.Builder()
.dockerHost(config.getDockerHost()) // DockerClientConfig에서 Docker 호스트 정보 가져오기
.sslConfig(config.getSSLConfig()) // SSL 구성 (TLS 인증서 등)
.maxConnections(100) // 최대 연결 수 설정
.connectionTimeout(Duration.ofSeconds(30)) // 연결 타임아웃 설정
.responseTimeout(Duration.ofSeconds(45)) // 응답 타임아웃 설정
.build();
// DockerClient 인스턴스 생성 -> DockerClientConfig와 DockerHttpClient를 결합하여 Docker 데몬에 명령을 전달할 수 있는 DockerClient 객체를 생성
private final DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
/**************************/
public ContainerInfoDto createContainer(CreateContainerRequestDto createContainerRequestDto){
//TODO : 이메일 검증
//TODO : 운영체제별, 버전별로 나누기
if(!("ubuntu").equals(createContainerRequestDto.getOs()) ||
!("22.04").equals(createContainerRequestDto.getVersion())){
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "현재는 Ubuntu 22.04만 지원합니다.");
}
//Version이 비어있을 경우
String os = createContainerRequestDto.getOs();
String version = (createContainerRequestDto.getVersion()) == null || createContainerRequestDto.getVersion().isEmpty()
? "latest" : createContainerRequestDto.getVersion();
//TODO : 후에 도커 이미지를 따로 경량화 시켰을 경우 해당 이미지로 변경
String imageName = os + ":" + version;
//TODO : 이미지 존재하는지 확인
//컨테이너 생성
CreateContainerResponse containerResponse = dockerClient.createContainerCmd(imageName)
.exec();
System.out.println("컨텐 아이디 : " + containerResponse.getId());
//컨테이너 아이디 가져오기
String containerId = containerResponse.getId();
//컨테이너 아이디, 포트번호 return
ContainerInfoDto containerInfoDto = ContainerInfoDto.builder()
.containerId(containerId)
.port("N/A") //TODO : 포트 나온 거 넣어두기
.build();
//TODO : 데이터베이스 저장
return containerInfoDto;
//TODO : 예외 처리
}
}
TODO 부분은 천천히..2, 3편에 걸쳐서 작성할 예정이다.
ContainerController 작성하기
Rest API 방식으로 작성할 것이고, 일단은 저 생성 함수를 컨트롤 하기 위해 Controller 작성을 해야하는데 진짜 야매로 임시로 일단 작동하는 지 테스트를 위해서 작성해준다.
package com.hanbat.dotcar.container;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequiredArgsConstructor
@RequestMapping("api/container")
public class ContainerController {
private final ContainerService containerService;
@PostMapping("/create")
public ResponseEntity<CreateContainerResponseDto> createContainer(@RequestBody CreateContainerRequestDto createContainerRequestDto){
ContainerInfoDto containerInfoDto = containerService.createContainer(createContainerRequestDto);
//TODO : 컨테이너 생성 실패 시에 나오는 예외처리
CreateContainerResponseDto createContainerResponseDto = CreateContainerResponseDto.builder()
.containerId(containerInfoDto.getContainerId())
.port(containerInfoDto.getPort())
.build();
return ResponseEntity.status(200).body(createContainerResponseDto);
}
}
api/container/create URL에다가 RequestDto 입력받은 거 넣어주면, ResponseDto 만들어서 그거 반환해주기. 그러면 ResponseDto도 만들어주어야 한다.
일단은 생성이 되었다는 가정하게 위와같이 임시로 작성을 해준 것이다.
CreateContainerResponseDto 작성
사용자에게 돌아가는 값이다
package com.hanbat.dotcar.container;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CreateContainerResponseDto {
private String containerId; //컨테이너 아이디
private String port; //포트 번호
}
테스트
실행을 하고
body부분을
{
"os": "ubuntu",
"version": "22.04",
"userEmail": "hey_minj@naver.com"
}
이렇게 채워주고 POST요청을 보내본다.
리턴으로
{
"containerId": "2004a923a4cd0b799a582ae3a645c553ba13ea5effc67dd76c405c4e16aa2aed",
"port": "N/A"
}
이렇게 값이 왔다.
실제로 도커에는 그러면 어떻게 되어있는지 확인해볼까?
docker desktop에는 방금 생성된 컨테이너가 존재하는 것이 보이고,
Created 상태인 것을 확인할 수 있다.
에필로그
이렇게 해서 docker-java로 컨테이너 생성한 것은 확인을 했는데, 이는 도커 명령어로 따지면
docker create ubuntu:22.04
이것을 친거나 다름없다.
보통 도커를 실행하고 연결하려면
docker run -p 4040:22 -it ubuntu:22.04
이렇게 해야 22.04로 내가 외부 4040 포트가 컨테이너 내부 22번 포트랑 연결되어서 들어가서 소통할 수 있는데,
저기까지 가야 이제 완벽히 생성을 할 수 있다고 가정할 수 있다.
그렇기에 2편에서는 포트바인딩. 도커에 접속하기 위한 22번 포트 와 내 컴퓨터에서 비어있는 랜덤한 포트를 묶어주고, 실행 및 유지까지 하도록 설정을 해보겠다.
추가적으로 이미지 불러오는 것도 해보자.
지금까지의 진행상황은
https://github.com/Vak-kas/TCAR_VM/tree/container
GitHub - Vak-kas/TCAR_VM: 가상머신
가상머신. Contribute to Vak-kas/TCAR_VM development by creating an account on GitHub.
github.com
여기서 확인할 수 있다.
'웹 개발' 카테고리의 다른 글
[SpringBoot] docker-java 사용하여 스프링부트로 도커 조작하기 - 컨테이너 생성(2/3) (0) | 2025.02.14 |
---|---|
[SpringBoot] docker-java 사용하여 스프링부트로 도커 조작하기 - 사전준비 (0) | 2025.02.13 |
[장고] 회원가입 기능 상세하게 마무리 (이메일 인증) (0) | 2024.03.08 |
[장고] 회원가입시 이메일 인증하기(이메일 유효성 검사) #5 (1) | 2024.02.11 |
[장고] JWT 사용하기 #4 (0) | 2024.02.10 |