Chapter 1. 도커 기초
[핵심만 콕! 쿠버네티스] 도서를 정리한 내용입니다.
목차
- [1.1. 도커 소개](##1.1. 도커 소개)
- [1.2. 도커 기본 명령](##1.2. 도커 기본 명령)
- [1.3. 도커 저장소](##1.3. 도커 저장소)
- [1.4. 도커 파일 작성](##1.4. 도커 파일 작성)
- [1.5. 도커 실행 고급](##1.5. 도커 실행 고급)
- [1.6. 마치며](##1.6. 마치며)
1.1. 도커 소개
도커는 가상실행 환경을 제공해주는 오픈소스 플랫폼
컨테이너
- 가상 실행 환경
- 도커 플랫폼이 설치된 곳이면 어디서든 실행할 수 있음
도커의 장점
- 표준화: 프로세스가 어떠한 방식으로 작동하든 상관없이 동일한 형식으로 프로세스를 실행 및 관리할 수 있음
- 이식성: 어디서 어떻게 만들어졌는지 상관없이 도커 플랫폼 위에서 실행하면 동일한 실행 환경으로 프로세스 실행할 수 있음
- 가벼움: 커널을 공유해서 가벼움
- 강한 보안: 컨테이너라는 고립된 환경에서 실행됨
1.1.1. 컨테이너와 가상머신
도커는 프로세스의 실행 환경을 가상화 함
<-> 기존의 가상 환경 기술과 어떠한 차이??
컨테이너와 가상머신
- 동일
- 리소스 가상화 고립
- 실제 동작 방식에서 다름
- 가상머신
- 기존의 서버에 하이퍼바이저를 설치, 그 위에 가상 OS와 APP을 패키징한 VM을 만들어 실행함
- 하드웨어 레벨의 가상화
- 컨테이너
- 운영체제를 제외한 나머지 애플리케이션 실행에 필요한 모든 파일 패키징
- OS 레벨의 가상화
- 가상머신
컨테이너
- 게스트 OS와 하이퍼바이저 없음
- -> 오버헤드 X
- -> 가벼움
- -> 복제와 배포가 더 쉬움
1.1.2. CD 플레이어, 도커
사용자가 CD 만 들고 있으면 CD 플레이어가 설치된 어느 컴퓨터에서든지 그 CD를 재생할 수 있음
==
도커 이미지만 가지고 있으면 도커 데몬이 설치된 어느 컴퓨터에서든지 도커 프로세스를 실행할 수 있음
도커 이미지: 사용자가 실행항 코드가 들어있는 바이너리 (수정 불가)
도커파일: 도커 이미지를 생성하기 위해 필요한 문서
도커 컨테이너: 도커 이미지가 실행되는 프로세스
도커 데몬: 컨테이너가 정상적으로 실행하기 위한 실행 환경
원격 레지스트리: 도커 이미지를 저장할 수 있는 원격 저장소
1.1.3. 도커 설치
도커엔진을 직접 설치하는 것이 아닌 도커 데스크탑을 설치하고자 한다.
(도커 바이너리를 통해 클라이언트를 설치하고 dockerd라는 도커 엔진을 위한 데몬을 추가 설치할 수 있지만 공식 홈페이지에서도 도커 데스크탑을 설치하기를 권장한다.)
https://docs.docker.com/desktop/install/mac-install/
여기로 이동하면 아래와 같이 Apple silicon을 위한 Docker Desktop을 다운받을 수 있다.
설치한 뒤 실행하면 다음과 같은 화면이 나오고 스크린샷의 왼쪽 아래처럼 초록색으로 도커가 표시되면 정상적으로 설치가 된 것이다!!
1.2. 도커 기본 명령
컨테이너 하나 실행 -> 프로세스 한 개 실행!!
1.2.1. 컨테이너 실행
docker run <IMAGE>:<TAG> [<args>]
간단한 예시인 cowsay를 실행해보자.
docker run docker/whalesay cowsay 'hello world!'
귀여운 고래가 출력되었다!
로컬에 docker/whalesay라는 이미지가 없어서 자동으로 이미지를 원격 저장소에서 가져와서 실행했다.
도커 이미지 주소의 형식
<레지스트리 이름>/<이미지 이름>:<태그>
레지스트리 주소는 기본적으로 docker.io 레지스티를 사용함
- 기본 레지스트리: docker.io
- 기본 태그: latest
위 예시의 이미지 정보는 사실 다음과 같다.
docker/whalesay == docker.io/docker/whalesay:latest
nginx
- 백그라운드 실행 (-d 옵션을 사용함)
docker run -d nginx
마지막에 리턴된
- 0797ae057b9f66a58389fed89a9d36f28257b998f262c49c9e93ebb5bc55774f
이 값이 실행된 컨테이너의 고유값인 container_id
이다.
실행된 nginx의 이미지 주소는 사실 다음과 같다.
nginx == docker.io/nginx:latest
1.2.2. 컨테이너 조회
실행한 컨테이너에 대해 조회해보자.
docker ps
방금전에 nginx를 -d 옵션으로 백그라운드로 실행했기에 실행중인 컨테이너를 확인할 수 있다.
TMI
Container ID가 여기선 짧다.
아까전에 docker run -d nginx
에서의 앞 12자리만 따온것이다.
긴 ID
- 64자리의 16진수로 이루어진 컨테이너 식별자
- 충돌 가능성 거의 없음
짧은 ID
- 긴 ID의 앞 12자리를 따서 생성됨
- 충돌 가능성은 있지만 충분한 식별 용도로 사용 가능함.
짧은 ID는 일반적으로 커맨드 라인에서 사용되며, 긴 ID는 API에서 사용됩니다. 두 ID 모두 해당 컨테이너를 식별하는 데 사용될 수 있지만, 긴 ID가 보다 유일하므로 컨테이너를 고유하게 식별할 때는 긴 ID를 사용하는 것이 좋습니다.
1.2.3. 컨테이너 상세정보 확인
docker inspect <CONTAINER_ID>
컨테이너에 대한 상세한 정보들이 JSON 형태로 출력됨
포함되는 정보들
- 컨테이너의 IP 주소, 포트 매핑 정보, 컨테이너가 실행되는 호스트와 같은 네트워크 설정
- 컨테이너 또는 이미지의 이름, ID, 레이블, 태그 및 작성자와 같은 기본 정보
- 컨테이너 또는 이미지의 파일 시스템 경로, 마운트, 환경 변수 및 컨테이너의 실행 명령어 등의 설정 정보
- 컨테이너 또는 이미지의 상태, 이벤트 및 로그와 같은 실행 정보
- 볼륨 또는 네트워크의 구성 정보와 사용 중인 컨테이너 정보 등
-> 다른 명령어들과 조합으로 사용되는 경우가 있다고함
꽤 유용한 정보들이 있는 것 같기도 함
Dockerfile이 어떻게 작성되어 있는 지 유추가 가능해보임
- ENTRYPOINT는 docker-entrypoint.sh를 실행하는 것이고
- CMD는 ["nginx", "-g", "daemone off;"]
1.2.4. 컨테이너 로깅
출력되는 로그 기록 확인할 수 있음
docker logs <CONTAINER_ID>
docker logs -f 0797ae057b9f
1.2.5. 컨테이너 명령 전달
컨테이너에 명령어를 전달해야 하는 경우가 있다.
docker exec <CONTAINER_ID> <CMD>
컨테이너로 exec를 이용해 curl 명령어로 localhost에 접근해보니 GET 로그가 남고 Welcome to nginx 가 출력된다. (nginx은 기본 80번 포트)
1.2.6. 컨테이너 / 호스트간 파일 복사
실행한 컨테이너와 호스트 서버간에 파일을 주고받을 수 있다.
docker cp <HOST_PATH> <CONTAINER_ID>:<CONTAiNER_PATH>
이는 호스트의 파일을 컨테이너로 전달하는 것인데, 컨테이너에서 호스트로 가져오는 것도 가능하다.
docker cp <CONTAINER_ID>:<CONTAiNER_PATH> <HOST_PATH>
nginx의 홈페이지를 가져와 보자.
docker cp 0797ae057b9f:/usr/share/nginx/html/index.html index.html
1.2.7. 컨테이너 중단
docker stop <CONTAINER_ID>
백그라운드에서 실행중인 nginx 컨테이너 중지
docker stop 0797ae057b9f
container_id가 반환된다.
중단된 것이다. 삭제된 건 아니다. 아래 명령어로 모든 상태의 컨테이너를 조회할 수 있다.
docker ps -a
1.2.8. 컨테이너 재개
종료된 컨테이너를 다시 재시작 해보자.
docker start <CONTAINER_ID>
1.2.9. 컨테이너 삭제
중단된 상태로 두는 게 아니라 삭제하자.
docker rm <CONTAINER_ID>
중단된(stop) 상태에서만 삭제할 수 있다. 하지만 강제로 삭제할수도 있다. -f 옵션 이용
1.2.10. Interactive 컨테이너
-it 옵션을 통해 직접 컨테이너 안으로 접속하여 작업할 수도 있다.
-it는 interactive (stdin, stout 연결), tty (터미널 연결)의 약자
ubuntu 16.04 이미지를 실행하여 bash 쉘을 실행하자.
docker run -it ubuntu:16.04 bash
이미 생성한 컨테이너에 접근하기 위해선 exec 와 -it를 시용하면 된다.
docker exec -it <CONTAINER_ID> bash
1.3. 도커 저장소
도커 허브는 도커 이미지 원격 저장소
1.3.1. 도커 허브 계정 만들기
1.3.2. 이미지 tag 달기
tag 명령어로 기존의 이미지에서 새로운 이름을 부여할 수 있다.
docker tag nginx <USERNAME>/nginx
docker tag nginx ssuwani/nginx
여기서 USERNAME은 docker hub의 도커허브 계정 이름을 입력해야 한다. (도커허브에 올리려면)
기존 nginx 이미지에서 새로운 tag를 달면 docker images 명령어에서 두개의 이미지가 보인다.
1.3.3. 이미지 확인
위에서 확인했지만 같은 IMAGE_ID를 가진 것 확인하자.
1.3.4. 도커 허브 로그인
이미지 업로드를 하기 위해선 터미널에서 로그인 해야한다.
이미 로그인한 이력이 있다면 ~/.docker/config.json
에 그 정보가 저장되며 아래와 같이 자동 로그인이 된다.
1.3.5. 이미지 업로드
docker push ssuwani/nginx
그리고 https://hub.docker.com/ 에 가서 확인해보면 다음과 같이 nginx 이미지가 latest 태그와 함께 업로드 되어 있는 것을 확인할 수 있다.
1.3.6. 이미지 다운로드
앞서 사용했던 docker run
을 수행하면 로컬에 이미지가 있다면 이미지로부터 컨테이너를 실행하고 없다면 등록된 레지스트리를 찾아가며 이미지를 다운받을 뒤 컨테이너를 실행했었다.
하지만 다운로드만 받을 수도 있다.
docker pull <IMAGE_NAME>
1.3.7. 이미지 삭제
docker rmi <IMAGE_NAME>
로컬에 존재하는 이미지를 삭제하는 것이다.
로컬에 존재하는 모든 이미지를 삭제하기 위한 tip
docker rmi $(docker images -q)
1.4. 도커 파일 작성
지금까지 다른 사용자가 만든 도커 이미지를 사용했음.
나만의 도커 이미지 만들기 ><..
준비물
- Dockerfile
Dockerfile에 포함되는 내용
- Dockerfile에 기반 이미지 지정 (Base Image)
- 추가 라이브러리 설치를 위한 명령
- 컨테이너 실행 시 수행할 명령 기술
1.4.1. Dockerfile 기초
hello.py
import os
import sys
my_ver = os.environ["my_ver"]
arg = sys.argv[1]
print(f"Hello {arg}, my version is {my_ver}!")
Dockerfile
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y \
curl \
python3-dev # 책에선 python-dev로 알려주는데 이렇게 하니 python2 버전이 설치된다. (f-string 안돼서 변경)
WORKDIR /root
COPY hello.py .
ENV my_ver 1.0
CMD ["python3", "hello.py", "suwan"]
- FROM: Base Image 를 나타냄. 해당 이미지를 기반으로 새로운 도커 이미지가 생성 됨
- RUN: 사용자가 지정한 명령을 실행하는 지시자
- WORKDIR: 이미지 작업 폴더를 지정함
- COPY: 로컬 호스트에 존재하는 파일을 이미지 안으로 복사하는 지시자.
- ENV: 이미지의 환경변수 지정
- CMD: 이미지 실행 시, default로 실행되는 명령
1.4.2. 도커 빌드
docker build <PATH> -t <IMAGE_NAME>:<TAG>
작성한 Dockerfile을 이미지로 변환하기 위한 명령어
PATH는 도커 빌드를 수행할 위치를 말함. (책에선 Dockerfile을 위치라고 말했는데,,, 아닌거 같은데,, 흠 ,, )
이제 아래 조건으로 빌드하자.
- PATH:
.
(현재 디렉토리) - IMAGE_NAME:
hello
- TAG:
1
docker build . -t hello:1
이제 실행해보자. (두번째 예제부터 cmd가 override 되는 점!!!!)
> docker run hello:1
Hello suwan, my version is 1.0!
> docker run hello:1 echo "hello world"
hello world
> docker run hello:1 cat hello.py
import os
import sys
my_ver = os.environ["my_ver"]
arg = sys.argv[1]
print(f"Hello {arg}, my version is {my_ver}!")
> docker run hello:1 pwd
/root
환경변수 주입 (-e 옵션)
> docker run -e my_ver=1.5 hello:1
# Hello suwan, my version is 1.5!
1.4.3. Dockerfile 심화
ARG
Dockerfile 안에서 사용할 수 있는 매개변수. (생성되는 이미지 내용 변경 가능)
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
python3-dev
ARG my_ver=1.0 # 추가
WORKDIR /root
COPY hello.py .
ENV my_ver $my_ver # 변경
CMD ["python3", "hello.py", "suwan"]
이미지 빌드 시 --build-arg 옵션을 이용해 ARG 값을 덮어씌울 수 있음
> docker build . -t hello:2 --build-arg my_ver=2.0
> docker run hello:2
# Hello suwan, my version is 2.0!
컨테이너 실행 시 환경변수 변경은 여전히 가능함!!
> docker run -e my_ver=2.5 hello:2
# Hello suwan, my version is 2.5!
ENTRYPOINT
CMD와 유사함! 실행 명령이 override 되지 않고 실행
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
python3-dev
WORKDIR /root
COPY hello.py .
ENV my_ver 1.0
ENTRYPOINT ["python3", "hello.py", "suwan"] # 변경
실행
> docker build . -t hello:3
> docker run hello:3
Hello suwan, my version is 1.0!
> docker run hello:3 echo "hello"
Hello suwan, my version is 1.0! # 위 결과와 동일함
이미지 자체가 실행 가능한 바이너리 처럼 바뀌었음.
docker run hello:3 echo "hello"
에서 echo
와 "hello"
는 각각 argv[2], argv[3] 으로 접근 가능하다.
args 를 동적으로 받기위한 Dockerfile 재정의
FROM ubuntu:18.04
RUN apt-get update \
&& apt-get install -y curl \
python3-dev
WORKDIR /root
COPY hello.py .
ENV my_ver 1.0
ENTRYPOINT ["python3", "hello.py"] # 변경
suwan
을 argument로 전달
> docker run hello:4 suwan
Hello suwan, my version is 1.0!
마치 python3 hello.py suwan 이 호출된 것처럼 동작함
CMD와 ENTRYPOINT의 차이점
- CMD는 default command
- ENTRYPOINT는 이미지를 실행가능한 바이너리로 만들어주는 지시자.
1.5. 도커 실행 고급
1.5.1. Network
docker run -p <HOST_PORT>:<CONTAINER_PORT> <IMAGE_NAME>
외부 트래픽을 컨테이너 내부로 전달하기 위해 로컬 호스트 서버와 컨테이너의 포트를 매핑시켜 트래픽을 포워딩 함
- 호스트 5001번 포트와 컨테이너의 80번 포트 매핑
docker run -p 5001:80 -d nginx
# curl localhost:5001
1.5.2. Volume
docker run -v <HOST_DIR>:<CONTAINER_DIR> <IMAGE_NAME>
컨테이너는 휘발성 프로세스이므로 삭제되면 내부의 데이터는 모두 삭제됨
-> 지속적으로 데이터를 보관하기 위해 볼륨 사용
-> 로컬 호스트와 컨테이너를 연결
docker run -p 5001:80 -v $(pwd):/usr/share/nginx/html -d nginx
위 명령어는 현재 명령어를 실행하는 디렉토리와 컨테이너의 /usr/share/nginx/html
를 연결하는 것이다.
1.5.3. Entrypint
앞서 ENTRYPOINT는 파라미터 전달 시 override 되지 않는다고 하였음
하지만, --entrypoint
옵션으로 강제로 override 할 수 있음
기존 예시 (ENTRYPOINT를 사용했었음)
> docker run hello:3 echo "hello world"
Hello suwan, my version is 1.0! # override 되지 않았었음
--entrypoint
를 사용해 override
> docker run --entrypoint echo hello:3 "hello world"
hello world
entrypoint는 echo로 하고 cmd가 "hello world"가 들어간 것임
1.5.4. User
기본적으로 컨테이너의 유저는 root
-> 일반유저를 사용하게 할 수도 있음
FROM ubuntu:18.04
RUN adduser --disabled-password --gecos "" ubuntu
USER ubuntu
build
docker build -t my-user .
docker run -it my-user bash
일반적으로 root 유저면 apt update가 되는데 ubuntu user에는 특정 권한을 주지 않았기 때문에 apt update가 되지 않음
하지만 이를 강제하는 건 아님. 컨테이너 생성 시 --user
옵션으로 실행할 유저를 선택할 수 있음
docker run --user root -it my-ver bash
1.6. 마치며
쿠버네티스
- 이러한 도커의 특징을 잘 이용함
- 여러대의 서버
- 여러개의 컨테이너
- 잘 실행!!