본문은 Docker 클라이언트 구현 시퀀스의 일환으로, 컨테이너를 이미지로 변환하는 기능을 구현하는 방법을 다룹니다. 이 기능은 실제 Docker CLI의 commit 명령어와 유사한 동작을 수행합니다.
전체 코드는 다음 링크에서 확인 가능합니다: https://github.com/lixd/mydocker 별점 부탁드립니다
Docker 기초 원리에 대한 이해를 돕기 위한 참고 자료:
- 내부 메커니즘: 네임스페이스, 콘그루스, 루트 파일시스템 등 Docker 핵심 구성 요소
- 네임스페이스 기반 격리: Linux 네임스페이스 활용한 컨테이너 격리 원리
- 리소스 제한: 콘그루스 기반 리소스 제어 및 관리
- 파일시스템: OverlayFS를 통한 유니온 파일시스템 구조
- 네트워크: veth pair 및 브릿지 기반 네트워크 구현 방식
개발 환경 정보:
root@mydocker:~# lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 20.04.2 LTS
Release: 20.04
Codename: focal
root@mydocker:~# uname -r
5.4.0-74-generic
주의: root 권한이 필요합니다
- 개요
이전 단계에서 pivotRoot와 OverlayFS 기술을 사용하여 /root/merged 디렉토리를 컨테이너 루트 파일시스템으로 설정했습니다. 이로 인해 컨테이너 내에서 발생한 모든 변경 사항은 해당 디렉토리에 반영됩니다.
따라서 mydocker commit 명령어는 해당 디렉토리를 간단히 타르 압축하여 저장하는 방식으로 구현할 수 있습니다
- 구현
실행 흐름
전체 패키징 프로세스는 다음과 같은 순서로 진행됩니다:
커밋 명령 처리
main_command.go 파일에서 commit 명령을 추가하고, 사용자 입력에서 이미지 이름을 추출합니다.
var commitCommand = cli.Command{
Name: "commit",
Usage: "컨테이너를 이미지로 변환",
Action: func(context *cli.Context) error {
if len(context.Args()) < 1 {
return fmt.Errorf("이미지 이름 누락")
}
imageName := context.Args().Get(0)
saveContainerImage(imageName)
return nil
},
}
메인 함수에 커밋 명령을 등록합니다:
func main() {
app := cli.NewApp()
app.Name = "mydocker"
app.Usage = usage
app.Commands = []cli.Command{
initCommand,
runCommand,
commitCommand,
}
// 기타 설정 생략
}
컨테이너 이미지 저장
commit.go 파일에 saveContainerImage 함수를 추가하여 컨테이너 파일시스템을 압축합니다.
func saveContainerImage(imageName string) {
rootfsPath := "/root/merged"
imageArchivePath := "/root/" + imageName + ".tar"
fmt.Printf("이미지 저장 경로: %s\n", imageArchivePath)
cmd := exec.Command("tar", "-czf", imageArchivePath, "-C", rootfsPath, ".")
if err := cmd.Run(); err != nil {
log.Errorf("디렉토리 압축 실패: %v", err)
}
}
- 테스트
테스트 절차는 다음과 같습니다:
- 컨테이너 실행
- 파일 생성
- 새로운 터미널에서 컨테이너를 이미지로 변환
- 압축 파일 해제 후 생성된 파일 존재 여부 확인
먼저 컨테이너를 실행합니다:
root@mydocker:~/feat-commit/mydocker# ./mydocker run -it /bin/sh
{"level":"info","msg":"resConf:\u0026{ 0 }","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"busybox:/root/busybox busybox.tar:/root/busybox.tar","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"mount overlayfs: [/usr/bin/mount -t overlay overlay -o lowerdir=/root/busybox,upperdir=/root/upper,workdir=/root/work /root/merged]","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"command all is /bin/sh","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"init come on","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"Current location is /root/merged","time":"2024-01-19T16:18:24+08:00"}
{"level":"info","msg":"Find path /bin/sh","time":"2024-01-19T16:18:24+08:00"}
파일을 생성합니다:
/ # echo KubeExplorer > tmp/hello.txt
/ # cat /tmp/hello.txt
KubeExplorer
다른 터미널에서 커밋 명령을 실행합니다:
root@mydocker:~/feat-commit/mydocker# ./mydocker commit myimage
이미지 저장 경로: /root/myimage.tar
/root 디렉토리 상태 확인:
root@mydocker:~# ls
busybox busybox.tar merged myimage.tar upper volume work
압축 파일 내용 확인:
root@mydocker:~# tar -tf myimage.tar |grep hello.txt
./tmp/hello.txt
- 요약
본 기능은 타르 명령을 이용한 간단한 루트 파일시스템 압축만 수행하며, 복잡한 로직은 포함하지 않습니다.
이번 단계에서는 다음과 같은 핵심 내용을 정리할 수 있습니다:
- 기본 이미지로 BusyBox를 사용해 컨테이너 환경을 구축
- OverlayFS를 활용한 계층적 파일시스템 구조 설명
- 볼륨 마운팅을 통한 외부 파일시스템 접근 방법
- 컨테이너 이미지 생성 기능 구현
이번 챕터에서는 이미지 저장 및 파일시스템 원리를 기초적으로 소개했으며, 이후 다양한 확장 기능 구현에 기반이 됩니다.
[0에서 시작하는 Docker 시리즈] 지속 업데이트 중입니다. 검색창에서 【탐구 클라우드 네이티브】를 검색하여 더 많은 글을 읽을 수 있습니다.
전체 코드: https://github.com/lixd/mydocker 별점 부탁드립니다
관련 코드는 feat-volume 분기에서 확인 가능합니다. 테스트 스크립트:
# 코드 클론
git clone -b feat-commit https://github.com/lixd/mydocker.git
cd mydocker
# 의존성 설치 및 컴파일
go mod tidy
go build .
# 테스트 실행
./mydocker run -it /bin/ls
./mydocker commit