Azure ServiceDefinition file - ProgramEntryPoint, runtime IP address and Port - azure

I'm running a command line program (happens to be Redis) inside a Windows Azure Worker Role using ProgramEntryPoint as follows
<WorkerRole name="Worker" vmsize="Small">
<Runtime executionContext="limited">
<Environment>
<Variable name="ADDRESS">
<RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/Endpoints/Endpoint[#name='Redis']/#address" />
</Variable>
<Variable name="PORT">
<RoleInstanceValue xpath="/RoleEnvironment/CurrentInstance/Endpoints/Endpoint[#name='Redis']/#port" />
</Variable>
</Environment>
<EntryPoint>
<ProgramEntryPoint commandLine="redis-server.exe" setReadyOnProcessStart="true" />
</EntryPoint>
</Runtime>
<Endpoints>
<InternalEndpoint name="Redis" protocol="tcp" port="6379" />
</Endpoints>
</WorkerRole>
So far, so good (it works).
I now want to run a slave instance of the server in another WorkerRole
<WorkerRole name="SlaveWorker" vmsize="Small">
<Runtime executionContext="limited">
<EntryPoint>
<ProgramEntryPoint commandLine="echo slaveof %ADDRESS% %PORT% | redis-server.exe -" setReadyOnProcessStart="true" />
</EntryPoint>
</Runtime>
<Imports>
<Import moduleName="Diagnostics" />
<Import moduleName="RemoteAccess" />
</Imports>
<Endpoints>
<InternalEndpoint name="Redis" protocol="tcp" port="6379" />
</Endpoints>
</WorkerRole>
You can see I need to tell the slave server where its master is using IP address and port; something that I don't know until Azure has allocated the network resources for that role. I've seen #smarx do something along these lines.
However I think there may be a couple of things wrong with this in my case
I'm setting environment variables in one role and hoping to use them in another - not going to work.
Even if the right data was available the way I need to pass it to the redis-server.exe is not recognized as a valid entry point with the echo at the beginning
Is the only way to know the runtime IP address and Port of another
worker role via code or is there a syntax I'm missing in the config
file?
If I manage to get the IP and Port, is the only way to make my
command line work to push it to a powershell script or batch file?
Thanks for your thoughts.

The only way one instance will know another's IP address will be if a.) it programmatically grabs it, or b.) the other instance publishes it to a wellknown location (e.g. table storage). In your case, it might be easiest to just have the slave role run a startup task that accesses the RoleEnvironment (via Powershell perhaps) and sets an Environment variable with the IP Address of the master. If you do this as a 'simple' type, I believe it will run before your ProgramEntryPoint does (blocking) and you can just use the env var in your command line there.
Couple thoughts here however:
How are you handling multi-instance within a role? Are you only planning on running a single instance?
Do you need two different roles? Why not a single role with 2 instances that decide via election which is master?

Related

ASP.NET 5 web application as Azure Web Role?

We have a ASP.NET 5 web application in our solution.
Typically, we could right click on the Cloud Service "Roles" item and add a new role from an existing project in the solution.
But it cannot identity this project as a Web Role:
How are we able to host a ASP.NET 5 project in a Azure Web Role?
Edit: We are using Azure SDK 2.7
Sorry, we don't support WebRoles at the moment. You might* be able to hack your way around but officially there is no support. That means that any hack you do, will be in a text editor, not from tooling.
However, you can use an Azure WebSite instead. That's fully supported.
* it might not work at all. I am not aware of anyone who did this.
You probably have to build your package yourself using CSPack. Here an example using PowerShell and CSPack:
# path to cspack
$cspackPath = Join-Path $env:ProgramFiles 'Microsoft SDKs\Azure\.NET SDK\v2.7\bin\cspack.exe'
$PackagePath = 'c:\mycloudpackage.cspkg'
$serviceDefinitionFile = 'c:\myProject\ServiceDefinition.csdef'
$webRoleName = 'MyWebRole'
$webRolePath = 'c:\myProject'
$webRoleEntryPoint = 'MyWebRole.dll'
# define the cspack parameters
$cspackParameter = #(
"/out:$PackagePath",
$serviceDefinitionFile,
"/role:$webRoleName;$webRolePath;$webRoleEntryPoint",
"/sites:$webRoleName;Web;$webRolePath"
)
# execute cspack
& $cspackExe #cspackParameter
It also allows you to host multiple sites on a single web role.
Edit: Cannot be done with Azure Storage Emulator...
I really struggled with this as I found the documentation seriously poor with no proper examples, so here's a full example of my scripts and files for anyone else, based on Martin Brandl's answer.
You do not need webRoleEntryPoint for only a web role. Only used for worker roles.
Create a new empty cloud service in your project. This will generate emptyServiceConfigiguration.Cloud.csfg, ServiceConfigiguration.Cloud.csfg and ServiceDefinition.csdef files for you as well as an empty roles folder. You could also add a web/worker role to let visual studio generate the configuration in them and then just modify them accordingly.
Modify these files (change the physicalDirectory to your own):
ServiceDefinition.csdef:
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="SoundVast.Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2015-04.2.6">
<WebRole name="WebRole1" vmsize="Small">
<Sites>
<Site name="Web" physicalDirectory="../SoundVast">
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" />
</Bindings>
</Site>
</Sites>
<ConfigurationSettings>
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" />
</ConfigurationSettings>
<Endpoints>
<InputEndpoint name="Endpoint1" protocol="http" port="80" />
</Endpoints>
</WebRole>
</ServiceDefinition>
<Site name="Web" physicalDirectory="../SoundVast"> is the important line, this physicalDirectory is relative to wherever your .csdef file is located and I wanted to make my main project SoundVast the web role which was located one level up.
ServiceConfiguration.Cloud.csfg and ServiceConfiguration.Local.csfg (both can be the same):
<?xml version="1.0" encoding="utf-8"?>
<ServiceConfiguration serviceName="SoundVast.Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="4" osVersion="*" schemaVersion="2015-04.2.6">
<Role name="WebRole1">
<Instances count="1" />
<ConfigurationSettings>
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="UseDevelopmentStorage=true" />
</ConfigurationSettings>
</Role>
</ServiceConfiguration>
The important part is that the role name matches your <Role name="WebRole1"> service definition files web role name.
# path to cspack
$cspackPath = Join-Path $env:ProgramFiles 'Microsoft SDKs\Azure\.NET SDK\v2.8\bin\cspack.exe'
$PackagePath = 'C:\Users\Yamo\Documents\visual studio 2015\Projects\SoundVast\SoundVast.Azure\SoundVast.cspkg'
$serviceDefinitionFile = 'C:\Users\Yamo\Documents\visual studio 2015\Projects\SoundVast\SoundVast.Azure\ServiceDefinition.csdef'
$webRoleName = 'WebRole1'
$webRolePath = 'C:\Users\Yamo\Documents\visual studio 2015\Projects\SoundVast\SoundVast.Azure'
# define the cspack parameters
$cspackParameter = #(
$serviceDefinitionFile,
"/role:$webRoleName;$webRolePath;",
"/sites:$webRoleName;SoundVast;$webRolePath",
"/out:$PackagePath"
)
# execute cspack
& $cspackPath #cspackParameter
A .cspkg file should now have been generated at the location of your $PackagePath.
I've just blogged on how to do this (with VS tooling support!) here: https://oren.codes/2017/10/16/using-asp-net-core-with-azure-cloud-services/
It appears that it isn't officially supported as a web role at this time. It seems that it is only compatible with the web apps and not the older web role. The current work around is documented on this site:
http://www.codeproject.com/Articles/331425/Running-an-EXE-in-a-WebRole-on-Windows-Azure

Deploying to new D-series Azure VM's

I'm trying to deploy a test instance of our Azure-based webservice to the new D-series Azure vm's. We make extensive use of temp files and are hoping we'll see some good performance improvements. Unfortunately we don't seem to be able to package or build using the new vmsizes. Our current csdef looks like this:
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="WebAPI.Azure" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" schemaVersion="2013-10.2.2">
<WebRole name="WebAPI" vmsize="Large">
<Sites>
<Site name="Web">
<Bindings>
<Binding name="Endpoint1" endpointName="NonSSL Endpoint" />
</Bindings>
</Site>
</Sites>
<Endpoints>
<InputEndpoint name="NonSSL Endpoint" protocol="http" port="80" />
<InternalEndpoint name="InternalHttpIn" protocol="http" />
</Endpoints>
</WebRole>
</ServiceDefinition>
If I switch the vmsize from "Large" to "Standard_D3" and try to build or package for publishing, I get this error:
Error 2 The XML specification is not valid: The 'vmsize' attribute is invalid - The value 'Standard_D3' is invalid according to its datatype 'http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition:RoleSize' - The Enumeration constraint failed. C:\Users\ablack\Desktop\WebAPI.Azure\ServiceDefinition.csdef 3 34 WebAPI.Azure
How do I get this schema updated? Or is there a way to override XML validation during the build & package process?
Apparently the 2.4 SDK removes the vmsize constraint entirely (it accepts any values, including made-up values like "Supersize"), so we will have to upgrade to that to try any new size options. This will complicate benchmarking (we'll need to get fresh baselines with the updated SDK first), but c'est la vie.

Why is CloudConfigurationManager using my Cloud.cscfg instead of Local.cscfg too?

I do realise that his question was asked and answered, but unfortunately the solution of complete clean, rebuild, restart.. doesn't work in my case and my lowly reputation doesn't allow me to comment. So I am I think compelled to ask it again with my info.
Sample code:
CloudStorageAccount storageAccount;
string settings = CloudConfigurationManager.GetSetting("StorageConnectionString");
storageAccount = CloudStorageAccount.Parse(settings);
I have my web.config section like this:
<appSettings>
<add key="owin:AppStartup" value="zzzz" />
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=xxxx;AccountKey=yyyy"/>
</appSettings>
In the ServiceConfiguration.Cloud.cscfg I have:
<ConfigurationSettings>
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="DefaultEndpointsProtocol=https;AccountName=nnnn" />
<Setting name="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=xxxx;AccountKey=yyyy"/>
</ConfigurationSettings>
and in the ServiceConfiguration.Local.cscfg I have:
<ConfigurationSettings>
<Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="UseDevelopmentStorage=true" />
<Setting name="StorageConnectionString" value="UseDevelopmentStorage=true" />
</ConfigurationSettings>
I converted this to an Azure project from a standard MVC web project in order to use the Azure storage blobs etc. I am finding that no matter what I seem to do it always uses the Azure storage.
As I step through the code snippet above.. I can clearly see the returned connection string as the one coming from the web.config app setting... I feel I must be doing something fundamentally wrong or missing something..?
A small point (maybe?) as I converted the project over, there was an error message (on a pop up and not saveable) about a connection string error and it not working. I hadn't even created this particular connection string at that time and the only other one (for localDB does work). That however is in the web.config section and as it ain't broke I didn't fix it to go into the ..
Any help would be appreciated.
Further Addition, from the comments by Igorek below, I did check the Role settings and they appear to be correct.
Then .. after a lot of messing around, some experiments which still didn't work, I've taken a step back. I actually don't want a cloud service, I ended up with one because I thought I needed one to access Blobs and Queues, I had already decided that WebJobs seems like the way to go first to keep as abstracted as possible.
So I have rolled back to prior to the Web SITE that I had before and found but I still CAN'T seem to get it to use development storage.. although I imagine that CLoudConfigurationManager probably doesn't handle Web Sites? Any tips?
Check into settings of your Role within the cloud project. It will have a default for which configuration it starts with. Simply swap from Cloud to Local.

Azure project lost endpoints and uses default now?

A weird thing happened to my project. I have an Azure WCF project which basically consists of the WebRole and the Azure project. Azure Project contains ServiceDefinition.csdef which in turn contains stuff like endpoint information.
I was playing around in my WebRole and manually set an endpoint there. However, my original issue, due to a stupid user error, did not require this. After I removed the endpoint devinition from web.config, my webrole still gets bound to port 6627 instead of the two endpoints described in my Azure project (80 & 8080). I can't find that port being mentioned anywhere so I'm guessing it is the default.
Here's the part of the web.config that I edited (the removed part is in comments). How do I revert back to getting the configuration from the Azure project?
<system.serviceModel>
<!-- services>
<service name="MyWebRole.MyService" behaviorConfiguration="MyWebRole.BasicUserInformationBehavior">
<endpoint address="" binding="mexHttpBinding" contract="MyWebRole.IMyService"/>
</service>
</services -->
<extensions>
<behaviorExtensions>
<add name="userInformationProcessor" type="MyWebRole.BasicUserInformationBehaviorExtensionElement, MyWebRole, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
<bindings />
<client />
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
<userInformationProcessor />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
[Edit] More information on the subject! The problem is related to compute emulator no longer starting at all! I don't know why the service works then, but I guess it's running it IIS alone.
I think the solution as mentioned in the comment is that you have to set up the Windows Azure project as the startup project not the webrole.

Azure mod-rewrite to SSL doesn't work

I have deployed an ASP.NET web application to Azure running in a web role. It is configured with a DNS name like 'myapp.cloudapp.net' and SSL (the certificate is self-signed, meaning we get a certificate warning but otherwise this is all set up and works fine)
I want to automagically route requests the come on http to https. So, I added an http endpoint and set up a rewrite rule as per the instructions here (second method):
http://blog.smarx.com/posts/redirecting-to-https-in-windows-azure-two-methods
It doesn't work properly.
Testing directly on the Azure machine (via remote desktop):
Access https://myapp.cloudapp.net - works
Access http://myapp.cloudapp.net - "Internet explorer cannot display the webpage"
So I go to IIS and 'browse' my site: this takes me there via the IP address:
Access https://[ipaddress] - works
Access http://[ipaddress] - works! Sends me to https://[ipaddress]
So what am I missing, that this works via IP address but not by host name?
I would like to hit http://myapp.cloudapp.net and be redirected to https://myapp.cloudapp.net.
For the record, here is the relevant parts of my configuration:
<Sites>
<Site name="Web">
<Bindings>
<Binding name="Endpoint1" endpointName="MyApp" />
<Binding name="Endpoint1" endpointName="MyAppHttp" />
</Bindings>
</Site>
</Sites>
<Endpoints>
<InputEndpoint name="MyApp" protocol="https" port="443" certificate="MYCERT" />
<InputEndpoint name="MyAppHttp" protocol="http" port="80" />
</Endpoints>
<Certificates>
<Certificate name="MYCERT" storeLocation="LocalMachine" storeName="My" />
</Certificates>
Can anyone help explain what I might be doing wrong?
Thanks in advance
Not a lot to go on, but one thing that jumps out at me is the 'name' of your bindings. Make them different.

Resources