상세 컨텐츠

본문 제목

[4차시/15기 공도웅] Docker-2

심화 스터디/데이터 엔지니어링 스터디

by 우주베게 2023. 4. 18. 18:23

본문

Recap

Container 는 뜻 그대로 화물 수송용 박스를 생각하면 됩니다. container에 다양한 화물을 넣고 다양한 운송수단에 적재되어 쉽게 옮길 수 있는데 서버에서도 마찬가지입니다. 서버 실행에 필요한 모든 것(코드, 런타임, 시스템도구)들을 container에 넣어 쉽게 추상화하고 어디서에든(GCP, AWS, Local-machine) 실행할 수 있습니다.

Docker image는 container의 모든 정보를 포함한 하나의 단위로 볼 수 있습니다.

Docker 를 사용하는 이유는 container를 활용하여 쉽게 개발환경과 운영환경을 동일하게 구성할 수 있기 때문입니다.


docker compose

Docker container를 동시에 여러개를 생성하여 구동하려면 어떻게 해야 할까?

C1이라는 컨테이너를 실행해주고, 컨테이너의 이미지는 centos7, nginx를 실행해줘, 근데 포트번호는 80포트를 80포트로 port forwarding 해줘, webdata라는 공간을 /xx/라는 디렉토리로 마운트해서 실행해줘 등등.. 로컬호스트 포트, 컨테이너 포트

docker run - - name c1 centos:7 /bin/hostnames
docker run  - - name webserver -p 80:80 -v webdata:/xx nginx:1.14

하나씩 docker run 명령어를 치는 것은 귀찮음과 더불어 생산성도 떨어짐. 이러한 문제를 해결하기 위하여 docker-compose 를 사용.


Docker compose란?

여러 컨테이너를 일괄적으로 정의하고 실행하고 관리 할 수 있는 툴 (YAML을 통해 Docker container 실행할 수 있게 해줌)

  • 하나의 서비스를 운영하기 위해서는 여러개의 애플리케이션이 동작해야함
  • 컨테이너화 된 애플리케이션들을 통합 관리할 수 있음

컨테이너를 만들 때 여러 옵션들을 docker command로 직접 입력해주어야 했는데, 이를 대신하여 Docker compose YAML을 형태로 미리 만들어서 도커 컴포즈에게 YAML 파일을 기준으로 도커 명령어로 해석하여 도커 컨테이너를 운영해줌

도커 컴포즈의 버전 (ver.2의 경우 depends on 등 추가가능)


docker storage

💡 where and how docker stores data and how it manages file systems of containers (여기서 data라 함은 files related to images and containers running on the docker host)

 

로컬 파일 시스템에서는 어디에 데이터를 저장할까?

when you install docker on a system, it creates this folder structure at var/lib/docker 등등..

this is where docker stores all its data by default


한번 image를 pull 받아볼까? (Docker image가 저장되는 방식)

도커 이미지를 pull 받게 되면 마치 여러개로 분리된 조각을 내려받는 것처럼 보인다.이렇게 분리된 데이터를 **레이어(Layer)**라고 한다. 레이어는 도커 이미지가 빌드될 때 Dockerfile에 정의된 명령문(Instructions)을 순서대로 실행하면서 만들어진다. 이 레이어들은 각각 독립적으로 저장되며 읽기 전용이기 때문에 임의로 수정할 수 없다.


Dockers layered architecture

 

when docker builds images, it builds these in a layered architecture.

도커 이미지를 build 할때, layered architecure를 쌓아올리게 된다. 베이스가 되어지는 OS (Ubuntu Layer)에 Package 관련 change를 반영하는 layer, dependencies를 반영하는 layer, 소스코드, 그리고 Entry point 까지

 

Each line of instruction in the docker file creates a new layer in the Docker images. since each layer only stores the changes from the previous layer, it is reflected in the size as well.

 

the advantages of this layered architecture is related to second application that has a different docker file but is very similar to our first application

 

도커 이미지 레이어가 중요한 이유는 이미지를 빌드할 때마다 이미 생성된 레이어가 캐시 되어 재사용 되기 때문에 빌드 시간을 단축할 수 있다. 이미지 A가 있는데 이미지 B를 다운받으면 레이어 D만 다운받게 된다.

 

*Dockerfile에 정의된 모든 명령문(Instructions)이 레이어가 되는 것은 아니다. RUN,  ADD, COPY 이 3가지 단계만이 레이어로 저장되고, CMD, LABEL, ENV, EXPOSE 등과 같이 메타 정보를 다루는 부분은 임시 레이어로 생성되지만 저장되지 않아 도커 이미지 사이즈에 영향을 주지 않는다.


도커 컨테이너가 실행될 때 생성되는 결과물들은 어디에 저장될까?

 

도커 컨테이너가 실행되면 모든 읽기 전용 레이어들을 순서대로 쌓은 다음 마지막에 쓰기 가능한 신규 레이어를 추가하게 된다. 그 다음 컨테이너 안에서 발생하는 결과물들이 쓰기 가능 레이어를 기록되게 되는 것이다.

즉, 아무리 많은 도커 컨테이너를 실행하더라도 기존 읽기 전용 레이어는 변하지 않고, 컨테이너마다 생성된 쓰기 가능 레이어에 데이터가 쌓이기 때문에 서로 겹치지 않으며 컨테이너가 종료되면 모두 사라지게 된다.

Copy-On-Write mechanism

 

once the build is complete you cannot modify the contents of these layers and so they are read only, and you can only modify them by initiation a new build when you run a container. based on of this image using the docker run command docker creates a container based off these layers and creates a new writable layer on top of the image layer the writable layer is used to store data created by the container such as log files by the applications, any temporary files generated by the container or just any file modified by the user on that container

writable layer는 app file에 의해 생성되는 로그 파일, 유저가 생성한 컨테이너에 의해 변경된 파일 등 데이터를 저장하는 용도로 사용.

 

the life of this layer though is only as long as the container is alive. when the container is destroyed this layer and all of the changes stored in it are also destroyed. we just said that the files in the image layer are read only meaning you cannot edit anything in those layers. Remember the same image layer may be shared between mulitple containers created from this image. So does it mean that i cannot modify this file inside the container? No, i can still modify this file but before i save the modified file, docker automatically creates a copy of the file in the read write layer and i will then by modifying a different version of the file in the read-write layer all future modifications will be done on this copy of the file in the read-write layer. ⇒ Copy-On-Write mechanism

 

the image layer being read only just means that the files in these layers will not be modified in the image itself so the image will remain the same all the time until you rebuild the image using the docker build command what happens when we get rid of the container? all of the data that was stored in the container layer also get deleted. the change we made to app.py and the new temp file we created will also get removed.


Volumes

 

So what if we wish to persist this data?

we could add a persistant volume to the container. to do this first create a volume using the docker create volume command

먼저 docker volume을 만들어주어야함

 

Then when we run the docker container using the docker run command, i could mount this volume inside the docker containers read-write layer using the dash v option like this.

-v option을 사용하여 해당 디렉토리에 volume을 만들고, 여기다가 데이터를 마운트해서 실행해줘

 

and the location inside my container which is the default location where MySQL stored data and that is var/lib/mysql

so all data written by the database is in fact stored on the volume created on the local host. Even if the container is destroyed the data is still active.

 

  • Docker Volume과 Bind mount
    • Docker Volume
    Docker Volume은 Docker에 의해 만들어지고 관리되는 하나의 Volume 이다. 주로 Host의 /var/lib/docker/volumes/ 경로에 저장되며, Container에 마운트하지 않아도 미리 생성이 가능하다.
    • Docker Bind mount
    Docker volume을 사용하지 않고 Host의 디렉토리에 직접 Access하는 것을 말한다.
    • Docker Volume vs Bind mount
    이 둘의 기능적인 측면으로만 놓고 봤을 땐, 별 차이가 없습니다.하지만 이 둘의 차이는 분명히 존재 합니다.
    • Docker volume으로 만들면 Docker가 관리하는 Directory내에 생성됩니다.
    • Docker cli or api를 통해서 volume은 관리가 가능 합니다. ex) docker volume prune
    • Mount는 사용자의 소유자와 권한 때문에 실행 및 수정에 문제가 있을 수 있습니다.즉, 보안에 취약합니다.반면 Volume은 여러가지 형태로 볼륨을 만들 수 있고, 관리할 수 있습니다.
    • cifar10 & mnist와 volume의 관계
    보통 우리가 새로운 패키지나 코드를 사용할 때에는 일반적으로 깃Git에 접속해 소스 코드를 가지고와서 사용해보곤 한다. 혹은 우리가 만든 코드를 실험해보기 위해서는 cifar10 혹은 mnist 등의 데이터를 디렉터리에 내려받아 사용한다. 이럴때 해당 컨테이너에 볼륨 지정을 통해 디렉터리를 마운트해서 해당 디렉터리의 데이터를 사용할 수 있게 만든다.옵션 값은 -v를 사용하고, 콜론 앞 주소는 호스트의 디렉터리를 의미하고, 콜론 뒤에 오는 부분은 컨테이너에 있는 디렉터리를 지정해준다. 컨테이너의 디렉터리는 컨테이너를 실행했을 경우 메인이 되는 폴더를 지정해주는데, 파이토치의 경우 workspace로 지정해주면 된다. (텐서플로의 경우 tf로 지정한다)

 


Storage drivers

 

so who is responsible for doing all of these operations. maintaining architecture, creating a writable layer, moving files across layers to enable copy and write etc. ⇒ storage drivers

Docker uses storage drivers enables layered architecture.

Some of the common storage drivers are AUFS, BTRFS, ZFS, Device Mapper, Overlay 등등 (it depends on underlying OS being used. ex - for ubuntu the default storage driver is AUFS)


Docker Engine - Namespace

namespace 는 하나의 system에서 수행되지만, 각각 별개의 독립된 공간인것 처럼 격리된 환경을 제공하는 lightweight 가상화 기술이다. 최근 Container 기반의 가상화 기술인 Docker나 LXC가 각광을 받고 있는데 모두 namespace를 기반으로 만들어 졌다. 이러한 namespace 는 기존에 잘 알려진 가상화 기술인 Hypervisor 와는 구조적으로 다르다. Hypervisor는 Hardware resource 를 가상화 한다. Hypervisor 위에 올라가는 Guest OS 에는 가상화 된 형태의 H/W 를 제공하게 되며, 따라서 각각의 Guest OS는 완전한 다른 환경으로 분리된다. 하지만 namespace의 경우에는 Hardware resource 레벨의 가상화가 아니다. 동일한 OS와 동일한 kernel 에서 작동하게 되며, 단지 각각의 고립된 사용 환경만 제공되는 것이다.

 

Linux Process ID (PID) 와 PID 1

 

Linux 에서 실행되는 모든 process들은 각각에 고유한 process ID(PID) 가 부여된다. kernel 에서는 이 Process들을 Tree 형태로 관리한다.Tree 형태라는 의미는 모든 process 가 부모-자식 관계 (parent-child hierarchy) 를 가지고 있다는 의미이다.

모든 Process의 최상단에 위치한 process가 존재하게 되는데 이것을 init process 라고 한다. 시스템에서 생성되는 모든 process들은 init process에 의해 실행되어 지며, init process에게는 유일하게 PID가 "1" 이라는 숫자를 할당 받게 된다. 즉,  PID 1 = init 이다.

PID namespace를 설명하기 전에 먼저 PID가 1인 init process에 대해 설명한 이유는, PID namespace가 갖게 되는 가장 큰 특징이 바로 init process 만이 가질수 있는 PID 1 을 독립적으로 추가 할당해 준다는 것이다.

 

https://t1.daumcdn.net/cfile/tistory/2350223755A518E725

 

[ PID namespace ]

 

4번 Process를 parent로 하는 6번 Process가 PID namespace로 구성 되게 된다고 가정하면,

6번 Process는 origin 역할을 하는 1번 root Process ID를 부여 받게 될 것이다.

그리고 해당 namespace 내부에서 실행되는 process들은 기존 PID 할당순서와 별개로 PID를 부여 받게 된다.

child namespace에서 PID 1을 할당 받았다고 해서 이 process 가 init process는 아니다. 그러나 그 역할은 init process와 비슷하게 수행되는 부분이 많다.

 

예를 들면, 위 그림에서 만약 8(3)번 process 가 죽게 되면, 9(4)번 process 는 parent가 없는 고아(Orphaned process) 가 되는데, 이때 자동으로 6(1)번 process가 parent가 되는 child reaping 이 일어난다. 본래는 init process가 담당하지만, PID namespace 내부에서는 PID 1 process가 이렇게 init 역할을 대신한다.

관련글 더보기

댓글 영역