• [Docker] Docker Client로 컨테이너 조작하기

    2021. 6. 10.

    by. SDev

    728x90

    *** 본 정리는 2021 멋쟁이사자처럼 세렝게티올빼미 프로젝트 kubernetes 그룹에서 학습을 목적으로 정리한 글입니다.

    참고 자료: Docker and Kubernetes : The Complete Guide - udemy


     

    • 이미지로부터 컨테이너 생성하고 실행하기

     

    run 커맨드는 이미지로부터 컨테이너를 생성하고, 뒤이어 바로 생성한 컨테이너를 실행하는 역할을 한다.

    • docker run = docker create + docker start
    • docker create = 컨테이너 생성, 이미지의 파일시스템 컨테이너에 탑재
    • docker start = 해당 컨테이너 이미지의 startup command 실행

     

    터미널에 '$ docker create hello-world'를 입력했을 때, 그 결과로 임의의 String이 출력되는데, 여기 출력되는 String은 생성된 컨테이너의 ID다.

     

    생성된 컨테이너는 '$ docker start -a <container Id>'로 실행할 수 있다.

     

    컨테이너를 실행해보면 run 커맨드로 실행했던 것과 같이 결과가 터미널로 출력된다.

     

    '-a' 는 해당 컨테이너를 실행시키고 그 결과를 터미널에 출력하라는 옵션이다. 만약 이를 포함하지 않고 '$ docker start <container Id>'로 컨테이너를 실행시키면 컨테이너는 내부적으로 실행되지만 터미널에는 이전과 같은 출력이 나타나지 않는다.

     

    • docker run은 실행시키고 결과를 출력하는 것이 default
    • docker start는 단순 실행만 하는 것이 default

     

     

    run 커맨드의 뒤에 추가 커맨드를 붙여 사용할 수도 있다.

    • $ docker run busybox echo hi there
    • $ docker run busybox echo bye there
    • $ docker run busybox echo how are you

     

    위 3개 커맨드는 각각 busybox 컨테이너를 생성하고 실행한다. 각각 커맨드마다 뒤에 붙인 echo 커맨드에 의해 "hi there", "bye there", "how are you"가 출력되는 것을 볼 수 있다.

     

     

    • $ docker run busybox ls

    결과로 bin, dev, etc, home, proc, root 출력된다. 이는 busybox 컨테이너 내부에 설치되어 있는 File이다.

     

    • docker run hello-world ls
    • docker run hello-world echo hi there

     

    반대로 hello-world의 경우 위 두 커맨드 모두 결과로 error가 출력된다. 이는 ls 커맨드와 echo 커맨드가 hello-world FS 안에 존재하지 않기 때문이다.

     

    ls와 echo 커맨드도 일종의 프로그램이다.

     

    busybox에 ls, echo 두 프로그램 모두 busybox 파일시스템 내부에 존재하는 것이고, hello-world는 hello-world 단일 프로그램으로 구성되어 있기에 실행되지 않는 것이다.

     

     

     

    • 실행 중인 컨테이너 목록 조회하기

    busybox 컨테이너를 생성하고 실행하면서 ping 을 실행하도록 한다. ping은 주기적으로 계속 google.com으로 네트워크 상태를 확인하기 때문에 이 컨테이너는 이전처럼 실행되고 바로 종료되지 않는다.

     

    • 새로운 터미널 생성(터미널2)
    • 터미널2 → $docker ps

     

    실행중인 busybox 이미지의 컨테이너를 확인할 수 있다. Container ID, IMAGE, COMMAND, CREATED, STATUS, PORTS, NAMES를 확인할 수 있다. 특이하게 컨테이너의 NAME은 생성 시점에 임의로 설정되어 있다.

     

    • 터미널1 → ctrl + c로 컨테이너 종료
    • 터미널2 → $ docker ps
    • 터미널2 → $ docker ps —all(hyphen 2개)

     

    busybox 컨테이너가 종료되고 나면 다시 'docker ps' 명령을 했을 때 실행중인 컨테이너는 없기 때문에 아무 것도 출력되지 않는다. 그러나 'docker ps —all' 명령은 이전에 생성되어 종료된 컨테이너의 정보까지 조회할 수 있다.

     

     

     

    • Restarting Stopped Container

    실행이 종료된 컨테이너를 다시 시작하려면 해당 컨테이너의 ID로 다시 start 하면 된다.

     

    • $ docker run busybox echo hi there
    • $ docker ps —all
    • $ docker start -a <Exited Container ID>

     

    이전에 생성했던 컨테이너의 hi there 커맨드가 실행되어 hi there가 터미널에 출력되는 것을 볼 수 있다.

     

    • $ docker start –a <Exited Container ID> echo bye there

    이미 생성했던 컨테이너의 default 커맨드는 이미 설정되어 있기 때문에 변경할 수 없다.

     

     

     

    • Removing stopped containers
      • $ docker ps —all
      • $ docker system prune

     

    'docker ps —all'을 실행하면 이전에 종료된 컨테이너들의 목록을 볼 수 있다. 이 컨테이너는 종료되었지만 삭제된 것은 아니다. 이를 삭제하기 위해 'docker system prune'을 사용할 수 있다. stopped된 컨테이너의 삭제와 더불어 build cache도 비운다.

     

     

     

    • Retrieving Log Outputs

    debug를 위해 컨테이너 내부의 로그를 출력하려면 어떻게 해야할까?

    • docker create busybox echo hi there
    • docker start <Container ID>
    • docker logs <Container ID>

     

    busybox 컨테이너는 hi there를 내부적으로 출력한 뒤 곧바로 종료(exit)된다. -a를 붙이지 않았기 때문에 터미널에는 출력되지 않는다. 다음으로 'docker logs <Container ID>' 명령을 실행하면 해당 컨테이너의 내부적으로 출력된 로그를 터미널에 출력해준다.

     

     

     

    • Stopping Container

     

    • $ docker create busybox ping google.com
    • $ docker start <Container ID>
    • 새로운 터미널 생성(터미널2)
    • 터미널2 → $ docker logs <Container ID>
    • 터미널2 → $ docker ps

     

    ping 동작을 하는 busybox를 생성 및 동작시키고, 다른 터미널로 이를 조회해 동작중임을 확인할 수 있다. 어떻게 이 컨테이너를 멈출까? 이전에는 터미널1에서 직접 조작해 멈추게 했다. 터미널2에서는 'stop'이나 'kill'을 사용해 컨테이너를 멈출 수 있다.

     

    둘 다 컨테이너를 멈추는 커맨드지만 차이가 있다.

     

    stop은 컨테이너 내부의 프로세스를 멈추고 컨테이너를 종료(shutdown)할 때 사용한다. 종료할 때 프로세스의 진행 상황같은 것을 저장하고 종료작업을 수행한다.

     

    반면 kill은 바로 shutdown 동작을 한다. 진행상황 저장과 같은 것을 고려하지 않고 바로 종료시킨다.

     

    stop의 경우 명령을 주고 나서 10초 내에 자동으로 stop이 정상적으로 수행되지 않으면 docker는 자동으로 kill을 수행한다.

     

    • 터미널2 → $ docker ps
    • 터미널2 → $ docker stop <container id>

    10초 정도가 지나고 나서야 종료 된 컨테이너의 ID를 볼 수 있다.

     

    ping command는 stop message에 적절히 respond하지 못하기 때문이다.

     

    결과적으로 stop하지 않고 10초가 지나서야 결국 kill동작이 수행되기 때문에 10초 후에나 결과를 볼 수 있다.

     

    • 터미널1 → $ docker start <container ID>
    • 터미널2 → $ docker kill <container ID>

    이번에는 바로 kill 명령을 수행하기 때문에 곧바로 컨테이너가 종료되었음을 볼 수 있다.

     

     

     

    • Multi-Command Container

     

    이번에는 redis를 설치하고 실행해본다.

    • $ redis-server

    local에 redis를 직접 설치한 상태라면 정상적으로 redis가 동작된다. (실습을 위해 설치할 필요는 없고, 그냥 읽어보면 된다.)

     

    • 새로운 터미널 생성(터미널2)
    • 터미널2 → $ redis-cli

    터미널2에서 redis-cli를 입력하면 redis server로 접근 가능한 프롬프트가 제공된다.

     

    • > set mynumber 5
    • > get mynumber

    프롬프트에서 mynumber 키에 5라는 값을 저장시키고, 이를 다시 조회할 수 있다.

    docker를 이용해 로컬 환경에 redis를 설치하지 않고도 손쉽게 이처럼 redis를 사용할 수 있다.

     

    • $ docker run redis

    redis 이미지를 다운받아 컨테이너를 생성하고 동작시키는데, 맨 마지막 라인에 “Ready to accept connection”이 출력된다면 준비가 끝난 것이다.

     

    • 새로운 터미널을 생성한다.(터미널2)
    • 터미널2 → redis cli

    connection refused라는 에러를 볼 수 있다. redis는 컨테이너 내부에서 동작하고 있는데 이를 컨테이너 외부에서 접근하기 때문이다!

     

     

    • 터미널2 → $ docker ps
    • 터미널2 →$ docker exec -it <container id> redis-cli
    • 터미널2 → > set myvalue 5
    • 터미널2 → > get myvalue

    exec 커맨드를 사용해서 컨테이너 내부에서 redis server 프로그램이 실행되고 있는데, redis client라는 두번째 프로그램을 실행시킨 것이다!

     

     

    • $ docker exec <container id> redis-cli

    '-it'를 빼고 실행시키면 아무런 반응이 없다. redis-cli는 실행되었지만 내부 text에 접근할 권한이 없기 때문이다. 실행되자마자 text input이 들어올 수 없다는 것을 확인하고 closed한 뒤 터미널로 kick back되었다.

     

     

     

    • -it 플래그의 목적

    앞서 학습한 것처럼 docker 컨테이너는 linux 환경에서 구동된다.

     

    각 컨테이너는 3개의 커뮤니케이션 채널 STDIN(standard in), STDOUT(standard out), STDERR(standard error)를 갖는다.

     

    • -it : -i + -t

    -it 플래그는 -i 플래그에 -t플래그를 더한 것과 100% 동일하지만 관습적으로 짧게 줄여 쓰기 위해 -it를 사용하는 것이다.

     

    • i : 실행중인 컨테이너 내부로 STDIN
    • t : 반대로 터미널로 들어오는 텍스트를 포맷된 형태로 스크린에 띄워줌

     

    • $ docker exec -i <container ID>

    input을 기다리고 있는 상태지만 커서 외에는 아무것도 나타나지 않는다.

     

    • > set myvalue 5 입력
    • > get myvalue 입력

    동작에는 이상이 없지만 편리하게 사용할 수 있는 indent 같은 포맷팅이 사라졌다는 것을 확인할 수 있다.

     

     

     

    • 컨테이너 내부의 커맨드 프롬프트 사용하기

    터미널을 두개 생성한다.

    • t1 : $docker ps
    • t2 : $docker exec -it <container ID > sh

    두 번째 터미널에서 #이 등장한다. 여기에 원하는 커맨드를 입력할 수 있다.

     

    • # cd ~/
    • # ls
    • # cd /
    • # ls

    디렉토리를 이동한 뒤 ls에 대한 응답이 출력되는 것을 볼 수 있다. 컨테이너 내부로 들어온 것이다.

     

    • # echo hi there
    • # export b=5
    • # echo $b

    컨테이너 내부의 터미널을 이처럼 조작할 수 있다. 이는 컨테이너 내부로 들어가 터미널을 켠 것과 같다. 디버깅 상황에 매우 유용하다.

     

    • # redis-cli

    컨테이너 내부로 들어와 있기 때문에 docker 명령이 아닌 바로 redis-cli를 실행시킬 수 있다. 이 상황에서 조작을 하다 다시 나오고 싶다면 ctrl+c는 내부에서 사용하는 예약어이기 때문에 ctrl+d로 나올 수 있다.(or exit command)

     

    sh도 하나의 프로그램이다. 컨테이너 내부에서 동작하는 프로그램이며, command processors의 일종이다.

    맥이라면 bash, windows라면 gitbash가 powershell이라 이미 사용해 본 경험이 있을 것이다.

    이 프로그램들은 터미널에 커맨드를 입력하고 이를 실행할 수 있도록 한다.

    즉 컨테이너 내부의 애플리케이션을 실행시킬 때 외부에서 bash를 이용할 수도 있고, bash에서 sh로 들어가 프로그램을 실행시킬 수도 있다.

     

     

     

    • Starting with a Shell

    앞에서 한 docker exec -it <container ID> 방식은 추가적인 애플리케이션을 동작시킬 때 사용했다. 처음부터 컨테이너로 들어가 조작하려면 어떻게 하면 될까?

    • $ docker run -it busybox sh

    실행시키는 순간부터 sh를 실행하면 바로 컨테이너 내부로 들어갈 수 있다.

     

     

     

    • Container Isolation
      • $ docker run -it busybox sh
      • # ls
      • 새로운 터미널 생성(터미널2)
      • 터미널2 → $ docker run -it busybox sh
      • 새로운 터미널 생성(터미널3)
      • 터미널3 →$ docker ps
      터미널 1, 2에서 각각 두 개의 컨테이너를 생성했다. 이 두 컨테이너는 서로 분리된 별개의 컨테이너다.

       

       
      • 터미널1 → # ls
      • 터미널1 → # touch hithere
      • 터미널1 → # ls
      터미널 1에서 실행중인 컨테이너에서 새로운 파일을 생성했다.

       
      • 터미널2 → # ls
      만들었던 파일이 터미널 2 컨테이너에는 존재하지 않는다. 분리된 파일시스템을 갖고 있으며 서로 데이터를 공유하지 않음을 알 수 있다. 서로 다른 컨테이너를 의도적으로 연결하려 하지 않는다면 일반적으로 컨테이너들은 철저히 분리해 생성되고 관리된다.

    댓글