martes, 26 de octubre de 2010
Azure: Desplegando y Depurando allí arriba en la nube
Después de ya un tiempo me he decidido a llevar a cabo mi primer deploy en la nube, no obstante una de las cosas que más me preocupa es el control de lo que realmente está ocurriendo. Para conseguir ser dueño de tal poder es muy importante tener en cuenta desde el principio el tratamiento de cierta información con objeto de poder consultarla para lograr finalmente coseguir la ubicación exacta de lo que ocurre, es decir, si ocurre un problema, localizarlo y solucionarlo.
Esto tiene una implicación muy importante, y es, que finalmente, se va a pensar en las trazas y en el control de errores desde el primer momento del proyecto. ¡Al fin un “hito” que no va a poder pasarse por alto, va a resultar que poco a poco vamos a ir haciendo las cosas bien! ¿Porque no pensamos tan detenidamente estas cosas en un sistema On-Premise? ¡Claro, en una última instancia, nos enfrentamos cara a cara con el servidor que esta en una sala más a la derecha y listo,
! Bueno, pues ahora nos tocará codificar algunas líneas más de código….
Por el momento veamos el deploy y todo lo que surge a su alrededor:
- Desde Visual Studio:
Click derecho sobre el proyecto de “Cloud Services” y “Publish…”. A continuación tendremos que seleccionar un certificado o crear uno nuevo directamnte desde el Wizard. Para crear uno basatará con incluir el siguiente comando en la cosola de “Windows Azure SDK v1.2”:
makecert -r -pe -a sha1 -n "CN=Windows Azure Authentication Certificate" -ss My -len 2048 -sp "Microsoft Enhanced RSA and AES Cryptographic Provider" –sy 24 elGuerreAzure.cer.
Si por alguna razón (el Visual Studio “peta”) hay que volver a lanzar de nuevo el deploy. En tal caso así como cuando queremos relanzar una publicación veremos este mensaje; (“Delete and Continue” y seguimos…) y a volver a esperar.
- Desde la Web “wwww.azure.com”:
Seleccionamos los paquetes de instalación; “.cspkg” y “.cscfg”:
A esperar los 20min de cortesía y listo,
Un tema muy importante a tener en cuenta es realizar el deploy con el IntelliTrace activado como mostraba en una imagen anterior. Esto va a permitir recuperar las trazas y poder localizar cualquier error, y sobre todo, los ocurridos durante el despliegue.
Para ello, acceder al “Server Explorer” desde Visual Studio, click derecho y “View IntelliTrace Logs”:
En ese momento comienza la descarga de las trazas desde el servidor, seguidamente podremos acceder al IntelliTrace y localizar el posible problema. Una detalle que me he encontrado es que las Dlls de MVC para ASP.Net no se encuentran instaladas en los Web Roles y por lo tanto hay que hacer un “Copy Local = True” de la misma para se incluya en el deploy y así se copie al/los servidor/es. Igualmente para el resto de Dlls que tampoco se encuentren instaladas.
Algunos errores durante el despliegue
- System.Net.WebException: Unable to connecto to the remote server
- System.ObjectDisposedException: Cannot access a disposed object.
- “System.Net.Sockets.SocketException: An attempt was made to access a socket in a way forbidden by its access permissions.”
Estos aprecen si el deploy en la nube se realiza con “UseDevelopmentStorage=true”,(de igual forma si “UseDevelopmentStorage=false”). La solución a estos, basatará con indicar los valores correctos tales como; “DefaultEndpointsProtocol=https;AccountName=[Your Account Name];AccountKey=[Your Account Key]. Es facil pero puede ocurrir que se nos olvide o que en algún caso la cadena no esté bien formada.
- “No valid combination of account information found.”
Este problema puede implicar un pequeño ajuste en nuestro código, aunque no siempre tiene que ser necesario, el porqué, aún no lo tengo muy claro. En otras casos también es posible que aparezca si la cadena de conexión a la nube no está correctamente formada:
1: CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) => 2: { 3: configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)); 4: }); 1: CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) => 2: {3: string connectionString;
4: if (RoleEnvironment.IsAvailable)
5: { 6: connectionString = RoleEnvironment.GetConfigurationSettingValue(configName); 7: } 8: else
9: { 10: connectionString = ConfigurationManager.AppSettings[configName]; 11: } 12: configSetter(connectionString); 13: });- Windows Azure - Resolving "The Path is too long after being fully qualified".
Aparece en muchas ocasiones al intentar compilar o publicar una aplicación. Puedemos solucionarlo fácilmente siguiendo los siguientes pasos. Más detalle aquí:
- Desde la consola; “Windows Azure SDK v1.2”, ejecutamos:
- csrun /devfabric:shutdow
- csrun /devfabric:clean
- Aseguramos que el icono de Azure no se encuentra en la barra de notificaciones.
- Finalmente creamos una nueva variable de entorno “_CSRUN_STATE_DIRECTORY“ y le asignamos el path que quieras “ej.: "C:\AZURE_RUN”
En un siguiente post veremos en detalle el Traceo y la monitorización,
saludos
Juanlu
Etiquetas: Azure, Depuración, Setup, Windows Azure
domingo, 18 de abril de 2010
Como hacer un setup personalizado en 15min con Visual Studio .NET
Muy buenas de nuevo, (y nunca mejor dicho lo de “nuevo”).
Llevo ya un tiempo sin aparecer por aquí, y la verdad, es que desde entonces no han dejado de aparecer novedades. Me siento como un niño pequeño que quiere jugar con sus nuevos juguetitos y, de alguna manera “no le dejan”, en fin….. Ya os contaré algunas cosas poco a poco.
En un intento de retomar mi aparición por aqui, os muestro a continuación y un pequeño artículo sobre la generación de Setup personalizados que escribí hace poco. A ver si os sirve:
Hace ya un tiempo quería comentar la facilidad de .net para la generación de setup’s personalizados y sobre todo como encaminarlos a la distribución vía SCCM (el antiguo SMS) o incluso, como sustitución a la generación de diferentes ficheros de configuración para nuestros “famosos” y diferentes entornos; desarrollo, integración y producción.
- ¿Por qué copiar de acá para allá diferentes ficheros de configuración que cambian de un día para otro?
- ¿No es mejor disponer de un mismo fichero de configuración y no compilar nada para el paso entre entornos?
Vamos a seguir detalladamente los pasos adecuados a partir de un sencillo ejemplo que consiste en un formulario Windows que utiliza un texto fijo recuperado del fichero de configuración y que podría ser cualquiera de nuestros proyectos actuales
1. Añadimos a la solución existente un proyecto de tipo “Setup and Deployment” con el nombre <NombreProyecto>Setup:
2. Añadimos un nuevo proyecto de tipo “Class library” y denominarlo por ejemplo, <NombreProyecto>CustomInstall:
3. Añadimos una nueva clase “CustomParameters.cs” o renombrar la clase “Class1.cs” generada por defecto con la siguiente información:
using System.Collections;
using System.Configuration.Install;
namespace DeployEn15MinCustomInstall
{
public class CustomParameters
{
private static System.Collections.IDictionary state = null;
public static string PREFIJO_SALUDO = "PrefijoSaludo";
private string _prefijoSaludo;
/// <summary>
/// Constructor para los instalables sin parametros de entrada
/// </summary>
/// <param name="savedState"></param>
public CustomParameters(IDictionary savedState)
{
state = savedState;
}
public string PrefijoSaludo
{
get { return _prefijoSaludo; }
set { _prefijoSaludo = value; }
}
/// <summary>
/// Recupera los parametros pasados al msi.
/// </summary>
/// <param name="installContext"></param>
public void LoadContext(InstallContext installContext)
{
this.PrefijoSaludo = installContext.Parameters[PREFIJO_SALUDO];
}
}
}
Donde; la constante “PREFIJO_SALUDO”, el atributo y la propiedad “PrefijoSaludo” serán sustituidos por cuantos parámetros sean necesarios pasar al ”.msi” para la instalación.
4. Añadimos una nueva clase “ConfigManager” con objeto de manejar la sección de “AppSettings” del fichero de configuración de la aplicación y poder añadir así nuevos valores a las key’s ya existentes.
public class ConfigManager
{
#region Attributos
private string _ConfigPath;
private Configuration _Config;
private AppSettingsSection _AppSettings;
#endregion
/// <summary>
/// Inicializa una nueva instancia de la clase<see cref="ConfigUpdater"/>.
/// </summary>
/// <param name="configPath">The config path.</param>
public ConfigManager(string configPath)
{
_ConfigPath = (configPath == null ? String.Empty : configPath);
try
{
_Config = ConfigurationManager.OpenExeConfiguration(_ConfigPath);
_AppSettings = _Config.AppSettings;
}
catch (Exception ex)
{
throw new ApplicationException(
String.Format("Error adding new configuration parameters into: '{0}'.{1}{2}",
_ConfigPath, ex.Message, ex.StackTrace));
}
}
/// <summary>
/// Añade una nueva clave (keyName) a la sección "appSettings"
/// del fichero de configuración con un valor (value).
/// </summary>
/// <param name="keyName">Nombre de la clave a añadir.</param>
/// <param name="value">Valor a asignar a la nueva clave.</param>
public void AddParam(string keyName, string value)
{
if (_AppSettings.Settings[keyName] == null)
_AppSettings.Settings.Add(keyName, value);
}
/// <summary>
/// Elimina del fichero de configuración una clave (keyName) de la sección "appSettings"
/// del fichero de configuración.
/// </summary>
/// <param name="keyName">Nombre de la clave a eliminar..</param>
public void RemoveParam(string keyName)
{
_AppSettings.Settings.Remove(keyName);
}
/// <summary>
/// Modifica o añade una clave (keyName) de la sección "appSettings".
/// </summary>
/// <param name="keyName">Nombre de la clare a añadir o modificar.</param>
/// <param name="value">Valor a asignar a la clave</param>
public void SaveParam(string keyName, string value)
{
if (_AppSettings.Settings[keyName] != null)
_AppSettings.Settings[keyName].Value = value;
else
AddParam(keyName, value);
}
/// <summary>
/// Guarda el fichero de configuración con los cambios realizados
/// en la sección "appSetting".
/// </summary>
public void Save()
{
if (!_AppSettings.ElementInformation.IsLocked)
_Config.Save();
else
throw new ApplicationException("Section was locked, could not update");
}
}
5. Añadimos al proyecto “CustomIntall” un nuevo ítem de tipo “Installer Class” y lo denominamos por ejemplo “CustomActions.cs”.
El contenido de este fichero será:
using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Reflection;
namespace DeployEn15MinCustomInstall
{
[RunInstaller(true)]
public partial class CustomActions : Installer
{
private const string PAREMETER_NOT_SPECIFIED = "El parámetro de instalación '{0}' no ha sido especificado. La instalación será cancelada.";
private CustomParameters _parameters = null;
// Añadir al array todos los parámetros requeridos durante la instalación.
// Dejarlo vacío sino hay parámetros requeridos.
private ArrayList requiredParameters = new ArrayList { CustomParameters.PREFIJO_SALUDO };
public CustomActions()
{
InitializeComponent();
}
public override void Install(System.Collections.IDictionary stateSaver)
{
#if DEBUG
Debugger.Launch();
#endif
base.Install(stateSaver);
// Si la instalación es ejectuada previo paso de parámetros.
if (this.Context != null && this.Context.Parameters.Count > 0)
{
_parameters = new CustomParameters(stateSaver);
_parameters.LoadContext(this.Context);
ConfigureFileConfig();
}
}
private void ConfigureFileConfig()
{
string assemblyPath = this.Context.Parameters["assemblyPath"];
string fileName = Path.GetFileNameWithoutExtension(assemblyPath).Replace("CustomInstall", ".exe");
string exeConfigPath = Path.Combine(Directory.GetParent(assemblyPath).FullName, fileName);
ConfigManager config = new ConfigManager(exeConfigPath);
this.UpdateAppSettings(config);
// Si todo es correcto se realiza el commit del fichero de configuración.
config.Save();
config = null;
}
private void UpdateAppSettings(ConfigManager config)
{
PropertyInfo[] props = _parameters.GetType().GetProperties();
foreach (PropertyInfo prop in props)
{
string propValue = prop.GetValue(_parameters, null) as string;
if (!String.IsNullOrEmpty(propValue))
{
config.SaveParam(prop.Name, propValue);
}
else
{
// Parámetro requerido no especificado
if (requiredParameters.Contains(prop.Name))
{
throw new InstallException(
String.Format(PAREMETER_NOT_SPECIFIED, prop.Name));
}
}
}
}
}
}
Las siguientes instrucciones
Las siguientes instrucciones van a permitir realizar una parada en tiempo de depuración con objeto de facilitar dicha labor. Esta instrucción tendrá más sentido cuando el ejecutable no se inicie directamente desde el Visual Studio.
#if DEBUG
Debugger.Launch();
#endif
6. Añadimos al proyecto de Setup dos items del tipo, “Primary Output”;
- “DeployEn15Min”(Ejecutable Windows Form) y,
- “DeployEn15MinCustomInstall”(Class Library que contiene el “Installer class”) .
7. Finalizado el paso anterior, nuestra solución tendrá la siguiente apariencia además de proyecto de Test correspondiente (como en cualquier proyecto debemos tener siempre):
8. A continuación seleccionamos el proyecto “DeployEn15MinSetup” y hacemos click en el icono de “Custom Actions”.
9. Sobre la carpeta “Install” de las Custom Actions, hamemos click derecho y seleccionamos del “Application Folder” el “Primary output” referente al “CustomInstall”.
10. Generamos un fichero de comandos “.bat” con la siguiente instrucción y lo ejecutamos en la misma carpeta en donde se encuentre el Setup:
msiexec /l* "DeployEn15Min.log" /i DeployEn15MinSetup.msi TARGETDIR="C:\Test\" allUsers=[ALLUSERS] PREFIJOSALUDO="Deploy en 15 minutos v1.1"
Ahora, el ejecutable recibe los parámetros pasados en el comando. Los que se muestran durante la instalación son parámetros estándares, sin embargo, si después de la instalación comprobamos el fichero de configuración resultante, veremos como el valor de la key “PrefijoSaludo” del “appSettings”, ha cambiado por el nuevo valor.
Es en este punto, donde cobra fuerza la instrucción, “Debugger.Launch();” antes comentada.
Para conseguir la modificación de todos nuestros parámetros de configuración durante la instalación, tendremos que añadir valores a la propiedad “CustomActionData” del Custom Action “Install” tal y como se indica en la siguiente figura para nuestro parámetro “PrefijoSaludo”:
Se añadirán tantos valores como parámetros sean susceptibles de recuperar en tiempo de instalación.
Los “custom actions”; “Commit”, “Rollback” y “Uninstall”, serán implementados de la misma manera siempre y cuando en nuestra clase DeployEn15MinCustomInstall.CustomInstall.cs sobrescribamos los métodos correspondientes. Adicionalmente podrán sobrescribirse otros métodos que ocurren antes o después de estas acciones, con lo que tendremos mayor control sobre cualquier “cosa” que queramos realizar durante y después de la instalación.
El comando “msiexec” recibe un parámetro /l* que permite indicar un fichero de log en el que se registrarán todos pasos generados durante la instalación. Su objetivo, detectar cualquier posible error durante dicho proceso.
Nota: Con objeto de mejorar la instalación de prerrequisitos, actualizaciones, parches etc. de nuestra aplicación, podemos utilizar un “.vbs” (Visual Basic Script) en lugar de un “.bat”, permitiendo de esta manera mayor control; acceso al registro de Windows, etc. El objetivo de esto es permitir a los administradores de sistemas las modificaciones adecuadas según las necesidades concretas de la distribución. Un ejemplo claro podría ser, comprobar una clave del registro de Windows para verificar la existencia de otro programa o prerrequisito, de una versión anterior, etc.
Un saludo desde mi “nueva casa”
Juanlu, El Guerre
Etiquetas: Framework .NET, msi, Setup, Visual Studio .NET