microsoft / DockerTools

Tools For Docker, including Visual Studio Provisioning and Publishing
Other
175 stars 26 forks source link

Map active solution configuration to docker-compose launchsettings #361

Closed aschwab closed 2 years ago

aschwab commented 2 years ago

I'm trying to create different Environments for my Docker-Compose based dapr/aca application. But I'm having troubles to map the Active-Solution Configuration to the actual Configuration inside my Container.

Here is an Example what I'm trying to achieve:

For instance we have 3 environments:

I would think of overriding the docker-compose somehow like creating an environment specific docker-compose override.

docker-compose:

...
services:
  gateway:
    image: ${DOCKER_REGISTRY-}gateway
    build:
      context: .
      dockerfile: Gateway/Gateway/Dockerfile
...

docker-compose.override.yaml

...
services:
  gateway:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
...

docker-compose.Dev.override.yaml

...
services:
  gateway:
    environment:
      - ASPNETCORE_ENVIRONMENT=Dev
...

docker-compose.Staging.override.yaml

...
services:
  gateway:
    environment:
      - ASPNETCORE_ENVIRONMENT=Staging
...

Now I'd set the Configurations for the Active solution configurations but this setting is not saved. image

For a normal csproj you would define something like the following to be able to set the project's configuration to something different than Debug.

any.csproj

  <PropertyGroup>
    <Configurations>Debug;Dev;Staging</Configurations>
  </PropertyGroup>

The dcproj does not react to this kind of configuration, the options inside the Configuration-Manager do only appear if you add something like this to the docker-compose.dcproj:

docker-compose.dcproj

...
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Dev|AnyCPU'" />
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Staging|AnyCPU'" />
...

The selection won't be saved and also the parameter $(ConfigurationName) which would normally be the projects selected configuration is not set during the build process.

Alternatively we could use the LaunchSettings to somehow propagate the Environment to the docker-compose.yaml ideally we could replace an environment variable to do something like this:

launchSettings.json

{
  "profiles": {
    "Debug": {
      "commandName": "DockerCompose",
      "commandVersion": "1.0",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      },
      "serviceActions": {
        "gateway": "StartDebugging",
        "redis": "StartWithoutDebugging",
        "zipkin": "StartWithoutDebugging"
      }
    },
   "Dev": {
      "commandName": "DockerCompose",
      "commandVersion": "1.0",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Dev"
      },
      "serviceActions": {
        "gateway": "StartDebugging",
        "redis": "StartWithoutDebugging",
        "zipkin": "StartWithoutDebugging"
      }
    }
   "Staging": {
      "commandName": "DockerCompose",
      "commandVersion": "1.0",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Staging"
      },
      "serviceActions": {
        "gateway": "StartDebugging",
        "redis": "StartWithoutDebugging",
        "zipkin": "StartWithoutDebugging"
      }
    }
  }
}

The correct launchSetting would need to be selected automatically by the specified Configuration inside the Configuration-Manager window.

It would be much of help to be able to test the Dev/Staging system with appSettings inside the containers which connect to the live-infrastructure instead of my local ones.

NCarlsonMSFT commented 2 years ago

@aschwab I did a quick check and it looks like my suggestions from https://github.com/microsoft/DockerTools/issues/252 are largely still working. You can use a compose file specified in AdditionalComposeFilePaths to override the settings from docker-compose.override.yml, and you can use the steps below to set the property on a per-configuration basis.

FYI when I was working on this the Configuration manager added the needed PropertyGroups for my configurations, but it had a slight delay.

For reference here is the relevant part: To create a new configuration with different compose files: 1) Open the Configuration Manager:
<Configuration Manager...> 2) Select the <New...> configuration:
<New...> 3) Enter a name for the new configuration, and pick a base configuration to copy, and click OK. New Solution Configuration 4) Unload the .dcproj and add a property group for the new configuration:

<PropertyGroup Condition="'$(Configuration)' == 'Debug With Redis'">
    <!-- This is the name of your main compose file w/o the .yml-->
    <DockerComposeBaseFilePath>docker-compose.test</DockerComposeBaseFilePath>
    <!-- This is a semicolon-delimited list of additional .yml files to include when launching -->
    <AdditionalComposeFilePaths>$(MSBuildPojectDirectory)docker-compose.redis.yml</AdditionalComposeFilePaths>
    <!-- Because this is a custom configuration you need to opt into Fast mode -->
    <DockerDevelopmentMode>Fast</DockerDevelopmentMode>
    <!-- Best to move these properties here from the Globals section -->
    <DockerLaunchAction>LaunchBrowser</DockerLaunchAction>
    <DockerServiceUrl>{Scheme}://localhost:{ServicePort}</DockerServiceUrl>
    <DockerServiceName>webapplication12</DockerServiceName>
</PropertyGroup>

5) Edit your service projects and add a property group for the new configuration:

<PropertyGroup Condition=" '$(Configuration)' == 'Debug With Redis'">
    <DebugSymbols>true</DebugSymbols>
    <Optimize>false</Optimize>
</PropertyGroup>
aschwab commented 2 years ago

@NCarlsonMSFT thanks alot for your solution, this seems to work perfectly fine. I guess the Configuration-Manager only saved those Configurations where there actually we're properties configured.

My docker-compose.dcproj looks something like this now:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" Sdk="Microsoft.Docker.Sdk">
  <PropertyGroup Label="Globals">
    <ProjectVersion>2.1</ProjectVersion>
    <ProjectGuid>3665ba0b-49be-4e79-9c48-a1657281978a</ProjectGuid>
  </PropertyGroup>
  <ItemGroup>
    <None Include=".dockerignore" />
    <None Include="docker-compose.base.yml">
      <DependentUpon>docker-compose.yml</DependentUpon>
    </None>
    <None Include="docker-compose.staging.yml">
      <DependentUpon>docker-compose.yml</DependentUpon>
    </None>
    <None Include="docker-compose.debug.yml">
      <DependentUpon>docker-compose.yml</DependentUpon>
    </None>
    <None Include="docker-compose.dev.yml">
      <DependentUpon>docker-compose.yml</DependentUpon>
    </None>
    <None Include="docker-compose.yml" />
  </ItemGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <AdditionalComposeFilePaths>docker-compose.base.yml;docker-compose.debug.yml</AdditionalComposeFilePaths>
    <DockerTargetOS>Linux</DockerTargetOS>
    <DockerLaunchAction>None</DockerLaunchAction>
    <DockerServiceUrl>{Scheme}://localhost:{ServicePort}/{Scheme}://{ServiceHost}:{ServicePort}</DockerServiceUrl>
    <DockerServiceName>gateway</DockerServiceName>
    <DockerDevelopmentMode>Fast</DockerDevelopmentMode>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Dev|AnyCPU'">
    <AdditionalComposeFilePaths>docker-compose.base.yml;docker-compose.dev.yml</AdditionalComposeFilePaths>
    <DockerTargetOS>Linux</DockerTargetOS>
    <DockerLaunchAction>None</DockerLaunchAction>
    <DockerServiceUrl>{Scheme}://localhost:{ServicePort}/{Scheme}://{ServiceHost}:{ServicePort}</DockerServiceUrl>
    <DockerServiceName>gateway</DockerServiceName>
    <DockerDevelopmentMode>Fast</DockerDevelopmentMode>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Staging|AnyCPU'">
    <AdditionalComposeFilePaths>docker-compose.base.yml;docker-compose.staging.yml</AdditionalComposeFilePaths>
    <DockerTargetOS>Linux</DockerTargetOS>
    <DockerLaunchAction>None</DockerLaunchAction>
    <DockerServiceUrl>{Scheme}://localhost:{ServicePort}/{Scheme}://{ServiceHost}:{ServicePort}</DockerServiceUrl>
    <DockerServiceName>gateway</DockerServiceName>
    <DockerDevelopmentMode>Fast</DockerDevelopmentMode>
  </PropertyGroup>
</Project>

I included a docker-compose.base.yml for general configurations which should affect all environments. Those environment-based additional compose files only set the ASPNETCORE_ENVIRONMENT env-variable.

docker-compose.base.yml

...
services:
  gateway:
    environment:
      - ASPNETCORE_URLS=https://+:443;http://+:80
      - DAPR_HTTP_PORT=3500
    ports:
      - "7000:80"
      - "7001:443"
      - "50001:50001"
    volumes:
      - ..
...

docker-compose.debug.yml

...
services:
  gateway:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
...

docker-compose.dev.yml

...
services:
  gateway:
    environment:
      - ASPNETCORE_ENVIRONMENT=Dev
...

docker-compose.staging.yml

...
services:
  gateway:
    environment:
      - ASPNETCORE_ENVIRONMENT=Staging
...
NCarlsonMSFT commented 2 years ago

@aschwab glad I could unblock you! FYI if you're using a launch profile you don't need the properties: DockerLaunchAction, DockerServiceUrl, nor DockerServiceName the launch profile overrides those settings (sorry the comment I copied from predates the launch profile)