Skip to content

Latest commit

 

History

History
460 lines (351 loc) · 15.8 KB

README_kr.md

File metadata and controls

460 lines (351 loc) · 15.8 KB

Visual Studio Code Dev Container 튜토리얼

인트로

The Visual Studio Code Remote - Containers extension lets you use a Docker container as a full-featured development environment.

개발에 필요한 툴들을 버전에 맞게 설치하고 일일히 업데이트 하는 것은 힘들고 귀찮은 일입니다. 도커비주얼 스튜디오 코드를 함께 사용함으로서 소프트웨어 개발 경험을 크게 개선할 수 있습니다.

이 튜토리얼에서는 express.js 서버와 PostgreSQL 데이터베이스를 포함한 개발 환경을 구축하는 것을 목표로 합니다. 이 프로젝트를 기준으로 다른 도커 컨테이너를 추가해서 사용하는 것도 좋습니다.

도커 허브에서 찾을 수 있는 이미지 목록입니다. 필요에 따라 창의적으로 조합해서 사용하세요.


튜토리얼 요구사항

튜토리얼에 들어가기 앞서 도커, 비주얼 스튜디오 코드, Remote - Containers VSCode 익스텐션이 컴퓨터에 설치되어 있어야 합니다.

아래의 링크에서 설치 방법을 찾을 수 있습니다:


자, 그럼 시작합시다

데브 컨테이너를 작동시키려면, 설정 파일 세개를 작성해야 합니다:

  • .devcontainer/Dockerfile
  • .devcontainer/docker-compose.yml
  • .devcontainer/devcontainer.json

각각의 파일을 살펴보고, 어떤 역할을 하는지 알아봅시다.


Dockerfile

Dockerfile은 도커 이미지를 만들기 위한 설계도입니다. Dockerfile로 만들어진 도커 이미지를 사용해서 도커 컨테이너를 생성하게 됩니다.

지금은 Dockerfile, 도커 이미지, 도커 컨테이너 간의 차이에 관해서 이해하지 못해도 괜찮습니다. 도커 컨테이너는 가상 머신과 거의 비슷한 역할을 하지만 도커 컨테이너가 가상 머신보다 생성/파괴/시작/종료가 쉽다는 점만 기억하세요.

만약 도커에 대해 더 자세한 설명을 원하신다면 도커 공식 문서를 참조하세요.

https://docs.docker.com/get-started/overview/

이게 Dockerfile의 첫번째 줄입니다:

FROM ubuntu:20.04

Dockerfile 은 도커 이미지의 설계도이며, FROM 커맨드를 이용해서 어떤 이미지를 템플릿으로 사용할지 정할 수 있습니다. 이 파일에서는 ubuntu:20.04를 사용했습니다.

이어서 Dockerfile에서 사용할 변수들을 선언합니다:

ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

ubuntu:20.04의 기본 유저는 루트 계정이기 때문에, 나중에 이 변수들을 사용해서 새로운 유저를 생성할 예정입니다.

ENV DEBIAN_FRONTEND=noninteractive

데비안과 우분투는 apt라고 하는 패키지 관리자를 사용하는데, apt는 파일 권한이나 시간대 설정 등의 이유로 유저 입력을 요구할 수 있습니다. 도커 이미지 생성을 완전히 자동화하기 위해서는 apt의 유저 입력을 비활성화 해야합니다. DEBIAN_FRONTEND 환경변수가 apt의 유저 입력 여부를 결정합니다.

이어서, apt를 사용하여 도커 이미지에 필요한 패키지들을 설치합시다.

RUN apt-get -y update --no-install-recommends \
    && apt-get -y install --no-install-recommends \
    build-essential \
    curl \
    ca-certificates \
    apt-utils \
    dialog \
    git \
    vim \
    && apt-get autoremove -y \
    && apt-get clean -y

더 필요한 패키지가 있다면, 위 커맨드에 추가하면 됩니다.

이제 curlca-certificate이 설치되었기 때문에 Node.js의 설치 스크립트를 다운로드하고 실행할 수 있습니다. 우분투의 패키지 저장소에는 오래된 버전의 노드만 있기 때문에, 아까 다른 패키지를 설치하면서 같이 설치하지 않았습니다. nodesource.com 에서 설치 스크립트를 다운로드 받아서 실행하고 나면, 노드 버전 12를 설치할 수 있게 됩니다.

RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -

nodesource.com 의 설치 스크립트를 실행하고 나면 apt를 사용해서 노드 버전 12를 설치할 수 있습니다.

RUN apt-get -y update --no-install-recommends \
    && apt-get -y install --no-install-recommends nodejs \
    && apt-get autoremove -y \
    && apt-get clean -y

앞에서 선언한 변수들 기억하시나요? 그 변수들을 사용해서 새로운 시스템 유저를 만들 차례입니다.

RUN groupadd --gid $USER_GID $USERNAME \
    && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME

NOTE: 만약 sudo 커맨드를 사용하고 싶다면, 추가적인 설정이 필요합니다. 이 튜토리얼에서는 sudo를 어떻게 설정하는지 다루지 않습니다.

DEBIAN_FRONTEND 환경변수를 다시 dialog로 되돌려서 도커 컨테이너를 사용할 때는 apt와 상호작용 할 수 있게 합니다.

ENV DEBIAN_FRONTEND=dialog

새로 만들어진 시스템 유저를 루트 대신 기본값으로 설정합니다.

USER $USERNAME

docker-compose.yml

이제 docker-compose.yml 파일을 살펴봅시다.

YAML은 설정 파일에 자주 쓰이는 파일 포맷입니다. 키-값 구조로 설정을 저장할 수 있기 때문에 복잡한 설정 파일을 작성하기 편리합니다. YAML에 대해서 더 알고 싶다면 공식 웹사이트를 확인하세요.

https://yaml.org/

docker-compose는 여러개의 도커 컨테이너를 한번에 다루기 위한 커맨드라인 도구입니다. docker-compose가 제어하는 각각의 컨테이너는 "서비스"라고 부르고, 이 튜토리얼에서는 두개의 서비스 (express 서버 + postgres 서버)를 사용할 것입니다.

docker-compose.yml 는 버전 넘버로 시작합니다.

version: '3.7'

이건 compose file의 버전 넘버입니다. compose file 3.7버전은 도커 엔진 19.03 이상의 버전만 지원하기 때문에 컴퓨터에 설치된 도커 버전이 compose file을 처리할 수 있는지 꼭 확인해야 합니다.

compose file의 버전 호환성 테이블은 여기에서 찾을 수 있습니다:

https://docs.docker.com/compose/compose-file/

YAML에는 anchor이라는 기능이 있습니다. 이 기능을 이용해서 YAML 파일 안에 반복되는 부분을 anchor로 정의하고 이후에 alias를 써서 불러올 수 있습니다.

튜토리얼의 docker-composeyml 파일은 최상위 키 x-environment로 시작합니다. "&"는 anchor를 의미합니다.

x-environment:
    &default-environment
    POSTGRES_USER: vscode
    POSTGRES_PASSWORD: notsecure
    POSTGRES_DB: tutorial

나중에 이 anchor을 alias로 불러 올 예정입니다.

x-environment 키 다음에는 docker-compose가 관리하는 서비스들을 설정하는 services 키가 있습니다.

그리고 그 services 키는 다시 각 서비스를 나타내는 app 키와 db 키를 가집니다.

app을 먼저 살펴봅시다. app의 첫번째 키는 build 입니다.

build:
    context: ..
    dockerfile: .devcontainer/Dockerfile

이름에서 알 수 있듯이 build 키는 서비스 컨테이너를 빌드하는 방법을 설정합니다. 이 경우, 빌드 컨텍스트가 ..(상위 디렉토리)이고, .devcontainer/Dockerfile을 사용해서 빌드해야 한다는 것을 알 수 있습니다. dockerfile의 경로가 context의 상대 경로임에 주목합시다.

app안의 다음 키는 environment 이고, 이 키는 서비스 컨테이너의 환경변수를 설정합니다.

environment:
    <<: *default-environment
    PORT: 3000

처음에 default-environment anchor을 작성한 것을 기억하나요? <<: *default-environment는 "default-environment의 모든 값을 environment로 불러오시오" 라는 뜻입니다. 또한, alias 이후에 PORT 키가 설정된 것을 볼 수 있습니다. PORT 변수는 나중에 express 서버가 어떤 포트를 listen 해야하는지 설정합니다.

ports:
    - 3000:3000

PORT 환경변수 때문에 express 서버는 3000번 포트를 listen 할 것입니다. 호스트의 포트를 컨테이너의 포트에 포워드 해주어야 이 express 서버에 접근할 수 있습니다. 이러한 포트 포워딩을 ports 키로 설정할 수 있습니다.

자, 이제 개발을 신나게 해주는 설정을 살펴봅시다!

volumes:
    - ..:/workspace

volumes키는 호스트 머신의 디렉토리를 서비스 컨테이너에 마운트 하게 설정합니다. 이 경우, 비주얼 스튜디오 코드의 워크스페이스를 app/workspace 경로에 마운트 합니다. 이렇게 설정함으로써 개발할 때 app 서비스 컨테이너에 접속한 상태에서 파일들을 수정할 수 있습니다. 서비스 컨테이너 안에서 수정된 파일들은 자동으로 호스트 머신과 동기화됩니다!

이게 바로 본 튜토리얼에서 만드는 세팅이 굉장한 이유입니다. 서비스 파일은 오직 Dockerfile에 의해서만 정의되기에, 완벽히 통제된 환경에서 개발을 진행할 수 있습니다. 컨테이너 환경에서 잘 동작하는 것은 배포 환경에서도 잘 동작할 것이고, 파이썬이나 MySQL 버전이 틀릴지 따위를 고민할 필요가 없습니다!

이제 app의 마지막 두 설정을 살펴봅시다.

user: vscode
command: sleep infinity

user 키는 서비스 컨테이너의 기본 유저를 설정하고, command 키는 서비스 컨테이너가 시작된 후에 무엇을 실행할지 설정합니다. 서비스 컨테이너들은 커맨드 실행이 끝나면 종료되기 때문에 sleep infinity 커맨드로 서비스 컨테이너를 살려둡니다.

이렇게 app 서비스의 설정을 알아보았습니다.

두번째 서비스는 db 입니다. app 서비스와는 다르게 db 서비스 컨테이너를 빌드할 때는 Dockerfile을 사용하지 않습니다. 그대신 Docker Hub에 올라와 있는 도커 이미지 하나를 사용할 것입니다.

image: "postgres:12"

image 키를 설정하면, docker-compose가 해당 도커 이미지를 Docker Hub 에서 내려받은 후, 받아진 이미지를 기반으로 서비스 컨테이너를 생성합니다. 여기에서는 postgres:12 이미지를 사용합니다.

restart: unless-stopped

restart 키는 재시작 정책을 설정합니다. unless-stopped 값으로 설정하면 컨테이너를 강제로 종료하지 않는 이상 항상 컨테이너를 재시작합니다.

environment: *default-environment

YAML alias가 다시 나왔습니다. 이번에는 덮어쓰는 값 없이 default-environment를 그대로 불러옵니다.

ports:
    - 5432:5432

PostgreSQL 서버의 기본 포트는 5432 입니다. 이 포트를 포워딩 해서 PostgreSQL에 접근할 수 있도록 설정합니다.

volumes:
    - pgdata:/var/lib/postgresql/data
    - ../postgresql/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d

db 서비스 컨테에너에는 볼륨을 두 개 마운트 합니다.

첫번째 볼륨 pgdata는 데이터베이스 파일들을 저장하는 용도입니다. pgdata가 상대경로가 아니라는 점에 주목해주세요. pgdata도커 볼륨입니다. 도커 볼륨은 도커에 의해 관리되는 특별한 저장공간이며, 컨테이너들에 마운트 할 수 있습니다.

두번째 볼륨 ../postgresql/docker-entrypoint-initdb.d/는 초기화 스크립트를 저장하는 디렉토리입니다. 데이터베이스 파일이 존재하지 않는다면, postgres 컨테이너는 데이터베이스와 유저를 생성하고, /docker-entrypoint-initdb.d 안에 있는 모든 스크립트를 실행합니다. 튜토리얼 앱에 todo 테이블이 필요하기 때문에, 초기화 스크립트로 CREATE TABLE 구문이 들어있는 SQL 스크립트를 사용합니다.

volumes:
    pgdata:

최상위 키 volumes는 재사용 가능한 볼륨을 설정할 때 씁니다. 여기에 보이는 빈 키 pgdata는 기본 드라이버를 사용한 도커 볼륨을 생성합니다.


devcontainer.json

출처

{
    "name": "Node.js",
    "dockerComposeFile": "docker-compose.yml",
    "service": "app",
    "workspaceFolder": "/workspace",
    "settings": {
        "terminal.integrated.shell.linux": "/bin/bash"
    },
    "extensions": []
}

devcontainer.json 파일은 "Remote - Containers" 익스텐션의 설정을 변경합니다.

  • dockerComposeFile : devcontainer.json 파일 기준 docker compose file의 상대경로.
  • service : VS Code가 실행 후 연결하여야 하는 서비스의 이름.
  • workspaceFolder : 컨테이너에 연결할 때 VS Code가 열어야 하는 기본 경로.
  • settings : 컨테이너의 settings.json 기본값 추가.
  • extensions : 컨테이너가 생성된 후 컨테이너 안에 설치해야 하는 익스텐션의 ID 행렬.

컨테이너 안에서 Workspace 열기

설정파일 세개를 모두 작성하였습니다.

이제 이 workspace를 데브 컨테이너 안에서 열어봅시다.

첫번째로, 아직 VS Code를 실행하지 않았다면 지금 실행하세요. 그리고, **"File > Open Folder ..."**를 선택해서 이 프로젝트의 디렉토리를 여세요.

First Local

좌측 하단에서 "Open a remote window" 버튼을 확인할 수 있습니다. (위 스크린샷에서 파란색 네모 버튼) 클릭해 주세요.

Remote Menu

위의 스크린샷과 같이 VS Code 메뉴가 열립니다. "Remote-Containers: Reopen in Container" 를 선택하세요.

Loading

도커 이미지가 생성되고 컨테이너가 시작될때까지 시간이 소요됩니다. 몇 분 (기기에 따라 몇 초) 정도 기다리면 VS Code가 데브 컨테이너 안에서 workspace를 열어줍니다.


마치며

축하합니다! 성공적으로 VS Code의 workspace를 데브 컨테이너 안에서 열었고, postgres 서버도 작동합니다.

이 코드 저장소의 app.js 파일에 초간단한 TODO API 서버 구현물이 있습니다. 서버를 실행하고 직접 테스트해 보세요!

npm ci
node app.js

REST API

Path Method Parameters Description
/ GET Hello, World!
/todo POST task Create TODO
/todo GET List TODO
/todo/finished POST id, finished Update TODO Status
/todo DELETE id Delete TODO