├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── M2 Introducing Containers on Azure └── README.md ├── M3 Running Containers Locally ├── running-postgres.ps1 └── running-redis.ps1 ├── M4 Creating Docker Images ├── Dockerfile ├── README.md ├── basic.Dockerfile ├── m4-01-build-dockerfile.ps1 ├── m4-02-build-dockerfile-v2.ps1 ├── m4-03-azure-container-registry.ps1 ├── multi-stage.Dockerfile └── win.Dockerfile ├── M5 Running Containers on Azure Container Instances ├── demo-1-ghost-aci.ps1 └── demo-2-private-container-registry.ps1 ├── M6 Running Containers on Web Apps for Containers ├── ci-cd-appservice.ps1 └── wordpress-appservice.ps1 ├── M7 Running Containers on Service Fabric ├── default-params.json ├── scale-front-end-params.json ├── service-fabric-mesh-demo.ps1 ├── service-fabric-mesh-volumes.ps1 ├── service-fabric-mesh-voting.ps1 ├── sfmesh-example-voting-app-linux.json ├── sfmesh-example-voting-app.json ├── sfmesh-volumes.json └── sfmesh-windows.json ├── M8 Running Containers on AKS ├── 01-aks.ps1 ├── example-vote-namespace.yml ├── example-vote-v2.yml ├── example-vote.yml └── sample-app.yaml ├── M9 Securing Containers └── README.md ├── README.md ├── SampleBackend ├── .gitignore ├── Controllers │ └── ValuesController.cs ├── Dockerfile ├── Program.cs ├── Properties │ └── launchSettings.json ├── README.md ├── SampleBackend.csproj ├── Startup.cs ├── appsettings.Development.json ├── appsettings.json └── win.Dockerfile ├── SampleWebApp ├── .gitignore ├── .vscode │ ├── launch.json │ └── tasks.json ├── Controllers │ ├── GreetingController.cs │ ├── HomeController.cs │ └── IndexViewModel.cs ├── Dockerfile ├── Program.cs ├── Properties │ └── launchSettings.json ├── README.md ├── Startup.cs ├── Views │ ├── Home │ │ └── Index.cshtml │ ├── Shared │ │ └── _Layout.cshtml │ └── _ViewStart.cshtml ├── appsettings.Development.json ├── appsettings.json ├── basic.Dockerfile ├── multi-stage.Dockerfile ├── samplewebapp.csproj └── win.Dockerfile ├── ServiceFabricWinDemo ├── .gitignore ├── ServiceFabricWinDemo.sln └── ServiceFabricWinDemo │ ├── ApplicationPackageRoot │ ├── ApplicationManifest.xml │ ├── SampleBackendPkg │ │ ├── Config │ │ │ └── Settings.xml │ │ └── ServiceManifest.xml │ └── SampleWebAppPkg │ │ ├── Config │ │ └── Settings.xml │ │ └── ServiceManifest.xml │ ├── ApplicationParameters │ ├── Cloud.xml │ ├── Local.1Node.xml │ └── Local.5Node.xml │ ├── PublishProfiles │ ├── Cloud.xml │ ├── Local.1Node.xml │ └── Local.5Node.xml │ ├── Scripts │ └── Deploy-FabricApplication.ps1 │ ├── ServiceFabricWinDemo.sfproj │ └── packages.config └── global.json.xyz /.gitignore: -------------------------------------------------------------------------------- 1 | out/ -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/aspnetdocker/bin/Debug/netcoreapp2.1/aspnetdocker.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/aspnetdocker", 16 | "stopAtEntry": false, 17 | "internalConsoleOptions": "openOnSessionStart", 18 | "launchBrowser": { 19 | "enabled": true, 20 | "args": "${auto-detect-url}", 21 | "windows": { 22 | "command": "cmd.exe", 23 | "args": "/C start ${auto-detect-url}" 24 | }, 25 | "osx": { 26 | "command": "open" 27 | }, 28 | "linux": { 29 | "command": "xdg-open" 30 | } 31 | }, 32 | "env": { 33 | "ASPNETCORE_ENVIRONMENT": "Development" 34 | }, 35 | "sourceFileMap": { 36 | "/Views": "${workspaceFolder}/Views" 37 | } 38 | }, 39 | { 40 | "name": ".NET Core Attach", 41 | "type": "coreclr", 42 | "request": "attach", 43 | "processId": "${command:pickProcess}" 44 | } 45 | ,] 46 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/aspnetdocker/aspnetdocker.csproj" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /M2 Introducing Containers on Azure/README.md: -------------------------------------------------------------------------------- 1 | # Useful Links 2 | 3 | - [Docker](https://www.docker.com/) 4 | - [Azure Container Registry](https://azure.microsoft.com/en-us/services/container-registry/) 5 | - [Docker Hub](https://hub.docker.com/) 6 | - [Azure Container Instances](https://azure.microsoft.com/en-us/services/container-instances/) 7 | - [Azure Web App for Containers](https://azure.microsoft.com/en-us/services/app-service/containers/) 8 | - [Azure Service Fabric](https://azure.microsoft.com/en-us/services/service-fabric/) 9 | - [Azure Kubernetes Service](https://azure.microsoft.com/en-us/services/kubernetes-service/) 10 | 11 | -------------------------------------------------------------------------------- /M3 Running Containers Locally/running-postgres.ps1: -------------------------------------------------------------------------------- 1 | # start a new container running postgres with an attached volume 2 | docker run -e POSTGRES_PASSWORD=password -d -p 5432:5432 -v postgres-data:/var/lib/postgresql/data ` 3 | --name postgres1 postgres 4 | 5 | # run an interactive shell against our container 6 | docker exec -it postgres1 sh 7 | 8 | # inside the shell: 9 | createdb -U postgres mydb # create a new db 10 | psql -U postgres mydb # connect to the db with the postgres CLI tool 11 | CREATE TABLE people (id int, name varchar(80)); # create a table 12 | INSERT INTO people (id,name) VALUES (2, 'Steph'); # insert a row into the table 13 | \q # exit the postgres CLI 14 | exit # exit the interactive shell 15 | 16 | # stop and delete the postgres1 container 17 | docker rm -f postgres1 18 | 19 | # check that the postgres-data volume still exists: 20 | docker volume ls 21 | 22 | # start a brand new container connected to the same volume 23 | docker run -e POSTGRES_PASSWORD=password -d -p 5432:5432 -v postgres-data:/var/lib/postgresql/data ` 24 | --name postgres2 postgres 25 | 26 | # run an interactive shell against this container 27 | docker exec -it postgres2 sh 28 | 29 | # inside the shell 30 | psql -U postgres mydb # connect to the db with the postgres CLI tool 31 | SELECT * FROM people; # check that the data we entered previously is still there 32 | \q # exit the postgres CLI 33 | exit # exit the interactive shell 34 | 35 | # stop delete the second container 36 | docker rm -f postgres2 37 | 38 | # delete the volume containing the database 39 | docker volume rm postgres-data 40 | -------------------------------------------------------------------------------- /M3 Running Containers Locally/running-redis.ps1: -------------------------------------------------------------------------------- 1 | # first follow instructions at https://docs.docker.com/install/ to install docker 2 | 3 | # run a new redis container 4 | docker run -d -p 6379:6379 --name redis1 redis 5 | 6 | # see that this container is running 7 | docker ps 8 | 9 | # view the log output for the container 10 | # (should see "ready to accept connections") 11 | docker logs redis1 12 | 13 | # see the images we have on our computer 14 | docker image ls 15 | 16 | # run an interactive shell 17 | docker exec -it redis1 sh 18 | 19 | # some commands to try inside the shell: 20 | ls -al # view contents of the container file system 21 | redis-cli # start the redis CLI 22 | ping # should respond with 'pong' 23 | set name mark # set a value in the cache 24 | get name # should respond with 'mark' 25 | incr counter # increment (and create) a new counte 26 | incr counter # increment it again 27 | get counter # should respond with '2' 28 | exit # exit from the redis CLI 29 | exit # exit from the interactive shell 30 | 31 | # run a second redis container, linked to the first and open an interactive shell 32 | docker run -it --rm --link redis1:redis --name client1 redis sh 33 | 34 | # some commands to try inside the shell 35 | redis-cli -h redis # start the redis CLI but connect to the other container 36 | get name # should respond with 'mark' 37 | get counter # should respond with '2' 38 | exit # exit from the redis CLI 39 | exit # exit from the interactive shell 40 | 41 | # observe that the second redis container is no longer running 42 | docker ps 43 | 44 | # stop the first redis container 45 | docker stop redis1 46 | 47 | # see all containers, even stopped ones (will only see redis1) 48 | docker ps -a 49 | 50 | # delete the docker redis container 51 | docker rm redis1 52 | 53 | # delete the redis image 54 | docker image rm redis -------------------------------------------------------------------------------- /M4 Creating Docker Images/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | 4 | # Copy csproj and restore as distinct layers 5 | COPY *.csproj ./ 6 | RUN dotnet restore 7 | 8 | # Copy everything else and build 9 | COPY . ./ 10 | RUN dotnet publish -c Release -o out 11 | 12 | # Build runtime image 13 | FROM mcr.microsoft.com/dotnet/aspnet:3.1 14 | WORKDIR /app 15 | COPY --from=build-env /app/out . 16 | ENTRYPOINT ["dotnet", "samplewebapp.dll"] -------------------------------------------------------------------------------- /M4 Creating Docker Images/README.md: -------------------------------------------------------------------------------- 1 | ### Module 4 Demo Files 2 | 3 | This folder contains the following Dockerfiles for the `SampleWebApp` ASP.NET Core application: 4 | 5 | - `basic.Dockerfile` - The simplest possible Dockerfile that assumes you've already built the application locally to the `out` folder 6 | - `multi-stage.Dockerfile` - This Dockerfile builds the ASP.NET Core application within a Docker container and then creates a Docker image to run the application. 7 | - `Dockerfile` - same as `multi-stage.Dockerfile` 8 | - `win.Dockerfile` - The equivalent of the basic Dockerfile, but explicitly targeting Windows 9 | 10 | It contains the following PowerShell scripts. These are not intended to be run as entire scripts, but to run them you should execute the commands one by one. 11 | 12 | - `m4-01-build-dockerfile.ps1` - shows the commands used in the Pluralsight course to build using the `basic.Dockerfile` 13 | - `m4-02-build-dockerfile-v2,ps1` - shows the commands used in the Pluralsight course to build using `multi-stage.Dockerfile` 14 | - `m4-03-azure-container-registry.ps1` - shows the commands used in the Pluralsight course to create an Azure Container Registry and upload a Docker image to it. 15 | 16 | ### Running the examples. 17 | 18 | To run these examples you should be in the `SampleWebApp` folder, which contains the source code for the ASP.NET Core application (and copies of these Dockerfiles). -------------------------------------------------------------------------------- /M4 Creating Docker Images/basic.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:3.1 2 | WORKDIR /app 3 | COPY ./out . 4 | ENTRYPOINT ["dotnet", "samplewebapp.dll"] -------------------------------------------------------------------------------- /M4 Creating Docker Images/m4-01-build-dockerfile.ps1: -------------------------------------------------------------------------------- 1 | # you need to be in the current folder of SampleWebApp to run this 2 | 3 | # before we start, we should have built the sample web app with 4 | # n.b. you need the ASP.NET SDK installed locally to run this 5 | dotnet publish -c Release -o out 6 | 7 | # build a docker image from the dockerfile 8 | docker build -t samplewebapp:basic -f basic.Dockerfile . 9 | 10 | # see the images we have 11 | docker image ls 12 | 13 | # run the image 14 | docker run -d -p 8080:80 --name myapp samplewebapp:basic 15 | 16 | # test it is working: 17 | http://localhost:8080 18 | 19 | # delete the container 20 | docker rm -f myapp -------------------------------------------------------------------------------- /M4 Creating Docker Images/m4-02-build-dockerfile-v2.ps1: -------------------------------------------------------------------------------- 1 | # you need to be in the current folder of SampleWebApp to run this 2 | 3 | # build our docker image and tag it v2 4 | docker build -t samplewebapp:v2 -f multi-stage.Dockerfile . 5 | 6 | # run our image 7 | docker run -d -p 8080:80 --name myappv2 samplewebapp:v2 8 | 9 | # test it 10 | http://localhost:8080 11 | 12 | # delete the container v2 13 | docker rm -f myappv2 -------------------------------------------------------------------------------- /M4 Creating Docker Images/m4-03-azure-container-registry.ps1: -------------------------------------------------------------------------------- 1 | # before you begin - make sure you're logged in to the azure CLI 2 | az login 3 | 4 | # ensure you choose the correct azure subscription if you have more than one 5 | az account set -s YourSub 6 | 7 | # create a resource group 8 | $resourceGroup = "PluralsightAcr" 9 | az group create -n $resourceGroup -l westeurope 10 | 11 | # create a new Azure container registry 12 | # IMPORTANT - use your own name here - it must be unique 13 | $registryName = "exampleacr1234" 14 | az acr create -g $resourceGroup -n $registryName --sku Basic 15 | 16 | # log in to our container registry 17 | az acr login -n $registryName 18 | 19 | # get the login server name 20 | $loginServer = az acr show -n $registryName ` 21 | --query loginServer --output tsv 22 | # OR: az acr list -g $resourceGroup -q "[].{acrLoginServer:loginServer}" -o table 23 | 24 | # see the images we have - should have samplewebapp:v2 25 | docker image ls 26 | 27 | # give it a new tag 28 | docker tag samplewebapp:v2 $loginServer/samplewebapp:v2 29 | 30 | # push the image to our Azure Container Registry 31 | docker push $loginServer/samplewebapp:v2 32 | 33 | # view the images in our ACR 34 | az acr repository list -n $registryName -o table 35 | 36 | # view the tags for the samplewebapp repository 37 | az acr repository show-tags -n $registryName --repository samplewebapp -o table 38 | 39 | # delete a repository from the container registry 40 | az acr repository delete -n $registryName -t samplewebapp:v2 41 | 42 | # to delete everything we made in this demo 43 | az group delete -n $resourceGroup -------------------------------------------------------------------------------- /M4 Creating Docker Images/multi-stage.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | 4 | # Copy csproj and restore as distinct layers 5 | COPY *.csproj ./ 6 | RUN dotnet restore 7 | 8 | # Copy everything else and build 9 | COPY . ./ 10 | RUN dotnet publish -c Release -o out 11 | 12 | # Build runtime image 13 | FROM mcr.microsoft.com/dotnet/aspnet:3.1 14 | WORKDIR /app 15 | COPY --from=build-env /app/out . 16 | ENTRYPOINT ["dotnet", "samplewebapp.dll"] 17 | -------------------------------------------------------------------------------- /M4 Creating Docker Images/win.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:3.1-nanoserver-2004 AS build-env 2 | WORKDIR /app 3 | 4 | # Copy csproj and restore as distinct layers 5 | COPY *.csproj ./ 6 | RUN dotnet restore 7 | 8 | # Copy everything else and build 9 | COPY . ./ 10 | RUN dotnet publish -c Release -o out 11 | 12 | # Build runtime image 13 | FROM mcr.microsoft.com/dotnet/aspnet:3.1-nanoserver-2004 14 | WORKDIR /app 15 | COPY --from=build-env /app/out . 16 | ENTRYPOINT ["dotnet", "samplewebapp.dll"] -------------------------------------------------------------------------------- /M5 Running Containers on Azure Container Instances/demo-1-ghost-aci.ps1: -------------------------------------------------------------------------------- 1 | # create a new resource group 2 | $resourceGroup = "AciGhostDemo" 3 | $location = "westeurope" 4 | az group create -n $resourceGroup -l $location 5 | 6 | # create a docker container using the ghost image from dockerhub 7 | $containerGroupName = "ghost-blog1" 8 | $dnsNameLabel = "ghostaci" 9 | az container create -g $resourceGroup -n $containerGroupName ` 10 | --image ghost ` 11 | --ports 2368 ` 12 | --ip-address public ` 13 | --dns-name-label $dnsNameLabel 14 | 15 | # see details about this container 16 | az container show ` 17 | -g $resourceGroup -n $containerGroupName 18 | 19 | # test it out 20 | Start-Process "http://$dnsNameLabel.westeurope.azurecontainer.io:2368" 21 | Start-Process "http://$dnsNameLabel.westeurope.azurecontainer.io:2368/admin" 22 | 23 | # view the logs 24 | az container logs ` 25 | -n $containerGroupName -g $resourceGroup 26 | 27 | # clean up everything 28 | az group delete -n $resourceGroup -y -------------------------------------------------------------------------------- /M5 Running Containers on Azure Container Instances/demo-2-private-container-registry.ps1: -------------------------------------------------------------------------------- 1 | # this demo to create a private container registry 2 | 3 | # create a resource group to use 4 | $resourceGroup = "AciPrivateRegistryDemo" 5 | $location = "westeurope" 6 | az group create -n $resourceGroup -l $location 7 | 8 | # ACR we'll be using 9 | $acrName = "pluralsightacr" 10 | 11 | # if we've not already created an Azure Container Registry 12 | # az acr create -g $resourceGroup -n $acrName --sku Basic --admin-enabled true 13 | 14 | # login to the registry with docker 15 | $acrPassword = az acr credential show -n $acrName ` 16 | --query "passwords[0].value" -o tsv 17 | $loginServer = az acr show -n $acrName ` 18 | --query loginServer --output tsv 19 | 20 | # log in to the ACR 21 | az acr login -n $acrName 22 | 23 | # if we want to use docker login instead: 24 | # docker login -u $acrName -p $acrPassword $loginServer 25 | 26 | $storageAccountName = "acishare$(Get-Random ` 27 | -Minimum 1000 -Maximum 10000)" 28 | 29 | # create a storage account 30 | az storage account create -g $resourceGroup ` 31 | -n $storageAccountName ` 32 | --sku Standard_LRS 33 | 34 | # get the connection string for our storage account 35 | $storageConnectionString = ` 36 | az storage account show-connection-string ` 37 | -n $storageAccountName -g $resourceGroup ` 38 | --query connectionString -o tsv 39 | # export it as an environment variable 40 | $env:AZURE_STORAGE_CONNECTION_STRING = $storageConnectionString 41 | 42 | # Create the file share 43 | $shareName="acishare" 44 | az storage share create -n $shareName 45 | 46 | # get the key for this storage account 47 | $storageKey=$(az storage account keys list ` 48 | -g $resourceGroup --account-name $storageAccountName ` 49 | --query "[0].value" --output tsv) 50 | 51 | # tag the image we want to use in our registry 52 | $image = "samplewebapp:latest" # can add a tag here 53 | $imageTag = "$loginServer/$image" 54 | docker tag $image $imageTag 55 | 56 | # push the image to our registry 57 | docker push $imageTag 58 | 59 | # see what images are in our registry 60 | az acr repository list -n $acrName --output table 61 | 62 | # create a new container group using the image from the private registry 63 | # username used to need to be $loginServer, but now seems can be $acrname 64 | $containerGroupName = "aci-acr" 65 | az container create -g $resourceGroup ` 66 | -n $containerGroupName ` 67 | --image $imageTag --cpu 1 --memory 1 ` 68 | --registry-username $acrName ` 69 | --registry-password $acrPassword ` 70 | --azure-file-volume-account-name $storageAccountName ` 71 | --azure-file-volume-account-key $storageKey ` 72 | --azure-file-volume-share-name $shareName ` 73 | --azure-file-volume-mount-path "/home" ` 74 | -e TestSetting=FromAzCli2 TestFileLocation=/home/message.txt ` 75 | --dns-name-label "aciacr" --ports 80 76 | 77 | 78 | # get the site address and launch in a browser 79 | $fqdn = az container show -g $resourceGroup -n $containerGroupName ` 80 | --query ipAddress.fqdn -o tsv 81 | Start-Process "http://$($fqdn)" 82 | 83 | # view the logs for our container 84 | az container logs -n $containerGroupName -g $resourceGroup 85 | 86 | az container exec -n $containerGroupName -g $resourceGroup --exec-command sh 87 | 88 | # within the container: 89 | echo "hello" > /home/message.txt 90 | exit 91 | 92 | az storage file list -s $shareName -o table 93 | 94 | $downloadPath = "$home\Downloads\message.txt" 95 | az storage file download -s $shareName -p "message.txt" ` 96 | --dest $downloadPath 97 | Start-Process $downloadPath 98 | 99 | 100 | az container delete -g $resourceGroup -n $containerGroupName 101 | 102 | # delete the resource group (ACR and container group) 103 | az group delete -n $resourceGroup -y -------------------------------------------------------------------------------- /M6 Running Containers on Web Apps for Containers/ci-cd-appservice.ps1: -------------------------------------------------------------------------------- 1 | # get logged in to the azure cli 2 | az login 3 | az account show --query name -o tsv 4 | az account set -s "MySubscription" 5 | 6 | # create a resource group in our preferred location to use 7 | $resourceGroup = "cicdappservice" 8 | $location = "westeurope" 9 | az group create -l $location -n $resourceGroup 10 | 11 | # create an app service plan to host 12 | $planName="cicdappservice" 13 | az appservice plan create -n $planName -g $resourceGroup -l $location ` 14 | --is-linux --sku S1 15 | 16 | 17 | # n.b. can't use anything but docker hub here 18 | # so we have to arbitrarily pick a runtime --runtime "node|6.2" or a public image like scratch 19 | $appName="cicd-pluralsight" 20 | az webapp create -n $appName -g $resourceGroup --plan $planName -i "scratch" 21 | 22 | $acrName = "pluralsightacr" 23 | $acrLoginServer = az acr show -n $acrName --query loginServer -o tsv 24 | $acrUserName = az acr credential show -n $acrName --query username -o tsv 25 | $acrPassword = az acr credential show -n $acrName --query passwords[0].value -o tsv 26 | 27 | # https://github.com/Azure/azure-cli/pull/3888/files - maybe don't need creds? 28 | az webapp config container set -n $appName -g $resourceGroup ` 29 | -c "$acrLoginServer/samplewebapp:latest" ` 30 | -r "https://$acrLoginServer" ` 31 | -u $acrUserName -p $acrPassword 32 | 33 | az webapp show -n $appName -g $resourceGroup --query "defaultHostName" -o tsv 34 | 35 | # create a staging slot (cloning from production slot's settings) 36 | az webapp deployment slot create -g $resourceGroup -n $appName ` 37 | -s staging --configuration-source $appName 38 | 39 | az webapp show -n $appName -g $resourceGroup -s staging --query "defaultHostName" -o tsv 40 | 41 | 42 | # enable CD for the staging slot 43 | az webapp deployment container config -g $resourceGroup -n $appName ` 44 | -s staging --enable-cd true 45 | 46 | # get the webhook 47 | $cicdurl = az webapp deployment container show-cd-url -s staging ` 48 | -n $appName -g $resourceGroup --query CI_CD_URL -o tsv 49 | 50 | # to configure the webhook on an ACR registry 51 | az acr webhook create --registry $acrName --name myacrwebhook --actions push ` 52 | --uri $cicdurl 53 | 54 | # push a new version of our app to the ACR 55 | docker push pluralsightacr.azurecr.io/samplewebapp:latest 56 | 57 | # perform a slot swap 58 | az webapp deployment slot swap -g $resourceGroup -n $appName ` 59 | --slot staging --target-slot production 60 | 61 | # clean up 62 | az group delete --name $resourceGroup --yes --no-wait 63 | 64 | # delete the webhook 65 | az acr webhook delete --registry $acrName --name myacrwebhook -------------------------------------------------------------------------------- /M6 Running Containers on Web Apps for Containers/wordpress-appservice.ps1: -------------------------------------------------------------------------------- 1 | # in this demo we run a wordpress site in linux container mode 2 | 3 | # get logged in to the azure cli 4 | az login 5 | az account show --query name -o tsv 6 | az account set -s "MySubscription" 7 | 8 | # create a resource group in our preferred location to use 9 | $resourceGroup = "wordpressappservice" 10 | $location = "westeurope" 11 | az group create -l $location -n $resourceGroup 12 | 13 | # create an app service plan to host 14 | $planName="wordpressappservice" 15 | az appservice plan create -n $planName -g $resourceGroup -l $location --is-linux --sku S1 16 | 17 | # create a MySql database 18 | # https://docs.microsoft.com/en-us/azure/mysql/quickstart-create-mysql-server-database-using-azure-cli 19 | Function Get-RandomString($length) 20 | { 21 | $validChars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWZYZ".ToCharArray() 22 | Return -join ((1..$length) | ForEach-Object { $validChars | Get-Random | ForEach-Object {[char]$_} }) 23 | } 24 | 25 | $mysqlServerName = "mysql-$((Get-RandomString 4).ToLower())" 26 | $adminUser = "wpadmin" 27 | $adminPassword = Get-RandomString 20 28 | # supported mysql versions: https://docs.microsoft.com/en-us/azure/mysql/concepts-supported-versions 29 | # n.b. location is required for this command 30 | # this wordpress demo also requires SSL enforcement to be disabled 31 | az mysql server create -g $resourceGroup -n $mysqlServerName ` 32 | --admin-user $adminUser --admin-password $adminPassword ` 33 | -l $location ` 34 | --ssl-enforcement Disabled ` 35 | --sku-name GP_Gen5_2 --version 5.7 36 | 37 | # open the firewall (use 0.0.0.0 to allow all Azure traffic for now) 38 | az mysql server firewall-rule create -g $resourceGroup ` 39 | --server $mysqlServerName --name AllowAppService ` 40 | --start-ip-address 0.0.0.0 --end-ip-address 0.0.0.0 41 | 42 | # create a new webapp based on our DockerHub image 43 | $appName="wordpress-$((Get-RandomString 4).ToLower())" 44 | $dockerRepo = "wordpress" # https://hub.docker.com/r/_/wordpress/ 45 | az webapp create -n $appName -g $resourceGroup --plan $planName -i $dockerRepo 46 | 47 | $wordpressDbHost = (az mysql server show -g $resourceGroup -n $mysqlServerName --query "fullyQualifiedDomainName" -o tsv) 48 | 49 | # configure settings 50 | az webapp config appsettings set ` 51 | -n $appName -g $resourceGroup --settings ` 52 | WORDPRESS_DB_HOST=$wordpressDbHost ` 53 | WORDPRESS_DB_USER="$adminUser@$mysqlServerName" ` 54 | WORDPRESS_DB_PASSWORD="$adminPassword" 55 | 56 | # launch in a browser 57 | $site = az webapp show -n $appName -g $resourceGroup --query "defaultHostName" -o tsv 58 | Start-Process https://$site 59 | 60 | # scale up app service 61 | az appservice plan update -n $planName -g $resourceGroup --number-of-workers 3 62 | 63 | 64 | # clean up 65 | az group delete --name $resourceGroup --yes --no-wait -------------------------------------------------------------------------------- /M7 Running Containers on Service Fabric/default-params.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "frontEndReplicaCount":{ 6 | "value": "1" 7 | }, 8 | "serviceReplicaCount":{ 9 | "value": "1" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /M7 Running Containers on Service Fabric/scale-front-end-params.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "frontEndReplicaCount":{ 6 | "value": "3" 7 | }, 8 | "serviceReplicaCount":{ 9 | "value": "1" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /M7 Running Containers on Service Fabric/service-fabric-mesh-demo.ps1: -------------------------------------------------------------------------------- 1 | # https://github.com/Azure-Samples/service-fabric-mesh/blob/master/templates/todolist/mesh_rp.windows.json 2 | # https://docs.microsoft.com/en-us/azure/service-fabric-mesh/service-fabric-mesh-tutorial-template-deploy-app 3 | # https://github.com/Azure-Samples/service-fabric-mesh/tree/master/src/todolistapp 4 | 5 | # check we are using the right subscription 6 | az account show --query name -o tsv 7 | 8 | # check if the extension we need is available 9 | az extension list -o table 10 | # to install 11 | az extension add --name mesh 12 | # to upgrade 13 | az extension update --name mesh 14 | 15 | # create a resource group 16 | $resGroup = "ServiceFabricMeshTest" 17 | az group create -n $resGroup -l "westeurope" 18 | 19 | # deploy the mesh application 20 | $templateFile = ".\sfmesh-windows.json" 21 | az mesh deployment create -g $resGroup --template-file $templateFile 22 | 23 | # should result in a message like: 24 | #> application sampleapp has been deployed successfully on network sampleappNetwork with public ip address 13.94.227.208 25 | #> To recieve additional information run the following to get the status of the application deployment. 26 | #> az mesh app show --resource-group ServiceFabricMeshTest --name sampleapp 27 | 28 | # get public ip address 29 | $networkName = "sampleappNetwork" 30 | $publicIp = az mesh network show -g $resGroup --name $networkName --query "ingressConfig.publicIpAddress" -o tsv 31 | 32 | # let's see if it's working 33 | Start-Process http://$publicIp 34 | 35 | # get status of application 36 | $appName = "sampleapp" 37 | az mesh app show -g $resGroup --name $appName 38 | 39 | # view logs for front-end container 40 | $frontEndServiceName = "frontend" 41 | $frontEndCodePackageName = "frontend" 42 | az mesh code-package-log get -g $resGroup --application-name $appName --service-name $frontEndServiceName --replica-name 0 --code-package-name $frontEndCodePackageName 43 | 44 | # see number of front end instances 45 | az mesh service show -g $resGroup --name $frontEndServiceName --app-name $appName --query "replicaCount" 46 | 47 | # scale up with a fresh deployment 48 | az mesh deployment create -g $resGroup --template-file $templateFile --parameters .\scale-front-end-params.json 49 | # often seems to fail (even though replicas are created) with error like 50 | # Unable to edit or replace deployment 'sfmesh-windows': previous deployment from '10/23/2018 12:55:17 PM' is still active (expiration time is '10/30/2018 12:55:06 PM'). Please see https://aka.ms/arm-deploy for usage details. 51 | 52 | # see number of front end instances 53 | az mesh service show -g $resGroup --name $frontEndServiceName --app-name $appName --query "replicaCount" 54 | 55 | # see summary of services 56 | az mesh service list -g $resGroup --app-name $appName -o table 57 | 58 | # delete everything 59 | az group delete -n $resGroup -y -------------------------------------------------------------------------------- /M7 Running Containers on Service Fabric/service-fabric-mesh-volumes.ps1: -------------------------------------------------------------------------------- 1 | # https://github.com/Azure-Samples/service-fabric-mesh/blob/master/templates/todolist/mesh_rp.windows.json 2 | # https://docs.microsoft.com/en-us/azure/service-fabric-mesh/service-fabric-mesh-tutorial-template-deploy-app 3 | # https://github.com/Azure-Samples/service-fabric-mesh/tree/master/src/todolistapp 4 | 5 | az account show --query name -o tsv 6 | 7 | # check if the extension we need is available 8 | az extension list 9 | # to install 10 | az extension add --name mesh 11 | # to upgrade 12 | az extension update --name mesh 13 | 14 | # create a resource group 15 | $resGroup = "ServiceFabricMeshTest" 16 | az group create -n $resGroup -l "westeurope" 17 | 18 | $storageAccountName = "acishare$(Get-Random ` 19 | -Minimum 1000 -Maximum 10000)" 20 | 21 | # create a storage account 22 | az storage account create -g $resGroup ` 23 | -n $storageAccountName ` 24 | --sku Standard_LRS 25 | 26 | # get the connection string for our storage account 27 | $storageConnectionString = ` 28 | az storage account show-connection-string ` 29 | -n $storageAccountName -g $resGroup ` 30 | --query connectionString -o tsv 31 | # export it as an environment variable 32 | $env:AZURE_STORAGE_CONNECTION_STRING = $storageConnectionString 33 | 34 | # Create the file share 35 | $shareName="sfshare" 36 | az storage share create -n $shareName 37 | 38 | # get the key for this storage account 39 | $storageKey=$(az storage account keys list ` 40 | -g $resGroup --account-name $storageAccountName ` 41 | --query "[0].value" --output tsv) 42 | 43 | 44 | $params = @{fileShareName=@{value=$shareName}; 45 | storageAccountName=@{value=$storageAccountName}; 46 | storageAccountKey=@{value=$storageKey}} | ConvertTo-Json -Compress 47 | 48 | # deploy the mesh application 49 | $templateFile = ".\m7-sfmesh-volumes.json" 50 | az mesh deployment create -g $resGroup --template-file $templateFile ` 51 | --parameters "{'fileShareName':{'value':'$shareName'},'storageAccountName':{'value':'$storageAccountName'},'storageAccountKey':{'value':'$storageKey'}}" 52 | 53 | # get public ip address 54 | $publicIp = az mesh network show -g $resGroup --name sampleAppNetwork --query "ingressConfig.publicIpAddress" -o tsv 55 | 56 | # let's see if it's working 57 | start http://$publicIp 58 | 59 | # get status of application 60 | $appName = "sampleapp" 61 | az mesh app show -g $resGroup --name $appName 62 | 63 | # view logs for front-end container 64 | $frontendServiceName = "frontend" 65 | $frontendCodePackageName = "frontend" 66 | az mesh code-package-log get -g $resGroup --application-name $appName --service-name $frontendServiceName --replica-name 0 --code-package-name $frontendCodePackageName 67 | 68 | # see summary of services 69 | az mesh service list -g $resGroup --app-name $appName -o table 70 | 71 | # scale up the back end 72 | az mesh deployment create -g $resGroup --template-file $templateFile ` 73 | --parameters "{'fileShareName':{'value':'$shareName'},'storageAccountName':{'value':'$storageAccountName'},'storageAccountKey':{'value':'$storageKey'},'serviceReplicaCount':{'value':'3'}}" 74 | 75 | # delete everything 76 | az group delete -n $resGroup -y -------------------------------------------------------------------------------- /M7 Running Containers on Service Fabric/service-fabric-mesh-voting.ps1: -------------------------------------------------------------------------------- 1 | az account show --query name -o tsv 2 | 3 | # check if the extension we need is available 4 | az extension list 5 | # to install 6 | az extension add --name mesh 7 | # to upgrade 8 | az extension update --name mesh 9 | 10 | # create a resource group 11 | $resGroup = "ServiceFabricMeshVote" 12 | az group create -n $resGroup -l "westeurope" 13 | 14 | # deploy the mesh application 15 | $templateFile = ".\sfmesh-example-voting-app.json" 16 | az mesh deployment create -g $resGroup --template-file $templateFile 17 | # [System.Console]::ResetColor() 18 | # get public ip address 19 | $networkName = "votingNetwork" 20 | $publicIp = az mesh network show -g $resGroup --name $networkName --query "ingressConfig.publicIpAddress" -o tsv 21 | 22 | # let's see if it's working 23 | Start-Process http://$($publicIp):8081 # voting 24 | Start-Process http://$($publicIp):8082 # results 25 | 26 | # get status of application 27 | $appName = "votingApp" 28 | az mesh app show -g $resGroup --name $appName 29 | 30 | # view logs for vote container 31 | az mesh code-package-log get -g $resGroup --application-name $appName --service-name vote --replica-name 0 --code-package-name vote 32 | 33 | # see summary of services 34 | az mesh service list -g $resGroup --app-name $appName -o table 35 | 36 | # explore the result service 37 | az mesh service show -g $resGroup --app-name $appName --name result 38 | 39 | # look at network 40 | az mesh network show -n votingNetwork -g $resGroup 41 | 42 | # scale up vote container to 3 instances (currently seems unreliable) 43 | # https://github.com/Azure/service-fabric-mesh-preview/issues/266 44 | az mesh deployment create -g $resGroup --template-file $templateFile ` 45 | --parameters "{'workerReplicaCount':{'value':'3'}}" 46 | 47 | # see services again 48 | az mesh service list -g $resGroup --app-name $appName -o table 49 | 50 | # explore the vote service 51 | az mesh service show -g $resGroup --app-name $appName --name vote -o table 52 | 53 | # see the replicas 54 | az mesh service-replica list -g $resGroup --app-name $appName --service-name vote -o table 55 | 56 | # explore a particular replica 57 | az mesh service-replica show -g $resGroup --app-name $appName --service-name vote --replica-name 0 58 | 59 | # delete everything 60 | az group delete -n $resGroup -y -------------------------------------------------------------------------------- /M7 Running Containers on Service Fabric/sfmesh-example-voting-app-linux.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "voteReplicaCount":{ 6 | "defaultValue": "1", 7 | "type": "string", 8 | "metadata": { 9 | "description": "The number of service replicas for the vote service." 10 | } 11 | }, 12 | "resultReplicaCount":{ 13 | "defaultValue": "1", 14 | "type": "string", 15 | "metadata": { 16 | "description": "The number of service replicas for the result service." 17 | } 18 | }, 19 | "workerReplicaCount":{ 20 | "defaultValue": "1", 21 | "type": "string", 22 | "metadata": { 23 | "description": "The number of service replicas for the worker service." 24 | } 25 | } 26 | }, 27 | "resources": [ 28 | { 29 | "apiVersion": "2018-07-01-preview", 30 | "name": "votingApp", 31 | "type": "Microsoft.ServiceFabricMesh/applications", 32 | "location": "[resourceGroup().location]", 33 | "dependsOn": [ 34 | "Microsoft.ServiceFabricMesh/networks/votingNetwork" 35 | ], 36 | "properties": { 37 | "services": [ 38 | { 39 | "name": "vote", 40 | "properties": { 41 | "description": "vote service", 42 | "osType": "Linux", 43 | "codePackages": [ 44 | { 45 | "name": "vote", 46 | "image": "dockersamples/examplevotingapp_vote:before", 47 | "endpoints": [ 48 | { 49 | "name": "VoteListener", 50 | "port": 80 51 | } 52 | ], 53 | "resources": { 54 | "requests": { 55 | "cpu": "0.5", 56 | "memoryInGB": "1" 57 | } 58 | } 59 | } 60 | ], 61 | "replicaCount": "[parameters('voteReplicaCount')]", 62 | "networkRefs": [ 63 | { 64 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 65 | } 66 | ] 67 | } 68 | }, 69 | { 70 | "name": "result", 71 | "properties": { 72 | "description": "result service.", 73 | "osType": "Linux", 74 | "codePackages": [ 75 | { 76 | "name": "result", 77 | "image": "dockersamples/examplevotingapp_result:before", 78 | "endpoints": [ 79 | { 80 | "name": "ResultListener", 81 | "port": 80 82 | } 83 | ], 84 | "resources": { 85 | "requests": { 86 | "cpu": "0.5", 87 | "memoryInGB": "1" 88 | } 89 | } 90 | } 91 | ], 92 | "replicaCount": "[parameters('resultReplicaCount')]", 93 | "networkRefs": [ 94 | { 95 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 96 | } 97 | ] 98 | } 99 | }, 100 | { 101 | "name": "worker", 102 | "properties": { 103 | "description": "worker service.", 104 | "osType": "Linux", 105 | "codePackages": [ 106 | { 107 | "name": "worker", 108 | "image": "dockersamples/examplevotingapp_worker:latest", 109 | "resources": { 110 | "requests": { 111 | "cpu": "0.5", 112 | "memoryInGB": "1" 113 | } 114 | } 115 | } 116 | ], 117 | "replicaCount": "[parameters('workerReplicaCount')]", 118 | "networkRefs": [ 119 | { 120 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 121 | } 122 | ] 123 | } 124 | }, 125 | { 126 | "name": "redis", 127 | "properties": { 128 | "description": "redis queue service.", 129 | "osType": "Linux", 130 | "codePackages": [ 131 | { 132 | "name": "redis", 133 | "image": "registry.hub.docker.com/library/redis:alpine", 134 | "endpoints": [ 135 | { 136 | "name": "RedisListener", 137 | "port": 6379 138 | } 139 | ], 140 | "environmentVariables": [ 141 | 142 | ], 143 | "resources": { 144 | "requests": { 145 | "cpu": "0.5", 146 | "memoryInGB": "1" 147 | } 148 | } 149 | } 150 | ], 151 | "replicaCount": "1", 152 | "networkRefs": [ 153 | { 154 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 155 | } 156 | ] 157 | } 158 | }, 159 | { 160 | "name": "db", 161 | "properties": { 162 | "description": "db service.", 163 | "osType": "Linux", 164 | "codePackages": [ 165 | { 166 | "name": "db", 167 | "image": "registry.hub.docker.com/library/postgres:9.4", 168 | "endpoints": [ 169 | { 170 | "name": "DbListener", 171 | "port": 5432 172 | } 173 | ], 174 | "environmentVariables": [ 175 | 176 | ], 177 | "resources": { 178 | "requests": { 179 | "cpu": "0.5", 180 | "memoryInGB": "1" 181 | } 182 | } 183 | } 184 | ], 185 | "replicaCount": "1", 186 | "networkRefs": [ 187 | { 188 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 189 | } 190 | ] 191 | } 192 | } 193 | ], 194 | "description": "Sample voting app. https://github.com/sixeyed/example-voting-app" 195 | } 196 | }, 197 | { 198 | "apiVersion": "2018-07-01-preview", 199 | "name": "votingNetwork", 200 | "type": "Microsoft.ServiceFabricMesh/networks", 201 | "location": "[resourceGroup().location]", 202 | "dependsOn": [], 203 | "properties": { 204 | "description": "votingNetwork description.", 205 | "addressPrefix": "10.0.0.4/22", 206 | "ingressConfig": { 207 | "layer4": [ 208 | { 209 | "name": "VoteIngress", 210 | "publicPort": "8081", 211 | "applicationName": "votingApp", 212 | "serviceName": "vote", 213 | "endpointName": "VoteListener" 214 | }, 215 | { 216 | "name": "ResultIngress", 217 | "publicPort": "8082", 218 | "applicationName": "votingApp", 219 | "serviceName": "result", 220 | "endpointName": "ResultListener" 221 | } 222 | ] 223 | } 224 | } 225 | } 226 | ] 227 | } -------------------------------------------------------------------------------- /M7 Running Containers on Service Fabric/sfmesh-example-voting-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "voteReplicaCount":{ 6 | "defaultValue": "1", 7 | "type": "string", 8 | "metadata": { 9 | "description": "The number of service replicas for the vote service." 10 | } 11 | }, 12 | "resultReplicaCount":{ 13 | "defaultValue": "1", 14 | "type": "string", 15 | "metadata": { 16 | "description": "The number of service replicas for the result service." 17 | } 18 | }, 19 | "workerReplicaCount":{ 20 | "defaultValue": "1", 21 | "type": "string", 22 | "metadata": { 23 | "description": "The number of service replicas for the worker service." 24 | } 25 | } 26 | }, 27 | "resources": [ 28 | { 29 | "apiVersion": "2018-07-01-preview", 30 | "name": "votingApp", 31 | "type": "Microsoft.ServiceFabricMesh/applications", 32 | "location": "[resourceGroup().location]", 33 | "dependsOn": [ 34 | "Microsoft.ServiceFabricMesh/networks/votingNetwork" 35 | ], 36 | "properties": { 37 | "services": [ 38 | { 39 | "name": "vote", 40 | "properties": { 41 | "description": "vote service", 42 | "osType": "Windows", 43 | "codePackages": [ 44 | { 45 | "name": "vote", 46 | "image": "dockersamples/examplevotingapp_vote:dotnet-nanoserver", 47 | "endpoints": [ 48 | { 49 | "name": "VoteListener", 50 | "port": 80 51 | } 52 | ], 53 | "environmentVariables": [ 54 | 55 | ], 56 | "resources": { 57 | "requests": { 58 | "cpu": "0.5", 59 | "memoryInGB": "1" 60 | } 61 | } 62 | } 63 | ], 64 | "replicaCount": "[parameters('voteReplicaCount')]", 65 | "networkRefs": [ 66 | { 67 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 68 | } 69 | ] 70 | } 71 | }, 72 | { 73 | "name": "result", 74 | "properties": { 75 | "description": "result service.", 76 | "osType": "Windows", 77 | "codePackages": [ 78 | { 79 | "name": "result", 80 | "image": "dockersamples/examplevotingapp_result:dotnet-nanoserver", 81 | "endpoints": [ 82 | { 83 | "name": "ResultListener", 84 | "port": 20002 85 | } 86 | ], 87 | "environmentVariables": [ 88 | { 89 | "name": "ConnectionStrings:ResultData", 90 | "value": "Server=db;Port=4000;Database=votes;User=root;SslMode=None" 91 | }, 92 | { 93 | "name": "ASPNETCORE_URLS", 94 | "value": "http://+:20002" 95 | } 96 | ], 97 | "resources": { 98 | "requests": { 99 | "cpu": "0.5", 100 | "memoryInGB": "1" 101 | } 102 | } 103 | } 104 | ], 105 | "replicaCount": "[parameters('resultReplicaCount')]", 106 | "networkRefs": [ 107 | { 108 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 109 | } 110 | ] 111 | } 112 | }, 113 | { 114 | "name": "worker", 115 | "properties": { 116 | "description": "worker service.", 117 | "osType": "Windows", 118 | "codePackages": [ 119 | { 120 | "name": "worker", 121 | "image": "dockersamples/examplevotingapp_worker:dotnet-nanoserver", 122 | "endpoints": [ 123 | 124 | ], 125 | "environmentVariables": [ 126 | { 127 | "name": "ConnectionStrings:VoteData", 128 | "value": "Server=db;Port=4000;Database=votes;User=root;SslMode=None" 129 | } 130 | ], 131 | "resources": { 132 | "requests": { 133 | "cpu": "0.5", 134 | "memoryInGB": "1" 135 | } 136 | } 137 | } 138 | ], 139 | "replicaCount": "[parameters('workerReplicaCount')]", 140 | "networkRefs": [ 141 | { 142 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 143 | } 144 | ] 145 | } 146 | }, 147 | { 148 | "name": "message-queue", 149 | "properties": { 150 | "description": "message-queue service.", 151 | "osType": "Windows", 152 | "codePackages": [ 153 | { 154 | "name": "message-queue", 155 | "image": "registry.hub.docker.com/library/nats:nanoserver", 156 | "endpoints": [ 157 | { 158 | "name": "MessageQueueListener", 159 | "port": 4222 160 | } 161 | ], 162 | "environmentVariables": [ 163 | 164 | ], 165 | "resources": { 166 | "requests": { 167 | "cpu": "0.5", 168 | "memoryInGB": "1" 169 | } 170 | } 171 | } 172 | ], 173 | "replicaCount": "1", 174 | "networkRefs": [ 175 | { 176 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 177 | } 178 | ] 179 | } 180 | }, 181 | { 182 | "name": "db", 183 | "properties": { 184 | "description": "db service.", 185 | "osType": "Windows", 186 | "codePackages": [ 187 | { 188 | "name": "tidb", 189 | "image": "dockersamples/tidb:nanoserver", 190 | "endpoints": [ 191 | { 192 | "name": "DbListener", 193 | "port": 4000 194 | } 195 | ], 196 | "environmentVariables": [ 197 | 198 | ], 199 | "resources": { 200 | "requests": { 201 | "cpu": "0.5", 202 | "memoryInGB": "1" 203 | } 204 | } 205 | } 206 | ], 207 | "replicaCount": "1", 208 | "networkRefs": [ 209 | { 210 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'votingNetwork')]" 211 | } 212 | ] 213 | } 214 | } 215 | ], 216 | "description": "Sample voting app. https://github.com/sixeyed/example-voting-app" 217 | } 218 | }, 219 | { 220 | "apiVersion": "2018-07-01-preview", 221 | "name": "votingNetwork", 222 | "type": "Microsoft.ServiceFabricMesh/networks", 223 | "location": "[resourceGroup().location]", 224 | "dependsOn": [], 225 | "properties": { 226 | "description": "votingNetwork description.", 227 | "addressPrefix": "10.0.0.4/22", 228 | "ingressConfig": { 229 | "layer4": [ 230 | { 231 | "name": "VoteIngress", 232 | "publicPort": "8081", 233 | "applicationName": "votingApp", 234 | "serviceName": "vote", 235 | "endpointName": "VoteListener" 236 | }, 237 | { 238 | "name": "ResultIngress", 239 | "publicPort": "8082", 240 | "applicationName": "votingApp", 241 | "serviceName": "result", 242 | "endpointName": "ResultListener" 243 | } 244 | ] 245 | } 246 | } 247 | } 248 | ] 249 | } -------------------------------------------------------------------------------- /M7 Running Containers on Service Fabric/sfmesh-volumes.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "location": { 6 | "defaultValue": "westeurope", 7 | "type": "String", 8 | "metadata": { 9 | "description": "Location of the resources." 10 | } 11 | }, 12 | "frontEndImage": { 13 | "defaultValue": "markheath/samplewebapp:win1709", 14 | "type": "string", 15 | "metadata": { 16 | "description": "The container image for the front end web service." 17 | } 18 | }, 19 | "serviceImage": { 20 | "defaultValue": "markheath/samplebackend:win1709", 21 | "type": "string", 22 | "metadata": { 23 | "description": "The container image for the back end service." 24 | } 25 | }, 26 | "frontEndCpu": { 27 | "defaultValue": "0.5", 28 | "type": "string", 29 | "metadata": { 30 | "description": "The CPU resources for the front end web service." 31 | } 32 | }, 33 | "serviceCpu": { 34 | "defaultValue": "0.5", 35 | "type": "string", 36 | "metadata": { 37 | "description": "The CPU resources for the back end service." 38 | } 39 | }, 40 | "frontEndMemory":{ 41 | "defaultValue": "1.0", 42 | "type": "string", 43 | "metadata": { 44 | "description": "The memory in GB for the front end web service." 45 | } 46 | }, 47 | "serviceMemory":{ 48 | "defaultValue": "1.0", 49 | "type": "string", 50 | "metadata": { 51 | "description": "The memory in GB for the back end service." 52 | } 53 | }, 54 | "frontEndReplicaCount":{ 55 | "defaultValue": "1", 56 | "type": "string", 57 | "metadata": { 58 | "description": "The number of service replicas for the front end web service." 59 | } 60 | }, 61 | "serviceReplicaCount":{ 62 | "defaultValue": "1", 63 | "type": "string", 64 | "metadata": { 65 | "description": "The number of service replicas for the back end service." 66 | } 67 | }, 68 | "fileShareName": { 69 | "type": "string", 70 | "metadata": { 71 | "description": "Name of the Azure Files file share that provides the volume for the container." 72 | } 73 | }, 74 | "storageAccountName": { 75 | "type": "string", 76 | "metadata": { 77 | "description": "Name of the Azure storage account that contains the file share." 78 | } 79 | }, 80 | "storageAccountKey": { 81 | "type": "securestring", 82 | "metadata": { 83 | "description": "Access key for the Azure storage account that contains the file share." 84 | } 85 | } 86 | }, 87 | "resources": [ 88 | { 89 | "apiVersion": "2018-07-01-preview", 90 | "name": "sampleapp", 91 | "type": "Microsoft.ServiceFabricMesh/applications", 92 | "location": "[parameters('location')]", 93 | "dependsOn": [ 94 | "Microsoft.ServiceFabricMesh/networks/sampleappNetwork", 95 | "Microsoft.ServiceFabricMesh/volumes/sampleappVolumeWindows" 96 | ], 97 | "properties": { 98 | "services": [ 99 | { 100 | "name": "frontend", 101 | "properties": { 102 | "description": "Front end web app.", 103 | "osType": "Windows", 104 | "codePackages": [ 105 | { 106 | "name": "frontend", 107 | "image": "[parameters('frontEndImage')]", 108 | "volumeRefs": [ 109 | { 110 | "name": "[resourceId('Microsoft.ServiceFabricMesh/volumes', 'sampleappVolumeWindows')]", 111 | "destinationPath": "C:\\app\\data" 112 | }], 113 | "endpoints": [ 114 | { 115 | "name": "FrontEndListener", 116 | "port": 20001 117 | } 118 | ], 119 | "environmentVariables": [ 120 | { 121 | "name": "TestSetting", 122 | "value": "Set in ARM Template" 123 | }, 124 | { 125 | "name": "TestGetUri", 126 | "value": "http://backend:20002/api/values" 127 | }, 128 | { 129 | "name":"TestFileLocation", 130 | "value":"C:\\app\\data\\message.txt" 131 | }, 132 | { 133 | "name": "ASPNETCORE_URLS", 134 | "value": "http://+:20001" 135 | } 136 | ], 137 | "resources": { 138 | "requests": { 139 | "cpu": "[parameters('frontEndCpu')]", 140 | "memoryInGB": "[parameters('frontEndMemory')]" 141 | } 142 | } 143 | } 144 | ], 145 | "replicaCount": "[parameters('frontEndReplicaCount')]", 146 | "networkRefs": [ 147 | { 148 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'sampleappNetwork')]" 149 | } 150 | ] 151 | } 152 | }, 153 | { 154 | "name": "backend", 155 | "properties": { 156 | "description": "Backend web api service.", 157 | "osType": "Windows", 158 | "codePackages": [ 159 | { 160 | "name": "sampleapp", 161 | "image": "[parameters('serviceImage')]", 162 | "endpoints": [ 163 | { 164 | "name": "BackendListener", 165 | "port": 20002 166 | } 167 | ], 168 | "environmentVariables": [ 169 | { 170 | "name": "ASPNETCORE_URLS", 171 | "value": "http://+:20002" 172 | } 173 | ], 174 | "resources": { 175 | "requests": { 176 | "cpu": "[parameters('serviceCpu')]", 177 | "memoryInGB": "[parameters('serviceMemory')]" 178 | } 179 | } 180 | } 181 | ], 182 | "replicaCount": "[parameters('serviceReplicaCount')]", 183 | "networkRefs": [ 184 | { 185 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'sampleappNetwork')]" 186 | } 187 | ] 188 | } 189 | } 190 | ], 191 | "description": "Service Fabric Mesh app with volumes demo." 192 | } 193 | }, 194 | { 195 | "apiVersion": "2018-07-01-preview", 196 | "name": "sampleappNetwork", 197 | "type": "Microsoft.ServiceFabricMesh/networks", 198 | "location": "[parameters('location')]", 199 | "dependsOn": [], 200 | "properties": { 201 | "description": "Network for sample app", 202 | "addressPrefix": "10.0.0.4/22", 203 | "ingressConfig": { 204 | "layer4": [ 205 | { 206 | "name": "FrontEndIngress", 207 | "publicPort": "80", 208 | "applicationName": "sampleapp", 209 | "serviceName": "frontend", 210 | "endpointName": "FrontEndListener" 211 | } 212 | ] 213 | } 214 | } 215 | }, 216 | { 217 | "apiVersion": "2018-07-01-preview", 218 | "name": "sampleappVolumeWindows", 219 | "type": "Microsoft.ServiceFabricMesh/volumes", 220 | "location": "[parameters('location')]", 221 | "dependsOn": [], 222 | "properties": { 223 | "description": "Azure Files storage volume for front end web service.", 224 | "provider": "SFAzureFile", 225 | "azureFileParameters": { 226 | "shareName": "[parameters('fileShareName')]", 227 | "accountName": "[parameters('storageAccountName')]", 228 | "accountKey": "[parameters('storageAccountKey')]" 229 | } 230 | } 231 | } 232 | ] 233 | } -------------------------------------------------------------------------------- /M7 Running Containers on Service Fabric/sfmesh-windows.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2014-04-01-preview/deploymentTemplate.json", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "location": { 6 | "defaultValue": "westeurope", 7 | "type": "String", 8 | "metadata": { 9 | "description": "Location of the resources." 10 | } 11 | }, 12 | "frontEndImage": { 13 | "defaultValue": "markheath/samplewebapp:win1709", 14 | "type": "string", 15 | "metadata": { 16 | "description": "Front-end image" 17 | } 18 | }, 19 | "serviceImage": { 20 | "defaultValue": "markheath/samplebackend:win1709", 21 | "type": "string", 22 | "metadata": { 23 | "description": "Back end image" 24 | } 25 | }, 26 | "frontEndCpu": { 27 | "defaultValue": "0.5", 28 | "type": "string", 29 | "metadata": { 30 | "description": "The CPU resources for the front end web service." 31 | } 32 | }, 33 | "serviceCpu": { 34 | "defaultValue": "0.5", 35 | "type": "string", 36 | "metadata": { 37 | "description": "The CPU resources for the back end service." 38 | } 39 | }, 40 | "frontEndMemory":{ 41 | "defaultValue": "1.0", 42 | "type": "string", 43 | "metadata": { 44 | "description": "The memory in GB for the front end web service." 45 | } 46 | }, 47 | "serviceMemory":{ 48 | "defaultValue": "1.0", 49 | "type": "string", 50 | "metadata": { 51 | "description": "The memory in GB for the back end service." 52 | } 53 | }, 54 | "frontEndReplicaCount":{ 55 | "defaultValue": "1", 56 | "type": "string", 57 | "metadata": { 58 | "description": "The number of service replicas for the front end web service." 59 | } 60 | }, 61 | "serviceReplicaCount":{ 62 | "defaultValue": "1", 63 | "type": "string", 64 | "metadata": { 65 | "description": "The number of service replicas for the back end service." 66 | } 67 | } 68 | }, 69 | "resources": [ 70 | { 71 | "apiVersion": "2018-07-01-preview", 72 | "name": "sampleapp", 73 | "type": "Microsoft.ServiceFabricMesh/applications", 74 | "location": "[parameters('location')]", 75 | "dependsOn": [ 76 | "Microsoft.ServiceFabricMesh/networks/sampleappNetwork" 77 | ], 78 | "properties": { 79 | "services": [ 80 | { 81 | "name": "frontend", 82 | "properties": { 83 | "description": "Front end service", 84 | "osType": "Windows", 85 | "codePackages": [ 86 | { 87 | "name": "frontend", 88 | "image": "[parameters('frontEndImage')]", 89 | "endpoints": [ 90 | { 91 | "name": "frontendListener", 92 | "port": 20001 93 | } 94 | ], 95 | "environmentVariables": [ 96 | { 97 | "name": "TestSetting", 98 | "value": "Set in ARM Template" 99 | }, 100 | { 101 | "name": "TestGetUri", 102 | "value": "http://backend:20002/api/values" 103 | }, 104 | { 105 | "name": "ASPNETCORE_URLS", 106 | "value": "http://+:20001" 107 | } 108 | ], 109 | "resources": { 110 | "requests": { 111 | "cpu": "[parameters('frontEndCpu')]", 112 | "memoryInGB": "[parameters('frontEndMemory')]" 113 | } 114 | } 115 | } 116 | ], 117 | "replicaCount": "[parameters('frontEndReplicaCount')]", 118 | "networkRefs": [ 119 | { 120 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'sampleappNetwork')]" 121 | } 122 | ] 123 | } 124 | }, 125 | { 126 | "name": "backend", 127 | "properties": { 128 | "description": "Backend service.", 129 | "osType": "Windows", 130 | "codePackages": [ 131 | { 132 | "name": "backend", 133 | "image": "[parameters('serviceImage')]", 134 | "endpoints": [ 135 | { 136 | "name": "backendListener", 137 | "port": 20002 138 | } 139 | ], 140 | "environmentVariables": [ 141 | { 142 | "name": "ASPNETCORE_URLS", 143 | "value": "http://+:20002" 144 | } 145 | ], 146 | "resources": { 147 | "requests": { 148 | "cpu": "[parameters('serviceCpu')]", 149 | "memoryInGB": "[parameters('serviceMemory')]" 150 | } 151 | } 152 | } 153 | ], 154 | "replicaCount": "[parameters('serviceReplicaCount')]", 155 | "networkRefs": [ 156 | { 157 | "name": "[resourceId('Microsoft.ServiceFabricMesh/networks', 'sampleappNetwork')]" 158 | } 159 | ] 160 | } 161 | } 162 | ], 163 | "description": "Sample app on service fabric mesh." 164 | } 165 | }, 166 | { 167 | "apiVersion": "2018-07-01-preview", 168 | "name": "sampleappNetwork", 169 | "type": "Microsoft.ServiceFabricMesh/networks", 170 | "location": "[parameters('location')]", 171 | "dependsOn": [], 172 | "properties": { 173 | "description": "Sample app Network", 174 | "addressPrefix": "10.0.0.4/22", 175 | "ingressConfig": { 176 | "layer4": [ 177 | { 178 | "name": "frontendIngress", 179 | "publicPort": "80", 180 | "applicationName": "sampleapp", 181 | "serviceName": "frontend", 182 | "endpointName": "frontendListener" 183 | } 184 | ] 185 | } 186 | } 187 | } 188 | ] 189 | } -------------------------------------------------------------------------------- /M8 Running Containers on AKS/01-aks.ps1: -------------------------------------------------------------------------------- 1 | # PART 1 -Getting Started with Azure CLI 2 | # 3 | # 1. Login: 4 | az login 5 | # 6 | # 2. See which subscription is selected 7 | az account show --query name -o tsv 8 | # 9 | # 3. See which subscriptions are available 10 | az account list -o table 11 | # 12 | # 4. Select the subscription you want to use 13 | az account set -s "MySub" 14 | 15 | 16 | # PART 2 - Getting Started with AKS 17 | # https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough 18 | # 19 | 20 | # 1. Create a resource group 21 | $resourceGroup = "AKSDemo" 22 | $location = "westeurope" # see valid regions at https://azure.microsoft.com/en-gb/global-infrastructure/services/?products=kubernetes-service 23 | az group create -n $resourceGroup -l $location 24 | 25 | # 3. Create our AKS Cluster (takes about 8 minutes) 26 | $clusterName = "MarkAks" 27 | # could also say --enable-addons monitoring 28 | az aks create -g $resourceGroup -n $clusterName --node-count 1 --generate-ssh-keys 29 | 30 | # 3b. to check it worked 31 | az aks show -g $resourceGroup -n $clusterName 32 | 33 | # check we have kubectl (should have if we've installed docker for windows) 34 | kubectl version --short 35 | 36 | # if not install the kubectl CLI (needs to be done from Administrator prompt) 37 | ### az aks install-cli 38 | # update path to be able to find kubectl: 39 | ### $env:path += ';C:\Users\mheath\.azure-kubectl' 40 | 41 | # 5. Get credentials and set up for kubectl to use (may need to confirm to overwrite existing kubeconfig entries) 42 | az aks get-credentials -g $resourceGroup -n $clusterName 43 | 44 | # 6. Check we're connected 45 | kubectl get nodes 46 | 47 | # example output: 48 | #NAME STATUS ROLES AGE VERSION 49 | #aks-nodepool1-29826014-0 Ready agent 59m v1.12.8 50 | 51 | # PART 3 - Running an app on AKS 52 | # https://github.com/Azure-Samples/azure-voting-app-redis 53 | 54 | # 1. deploy the app 55 | kubectl apply -f sample-app.yaml 56 | 57 | # example output: 58 | # deployment.apps "samplebackend" created 59 | # service "samplebackend" created 60 | # deployment.apps "samplewebapp" created 61 | # service "samplewebapp" created 62 | 63 | # 2. find out where it is 64 | kubectl get service samplewebapp --watch 65 | # example output: 66 | # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 67 | # samplewebapp LoadBalancer 10.0.215.132 8080:30984/TCP 6m 68 | # .. later: 69 | # samplewebapp LoadBalancer 10.0.215.132 104.40.183.133 8080:30984/TCP 6m 70 | 71 | # 3. launch app in browser (use IP address from previous command) 72 | Start-Process http://52.232.105.209:8080 73 | 74 | # 4. see the status of our pods 75 | kubectl get pod 76 | 77 | # 5. view logs from a pod 78 | # kubectl logs <> 79 | 80 | # PART 3 - SCALING 81 | # we can scale the cluster 82 | az aks scale -g $resourceGroup -n $clusterName --node-count 3 83 | 84 | # see the nodes 85 | kubectl get nodes 86 | 87 | # deploy the example vote app 88 | kubectl apply -f .\example-vote.yml 89 | 90 | # watch for the public ip addresses of the vote and result services 91 | kubectl get service --watch 92 | 93 | # change the vote deployment to 3 replicas with eggs and bacon 94 | kubectl apply -f .\example-vote-v2.yml 95 | 96 | # enable the kube-dashboard 97 | az aks enable-addons --addons kube-dashboard -g $resourceGroup -n $clusterName 98 | 99 | # run kubernetes dashboard (now takes you to Azure Portal) 100 | az aks browse -g $resourceGroup -n $clusterName 101 | 102 | ### BONUS STEPS 103 | # how to directly scale to three replicas of our front end container 104 | kubectl scale --replicas=3 deployment/samplewebapp 105 | 106 | # how to upgrade a container directly 107 | kubectl set image deployment samplewebapp samplewebapp=markheath/samplewebapp:v2 108 | 109 | # delete an app deployed with kubectl apply 110 | kubectl delete -f .\example-vote-v2.yml 111 | 112 | # deploy a second instance to another namespace 113 | kubectl create namespace staging 114 | 115 | kubectl apply -f .\example-vote.yml -n staging 116 | kubectl get service -n staging 117 | 118 | # for if you are using the newer vote service which doesn't expose 119 | # external services 120 | kubectl port-forward -n vote service/vote 5000:5000 121 | kubectl port-forward -n vote service/result 5001:5001 122 | 123 | 124 | # Clean up 125 | az group delete -n $resourceGroup --yes --no-wait 126 | 127 | -------------------------------------------------------------------------------- /M8 Running Containers on AKS/example-vote-namespace.yml: -------------------------------------------------------------------------------- 1 | # namespace 2 | --- 3 | apiVersion: v1 4 | kind: Namespace 5 | metadata: 6 | name: vote 7 | 8 | # redis 9 | --- 10 | apiVersion: v1 11 | kind: Service 12 | metadata: 13 | labels: 14 | app: redis 15 | name: redis 16 | namespace: vote 17 | spec: 18 | type: ClusterIP 19 | ports: 20 | - name: "redis-service" 21 | port: 6379 22 | targetPort: 6379 23 | selector: 24 | app: redis 25 | --- 26 | apiVersion: apps/v1 27 | kind: Deployment 28 | metadata: 29 | labels: 30 | app: redis 31 | name: redis 32 | namespace: vote 33 | spec: 34 | replicas: 1 35 | selector: 36 | matchLabels: 37 | app: redis 38 | template: 39 | metadata: 40 | labels: 41 | app: redis 42 | spec: 43 | containers: 44 | - image: redis:alpine 45 | name: redis 46 | ports: 47 | - containerPort: 6379 48 | name: redis 49 | volumeMounts: 50 | - mountPath: /data 51 | name: redis-data 52 | volumes: 53 | - name: redis-data 54 | emptyDir: {} 55 | 56 | # db 57 | --- 58 | apiVersion: v1 59 | kind: Service 60 | metadata: 61 | labels: 62 | app: db 63 | name: db 64 | namespace: vote 65 | spec: 66 | type: ClusterIP 67 | ports: 68 | - name: "db-service" 69 | port: 5432 70 | targetPort: 5432 71 | selector: 72 | app: db 73 | --- 74 | apiVersion: apps/v1 75 | kind: Deployment 76 | metadata: 77 | labels: 78 | app: db 79 | name: db 80 | namespace: vote 81 | spec: 82 | replicas: 1 83 | selector: 84 | matchLabels: 85 | app: db 86 | template: 87 | metadata: 88 | labels: 89 | app: db 90 | spec: 91 | containers: 92 | - image: postgres:9.4 93 | name: postgres 94 | env: 95 | - name: POSTGRES_USER 96 | value: postgres 97 | - name: POSTGRES_PASSWORD 98 | value: postgres 99 | ports: 100 | - containerPort: 5432 101 | name: postgres 102 | volumeMounts: 103 | - mountPath: /var/lib/postgresql/data 104 | name: db-data 105 | volumes: 106 | - name: db-data 107 | emptyDir: {} 108 | 109 | # result 110 | --- 111 | apiVersion: v1 112 | kind: Service 113 | metadata: 114 | labels: 115 | app: result 116 | name: result 117 | namespace: vote 118 | spec: 119 | type: NodePort 120 | ports: 121 | - name: "result-service" 122 | port: 5001 123 | targetPort: 80 124 | nodePort: 31001 125 | selector: 126 | app: result 127 | --- 128 | apiVersion: apps/v1 129 | kind: Deployment 130 | metadata: 131 | labels: 132 | app: result 133 | name: result 134 | namespace: vote 135 | spec: 136 | replicas: 1 137 | selector: 138 | matchLabels: 139 | app: result 140 | template: 141 | metadata: 142 | labels: 143 | app: result 144 | spec: 145 | containers: 146 | - image: dockersamples/examplevotingapp_result:before 147 | name: result 148 | ports: 149 | - containerPort: 80 150 | name: result 151 | 152 | # vote 153 | --- 154 | apiVersion: v1 155 | kind: Service 156 | metadata: 157 | labels: 158 | app: vote 159 | name: vote 160 | namespace: vote 161 | spec: 162 | type: NodePort 163 | ports: 164 | - name: "vote-service" 165 | port: 5000 166 | targetPort: 80 167 | nodePort: 31000 168 | selector: 169 | app: vote 170 | --- 171 | apiVersion: apps/v1 172 | kind: Deployment 173 | metadata: 174 | labels: 175 | app: vote 176 | name: vote 177 | namespace: vote 178 | spec: 179 | replicas: 1 180 | selector: 181 | matchLabels: 182 | app: vote 183 | template: 184 | metadata: 185 | labels: 186 | app: vote 187 | spec: 188 | containers: 189 | - image: dockersamples/examplevotingapp_vote:before 190 | name: vote 191 | ports: 192 | - containerPort: 80 193 | name: vote 194 | # worker 195 | --- 196 | apiVersion: apps/v1 197 | kind: Deployment 198 | metadata: 199 | labels: 200 | app: worker 201 | name: worker 202 | namespace: vote 203 | spec: 204 | replicas: 1 205 | selector: 206 | matchLabels: 207 | app: worker 208 | template: 209 | metadata: 210 | labels: 211 | app: worker 212 | spec: 213 | containers: 214 | - image: dockersamples/examplevotingapp_worker 215 | name: worker -------------------------------------------------------------------------------- /M8 Running Containers on AKS/example-vote-v2.yml: -------------------------------------------------------------------------------- 1 | # redis 2 | --- 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | labels: 7 | app: redis 8 | name: redis 9 | spec: 10 | clusterIP: None 11 | ports: 12 | - name: redis-service 13 | port: 6379 14 | targetPort: 6379 15 | selector: 16 | app: redis 17 | --- 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: redis 22 | labels: 23 | app: redis 24 | spec: 25 | replicas: 1 26 | selector: 27 | matchLabels: 28 | app: redis 29 | template: 30 | metadata: 31 | labels: 32 | app: redis 33 | spec: 34 | containers: 35 | - name: redis 36 | image: redis:alpine 37 | ports: 38 | - containerPort: 6379 39 | name: redis 40 | 41 | # db 42 | --- 43 | apiVersion: v1 44 | kind: Service 45 | metadata: 46 | labels: 47 | app: db 48 | name: db 49 | spec: 50 | clusterIP: None 51 | ports: 52 | - name: db 53 | port: 5432 54 | targetPort: 5432 55 | selector: 56 | app: db 57 | --- 58 | apiVersion: apps/v1 59 | kind: Deployment 60 | metadata: 61 | name: db 62 | labels: 63 | app: db 64 | spec: 65 | replicas: 1 66 | selector: 67 | matchLabels: 68 | app: db 69 | template: 70 | metadata: 71 | labels: 72 | app: db 73 | spec: 74 | containers: 75 | - name: db 76 | image: postgres:9.4 77 | env: 78 | - name: PGDATA 79 | value: /var/lib/postgresql/data/pgdata 80 | - name: POSTGRES_USER 81 | value: postgres 82 | - name: POSTGRES_PASSWORD 83 | value: postgres 84 | ports: 85 | - containerPort: 5432 86 | name: db 87 | volumeMounts: 88 | - name: db-data 89 | mountPath: /var/lib/postgresql/data 90 | volumes: 91 | - name: db-data 92 | persistentVolumeClaim: 93 | claimName: postgres-pv-claim 94 | --- 95 | apiVersion: v1 96 | kind: PersistentVolumeClaim 97 | metadata: 98 | name: postgres-pv-claim 99 | spec: 100 | accessModes: 101 | - ReadWriteOnce 102 | resources: 103 | requests: 104 | storage: 1Gi 105 | 106 | # result 107 | --- 108 | apiVersion: v1 109 | kind: Service 110 | metadata: 111 | name: result 112 | labels: 113 | app: result 114 | spec: 115 | type: LoadBalancer 116 | ports: 117 | - port: 5001 118 | targetPort: 80 119 | name: result-service 120 | selector: 121 | app: result 122 | --- 123 | apiVersion: apps/v1 124 | kind: Deployment 125 | metadata: 126 | name: result 127 | labels: 128 | app: result 129 | spec: 130 | replicas: 1 131 | selector: 132 | matchLabels: 133 | app: result 134 | template: 135 | metadata: 136 | labels: 137 | app: result 138 | spec: 139 | containers: 140 | - name: result 141 | image: dockersamples/examplevotingapp_result:before 142 | ports: 143 | - containerPort: 80 144 | name: result 145 | 146 | # vote 147 | --- 148 | apiVersion: v1 149 | kind: Service 150 | metadata: 151 | name: vote 152 | labels: 153 | apps: vote 154 | spec: 155 | type: LoadBalancer 156 | ports: 157 | - port: 5000 158 | targetPort: 80 159 | name: vote-service 160 | selector: 161 | app: vote 162 | --- 163 | apiVersion: apps/v1 164 | kind: Deployment 165 | metadata: 166 | name: vote 167 | labels: 168 | app: vote 169 | spec: 170 | replicas: 3 171 | selector: 172 | matchLabels: 173 | app: vote 174 | template: 175 | metadata: 176 | labels: 177 | app: vote 178 | spec: 179 | containers: 180 | - name: vote 181 | image: dockersamples/examplevotingapp_vote:before 182 | ports: 183 | - containerPort: 80 184 | name: vote 185 | env: 186 | - name: OPTION_A 187 | value: "eggs" 188 | - name: OPTION_B 189 | value: "bacon" 190 | 191 | # worker 192 | --- 193 | apiVersion: v1 194 | kind: Service 195 | metadata: 196 | labels: 197 | apps: worker 198 | name: worker 199 | spec: 200 | clusterIP: None 201 | selector: 202 | app: worker 203 | --- 204 | apiVersion: apps/v1 205 | kind: Deployment 206 | metadata: 207 | labels: 208 | app: worker 209 | name: worker 210 | spec: 211 | replicas: 1 212 | selector: 213 | matchLabels: 214 | app: worker 215 | template: 216 | metadata: 217 | labels: 218 | app: worker 219 | spec: 220 | containers: 221 | - image: dockersamples/examplevotingapp_worker 222 | name: worker -------------------------------------------------------------------------------- /M8 Running Containers on AKS/example-vote.yml: -------------------------------------------------------------------------------- 1 | # redis 2 | --- 3 | apiVersion: v1 4 | kind: Service 5 | metadata: 6 | labels: 7 | app: redis 8 | name: redis 9 | spec: 10 | clusterIP: None 11 | ports: 12 | - name: redis-service 13 | port: 6379 14 | targetPort: 6379 15 | selector: 16 | app: redis 17 | --- 18 | apiVersion: apps/v1 19 | kind: Deployment 20 | metadata: 21 | name: redis 22 | labels: 23 | app: redis 24 | spec: 25 | replicas: 1 26 | selector: 27 | matchLabels: 28 | app: redis 29 | template: 30 | metadata: 31 | labels: 32 | app: redis 33 | spec: 34 | containers: 35 | - name: redis 36 | image: redis:alpine 37 | ports: 38 | - containerPort: 6379 39 | name: redis 40 | 41 | # db 42 | --- 43 | apiVersion: v1 44 | kind: Service 45 | metadata: 46 | labels: 47 | app: db 48 | name: db 49 | spec: 50 | clusterIP: None 51 | ports: 52 | - name: db 53 | port: 5432 54 | targetPort: 5432 55 | selector: 56 | app: db 57 | --- 58 | apiVersion: apps/v1 59 | kind: Deployment 60 | metadata: 61 | name: db 62 | labels: 63 | app: db 64 | spec: 65 | replicas: 1 66 | selector: 67 | matchLabels: 68 | app: db 69 | template: 70 | metadata: 71 | labels: 72 | app: db 73 | spec: 74 | containers: 75 | - name: db 76 | image: postgres:9.4 77 | env: 78 | - name: PGDATA 79 | value: /var/lib/postgresql/data/pgdata 80 | - name: POSTGRES_USER 81 | value: postgres 82 | - name: POSTGRES_PASSWORD 83 | value: postgres 84 | ports: 85 | - containerPort: 5432 86 | name: db 87 | volumeMounts: 88 | - name: db-data 89 | mountPath: /var/lib/postgresql/data 90 | volumes: 91 | - name: db-data 92 | persistentVolumeClaim: 93 | claimName: postgres-pv-claim 94 | --- 95 | apiVersion: v1 96 | kind: PersistentVolumeClaim 97 | metadata: 98 | name: postgres-pv-claim 99 | spec: 100 | accessModes: 101 | - ReadWriteOnce 102 | resources: 103 | requests: 104 | storage: 1Gi 105 | 106 | # result 107 | --- 108 | apiVersion: v1 109 | kind: Service 110 | metadata: 111 | name: result 112 | labels: 113 | app: result 114 | spec: 115 | type: LoadBalancer 116 | ports: 117 | - port: 5001 118 | targetPort: 80 119 | name: result-service 120 | selector: 121 | app: result 122 | --- 123 | apiVersion: apps/v1 124 | kind: Deployment 125 | metadata: 126 | name: result 127 | labels: 128 | app: result 129 | spec: 130 | replicas: 1 131 | selector: 132 | matchLabels: 133 | app: result 134 | template: 135 | metadata: 136 | labels: 137 | app: result 138 | spec: 139 | containers: 140 | - name: result 141 | image: dockersamples/examplevotingapp_result:before 142 | ports: 143 | - containerPort: 80 144 | name: result 145 | 146 | # vote 147 | --- 148 | apiVersion: v1 149 | kind: Service 150 | metadata: 151 | name: vote 152 | labels: 153 | apps: vote 154 | spec: 155 | type: LoadBalancer 156 | ports: 157 | - port: 5000 158 | targetPort: 80 159 | name: vote-service 160 | selector: 161 | app: vote 162 | --- 163 | apiVersion: apps/v1 164 | kind: Deployment 165 | metadata: 166 | name: vote 167 | labels: 168 | app: vote 169 | spec: 170 | replicas: 1 171 | selector: 172 | matchLabels: 173 | app: vote 174 | template: 175 | metadata: 176 | labels: 177 | app: vote 178 | spec: 179 | containers: 180 | - name: vote 181 | image: dockersamples/examplevotingapp_vote:before 182 | ports: 183 | - containerPort: 80 184 | name: vote 185 | 186 | # worker 187 | --- 188 | apiVersion: v1 189 | kind: Service 190 | metadata: 191 | labels: 192 | apps: worker 193 | name: worker 194 | spec: 195 | clusterIP: None 196 | selector: 197 | app: worker 198 | --- 199 | apiVersion: apps/v1 200 | kind: Deployment 201 | metadata: 202 | labels: 203 | app: worker 204 | name: worker 205 | spec: 206 | replicas: 1 207 | selector: 208 | matchLabels: 209 | app: worker 210 | template: 211 | metadata: 212 | labels: 213 | app: worker 214 | spec: 215 | containers: 216 | - image: dockersamples/examplevotingapp_worker 217 | name: worker -------------------------------------------------------------------------------- /M8 Running Containers on AKS/sample-app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: samplebackend 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | app: samplebackend 10 | template: 11 | metadata: 12 | labels: 13 | app: samplebackend 14 | spec: 15 | containers: 16 | - name: samplebackend 17 | image: markheath/samplebackend:linux 18 | imagePullPolicy: Always 19 | ports: 20 | - containerPort: 80 21 | --- 22 | apiVersion: v1 23 | kind: Service 24 | metadata: 25 | name: samplebackend 26 | spec: 27 | ports: 28 | - port: 8081 29 | targetPort: 80 30 | selector: 31 | app: samplebackend 32 | --- 33 | apiVersion: apps/v1 34 | kind: Deployment 35 | metadata: 36 | name: samplewebapp 37 | spec: 38 | replicas: 1 39 | selector: 40 | matchLabels: 41 | app: samplewebapp 42 | template: 43 | metadata: 44 | labels: 45 | app: samplewebapp 46 | spec: 47 | containers: 48 | - name: samplewebapp 49 | image: markheath/samplewebapp:linux 50 | imagePullPolicy: Always 51 | ports: 52 | - containerPort: 80 53 | env: 54 | - name: TestSetting 55 | value: "hello from Kubernetes" 56 | - name: TestGetUri 57 | value: "http://samplebackend:8081/api/values" 58 | --- 59 | apiVersion: v1 60 | kind: Service 61 | metadata: 62 | name: samplewebapp 63 | labels: 64 | apps: samplewebapp 65 | spec: 66 | type: LoadBalancer 67 | ports: 68 | - port: 8080 69 | targetPort: 80 70 | selector: 71 | app: samplewebapp -------------------------------------------------------------------------------- /M9 Securing Containers/README.md: -------------------------------------------------------------------------------- 1 | ### Useful Container Security Links 2 | 3 | - [ACR Tasks](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-tasks-overview) 4 | - [AKS Cluster Upgrades](https://docs.microsoft.com/en-us/azure/aks/concepts-security#cluster-upgrades) 5 | - [Service Fabric Cluster Upgrades](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-cluster-upgrade) 6 | - [AKS Authentication Best Practices](https://docs.microsoft.com/en-us/azure/aks/operator-best-practices-identity) 7 | - [ACI Secret Volumes](https://docs.microsoft.com/en-us/azure/container-instances/container-instances-volume-secret) 8 | - [App Service Key Vault References](https://docs.microsoft.com/en-us/azure/app-service/app-service-key-vault-references) 9 | - [Service Fabric Secret Management](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-application-secret-management) 10 | - [Kubernetes Secrets](https://docs.microsoft.com/en-us/azure/aks/concepts-security#kubernetes-secrets) 11 | - [Using Managed Identities with ACI](https://docs.microsoft.com/en-us/azure/container-instances/container-instances-managed-identity) 12 | - [Managed Identities with Web App for Containers](https://azure.microsoft.com/en-us/blog/simplifying-security-for-serverless-and-web-apps-with-azure-functions-and-app-service/) 13 | - [Service Fabric Security Best Practices](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-best-practices-security) 14 | - [Managed Identities for VMSS](https://docs.microsoft.com/en-us/azure/active-directory/managed-identities-azure-resources/qs-configure-portal-windows-vmss) 15 | - [AKS Pod Identities](https://docs.microsoft.com/en-us/azure/aks/operator-best-practices-identity#use-pod-identities) 16 | - [AAD Pod Identity (for AKS)](https://github.com/Azure/aad-pod-identity) 17 | - [ACI Virtual Network Support](https://docs.microsoft.com/en-us/azure/container-instances/container-instances-vnet) 18 | - [Service Fabric Mesh Networks and Gateways](https://docs.microsoft.com/en-us/azure/service-fabric-mesh/service-fabric-mesh-networks-and-gateways) 19 | - [AKS Networking Best Practices](https://docs.microsoft.com/en-gb/azure/aks/operator-best-practices-network) 20 | - [Azure Security Center for containers](https://azure.microsoft.com/en-gb/blog/detecting-threats-targeting-containers-with-azure-security-center/) 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Deploy and Manage Azure Containers 2 | 3 | This repository contains example scripts and code to accompany the "[Microsoft Azure Developer: Deploy and Manage Containers](https://pluralsight.pxf.io/c/1192349/424552/7490?u=www%2Epluralsight%2Ecom%2Fcourses%2Fmicrosoft-azure-containers-deploying-managing)" Pluralsight course by Mark Heath. -------------------------------------------------------------------------------- /SampleBackend/.gitignore: -------------------------------------------------------------------------------- 1 | /obj 2 | /bin -------------------------------------------------------------------------------- /SampleBackend/Controllers/ValuesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace SampleBackend.Controllers 8 | { 9 | [Route("api/[controller]")] 10 | [ApiController] 11 | public class ValuesController : ControllerBase 12 | { 13 | // GET api/values 14 | [HttpGet] 15 | public ActionResult> Get() 16 | { 17 | return new string[] { "hello", "from", "backend", System.Environment.MachineName }; 18 | } 19 | 20 | // GET api/values/5 21 | [HttpGet("{id}")] 22 | public ActionResult Get(int id) 23 | { 24 | return $"value{id}"; 25 | } 26 | 27 | // POST api/values 28 | [HttpPost] 29 | public void Post([FromBody] string value) 30 | { 31 | } 32 | 33 | // PUT api/values/5 34 | [HttpPut("{id}")] 35 | public void Put(int id, [FromBody] string value) 36 | { 37 | } 38 | 39 | // DELETE api/values/5 40 | [HttpDelete("{id}")] 41 | public void Delete(int id) 42 | { 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /SampleBackend/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | 4 | # Copy csproj and restore as distinct layers 5 | COPY *.csproj ./ 6 | RUN dotnet restore 7 | 8 | # Copy everything else and build 9 | COPY . ./ 10 | RUN dotnet publish -c Release -o out 11 | 12 | # Build runtime image 13 | FROM mcr.microsoft.com/dotnet/aspnet:3.1 14 | WORKDIR /app 15 | COPY --from=build-env /app/out . 16 | ENTRYPOINT ["dotnet", "SampleBackend.dll"] -------------------------------------------------------------------------------- /SampleBackend/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleBackend 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SampleBackend/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:1327", 8 | "sslPort": 44370 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "api/values", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "SampleBackend": { 21 | "commandName": "Project", 22 | "launchBrowser": true, 23 | "launchUrl": "api/values", 24 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 25 | "environmentVariables": { 26 | "ASPNETCORE_ENVIRONMENT": "Development" 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /SampleBackend/README.md: -------------------------------------------------------------------------------- 1 | Simple ASP.NET Core web api deployable as docker container 2 | 3 | To build and run as a container 4 | ``` 5 | $ docker build -t samplebackend . 6 | $ docker run -d -p 8081:80 --name backend samplebackend 7 | ``` 8 | 9 | To test: http://localhost:8081/api/values 10 | 11 | 12 | To build a Windows Docker image (compatible with windows 2019 server build 2004). You need to switch Docker Desktop to Windows mode to do this. 13 | ``` 14 | $ docker build -t samplebackend:nano -f win.Dockerfile . 15 | $ docker run -d -p 8081:80 samplebackend:nano 16 | ``` -------------------------------------------------------------------------------- /SampleBackend/SampleBackend.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /SampleBackend/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.HttpsPolicy; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.Extensions.Configuration; 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Hosting; 12 | using Microsoft.Extensions.Logging; 13 | using Microsoft.Extensions.Options; 14 | 15 | namespace SampleBackend 16 | { 17 | public class Startup 18 | { 19 | public Startup(IConfiguration configuration) 20 | { 21 | Configuration = configuration; 22 | } 23 | 24 | public IConfiguration Configuration { get; } 25 | 26 | // This method gets called by the runtime. Use this method to add services to the container. 27 | public void ConfigureServices(IServiceCollection services) 28 | { 29 | services.AddMvc(); 30 | } 31 | 32 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 33 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 34 | { 35 | if (env.IsDevelopment()) 36 | { 37 | app.UseDeveloperExceptionPage(); 38 | } 39 | else 40 | { 41 | app.UseHsts(); 42 | } 43 | 44 | app.UseHttpsRedirection(); 45 | app.UseRouting(); 46 | app.UseEndpoints(endpoints => 47 | { 48 | endpoints.MapControllerRoute( 49 | "default", "{controller=Home}/{action=Index}/{id?}"); 50 | }); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SampleBackend/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /SampleBackend/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Warning" 5 | } 6 | }, 7 | "AllowedHosts": "*" 8 | } 9 | -------------------------------------------------------------------------------- /SampleBackend/win.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:3.1-nanoserver-2004 AS build-env 2 | WORKDIR /app 3 | 4 | # Copy csproj and restore as distinct layers 5 | COPY *.csproj ./ 6 | RUN dotnet restore 7 | 8 | # Copy everything else and build 9 | COPY . ./ 10 | RUN dotnet publish -c Release -o out 11 | 12 | # Build runtime image 13 | FROM mcr.microsoft.com/dotnet/aspnet:3.1-nanoserver-2004 14 | WORKDIR /app 15 | COPY --from=build-env /app/out . 16 | ENTRYPOINT ["dotnet", "samplebackend.dll"] -------------------------------------------------------------------------------- /SampleWebApp/.gitignore: -------------------------------------------------------------------------------- 1 | /obj 2 | /bin 3 | /out -------------------------------------------------------------------------------- /SampleWebApp/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (web)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp2.1/samplewebapp.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}", 16 | "stopAtEntry": false, 17 | "internalConsoleOptions": "openOnSessionStart", 18 | "launchBrowser": { 19 | "enabled": true, 20 | "args": "${auto-detect-url}", 21 | "windows": { 22 | "command": "cmd.exe", 23 | "args": "/C start ${auto-detect-url}" 24 | }, 25 | "osx": { 26 | "command": "open" 27 | }, 28 | "linux": { 29 | "command": "xdg-open" 30 | } 31 | }, 32 | "env": { 33 | "ASPNETCORE_ENVIRONMENT": "Development" 34 | }, 35 | "sourceFileMap": { 36 | "/Views": "${workspaceFolder}/Views" 37 | } 38 | }, 39 | { 40 | "name": ".NET Core Attach", 41 | "type": "coreclr", 42 | "request": "attach", 43 | "processId": "${command:pickProcess}" 44 | } 45 | ,] 46 | } -------------------------------------------------------------------------------- /SampleWebApp/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/samplewebapp.csproj" 11 | ], 12 | "problemMatcher": "$msCompile" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /SampleWebApp/Controllers/GreetingController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace SampleWebApp.Controllers 6 | { 7 | [Route("api/[controller]")] 8 | [ApiController] 9 | public class GreetingController : ControllerBase 10 | { 11 | private static string greeting = "Hello"; 12 | public GreetingController() 13 | { 14 | } 15 | 16 | [HttpGet] 17 | public ActionResult Get() 18 | { 19 | return greeting; 20 | } 21 | 22 | [HttpPost] 23 | public ActionResult Post([FromBody]string newGreeting) 24 | { 25 | greeting = newGreeting; 26 | return new OkResult(); 27 | } 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /SampleWebApp/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System.Text.Encodings.Web; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.AspNetCore.Hosting; 5 | using System.Net.Http; 6 | using System.IO; 7 | using System.Threading.Tasks; 8 | using System; 9 | 10 | namespace SampleWebApp.Controllers 11 | { 12 | public class HomeController : Controller 13 | { 14 | private readonly IConfiguration config; 15 | private readonly IWebHostEnvironment hostingEnv; 16 | private readonly IHttpClientFactory httpClientFactory; 17 | public HomeController(IConfiguration config, IWebHostEnvironment hostingEnv, 18 | IHttpClientFactory httpClientFactory) 19 | { 20 | this.config = config; 21 | this.hostingEnv = hostingEnv; 22 | this.httpClientFactory = httpClientFactory; 23 | } 24 | 25 | public async Task Index() 26 | { 27 | var viewModel = new IndexViewModel(); 28 | viewModel.EnvironmentName = hostingEnv.EnvironmentName; 29 | viewModel.HostName = System.Environment.MachineName; 30 | viewModel.TestSetting = config["TestSetting"]; 31 | viewModel.TestFile = ""; 32 | viewModel.TestFileLocation = config["TestFileLocation"]; 33 | 34 | if (System.IO.File.Exists(viewModel.TestFileLocation)) 35 | { 36 | viewModel.TestFile = System.IO.File.ReadAllText(viewModel.TestFileLocation); 37 | } 38 | 39 | viewModel.WebApiGetUri = config["TestGetUri"]; 40 | var (success, message) = await GetTestUri(viewModel.WebApiGetUri); 41 | viewModel.WebApiGetResult = message; 42 | viewModel.WebApiGetSuccess = success; 43 | 44 | return View(viewModel); 45 | } 46 | 47 | private async Task<(bool,string)> GetTestUri(string uri) 48 | { 49 | if (!string.IsNullOrEmpty(uri)) 50 | { 51 | var client = httpClientFactory.CreateClient(uri); 52 | try 53 | { 54 | var body = await client.GetStringAsync(uri); 55 | return (true,body); 56 | } 57 | catch(Exception e) 58 | { 59 | return (false,e.Message); 60 | } 61 | } 62 | return (false,"No Test Uri provided"); 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /SampleWebApp/Controllers/IndexViewModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.AspNetCore.Mvc.RazorPages; 3 | using Microsoft.Extensions.Configuration; 4 | using System; 5 | using System.IO; 6 | using System.Net.Http; 7 | using System.Threading.Tasks; 8 | 9 | namespace SampleWebApp.Controllers 10 | { 11 | public class IndexViewModel 12 | { 13 | 14 | public string TestSetting { get; set; } 15 | 16 | public string EnvironmentName { get; set;} 17 | public string HostName { get; set;} 18 | 19 | public string TestFile { get; set; } 20 | public string TestFileLocation { get; set; } 21 | 22 | public string WebApiGetResult { get; set; } 23 | public string WebApiGetUri { get; set; } 24 | public bool WebApiGetSuccess { get; set; } 25 | } 26 | } -------------------------------------------------------------------------------- /SampleWebApp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:3.1 2 | WORKDIR /app 3 | COPY ./out . 4 | ENTRYPOINT ["dotnet", "samplewebapp.dll"] -------------------------------------------------------------------------------- /SampleWebApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.Logging; 10 | 11 | namespace SampleWebApp 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | CreateWebHostBuilder(args).Build().Run(); 18 | } 19 | 20 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) => 21 | WebHost.CreateDefaultBuilder(args) 22 | .UseStartup(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /SampleWebApp/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:36139", 7 | "sslPort": 44349 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "samplewebapp": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /SampleWebApp/README.md: -------------------------------------------------------------------------------- 1 | Simple ASP.NET Core app deployable as docker container with some diagnostic information 2 | 3 | To run this application in a Docker container (using a multi-stage Docker file) 4 | You need to be in the SampleWebApp folder to run these commands. 5 | 6 | ```powershell 7 | $ docker build -t samplewebapp . 8 | # docker build -t -f multi-stage.Dockerfile . 9 | $ docker run -d -p 8080:80 -e TestSetting=FromDocker -e TestFileLocation=/home/message.txt --name myapp samplewebapp 10 | ``` 11 | 12 | To see it running, visit `http://localhost:8080`. 13 | 14 | Kill the container with 15 | 16 | ``` 17 | $ docker rm -f myapp 18 | ``` 19 | 20 | To build a Windows Docker image (compatible with windows 2019 server build 2004). You need to switch Docker Desktop to Windows mode to do this. 21 | ``` 22 | $ docker build -t samplewebapp:nano -f win.Dockerfile . 23 | $ docker run -d -p 8080:80 -e TestSetting=Win2004 -e TestFileLocation=/home/message.txt --name myapp samplewebapp:nano 24 | ``` -------------------------------------------------------------------------------- /SampleWebApp/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | 11 | namespace SampleWebApp 12 | { 13 | public class Startup 14 | { 15 | // This method gets called by the runtime. Use this method to add services to the container. 16 | // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 17 | public void ConfigureServices(IServiceCollection services) 18 | { 19 | services.AddMvc(); 20 | services.AddHttpClient(); 21 | } 22 | 23 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 24 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 25 | { 26 | if (env.IsDevelopment()) 27 | { 28 | app.UseDeveloperExceptionPage(); 29 | } 30 | 31 | app.UseRouting(); 32 | 33 | app.UseEndpoints(endpoints => 34 | { 35 | endpoints.MapControllerRoute( 36 | "default", "{controller=Home}/{action=Index}/{id?}"); 37 | }); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /SampleWebApp/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | @using SampleWebApp.Controllers 2 | @model IndexViewModel 3 | 4 |
    5 |
  • Host Name: @Model.HostName
  • 6 |
  • Environment: @Model.EnvironmentName
  • 7 |
  • Test Setting: @Model.TestSetting
  • 8 |
  • Test File Location: @Model.TestFileLocation
  • 9 |
  • Test File Contents: @Model.TestFile
  • 10 |
  • Web API Get URI: @Model.WebApiGetUri
  • 11 |
  • Web API Get Result: @Model.WebApiGetResult
  • 12 |
-------------------------------------------------------------------------------- /SampleWebApp/Views/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Microsoft Azure Developer: Build and Manage Containers 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |

Microsoft Azure Developer: Build and Manage Containers

18 |

Get up and running with containers on Azure

19 |
20 |
21 | @RenderBody() 22 |
23 | @RenderSection("scripts", required: false) 24 | 25 | -------------------------------------------------------------------------------- /SampleWebApp/Views/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | @{ 2 | Layout = "_Layout"; 3 | } -------------------------------------------------------------------------------- /SampleWebApp/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "TestSetting": "Set in appsettings.Development.json", 3 | "TestGetUri": "http://localhost:5000/api/Greeting" 4 | } -------------------------------------------------------------------------------- /SampleWebApp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "TestSetting": "Set in appsettings.json", 3 | "TestFileLocation": "README.md", 4 | "TestGetUri": "https://cat-fact.herokuapp.com/facts/random" 5 | } -------------------------------------------------------------------------------- /SampleWebApp/basic.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:3.1 2 | WORKDIR /app 3 | COPY ./out . 4 | ENTRYPOINT ["dotnet", "samplewebapp.dll"] -------------------------------------------------------------------------------- /SampleWebApp/multi-stage.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:3.1 AS build-env 2 | WORKDIR /app 3 | 4 | # Copy csproj and restore as distinct layers 5 | COPY *.csproj ./ 6 | RUN dotnet restore 7 | 8 | # Copy everything else and build 9 | COPY . ./ 10 | RUN dotnet publish -c Release -o out 11 | 12 | # Build runtime image 13 | FROM mcr.microsoft.com/dotnet/aspnet:3.1 14 | WORKDIR /app 15 | COPY --from=build-env /app/out . 16 | ENTRYPOINT ["dotnet", "samplewebapp.dll"] 17 | -------------------------------------------------------------------------------- /SampleWebApp/samplewebapp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /SampleWebApp/win.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/sdk:3.1-nanoserver-2004 AS build-env 2 | WORKDIR /app 3 | 4 | # Copy csproj and restore as distinct layers 5 | COPY *.csproj ./ 6 | RUN dotnet restore 7 | 8 | # Copy everything else and build 9 | COPY . ./ 10 | RUN dotnet publish -c Release -o out 11 | 12 | # Build runtime image 13 | FROM mcr.microsoft.com/dotnet/aspnet:3.1-nanoserver-2004 14 | WORKDIR /app 15 | COPY --from=build-env /app/out . 16 | ENTRYPOINT ["dotnet", "samplewebapp.dll"] -------------------------------------------------------------------------------- /ServiceFabricWinDemo/.gitignore: -------------------------------------------------------------------------------- 1 | */obj 2 | */bin 3 | */pkg 4 | /packages 5 | /.vs 6 | *.user -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28010.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{A07B5EB6-E848-4116-A8D0-A826331D98C6}") = "ServiceFabricWinDemo", "ServiceFabricWinDemo\ServiceFabricWinDemo.sfproj", "{CC27CB67-3A62-475C-8AE6-F87B813E3CAE}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {CC27CB67-3A62-475C-8AE6-F87B813E3CAE}.Debug|x64.ActiveCfg = Debug|x64 15 | {CC27CB67-3A62-475C-8AE6-F87B813E3CAE}.Debug|x64.Build.0 = Debug|x64 16 | {CC27CB67-3A62-475C-8AE6-F87B813E3CAE}.Debug|x64.Deploy.0 = Debug|x64 17 | {CC27CB67-3A62-475C-8AE6-F87B813E3CAE}.Release|x64.ActiveCfg = Release|x64 18 | {CC27CB67-3A62-475C-8AE6-F87B813E3CAE}.Release|x64.Build.0 = Release|x64 19 | {CC27CB67-3A62-475C-8AE6-F87B813E3CAE}.Release|x64.Deploy.0 = Release|x64 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | GlobalSection(ExtensibilityGlobals) = postSolution 25 | SolutionGuid = {AA9A2C59-2738-48CC-ABC2-1B8BC10D055F} 26 | EndGlobalSection 27 | EndGlobal 28 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/ApplicationPackageRoot/ApplicationManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 47 | 49 | 50 | 51 | 52 | 53 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/ApplicationPackageRoot/SampleBackendPkg/Config/Settings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/ApplicationPackageRoot/SampleBackendPkg/ServiceManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | markheath/samplebackend:win1709 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/ApplicationPackageRoot/SampleWebAppPkg/Config/Settings.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/ApplicationPackageRoot/SampleWebAppPkg/ServiceManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 7 | 8 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | markheath/samplewebapp:win1709 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/ApplicationParameters/Cloud.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/ApplicationParameters/Local.1Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/ApplicationParameters/Local.5Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/PublishProfiles/Cloud.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 23 | 25 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/PublishProfiles/Local.1Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/PublishProfiles/Local.5Node.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/Scripts/Deploy-FabricApplication.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Deploys a Service Fabric application type to a cluster. 4 | 5 | .DESCRIPTION 6 | This script deploys a Service Fabric application type to a cluster. It is invoked by Visual Studio when deploying a Service Fabric Application project. 7 | 8 | .NOTES 9 | WARNING: This script file is invoked by Visual Studio. Its parameters must not be altered but its logic can be customized as necessary. 10 | 11 | .PARAMETER PublishProfileFile 12 | Path to the file containing the publish profile. 13 | 14 | .PARAMETER ApplicationPackagePath 15 | Path to the folder of the packaged Service Fabric application. 16 | 17 | .PARAMETER DeployOnly 18 | Indicates that the Service Fabric application should not be created or upgraded after registering the application type. 19 | 20 | .PARAMETER ApplicationParameter 21 | Hashtable of the Service Fabric application parameters to be used for the application. 22 | 23 | .PARAMETER UnregisterUnusedApplicationVersionsAfterUpgrade 24 | Indicates whether to unregister any unused application versions that exist after an upgrade is finished. 25 | 26 | .PARAMETER OverrideUpgradeBehavior 27 | Indicates the behavior used to override the upgrade settings specified by the publish profile. 28 | 'None' indicates that the upgrade settings will not be overridden. 29 | 'ForceUpgrade' indicates that an upgrade will occur with default settings, regardless of what is specified in the publish profile. 30 | 'VetoUpgrade' indicates that an upgrade will not occur, regardless of what is specified in the publish profile. 31 | 32 | .PARAMETER UseExistingClusterConnection 33 | Indicates that the script should make use of an existing cluster connection that has already been established in the PowerShell session. The cluster connection parameters configured in the publish profile are ignored. 34 | 35 | .PARAMETER OverwriteBehavior 36 | Overwrite Behavior if an application exists in the cluster with the same name. Available Options are Never, Always, SameAppTypeAndVersion. This setting is not applicable when upgrading an application. 37 | 'Never' will not remove the existing application. This is the default behavior. 38 | 'Always' will remove the existing application even if its Application type and Version is different from the application being created. 39 | 'SameAppTypeAndVersion' will remove the existing application only if its Application type and Version is same as the application being created. 40 | 41 | .PARAMETER SkipPackageValidation 42 | Switch signaling whether the package should be validated or not before deployment. 43 | 44 | .PARAMETER SecurityToken 45 | A security token for authentication to cluster management endpoints. Used for silent authentication to clusters that are protected by Azure Active Directory. 46 | 47 | .PARAMETER CopyPackageTimeoutSec 48 | Timeout in seconds for copying application package to image store. 49 | 50 | .EXAMPLE 51 | . Scripts\Deploy-FabricApplication.ps1 -ApplicationPackagePath 'pkg\Debug' 52 | 53 | Deploy the application using the default package location for a Debug build. 54 | 55 | .EXAMPLE 56 | . Scripts\Deploy-FabricApplication.ps1 -ApplicationPackagePath 'pkg\Debug' -DoNotCreateApplication 57 | 58 | Deploy the application but do not create the application instance. 59 | 60 | .EXAMPLE 61 | . Scripts\Deploy-FabricApplication.ps1 -ApplicationPackagePath 'pkg\Debug' -ApplicationParameter @{CustomParameter1='MyValue'; CustomParameter2='MyValue'} 62 | 63 | Deploy the application by providing values for parameters that are defined in the application manifest. 64 | #> 65 | 66 | Param 67 | ( 68 | [String] 69 | $PublishProfileFile, 70 | 71 | [String] 72 | $ApplicationPackagePath, 73 | 74 | [Switch] 75 | $DeployOnly, 76 | 77 | [Hashtable] 78 | $ApplicationParameter, 79 | 80 | [Boolean] 81 | $UnregisterUnusedApplicationVersionsAfterUpgrade, 82 | 83 | [String] 84 | [ValidateSet('None', 'ForceUpgrade', 'VetoUpgrade')] 85 | $OverrideUpgradeBehavior = 'None', 86 | 87 | [Switch] 88 | $UseExistingClusterConnection, 89 | 90 | [String] 91 | [ValidateSet('Never','Always','SameAppTypeAndVersion')] 92 | $OverwriteBehavior = 'Never', 93 | 94 | [Switch] 95 | $SkipPackageValidation, 96 | 97 | [String] 98 | $SecurityToken, 99 | 100 | [int] 101 | $CopyPackageTimeoutSec 102 | ) 103 | 104 | function Read-XmlElementAsHashtable 105 | { 106 | Param ( 107 | [System.Xml.XmlElement] 108 | $Element 109 | ) 110 | 111 | $hashtable = @{} 112 | if ($Element.Attributes) 113 | { 114 | $Element.Attributes | 115 | ForEach-Object { 116 | $boolVal = $null 117 | if ([bool]::TryParse($_.Value, [ref]$boolVal)) { 118 | $hashtable[$_.Name] = $boolVal 119 | } 120 | else { 121 | $hashtable[$_.Name] = $_.Value 122 | } 123 | } 124 | } 125 | 126 | return $hashtable 127 | } 128 | 129 | function Read-PublishProfile 130 | { 131 | Param ( 132 | [ValidateScript({Test-Path $_ -PathType Leaf})] 133 | [String] 134 | $PublishProfileFile 135 | ) 136 | 137 | $publishProfileXml = [Xml] (Get-Content $PublishProfileFile) 138 | $publishProfile = @{} 139 | 140 | $publishProfile.ClusterConnectionParameters = Read-XmlElementAsHashtable $publishProfileXml.PublishProfile.Item("ClusterConnectionParameters") 141 | $publishProfile.UpgradeDeployment = Read-XmlElementAsHashtable $publishProfileXml.PublishProfile.Item("UpgradeDeployment") 142 | $publishProfile.CopyPackageParameters = Read-XmlElementAsHashtable $publishProfileXml.PublishProfile.Item("CopyPackageParameters") 143 | 144 | if ($publishProfileXml.PublishProfile.Item("UpgradeDeployment")) 145 | { 146 | $publishProfile.UpgradeDeployment.Parameters = Read-XmlElementAsHashtable $publishProfileXml.PublishProfile.Item("UpgradeDeployment").Item("Parameters") 147 | if ($publishProfile.UpgradeDeployment["Mode"]) 148 | { 149 | $publishProfile.UpgradeDeployment.Parameters[$publishProfile.UpgradeDeployment["Mode"]] = $true 150 | } 151 | } 152 | 153 | $publishProfileFolder = (Split-Path $PublishProfileFile) 154 | $publishProfile.ApplicationParameterFile = [System.IO.Path]::Combine($PublishProfileFolder, $publishProfileXml.PublishProfile.ApplicationParameterFile.Path) 155 | 156 | return $publishProfile 157 | } 158 | 159 | $LocalFolder = (Split-Path $MyInvocation.MyCommand.Path) 160 | 161 | if (!$PublishProfileFile) 162 | { 163 | $PublishProfileFile = "$LocalFolder\..\PublishProfiles\Local.xml" 164 | } 165 | 166 | if (!$ApplicationPackagePath) 167 | { 168 | $ApplicationPackagePath = "$LocalFolder\..\pkg\Release" 169 | } 170 | 171 | $ApplicationPackagePath = Resolve-Path $ApplicationPackagePath 172 | 173 | $publishProfile = Read-PublishProfile $PublishProfileFile 174 | 175 | if (-not $UseExistingClusterConnection) 176 | { 177 | $ClusterConnectionParameters = $publishProfile.ClusterConnectionParameters 178 | if ($SecurityToken) 179 | { 180 | $ClusterConnectionParameters["SecurityToken"] = $SecurityToken 181 | } 182 | 183 | try 184 | { 185 | [void](Connect-ServiceFabricCluster @ClusterConnectionParameters) 186 | } 187 | catch [System.Fabric.FabricObjectClosedException] 188 | { 189 | Write-Warning "Service Fabric cluster may not be connected." 190 | throw 191 | } 192 | } 193 | 194 | $RegKey = "HKLM:\SOFTWARE\Microsoft\Service Fabric SDK" 195 | $ModuleFolderPath = (Get-ItemProperty -Path $RegKey -Name FabricSDKPSModulePath).FabricSDKPSModulePath 196 | Import-Module "$ModuleFolderPath\ServiceFabricSDK.psm1" 197 | 198 | $IsUpgrade = ($publishProfile.UpgradeDeployment -and $publishProfile.UpgradeDeployment.Enabled -and $OverrideUpgradeBehavior -ne 'VetoUpgrade') -or $OverrideUpgradeBehavior -eq 'ForceUpgrade' 199 | 200 | $PublishParameters = @{ 201 | 'ApplicationPackagePath' = $ApplicationPackagePath 202 | 'ApplicationParameterFilePath' = $publishProfile.ApplicationParameterFile 203 | 'ApplicationParameter' = $ApplicationParameter 204 | 'ErrorAction' = 'Stop' 205 | } 206 | 207 | if ($publishProfile.CopyPackageParameters.CopyPackageTimeoutSec) 208 | { 209 | $PublishParameters['CopyPackageTimeoutSec'] = $publishProfile.CopyPackageParameters.CopyPackageTimeoutSec 210 | } 211 | 212 | if ($publishProfile.CopyPackageParameters.CompressPackage) 213 | { 214 | $PublishParameters['CompressPackage'] = $publishProfile.CopyPackageParameters.CompressPackage 215 | } 216 | 217 | # CopyPackageTimeoutSec parameter overrides the value from the publish profile 218 | if ($CopyPackageTimeoutSec) 219 | { 220 | $PublishParameters['CopyPackageTimeoutSec'] = $CopyPackageTimeoutSec 221 | } 222 | 223 | if ($IsUpgrade) 224 | { 225 | $Action = "RegisterAndUpgrade" 226 | if ($DeployOnly) 227 | { 228 | $Action = "Register" 229 | } 230 | 231 | $UpgradeParameters = $publishProfile.UpgradeDeployment.Parameters 232 | 233 | if ($OverrideUpgradeBehavior -eq 'ForceUpgrade') 234 | { 235 | # Warning: Do not alter these upgrade parameters. It will create an inconsistency with Visual Studio's behavior. 236 | $UpgradeParameters = @{ UnmonitoredAuto = $true; Force = $true } 237 | } 238 | 239 | $PublishParameters['Action'] = $Action 240 | $PublishParameters['UpgradeParameters'] = $UpgradeParameters 241 | $PublishParameters['UnregisterUnusedVersions'] = $UnregisterUnusedApplicationVersionsAfterUpgrade 242 | 243 | Publish-UpgradedServiceFabricApplication @PublishParameters 244 | } 245 | else 246 | { 247 | $Action = "RegisterAndCreate" 248 | if ($DeployOnly) 249 | { 250 | $Action = "Register" 251 | } 252 | 253 | $PublishParameters['Action'] = $Action 254 | $PublishParameters['OverwriteBehavior'] = $OverwriteBehavior 255 | $PublishParameters['SkipPackageValidation'] = $SkipPackageValidation 256 | 257 | Publish-NewServiceFabricApplication @PublishParameters 258 | } -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/ServiceFabricWinDemo.sfproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | cc27cb67-3a62-475c-8ae6-f87b813e3cae 6 | 2.3 7 | 1.5 8 | 1.6.7 9 | v4.6.2 10 | 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Service Fabric Tools\Microsoft.VisualStudio.Azure.Fabric.ApplicationProject.targets 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ServiceFabricWinDemo/ServiceFabricWinDemo/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /global.json.xyz: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "2.1.402" 4 | } 5 | } --------------------------------------------------------------------------------