Search code examples
dockerbitbucketbitbucket-pipelines

How do I connect to a BitBucket service containers from a multi staged docker build?


Having worked with other hosted CI solutions this seems to be simple but with BitBucket it doesn't seem to be playing ball. I'm guessing this is because the build steps are running inside a docker container and the docker CLI is just mounted into that container?

However, I was wondering if there's a way to get this working?

We have a multi-staged docker build that runs a build and then runs a set of tests against a database. Below is a cut-down version of this.

FROM mcr.microsoft.com/dotnet/runtime:5.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["docker-build-problem-example.csproj", "./"]
RUN dotnet restore "docker-build-problem-example.csproj"
COPY . .
WORKDIR "/src/"
RUN dotnet build "docker-build-problem-example.csproj" -c Release -o /app/build

FROM build AS test
RUN dotnet test "docker-build-problem-example.csproj" -c Release

FROM test AS publish
RUN dotnet publish "docker-build-problem-example.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "docker-build-problem-example.dll"]

The test stage runs the following example test which just queries a MongoDB database.

public class UnitTest1
{
    [Fact]
    public async Task Test1()
    {
        var client = new MongoClient($"mongodb://{GetHostName()}");
        var database = client.GetDatabase("test");
        var collection = database.GetCollection<BsonDocument>("docs");

        var items = await collection.Find(Builders<BsonDocument>.Filter.Empty)
            .ToListAsync();

        Assert.Empty(items);
    }

    private string GetHostName()
    {
        return Environment.GetEnvironmentVariable("CI") is { }
            ? "127.0.0.1" : "host.docker.internal";
    }
}

The above uses 127.0.0.1 as the hostname as this is what the docs say to use, however, this does just timeout and won't connect.

Is there a way to just connect these containers together so they can talk?

Here's my bitbucket.yml file.

image: atlassian/default-image:2

pipelines:
  default:
    - step:
        name: 'Build and Test'
        services:
           - mongo
           - docker
        script:
           - docker ps
           - docker network ls
           - docker build .

definitions: 
  services: 
    mongo: 
      image: mongo

I've uploaded a public example here - https://bitbucket.org/kev_bite/docker-build-problem-example/src/master/

And here's the pipeline build - https://bitbucket.org/kev_bite/docker-build-problem-example/addon/pipelines/home#!/results/5


Solution

  • The complete process I use is....

    1. I start a postgres database as a container in the bitbucket pipeline. (Same as your mongodb service)

    2. I then pass the address of the docker host (found using the $BITBUCKET_DOCKER_HOST_INTERNAL) environment variable into the docker build command like follows

    docker build -t image_name . --build-arg DATABASE_SERVER=$BITBUCKET_DOCKER_HOST_INTERNAL

    1. In the docker file, DATABASE_SERVER is defined as an ARG ie. ARG DATABASE_SERVER

    2. Again in the docker file, I then map that ARG to an environment variable

    ENV POSTGRES_ENDPOINT=$DATABASE_SERVER

    1. My test code, then uses the POSTGRES_ENDPOINT environment variable