[Docker] 도커 등장~컨테이너 이미지 만들기

Park Yeongseo·2023년 9월 15일
1

Docker

목록 보기
1/2
post-thumbnail

[1] 도커 등장

1. 서버 운영의 발전사

자체 서버 운영 → 설정 관리 도구 → 가상머신 → 클라우드 → PaaS → 도커 → 쿠버네티스 → 서비스 메시

1. 자체 서버 운영

  • 서버 주문부터 시작해서 서버 설정을 위해 많은 노력과 시간이 필요
  • 성능이 좋은 걸 미리 구매하고 효율적인 사용을 위해 여러 앱들을 설치할 필요가 있음

    해결 노력 - 문서화)
    이 각 서버들의 상태, 설정 등을 문서화해서 관리.
    ❓ 과연 문서가 정확한지에 대한 보장이 없음. ex) OS, 버전 차이 관리 등

2. 설정 관리 도구

  • Chef, Ansible, …
  • 코드와 버전 관리를 위한 설정 관리 도구를 이용, 코드 및 버전 관리에 있어서는 도움이 됨.
  • 하지만 공부해야 할 양이 많고, 한 서버에 다른 버전을 여럿 설치하는 것은 불가

3. 가상 머신

  • 한 서버에 여럿 설치할 수도 있고 현재 상태를 저장하는 것도 가능.
  • 하지만 너무 크고 느림

4. 클라우드 - IaaS

  • 하드웨어 파편화 문제 해결.
  • 가상화된 환경만으로 아키텍처 구성 가능.
  • 이미지 기반 서버 상태 관리.

5. PaaS

  • 소스만 저장해주면 알아서 다 배포해줌.
  • 일반화된 프로비저닝 방법을 제공, 서버 운영에 도움.
  • app을 PaaS 방식에 맞게 작성해야 함.
  • 서버에 대한 원격 접속 X, 파일 시스템 X, 서버 패키지 X, 제한적 로그 수집, …
    => 기본적 배포는 쉽지만 새로운 기능이 필요한 경우에는 사용하기 힘듦

2. 도커와 쿠버네티스의 등장

컨테이너

운영 체제 안에서 커널을 공유하며 개별적인 실행 환경을 제공하는 격리된 공간.

개별적인 실행 환경
CPU, 네트워크, 메모리와 같은 시스템 자원을 독자적으로 사용하도록 할당된 환경

컨테이너 내부에서 실행되는 앱들은 서로 영향을 미치지 않고 독립적으로 작동한다. 사실 유닉스, 리눅스는 이렇게 한 호스트 운영 체제 안에서 자원을 분리 할당하고 실행되는 프로세스를 격리해서 관리하는 방법을 이미 제공해왔지만, 그 관리 방법이 너무 복잡했다.

도커

이걸 쉽게 만들어 주는 도구로 등장한 것들 중 하나가 도커.

컨테이너 관리 도구들
Containerd, CRIO-O, Kata Containers, Docker, …

쉽게 말해 도커는 컨테이너 사용법을 명령어로 정리한 것으로, 사용자가 따로 신경 쓰지 않아도 컨테이너 생성시 개별적인 실행 환경을 분리하고 자원을 할당해주는 도구이다. 클라우드 이미지보다 관리하기 쉽고, 성능 저하도 거의 없고, 복잡한 기술을 몰라도 쓸 수 있고, 이미지 빌드 기록도 남고, 재현 및 수정도 가능하고, 오픈 소스라 특정 회사 기술에 종속적이지도 않다.

도커는 명령어를 입력하는 CLI와 명령어를 받아들이는 도커 데몬으로 구성되어 있다. 네트워크를 통한 호출로 작동해, 구조적으로는 복잡한 편이지만 이를 모두 도커에서 관리하므로 사용자 입장에서는 신경 쓰지 않아도 된다.


[2] 도커로 컨테이너 다루기

1. 도커 설치

https://docs.docker.com/engine/install/ubuntu/ 에 잘 설명돼있다

2. 컨테이너와 이미지

도커에서 컨테이너는 이미지의 인스턴스다.

이미지 검색하고 내려받기

이미지는 레지스트리에 모여있다. 별도 레지스트리를 지정하지 않으면 기본적으로는 도커 허브에서 이미지를 찾는다.

docker search <검색어> 명령을 통해 특정 검색어를 포함하는 이미지가 있는지 찾을 수 있고, 이 이미지는 고유 목적에 맞게 패키지 되어 있다.

INDEXNAMEDESCRIPTIONSTARSOFFICIALAUTOMATED
이미지가 저장된 레지스트리의 이름검색된 이미지 이름.
공식 이미지를 제외한 나머지는 ‘레지스트리 주소/저장소 소유자/이미지 명’의 형태이미지에 대한 설명좋아요 수개발 업체에서 제공하는 공식 이미지이면 [OK]도커 허브에서 자체 제공하는 이미지 빌드 자동화 기능을 활용해 생성한 이미지면 [OK]

검색한 레지스트리는 docker pull 을 통해 내려받을 수 있다.

  • 태그 : default는 latest . 이름이 동일한 이미지에 추가하는 식별자. 이름이 동일해도 도커 이미지의 버전이나 플랫폼 등이 다를 수 있으므로, 이를 구분하는 데 사용됨
  • 레이어 : 한 이미지는 여러 개의 레이어로 이루어져 있다. 이 각 레이어가 잘 내려받아지면 레이어마다 Pull complete 메시지가 발생함
  • 다이제스트 : 이미지 고유 식별자. 이미지에 포함된 내용 및 이미지 생성환경 식별이 가능. 이름과 태그는 이미지를 생성할 떄 임의로 지정하기 때문에 이름이나 태그가 같다고 해서 같은 이미지임을 보장할 수는 없지만, 다이제스트는 고유한 값이기 때문에 다이제스트가 같은 이미지는 이름이나 태그가 달라도 같은 이미지다.
  • ****상태**** : 이미지를 내려받은 레지스트리, 이미지, 태그 등의 상태 정보를 확인할 수 있음. ‘레지스트리 이름/이미지 이름:태그’의 형식.

이미지의 레이어 구조

이미지는 여러 app과 파일들을 담고 있다는 점에서 압축 파일과도 비슷하다. 내부에 동일한 파일이 포함된 이미지의 경우, 여러 이미지에 동일한 레이어를 공유해 전체 용량이 감소한다.

레이어를 재사용하기 때문에 여러 이미지를 내려받아도 디스크 용량을 효율적으로 사용할 수 있다.

3. 컨테이너 실행하기

  1. docker run 으로 컨테이너를 생성하면 컨테이너를 식별할 수 있는 고유 ID를 담은 16진수의 문자열이 나온다.
  2. docker ps 로 생성한 컨테이너들의 상태를 확인할 수 있다.
CONTAINER IDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
컨테이너 ID사용한 이미지컨테이너 생성시 내부에서 작동할 프로그램을 실행하는 명령어컨테이너 생성 시각컨테이너 작동 시작 시간.
중지했다가 다시 시작할 경우 초기화됨컨테이너가 사용하는 포트와 프로토콜컨테이너 이름을 표시.

docker run --name <이름> 옵션으로 직접 지정할 수 있음.
미지정 시에는 컨테이나 시작 시 도커가 임의로 부여한 값이 나타남 |

docker ps -f(--filter) <필터링 대상> : <필터링 대상>key=value 형식으로 입력하면, 해당 value에 해당하는 문자열을 포함하는 경우를 필터링할 수 있다.

  • 필터링 키에는 id, name, label, exited, status, ancestor, …등이 있다.

컨테이너는 **변경 불가능한 인프라(immutable infrastructure)**를 지향한다. 초기에 인프라를 굿헝하면 임의로 설정을 변경할 수 없고, 따라서 적용된 서정을 변경하려면 새로운 컨테이너를 생성해야 한다. 이 덕에 컨테이너로 배포된 인프라는 배포된 상태를 유지한다.

컨테이너-호스트 연결

호스트에서 외부로부터의 요청을 받더라도 컨테이너와 호스트의 포트가 서로 연결되어 있지 않으면, 그 요청은 컨테이너에까지 도달하지 못하고, 따라서 응답 또한 컨테이너에서 처리해주지 못한다. 해당 요청에 대한 응답이 컨테이너에서 이루어지기를 원한다면 호스트의 포트로 들어온 것을 컨테이너의 포터로 연결해주는 추가적인 설정이 필요하다.

docker run-p(--publish) <요청받을 호스트 포트>:<연결할 컨테이너 포트> 옵션을 추가해 컨테이너를 실행하면 호스트의 해당 포트로 들어오는 요청을 컨테이너 내부의 정해진 포트로 전달할 수 있게 된다.

4. 컨테이너 내부 파일 변경하기

컨테이너 내부에서 컨테이너 외부의 파일을 사용할 수 있는 방법들. 웹 페이지를 연결하는 것과 같이 오랫동안 고정된 내용을 각 사용자마다 다르게 취하는 경우에는 바인드 마운트, 또는 볼륨이 가장 효과적이다.

docker cp

docker cp <호스트 경로> <컨테이너 이름>:<컨테이너 내부 경로>형식으로 호스트에 위치한 파일을 구동 중인 컨테이너 내부에 복사. 임시로 필요한 파일이 있는 경우, 혹은 컨테이너에 저장된 설정 및 로그를 추출해 확인하는 목적으로 사용

Dockerfile ADD

이미지는 Dockerfile을 기반으로 만들어지는데, 여기서 ADD 구문으로 컨테이너 내부로 복사할 파일을 지정하면 이미지를 빌드할 때 지정한 파일이 이미지 내부로 복사된다.

바인드 마운트

호스트 파일 시스템과 컨테이너 내부를 연결해 어느 한쪽에서 작업한 내용을 양쪽에 동시에 반영하는 방법. 새 컨테이너를 구동할 때도 호스트와 연결할 파일, 디렉토리 경로만 지정하면 다른 컨테이너에 있는 파일을 새로 생성한 컨테이너와 연결할 수 있다. DB의 데이터나 서버의 첨부 파일 등과 같이 컨테이너가 바뀌어도 없어지면 안 되는 자료들은 이 방법으로 보존할 수 있다.

볼륨

호스트의 파일 시스템과 컨테이너 내부를 연결하는 것은 동일하지만, 호스트의 특정 디렉토리가 아니라 도커가 관리하는 볼륨을 컨테이너 와 연결함. 도커가 관리하는 볼륨 공간을 공유 디렉토리에 생성하면 다른 호스트에서도 도커가 관리하는 볼륨을 함께 사용할 수 있다.

5. 사용하지 않는 컨테이너 정리하기

컨테이너, 이미지를 삭제하기 전에는 먼저 컨테이너를 정지해야한다. 이외에도 동일한 호스트의 포트를 사용하는 컨테이너를 배포하거나 작동 중인 컨테이너 사용 자체를 종료할 때에도 컨테이너를 정지해야 한다.

  1. docker stop <컨테이너 이름 | ID> 를 통해 컨테이너를 정지할 수 있다.
  2. docker rm <컨테이너 이름 | ID>를 통해 컨테이너를 삭제할 수 있다
  3. docker rmi <이미지 이름 : tag> 를 통해 이미지를 삭제할 수 있다.
    • 단, 이 때 이미지에 해당하는 컨테이너가 먼저 삭제된 이후에야 이미지를 삭제할 수 있다.

[3] 컨테이너 이미지 만들기

컨테이너 인프라 환경을 구성할 때, 이미 제공된 이미지를 사용할 수도 있지만 직접 만든 애플리케이션으로 컨테이너를 만들 수도 있다.

컨테이너 이미지를 만드는 방법은 다음의 4가지다.

  • 기본적인 빌드
  • 용량 줄이기
  • 컨테이너 내부 빌드
  • 멀티 스테이지

1. 기본 방법으로 빌드하기

에서 예제는 스프링부트를 이용해 만든 자바 소스 코드로 이미지를 빌드하고 있다.

과정

자바 소스 빌드 → 도커파일 작성 → 도커파일 빌드 → 빌드 완료

설명

자바로 작성된 소스 코드를 실행 가능한 바이너리로 만든다. 만들어진 JAR 파일이 들어있는 디렉토리에서 docker build 명령으로 컨테이너 이미지를 빌드한다.

docker build -t basic-img .

-t basic-img 만들어 낼 이미지 태그를 “basic-img”로 정함

. 이미지에 원하는 내용을 추가/변경하는 데 필요한 작업 공간을 현재 디렉토리로 지정

Dockerfile
컨테이너 이미지를 빌드하기 위한 정보를 담고 있는 파일.
빌드용 DSL(Domain-Specific Languages, 도메인 특화 언어)로 작성된 파일.

FROM <이미지 이름>:[태그]기초 이미지를 가져와 내부에서 컨테이너 이미지를 빌드. 선택한 기초 이미지에 따라 다양한 환경의 컨테이너를 빌드 가능(여기에서 기초 이미지는 openjdk를 사용하고 있음)
LABEL <레이블 이름>=<값>이미지에 부가적인 설명을 위한 레이블을 추가
EXPOSE <숫자>생성된 이미지로 컨테이너를 구동할 때 어떤 포트를 사용하는지 알려줌. 다만 이것만으로 컨테이너 포트와 호스트 포트를 자동으로 연결해주지는 않음. 반드시 docker run -p로 포트를 연결해야 함
COPY <호스트 경로> <컨테이너 경로>새로 생성하는 컨테이너 이미지로 필요한 파일을 복사
WORKDIR <경로>이미지의 현재 작업 위치를 경로로 변경
ENTRYPOINT [”명령어”, “옵션”, … “옵션”]컨테이너 구동 시 가장 먼저 이 명령을 실행함.
RUN <명령>해당 명령을 실행함. ex) RUN git clone …

2. 컨테이너 용량 줄이기

기본적인 방법으로 빌드된 이미지는 용량이 상당히 클 수 있다. 불필요하게 큰 공간을 점유하는 건 비용 낭비이기도 하고, 성능에 영향을 미칠 수도 있다. 컨테이너 이미지 용량을 줄여 빌드하는 방법을 알아보자

과정

도커파일 작성 → 도커파일 빌드 → 빌드 완료

설명

기본 방법보다 단계가 하나 줄고, 기초 이미지도 openjdk에서 GCR(Google Container Registry)에서 제공하는 distroless로 변경된다. 이것만 해도 불필요하게 쓰이는 공간들이 줄어든다. openjdk에는 이미 자바 개발 도구가 포함되어 있기 때문에.

distroless
자바 실행을 위해 경량화된 이미지.

❓왜 openjdk에 개발 도구가 포함돼 있는데, 호스트에서 빌드하고 COPY로 넘길까?

3. 컨테이너 내부 빌드

자바 소스를 컨테이너 이미지에서 빌드하는 경우

과정

도커파일 작성 → 도커파일 빌드(이미지 내 빌드) → 빌드 완료

설명

컨테이너 이미지 내부에 소스 코드를 받고 나서 (RUN git clone 깃주소), 컨테이너 이미지 내부에서 자바 소스 코드를 빌드하는 경우.

컨테이너 내부에서 빌드를 진행하기 때문에 빌드 중간에 생성한 파일과 내려받은 라이브러리 캐시들이 최종 이미지에도 그대로 남음.

컨테이너 이미지는 커지면 커질수록 비효율적으로 작동하므로, openjdk로 컨테이너 내부에서 컨테이너를 빌드하는 것은 좋지 않음. 하지만 Dockerfile만 빌드하면 컨테이너를 바로 생성할 수 있기에 편리하기는 함.

4. 멀티 스테이지 ⭐⭐⭐

컨테이너 이미지를 만들면서 빌드 등에는 필요하지만, 최종 컨테이너 이미지에는 필요 없는 환경을 제거할 수 있도록 단계를 나누어 기반 이미지를 만드는 방법이다. 빌드 위치와 최종 이미지를 분리, 최종 이미지는 빌드된 JAR을 가지고 있지만 용량은 줄일 수 있다. 최종 이미지의 용량도 줄일 수 있고 호스트에 어떤 빌드애차지는 docker-ce 17.06 버전부터 지원한다.

과정

도커파일 작성 → 도커파일 빌드(빌드/최종이미지 분리) → 빌드 완료

설명

컨테이너 내부에서 openjdk로 자바 소스를 빌드해 JAR로 만든다. 이후 빌드된 JAR을 경량화 이미지에 복사한다. 앞서 **2. 컨테이너 용량 줄이기**와의 차이는 빌드를 호스트에서 했느냐, 컨테이너 내에서 했느냐 밖에 없다.

내부의 컨테이너 이미지를 확인해보면 으로 표시되는 이미지가 있을 수 있다. 이렇게 이름이 없는 이미지를 댕글링(dangling) 이미지라고 하는데, 멀티 스테이지 과정에서 자바 소스를 빌드하면서 생성된 이미지로 보면 된다. 공간을 적게 사용하는 것이 목적이므로 이 이미지는 삭제하면 된다.

0개의 댓글