도커 길잡이-1. 도커 기본 개념

Ho Kim·2022년 12월 19일
0

도커 길잡이

목록 보기
1/1

도커에 대해서는 이전 글 [도커와 쿠버네티스 - 도커]에서 다룬 적이 있다. 당시에는 42 과제를 하면서 공부했던 터라 사실 이해도가 그리 높지 않은 상태였는데, 쓰면 쓸수록 도커의 편리함에 감탄하게 되는 것 같다.

읽지 않아도 되는 뒷이야기
작년 초반에는 정말 작은 서버를 썼는데 그러다보니 클라우드 서버에 메모리가 모자라 오류가 생겼고, 자꾸 서버 자체를 리스타트 해야했다.

여기서 첫번째 문제가 생겼다. 이전에는 pm2를 써서 배포했는데, pm2 자체를 배포할때만 쓰다보니 커맨드를 자꾸 잊어서 매번 다시 검색하는 불상사가 발생한 것이다.

그러다가 더 좋은 사양의 배포 서버를 쓰게 되었는데, 여기서 두번째 문제가 생겼다. 개발서버에 배포했던게 기억이 안나서 다시 하나하나 검색해가면서 환경 구성 하는게 너무 귀찮았다.
그리고 지금은 스타트업 지원 사업으로 NCP를 쓰고 있지만 언젠가는 aws 로 옮길 수도 있는데, 그럴 경우 이런 환경 세팅 작업을 다시 하는 것은 시간낭비라고 생각했다. 그래서 도커라이즈 하고싶다, 해야한다고 우겨서 결국 구성하게 되었다.

지금 기간으로 따지면 만 7개월째 사용중인데 쓸수록 만족스럽기만 한 것 같다.

1. 도커란?

지난 글에서 설명했지만 많이 이론적인 설명이라 와닿지는 않았을 수 있다.

도커는 간단하게 말하자면 내 컴퓨터에 깨끗한 기본 환경 OS을 생성해주는 것이다. aws로 새 서버 접속했을때 나오는 바로 그 환경 말이다.

aws에 접속하면 서버를 올리기 위해 Node 혹은 Python 등 필요한 환경을 세팅하는 작업을 할 것이다.
서버 세팅을 위해 해야하는 작업을 DockerFile이라는 가상os 설정 설명서에 적어두면 도커가 이 도커 파일을 보고 기본세팅을 마친 상태의 가상 OS를 만들어준다.

dockerfile: 기능설명서라고 생각하면 된다. (설정파일)
image : 기능설명서를 바탕으로 생성된 청사진이라고 생각하면 된다. (설정 완료 상태로 실행 대기중인 OS)
도커파일을 build하면 생성된다.
container : 청사진을 바탕으로 실제로 지어진 건물이라고 생각하면 된다. (설정완료 상태로 실행중인 OS)
이미지를 run하면 만들어진다.

이미지는 클래스고, 컨테이너는 인스턴스라고 생각하면 이해하기 쉬울것이다.

이렇게 도커파일을 만들고 빌드하면 docker run 커맨드 한번에 맥에서든 리눅스에서든 윈도우에서든 별도의 작업 없이 동일한 서버를 바로 올릴 수 있다.

이 얼마나 편리한 기능이란 말인가!

2. 도커 명령어

도커를 써본 결과 도커는 기본 기능을 쓰려면 딱 7가지 명령어만 알면 된다.

  1. 상태 확인
    docker ps
    현재 돌고있는 도커 컨테이너를 모두 보여준다.
    -a 옵션을 주면 중단된 컨테이너도 보여준다.
    자세한 옵션은 도커 문서에서 보면 된다.

  2. 빌드
    docker build -t {이미지 이름} {도커파일 위치}
    도커파일을 바탕으로 설정이 모두 완료된 이미지를 생성한다.
    -t는 태그 옵션인데 이미지의 이름을 정할 수 있게 해준다.

  3. 실행
    docker run -d -p {컨테이너 외부 포트}:{컨테이너 내부 포트} {이미지 이름}
    빌드 완료된 이미지를 실행시킨다. 실제로 가상 os를 컴퓨터에 띄우는 커맨드다.
    -p는 포트 옵션인데 컨테이너의 포트를 외부와 연결해준다.

    컨테이너는 독립적인 환경이다.
    컨테이너 내부에서 사용중인 포트는 외부에서 접근할 수 없다. 컨테이너 내부에서 3000번 포트에 서버를 아무리 돌려도 localhost에서는 접근불가능하다.
    위 그림은 docker run -p 3000:3000 -p 3307:3306 한 이미지다.

    -localhost:3000으로 접근하면 container:3000 페이지를 볼 수 있고,
    -localhost:3307으로 접근하면 container:3306데이터베이스로 접근된다.
    -container:8000은 로컬호스트에서 접근할 수 없다.

-d는 백그라운드 실행 옵션이다.

  1. 컨테이너 진입
    docker exec -it {컨테이너 아이디} bin/bash
    실행중인 가상 os에 쉘로 접근할 수 있는 커맨드다.

  2. 컨테이너 재시작
    docker restart {컨테이너 아이디}
    실행중인 가상 os를 재시작한다.

  3. 컨테이너 중단
    docker stop {컨테이너 아이디}
    실행중인 가상 os를 중단한다.

  4. 컨테이너 삭제
    docker rm {컨테이너 아이디}
    실행중인 가상 os를 삭제하는 커맨드다. 실행중인 컨테이너는 stop 후에 삭제해야한다. 강제 삭제해도 괜찮다면 -f옵션으로 강제로 삭제할 수 있다.

나머지는 부가기능이다.
필요할때 공식 문서를 살펴보고 쓰면 될 것이다.

3. 도커파일

Dockerfile을 만들때는 파일명에 스펠링과 대소문자에 주의해야한다.

도커파일은 일반적인 코드처럼 위부터 순차적으로 실행된다.

도커파일에서 꼭 알아야할 구문은 4가지다.

  1. FROM {이미지이름}:{버전}
    상위 이미지를 지정한다.
    상위 이미지를 ubuntu로 지정해 node 부터 설치할 수도 있고, 상위 이미지를 node로 지정해 node가 이미 설치된 상태부터 시작할수도 있다.
    상위 이미지로 사용할 이미지는 도커이미지 저장소에서 검색하면 된다.

    FROM python:3.9.8
    버전은 필수 항목이 아니지만 지정하지 않으면 최신 이미지를 불러온다.
    개발환경은 버전에 영향을 많이 받으니 위처럼 버전을 꼭 지정해주자.

  2. COPY {로컬 파일 path} {컨테이너 파일 path}
    COPY ./requirements.txt /srcs/requirements.txt
    로컬 저장소의 파일을 컨테이너 내부로 복사한다.

  1. RUN
    RUN pip install --upgrade pip
    가상 os에서 명령을 실행한다.

  2. ENTRYPOINT 혹은 CMD
    ENTRYPOINT ["/bin/bash", "init.sh"]
    CMD ["/bin/bash", "init.sh"]
    두 명령어는 컨테이너가 최종적으로 실행할 명령어를 지정한다.
    다만 ENTRYPOINT변경 불가능한 최종 명령어, CMD는 최종명령어의 기본값이다.
    도커파일에 CMD [명령어1]로 최종 명령어를 지정하고 쉘에서 docker run {이미지 이름} 명령어2으로 실행하면 명령어2가 최종적으로 실행된다..

+) WORKDIR
뒤따르는 명령어에 대한 작업 디렉토리를 설정한다.

4. 도커파일 만들고 실행해보기

이제 도커파일을 만들고 실행해보자.
당연히 도커를 먼저 설치해야하는데, 여기서는 스킵하겠다.

여기서 할 것은 [도커로 CRA로 리액트 실행시키기]다.

1.Dockerfile 만들기

docker hubnode를 기반으로 할 것이다.
도커허브에서 노드를 검색해본다.

node:<version>
node:<version>-alpine
node:<version>-slim

세가지가 있는데, 설명을 읽어보고 알맞은 것을 쓰면 된다.
참고로 알파인은 리눅스 기반의 가벼운 os다.
이번에는 알파인을 한번 써 볼 것이다.

# Dockerfile
FROM node:19.3-alpine

2.접속해서 상세 설정하기(명령어 기록하기)

1) 실행 중단 방지 설정
외부에서 내부를 보지 않고 설정하는 것은 설정이 복잡해질수록 어렵다.
그래서 나는

  1. 일단 실행이 중단되지 않도록 sleep을 걸고
  2. 내부에 진입해서
  3. 명령어를 기록하면서

세팅하는 편이다.

# Dockerfile
FROM node:19.3-alpine

ENTRYPOINT [ "sleep", "infinity" ]

도커파일에서 entrypoint에 슬립을 건다.

2) 실행 및 접속

위처럼 세팅하고 빌드 및 실행시킨다.

> docker build -t node-react .
> docker run -d -p 3000:3000 node-react  

이렇게하면 -d 옵션을 주었으므로 백그라운드로 실행된다.

이제 접속해보자.

> docker exec -it {컨테이너 아이디} /bin/sh

{컨테이너 아이디} 위치에는 ps로 확인한 컨테이너 아이디를 넣으면 된다.

알파인에는 bash가 없어서 sh로 연다.

첫번째 밑줄은 현재 os에서 입력한 것이다. 해당 명령어로 도커 쉘에 진입했다.
두번째 명령어는 도커 쉘에서 입력한 ls 다. 도커 내부의 파일 목록을 보여준다.
npm -v를 쳐보면 npm이 이미 깔려있다는 것을 알 수 있다.

3) 앱 만들기
cra로 리액트 앱을 만든다.

npx create-react-app myapp

입력하고 조금 기다리면 앱이 만들어진다.
한번 실행해보자.

cd myapp
npm start


npm startendpoint이다.
이러한 엔드포인트를 ENTRYPOINT, 혹은 CMD에 적어야 한다.

앱이 실행될 동안 컨테이너가 유지될 것이고, 앱이 종료되는 순간 컨테이너도 종료될 것이다.
앱과 컨테이너의 생명주기를 일치시키는 것은 중요한데, 그래야 앱이 죽었을 때 자동재시작 설정을 할 수 있기 때문이다. 자동재시작은 다음 글에서 다룰 것이다.

이렇게 작성하는 명령어들은 Dockerfile에 잊지 말고 적어두어야한다.

# Dockerfile
FROM node:19.3-alpine
RUN npx create-react-app myapp
RUN cd myapp
ENTRYPOINT [ "npm", "start" ]

4) 실행 확인하기
-p로 포트를 연결시켜두었으므로 로컬호스트에서 확인 가능해야한다.

잘 접속되는것을 확인했다.

3. 도커파일 실행해보기

위에서 쉘에 진입해 환경설정을 하면서 명령어를 채웠다.
이제 build와 run 명령어를 이용해 손쉽게 리액트를 실행할 수 있다.

도커파일을 바꿨으면 build를 다시해야한다. 빌드된 이미지는 도커파일의 변경을 감지하고 새로 생성되지 않기 때문이다.
수정 후 재빌드를 잊으면 이미지가 계속 유지되므로 아무리 여러번 run을 해도 이전 도커파일의 실행 결과를 보게 될 것이다.

1) 테스트 실행

# Dockerfile
FROM node:19.3-alpine
RUN npx create-react-app myapp
RUN cd myapp
ENTRYPOINT [ "npm", "start" ]

위처럼 도커파일을 만들고 쉘에서 실행한다.
테스트 실행때는 백그라운드 말고 포그라운드로 실행하는것이 좋다.
잘못되었을 경우 결과를 바로 알 수 있기 때문이다.

-p 3000:3000으로 컨테이너 외부 3000번으로 다시 연결하려고 하면 port is already allocated오류가 뜰 것이다.
이전 도커 컨테이너를 중지하지 않았으니 로컬호스트의 3000포트를 다시 연결해줄 수 없다.
대신 3001번을 쓰자.

> docker build -t node-react .
> docker run -p 3001:3000 node-react  

2) 에러 수정
run 하고나면 오류가 뜬다.

package.json이 없다는 걸 보면 npm start 위치가 잘못 되어서 뜬 오류다.
RUN cd myappENTRYPOINT의 실행 위치에 영향을 주지 않은 것이다.

이때는 RUN이 아니라 WORKDIR을 써야한다.
WORKDIR뒤따르는 명령어에 대한 작업 디렉토리를 설정한다.

# Dockerfile
FROM node:19.3-alpine
RUN npx create-react-app myapp
WORKDIR "/myapp"
ENTRYPOINT [ "npm", "start" ]

이렇게 하고 빌드 및 실행하면 잘 실행되는 것을 볼 수 있다.

+) ps
ps로 보면 컨테이너 아이디, 이미지, 최종 실행 커맨드, 연결된 포트 등 다양한 정보를 알 수 있다.
쓰다보면 ls 만큼 자주 쓰게된다.

0개의 댓글