docker
도커는 컨테이너 기반 가상화 기술을 활용하여, 호스트 시스템의 커널을 공유하면서 애플리케이션을 실행하는데 필요한 모든 라이브러리 및 종속성을 포함합니다. 이는 가볍고 빠른 실행이 가능하며, 확장성과 유연성이 좋으며, 인프라 구성 관리가 용이하다는 특징을 가지고 있습니다. 최근 웹 프로젝트를 진행하면서, Git Action과 함께 도커를 활용하여 통합 및 배포에 활용하고자 합니다. 이번 포스트에서는 도커에 대해 공부한 내용을 정리하려고 합니다. ‘시작하세요 도커&쿠버네틱스’라는 책을 읽고 학습했습니다.
도커 이미지와 컨테이너
도커 이미지는 여러 계층(layer)으로 구성된 바이너리 파일이며, 컨테이너를 생성하고 실행할 때 읽기 전용으로 사용됩니다.
도커 이미지는 UnionFS(OverlayFS, AUFS 등)를 기반으로 계층화된 구조를 가지며, 새로운 이미지가 생성될 때 변경된 부분만 새로운 계층으로 추가됩니다. 기존의 계층은 변경되지 않은 채 유지되며, 컨테이너는 이러한 계층을 읽기 전용으로 사용합니다. 그래서, 이미지를 삭제할 때, 하위(자식) 계층의 이미지가 존재한다면, 해당 이미지는 untag 상태가 되며, 이름만 삭제됩니다(댕글리 이미지). 하위 계층의 이미지가 삭제되고 나서야 untaged 이미지가 삭제됩니다. 또한, 해당 이미지로 생성된 컨테이너가 있다면, 컨테이너가 해당 이미지를 참조하기 때문에 삭제가 이루어지지 않습니다. force 옵션으로 삭제해도 해당 이미지는 실제로 삭제되지 않습니다.
도커의 계층화된 이미지 관리 방식은 기본적으로 레이어(layer) 기반의 이미지 스냅샷 방식에 가깝습니다. 즉, 특정 계층을 변경하면 새로운 계층이 추가되고, 기존 계층은 그대로 유지됩니다.
도커 이미지의 이름 형식은 다음과 같습니다. [저장소 이름]/[이미지 이름]:[태그]
저장소 이름(repository name)
은 생략할 수 있으며, 생략 시 Docker Hub의 공식 이미지를 가리킵니다.태그(tag)
는 이미지의 버전을 나타내며, 생략할 경우 기본적으로latest
로 설정됩니다.이미지 이름(image name)
은 반드시 포함되어야 합니다.
컨테이너 실행
컨테이너는 기본적으로 독립적인 프로세스를 실행하는 환경이며, 실행 중인 프로세스가 종료되면 컨테이너도 종료됩니다.
컨테이너의 기본 프로세스(PID 1)
컨테이너 내부에서 실행되는 기본 프로세스(PID 1)가 종료되면 컨테이너는 중지됩니다.
- 단일 프로세스 컨테이너(single process container): 실행된 애플리케이션이 종료되면 컨테이너도 자동으로 종료됨.
- 멀티 프로세스 컨테이너:
docker exec
등을 이용해 추가적인 프로세스를 실행할 수 있지만, 기본 프로세스가 종료되면 컨테이너는 종료됨. - 백그라운드 실행:
d
옵션을 사용하여 백그라운드에서 실행 가능하지만,ENTRYPOINT
또는CMD
로 지정된 프로세스가 종료되면 컨테이너는 중지됨.
하나의 컨테이너에서 여러 개의 프로세스를 실행하는 것은 권장되지 않으며, 일반적으로 하나의 컨테이너는 하나의 주요 프로세스를 실행하는 것이 바람직합니다.
컨테이너 로그 확인
컨테이너의 실행 중 로그를 확인하기 위해서는 docker logs
명령어를 사용할 수 있습니다.
docker logs <컨테이너 이름 또는 ID>
도커 로그 드라이버
도커는 컨테이너의 로그를 다양한 로그 드라이버를 사용하여 저장할 수 있습니다. 기본적으로 json-file
로그 드라이버가 사용되지만, syslog
, journald
, awslogs
, fluentd
등의 로그 드라이버를 사용할 수도 있습니다.
- syslog:
syslog
는 UNIX/Linux 시스템의 표준 로그 관리 시스템으로, 컨테이너의 로그를/var/log/syslog
등에 기록할 수 있습니다.docker run --log-driver=syslog
옵션을 사용하면 도커 컨테이너의 로그를syslog
에 저장할 수 있습니다.
- rsyslog:
syslog
의 확장판으로, 네트워크를 통한 원격 로그 전송 기능이 향상되었습니다.- 컨테이너 로그를 중앙 집중식 로그 서버에 저장하려면
rsyslog
를 설정해야 합니다. - 예를 들어,
/etc/rsyslog.conf
를 수정하여 원격 로그 서버로 전송할 수 있습니다.
도커 볼륨
도커 볼륨을 활용하는 방법은 다음과 같습니다.
v
옵션을 사용하여 호스트 디렉터리를 컨테이너 디렉터리에 마운트-volumes-from
옵션을 사용하여 다른 컨테이너와 볼륨을 공유docker volume create
명령어를 사용하여 독립적인 볼륨을 생성
docker volume create my_volume
docker run -v my_volume:/app/data my_ima
볼륨의 특징
- 호스트 파일 시스템과 독립적으로 관리되며, 컨테이너가 삭제되더라도 볼륨 데이터는 유지됨.
docker volume ls
명령어로 생성된 볼륨을 확인할 수 있음.docker volume rm <볼륨명>
명령어로 볼륨을 삭제할 수 있음.
도커 네트워크
도커 컨테이너는 기본적으로 브릿지(bridge) 네트워크를 사용하여 호스트와 통신합니다.
- 컨테이너의 eth0 인터페이스는 호스트의
vethXXX
인터페이스와 연결됨. vethXXX
인터페이스는 docker0 브리지와 바인딩됨.- docker0 브리지는 외부 네트워크와의 연결을 담당함.
브릿지 하나는 하나의 서브넷을 의미합니다.
도커 네트워크 드라이버
bridge
: 기본 네트워크로, 컨테이너 간 내부 통신이 가능하며 포트 매핑을 통해 외부와 통신 가능.host
: 컨테이너가 호스트의 네트워크 스택을 공유하여, 별도의 네트워크 인터페이스 없이 실행됨.none
: 네트워크를 비활성화하여, 외부와의 통신이 불가능한 상태로 실행됨.
도커 이미지 생성 및 배포
이미지 빌드
docker commit <컨테이너 ID> <도커 허브 계정>/<저장소 이름>:<태그>
이미지 배포
save나 export와 길은 방법으로 이미지를 단일 파일로 추출해서 배포할 수도 있지만 이미지 파일의 크기가 너무 크거나 도커 엔진의 수가 많다면 이미지를 파일로 배포하기 어렵습니다. 그래서, 대부분 도커 허브를 이용하거나 도커 사설 레지스트리를 이용합니다. 생성된 도커 이미지를 도커 허브에 push해서 필요할 때마다 pull해서 사용할 수 있습니다.
도커 허브는 중앙 이미지 저장소를 말하며, 도커가 공식적으로 제공하고 있는 이미지 저장소로서,도커 계정을 가지고 있다면 누구든지 이미지를 올리고 내려받을 수 있기 때문에 다른 사람들에게 이미지를 쉽게 공유할 수 있습니다. 도커 허브 저장소를 이용할 때는 주의할 점이 있습니다. 도커 허브에 이미지를 저장할 저장소의 이름이 배포할 이미지의 [저장소 이름]/[이미지 이름]과 같아야 합니다
도커 허브에 이미지를 업로드하려면 저장소 이름과 로컬 이미지 이름을 일치시켜야 합니다.
docker image tag <기존의 이미지명>:<기존의 태그명> <새로운 이미지명>:<새로운 태그명>
docker push <도커 허브 계정>/<저장소 이름>:<태그>
docker tag를 사용하여 도커 허브 저장소와 로컬 이미지의 이름을 맞춰야 합니다.
Dockerfile을 사용한 빌드
도커 이미지를 생성하는 방법은 기본 컨테이너를 만들어서 패키지를 설치하고 소스를 깃(Git)에서 복제하거나 호스트에서 복사해서 컴파일 및 빌드하는 등의 작업을 가집니다. 이러한 작업은 일일이 수작업으로 해야하고, 만들어진 이미지가 어떤 방법으로 만들어 졌는지 알기 힘듭니다. 그래서, 도커 이미지를 효율적으로 관리하려면 Dockerfile을 사용하여 이미지를 빌드하는 것이 좋습니다. 이 과정에서 도커 엔진은 이미지와 컨테이너가 생성되었다가 삭제되는 과정을 반복합니다. 또한, Dockerfile이 있는 디렉터리가 build 명령어가 실행될 때의 현재 디렉터리(빌드 컨텍스트)가 됩니다.
Dockerfile 예제
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y curl
COPY ./app /app
CMD ["/app/start.sh"]
이미지 빌드 및 실
docker build -t my_image .
docker run my_image
멀티스테이지 빌드
일반적으로 애플리케이션을 빌드할 때는 많은 의존성 패키지와 라이브러리를 필요로 합니다. 그래서, 간단한 소스 코드를 컴파일하고 실행하는 이미지라도 생성된 이미지의 크기가 큽니다. 이를 해결하기 위해 도커 엔진에서는 이미지의 크기를 줄이기 위해 멀티 스테이지(Multistage) 빌드 방법을 사용할 수 있습니다.
FROM golang:alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o app main.go
FROM alpine
WORKDIR /app
COPY --from=builder /app/app .
CMD ["./app"]
멀티스테이지 빌드는 최종 이미지를 작게 유지하는 데 도움을 줍니다.
Docker Daemon
도커는 클라이언트-서버 구조로 동작합니다.
/usr/bin/docker
클라이언트는/var/run/docker.sock
을 통해 도커 데몬과 통신합니다.- 원격 API를 사용하면 외부 서버에서도 도커 컨테이너를 제어할 수 있습니다.
- curl을 사용해서 원격 도커 데몬에 api를 요청할 수 있습니다.
dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
# -H tcp://0.0.0.0:2375 → 모든 IP에서 접속 가능하도록 설정
# -H unix:///var/run/docker.sock → 기존 로컬 소켓도 유지
curl http://<서버 IP>:2375/version