am I..? 제가 할 수 있을까요? Docker가 뭐죠? 이름만 들어봤는데..
리눅스 서버에 제 이름 이니셜로 디렉토리를 생성 후 해당 디렉토리 하위에 git 클론 받아서 도커 이미지를 빌드하고 8089:80로 서버를 띄우라는 미션을 받았었습니다.
당시 저는 프론트엔드 직무로 입사한 지 3주차였고, 도커는 만져본 적도 깔아본 적도 없었습니다.. 프로젝트를 할 때는 백엔드 개발자분들도 docker 사용했던 분들이 안계셨어서 기회가 없었죠..
리눅스 명령어도 익숙치 않은 저는 얼레벌레 vscode로 ssh 연결하고 도커 이미지를 빌드하는 과정을 서칭했고 결과적으로 도커 띄우는 것에 성공..! 했었습니다. 그 과정을 한 번 기록해보고자 합니다.. 총총
vscode extension에 Remote - SSH라는 것이 있습니다. 요놈을 설치해줍니다.
이제 끝입니다. 거의 다했습니다.
Remote - SSH를 설치하면 왼쪽 사이드바로 저 아이콘이 생깁니다. 저 아이콘을 누르면 사이드바가 열리는데, 제일 상단에 셀렉트를 원격(터널/SSH)로 설정해주시면 됩니다.
그리고 톱니바퀴를 누르면 업데이트 할 SSH 구성 파일 선택하는 부분이 나옵니다. ssh 구성 파일을 선택해주면 config파일 내에 접근할 수 있습니다. 이 중 맨 첫번째 Users/{유저명}/.ssh/config
를 눌러줍니다.
원격 탐색기에 이제 host 설정을 넣어주면 됩니다. 아마 다 전달해주실거라 생각합니다.
예시로 다음과 같이 작성하면 됩니다.
Host 0.0.0.0
HostName 0.0.0.0
User shy
IdentityFile C:\Users\_pem\shy.pem
Port 22
호스트를 추가 누르면 해당 호스트로 연결이 됩니다. 새창 버튼 누르시면 vscode 새 창이 열리면서 ssh 접속이 완료됩니다!
도커 명령어를 작성해서 이제 컨테이너를 만들고 이미지를 빌드해야겠죠. 그런데 그 전에 중요한 게 있어요. 바로 Dockerfile을 프로젝트 최상단에 작성하는 것입니다. Dockerfile은 Image를 만드는 스크립트이고 그걸 우리 입맛대로 만드는 것이라고 생각하면 됩니다.
Dockerfile은 명령어가 Layer 형식으로 실행됩니다. 그래서 소스코드인 src ./가 수정될 경우 이후 레이어만 다시 빌드 후 나머지 Layer는 재사용할 수 있어 이미지 제작 시간 단축할 수 있다는 장점이 있습니다!
뭐 어떻게 작성해야할지 모른다고요? 여기 vercel/next.js/Dockerfile 예시가 있습니다. 저도 Dockerfile 작성법을 몰라 이것을 기준으로 정리해보고자 합니다.
FROM node:18-alpine AS base
# From {baseImage명}:{version}
node:18-alpine는 Docker
가 사용할 기본 이미지를 설정합니다. 여기서는 Node.js 18 버전을 실행하는 Alpine Linux를 사용합니다.Alpine
은 linux 배포판으로 docker이미지의 크기를 최소화하는데 유용하다고 합니다. As base
는 이미지 레이어의 이름을 base
로 별칭 지정합니다. 이 이름은 다음 스크립트들에서 참조할 때 사용합니다.FROM base AS deps
RUN apk add --no-cache 'libc6-compat'
WORKDIR /app
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN rm -rf ./.next/cache
From base As deps
는 새로운 빌드 스테이지를 시작하며, 이전에 정의한 base
이미지를 기반으로 합니다. Run apk add --no-cache libc6-compat
는 Alpine 리눅스에서 특정 C 라이브러리(libc6-compat
)를 설치합니다. 특정 Node.js 패키지가 시스템 레벨의 라이브러리에 의존할 때 필요할 수 있습니다.WORKDIR /app
은 작업 디렉토리를 /app으로 설정합니다.COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
는 프로젝트의 의존성 파일을 컨테이너 내 /app 디렉토리로 복사합니다.RUN npm run dev
처럼 작성하시면 됩니다.FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN yarn build
FROM base AS builder
는 빌드 스테이지를 시작합니다.deps
스테이지에서 설치한 node_modules를 현재 스테이지로 복사합니다.# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /usr/src/app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# builder된 곳에서 만들어진 public을 해당 runner 이미지에 추가해주기 위함
COPY --from=builder /usr/src/app/public ./public
COPY --from=builder --chown=nextjs:nodejs /usr/src/app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /usr/src/app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
CMD ["node", "server.js"]
FROM base AS runner
는 최종 실행 스테이지를 정의합니다. module.exports = {
output: 'standalone',
}
```
- 이렇게 할 경우 next.js에서 자동으로 프로덕션 배포에 필요한 파일들만 추출해서 독립 실행 가능한 .next/standalone 폴더를 만들어줍니다. 그러면 **node_modules를 설치하지 않고도 자체적으로 배포**할 수 있습니다. 이때 **프로젝트를 실행할 수 있는 server.js파일을 만들어 주기 때문에 이걸로 프로젝트를 실행**하면 됩니다.
- public폴터와 같이 static폴더 하위에 있는 내용은 standalone에 포함되지 않으므로, 따로 가지고 와야 합니다.
이제 진짜 끝입니다. 이제 드디어 도커 이미지 생성할 수 있습니다.
도커 명령어는 Dockerfile이 있는 곳에서 명령어를 쳐야 작동됩니다. 왜냐면 당연히 Docker가 빌드 과정에서 Dockerfile과 해당 디렉토리의 파일 및 폴더를 참조하기 때문입니다.
그리고 사수분께서 도커 명령어를 작성할 때는 계정을 관리자로 전환한 뒤에 실행해야한다고 하셨었습니다. 시스템 리소스를 직접적으로 접근하고 제어하기 위해서, 보안 문제로 인해, Docker Daemon과 통신하며 동작하기때문에, 권한 부족으로 인한 에러 방지 등의 이유가 있다고 합니다.
sudo su -
su - shy
docker build -t [image_name] [path_to_dockerfile]
docker build -t frontend .
- -`t` : tag옵션 의미, 이미지 별칭을 지정해줍니다.
docker run --name [실행image명] -v $(pwd):[workdir] -p [입력포트]:[컨테이너내부포트] [컨테이너명]
docker run --name front -v $(pwd):/home/app -p 8089:8089 front-container
docker images
여기까지 하면 도커 이미지를 빌드하고 실행한 것입니다.
하지만 host 안에 있는 코드의 경로와 docker 컨테이너 안에 있는 코드의 경로는 다르며, 코드를 수정한 것을 반영하려면 docker 이미지를 재빌드 해야합니다. 도커 안에서 vi 편집기로 수정하거나 이미지를 재빌드 하는 것 보다는 코드를 마운트 시켜 바로 수정사항이 반영되게 하는게 더 좋겠죠? 따라서 도커 볼륨을 사용해 도커 컨테이너에서 해당 파일에 접근해서 사용하는 방법으로 동기화 할 것 입니다.
docker run -d **-v [리눅스 서버 안에 있는 코드의 경로]:[docker 컨테이너 안에 있는 코드의 경로]** -p [입력포트]:[컨테이너내부포트] [컨테이너 명]:[컨테이너 버전]
docker run -d -v /home/ubuntu/.docker/front-container/app:/var/www/html/app -p 8089:8089 front-container:v0.1
이렇게 하면 컨테이너 생성 시 도커 볼륨을 통해 host와 도커 컨테이너의 html 폴더를 동기화 할 수 있습니다.
- -d
: 백그라운드모드로 실행
- -p
: [호스트포트][컨테이너포트] 포트 연결
- -v
: 로컬과 컨테이너 파일 연동
docker kill [컨테이너 id || 컨테이너 이름]
docker stop [컨테이너 id || 컨테이너 이름]
docker rmi [이미지 id || 이미지 이름]
docker builder prune # 빌드 캐시 삭제
docker system prune # 이밈지, 정지 중 컨테이너, 네트워크 다 지우고 싶을 때
docker start [OPTIONS] CONTAINER [CONTAINER...]
일단 여기까지 제가 직접 써봤던 도커 명령어들입니다. 추후 여러개의 도커 컨테이너를 띄울 수 있는 도구인 도커 컴포즈라든지 사용해 볼 수 있을 기회가 있으면 좋을 것 같습니다. 후후
혹시나 틀린 부분이 있다면 언제든지 댓글로 올려주세요!
자료 출처
next.js Dockerfile
Next.js 도커 이미지를 만들어서 CI/CD를 통해 NCloud 배포(1)
docker 기본 명령어