[Container] Finch - Docker Desktop 대체 오픈소스 컨테이너 도구 사용법
Finch 란?
Finch는 AWS에서 개발한 오픈소스 컨테이너 개발 도구입니다. macOS / Windows / Linux 환경에서 Docker Desktop의 대안으로 사용할 수 있으며, containerd와 nerdctl을 기반으로 동작합니다. 라이선스 비용 없이 로컬 환경에서 컨테이너 이미지를 빌드, 실행, 배포할 수 있어 개인 개발자나 소규모 팀에게 유용한 도구입니다.
이번 포스팅에서는 Finch의 기본 사용법에 대해 작성하도록 하겠습니다.
테스트 환경
- 테스트 환경: macOS (Apple Silicon / ARM64)
- Finch Server Version: v2.1.3
- VM Kernel: 6.12.53-69.119.amzn2023.aarch64
- VM Operating System: Fedora Linux 42 (Cloud Edition)
Finch VM 시작
Finch는 Linux 컨테이너를 실행하기 위해 내부적으로 경량 VM(Lima)을 사용합니다. VM이 중지된 상태에서 명령어를 실행하면 아래와 같은 에러가 발생합니다.
$ finch stats
FATA[0000] instance "finch" is stopped, run `finch vm start` to start the instance
아래와 같이 VM을 시작합니다.
$ finch vm start
INFO[0000] Starting existing Finch virtual machine...
INFO[0037] Finch virtual machine started successfully
위와 같이 Finch VM이 정상적으로 시작된 것을 확인 할 수 있습니다.
컨테이너 이미지 Pull
아래와 같이 Docker Hub에서 httpd 이미지를 가져옵니다.
$ finch pull httpd
docker.io/library/httpd:latest: resolved |++++++++++++++++++++++++++++++++++++++|
index-sha256:dd178595edd6d4f49296f62f9587238db2cd1045adfff6fccc15a6c4d08f5d2e: done |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:d455e832e73abe8385397a4942fbb118a0ad7bdc37a5c695c2bce15f88bd8c5e: done |++++++++++++++++++++++++++++++++++++++|
config-sha256:3bd0a578c1bd6b6cb48eaddd1c8daaba6f1362fc792a4a1f9618275b20ed5314: done |++++++++++++++++++++++++++++++++++++++|
elapsed: 1.5 s total: 0.0 B (0.0 B/s)
컨테이너 실행
아래와 같이 httpd 컨테이너를 백그라운드로 실행합니다.
$ finch container run --name my-httpd -d -p 8080:80 httpd
38f1c0971d8b95cfabb27fb051979a75bafcf7ed0ac833dc69d03de2649cb620
실행 중인 컨테이너를 확인합니다.
$ finch ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
38f1c0971d8b docker.io/library/httpd:latest "httpd-foreground" 11 seconds ago Up 0.0.0.0:8080->80/tcp my-httpd
위와 같이 my-httpd 컨테이너가 정상적으로 실행 중인 것을 확인 할 수 있습니다.
컨테이너 동작 확인
아래와 같이 curl 명령어로 웹 서버가 정상 동작하는지 테스트합니다.
$ curl localhost:8080
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>It works! Apache httpd</title>
</head>
<body>
<p>It works!</p>
</body>
</html>
위와 같이 Apache httpd 웹 서버가 정상적으로 응답하는 것을 확인 할 수 있습니다.
컨테이너 이미지 빌드
이번 섹션에서는 Finch를 사용하여 직접 컨테이너 이미지를 빌드하는 방법에 대해 기록하도록 하겠습니다.
샘플 애플리케이션 준비
간단한 Python Flask 웹 애플리케이션을 예제로 사용합니다. 아래와 같이 프로젝트 디렉토리를 생성하고 파일을 준비합니다.
$ mkdir finch-demo && cd finch-demo
아래와 같이 Flask 애플리케이션 파일을 작성합니다.
$ vi app.py
from flask import Flask
import os
app = Flask(__name__)
@app.route('/')
def hello():
hostname = os.uname().nodename
return f'''
<!DOCTYPE html>
<html>
<head>
<title>Finch Demo App</title>
</head>
<body>
<h1>Hello from Finch!</h1>
<p>Container Hostname: {hostname}</p>
<p>Built with Finch - Open Source Container Tool</p>
</body>
</html>
'''
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
다음으로 의존성 파일을 작성합니다.
$ vi requirements.txt
flask==3.0.0
Containerfile 작성
아래와 같이 Containerfile을 작성합니다. Finch는 Dockerfile과 Containerfile 모두 지원합니다.
$ vi Containerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
프로젝트 구조는 아래와 같습니다.
$ ls -l
total 24
-rw-r--r--@ 1 cheolhee staff 434 Jan 20 13:38 app.py
-rw-r--r--@ 1 cheolhee staff 166 Jan 20 13:38 Containerfile
-rw-r--r--@ 1 cheolhee staff 13 Jan 20 13:38 requirements.txt
이미지 빌드
아래와 같이 finch build 명령어를 사용하여 이미지를 빌드합니다.
$ finch build -t finch-demo:v1.0 .
[+] Building 9.0s (10/10) FINISHED
=> [internal] load build definition from Containerfile 0.0s
=> => transferring dockerfile: 208B 0.0s
=> [internal] load metadata for docker.io/library/python:3.11-slim 2.7s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/5] FROM docker.io/library/python:3.11-slim@sha256:5be45dbade29bebd6886af6b438fd7e0b4eb7b611f39ba62b430263f82de36d2 3.5s
=> => resolve docker.io/library/python:3.11-slim@sha256:5be45dbade29bebd6886af6b438fd7e0b4eb7b611f39ba62b430263f82de36d2 0.0s
=> => sha256:518abaec573aacfc293edf9e19b26274fc7ee5d3448e80f90eecdae829acd7e7 249B / 249B 0.5s
=> => sha256:068949646e5ac13b4b3f99bca3f1249ca853fc5bd30512ea7d447af7a649b011 14.31MB / 14.31MB 1.8s
=> => sha256:d67bedf18e0abdffd887db745e6a45c9173ae1b93fd7fac8d7a8c6b2e5346455 1.27MB / 1.27MB 1.7s
=> => sha256:d637807aba98f742a62ad9b0146579ceb0297a3c831f56b2361664b7f5fbc75b 30.13MB / 30.13MB 2.8s
=> => extracting sha256:d637807aba98f742a62ad9b0146579ceb0297a3c831f56b2361664b7f5fbc75b 0.4s
=> => extracting sha256:d67bedf18e0abdffd887db745e6a45c9173ae1b93fd7fac8d7a8c6b2e5346455 0.0s
=> => extracting sha256:068949646e5ac13b4b3f99bca3f1249ca853fc5bd30512ea7d447af7a649b011 0.2s
=> => extracting sha256:518abaec573aacfc293edf9e19b26274fc7ee5d3448e80f90eecdae829acd7e7 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 527B 0.0s
=> [2/5] WORKDIR /app 0.1s
=> [3/5] COPY requirements.txt . 0.0s
=> [4/5] RUN pip install --no-cache-dir -r requirements.txt 2.0s
=> [5/5] COPY app.py . 0.0s
=> exporting to image 0.6s
=> => exporting layers 0.5s
=> => exporting manifest sha256:e045216b9a5483b26e5c9b16630913ce1c2b35b279158af02bae3d0a0080176d 0.0s
=> => exporting config sha256:8c89f20c21344cbd96815ff7340b7e8f52784e4ab67b09b6239efd3c40fcb443 0.0s
=> => naming to docker.io/library/finch-demo:v1.0 0.0s
=> => unpacking to docker.io/library/finch-demo:v1.0 0.1s
위와 같이 이미지 빌드가 완료된 것을 확인 할 수 있습니다.
빌드된 이미지 확인
아래와 같이 빌드된 이미지를 확인합니다.
$ finch images
REPOSITORY TAG IMAGE ID CREATED PLATFORM SIZE BLOB SIZE
finch-demo v1.0 e045216b9a54 11 seconds ago linux/arm64 184.4MB 51.4MB
jekyll/jekyll latest 400b8d1569f1 21 hours ago linux/amd64 907.3MB 322.1MB
위와 같이 finch-demo:v1.0 이미지가 정상적으로 빌드된 것을 확인 할 수 있습니다.
빌드한 이미지로 컨테이너 실행
아래와 같이 빌드한 이미지를 사용하여 컨테이너를 실행합니다.
$ finch run --name demo-app -d -p 5000:5000 finch-demo:v1.0
acdbff628decca37867d875494c9fd9615df724ded3205bd72d8377761905652
실행 중인 컨테이너를 확인합니다.
$ finch ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
acdbff628dec docker.io/library/finch-demo:v1.0 "python app.py" 9 seconds ago Up 0.0.0.0:5000->5000/tcp demo-app
동작 확인
아래와 같이 curl 명령어로 애플리케이션이 정상 동작하는지 확인합니다.
$ curl localhost:5000
<!DOCTYPE html>
<html>
<head>
<title>Finch Demo App</title>
</head>
<body>
<h1>Hello from Finch!</h1>
<p>Container Hostname: acdbff628dec</p>
<p>Built with Finch - Open Source Container Tool</p>
</body>
</html>
위와 같이 Finch로 빌드한 Flask 애플리케이션이 정상적으로 동작하는 것을 확인 할 수 있습니다.
컨테이너 관리 명령어
컨테이너 삭제
아래 명령어로 실행 중인 컨테이너를 강제 삭제할 수 있습니다.
$ finch container rm -f my-httpd
my-httpd
컨테이너 재시작
$ finch container restart blog
blog
컨테이너 로그 확인
-f 옵션을 사용하여 실시간 로그를 확인할 수 있습니다.
$ finch logs -f blog
Fetching gem metadata from https://rubygems.org/..........
Using bundler 2.3.25
Fetching public_suffix 5.0.3
Using colorator 1.1.0
Fetching concurrent-ruby 1.2.2
Installing public_suffix 5.0.3
Installing concurrent-ruby 1.2.2
Using eventmachine 1.2.7
....
이미지 관리
이미지 목록 확인
$ finch images
REPOSITORY TAG IMAGE ID CREATED PLATFORM SIZE BLOB SIZE
httpd latest dd178595edd6 2 minutes ago linux/arm64 159.2MB 45.5MB
이미지 삭제
$ finch rmi dd178595edd6
Untagged: docker.io/library/httpd:latest@sha256:dd178595edd6d4f49296f62f9587238db2cd1045adfff6fccc15a6c4d08f5d2e
Deleted: sha256:37127a0fa4c7aa2f661f5d88d5e0b6194640848159d7a56c5c0b7afb445aa6fe
Deleted: sha256:e2be4c959abac10d36314c871065c3ddeb6b1b76cfebd5bce06a05a5dde1d68e
Deleted: sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef
Deleted: sha256:0477352d89d2859cc39547b0769fc84ad80cb2fa92caca62531acef033043ea8
Deleted: sha256:f025424b0a4653e7ecc10f1d1f8a35458ae674db8c1f01df825a9b74808621b3
Deleted: sha256:3958c2a874d9078e3c5663b3a82cd3d9b87f2c19c9bdf2f720f6b9902c475eb2
Finch Compose를 이용한 멀티 컨테이너 실행
Finch는 Docker Compose와 호환되는 finch compose 명령어를 제공합니다. 이번 섹션에서는 Finch Compose를 사용하여 여러 컨테이너를 함께 실행하는 방법에 대해 기록하도록 하겠습니다.
샘플 애플리케이션 준비
Flask 웹 애플리케이션과 Redis를 연동하는 간단한 방문자 카운터 예제를 사용합니다. 아래와 같이 프로젝트 디렉토리를 생성합니다.
$ mkdir finch-demo && cd finch-demo
아래와 같이 Flask 애플리케이션 파일을 작성합니다.
$ vi app.py
from flask import Flask
import redis
import os
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
@app.route('/')
def hello():
count = get_hit_count()
hostname = os.uname().nodename
return f'''
<!DOCTYPE html>
<html>
<head>
<title>Finch Compose Demo</title>
</head>
<body>
<h1>Hello from Finch Compose!</h1>
<p>Container Hostname: {hostname}</p>
<p>Visit Count: {count}</p>
</body>
</html>
'''
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
다음으로 의존성 파일을 작성합니다.
$ vi requirements.txt
flask==3.0.0
redis==5.0.1
Containerfile 작성
아래와 같이 Containerfile을 작성합니다.
$ vi Containerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 5000
CMD ["python", "app.py"]
Compose 파일 작성
아래와 같이 compose.yaml 파일을 작성합니다. Finch는 Docker Compose 파일 형식과 호환됩니다.
$ vi compose.yaml
services:
web:
build: .
ports:
- "5000:5000"
depends_on:
- redis
restart: unless-stopped
redis:
image: redis:alpine
volumes:
- redis-data:/data
restart: unless-stopped
volumes:
redis-data:
프로젝트 구조는 아래와 같습니다.
$ ls -l
total 32
-rw-r--r--@ 1 cheolhee staff 748 Jan 20 13:43 app.py
-rw-r--r--@ 1 cheolhee staff 243 Jan 20 13:44 compose.yaml
-rw-r--r--@ 1 cheolhee staff 166 Jan 20 13:44 Containerfile
-rw-r--r--@ 1 cheolhee staff 26 Jan 20 13:43 requirements.txt
Compose로 컨테이너 실행
아래와 같이 finch compose up 명령어를 사용하여 멀티 컨테이너 환경을 실행합니다.
$ finch compose up -d
INFO[0000] Creating network finch-demo_default
INFO[0000] Creating volume finch-demo_redis-data
WARN[0000] Ignoring: service web: depends_on: redis: [Required]
INFO[0000] Ensuring image redis:alpine
docker.io/library/redis:alpine: resolved |++++++++++++++++++++++++++++++++++++++|
index-sha256:6cbef353e480a8a6e7f10ec545f13d7d3fa85a212cdcc5ffaf5a1c818b9d3798: done |++++++++++++++++++++++++++++++++++++++|
manifest-sha256:1023a5ac993a92700157e724a9aa077428b9f5d1ef54f0ab1aa5e74371eab6c3: done |++++++++++++++++++++++++++++++++++++++|
config-sha256:733d545e87b547f001d2a7965cb668561dd4e92751f84c519c8b6892c4162961: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:25e9d7d40e446acd1c87bc032302c12d6f69eb8ee0006879fb2be002ac7278db: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:ce3e85348d1524ee75ee16ea693b051e47a77cf7bfc2fd5fb21474f22f1d7ee6: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:b0abe8c185e79f4788817e25569e9b70ecb3d192996c09a18290cbb77f51b923: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:4e748fe7e898af4bba0af4ecc768150ae7883bf856f039c2c1051c47bb032a61: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:6b59a28fa20117e6048ad0616b8d8c901877ef15ff4c7f18db04e4f01f43bc39: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:d8d7a3e38cc296564e247ed12159d54a7ecdd0725b4664fe6d4f70420fa41523: done |++++++++++++++++++++++++++++++++++++++|
layer-sha256:4f4fb700ef54461cfa02571ae0db9a0dc1e0cdb5577484a6d75e68dc38e8acc1: done |++++++++++++++++++++++++++++++++++++++|
elapsed: 6.9 s total: 31.7 M (4.6 MiB/s)
INFO[0006] Building image finch-demo-web
[+] Building 4.1s (10/10) FINISHED
=> [internal] load build definition from Containerfile 0.0s
=> => transferring dockerfile: 208B 0.0s
=> [internal] load metadata for docker.io/library/python:3.11-slim 1.3s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/5] FROM docker.io/library/python:3.11-slim@sha256:5be45dbade29bebd6886af6b438fd7e0b4eb7b611f39ba62b430263f82de36d2 0.0s
=> => resolve docker.io/library/python:3.11-slim@sha256:5be45dbade29bebd6886af6b438fd7e0b4eb7b611f39ba62b430263f82de36d2 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 854B 0.0s
=> CACHED [2/5] WORKDIR /app 0.0s
=> [3/5] COPY requirements.txt . 0.1s
=> [4/5] RUN pip install --no-cache-dir -r requirements.txt 2.1s
=> [5/5] COPY app.py . 0.0s
=> exporting to image 0.7s
=> => exporting layers 0.5s
=> => exporting manifest sha256:26a0db072b8e9c4fe1959e74a5ffbdd8022032094190a7546e928c581f4efa5a 0.0s
=> => exporting config sha256:9583b0d9807ddc747aa3b5203bafc124edad0a624b89a3e3fa1b7a02a5de6145 0.0s
=> => naming to docker.io/library/finch-demo-web:latest 0.0s
=> => unpacking to docker.io/library/finch-demo-web:latest 0.1s
INFO[0011] Creating container finch-demo-redis-1
INFO[0011] Running [/usr/local/bin/nerdctl run --cidfile=/tmp/compose-3033891241/cid -l=com.docker.compose.project=finch-demo -l=com.docker.compose.service=redis -d --name=finch-demo-redis-1 --pull=never --net=finch-demo_default --hostname=redis --restart=unless-stopped -v=finch-demo_redis-data:/data redis:alpine]
WARN[0000] volume "finch-demo_redis-data" already exists and will be returned as-is
INFO[0011] Creating container finch-demo-web-1
INFO[0011] Running [/usr/local/bin/nerdctl run --cidfile=/tmp/compose-3371262174/cid -l=com.docker.compose.project=finch-demo -l=com.docker.compose.service=web -d --name=finch-demo-web-1 --pull=never --net=finch-demo_default --hostname=web -p=5000:5000/tcp --restart=unless-stopped finch-demo-web]
위와 같이 웹 애플리케이션과 Redis 컨테이너가 함께 실행된 것을 확인 할 수 있습니다.
실행 중인 컨테이너 확인
아래와 같이 실행 중인 컨테이너를 확인합니다.
$ finch ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
946badd3438c docker.io/library/finch-demo-web:latest "python app.py" 22 seconds ago Up 0.0.0.0:5000->5000/tcp finch-demo-web-1
c4e7f14ce140 docker.io/library/redis:alpine "docker-entrypoint.s…" 22 seconds ago Up finch-demo-redis-1
위와 같이 finch-demo-web-1과 finch-demo-redis-1 컨테이너가 정상적으로 실행 중인 것을 확인 할 수 있습니다.
동작 확인
아래와 같이 curl 명령어로 애플리케이션이 정상 동작하는지 확인합니다. 요청할 때마다 방문자 카운트가 증가합니다.
$ curl localhost:5000
<!DOCTYPE html>
<html>
<head>
<title>Finch Compose Demo</title>
</head>
<body>
<h1>Hello from Finch Compose!</h1>
<p>Container Hostname: web</p>
<p>Visit Count: 1</p>
</body>
</html>
$ curl localhost:5000
<!DOCTYPE html>
<html>
<head>
<title>Finch Compose Demo</title>
</head>
<body>
<h1>Hello from Finch Compose!</h1>
<p>Container Hostname: web</p>
<p>Visit Count: 2</p>
</body>
</html>
위와 같이 Flask 애플리케이션과 Redis가 정상적으로 연동되어 방문자 카운트가 증가하는 것을 확인 할 수 있습니다.
Compose 환경 종료
아래와 같이 finch compose down 명령어를 사용하여 모든 컨테이너와 네트워크를 종료합니다.
$ finch compose down
INFO[0000] Removing container finch-demo-web-1
INFO[0000] Removing container finch-demo-redis-1
INFO[0001] Removing network finch-demo_default
볼륨까지 함께 삭제하려면 -v 옵션을 추가합니다.
$ finch compose down -v
INFO[0000] Removing container finch-demo-web-1
INFO[0000] Removing container finch-demo-redis-1
INFO[0001] Removing volume finch-demo_redis-data
INFO[0001] Removing network finch-demo_default
시스템 정보 확인
아래와 같이 Finch 시스템 정보를 확인할 수 있습니다.
$ finch system info
Client:
Namespace: finch
Debug Mode: false
Server:
Server Version: v2.1.3
Storage Driver: overlayfs
Logging Driver: json-file
Cgroup Driver: systemd
Cgroup Version: 2
Plugins:
Log: fluentd journald json-file none syslog
Storage: native overlayfs stargz
Security Options:
seccomp
Profile: builtin
cgroupns
Kernel Version: 6.12.53-69.119.amzn2023.aarch64
Operating System: Fedora Linux 42 (Cloud Edition)
OSType: linux
Architecture: aarch64
CPUs: 4
Total Memory: 7.739GiB
Name: lima-finch
ID: 501dce95-ed66-4c39-852d-642a4008f6d0
위와 같이 Finch VM은 내부적으로 Fedora Linux 42 기반으로 동작하며, overlayfs 스토리지 드라이버와 systemd cgroup을 사용하는 것을 확인 할 수 있습니다.
Docker 명령어와 비교
Finch는 nerdctl을 기반으로 하기 때문에 Docker CLI와 거의 동일한 명령어 체계를 사용합니다.
| Docker 명령어 | Finch 명령어 |
|---|---|
docker pull |
finch pull |
docker run |
finch run 또는 finch container run |
docker ps |
finch ps |
docker images |
finch images |
docker logs |
finch logs |
docker rm |
finch container rm |
docker rmi |
finch rmi |
docker build |
finch build |
docker compose up |
finch compose up |
docker compose down |
finch compose down |
참고
- Finch GitHub: https://github.com/runfinch/finch
- Finch 공식 사이트: https://runfinch.com/
위와 같은 방법으로 macOS 환경에서 Docker Desktop 대신 Finch를 사용하여 컨테이너를 관리할 수 있습니다. Docker CLI와 유사한 명령어 체계를 제공하기 때문에 기존 Docker 사용 경험이 있다면 쉽게 적응할 수 있습니다.