51
Deploying .NET services with Disnix Sander van der Burg Delft University of Technology, EEMCS, Department of Software Technology Philips Healthcare, Philips Informatics Infrastructure (PII), Best February 2, 2012 Sander van der Burg Deploying .NET services with Disnix

Deploying .NET services with Disnix

Embed Size (px)

DESCRIPTION

Presentation given at Philips Healthcare about deploying services implemented in .NET with Disnix

Citation preview

Page 1: Deploying .NET services with Disnix

Deploying .NET services with Disnix

Sander van der Burg

Delft University of Technology, EEMCS,Department of Software Technology

Philips Healthcare, Philips Informatics Infrastructure (PII),Best

February 2, 2012

Sander van der Burg Deploying .NET services with Disnix

Page 2: Deploying .NET services with Disnix

Service Oriented Computing

The Service Oriented Computing (SOC) paradigm is very popularto build distributed applications.

Sander van der Burg Deploying .NET services with Disnix

Page 3: Deploying .NET services with Disnix

Service Oriented Computing

Systems are composed of distributable components (or services),working together to achieve a common goal.

Sander van der Burg Deploying .NET services with Disnix

Page 4: Deploying .NET services with Disnix

Deployment

Deployment of Service Oriented Systems is complex:

Many types of components. Various operating systems. Multiplevariants.

Sander van der Burg Deploying .NET services with Disnix

Page 5: Deploying .NET services with Disnix

Deployment

Deployment of Service Oriented Systems is labourious:

Sander van der Burg Deploying .NET services with Disnix

Page 6: Deploying .NET services with Disnix

Deployment

Deployment of Service Oriented systems is error-prone

Upgrading is even harder

Sander van der Burg Deploying .NET services with Disnix

Page 7: Deploying .NET services with Disnix

Deployment

Deployment of Service Oriented systems is error-prone

Upgrading is even harder

Sander van der Burg Deploying .NET services with Disnix

Page 8: Deploying .NET services with Disnix

Deployment

Deployment of Service Oriented systems is error-prone

Upgrading is even harder

Sander van der Burg Deploying .NET services with Disnix

Upgrading

How to make upgrades safe and reliable?

What to do with incoming connections?

Rollbacks?

Page 9: Deploying .NET services with Disnix

Example case: StaffTracker

Sander van der Burg Deploying .NET services with Disnix

Page 10: Deploying .NET services with Disnix

Disnix

A toolset for distributed deployment of service-oriented systems:

Model-driven

Component-agnostic

Supported on multiple platforms:

Linux, FreeBSD, Mac OS X, Windows (through Cygwin)

Sander van der Burg Deploying .NET services with Disnix

Page 11: Deploying .NET services with Disnix

Disnix

Disnix is built on top of the Nix package manager:

Nix expression language to declaratively specify build actions

Ability to store multiple versions and variants simultaneously

Complete dependencies

Reliable and atomic upgrades and rollbacks

Garbage collector to safely remove components no longer inuse

Sander van der Burg Deploying .NET services with Disnix

Page 12: Deploying .NET services with Disnix

Disnix: Models

$ disnix-env -s services.nix -i infrastructure.nix -d distribution.nix

Sander van der Burg Deploying .NET services with Disnix

Page 13: Deploying .NET services with Disnix

Disnix expressions

{ stdenv, fetchurl, unzip, apacheAnt }:

{ staff }:

stdenv.mkDerivation {

name = "StaffService";

src = fetchurl {

url = http://.../StaffService-0.1.zip;

sha256 = "0fpjlr3bfind0y94bk442x2p...";

};

buildInputs = [ unzip apacheAnt ];

buildCommand = ’’

unzip $src

ant webapp

mkdir -p $out/webapps

cp *.war $out/webapps

cat > $out/conf/Catalina/StaffService.xml <<EOF

<Context>

<Resource name="jdbc/staff" auth="Container" type="javax.sql.DataSource"

url="jdbc:mysql://${staff.target.hostname}/${staff.name}" ... />

</Context>

EOF

’’;

}

Sander van der Burg Deploying .NET services with Disnix

Page 14: Deploying .NET services with Disnix

Disnix expressions

{ stdenv, fetchurl, unzip, apacheAnt }:

{ staff }:

stdenv.mkDerivation {

name = "StaffService";

src = fetchurl {

url = http://.../StaffService-0.1.zip;

sha256 = "0fpjlr3bfind0y94bk442x2p...";

};

buildInputs = [ unzip apacheAnt ];

buildCommand = ’’

unzip $src

ant webapp

mkdir -p $out/webapps

cp *.war $out/webapps

cat > $out/conf/Catalina/StaffService.xml <<EOF

<Context>

<Resource name="jdbc/staff" auth="Container" type="javax.sql.DataSource"

url="jdbc:mysql://${staff.target.hostname}/${staff.name}" ... />

</Context>

EOF

’’;

}

Sander van der Burg Deploying .NET services with Disnix

Disnix expression

Builds and configures a component from itsintra-dependencies and inter-dependencies

Any build tool can be invoked as long as it is pure

For example: Apache Ant, Perl, Python projects arealso supportedYou can also deploy binary software, by patching it

Output is produced in a unique directory in the Nixstore

Page 15: Deploying .NET services with Disnix

Intra-dependency composition

{pkgs, system}:

rec {

apacheAnt = ...

stdenv = ...

staff = import ../pkgs/staff {

inherit stdenv;

};

StaffService = import ../pkgs/StaffService {

inherit stdenv fetchurl unzip apacheAnt;

};

StaffTracker = import ../pkgs/StaffTracker {

inherit stdenv fetchurl unzip apacheAnt;

};

...

}

Composes components locally, by calling functions withintra-dependency arguments.

Sander van der Burg Deploying .NET services with Disnix

Page 16: Deploying .NET services with Disnix

Service model

{distribution, pkgs, system}:

rec {

staff = {

name = "staff";

pkg = pkgs.staff;

type = "mysql-database";

};

StaffService = {

name = "StaffService";

pkg = pkgs.StaffService;

dependsOn = { inherit staff; };

type = "tomcat-webapplication";

};

StaffTracker = {

name = "StaffTracker";

pkg = pkgs.StaffTracker;

dependsOn = { inherit StaffService ...; };

type = "tomcat-webapplication";

};

...

}

Sander van der Burg Deploying .NET services with Disnix

Page 17: Deploying .NET services with Disnix

Service model

{distribution, pkgs, system}:

rec {

staff = {

name = "staff";

pkg = pkgs.staff;

type = "mysql-database";

};

StaffService = {

name = "StaffService";

pkg = pkgs.StaffService;

dependsOn = { inherit staff; };

type = "tomcat-webapplication";

};

StaffTracker = {

name = "StaffTracker";

pkg = pkgs.StaffTracker;

dependsOn = { inherit StaffService ...; };

type = "tomcat-webapplication";

};

...

}

Sander van der Burg Deploying .NET services with Disnix

Service model

Defines the distributable components (or services)

Refers to the intra-dependency closure of each service

Composes components from its inter-dependencies

Specifies component types for activation/deactivation

Page 18: Deploying .NET services with Disnix

Infrastructure model

{

test1 = {

hostname = "test1.net";

tomcatPort = 8080;

mysqlUser = "user";

mysqlPassword = "secret";

mysqlPort = 3306;

targetEPR = http://test1.net/.../DisnixService;

system = "i686-linux";

};

test2 = {

hostname = "test2.net";

tomcatPort = 8080;

...

targetEPR = http://test2.net/.../DisnixService;

system = "x86_64-linux";

};

}

Captures machines in the network and their relevant properties andcapabilities.

Sander van der Burg Deploying .NET services with Disnix

Page 19: Deploying .NET services with Disnix

Distribution model

{infrastructure}:

{

staff = [ infrastructure.test1 ];

StaffService = [ infrastructure.test2 ];

StaffTracker = [ infrastructure.test1 infrastructure.test2 ];

...

}

Maps services to machines

Sander van der Burg Deploying .NET services with Disnix

Page 20: Deploying .NET services with Disnix

Disnix: Deployment

Complete deployment process is derived from the models:

Building services from source code

Optionally, build can be delegated to a capable machine

Transferring services (and intra-dependencies) to targetmachines

Deactivation of obsolete services and activation of newservices in the right order

Optionally, incoming connections can be blocked/queued

Sander van der Burg Deploying .NET services with Disnix

Page 21: Deploying .NET services with Disnix

Building services

Main idea: store all packagesin isolation from each other:

/nix/store/rpdqxnilb0cg...-firefox-3.5.4

Paths contain a 160-bitcryptographic hash of allinputs used to build thepackage:

Sources

Libraries

Compilers

Build scripts

. . .

/nix/storel9w6773m1msy...-openssh-4.6p1

bin

ssh

sbin

sshdsmkabrbibqv7...-openssl-0.9.8e

lib

libssl.so.0.9.8c6jbqm2mc0a7...-zlib-1.2.3

lib

libz.so.1.2.3im276akmsrhv...-glibc-2.5

lib

libc.so.6

Sander van der Burg Deploying .NET services with Disnix

Page 22: Deploying .NET services with Disnix

Runtime dependencies

Nix detects runtime dependencies of a build

Nix scans all files of a component for hash-codes of adependency, e.g. RPATH defined in a ELF header

Uses a notion of closures to guarantee completeness:

If a specific component needs to be deployed, all its runtimedependencies are deployed first

Sander van der Burg Deploying .NET services with Disnix

Page 23: Deploying .NET services with Disnix

Runtime dependencies

/nix/storenqapqr5cyk...-glibc-2.9

lib

libc.so.6ccayqylscm...-jre-1.6.0 16

binjava

n7s283c5yg...-SDS2Util

sharejava

SDS2Util.jar

y2ssvzcd86...-SDS2EventGenerator

bin

SDS2EventGenerator

sharejava

SDS2EventGenerator.jar

Sander van der Burg Deploying .NET services with Disnix

Page 24: Deploying .NET services with Disnix

Transferring closures

Copy services and intra-dependencies to target machines inthe network

Only components missing on target machines are transferred

Always safe. Files are never overwritten/removed

Sander van der Burg Deploying .NET services with Disnix

Page 25: Deploying .NET services with Disnix

Transition phase

Inter-dependency graph is derived from specifications

Deactivate obsolete services (none, if activating newconfiguration)

Activate new services

Derive order and dependencies from dependency-graph

Sander van der Burg Deploying .NET services with Disnix

Page 26: Deploying .NET services with Disnix

Service activation

Every service has a type: mysql-database,tomcat-webapplication, process, ...

Types are attached to external processes, which take 2arguments

activate or deactivateNix store component

Infrastructure model properties are passed as environmentvariables

Sander van der Burg Deploying .NET services with Disnix

Page 27: Deploying .NET services with Disnix

Atomic upgrading

Two-phase commit protocol mapped onto Nix primitives

Commit-request-phase (distribution phase):

Build sourcesTransfer service closures

Commit-phase (transition phase):

Deactivating/activating servicesOptionally block/queue connections from end-users

Sander van der Burg Deploying .NET services with Disnix

Page 28: Deploying .NET services with Disnix

Atomic upgrading

In case of failure during commit-request: system is notaffected

No files overwrittenWill be removed by garbage collector

In case of failure during commit:

Rollback (transition to previous configuration)

Sander van der Burg Deploying .NET services with Disnix

Page 29: Deploying .NET services with Disnix

Disnix: Examples

Example cases:

Staff Tracker. Toy SOA system which comes in two variants(PHP/MySQL and Webservices/Java).

ViewVC. Open-source web based Subversion repository viewer(http://viewvc.tigris.org).

SDS2. Industrial SOA case study from Philips Research.Java EE based.

Sander van der Burg Deploying .NET services with Disnix

Page 30: Deploying .NET services with Disnix

Deploying .NET services

So far, Disnix case studies are deployed on UNIX-like systems,using portable component technologies

Deployment of .NET services on a Windows infrastructure ischallenging because of the underlying purely functionaldeployment model of Nix and its UNIX heritage.

Sander van der Burg Deploying .NET services with Disnix

Page 31: Deploying .NET services with Disnix

Requirements

Buildtime support:

Compile Visual Studio solutions and produce assemblies

Invoke MSBuild.exe with right parameters

The build process must find its buildtime dependencies

Runtime support:

Assemblies must find its runtime dependencies

Integrate .NET CLR dependency resolver with Nix

Activation support:

Activate web application components in IIS

Activate databases in Microsoft SQL server

Sander van der Burg Deploying .NET services with Disnix

Page 32: Deploying .NET services with Disnix

Disnix extensions

How to extend Disnix to support .NET service deployment?

Sander van der Burg Deploying .NET services with Disnix

Page 33: Deploying .NET services with Disnix

Windows support

Nix is supported on Windows through Cygwin:

Offers UNIX implementation on top of Windows

Allows combining UNIX and Windows utilities

Sander van der Burg Deploying .NET services with Disnix

Page 34: Deploying .NET services with Disnix

Windows support

Several Cygwin utilities map Windows concepts to UNIX conceptsand vica versa:

Can be used to glue UNIX tooling to Windows processes

cygpath, UNIX path names <=> Windows path names

Sander van der Burg Deploying .NET services with Disnix

Page 35: Deploying .NET services with Disnix

Implementing build support

A .NET Nix build function dotnetenv.buildSolution isimplemented:

Takes source code of a Visual Studio C# solution

Produces output of the solution into a unique store path,based on the input arguments of this function

Sander van der Burg Deploying .NET services with Disnix

Page 36: Deploying .NET services with Disnix

Build function implementation

{stdenv, dotnetfx}:

{ name, src, slnFile, targets ? "ReBuild"

, options ? "/p:Configuration=Debug;Platform=Win32"

, assemblyInputs ? []

}:

stdenv.mkDerivation {

inherit name src;

buildInputs = [ dotnetfx ];

installPhase = ’’

for i in ${toString assemblyInputs}; do

windowsPath=$(cygpath --windows $i)

AssemblySearchPaths="$AssemblySearchPaths;$windowsPath"

done

export AssemblySearchPaths

ensureDir $out

outPath=$(cygpath --windows $out)\\

MSBuild.exe ${slnFile} /nologo /t:${targets} \

/p:OutputPath=$outPath ${options} ...

’’;

}

Sander van der Burg Deploying .NET services with Disnix

Page 37: Deploying .NET services with Disnix

Disnix expression

{dotnetenv}:

{zipcodes}:

dotnetenv.buildSolution {

name = "ZipcodeService";

src = ../../../../services/webservices/ZipcodeService;

baseDir = "ZipcodeService";

slnFile = "ZipcodeService.csproj";

targets = "Package";

preBuild = ’’

sed -e ’s|.\SQLEXPRESS|${zipcodes.target.hostname}\SQLEXPRESS|’ \

-e ’s|Initial Catalog=zipcodes|Initial catalog=${zipcodes.name}|’ \

-e ’s|User ID=sa|User ID=${zipcodes.target.msSqlUsername}|’ \

-e ’s|Password=admin123$|Password=${zipcodes.target.msSqlPassword}|’ \

Web.config

’’;

}

Builds and configures a Visual Studio ASP.NET project

Sander van der Burg Deploying .NET services with Disnix

Page 38: Deploying .NET services with Disnix

Runtime dependencies

The .NET runtime has complicated means to locate runtimedependencies.

Sander van der Burg Deploying .NET services with Disnix

Page 39: Deploying .NET services with Disnix

Resolving runtime dependencies

.NET runtime locaties assemblies:

Determines the correct version of the assembly (strong-namedassemblies only)

Checks whether the assembly has been bound before(strong-named assemblies only)

If so, it uses this versionShares same assemblies in memory between applications

Checks the Global Assembly Cache (GAC) (strong-namedassemblies only)

Probes the assembly

Checking the <codebase> element in the application .configProbing heuristics

Sander van der Burg Deploying .NET services with Disnix

Page 40: Deploying .NET services with Disnix

Assembly probing

A <codebase> element in the application .config file can specifyits own assembly references:

Private assemblies can only reside in the directory or asubdirectory of the executable

Strong-named assemblies can be invoked from an arbitrarylocation, including remote locations

Referenced assembly is cached, therefore a strong-name isrequired

Sander van der Burg Deploying .NET services with Disnix

Page 41: Deploying .NET services with Disnix

Resolving runtime dependencies

You can only automatically resolve runtime dependencies inthe basedir as an executable.

.NET reflection API can load private assemblies from anylocation

.NET CLR fires an AssemblyResolve event if a dependencycannot be found

Can be used to load a private runtime assembly from arbitrarylocations, e.g. the Nix store

Sander van der Burg Deploying .NET services with Disnix

Page 42: Deploying .NET services with Disnix

Wrapper class

namespace HelloWorldWrapper {

class HelloWorldWrapper {

private String[] AssemblySearchPaths = {

@"C:\cygwin\nix\store\23ga...-ALibrary",

@"C:\cygwin\nix\store\833p...-BLibrary"

};

private String ExePath =

@"C:\cygwin\nix\store\27f2...-Executable\Executable.exe";

private String MainClassName = "SomeExecutable.Executable";

public HelloWorldWrapper(string[] args) {

AppDomain currentDomain = AppDomain.CurrentDomain;

currentDomain.AssemblyResolve +=

new ResolveEventHandler(MyResolveEventHandler);

Assembly exeAssembly = Assembly.LoadFrom(ExePath);

Type mainClass = exeAssembly.GetType(MainClassName);

MethodInfo mainMethod = mainClass.GetMethod("Main");

mainMethod.Invoke(this, new Object[] {args});

}

static void Main(string[] args) {

new HelloWorldWrapper(args);

}

Sander van der Burg Deploying .NET services with Disnix

Page 43: Deploying .NET services with Disnix

Wrapper class

private Assembly MyResolveEventHandler(object sender, ResolveEventArgs args) {

Assembly MyAssembly;

String assemblyPath = "";

String requestedAssemblyName =

args.Name.Substring(0, args.Name.IndexOf(","));

foreach (String curAssemblyPath in AssemblySearchPaths)

{

assemblyPath = curAssemblyPath + "/" +

requestedAssemblyName + ".dll";

if (File.Exists(assemblyPath))

break;

}

MyAssembly = Assembly.LoadFrom(assemblyPath);

return MyAssembly;

}

Sander van der Burg Deploying .NET services with Disnix

Page 44: Deploying .NET services with Disnix

Wrapper function

{dotnetenv, MyAssembly1, MyAssembly2}:

dotnetenv.buildWrapper {

name = "My.Test.Assembly";

src = /path/to/source/code;

slnFile = "Assembly.sln";

assemblyInputs = [

dotnetenv.assembly20Path

MyAssembly1

MyAssembly2

];

namespace = "TestNamespace";

mainClassName = "MyTestApplication";

mainClassFile = "MyTestApplication.cs";

}

Builds a Visual Studio project and creates a wrapper around theexecutable

Sander van der Burg Deploying .NET services with Disnix

Page 45: Deploying .NET services with Disnix

Activation scripts

Activation scripts for mssql-database and iis-webapplicationmust be provided

mssql-database invokes OSQL.EXE to automatically create adatabase and to import table definitions

iis-webapplication invokes MSDeploy.exe to deploy webapplications on a IIS server

Sander van der Burg Deploying .NET services with Disnix

Page 46: Deploying .NET services with Disnix

Example case: StaffTracker.NET

Ported from Java / Apache Axis2 to .NET / C# / WCF

Sander van der Burg Deploying .NET services with Disnix

Page 47: Deploying .NET services with Disnix

Demo

Sander van der Burg Deploying .NET services with Disnix

Page 48: Deploying .NET services with Disnix

Conclusion

With a number of Disnix extensions it’s possible to automaticallydeploy a service-oriented system using .NET technology

Sander van der Burg Deploying .NET services with Disnix

Page 49: Deploying .NET services with Disnix

Limitations

Disnix only manages service components, not infrastructure

Microsoft Windows, SQL server and IIS must be installed byother means

.NET framework is not deployed by Nix/Disnix

.NET deployment features are relatively new and untested

Sander van der Burg Deploying .NET services with Disnix

Page 50: Deploying .NET services with Disnix

References

Nix, Nixpkgs, Hydra, NixOS, Disnix, http://nixos.org.

Cygwin, http://www.cygwin.com

StaffTracker.NET example,http://nixos.org/disnix/examples.html.

Sander van der Burg Deploying .NET services with Disnix

Page 51: Deploying .NET services with Disnix

Questions

Sander van der Burg Deploying .NET services with Disnix