-
Notifications
You must be signed in to change notification settings - Fork 0
Expand README with Aspire orchestration, persistent volumes, and CI/CD documentation #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,280 @@ | ||
| # Persistent Volume & GHCR Publish Sample | ||
|
|
||
| This sample is based on the aspire-samples/volumemount sample. It demonstrates how to configure a SQL Server container to use a persistent volume in Aspire, so that the data is persisted across app launches. It shows how to write files (image uploads) to a persistent volume from a Blazor Web app. | ||
| This sample demonstrates how to use <a href="https://aspire.dev">Aspire</a> to orchestrate a multi-container application with persistent data storage and automated deployment to production. It showcases: | ||
|
|
||
| In addition to this, it also shows how to publish the docker-compose artifacts and push the BlazorWeb image to GitHub Container Registry from a GitHub Actions workflow. | ||
| - **Persistent SQL Server volumes** - Database data persists across container restarts | ||
| - **Persistent file storage volumes** - User-uploaded images persist across container restarts | ||
| - **Aspire Docker Integration** - Seamless configuration and orchestration of containerized services | ||
| - **GitHub Actions CI/CD** - Automated building, publishing, and deploying using the Aspire CLI | ||
| - **Identity management** - ASP.NET Core Identity with SQL Server backend | ||
| - **Blazor Server UI** - Interactive web application for file uploads | ||
|
|
||
| ## What This Sample Demonstrates | ||
|
|
||
| ### Aspire AppHost Configuration | ||
|
|
||
| The `AppHost.cs` file in the `VolumeMount.AppHost` project is the heart of the Aspire orchestration. It demonstrates: | ||
|
|
||
| #### 1. Docker Compose Environment Setup | ||
| ```csharp | ||
| var compose = builder.AddDockerComposeEnvironment("volumemount-env") | ||
| .WithProperties(env => { env.DashboardEnabled = true; }) | ||
| .ConfigureComposeFile(composeFile => | ||
| { | ||
| // Define top-level volumes for sharing across services | ||
| composeFile.AddVolume(new Volume | ||
| { | ||
| Name = "volumemount-blazor-uploads", | ||
| Driver = "local" | ||
| }); | ||
| }); | ||
| ``` | ||
|
|
||
| #### 2. Container Registry Configuration | ||
| ```csharp | ||
| var endpoint = builder.AddParameter("registry-endpoint"); | ||
| var repository = builder.AddParameter("registry-repository"); | ||
| builder.AddContainerRegistry("container-registry", endpoint, repository); | ||
| ``` | ||
|
|
||
| This configures the container registry (GitHub Container Registry in this case) where the BlazorWeb container image will be pushed during deployment. | ||
|
|
||
| #### 3. SQL Server with Persistent Volume | ||
| ```csharp | ||
| var sqlPassword = builder.AddParameter("sqlserver-password", secret: true); | ||
| var sqlserver = builder.AddSqlServer("sqlserver", password: sqlPassword) | ||
| .WithDataVolume("volumemount-sqlserver-data") | ||
| .WithLifetime(ContainerLifetime.Persistent) | ||
| .WithHostPort(1433) | ||
| .WithEndpoint("tcp", e => | ||
| { | ||
| e.Port = 1433; | ||
| e.TargetPort = 1433; | ||
| e.Protocol = ProtocolType.Tcp; | ||
| e.IsProxied = false; | ||
| e.IsExternal = true; | ||
| }); | ||
| ``` | ||
|
|
||
| **Key Features:** | ||
| - `WithDataVolume()` - Mounts a named volume to `/var/opt/mssql` to persist database files | ||
| - `WithLifetime(ContainerLifetime.Persistent)` - Container stays running across app restarts | ||
| - `WithEndpoint()` with `IsProxied = false` and `IsExternal = true` - Allows direct connection from SQL Server Management Studio (SSMS) even when Aspire isn't running | ||
|
|
||
| #### 4. Blazor Web Application with File Upload Volume | ||
| ```csharp | ||
| var blazorweb = builder.AddProject<Projects.VolumeMount_BlazorWeb>("blazorweb") | ||
| .WithExternalHttpEndpoints() | ||
| .WithReference(sqlDatabase) | ||
| .WaitFor(sqlDatabase) | ||
| .WithRemoteImageTag("latest") | ||
| .PublishAsDockerComposeService((resource, service) => | ||
| { | ||
| // Mount volume for persistent file uploads | ||
| service.AddVolume(new Volume | ||
| { | ||
| Name = "volumemount-blazor-uploads", | ||
| Source = "volumemount-blazor-uploads", | ||
| Target = "/app/wwwroot/uploads", | ||
| Type = "volume", | ||
| ReadOnly = false | ||
| }); | ||
|
|
||
| // Configure permissions for file uploads | ||
| service.User = "root"; | ||
| service.Command = new List<string> | ||
| { | ||
| "/bin/sh", "-c", | ||
| "chown -R app:app /app/wwwroot/uploads && chmod -R 755 /app/wwwroot/uploads && exec su app -c 'dotnet /app/VolumeMount.BlazorWeb.dll'" | ||
| }; | ||
| }); | ||
| ``` | ||
|
|
||
| **Key Features:** | ||
| - `WithRemoteImageTag("latest")` - Tags the container image for pushing to registry | ||
| - `PublishAsDockerComposeService()` - Customizes the Docker Compose service definition | ||
| - Volume mounting to `/app/wwwroot/uploads` - Persists user-uploaded images | ||
| - Permission management - Ensures the app user can write to the uploads directory | ||
|
|
||
| ### Persistent Volumes Explained | ||
|
|
||
| #### SQL Server Volume (`volumemount-sqlserver-data`) | ||
| - **Purpose:** Stores SQL Server database files (`.mdf`, `.ldf`) | ||
| - **Mount Point:** `/var/opt/mssql` inside the container | ||
| - **Benefits:** | ||
| - Database persists across container restarts | ||
| - No data loss when redeploying applications | ||
| - Allows database upgrades without data migration | ||
|
|
||
| #### Blazor Uploads Volume (`volumemount-blazor-uploads`) | ||
| - **Purpose:** Stores user-uploaded image files | ||
| - **Mount Point:** `/app/wwwroot/uploads` inside the container | ||
| - **Benefits:** | ||
| - Uploaded images persist across container restarts | ||
| - Multiple container instances can share the same upload storage | ||
| - Files remain available after application updates | ||
|
|
||
| ### File Upload Implementation | ||
|
|
||
| The Blazor application uses a `PhotoUploadService` that writes files directly to the persistent volume: | ||
|
|
||
| ```csharp | ||
| public async Task<bool> UploadPhotoAsync(IBrowserFile photo) | ||
| { | ||
| // Upload path is configured to use the mounted volume | ||
| var fileName = $"{Guid.NewGuid()}{Path.GetExtension(photo.Name)}"; | ||
| var filePath = Path.Combine(_uploadPath, fileName); | ||
|
|
||
| await using var fileStream = new FileStream(filePath, FileMode.Create); | ||
| await using var uploadStream = photo.OpenReadStream(maxAllowedSize: 10485760); | ||
| await uploadStream.CopyToAsync(fileStream); | ||
| } | ||
| ``` | ||
|
|
||
| ## GitHub Actions Workflow - Aspire CLI Integration | ||
|
|
||
| The `.github/workflows/aspire-build-push.yml` workflow demonstrates automated deployment using the Aspire CLI: | ||
|
|
||
| ### Workflow Steps | ||
|
|
||
| #### 1. Build and Restore | ||
| ```yaml | ||
| - name: Setup .NET | ||
| uses: actions/setup-dotnet@v4 | ||
| with: | ||
| dotnet-version: '10.0.x' | ||
|
|
||
| - name: Restore dependencies | ||
| run: dotnet restore | ||
|
|
||
| - name: Build solution | ||
| run: dotnet build --configuration Release --no-restore | ||
| ``` | ||
|
|
||
| #### 2. Install Aspire CLI | ||
| ```yaml | ||
| - name: Install Aspire CLI | ||
| run: dotnet tool install --global aspire.cli --prerelease | ||
| ``` | ||
|
|
||
| The Aspire CLI is a global .NET tool that provides commands for publishing and deploying Aspire applications. | ||
|
|
||
| #### 3. Publish Docker Compose Artifacts | ||
| ```yaml | ||
| - name: Prepare Docker Compose with Aspire | ||
| run: | | ||
| aspire publish \ | ||
| --project VolumeMount.AppHost/VolumeMount.AppHost.csproj \ | ||
| --output-path ./aspire-output | ||
| ``` | ||
|
|
||
| **What `aspire publish` does:** | ||
| - Generates a `docker-compose.yaml` file based on the AppHost configuration | ||
| - Creates an `.env` template file for environment variables | ||
| - Packages all configuration needed to deploy the application | ||
| - Outputs artifacts to `./aspire-output/` directory | ||
|
|
||
| **Generated Files:** | ||
| - `docker-compose.yaml` - Complete service definitions for all containers | ||
| - `.env` - Template for required environment variables (passwords, ports, etc.) | ||
|
|
||
| #### 4. Push Container Image to GitHub Container Registry | ||
| ```yaml | ||
| - name: Login to GHCR | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ghcr.io | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Push images with Aspire | ||
| env: | ||
| Parameters__registry_endpoint: ghcr.io | ||
| Parameters__registry_repository: bethmassi/volumemount | ||
| run: aspire do push | ||
| ``` | ||
|
|
||
| **What `aspire do push` does:** | ||
| - Builds the Docker container image for the BlazorWeb project | ||
| - Tags the image with the configured registry endpoint and repository | ||
| - Pushes the image to GitHub Container Registry (ghcr.io) | ||
| - Uses parameters defined in the AppHost configuration | ||
|
|
||
| **Environment Variables:** | ||
| - `Parameters__registry_endpoint` - Maps to the `registry-endpoint` parameter in AppHost | ||
| - `Parameters__registry_repository` - Maps to the `registry-repository` parameter in AppHost | ||
|
|
||
| ### Deployment Artifacts | ||
|
|
||
| After the workflow runs, you get: | ||
| 1. **Docker Compose files** uploaded as GitHub Actions artifacts (retained for 30 days) | ||
| 2. **Container image** published to GitHub Container Registry at `ghcr.io/bethmassi/volumemount/blazorweb:latest` | ||
|
|
||
| These can be used to deploy the application to any Docker-compatible environment: | ||
| ```bash | ||
| # Download the docker-compose.yaml and .env files | ||
| # Set required environment variables in .env file | ||
| docker-compose up -d | ||
| ``` | ||
|
|
||
| ## Local Development | ||
|
|
||
| To run the sample locally: | ||
|
|
||
| 1. Install [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) | ||
| 2. Install <a href="https://www.docker.com/products/docker-desktop">Docker Desktop</a> | ||
| 3. Install <a href="https://aspire.dev/get-started/install-cli/">Aspire CLI</a> | ||
| 4. Set the SQL Server password as a user secret: | ||
| ```bash | ||
| dotnet user-secrets set "Parameters:sqlserver-password" "YourSecurePassword123!" --project VolumeMount.AppHost | ||
| ``` | ||
| 5. Run the AppHost: | ||
| ```bash | ||
| cd VolumeMount.AppHost | ||
| aspire run | ||
| ``` | ||
| 6. Open the Aspire Dashboard (URL shown in console output) | ||
| 7. Access the BlazorWeb application through the dashboard | ||
|
|
||
| ## Local Deployment | ||
|
|
||
| To containerize and deploy the application locally: | ||
|
|
||
| ```bash | ||
| aspire deploy | ||
| ``` | ||
|
|
||
| This will set up the containerized application and volume mounts on your Docker desktop. | ||
|
|
||
| **Running the Aspire Dashboard:** | ||
| - The dashboard will be available at `http://localhost:18888` (default port) | ||
| - You can view logs, metrics, and traces for all services | ||
| - Navigate to the services tab to see all running containers | ||
|
|
||
| **Running the BlazorWeb app:** | ||
| - Once deployed, the BlazorWeb application will be available on the port specified in your `.env` file (default: 8080) | ||
| - Access it at `http://localhost:8080` or the configured port | ||
| - The app connects to the SQL Server container automatically via the configured connection string | ||
|
|
||
| ## Production Deployment | ||
|
|
||
| To deploy to production using the published artifacts: | ||
|
|
||
| 1. Download the `docker-compose.yaml` and `.env` files from the Aspire publish output. These are the artifacts you can download as an archive (.zip) from the Actions workflow run. | ||
| 2. Configure environment variables in the `.env` file: | ||
| ``` | ||
| SQLSERVER_PASSWORD=YourSecurePassword | ||
| BLAZORWEB_IMAGE=ghcr.io/bethmassi/volumemount/blazorweb:latest | ||
| BLAZORWEB_PORT=8080 | ||
| ``` | ||
| 3. Deploy using Docker Compose: | ||
| ```bash | ||
| docker-compose up -d | ||
| ``` | ||
|
|
||
| ## Key Takeaways | ||
|
|
||
| - **Aspire simplifies container orchestration** - Configure everything in C# with strong typing | ||
| - **Persistent volumes preserve data** - Both database and uploaded files survive container restarts | ||
| - **Aspire CLI enables GitOps** - Generate deployment artifacts and publish images in CI/CD pipelines using the aspire CLI commands `aspire publish` and `aspire do push`. | ||
| - **Flexible deployment options** - Same configuration works for local development and production | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to add this part for instruction on local 'aspire deploy'. It would be a good idea to also add how to run the blazorweb app on localhost as well as how to run the Aspire dashboard.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added "Local Deployment" section with
aspire deployinstructions, plus details on running the Aspire Dashboard (at http://localhost:18888) and BlazorWeb app (at http://localhost:8080 or configured port) in commit 99262cb