[Docker] 3. image 만들기
Dockerfile 명령어
명령어 | 설명 |
FROM | 베이스 이미지를 설정하는 명령어(<이미지 이름>:<태그> 형식으로 설정) |
MAINTAINER | 관리자의 정보를 기재하는 부분, 빌드에 영향을 주지 않기 때문에 생략하는 경우도 있다. |
COPY | 파일이나 디렉토리를 이미지로 복사하는 명령어 소스를 복사할 때 많이 사용된다. 이미지 파일이 위치할 경로는 절대 경로 방식으로 설정해야 한다. URL을 사용할 수 없고 압축이 해제되지 않고 그대로 복사된다. |
ADD | 파일이나 디렉토리를 이미지로 복사하는 명령어이다. COPY 명령어와 비슷하지만 ADD는 상대 경로를 사용할 수 있고, URL을 사용할 수 있다. 또한 압축 파일이 있으면 압축 파일을 풀어서 추가한다. |
RUN | 이미지를 만드는 과정에서 이미지에서 사용해야 하는 명령어를 실행해주는 명령어 |
CMD | 컨테이너가 실행되었을 때 실행되는 명령어를 정의하는 명령어 Dockerfile에 CMD 명령어가 여러 번 사용되어도 맨 마지막 명령어만 유효하다. 여러 명령어를 실행하고자 한다면 run.sh 파일을 만들어 해당 파일에 실행하고자 하는 명령어를 입력해 주어야 한다. docker run을 통해 인자값을 전달하면 CMD 명령어에 명시된 인자는 무시한다. (매개변수 영향 받음) |
ENTRYPOINT | 컨테이너가 run으로 실행되었을 때 실행되는 명령어를 정의하는 명령어이다. CMD 명령어와 유사하지만 run을 통해 인자값이 전달되어도 ENTRYPOINT로 지정해 놓은 명령어가 그대로 실행된다. (매개변수 영향 없음) |
WORKDIR | COPY, ADD, RUN, CMD 등이 수행될 기본 디렉토리를 설정해주는 명령어 |
EXPOSE | 컨테이너와 호스트를 연결할 포트를 지정해주는 명령어 |
ENV | RUN, CMD, ENTRYPOINT 명령어에서 사용하는 환경변수를 지정하는 명령어 사용 시 환경변수명 앞에 $를 붙여 사용한다. |
VOLUME | 컨테이너 외부에 파일 시스템을 연결하기 위해 사용한다. |
실습
(Docker 실습에 이용할 간단한 spring project와 vue project는 미리 작성해두었다.)
gradle 탭에서 build를 눌러주면
아래와 같이 .jar 파일이 생성된다.
0.0.3 버전인 이유는
build.gradle에 아래와 같이 작성해주었기 때문이다.
version = '0.0.3-SNAPSHOT'
만약 이 과정을 터미널에서 해주고 싶다면, 아래와 같이 명령어를 입력하면 된다.
./gradlew build
# 위와 같이 입력해주면 .jar가 계속해서 쌓이게 되는데, 아래 명령어를 사용해서 build해주면 현재 버전만 남게된다.
./gradlew clean build
프로젝트의 최상위 경로에 Dockerfile을 생성하고, 안에 아래와 같이 코드를 작성한다.
(이미 gradle build를 해서 .jar가 있는 경우. 없는 경우의 Dockerfile 작성은 아래에 있다.)
# FROM절: 활용가능한 image를 써준다. (내가 사용할 기반 image)
# Open JDK 17이 설치된 Apline Linux 이미지를 기반으로 함
FROM openjdk:17-alpine
# 빌드된 jar 파일(build/libs/*.jar)을 컨테이너의 루트 디렉토리에 app.jar로 복사함
COPY build/libs/*.jar app.jar
# 컨테이너 시작 시 app.jar 파일을 실행하는 명령을 설정
ENTRYPOINT ["java", "-jar", "app.jar"]
Dockerfile이 있는 위치에서 아래 명령어 실행하여 image를 만들어준다.
docker build -t <dockerhub 계정명>/<image 이름>
docker build -t hyomee2/sw_boot_project .
# 여기서 .의 의미는 현재 경로의 DockerFile을 이용하겠다는 의미
아래 명령어를 활용하여 image가 잘 만들어졌는지 확인한다.
(잘 만들어졌다면 명령어를 실행했을 때 hyomee2/sw_boot_project image가 있어야 한다.)
docker images
이 image를 컨테이너화 하려면 아래 명령어를 이용한다.
# first라는 container 이름을 지어줬다.
docker run -d -p 8055:8080 --name first hyomee2/sw_boot_project
# 위와 같이 작성하면 8055라는 포트번호로 container가 구동됐을 때,
# 8055로 요청이 들어오면 8080으로 연결시키겠다는 의미이다.
# container의 외부 포트로 요청했을 경우 내부로 연결된다는 의미.
잘 작동하는지 확인하려면 localhost:8055/health에 접속하면 된다. (localhost:8080/health는 안된다!)
gradle build부터 이미지를 통해 하려면(.jar을 만드는 과정을 여기서 하려면) 아래와 같이 Dockerfile를 작성할 수 있다.
# Open JDK 17이 설치 된 Apline Linux 이미지를 기반으로 함
# FROM openjdk:17-alpine
# 빌드 된 jar 파일을 컨테이너의 루트 디렉토리에 app.jar로 복사 함
# COPY build/libs/*.jar app.jar # 이 경로의 jar 파일을 copy해서 진행하겠다?
# 컨테이너 시작 시 app.jar 파일을 실행하는 명령을 설정
# ENTRYPOINT ["java", "-jar", "app.jar"]
## 1. 빌드 스테이지
# Gradle을 이미지로 가져와 빌드 스테이지를 build 라는 이름으로 설정
FROM gradle:7.6.1-jdk17-alpine AS build
# 컨테이너 내부 작업 디렉토리를 /app으로 설정
WORKDIR /app
# 현재 디렉토리의 모든 파일과 폴더를 컨테이너의 /app으로 복사
COPY . .
# Gradle을 사용하여 프로젝트 빌드 (데몬 프로세스 사용 X)
RUN gradle clean build --no-daemon
## 2. 실행 스테이지
FROM openjdk:17-alpine
# 빌드 스테이지에서 생성 된 jar 파일을 복사
COPY --from=build /app/build/libs/*.jar ./ # 여기서 from=build 라는건 10행을 말하는거.(as build)
# jar 파일을 나열하고 grep으로 plain 포함 되지 않은 줄을 선택하여 app.jar로 변경
RUN mv $(ls *.jar | grep -v plain) app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
image 생성하기
docker build -t <dockerhub 계정명>/<image 이름>
docker build -t hyomee2/sw_boot_project2 .
image를 container화 하기
docker run -d -p 8056:8080 --name first2 hyomee2/sw_boot_project2
localhost:8056/health에 접속해서 정상적으로 구동되는지 확인한다.
아래 명령어를 이용해서 docker hub에 image를 push할 수 있다.
docker push <계정이름>/<image 이름>
docker push hyomee2/sw_boot_project
위의 과정을 통해 bootProject(백엔드)에서 Dockerfile과 image를 만들었고,
위의 과정과 마찬가지로 vueProject(프론트엔드)에서도 Dockerfile 생성, build(image 생성)와 run(container 구동)을 해준다.
먼저 아래와 같이 vueProject의 최상단에 Dockerfile을 작성해준다.
#Node.js LTS 버전이 설치 된 alpine linux 이미지를 가져온다 (node를 사용하겠다.)
FROM node:lts-alpine
#컨테이너 내부 작업 디레토리를 /app으로 설정
WORKDIR /app
#현재 디렉토리 내의 모든 내용을 작업 디렉토리로 복사
COPY . .
#npm을 통해 종속성 설치
RUN npm install
#npm run dev 명령 실행(모든 네트워크 인터페이스에서 접근 가능하도록 설정)
CMD ["npm", "run","dev","--","--host","0.0.0.0"]
Dockerfile이 있는 위치에서 image를 생성해준다.
docker build -t <dockerhub 계정명>/<image 이름>
docker build -t hyomee2/sw_vue_project .
image가 잘 만들어졌다면 container화 시켜준다.
docker run -d -p 8011:5173 --name secone hyomee2/sw_vue_project
백엔드와 프론트엔드를 통신할 때 우리는 container의 포트번호를 이용해야 한다.
(여기선 8011과 8056 -> 따라서 CORS 설정도 필요하다.)
기존의 vueProject의 app.vue에는 8080에 요청을 하고 있었는데, 8056에 요청하도록 바꿔준다.
const sendPlus = async() => {
const response = await fetch(`http://localhost:8056/plus?num1=${num1.value}&num2=${num2.value}`);
const data = await response.json();
console.log('data: ' , data);
result.value = data.sum;
}
container가 구동된 상태에선 코드를 수정해도 적용되지 않으므로,
이런 경우 container를 내리고 image를 다시 만들어서 container을 다시 구동하자.
# container 내리기
docker rm -f second # -f: 강제로, second: container 이름
수정한 뒤 container를 다시 구동한 뒤 localhost:8011에 접속해도 에러가 발생하는데,
이전에는 8080를 찾지 못한다는 에러였지만, 이제는 통신하고자 하는 건 있는데 CORS 문제가 발생한다.
-> bootProject에서 5173에 대해 CORS 처리가 되어 있었는데, 8011로 변경하자.
(이번에도 수정했으니 container 내리고 image 다시 만들어서 container를 다시 구동하자.)
// bootProject의 WebConfig에서 CORS 설정 수정
// cors 설정
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:8011")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS"); // options: preflight 같은 예비 요청에 관함
}
}