Docker 이미지 생성 및 컨테이너 돌리기

안상운·2024년 8월 22일

Docker

목록 보기
8/14
post-thumbnail

1. Demo 어플리케이션

먼저 도커를 사용하지 않고 어플리케이션을 돌려보자.


이제 실제 회사들과 조직들이 어떻게 도커와 컨테이너들을 사용하는지 알기 위해서 간단한 3계층 구조의 어플리케이션으로 실습을 진행해 볼것이다.

It has the following components:

  • 리액트 프런트앤드
  • NodeJS API
  • Golang API
  • Postgres 데이터베이스

위의 각각의 component들은 사실 매우 간단하지만, 컨테이너화와 시스템설정에 있어서 중요한 점들이 무엇인지 알게 해주는 좋은 예시

일단 컨테이너화 하기전에 내 컴퓨터에서 먼저 돌려보자.

일단 window 환경에서 make 명령어를 사용하기 위해서 gnuwin32를 먼저 설치한다.

https://github.com/luckyPrice/Minimal-3-tier-web-application/tree/main/05-example-web-application

make run-pogres

이제 데이테 베이스가 컨테이너 안에서 작동이 되는 것을 알 수 있다.

package.json에있는 dependency를 살펴보자.

express: rest api들을 만드는 패키지
moragan: api에 대한 로그 기록을 더 쉽게 수성 할 수 있게 도와줌
pg: client를 만들도록 허락해주고, 데이터베이스에 연결해줌

 

이제 앱을 돌리기 위해서는 컨테이너 안에서 데이터 베이스를 환경변수에 설정 해주고 npm을 run 해보자.

make run-api-node
DATABASE_URL:=postgres://postgres:foobarbaz@localhost:5432/postgres
run-api-node:
	@echo Starting node api
	cd api-node && \
		DATABASE_URL=${DATABASE_URL} \
		npm run dev

직접 localhost:3000로 들어가 보자

새로고침을 누를때마다 GET 메소드가 새롭게 리셋된다.

이제 golang 부분이다.
먼저 내가 지정한 경로에 go mod 안에있는 dependency들을 다운 받자.

mkdir go-workspace
export GOPATH=$PWD/go-workspace
go mod download
go run main.go

이번에는 golang에서 데이터베이스의 환경변수를 받을 수 지정하고 api-golang에 있는 main.go를 run해보겠다.

make run-api-golang
run-api-golang:
	@echo Starting golang api
	cd api-golang && \
		DATABASE_URL=${DATABASE_URL} \
		go run main.go

마지막으로 react를 실행 시켜보자. 포트는 5173이다. 이 리액트 앱은 노드 js와 Golang API 둘다 호출해서 결과를 보여준다.
This will start the development server, listening on port 5173. The React app calls both the Node.js and Golang APIs and displays the responses.

짜짠 우리가 원하는 데로 정상적으로 작동이 된다....!

 

근데 사실 이렇게 서비스들을 각각 run시키고 서로 상호작용하게 만드는 것은 상당히 귀찮고 복잡하다.
각각의 서비스들을 세팅했고, 내 컴퓨터시스템에 맞춰서 맞는 버전을 깔아야한다.
도커는 이러한 과정들을 매우 쉽게 만들어준다.

이제 우리는 이 컴포넌트들을 컨테이너화 시킨후 컨테이너를 구동하고, 적절하게 구성하는 법을 알아보자. 또, 컨테이너화된 설정 내에서 애플리케이션을 모두 0에서 빠르게 개발하고 반복할 수 있도록 개발 환경을 설정하는 방법도 함께 알아보자.

2. 컨테이너 이미지 만들기

도커파일

도커파일이란 사용자가 이미지를 조합하기 위해 명령줄에서 호출할 수 있는 모든 명령이 포함된 텍스트 문서를말함.

👨‍🍳 Application Recipe:
---------------------------------------
1. Start with an Operating System
2. Install the language runtime
3. Install any application dependencies
4. Set up the execution environment
5. Run the application

docker build

도커파일은 build context와 연결된다.
build context는 우리의 로컬파일에 있는 폴더나 디렉토리 안에있는 소스코드들, github repostiory에 접근 할 수 있는 url등을 포함하고있는 파일이다.

이 두개를 함께 도커의 빌드 명령을 실행하는데, 이러한 명령을 바탕으로 도커는 컨테이너 이미지를 만들어낸다.

.dockerignore 파일은 build context에 포함되는 파일로, 도커에게 특정한 파일을 무시하라고 전달해주는 파일이다. 예를들어 노드 모듈을 로컬에서 이미 받았다면 그것은 도커파일에 설치될것이므로 도터 이미지에 복사 하고 싶지 않을 것이다. 이는 호스트 시스템과 컨테이너 내 설치 간의 비호환성을 방지해 준다.

도커는 도커파일에 있는 설명들을 참조해서 이미지를 빌드할수 있다. Dockerfile은 텍스트로 된 문서로, 사용자가 이미지를 조합하기 위해 명령줄에서 호출할 수 있는 모든 명령이 포함되어있다.

도커파일 만들어보기

🔒 - Security improvement
🏎️ - Build speed improvement
👁️ - Clarity improvement

 

NodeJS API Dockerfile

 
Naive Implementation

도커 허브에 있는 공식 노드 컨테이너 이미지로 도커 파일을 시작해보자. 전체 빌드를 복사하고, npm과 dependency를 설치하고, 시작 시 실행될 명령을 설정.

FROM node
COPY . .
RUN npm install
CMD [ "node", "index.js" ]

이제 기술적으로는 위와 다르지 않지만 개선할 수 있는 방법을 알아보자

 
Pin the Base Image (🔒+🏎️)

첫번쨰로 이 도커파일의 성능을 향상시킬 수 있는것은 바로 기본 이지미를 특정 버전에 고정시키는 것이다. 태그가 없으면 Docker는 이미지에 적용되는 기본 태그인 최신 버전의 "latest" 태그를 사용한다. 이로 인해 업스트림 이미지가 새로 업데이트될 때마다 기본 이미지가 변경되어 필연적으로 애플리케이션이 중단된다.

애플리케이션의 요구 사항을 충족하기 위해 작고 안전한 특정 기본 이미지를 선택할 수 있다...!

#-------------------------------------------
# 버전 고정 (use slim for reduced image size)
FROM node:19.6-bullseye-slim
#-------------------------------------------
COPY . .
RUN npm install
CMD [ "node", "index.js" ]

만약 마이너 버전에 고정하면 알려진 주요 변경 사항을 방지하는 동시에 버그 수정이 포함된 패치 버전을 활용할 수 있다.
기본 이미지를 실제로 잠그려면 다음과 같은 특정 이미지 해시를 참조할 수 있습니다.

FROM node:19.6-bullseye-slim@sha256:e684615bdfb71cb676b3d0dfcc538c416f7254697d8f9639bd87255062fd1681

 
Set a Working Directory (👁️)

기본적으로, 우리의 기본 이미지는 작업공간에 루트 경로(/)를 가지고 있지만 특정 언어 + 프레임워크의 규칙을 기반으로 다른 것으로 설정해야 한다.

이를 통해 파일 시스템안에 우리의 앱을 위한 공간을 만들 수 있다.

FROM node:19.6-bullseye-slim
#-------------------------------------------
# 작업공간 경로/
WORKDIR /usr/src/app
#-------------------------------------------
COPY . .
RUN npm install
CMD [ "node", "index.js" ]

 
Copy package.json and package-lock.json Before the Source Code (🏎️)

Dockerfile 내의 각각의 명령은 이미지 내에 새 레이어를 생성한다. Docker는 이러한 레이어를 캐시처리하여 후속 빌드의 속도를 높인다. 이전에는 소스 코드를 변경할 때마다 "COPY . ." 에 대한 레이어 캐시가 무효화되고 지시에 따라 빌드가 모든 종속성을 다시 설치하게 됩니다(이러면 느려짐).

npm install을 실행하기 전에 종속성 구성 파일만 복사하면 계층 캐시를 보호하고 소스 코드가 변경될 때마다 종속성을 다시 설치하는 것을 방지할 수 있음.

.dockerignore 파일을 사용하여 컨테이너 이미지(예: node_modules 디렉터리)에 포함하면 안 되는 파일을 지정할 수도 있다.

FROM node:19.6-bullseye-slim
WORKDIR /usr/src/app
#-------------------------------------------
# Copy only files required to install dependencies (better layer caching)
COPY package*.json ./
RUN npm install
# Copy remaining source code AFTER installing dependencies.
# Again, copy only the necessary files
COPY ./src/ .
#-------------------------------------------
CMD [ "node", "index.js" ]

 
Use a non-root USER (🔒)

적절하게 구성된 경우 컨테이너는 내부의 루트 사용자와 호스트 시스템 사용자 사이에 일부 보호(사용자 네임스페이스를 통해)를 제공하지만 루트가 아닌 사용자로 설정하면 심층적인 보안 접근 방식에 대한 또 다른 계층이 제공됨...!

노드 기본 이미지에는 이미 이 목적으로 사용할 수 있는 node라는 사용자가 있음.

FROM node:19.6-bullseye-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
#-------------------------------------------
# Use non-root user
# Use --chown on COPY commands to set file permissions
USER node
COPY --chown=node:node ./src/ .
#-------------------------------------------
CMD [ "node", "index.js" ]

 
Configure for the Production Environment (🔒 + 🏎️)

많은 Node.js 패키지는 NODE_ENV 환경 변수를 찾고 production으로 설정된 경우 다르게 동작한다(로깅이 감소하는 등). 기본적으로 런타임에 설정되도록 Dockerfile 내에서 이를 설정할 수 있다.

또한 npm install을 사용하는 대신 package-lock.json 파일을 사용하고 설치된 종속성이 해당 파일에서 완전히 지정된 버전과 일치하는지 확인하는 npm ci 또는 "clean install"을 사용하는 것이 좋다. --only=production을 사용하면 불필요한 개발 종속성 설치를 방지하여 공격 노출 영역을 줄이고 이미지 크기를 더욱 줄일 수 있다.

FROM node:19.6-bullseye-slim
#-------------------------------------------
# Set NODE_ENV
ENV NODE_ENV production
#-------------------------------------------
WORKDIR /usr/src/app
COPY package*.json ./
#-------------------------------------------
# Install only production dependencies
RUN npm ci --only=production
#-------------------------------------------
USER node
COPY --chown=node:node ./src/ .
CMD [ "node", "index.js" ]

 

Golang API Dockerfile

Naive Implementation

FROM golang
WORKDIR /app
COPY . .
RUN go mod download
CMD ["go", "run", "./main.go"]

 
Pin the Base Image (🔒+🏎️)

#-------------------------------------------
# Pin specific version for stability. Use debian for easier build utilities.
FROM golang:1.19-bullseye AS build
#-------------------------------------------
WORKDIR /app
COPY . .
RUN go mod download
CMD ["go", "run", "./main.go"]

마이너 버전에 고정하면 알려진 주요 변경 사항을 방지하는 동시에 버그 수정이 포함된 패치 버전을 활용할 수 있다. 기본 이미지를 실제로 잠그려면 다음과 같은 특정 이미지 해시를 참조

FROM golang:1.19-bullseye@sha256:1370f30629243bb65e3e0f780ae08a54e50fc5b7e96f0b79e62ee846788d1178

 
Build the Binary in the Dockerfile (🏎️)

go run ./main.go 명령은 애플리케이션을 빌드하고 실행해준다. 이것을 CMD로 사용하면 컨테이너는 시작할 때마다 애플리케이션을 빌드해야 한다.
우리는 컨테이너 이미지 내에서 애플리케이션을 빌드한 다음 시작 시 빌드 바이너리로 실행하도록 해야 한다.

FROM golang:1.19-bullseye
WORKDIR /app
COPY . .
RUN go mod download
#-------------------------------------------
# 런타임이 아닌 빌드 중에 애플리케이션 컴파일
RUN go build -o api-golang
CMD ["./api-golang"]
#-------------------------------------------

 
Copy go.mod and go.sum Before Source Code (🏎️)

Dockerfile 내의 각각의 명령은 이미지 내에 새 레이어를 생성한다. Docker는 이러한 레이어를 캐시처리하여 후속 빌드의 속도를 높인다. 이전에는 소스 코드를 변경할 때마다 "COPY . ." 에 대한 레이어 캐시가 무효화되고 지시에 따라 빌드가 모든 종속성을 다시 설치하게 됩니다(이러면 느려짐).

npm install을 실행하기 전에 종속성 구성 파일만 복사하면 계층 캐시를 보호하고 소스 코드가 변경될 때마다 종속성을 다시 설치하는 것을 방지할 수 있음.

.dockerignore 파일을 사용하여 컨테이너 이미지(예: node_modules 디렉터리)에 포함하면 안 되는 파일을 지정할 수도 있다.

FROM golang:1.19-bullseye
WORKDIR /app
#-------------------------------------------
# Copy only files required to install dependencies (better layer caching)
COPY go.mod go.sum ./
RUN go mod download
COPY . .
#-------------------------------------------
RUN go build -o api-golang
CMD ["./api-golang"]

 
Separate Build and Deploy Stages

애플리케이션을 구축하려면 golang과 관련된 상당한 양의 유틸리티가 필요합니다(golang 기본 이미지에 포함됨). 그러나 런타임에는 그런것이 필요하지 않다.

여기서 Docker의 다단계 빌드 기능을 사용한다면 최종 배포 가능한 이미지를 훨씬 더 작게 만들 수 있다!

FROM golang:1.19-bullseye AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
#-------------------------------------------
# 바이너리를 정적으로 연결하는 플래그 추가
RUN go build \
  -ldflags="-linkmode external -extldflags -static" \
  -tags netgo \
  -o api-golang
# 배포 가능한 이미지에 대해 별도의 단계 사용
FROM scratch
WORKDIR /
# 빌드 단계에서 바이너리를 복사
COPY --from=build /app/api-golang api-golang
#-------------------------------------------
CMD ["/api-golang"]

 
Set ENV and Expose Port (👁️)

우리가 사용하고 있는 API 프레임워크(Gin)는 GIN_MODE 환경 변수를 사용하여 개발 환경에서 실행 중인지 프로덕션 환경에서 실행 중인지 확인한다.

배포 가능한 이미지에 대해 GIN_MODE=release를 설정하여 프로덕션 모드에서 실행되도록 할 수 있음.

FROM golang:1.19-bullseye AS build
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build \
  -ldflags="-linkmode external -extldflags -static" \
  -tags netgo \
  -o api-golang

FROM scratch
WORKDIR /
#-------------------------------------------
# Set gin mode
ENV GIN_MODE=release
#-------------------------------------------
COPY --from=build /app/api-golang api-golang
CMD ["/api-golang"]

 
Use a non-root USER (🔒)

적절하게 구성된 경우 컨테이너는 내부의 루트 사용자와 호스트 시스템 사용자 사이에 일부 보호(사용자 네임스페이스를 통해)를 제공하지만 루트가 아닌 사용자로 설정하면 심층적인 보안 접근 방식에 대한 또 다른 계층이 제공된다.

useradd 명령을 사용하여 루트가 아닌 사용자를 빌드 단계에 추가한 다음 해당 파일을 스크래치에 복사하여 사용할 수 있음.

FROM golang:1.19-bullseye AS build
#-------------------------------------------
# Add non root user
RUN useradd -u 1001 nonroot
#-------------------------------------------
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build \
  -ldflags="-linkmode external -extldflags -static" \
  -tags netgo \
  -o api-golang

FROM scratch
WORKDIR /
ENV GIN_MODE=release
#-------------------------------------------
# Copy the passwd file
COPY --from=build /etc/passwd /etc/passwd
COPY --from=build /app/api-golang api-golang
# Use nonroot user
USER nonroot
#-------------------------------------------
CMD ["/api-golang"]

 
Add Useful Metadata (👁️)

FROM golang:1.19-bullseye AS build
#-------------------------------------------

#-------------------------------------------
RUN useradd -u 1001 nonroot
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN go build \
  -ldflags="-linkmode external -extldflags -static" \
  -tags netgo \
  -o api-golang

FROM scratch
WORKDIR /
ENV GIN_MODE=release
#-------------------------------------------
# 포트
EXPOSE 8080
#-------------------------------------------
COPY --from=build /etc/passwd /etc/passwd
COPY --from=build /app/api-golang api-golang
USER nonroot
CMD ["/api-golang"]

 
Use a Cache Mount to Speed Up Dependency Installation (🏎️)

Buildkit은 Dockerifle 내의 특정 RUN 명령에 대한 캐시 마운트를 지정하는 기능을 포함하여 많은 유용한 기능을 제공한다. 이러한 방식으로 캐시를 지정하면 이전에 설치된 종속성이 로컬에 저장되므로 종속성을 변경할 때 인터넷에서 모든 종속성을 다시 다운로드할 필요가 없다.

참고: 원격 연속 통합 시스템(예: GitHub Actions)에서 이미지를 빌드하는 경우 파이프라인 실행 전반에 걸쳐 이 캐시를 저장하고 검색하도록 해당 시스템을 구성해야 함.

FROM golang:1.19-bullseye AS build
RUN useradd -u 1001 nonroot
WORKDIR /app
COPY go.mod go.sum ./
#-------------------------------------------
# Use cache mount to speed up install of existing dependencies
RUN --mount=type=cache,target=/go/pkg/mod \
  --mount=type=cache,target=/root/.cache/go-build \
  go mod download
#-------------------------------------------
COPY . .
RUN go build \
  -ldflags="-linkmode external -extldflags -static" \
  -tags netgo \
  -o api-golang

FROM scratch
ENV GIN_MODE=release
EXPOSE 8080
WORKDIR /
COPY --from=build /etc/passwd /etc/passwd
COPY --from=build /app/api-golang api-golang
USER nonroot
CMD ["/api-golang"]

 

React Client Dockerfile

 
Naive Implementation

FROM node
COPY . .
RUN npm install
CMD ["npm", "run", "dev"]

 
Pin the Base Image (🔒+🏎️)

#-------------------------------------------
# Pin specific version for stability
FROM node:19.4-bullseye
#-------------------------------------------
COPY . .
RUN npm install
CMD ["npm", "run", "dev"]

 
Set WORKDIR and COPY package.json File

FROM node:19.4-bullseye
#-------------------------------------------
# Specify working directory other than /
WORKDIR /usr/src/app
# Copy only files required to install dependencies (better layer caching)
COPY package*.json ./
#-------------------------------------------
RUN npm install
COPY . .
CMD ["npm", "run", "dev"]

 
Use a Cache Mount (🏎️)

종속성 설치 속도를 높이기 위해 캐시 마운트를 추가하고 npm에 이를 사용하도록 지시할 수 있다.

FROM node:19.4-bullseye
WORKDIR /usr/src/app
COPY package*.json ./
#-------------------------------------------
# Use cache mount to speed up install of existing dependencies
RUN --mount=type=cache,target=/usr/src/app/.npm \
  npm set cache /usr/src/app/.npm && \
  npm install
#-------------------------------------------
COPY . .
CMD ["npm", "run", "dev"]

 
Separate Build and Deploy Stages

우리의 React 앱은 다양한 방법으로 배포할 수 있는 정적 파일 세트(HTML, CSS 및 JS)로 구축된다.
여기에서는 처음 단계에서 애플리케이션을 빌드한 다음 이를 Nginx를 실행하는 두 번째 단계에 복사!

FROM node:19.4-bullseye AS build
WORKDIR /usr/src/app
COPY package*.json ./
RUN --mount=type=cache,target=/usr/src/app/.npm \
  npm set cache /usr/src/app/.npm && \
  npm install
COPY . .
RUN npm run build
#-------------------------------------------
# Use separate stage for deployable image
FROM nginxinc/nginx-unprivileged:1.23-alpine-perl
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build usr/src/app/dist/ /usr/share/nginx/html
EXPOSE 8080
#-------------------------------------------

효율적인 도커파일을 만드는 방법에 대해서는 아래 자료를 참고.
https://velog.io/@luckyprice1103/%EC%A2%8B%EC%9D%80-dockerfile-%EB%A7%8C%EB%93%9C%EB%8A%94-%EB%B0%A9%EB%B2%95

지금까지는 나의 로컬 환경에서 콘테이너 이미지를 만들었지만, 만약 내가 팀 단위로 일을 할때에는 만든 컨테이너를 어딘가에서 배포 해야한다.
이때 사용하는 것이 컨테이너 레지스트리이다.

3. 컨테이너 레지스트리

컨테이너 레지스트리는 컨테이너 이미지를 저장하고 액세스하는 데 사용되는 저장소 또는 저장소 모음.

레지스트리들은 주로 클라우드에 저장되고, 사용자에게 만들어진 이미지를 push 하거나 사용자의 개발환경에 pull 할 수 있도록 해준다.

컨테이너 레지스트리는 public이 될 수도 있고 private 가 될 수 도 있따.
널리 사용되는 컨테이너 레지스트리에는 Docker Hub, GitHub Container Registry 및 Google, Amazon 및 Azure에서 제공하는 것들이 있다.

목표: INSERT_IMAGE

인증 및 이미지 푸시

public 레지스트리 같은 경우 pull을 하기 위해 인증을 할 필요는 없지만 push를 하기 위해서는 인증이 필요하다. 하지만 pirvate 레지스트리는 push와 pull 모두 인증이 필요하다.

도커허브에 push 하기

도커허브에 인증
docker login

도커허브 사용자 이름과 저장소 이름을 사용하여 이미지에 태그 지정
docker tag my_scratch_image:latest myusername/my_scratch_image:latest

도커허브에 이미지 푸시
docker push myusername/my_scratch_image:latest

 
도커 허브에 인증

 
도커허브 사용자 이름과 저장소 이름을 사용하여 이미지에 태그 지정 후 push

도커허브에 push 성공...!

도커허브에 push를 성공했다.
이번에는 깃허브 컨테이너 registry에 push해보자

깃허브 컨테이너 registry에 push

GitHub Container Registry 인증.
echo "TOKEN" | docker login ghcr.io -u USERNAME --password-stdin

이미지에 레지스트리 접두사, GitHub 사용자 이름, 저장소 이름을 태그로 지정
docker tag my_scratch_image:latest ghcr.io/myusername/my_scratch_image:latest

깃허브 컨테이너 registry에 push
docker push ghcr.io/myusername/my_scratch_image:latest

 
personal access token 을 사용해서 인증

GitHub Container Registry 인증.

이미지에 레지스트리 접두사, GitHub 사용자 이름, 저장소 이름을 태그로 지정 후 push

GitHub Container Registry에 push 성공...!

4. 컨테이너 돌리기

지금까지 우리는 컨테이너를 만들고 컨테이너 레지스트리에 push 하는 법을 알아보았다.
이번에는 컨테이너를 run하는 방법에 대해서 알아보도록 하자.

Docker Run VS Docker Compose

도커 데스크 탑에서 컨테이너를 돌리기 위한 주요한 두가지 방법이 있는데, docker run과 docker compse이다.

일회성 목적의 컨테이너의 경우 docker run을 사용하면 충분하다.
반면 docker compose를 사용하면 YAML 파일 내에서 모든 애플리케이션 구성을 지정할 수 있으므로 여러 컨테이너화된 서비스가 있는 애플리케이션에 대해 더 직관적이고 쉽게 작업할 수 있다.

도커 실행 관련 옵션들.

샘플 Docker 실행

  1. 도커 이미지 빌드

가장먼저 컨테이너 이미지를 빌드해야한다.

repo 구조로 인해 -f 플래그를 전달하여 Dockerfile이 있는 위치를 docker에 알리고 올바른 하위 디렉터리를 가리키는 컨텍스트를 전달해야 한다.

DOCKERCONTEXT_DIR:=../05-example-web-application/
DOCKERFILE_DIR:=../06-building-container-images/

docker build -t client-react-vite -f ${DOCKERFILE_DIR}/client-react/Dockerfile.3 ${DOCKERCONTEXT_DIR}/client-react/
docker build -t client-react-ngnix -f ${DOCKERFILE_DIR}/client-react/Dockerfile.5 ${DOCKERCONTEXT_DIR}/client-react/
docker build -t api-node -f ${DOCKERFILE_DIR}/api-node/Dockerfile.7 ${DOCKERCONTEXT_DIR}/api-node/
docker build -t api-golang -f ${DOCKERFILE_DIR}/api-golang/Dockerfile.6 ${DOCKERCONTEXT_DIR}/api-golang/
  1. 도커 네트워크 생성

network라는 이름의 새로운 도커 네트워크 생성

  1. Postgres 컨테이너 실행

데이터베이스부터 시작하여 react 클라이언트까지 실행 시켜 주자.

docker run -d \
  --name db \
  --network my-network \
  -e POSTGRES_PASSWORD=foobarbaz \
  -v pgdata:/var/lib/postgresql/data \
  -p 5432:5432 \
  --restart unless-stopped \
  postgres:15.1-alpine

PostgeSQL 컨테이너를 시작하기 위해 다음을 추가한다..

-d: 백그라운드에서 실행
--network my-network: 우리가 만든 도커 네트워크에 부착
--restart unless-stopped: 데이터베이스가 충돌이 난다면 자동으로 재시작.

  1. Node API 컨테이너 실행

환경 변수를 설정.

DATABASE_URL=postgres://postgres:foobarbaz@db:5432/postgres
docker run -d \
		--name api-node \
		--network network \
		-e DATABASE_URL=${DATABASE_URL} \
		-p 3000:3000 \
		--restart unless-stopped \
		api-node

localhost:3000도 정상적으로 작동이 잘 된다....!
즉, node api가 잘 작동이 된다.

  1. Golang API 컨테이너 실행
docker run -d \
		--name api-golang \
		--network network \
		-e DATABASE_URL=${DATABASE_URL} \
		-p 8080:8080 \
		--restart unless-stopped \
		api-golang

이번에는 localhost:8080에서 golang-api가 잘 실행된다.

  1. Vite와 Nginx Clients 실행

우리는 vite를 사용하여 만든 development 서버와 보다 production 준비가 완료된 nginx 기반 클라이언트를 모두 실행할 것이다.

여기서 한가지 알아야 할점은 vite config 같은 경우 도커 안에서와 도커 밖에서의 코드가 다르다.

도커 밖

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import dns from 'dns';

dns.setDefaultResultOrder('verbatim');

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api/golang': {
        target: 'http://localhost:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api\/golang/, ''),
        secure: false,
      },
      '/api/node': {
        target: 'http://localhost:3000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api\/node/, ''),
        secure: false,
      },
    },
  },
});
도커 안

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import dns from 'dns';

dns.setDefaultResultOrder('verbatim');

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    proxy: {
      '/api/golang': {
        target: 'http://api-golang:8080',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api\/golang/, ''),
        secure: false,
      },
      '/api/node': {
        target: 'http://api-node:3000',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api\/node/, ''),
        secure: false,
      },
    },
  },
});

도커 허브 안에서는 링크 옵션을 전달했기 때문에 localhost에서 참조하는 대신 컨테이너의 별칭으로 참조해야한다.

docker run -d \
		--name client-react-vite \
		--network network \
		-v ${PWD}/client-react/vite.config.js:/usr/src/app/vite.config.js \
		-p 5173:5173 \
		--restart unless-stopped \
		client-react-vite
        
docker run -d \
		--name client-react-nginx \
		--network my-network \
		-p 80:8080 \
		--restart unless-stopped \
		client-react-ngnix


localhost:5173이 잘 작동이 된다.

0개의 댓글