Docker의 개념 및 Docker 이미지, 컨테이너 🐳

waterlyn·2023년 4월 8일
0
post-thumbnail

Docker는 무엇일까?

  • 애플리케이션을 어디서든 쉽고 빠르게 구축하고, 테스트 및 배포할 수 있는 소프트웨어 플랫폼이다.
  • 도커에서는 컨테이너라는 단위로 소프트웨어를 구상한다.
    • 컨테이너는 라이브러리, 시스템 도구, 코드 등 소프트웨어 실행에 필요한 모든 것이 포함되어 있다.
    • 따라서 도커를 사용하면 컨테이너 환경에서 독립적으로 애플리케이션을 실행할 수 있는 환경을 만들 수 있게 된다.
    • 독립된 환경에서 독립된 실행이 보장되기 때문에 일관된 결과를 보장할 수 있다.

컨테이너

  • 격리된 공간에서 프로세스가 동작하는 기술.
  • 가상 머신을 사용하는 방식이 아니라 (OS 자체를 가상화 하는 방식이 아니다.) 프로세스를 격리하는 방식이다.

도커는 리눅스 컨테이너를 활용한 기술

  • 도커는 리눅스의 프로세스를 격리하는 기술을 사용했는데, 이를 통해 컴퓨터에 독립적인 컴퓨팅 공간을 만들 수 있다.
  • 이것이 기존의 가상 환경 구성과 다른 점은?
    • 가상 머신은 환경(OS) 자체를 가상화해버리기 때문에 굉장히 무거워질 수 있다.

    • 하지만 도커 컨테이너는 호스트 OS의 커널을 공유하여 단순히 하나의 격리된 프로세스로 동작할 수 있게 된다.

    • 가상 머신을 사용하는 것보다 속도가 훨씬 빠르고 (당연히 가볍겠죠?) CPU와 메모리 사용량이 현저히 낮아지게 된다.

      host os 위에 가상 os가 하나씩 올라가게 되기 때문에 굉장히 무거워보인다!
      🤔 host os 위에 가상 os가 하나씩 올라가게 되기 때문에 굉장히 무거워보인다!

    • 컨테이너라는 가상의 격리 환경을 만들기 위해 리눅스의 namespace와 cgroup이라는 기능을 사용한다.

어떻게 프로세스가 독립되지?

도커는 리눅스의 프로세스 격리 기술을 사용한다.

리눅스는 가상 환경을 구성하는 것이 아니라 호스트 OS의 커널을 공유하여 프로세스 자체를 격리시킬 수 있다.

리눅스가 사용하는 기술은 다음과 같다.

  • namespace
  • cgroup

namespace

namespace는 하나의 아파트에 독립된 호실이 여러 개 존재하여 모두 독립된 공간에서 살 수 있도록 하는 기능과 동일한 개념이라고 볼 수 있다.

하나의 OS에서 제공하는 커널을 공유하여 물리적으로 하드웨어를 분리하는 것이 아닌 프로세스가 격리되도록 하는 기술이다. 즉, 리눅스의 하드웨어 리소스를 가상화하는 것이 아닌, 리눅스 내의 자원을 가상화한다.

프로세스는 이를 통해 시스템 등에 대해 독립될 수 있게 된다. (user, 파일, 네트워크, 호스트명 등에 대해 독립된다.)

cgroup

cgroup은 control groups의 약자로 프로세스들이 사용할 수 있는 컴퓨팅 자원들을 제한하고 격리 시킬 수 있는 리눅스 커널의 막강한 기능이다.

cgroup으로 Memory, CPU, Network, Device, I/O 등을 제한 할 수 있다.

위의 두 가지 기능을 통해 구현된 리눅스 프로세스 격리 기술로 도커 컨테이너가 구현되어 있는 것을 알고 넘어가면 좋을 것 같다.

도커를 사용하면 이점이 뭘까?

프로세스를 독립적으로 이용하게 되면서 얻을 수 있는 이점은 다음과 같다.

  • 서버에 여러 컨테이너를 실행시킬 수 있으면서 독립적으로 실행되기 때문에 가상 머신을 사용하는 것과 동일한 효과를 얻을 수 있다.
  • 실행 중인 컨테이너에 접속이 가능하기 때문에 독립된 공간에서 필요한 일을 수행할 수 있게 된다.
  • 리눅스에서와 동일하게 패키지 매니저(apt-get, yum)를 통해 다양한 환경을 구성할 수 있고, 다양한 프로세스를 백그라운드로 실행이 가능하다.
  • 새로운 환경 (컨테이너)를 만드는데에 시간과 비용이 크지 않다.

컨테이너는 어떻게 만들까?

도커에서는 컨테이너를 만들기 위해 이미지라는 기술을 사용한다.

이미지는 컨테이너 실행에 필요한 파일과 설정을 포함하고 있다. 상태값을 가지지 않으며 변하지 않는 것이 특징이다. 같은 이미지로 여러 개의 컨테이너를 만들 수 있고, 컨테이너의 상태가 바뀌거나 삭제되어도 이미지에는 전혀 영향이 없기 때문에 이미지는 변하지 않고 계속 남아있다.

도커에서는 Docker hub 라는 클라우드 서비스를 제공하는데, 여기에 이미지를 올려 어디서든 가져와 사용할 수 있으며, Docker Registry 라는 저장소를 직접 만들어 관리도 가능하다.

도커의 이미지의 큰 특징들은 다음과 같다.

  • 도커 이미지는 가상 머신에 비해 매우 가볍게 활용이 가능하다.
  • 상태값을 가지지 않고 변하지 않는다.
  • 하나의 이미지를 통해 여러 컨테이너 실행이 가능하다. 즉, 하나의 이미지로 여러 실행 환경을 구성할 수 있게 되는 것이다.
  • 컨테이너를 삭제하더라도 이미지는 유지된다.
  • 도커는 Dockerfile이라는 파일로 이미지를 만든다.

Dockerfile ?! 이건 대체 뭐야 ?

도커는 이미지를 만들기 위해 Dockerfile을 사용하는데, 이는 DSL(Domain Specific Language) 언어를 사용하여 이미지를 생성하도록 한다.

Dockerfile은 단순히 텍스트 파일이며, 일반적으로 프로젝트 소스파일 내에서 같이 관리된다.

Docker의 공식문서에서 Dockerfile 작성에 대한 문서가 존재하니 참고하면 좋을 것 같다.

Dockerfile에서 가장 중요한 키워드를 꼽자면 FROMRUN 이다. 이 두 가지로 이미지를 만들 수 있다.

  • FROM
    FROM <image>:<tag>
    
    ex) 
    FROM ubuntu:16.04
    베이스 이미지를 지정하는 키워드이다. 이미지 생성을 위해서는 베이스 이미지가 반드시 필요하다.

    The FROM instruction initializes a new build stage and sets the Base Image for subsequent instructions. As such, a valid Dockerfile must start with a FROM instruction.

    도커 공식문서에서 가져온 것으로, FROM 키워드는 새로운 빌드 환경을 선언하는 것이며, Dockerfile의 최상단에 반드시 위치해야 한다고 말하고 있다. tag는 버전을 지정하는 것으로 가능하면 구체적인 버전을 지정하는 것이 좋다.
  • RUN
    RUN <command>
    
    ex)
    RUN bundle install
    가장 많이 사용되는 키워드 중 하나로 말 그대로 명령어를 실행하는 것이다. 이 키워드는 내부적으로 /bin/sh -c를 붙여서 실행한다.
    RUN ["executable", "param1", "param2"]
    이 키워드는 위와 같은 형식으로도 작성이 가능한데, ,가 공백으로 변경되어 → executable param1 param2로 실행되는 것이다.

우리 프로젝트에서의 Dockerfile은?

FROM openjdk:17
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
  • FROM: openJDK의 17 버전을 사용하여 빌드 환경을 구성한다.
  • ARG: JAR_FILE이라는 변수에 JAR 파일 경로를 넣어둔다. (단순 변수 설정)
  • COPY: 지정한 경로의 JAR 파일을 app.jar라는 이름으로 복사한다.
  • ENTRYPOINT: 도커 컨테이너가 생성될 때 실행하는 커맨드로 지정한다. 현재는 jar 파일을 실행하는 것으로 지정해둔 것으로 앱 어플리케이션을 도커 컨테이너로 띄운다는 것을 의미한다.

도커 설치하기

리눅스 환경에서 도커를 사용하기 위해서는 도커 설치가 필요하다.

curl -fsSL https://get.docker.com/ | sudo sh

도커 명렁어 사용에 대한 설명은 이 블로그를 추천한다!

Docker-compose

위의 과정을 천천히 읽다보면 이런 생각이 들 수 있다.

💭 그러면 도커로 컨테이너를 띄워 앱을 실행시키려면 명령어로 직접 일일히 실행해야 하나..? 🤔

간단한 작업만 한다면 물론 커맨드 라인으로 명령어를 작성하여 컨테이너를 띄우는 것이 어렵지 않게 느껴질 것이다. 하지만 점차 여러가지 설정이 많아지고 컨테이너의 조합이 많아진다면 명령어를 한 번 작성하는 것도 쉽지 않은 일이 될 것이다.

도커에서는 이런 설정을 편히 할 수 있는 기술로 Docker-compose라는 기능을 제공한다.

YAML 방식의 설정 파일을 사용하여 복잡한 설정을 쉽게 할 수 있도록 하는 기술이다.

이것 또한 공식 문서에서 작성에 대한 레퍼런스가 나와있으니 작성하게 될 때 참고하면 좋을 것이다.

docker compose를 사용하려면 컨테이너를 띄우는 환경에 도커를 설치한 것처럼 docker compose도 설치해줘야 한다.

curl -L "https://github.com/docker/compose/releases/download/1.9.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

주의

docker compose 파일을 최신 버전으로 작성한다면 설정 파일을 적용할 때 버전 관련 문제로 키워드를 찾을 수 없다는 오류를 만날 수 있다.

그런 경우에는 설치되어 있는 docker compose의 버전을 업그레이드 해서 키워드를 포함하는 버전으로 받아주면 잘 실행되는 것을 확인할 수 있다. ☺️

우리 프로젝트의 docker-compose 파일은??

version: '3.7'

services:
  spring:
    container_name: java-and-dining
    image: ${DOCKER_HUB_USERNAME}/java-and-dining
    restart: on-failure
    expose:
      - 8080
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://spring-db:3306/dining
      SRPING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: ${ROOT_PW}
    ports:
      - 8080:8080
    depends_on:
      - db
    logging:
      driver: awslogs
      options:
        awslogs-group: "java-and-dining"
        awslogs-region: "ap-northeast-2"
        awslogs-stream: "app-log"

  db:
    container_name: spring-db
    image: mysql
    restart: on-failure
    environment:
      MYSQL_DATABASE: dining
      MYSQL_ROOT_PASSWORD: ${ROOT_PW}
      TZ: Asia/Seoul
    ports:
      - 3306:3306
    volumes:
      - mysql-data:/var/lib/mysql
    command:
      - --character-set-server=utf8mb4
      - --collation-server=utf8mb4_unicode_ci
    logging:
      driver: awslogs
      options:
        awslogs-group: "java-and-dining"
        awslogs-region: "ap-northeast-2"
        awslogs-stream: "db-log"

volumes:
  mysql-data:

조금 복잡할 수 있는데 찬찬히 보면 어렵지 않다. 모두 도커 컨테이너에 대한 설정이라고 생각하고 하나씩 살펴보자.

  • version: docker compose의 버전을 명시한다.
  • services: 우리가 띄우고자 하는 컨테이너 설정을 본격적으로 하는 곳이다.
  • spring: 큰 하나의 컨테이너 설정의 인덴트이며 이름은 자유롭게 정해도 좋다. → 하나의 서비스를 정의하는 것으로 생각하면 좋을 것 같다.
  • container_name: 우리가 띄우고자 하는 컨테이너의 이름을 직접 지정할 수 있다.
  • image: 컨테이너를 띄우기 위해 사용하는 이미지를 명시해야 한다. → 왜 이미지를 명시하는지 이제 아시겠죠?!
  • expose: 도커 컨테이너로 접근하는 포트를 지정하는 설정이다.
    • 8080으로 설정해두면 이 컨테이너는 8080 포트로만 접근이 가능하도록 설정되는 것이다.
  • ports: 이것도 마찬가지로 포트를 지정하는 설정인데, expose와는 다른 성격이다.
    • {외부포트}:{내부포트} 의 형태이며, 외부 포트로 설정된 포트로부터 들어오는 요청을 내부 포트로 설정된 포트로 매핑한다는 의미이다.
    • 우리는 8080:8080 으로 설정했으므로 외부에서 8080 포트로 접근하는 것을 내부의 8080 포트로 연결해준다는 의미가 되는 것이다.
  • depends_on: spring 서비스와 db 서비스를 연결한다는 종속성을 부여하는 것이다. 만약 종속으로 연결된 컨테이너가 준비되지 않을 경우 애플리케이션은 시작되지 않게 된다.
    • 그래서 restart와 같은 옵션으로 컨테이너가 모두 잘 준비 될 때 까지 계속해서 컨테이너를 복원하는 등의 조치가 필요하다.
  • volumes: 도커에서 데이터베이스 역할을 하는 것으로 생각하면 된다. 이미지는 상태를 갖지 않기 때문에 컨테이너가 삭제된 후에 동일한 이미지로 다시 생성되어도 데이터가 남지 않고 모두 사라지게 된다.
    • 도커는 이를 해결하기 위한 기술로 volume이라는 것을 제공한다.
    • 맨 마지막 문단에서 mysql-data라고 선언해 두면 해당하는 이름의 볼륨을 생성하라는 의미이다.
    • 컨테이너에서 볼륨을 설정해두면 해당하는 볼륨에 데이터가 저장되게 되고 컨테이너가 삭제되고 다시 실행되어도 데이터가 날아가지 않고 잘 남아있는 것을 확인할 수 있다.
  • logging: 컨테이너 로깅에 대한 설정이다. 현재 우리는 aws의 cloudwatch 설정을 넣어 연결해주고 있다.

이렇게 docker compose 파일을 가지고 있다면 다음과 같은 명령어로 한번에 컨테이너를 실행할 수 있다.

(sudo) docker-compose up -d
  • -d: detached mode로, 쉽게 말해 백그라운드로 실행되도록 하는 옵션이다.

참고

cgroups

Linux namespace

docker 이해하기

docker 프로세스 격리

Dockerfile reference

초보를 위한 도커 안내서 - 설치하고 컨테이너 실행하기

👉🏻 이해가 잘 안된다면 이 사이트의 1편부터 읽어보면 좋을 듯 합니다 !!

profile
Hello there 🖤

1개의 댓글

comment-user-thumbnail
2023년 4월 20일

꼼꼼히 정리하셨네요... 담에 요걸로 적용해보겠습니닷!🥳

답글 달기