글을 시작하기에 앞서,
나는 3년차 프론트엔드 개발자로, 그 동안 회사에서 프론트 개발 위주의 업무만 했었다.
당장 프론트만 해도 기술 트렌드가 금방금방 바뀌다보니 그거 따라잡기 바빴었다. (react 했다가 vue 했다가.. 다시 react+next하고 난리법석)
그러다가 이번에 이직을 하게 되었는데, 주니어로만 구성되어있던 전 직장과 달리 감사하게도 나 빼고 다 시니어 개발자분들로 개발팀이 구성되어 있었고 이번에 주니어로 내가 합류하게 되면서 많은 가르침을 받고 있다.
프론트엔드 개발자도 인프라에 대해 어느정도 알아두면 좋다며 docker, kubernetes, aws 등등 공부해보자 하셨고, 프로젝트가 잠시 여유있는 기간 동안 인프라에 대해 공부해보게 되었다. (즉 이 글은 인프라에 매우 무지한 프론트 개발자가 찍먹한 인프라를 기록용으로 쓰는 글이라는 점...ㅎㅎ)
컨테이너
는 실행 환경까지 포함하여 독립적으로 프로그램을 실행할 수 있도록 도와주는 기술이다. 컨테이너 환경을 묶어서 배포한 컨테이너 이미지를 내려받아 구동하면 각종 설정 과정을 줄일 수 있다. 이러한 컨테이너를 사용할 때 필요한 도구가 컨테이너 런타임
이며, 그 중 유명한 것이 도커
이다.
*용어 정리
컨테이너
: 앱이 구동되는 환경까지 감싸서 실행할 수 있도록 하는 격리 기술
도커
: 컨테이너를 다루는 도구 (컨테이너 런타임)
쿠버네티스
: 컨테이너 런타임을 통해 컨테이너를 오케스트레이션하는 도구
오케스트레이션
: 여러 서버에 걸친 컨테이너 및 사용하는 환경 설정을 관리하는 행위
Docker
는 컨테이너를 사용해 애플리케이션을 실행하기 위한 플랫폼이다. 컨테이너는 애플리케이션과 필요한 모든 라이브러리, 의존성을 하나의 패키지로 묶어 운영체제에 독립적인 환경을 제공한다. 즉, 어떤 환경에서든 프로그램이 동일하게 운영되게 도와준다.
Docker Image
는 컨테이너를 실행하기 위한 불변의 템플릿이다. 애플리케이션 코드, 라이브러리, 환경설정 파일 등을 하나의 이미지로 저장하는 것이다.
프로젝트 루트 위치에 Dockerfile 생성
> Dockerfile
# 베이스 이미지 선택
FROM node:22
# 작업 디렉토리 설정
WORKDIR /app
# 패키지 파일 복사
COPY package*.json ./
# 의존성 설치
RUN npm install
# 애플리케이션 코드 복사
COPY . .
# 애플리케이션 실행
CMD ["npm", "start"]
# 컨테이너가 사용할 포트
EXPOSE 3000
services:
web:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:3000"
volumes:
- ./app:/app
environment:
NODE_ENV: production
docker build -t <이미지_이름>:<태그> <경로>
> docker build -t my-web:1.0 .
my-web
: 이름으로 이미지 태깅
1.0
: 태그 정보
.
: 현재 디렉토리를 빌드 컨텍스트로 사용
docker-compose build
docker images
> docker images
> 결과물
> REPOSITORY TAG IMAGE ID CREATED SIZE
> my-web 1.0 2d22502f7d55 30 seconds ago 3.21GB
REPOSITORY
: 이미지 이름
TAG
: 이미지 태그
IMAGE ID
: 이미지의 고유 ID
SIZE
: 이미지 크기
docker run -p <호스트_포트>:<컨테이너_포트> <이미지_이름>:<태그>
> docker run -p 8080:3000 my-web:1.0
p 8080:3000
: 호스트의 8080번 포트를 컨테이너의 3000번 포트와 연결
my-web:1.0
: 사용할 이미지 이름과 태그
동작 방식
∙호스트 포트 : 도커 컨테이너 외부에서 접근 시 사용되는 포트
∙컨테이너 포트 : 도커 컨테이너 내에서 실행되는 포트
호스트 포트를 통해 브라우저나 다른 클라이언트가 컨테이너 내 프로젝트에 접근한다. → 컨테이너는 내부적으로 3000번 포트에서 프로젝트를 실행하고 있으며, 외부에서 직접 접근 불가능하다. → 8080번 포트로 접근 시 컨테이너의 300번 포트로 요청이 전달된다.
docker volume create <볼륨 이름>
> docker volume create volumeTest
// 볼륨 리스트 확인
> docker volume ls
> DRIVER VOLUME NAME
> local volumeTest
// 볼륨 정보 확인
> docker volume inspect volumeTest
>
[
{
"CreatedAt": "2024-12-26T04:18:25Z",
"Driver": "local",
"Mountpoint": "/var/lib/docker/volumes/volumeTest/_data",
"Name": "volumeTest",
"Options": null,
"Scope": "local"
}
]
> docker run -p 8080:3000 -v /Users/joy/log:/app/logs my-web:1.0
→ 확인
// 실행 중인 컨테이너 확인
> docker ps
> CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
> 7cbce098572b my-web:1.0 "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 0.0.0.0:8080->3000/tcp gifted_mclaren
> docker exec -it 7cbce098572b sh // 컨테이너 접속
> cd /app
> ls -l // 파일 리스트 확인
-rw-r--r-- 1 root root 365 Dec 26 05:23 Dockerfile
-rw-r--r-- 1 root root 1253 Dec 26 00:45 README.md
-rw-r--r-- 1 root root 238 Dec 26 05:15 app.log
-rw-r--r-- 1 root root 393 Dec 24 06:12 eslint.config.mjs
drwxr-xr-x 2 root root 4096 Dec 26 05:33 logs
-rw-r--r-- 1 root root 228 Dec 24 06:17 next-env.d.ts
-rw-r--r-- 1 root root 744 Dec 26 02:15 next.config.mjs
drwxr-xr-x 1 root root 16384 Dec 26 05:32 node_modules
-rw-r--r-- 1 root root 298276 Dec 26 00:13 package-lock.json
-rw-r--r-- 1 root root 807 Dec 26 00:13 package.json
-rw-r--r-- 1 root root 135 Dec 24 06:12 postcss.config.mjs
drwxr-xr-x 2 root root 4096 Dec 24 06:12 public
drwxr-xr-x 12 root root 4096 Dec 24 06:22 src
-rw-r--r-- 1 root root 4328 Dec 26 00:37 tailwind.config.ts
-rw-r--r-- 1 root root 602 Dec 24 06:12 tsconfig.json
> exit
// 로그 파일 생성 확인
> ls /Users/joy/log
(1). 도커 로그인
> docker login
> Authenticating with existing credentials...
> Login Succeeded
(2). 이미지 태그 지정
docker tag <로컬_이미지> <dockerhub_사용자명>/<리포지토리_이름>:<태그>
> docker tag my-web:1.0 joy/my-web:1.0
(3). 이미지 푸시
docker push <dockerhub_사용자명>/<리포지토리_이름>:<태그>
> docker push joy/my-web:1.0
>
The push refers to repository [docker.io/joy/my-web]
...
92b12b0dccf2: Pushed
fd63102cac36: Pushed
1.0: digest: sha256:209aff250058014a9c073d2a311ee9644ca2d98221105d6f741aa3a7b712b0e3 size: 856
(4). dockerhub 업로드 확인
(1). aws configure 설정
> aws configure
> AWS Access Key ID [None]:
> AWS Secret Access Key [None]:
> Default region name [None]: ap-northeast-2
> Default output format [None]:
// 작업 증명 활성화 홧인
> aws sts get-caller-identity
>
{
"UserId": "",
"Account": "",
"Arn": ""
}
(2). ecr 레포지토리 생성
(3). aws ecr 로그인
aws ecr create-repository --repository-name <리포지토리_이름> --region <리전_이름>
> aws ecr get-login-password --region ap-southeast-2 | docker login --username ~~~
> Login Succeeded
(4). 이미지 태그 변경
docker tag <로컬_이미지_이름>:<태그> <AWS_Account_ID>.dkr.ecr.<리전_이름>.amazonaws.com/<리포지토리_이름>:<태그>
> docker tag my-web:1.0 ~~~
(5). 이미지 푸시
docker push <AWS_Account_ID>.dkr.ecr.<리전_이름>.amazonaws.com/<리포지토리_이름>:<태그>
> docker push ~~~
> The push refers to repository ~~~
...
3981e863d195: Pushed
92b12b0dccf2: Pushed
ff27b22d4715: Pushed
7bd0f29b7dc7: Pushed
1.0: digest: sha256:209aff250058014a9c073d2a311ee9644ca2d98221105d6f741aa3a7b712b0e3 size: 856
(6). 이미지 업로드 확인