도커와 CI 환경 - 2

지원·2024년 3월 10일
0

Docker

목록 보기
2/7

도커 이미지 내부 파일 구조 보기

  • docker run IMAGE_NAME ls
  • docker : 도커 클라이언트 언급
  • run : 컨테이너 생성 및 실행
  • IMAGE_NAME : 컨테이너를 위한 이미지
  • ls : 현재 디렉토리의 파일 리스트 표출
  • ls 위치에는 원래 이미지가 가지고 있는 시작 명령어를 무시하고 여기에 있는 커맨드를 실행하게 하는 자리다.

docker run alpine ls 을 실행했을 때

  1. Alpine 이미지를 이용해서 컨테이너를 생성
  2. 생성할 때 Alpine 이미지 안에 들어있던 파일 스냅샷들이 컨테이너 안에 있는 하드 디스크로 다운로드
  3. 이미지 이름 뒤에 다른 명령어를 더 붙여서 원래 이미지 안에 들어있는 기본 커맨드는 무시가 되고 ls 명령어가 실행

전에 가져왔었던 hello-world 이미지로 ls 명령어를 사용하면 에러가 발생한다.

  • 그 이유는 위에 2번에서 파일 스냅샷이 하드 디스크 부분으로 들어가는데 그 부분에 무엇이 있는지에 따라서 쓸 수 있는 커맨드가 있고 쓰지 못 하는 커맨드도 있을 수 있다.
  • alpine 의 파일 스냅샷 에서는 ls 을 실행할 수 있지만 hello-world 의 파일 스냅샷에는 ls 를 실행할 수 없는 것이다.

컨테이너들 나열하기

  • docker ps
  • docker : 도커 클라이언트 언급
  • ps : Process Status

docker ps 명령어 실행

  • CONTAINER ID : 컨테이너의 고유한 아이디 해쉬값 , 실제로는 더 길지만 일부분만 표출

  • IMAGE : 컨테이너 생성 시 사용한 도커 이미지

  • COMMAND : 컨테이너 시작시 실행될 명령어
    -> 대부분 이미지에 내장되어 있기 때문에 별도 설정이 필요 없다.

  • CREATED : 컨테이너가 생성된 시간

  • STATUS : 컨테이너의 상태 (Up , Exited , Pause)

  • PORTS : 컨테이너가 개방한 포트와 호스트에 연결한 포트
    -> 특별한 설정을 하지 않은 경우 출력되지 않음

  • NAMES : 컨테이너 고유한 이름
    -> 컨테이너 생성시 --name 옵션으로 이름을 설정하지 않으면 도커 엔진이 임의로 형용사와 명사를 조합해 설정
    -> rename 명령어로 이름을 변경할 수 있다. (docker rename original-name changed-name)

모든 컨테이너 나열

  • docker ps -a
  • a 는 All 을 의미

도커 컨테이너의 생명주기

  • 생성 -> 시작 -> 실행 -> 중지 -> 삭제

사실 docker run 명령어는 docker create + docker start 의 과정을 한 번에 해준 명령어이다.

  • docker create : 이미지가 만들어지고 이미지 안에 있는 파일 스냅샷이 하드 디스크 부분으로 가는 명령어이다.
  • docker start : 이미지 안에 있는 시작 시 실행할 명령어가 컨테이너에 들어가면서 실행되는 명령어이다.

docker create

  • 생성
  • create 명령어를 실행하면 컨테이너 ID 를 반환한다.

docker start

  • 시작 -> 실행

docker run

  • 생성 -> 시작 -> 실행

docker start -a CONTAINERID

  • -a 는 attach 를 의미
  • 만약 -a 없이 실행하면 어떻게 될까?
  • CONTAINERID 만 반환한다.
  • -a 가 있으면 도커 컨테이너가 실행이 될 때 그 쪽에 붙어 있다가 그곳에서 나오는 아웃풋들을 화면에 노출시켜주는 것이다.
  • CONTAINERID 에는 ID 전부다 쓰지 않고 일부분만 적어도 정상적으로 실행이 된다.

docker stop vs docker kill

  • 이번에는 위에 생명 주기에서 중지 부분을 알아보자.
  • docker stop 과 docker kill 로 중지할 수 있다.
  • 즉 docker stop 과 docker kill 은 생명주기에서 실행 -> 중지 부분의 해당한다.

stop 과 kill 의 공통점과 차이점

  • 공통점은 둘 다 실행중인 컨테이너를 중지시킨다는 점이다.
  • 차이점으로는 stop 은 자비롭게 중지 시키지만, kill 경우는 어떠한 것도 기다리지 않고 바로 컨테이너를 중지시킨다.
  • 즉 stop 은 그동안 하던 작업들을 완료하고 컨테이너를 중지시키지만, kill 은 바로 중지시킨다는 것이다.
  • 더 자세히 알아보면 docker stop 은 SIGTERM 이라는 정리하는 시간을 거치고 SIGKILL 명령이 실행되지만 docker kill 은 SIGTERM 없이 바로 SIGKILL 이 실행된다.

실제로 stop 으로도 중지해보고 kill 로도 중지 시켜봤는데 stop 은 약간의 시간 뒤에 중지가 되지만 kill 은 바로 중지가 된다.

컨테이너 삭제

  • 이번에는 생명주기 중에서 삭제 부분을 알아보자.

중지된 컨테이너를 삭제하고 싶을 때

  • docker rm ID or NAME
  • 실행 중인 컨테이너는 먼저 중지한 후에 삭제를 할 수 있다.

모든 컨테이너를 삭제하고 싶을 때

  • docker rm 'docker ps -a -p'

이미지를 삭제하고 싶을 때

  • docker rmi IAMGE_ID

한 번에 사용하지 않는 컨테이너 , 이미지 , 네트워크 모두 삭제하고 싶을 때

  • docker system prune
  • 도커를 쓰지 않을 때 모두 정리하고 싶을 때 사용하면 좋다.

실행 중인 컨테이너에 명령어 전달

  • docker exec CONTAINER_ID
  • 실제로 docker exec hello-world 의 CONTAINER_ID ls 명령어 실행
  • 그 결과 해당 컨테이너의 파일 리스트들이 잘 출력이 된다.
  • 하지만 docker run alpine ls 와 같은 결과가 나오는데 의미가 다르다.

docker run 은 새로운 컨테이너를 만들어서 실행
docker exec 은 이미 실행 중인 컨테이너에 명령어를 전달

  • 실행 중인 컨테이너 안으로 들어가서 ls 명령어를 실행

Redis 를 이용한 컨테이너 이해

  • Redis 를 도커 환경에서 실행을 해보자.

Redis 동작 원리

  1. Redis 서버 실행 (docker run redis)
  2. Redis 클라이언트 실행 (redis-cli)
  3. Redis 클라이언트에서 명령어를 실행하면 Redis 서버로 이동

즉 Redis 서버를 먼저 실행 시키고 Redis 클라이언트를 통해 서버에 명령어를 전달해야한다.

  • 먼저 docker run redis 로 서버를 작동 시키고 그 후 다른 터미널을 열어서 redis-cli 를 실행해본다.
  • 하지만 에러가 발생한다..
  • 에러가 발생하는 이유는 Redis 클라이언트가 Redis 서버가 있는 컨테이너 밖에서 실행을 하려고 하기 때문에 Redis 서버에 접근을 할 수 없는 것이다.
  • 해결하기 위해서는 Redis 클라이언트도 같은 컨테이너 안에서 실행을 해야한다.

위에서 배웠던 exec 명령어를 사용하면 된다.

  • docker exec -it CONTAINER_ID redis-cli
  • 정상적으로 실행이 된다.
  • -it 명령어는 실행 한 후 계속 명령어를 적을 수 있도록 한다.
  • i 는 Interactive , t 는 terminal
  • 만약 없다면 redis-cli 만 키고 바로 밖으로 나와진다.

실행 중인 컨테이너에서 터미널 사용하기

  • 지금까지 실행중인 컨테이너에 명령어를 전달할 떄는 다음과 같이 명령어를 작성해야 한다.
  • docker exec -it CONTAINER_ID 실행시킬 명령어
  • 명령어 하나를 전달하기 위해서 이 모든 것을 계속 입력을 해줘야 하는데 이러한 문제점을 해결하기 위해 컨테이너 안에 쉘이나 터미널 환경으로 접속을 해준다.
  • 마지막 명령어를 sh 로 하게 되면 쉘 환경으로 접속이 된다.
  • docker exec -it CONTAINER_ID sh
  • 쉘 환경 안에서 ls , touch 등 편하게 명령어를 작성할 수 있다.
  • exec 가 아닌 run 을 사용해도 된다.
  • docker run -it IMAGE_NAME 명령어

도커 이미지 생성하는 순서

현재까지는 도커 이미지를 항상 도커 허브에 이미 있던 것들만 가져왔지만, 직접 도커 이미지를 만들어서 사용할 수도 있고 직접 만든 도커 이미지를 도커 허브에 올려서 공유할 수 있다.

컨테이너는 도커 이미지로 만든다고 했는데 그렇다면 도커 이미지는 어떻게 생성할까?

  • 도커 파일 작성 -> 도커 클라이언트 -> 도커 서버 -> 이미지 생성

도커 파일? 도커 클라이언트? 도커 서버?

  • 도커 파일이란 도커 이미지를 만들기 위한 설정 파일로 컨테이너가 어떻게 행동해야 하는지에 대한 설정을 정의한다.
  • 도커 파일에 입력된 명령들이 도커 클라이언트에 전달되어야 한다.
  • 도커 서버는 도커 클라이언트에 전달된 모든 중요한 작업들을 하는 곳이다.

Dockerfile 만들기

  • 도커 파일이란 도커 이미지를 만들기 위한 설정 파일이며, 컨테이너가 어떻게 행동해야 하는지에 대한 설정들을 정의하는 것이라고 했다.

도커 파일 만드는 순서

  • 우선 도커 이미지가 필요로 하는 것이 무엇인지를 생각해야 한다.
  • 도커 이미지에는 시작 시 실행할 명령어 , 파일 스냅샷이 존재한다.
  1. 베이스 이미지를 명시 (파일 스냅샷에 해당)
  2. 추가적으로 필요한 파일을 다운 받기 위한 몇 가지 명령어를 명시 (파일 스냅샷에 해당)

베이스 이미지란?

  • 도커 이미지는 여러 개의 레이어들로 되어 있다.
  • 그 중에서 베이스 이미지는 이 이미지의 기반이 되는 부분이다.
  • 즉 베이스 이미지는 간단하게 macOS , Winodw 와 같은 OS 라고 생각하면 된다.
  • 이미지에 무언가를 추가하고 싶다면 레이어라는 단위를 추가하는 것이고 이것을 레이어 캐싱이라고 부른다.

우리가 만들 도커 이미지의 목표는 hello 라는 문구를 출력해보자.

  • 도커 파일을 만들고 여기에 Dockerfile 생성후 아래와 같은 형식으로 작성하면 된다.
# 베이스 이미지를 명시해준다.
FROM baseIamge

# 추가적으로 필요한 파일들을 다운로드 받는다.
RUN command

# 컨테이너 시작시 실행 될  명령어를 명시해준다.
CMD [ "executable" ]

FROM

  • 이미지 생성 시 기반이 되는 이미지 레이어
  • 이미지이름:태그 형식으로 작성
  • 태그를 안 붙이면 자동적으로 가장 최신 것으로 다운로드 (latest)

RUN

  • 도커 이미지가 생성되기 전에 수행할 쉘 명령어

CMD

  • 컨테이너가 시작되었을 때 실행할 실행 파일 또는 셸 스크립트로 해당 명령어는 DockerFile 내 한 번만 쓸 수 있다.
# 베이스 이미지를 명시해준다.
FROM alpine

# 추가적으로 필요한 파일들을 다운로드 받는다.
# RUN command

# 컨테이너 시작시 실행 될  명령어를 명시해준다.
CMD [ "echo" , "hello" ]
  • hello 를 출력하는 기능에는 사이즈가 큰 베이스 이미지를 사용할 필요가 없기 때문에 사이즈가 작은 alpine 을 사용
  • 문자 출력을 하려면 echo 가 있어야 하는데 이것은 alpine 안에 있기 떄문에 RUN 부분은 생략해도 상관 없다.

이렇게 도커 파일은 만들었고 이 파일을 가지고 이미지를 어떻게 만들어야 하는지를 알아보자.

도커 파일로 도커 이미지 만들기

  • 도커 파일 -> 도커 클라이언트 -> 도커 서버 -> 이미지

위에서 만든 도커 파일에 입력된 것들을 도커 클라이언트에 전달되어서 도커 서버가 인식하게 해야한다.

  • 그렇게 하기 위해서는 docker build . or docker build ./ 명령어를 실행해야 한다.

docker build

  • 해당 디렉토리 내에서 dockerfile 이라는 파일을 찾아서 도커 클라이언트에 전달시켜준다.
  • docker build 뒤에 . 이나 / 은 둘 다 현재 디렉토리를 가리킨다.

build 과정

  • 위에서 작성한 도커 파일을 기준으로 설명
  1. alpine 이미지를 가져온다.
  2. 임시 컨테이너 생성 후 그 컨테이너에 시작 시 사용할 명령어를 포함시킨다.
  • 생성한 임시 컨테이너를 지우고 새로운 이미지를 만든다.

더 자세히 알아보자.
1. 먼저 베이스 이미지를 임시 컨테이너에 넣어준다.

  • 위에서 작성한 도커 파일에서는 베이스 이미지가 alpine 이기 때문에 alpine 을 임시 컨테이너에 넣는다.
  • 만약 베이스 이미지 이외의 이미지도 있다면 그것도 넣어준다.
  • 그 과정에 임시 컨테이너의 하드 디스크에 alpine 파일 시스템 스냅샷을 추가한다.
  1. 도커 파일에 있었던 CMD 에 있는 명령어도 임시 컨테이너에 넣어준다.
  • 이 부분이 이미지 안에 시작 시 실행할 명령어에 들어가게 된다.
  1. 그 임시 컨테이너를 토대로 새로운 이미지가 만들어진다.
  • 만들어진 이후에 임시 컨테이너는 지워진다.

정리

  • 베이스 이미지에서 다른 종속성이나 새로운 커맨드를 추가할 때는 임시 컨테이너를 만든 후 그 컨테이너를 토대로 새로운 이미지를 만들고 그 임시 컨테이너는 지워준다.

이미지 -> 새로운 명령어 + 새로운 파일 스냅샷으로 임시 컨테이너 생성 -> 임시 컨테이너로 새로운 이미지 생성

내가 만든 이미지 기억하기 쉬운 이름으로 추가

  • 도커 파일로 만든 이미지를 컨테이너에서 실행
  • docker run -it IMAGE_ID
  • 하지만 IMAGE_ID 를 모두 입력하기에는 너무 길고 기억하기도 어렵다.

IP Address 가 기억하기 힘들어서 Domain 에 이름을 새로 만들어서 사용하는 것 처럼 도커 이미지에도 이름을 줄 수 있다.

  • docker build -t supportkim/hello:latest .
  • docker build -t 나의 도커 아이디 / 저장소 및 프로젝트 이름 : 버전
  • 위와 같은 형태에 맞춰서 이름을 부여하면 된다.

위와 같이 설정한 후 docker run -it supportkim/hello 을 실행하면 정상적으로 hello 가 출력된다!

참고자료

profile
덕업일치

0개의 댓글