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
相关产品推荐
相关产品推荐

