프로젝트 소개
# go-docker-testsuite
Docker 컨테이너에서 통합 테스트용 타사 종속성을 실행하기 위한 Go 라이브러리입니다. 데이터베이스, 큐, 캐시, 객체 스토리지 등 모든 Docker 이미지를 실행하고 Go 테스트에서 연결할 수 있습니다.
## 기능
- **Container** — 모든 Docker 이미지를 생성, 실행, 출력 대기, 정리하는 저수준 래퍼
- **Group** — 격리된 Docker 네트워크에서 IP 수준 연결로 여러 컨테이너 실행
- **Applications** — 인기 서비스(MySQL, PostgreSQL, Redis, Kafka 등)용 즉시 사용 가능한 래퍼
- **Hooks** — 컨테이너별 수명주기 콜백(BeforeRun, AfterRun, BeforeClose, AfterClose)
- **Exec** — 실행 중인 컨테이너 내부에서 명령 실행 및 stdout, stderr, 종료 코드 캡처
- **수명주기 명령** — exec를 통한 시작/준비 완료 후 명령 실행
- **컨테이너로 파일 복사** — `WithFiles`로 시작 전 파일 시드
- **컨테이너에서 파일 복사** — 실행 중인 컨테이너에서 tar 스트림으로 파일 읽기
- **리소스 제한** — 호스트 및 CI 보호를 위한 CPU/메모리/pids 제한
- **Matchers** — 부분 문자열, 정확히 일치, 정규식 매처로 컨테이너 로그 대기
- **대기 전략** — 구성 가능한 준비 상태 프로브(ForLog, ForHTTPGet, ForCommand, ForTCPConnection)
- **환경 빌더** — 타입화된 환경 변수용 유창한 DSL
- **포트 바인딩** — 무작위 또는 1:1 할당을 통한 DNAT 포트 매핑
- **네트워크 모드** — 호스트 또는 사용자 정의 네트워크 지원
- **IMAGE_PREFIX** — 프록시/미러를 통한 이미지 라우팅
- **`*testing.T` 바인딩** — 안전한 `t.Parallel()`과 함께 자동 정리 및 로깅
## 요구 사항
- Go 1.26 이상
- 실행 중인 Docker 데몬(`DOCKER_HOST`를 통한 원격 호스트 지원)
## 설치
멀티 모듈 워크스페이스: 루트는 핵심 모듈이고, `applications/<name>` 아래의 각 애플리케이션은 자체 Go 모듈입니다. 필요한 것만 가져오세요:
```sh
go get github.com/teran/go-docker-testsuite
# 특정 애플리케이션 래퍼:
go get github.com/teran/go-docker-testsuite/applications/redis
go get github.com/teran/go-docker-testsuite/applications/postgres
```
## 애플리케이션
즉시 사용 가능한 래퍼(각각 타입화된 클라이언트 인터페이스를 반환하고 시작, 상태 확인, 정리를 처리):
- AWS SDK v2를 사용한 Ceph (RGW)
- clickhouse-go를 사용한 ClickHouse
- SQLite를 사용한 Forgejo
- vtysh를 사용한 FRR 라우팅 스위트
- client-go를 사용한 K3s
- Sarama를 사용한 Kafka
- gomemcache를 사용한 Memcache
- MinIO (S3 호환)
- mongo-driver를 사용한 MongoDB
- MySQL / MariaDB / Percona Server
- PostgreSQL + Redis를 사용한 NetBox
- Nginx
- opensearch-go를 사용한 OpenSearch
- PostgreSQL + Valkey를 사용한 Paperless-ngx
- pgx를 사용한 PostgreSQL
- prometheus/client_golang을 사용한 Prometheus
- RabbitMQ (AMQP + 관리 API)
- go-redis를 사용한 Redis
- gocql을 사용한 ScyllaDB
- Vault
- Libvirtd (KVM/QEMU)
많은 패키지에는 pkg.go.dev에서 테스트 가능한 예제가 포함되어 있습니다.
## 사용법
### 빠른 시작 — MySQL
```go
package main
import (
"context"
"database/sql"
"time"
_ "github.com/go-sql-driver/mysql"
"github.com/teran/go-docker-testsuite/applications/mysql"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
app, err := mysql.New(ctx, "index.docker.io/library/mysql:8.0.4")
if err != nil {
panic(err)
}
defer app.Close(ctx)
if err := app.CreateDB(ctx, "important_database"); err != nil {
panic(err)
}
db, err := sql.Open("mysql", app.MustDSN("important_database"))
if err != nil {
panic(err)
}
defer db.Close()
if _, err := db.ExecContext(ctx, "SELECT 1"); err != nil {
panic(err)
}
}
```
### 다중 컨테이너 그룹
```go
package main
import (
"context"
"time"
"github.com/teran/go-docker-testsuite"
"github.com/teran/go-docker-testsuite/wait"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
app := docker.NewApplication(
c,
docker.HookFunc(func(ctx context.Context, ht docker.HookType, c docker.Container) error {
return wait.Wait(ctx, c, wait.ForLog(docker.NewSubstringMatcher("ready")))
}),
)
g, err := docker.NewGroup("my-services", app1, app2)
if err != nil {
panic(err)
}
if err := g.Run(ctx); err != nil {
panic(err)
}
defer g.Close(ctx)
}
```
### 대기 전략
```go
if err := wait.Wait(ctx, c, wait.ForHTTPGet(8080,
wait.WithPath("/health"),
wait.WithResponseStatuses(200),
)); err != nil {
panic(err)
}
```
기타 전략: `ForLog`, `ForCommand`, `ForTCPConnection`. `ForAll` / `ForAny` / `ForAtLeast`와 결합하세요.
### `*testing.T` 바인딩
```go
func TestRedis(t *testing.T) {
t.Parallel()
c, err := docker.NewContainerWithT(
t,
"redis",
images.Redis,
nil,
docker.NewEnvironment(),
docker.NewPortBindings().PortDNAT(docker.ProtoTCP, 6379),
)
if err != nil {
t.Fatal(err)
}
c.RunT(ctx) // 빠른 실패; t.Cleanup 등록
}
```
### 수명주기 훅
```go
docker.HookTypeBeforeRun // 컨테이너 시작 전
docker.HookTypeAfterRun // 컨테이너 시작 후
docker.HookTypeBeforeClose // 컨테이너 중지 전
docker.HookTypeAfterClose // 컨테이너 중지 후
```
### Exec 및 수명주기 명령
```go
res, err := c.Exec(ctx, []string{"echo", "hello"})
if err != nil {
panic(err)
}
if err := res.Error(); err != nil {
panic(err)
}
fmt.Printf("exit code: %d\n", res.ExitCode)
fmt.Printf("stdout: %s", res.Stdout)
```
### 컨테이너 안팎으로 파일 복사
작은 콘텐츠에는 `FileFromBytes`와 함께 `WithFiles`를 사용하고, 큰 파일에는 `io.Reader` + `Size`를 사용하세요. `docker.CopyFromContainer`로 파일을 다시 읽으세요.
### 이미지 접두사 / 프록시
```sh
export IMAGE_PREFIX=registry-mirror.example.com
```
## 모듈 및 릴리스
단일 버전 번호를 가진 멀티 모듈 워크스페이스로, 별도로 태그됩니다(예: 핵심 `v1.6.0`, 애플리케이션 `applications/redis/v1.6.0`). 릴리스 순서: 핵심 먼저, 그 다음 애플리케이션. 태그에는 Makefile을 사용하세요.
## 라이선스
Apache License, Version 2.0
Comments
0 people shared their preference · Deer Point appears after 10 participants
Sign in to join the discussion.