You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dagger Go SDK中Postgres服务就绪等待与服务同步执行问题

问题描述

使用Dagger v0.15.1的Go SDK编写集成测试时,遇到服务启动顺序问题:需要先启动Postgres服务并等待就绪,再启动Liquibase执行数据库迁移,最后运行npm run migrate:db。但当前代码中Liquibase会在Postgres未就绪前启动,抛出UnknownHostException,且npm命令也会提前执行。

当前代码

func (m *DaggerEnvy) IntegrationTest(ctx context.Context, source *dagger.Directory,
    npmrcFile *dagger.Secret,
    // optional
    // +default="postgres"
    dbName,
    // optional
    // +default="postgres"
    dbUser,
    // optional
    // +default="postgres"
    dbPassword string,
) (string, error) {

    // postgres service
    postgresService := m.postgresService(ctx, dbName, dbUser, dbPassword)
    postgresService.Start(ctx)
    defer postgresService.Stop(ctx)

    // liquibase service
    liquibaseService := m.liquibaseContainer(ctx, source, postgresService, dbName)
    liquibaseService.Start(ctx)
    defer liquibaseService.Stop(ctx)

    return m.buildNodeEnv(source, npmrcFile).
        WithServiceBinding("postgres-service", postgresService).
        WithServiceBinding("liquibase-service", liquibaseService).
        WithExec([]string{"npm", "run", "migrate:db"}).
        Stderr(ctx)
}


func (m *DaggerEnvy) postgresService(
    ctx context.Context,
    // optional
    // +default="postgres"
    dbName,
    // optional
    // +default="root"
    dbUser,
    // optional
    // +default="postgres"
    dbPassword string,
) *dagger.Service {
    // ? should optional parameters be stored in .env file?
    // postgresDataVolume := dag.CacheVolume("./postgres-data") // Path for local persistence

    return dag.Container().From("postgres:17").
        WithEnvVariable(constants.POSTGRES_USER, dbUser).
        WithEnvVariable(constants.POSTGRES_PASSWORD, dbPassword).
        WithEnvVariable(constants.POSTGRES_DB, dbName).
        WithExposedPort(5432).
        // WithMountedCache("/var/lib/postgresql/data", postgresDataVolume).
        AsService(dagger.ContainerAsServiceOpts{
            UseEntrypoint: true,
        })
}

func (m *DaggerEnvy) liquibaseContainer(
    ctx context.Context,
    source *dagger.Directory,
    postgresService *dagger.Service,
    // +optional
    // +default="postgres"
    dbName string,
) *dagger.Service {
    return dag.Container().
        From("liquibase").
        WithFile("/liquibase/master.yaml", source.File("src/server/schema/liquibase/master.yaml")).
        WithDirectory("/liquibase/changelog", source.Directory("src/server/schema/changelog")).
        WithEnvVariable("LIQUIBASE_COMMAND_URL", fmt.Sprintf("jdbc:postgresql://postgres-service:5432/%s", dbName)).
        WithEnvVariable("LIQUIBASE_COMMAND_USERNAME", "postgres").
        WithEnvVariable("LIQUIBASE_COMMAND_PASSWORD", "postgres").
        WithEnvVariable("LIQUIBASE_COMMAND_CHANGELOG_FILE", "master.yaml").
        WithExec([]string{"liquibase", "update"}).
        AsService()
}

错误信息

Starting Liquibase at 12:14:08 using Java 17.0.13 (version 4.30.0 #4943 buil    
 2024-10-31 17:00+0000)                                                         
Liquibase Version: 4.30.0                                                       
Liquibase Open Source 4.30.0 by Liquibase                                       
ERROR: Exception Details                                                        
ERROR: Exception Primary Class:  UnknownHostException                           
ERROR: Exception Primary Reason:  postgres-service                              
ERROR: Exception Primary Source:  4.30.0                                        
                                                                                
Unexpected error running Liquibase: Connection could not be created to jdbc:    
postgresql://postgres-service:5432/postgres with driver org.postgresql.Drive    
r.  The connection attempt failed.                                              
                                                                                
For more information, please use the --log-level flag                           
! process "liquibase update" did not complete successfully: exit code: 1
解决方案

1. 移除手动服务启停调用

Dagger框架会自动管理服务的生命周期,手动调用Start()/Stop()会打乱依赖顺序,直接删除这些代码即可。

2. 为Postgres添加就绪检查

Postgres启动后需要时间完成初始化,通过WithHealthcheck()配置就绪检查,确保服务可用后再执行后续步骤:

func (m *DaggerEnvy) postgresService(
    ctx context.Context,
    dbName, dbUser, dbPassword string,
) *dagger.Service {
    return dag.Container().From("postgres:17").
        WithEnvVariable(constants.POSTGRES_USER, dbUser).
        WithEnvVariable(constants.POSTGRES_PASSWORD, dbPassword).
        WithEnvVariable(constants.POSTGRES_DB, dbName).
        WithExposedPort(5432).
        // 配置Postgres就绪检查
        WithHealthcheck(dagger.ContainerWithHealthcheckOpts{
            Cmd:          []string{"pg_isready", "-U", dbUser, "-d", dbName},
            Interval:     "1s",
            Timeout:      "5s",
            Retries:      10,
            StartPeriod:  "5s",
        }).
        AsService(dagger.ContainerAsServiceOpts{
            UseEntrypoint: true,
        })
}

3. 将Liquibase改为一次性执行容器

Liquibase是一次性迁移任务,不需要作为长期服务运行。改为直接执行迁移命令,通过服务绑定依赖Postgres,Dagger会自动等待Postgres就绪后再执行:

func (m *DaggerEnvy) runLiquibaseMigration(
    ctx context.Context,
    source *dagger.Directory,
    postgresService *dagger.Service,
    dbName, dbUser, dbPassword string,
) error {
    _, err := dag.Container().
        From("liquibase").
        WithFile("/liquibase/master.yaml", source.File("src/server/schema/liquibase/master.yaml")).
        WithDirectory("/liquibase/changelog", source.Directory("src/server/schema/changelog")).
        WithEnvVariable("LIQUIBASE_COMMAND_URL", fmt.Sprintf("jdbc:postgresql://postgres-service:5432/%s", dbName)).
        WithEnvVariable("LIQUIBASE_COMMAND_USERNAME", dbUser).
        WithEnvVariable("LIQUIBASE_COMMAND_PASSWORD", dbPassword).
        WithEnvVariable("LIQUIBASE_COMMAND_CHANGELOG_FILE", "master.yaml").
        // 绑定Postgres服务,自动等待其就绪
        WithServiceBinding("postgres-service", postgresService).
        WithExec([]string{"liquibase", "update"}).
        ExitCode(ctx)
    return err
}

4. 调整集成测试执行顺序

在IntegrationTest中,先执行Liquibase迁移并等待完成,再运行npm命令,确保所有依赖就绪:

func (m *DaggerEnvy) IntegrationTest(ctx context.Context, source *dagger.Directory,
    npmrcFile *dagger.Secret,
    dbName, dbUser, dbPassword string,
) (string, error) {
    // 创建Postgres服务
    postgresService := m.postgresService(ctx, dbName, dbUser, dbPassword)

    // 先执行Liquibase迁移,等待完成
    if err := m.runLiquibaseMigration(ctx, source, postgresService, dbName, dbUser, dbPassword); err != nil {
        return "", err
    }

    // 再执行npm迁移命令,绑定Postgres服务
    return m.buildNodeEnv(source, npmrcFile).
        WithServiceBinding("postgres-service", postgresService).
        WithExec([]string{"npm", "run", "migrate:db"}).
        Stderr(ctx)
}

5. 修复Liquibase硬编码凭证

原代码中Liquibase硬编码了用户和密码,改为使用传入的参数,保持配置一致性,避免因凭证不一致导致的连接失败。


内容的提问来源于stack exchange,提问作者Shailesh B

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:17:33