article thumbnail image
Published 2021. 12. 10. 19:17

Docker 설치

Docker 를 이용한 배포하기

 

하나의 이미지를 다루는 방식과 두개 이상의 이미지를 다루는 방식으로 나눠서 알아보자

 

먼저 Docker 설치가 필요
ubuntu를 사용하는 경우 아래 링크를 참고해 설치하자
https://docs.docker.com/engine/install/ubuntu/

 

Install Docker Engine on Ubuntu

 

docs.docker.com

 

Docker Engine 설치가 끝나면, Docker Compose 설치도 진행해야 한다
https://docs.docker.com/compose/install/

 

Install Docker Compose

 

docs.docker.com

 

위 설치가 다 정상적으로 되었다면, docker-compose --version, docker --version 을 해서 오류없이 버전이 정상 출력되면 정상 설치된 것이다

주의: ubuntu 운영체제로 진행할 때는 모든 명령어 앞에 기본적으로 sudo를 붙인다고 생각하면 된다

학습 목표

* 컨테이너 기술과 도커의 필요성
* 컨테이너와 이미지, 레지스트리
* 대표적인 레지스트리인 Docker Hub를 통한 이미지 검색, 사용
* 한 개의 이미지를 이용한 컨테이너 구축
* 두 개 이상의 이미지를 이용한 컨테이너 구축(어떻게 연결되는지 확인)
* Docker CLI에서 명령어를 사용한 이미지 생성/수정/배포, 컨테이너 생성/삭제

Docker를 사용하는 이유

컨테이너 기술과 Docker의 탄생 배경

  • 컨테이너
    • 우리가 일반적으로 생각하는 컨테이너
      • 항구에서 물류 운반 시에 그 컨테이너
      • 컨테이너가 생기면서 물류에는 혁신이 일어남
        • 물자를 싣고 내릴 때, 선박의 계류 시간을 획기적으로 단축
        • 물자를 싣고 내릴 때, 필요한 인력을 대폭 감소

이러한 컨테이너 기술을 소프트웨어 수송; 배포에 적용한게 리눅스 컨테이너(lxc)

 

애플리케이션을 쉽게 컨테이너화할 수 있게 해주는 생태계 혹은 커뮤니티인, Docker에서 제공하는 Docker Hub 라는 소프트웨어 저장소가 등장하면서 개발자들이 쉽게 애플리케이션을 포장하고 컨테이너 방식으로 실행할 수 있게 됨

  • 컨테이너 방식으로 애플리케이션을 실행하는 것의 장점
    • 실행 환경에 구애받지 않고 애플리케이션을 실행할 수 있다

컨테이너 방식의 장점

의존성 충돌 문제를 해결

기본적으로 특정 애플리케이션을 실행하기 위해서는 반드시 어떤 환경(제반 환경)이 구축되어 있어야 한다
→ ex) 윈도우용 프로그램 설치시 .NET Framework 를 설치해라는 문구

 

이처럼 어떤 프로그램(A) 실행에 다른 프로그램(B)가 반드시 필요한 경우
프로그램 A는 프로그램 B에 의존 관계를 가지고 있다 라고 함

 

그런데, 하나의 프로그램에 의존적인 여러 프로그램들 그 각각이 서로 다른 버전에 대해서 의존적이라면, 의존성 충돌의 문제가 발생할 수 있다

// 의존성 충돌의 예

wordpress 5.2 → php 7
drupal 8.1 → php 6

둘다 설치하는 경우?
wordpress 5.2 → php ?
drupal 8.1 → php ?

컨테이너 기술을 통해 이러한 의존성 충돌의 문제를 해결 가능하다
→ 컨테이너 내에 애플리케이션을 구성함
→ 컨테이너에서 실행되는 애플리케이션은 어떠한 의존성도 공유하지 않고, 각자 고유의 의존성을 가지고 있음
→ 컨테이너에서 철저하게 자가 격리된다고 생각하면 편함(실행 환경이 격리됨)

컨테이너 내에서 격리되고, 독립적으로 소유되는 자원

  • 프로세스
    • 특정 컨테이너에서 작동하는 프로세스는 기본적으로 그 컨테이너 안에서만 액세스 가능
    • 컨테이너 안에서 실행되는 프로세스는 다른 컨테이너의 프로세스에 영향x
  • 네트워크
    • 기본적으로 컨테이너 1개에 하나의 IP 주소 할당됨
  • 파일 시스템
    • 컨테이너 안에서 사용되는 파일 시스템은 구획화됨
      • 해당 컨테이너에서의 명령이나 파일 액세스 등을 제한 가능

컨테이너와 가상머신?
리눅스 컨테이너 기술은 엄밀히 말하면 가상 머신의 접근 방법과는 다르다. 단지, 컨테이너가 가상 머신과 비슷한 수준의 격리성을 제공한다는 점 정도만 알면 된다

개발과 배포 환경을 일치

개발시의 환경 구축(개발팀)

  • 도커가 없는 개발 환경 구축
    • 여러 개발자가 하나의 애플리케이션 개발을 위해, 비슷한 개발 환경을 구축해야 함(특정 버전의 프로그램, 시스템별 맞는 환경 변수 구성 등)
      → 사전 개발 환경을 비슷하게 맞추는 데만도 시간적 소요가 많이 듬(험난한 과정)
  • 도커를 이용한 개발 환경 구축: 개발 환경 일치
    • 도커가 실행 중이면, 어떠한 운영체제든 상관없이 동일한 명령어로 어떤 프로그램을 설치, 실행 등 가능
      → OS에 상관없이 즉시 애플리케이션 실행 환경 구축 가능
      → 개발을 컨테이너 위에서 진행할 경우, 모든 개발팀이 동일한 환경에서 개발을 진행 가능

배포 시의 문제 해결(배포 환경 걱정x)

개발 환경의 일치 이슈는 서비스 배포 환경에도 동일하게 적용할 수 있다
어떤 애플리케이션이 특정 런타임 환경 위에서 실행되고, 사용자에게 이를 제공한다 는 것인데, 이는 앞서 말한 실행 환경 구성과 본질적으로 다를 것이 없기 때문

  • 배포 파일 업로드 방식의 개선
    • 기존: 서버에 파일 하나하나씩 업로드
    • 도커: 컨테이너의 담아서 업로드

이에 따라 서버도 컨테이너의 담긴 애플리케이션을 실행하는 방식으로 서비스를 제공한다
→ ex) AWS의 EC2 상에 Docker를 설치하거나, AWS ECS 서비스를 이용하면 더욱 쉽게 애플리케이션 배포 가능

손쉬운 수평적 확장 및 새로운 내용 배포

컨테이너 기술의 가장 큰 장점인 실행 환경의 일치 를 생각하면 쉽게 이해 가능하다
서버에 대한 트래픽이 증가하여 서버를 증설할 때, 컨테이너 기술을 활용하면
→ 동일한 애플리케이션 구성(이미지)을 바탕으로 새로운 서버에 해당 애플리케이션을 컨테이너로 실행하고, 로드 밸런서에 이 서버를 추가해 주면 끝(수평적 확장 끝)

  • 손쉬운 새로운 내용 배포: 위를 응용한 것. 새로운 버전의 애플리케이션을 여러 서버 중 몇대에만 운영하여 테스트도 가능(문제점 미리 확인용)
    • 오케스트레이션 도구(ex. 쿠버네티스)가 이러한 일을 해주는 도구

도커의 핵심 키워드 정리

  • 컨테이너
    • 애플리케이션이 의존성, 네트워크 환경, 파일 시스템에 구애받지 않고, 도커라는 기술 위에 실행될 수 있도록 만든 애플리케이션 상자(박스)
  • 이미지: 애플리케이션 및 애플리케이션 구성을 함께 담아 둔 템플릿
    • 실행되는 모든 컨테이너는 이미지로부터 생성됨
    • 이미지로 여러 개의 컨테이너 생성 가능
    • 기본 이미지로부터 변경 사항을 추가/커밋해서 또다른 이미지 생성 가능
  • 레지스트리
    • 이미지는 레지스트리에 저장됨
    • 대표적인 이미지 레지스트리: Docker Hub, Amazon ECR
    • 도커 CLI에서 이미지를 통해 컨테이너 생성 시, 호스트 컴퓨터에 이미지가 존재하지 않으면 기본 레지스트리로부터 다운로드받음

Docker CLI

Docker 이미지와 컨테이너를 다루기 위한 Docker CLI 명령어들

주의: ubuntu에서 Docker CLI 명령어를 사용시에는 관리자 권한(sudo)로 실행해야 한다
그렇지 않으면 permission denied 뜬다

Docker 공식 문서에서 사용법 및 명령어, 옵션 등을 확인할 수 있음
https://docs.docker.com/engine/reference/commandline/container_run/

 

docker container run

docker container run: Run a command in a new container

docs.docker.com

  • 사용법: Docker CLI, Docker-Compose, API Reference 참고
  • 환경 및 빌드 파일 구성: DockerFile, Docker-Compose File 참고

Docker CLI 연습 1: 기초 명령어

연습1: docker/whalesay

docker/whalesay 이미지로 연습해 보자

docker/whalesay는 레지스트리 계정, 레포지토리 이름, 태그 총 3가지 정보로 구성되어 있다

  • docker/whalesay 의 구성 형태
Registry_Account/Repository_Name:Tag
  • 레지스트리
    • Docker Hub: https://hub.docker.com/
    • 도커 이미지를 관리하는 공간
    • 지정하지 않으면 기본값(도커 허브)
  • 레포지토리
    • 레지스트리 내에 도커 이미지가 저장되는 공간
    • 이미지 이름이 사용되기도 함
    • GitHub의 레포지토리와 유사한 느낌?
  • 태그
    • 같은 이미지라도 버전 별로 안의 내용이 다른 경우 존재하니깐
    • 해당 이미지를 설명하는 버전 정보를 주로 입력
    • 지정하지 않으면 기본값으로 lastest 태그를 붙인 이미지를 가져옴

Docker Hub에서 Docker Image를 찾거나, Docker Image의 사용방법을 확인할 수 있다

사용법 등을 확인했다면, CLI에서 진행

## 우분투면 앞에 sudo 붙이기! 
docker image pull docker/whalesay:lastest    
## {image} pull        레지스트리에서 이미지 혹은 레포지토리를 가져옴

docker image ls        ## 이미지 리스트를 출력

## 받아온 이미지를 실행(이미지 → 컨테이너)
## docker container run [OPTIONS] IMAGE [COMMAND] [ARG…]

docker container run --name 컨테이너_이름 docker/whalesay:latest cowsay boo
## 컨테이너_이름을 이름으로 갖는 컨테이너 실행
## [command]는 초기 컨테이너 실행시 수행되는 명령어
## [arg…]는 command에 넘겨질 파라미터

docker container ps -a        ## 모든 컨테이너의 리스트를 출력
## {container} ps: 컨테이너의 리스트 출력
## -a 옵션: default로는 실행되는 컨테이너지만 종료된 컨테이너를 포함한 모든 컨테이너 출력

docker container rm 컨테이너이름
## {container} rm : 컨테이너를 삭제. 컨테이너이름은 
## ps 명령을 통한 NAMES나 CONTAINER ID를 사용

## 마찬가지로 이미지도 지울 수 있다
docker image rm 이미지이름

 

이 같은 과정은 한번에도 연속되게 실행 가능하다

docker container run --name 컨테이너이름 --rm docker/whalesay cowsay boo

## 하나의 이미지를 받아와 컨테이너로 실행하고, 컨테이너와 관련된 리소스를 삭제하는 작업
## {container} run: 컨테이너를 실행. 이미지가 없다면 이미지를 받아온 뒤(pull) 실행함
## --rm 옵션: 컨테이너를 `일회성`으로 실행. 컨테이너가 중지되거나 종료될 때, 관련 리소스를 모두 제거

docker image rm docker/whalesay    
## 자동 다운로드(pull)되었으므로 다시 삭제
  • container는 ctrl+c로 종료 가능
  • 도커의 경우 같은 기능을 수행하는 명령어가 다양하게 존재한다
## rmi 또한 rm과 같은 기능을 수행한다
docker rmi [OPTIONS] IMAGE [IMAGE...]

연습2: danielkraic/asciiquarium

docker run -it --rm danielkraic/asciiquarium:latest

## -it 옵션: -i, -t 를 동시에 사용한 옵션. 사용자와 컨테이너 간 인터렉션이 필요한 경우 사용
## 위에서는 출력되는 화면을 사용자가 지속적으로 보기 위해 사용함
## 가령, Python 명령이 필요하거나 추가로 다른 입력을 받는 경우 이 옵션을 지정하여 사용

Docker CLI 연습 2: copy, dockerfile

다른 사람이 제공한 도커 이미지를 받아 사용하는 경우, 원하는 모든 기능이 구성되어 있지 않을 수 있다
→ 도커 이미지에 파일을 추가하여 도커 이미지를 새로 만드는 방법
→ 로컬에 저장된 파일과 함께 도커 이미지를 이용(로컬 파일과 도커 이미지를 연결)

 

이 방법은 크게 2가지 방식이 있다

  • CP(copy)를 이용하는 방법
    • 호스트와 컨테이너 사이에 파일을 복사
  • Docker Volume을 이용하는 방법
    • 호스트와 컨테이너 사이의 공간을 마운트(mount)
  • 마운트?
    • 저장 공간을 다른 장치에서 접근할 수 있도록 경로를 허용해서, 마치 하나의 저장 공간을 이용하는 것처럼 보이게 하는 작업

Docker 컨테이너에 파일을 복사(cp)

게임 서버웹 서버 같은 경우 사용할 도구가 도커 이미지에 모두 구성되어 있지 않은 경우가 있음

  • 웹서버는 도커 컨테이너로 실행
  • 웹 서버를 구성하는 파일은 직접 만들거나 가져온 파일로 구성
  • 장점
    • 서버에 문제가 생기는 것을 호스트와 별개로 파악 가능
    • 문제가 생긴 서버를 끄고, 공장 초기화하듯 도커 이미지로 서버 재구동하면 됨(긴급처방하기 좋음)

해당 레포의 게임 서버 파일(팩맨 게임 서버)을 이용해서 팩맨 게임 서버를 실행해보자
https://github.com/platzhersh/pacman-canvas

 

GitHub - platzhersh/pacman-canvas: An old classic, re-written in HTML5.

An old classic, re-written in HTML5. Contribute to platzhersh/pacman-canvas development by creating an account on GitHub.

github.com

httpd 웹 서버

먼저 서버를 구동하기 위해 httpd 도커 이미지를 통해 httpd 웹 서버를 구현
→ httpd(http daemon)은 apache http server를 실행하기 위한 오픈 소스 웹 서버 sw

기본적으로 httpd 는 /usr/local/apache2/htdocs/ 경로에 웹 서버와 관련된 파일들이 저장되어 있다면, 해당 파일을 기반으로 웹 서버가 실행되도록 함

## 레포 클론
 git clone git@github.com:codestates/pacman-canvas.git
## git clone git@github.com:platzhersh/pacman-canvas.git

##docker container run
docker container run --name 컨테이너이름 -p 818:80 httpd

## -p 옵션: 로컬 호스트의 포트와 컨테이너의 포트를 연결
## -p [로컬호스트의 포트]:[컨테이너의 포트]
## httpd는 일정 시간 연결 기록이 없으면 서버 가동이 중지됨
## 명령어가 실행되면 터미널을 끄지 말고(켜둔 상태)로 다른 터미널에서 다음 작업을 진행하자

## 컨테이너를 백그라운드에서 실행하려면 -d 옵션을 사용

httpd가 실행되었다면, 웹 브라우저에서 해당 포트로 접속해보자(localhost:818 또는 127.0.0.1:818)
→ 정상적으로 서버가 열려 있으면, 새로운 터미널에서 작업 진행

## docker container cp 명령어를 통해 로컬 호스트의 파일을 컨테이너에 전달

docker container cp ./ 컨테이너이름:/usr/local/apache2/htdocs/
## 경로를 입력할 때, 상대 경로와 절대 경로를 주의해서 작업할 것!
## docker container cp 명령은 앞 경로의 파일(`./`)을 뒤 경로에 복사하는 명령어
## `./` 으로 한 것은 현재 위치가 pacmac-canvas 디렉토리 안이기 때문임!

다시 웹 브라우저의 해당 경로(httpd)로 접속하면 게임이 구동됨을 알 수 있다

docker exec -it 컨테이너이름 bash 명령어를 통해 컨테이너 내부 터미널로 접속할 수 있다
→ 컨테이너가 뭔가 제대로 안된다거나 컨테이너 내부에 접근해서 뭔가 작업할 때 사용

Docker 이미지 만들기

앞서 만들어본 Docker Container를 이미지 파일로 변환하는 작업

  • 이미지로 만들 때의 장점
    • 이전에 작업했던 내용을 다시 수행 안해도 된다(반복작업 안해도됨)
      배포 및 관리가 유용

이미지를 만드는 방법에는 크게 2가지가 있다

구동한 Docker Container를 이미지로 만드는 방법

 

docker container commit

docker container commit: Create a new image from a container's changes

docs.docker.com

docker container commit 컨테이너이름 my_pacman:1.0
## 구동한 컨테이너를 my_pacman 이름의 이미지(레포)로 만듬(1.0은 태그)

## 잘되는지는 생성한 이미지를 구동해보면 된다
docker run --name 이름 -p 900:80 my_pacman:1.0

Docker Image 빌드를 위한 파일인 Dockerfile 로 만드는 방법

해당 공식문서를 참고해서 해보자
https://docs.docker.com/engine/reference/builder/

 

Dockerfile reference

 

docs.docker.com

dockerfile을 만들고 dockerfile 대로 이미지를 build 하는 방법

  • dockerfile이란?
    이미지 파일의 설명서 같은거
  • docker build는 Dockerfile로 도커 이미지 파일을 생성하는 명령어
 ## --tag 는 name:tag 형식으로 이미지를 생성할 수 있다
 ## 지정한 경로에 있는 Dockerfile을 찾아서 빌드

 docker build --tag my_pacman:2.0 . 
##끝에 꼭 "."을 명령어에 꼭 포함해야 한다!
  • 생성된 이미지를 이용해 901 포트(원하는 포트)에 웹 서버 구동
docker run --name 원하는이름 -p 901:80 my_pacman:2.0

두개의 docker 이미지를 다루기

docker-compose CLI

두 개 이상의 도커 컨테이너를 연결하는 명령어이다

 

docker compose 공식 문서를 참고하자
https://docs.docker.com/compose/reference/

 

Overview of docker-compose CLI

 

docs.docker.com

docker-compose up ## -d 옵션을 사용하면, 컨테이너를 백그라운드로 실행 가능
## docker-compose.yaml 에 정의된 이미지를 컨테이너로 실행

docker-compose down
## docker-compose.yaml(yml)(에 정의된 이미지를 이용해 실행된) 컨테이너를 종료

docker-compose up {특정이미지}
## 특정 이미지만 컨테이너로 실행

먼저, 위의 명령어들을 사용하려면 docker-compose.yml(또는 .yaml 확장자명) 파일이 존재해야 한다

  • docker-compose.yml
    • 하나의 docker-compose.yml에서 관리되는 컨테이너끼리는 동일한 docker network에서 구동된다
    • docker run 명령과 다르게 docker-compose.yml 파일 안에서 기본 network가 사용됨
## 원하는 경로(경로 무관)에서 `docker-compose.yml` 파일을 생성(껍데기)
touch docker-compose.yml    ## 껍데기 파일 생성
nano  docker-compose.yml    ## 코드 작업 진행

## 해당 파일이 있는 위치에서
docker-compose up -d        ## 해당 yml 파일을 실행(백그라운드 실행 옵션)

## 이제 브라우저에서 해당 포트로 실행된 화면을 확인하면 된다

 

아래는 테스트용 예제 소스 코드(복사해서 사용)

version: '3.8'

services:
  nginx:
    image: sebcontents/client
    restart: 'always'
    ports:
      - "8080:80"
    container_name: client

  node:
    image: sebcontents/server
    restart: 'always'
    ports:
      - "5000:3000"
    container_name: server

Volume 과 환경 변수 설정

도커 컨테이너와 데이터 볼륨이라는 개념은 아래 레퍼런스를 참고
https://www.joinc.co.kr/w/man/12/docker/Guide/DataWithContainer

 

도커 컨테이너 데이터 볼륨 관리

백업, 복구, 마이그레이션

www.joinc.co.kr

volume을 사용하고, 환경 변수 설정하는 작업을 해보자

 

테스트용 예제 소스 코드는 아래와 같다

//M1을 제외한 다른 노트북 모델용 yaml(yml) 파일

version: '3.8'

services:
  nginx:
    image: sebcontents/client
    restart: 'always'
    ports:
      - "8080:80"    ## 호스트포트 : 컨테이너포트
    container_name: client

  node:
    image: sebcontents/server
    restart: 'always'
    ports:
      - "5000:3000"
    container_name: server
    volumes:        // volume 을 사용한다는 것
      - "./volumefolder:/data"    
## 현재 경로의 `/volumefolder` 에 넣는 파일들을 해당 컨테이너의 `/data` 폴더에도 
## 넣는다는 뜻(업로드 개념 비슷?) 

  mysql:
    image: mysql:latest
    restart: 'always'
    ports:
      - "3307:3306"
    container_name: database
    environment:
      MYSQL_ROOT_PASSWORD: root_계정_비밀번호    // 사용할 루트 비번
      MYSQL_DATABASE: 초기_생성_데이터베이스    // 생성할 초기 db 이름
      MYSQL_USER: 유저_이름    // 생성할 유저이름
      MYSQL_PASSWORD: 유저_패스워드    // 생성할 유저 비번

마찬가지로 원하는 경로에서 docker-compose.yml 파일을 생성하고 위의 소스 코드를 붙여넣기하자
→ 해당 경로에서 (sudo) docker-compose up -d 로 실행하면, yml 파일이 위치한 곳에 volumefolder 라는 디렉토리가 생성됨을 확인 가능

 

해당 디렉토리 안에서 임의의 파일 하나를 만들어 보자
docker exec -it server bash 명령어를 통해 server 컨테이너 터미널에 접속하여 data 디렉토리로 이동(cd /data)하면 아까 생성한 임의의 파일이 여기에도 존재함을 알 수 있다!

 


보충 내용

컨테이너 기술과 가상 머신(vm)의 비교

        컨테이너            vm
==========================================
CPU 성능    공유                다 격리
메모리        공유
네트워크    격리
리눅스커널    공유
파일시스템    격리
프로세스    격리

docker의 장점

  • 특히 개발팀 단위로 협업할 때 유용
    • 각 개발 환경이 다를 수 있는데, 개발 환경 일치 및 일치 작업에 유리!
  • 배포 환경도 일치화할 수 있음

docker 이미지 만들기(유용)

dockerfile을 이용해서 만들 수 있다
→ 백엔드에서 주로 사용함

 

아래 node.js 공식문서를 따라해서 일단 껍데기를 만들어보자
https://nodejs.org/ko/docs/guides/nodejs-docker-webapp/

 

Node.js 웹 앱의 도커라이징 | Node.js

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

nodejs.org

## 어떤 특정 파일들과 코드 작업을 했다고 가정
## back → 기본 앱 서버
## front → fetch API로 설정했다고 가정
## front는 웹 서버 위에서 띄워야 한다

## Dockerfile 을 생성
## 그 후 파일 내용에 코드 작성
##  파일 내용의 소스 코드 예시는 아래와 같다

FROM httpd:2.4 # 베이스 이미지를 httpd:2.4 로 사용합니다.
COPY ./ /usr/local/apache2/htdocs/ 
## 호스트의 현재 경로에 있는 파일을 생성할 이미지 /usr/local/apache2/htdocs/ 에 복사

## 이 작업이 되면 back(서버쪽)에서도 Dockerfile을 생성해서
## 거기서 서버에 맞는 소스 코드를 작성해야 함
## 소스 코드 작업은 알아서…

## 둘다 소스 코드 작업이 끝났으면 `docker-compose.yml` 파일을 생성해서 그 안에서 작동 코드를 작성

## 작성 코드 예시

version: “3.0”

services:
    front:
        build: front
        ports:
            - 80:80
        depends_on:
            - back

    back:
        build: back
        ports:
            -3333:80

## 네트워크가 격리되어있으므로 둘다 80으로 띄워도 문제없다는 점!

## 소스 코드 작업이 끝나면, 
docker-compose up    ## 하고 웹 브라우저에서 구동됨을 확인
복사했습니다!