tutorial sirve servidores que para español elasticbeanstalk elastic ec2 aws c# .net amazon-web-services elastic-beanstalk

c# - sirve - Implementar un servicio.NET Windows con Amazon Elastic Beanstalk sin aplicación web



elasticbeanstalk que es (2)

Debido a que esta pregunta ha estado disponible por un tiempo y aún no tiene respuesta, pero continúa generando interés, permítame compartir mi solución con un problema muy similar: instalar un servicio de Windows en una instancia de EC2. Sin embargo, no estoy usando Beanstalk, ya que ese servicio está diseñado más para la implementación rápida de aplicaciones web. En su lugar, estoy usando directamente CloudFormation que Beanstalk usa debajo para implementar recursos relacionados con la aplicación web.

La pila espera VPC existente (nuestros tramos a través de varias zonas de disponibilidad), un depósito S3 que almacena todos los artefactos de construcción de servicio y un par de claves EC2. La plantilla crea una instancia de EC2 utilizando Windows AMI y algunos otros recursos, como el usuario de IAM con claves de acceso y un depósito de S3 en funcionamiento, solo para ilustrar cómo crear recursos adicionales que su servicio pueda necesitar. La plantilla también toma como parámetro el nombre de un paquete comprimido con todos los binarios de servicio y archivos de configuración cargados en el cubo S3 de artefactos de construcción (usamos el servidor de compilación TeamCity que nos lo hace, pero puede crear y cargar el paquete manualmente curso). Cuando crea una nueva versión del servicio, simplemente crea un nuevo paquete (por ejemplo, service.v2.zip), actualiza la pila con el nuevo nombre y el servicio se actualizará automáticamente. La plantilla contiene identificadores de AMI en 4 regiones diferentes, pero siempre puede agregar otras regiones si lo desea. Aquí está la plantilla de pila:

{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "Service stack template.", "Parameters": { "KeyPair": { "Type": "String", "Default": "MyDefaultKeys", "Description": "Name of EC2 Key Pair." }, "ServicePackageName": { "Type": "String", "Default": "service.zip", "Description": "Name of the zip package of the service files." }, "DeploymentBucketName": { "Type": "String", "Default": "", "Description": "Name of the deployment bucket where all the artifacts are." }, "VPCId": { "Type": "String", "Default": "", "Description": "Identifier of existing VPC." }, "VPCSubnets": { "Default": "", "Description": "Commaseparated list of existing subnets within the existing VPC. Could be just one.", "Type": "CommaDelimitedList" }, "VPCSecurityGroup": { "Default": "", "Description": "Existing VPC security group. That should be the ID of the VPC''s default security group.", "Type": "String" } }, "Mappings": { "Region2WinAMI": { "us-east-1": { "64": "ami-40f0d32a" }, "us-west-1": { "64": "ami-20601740" }, "us-west-2": { "64": "ami-ff4baf9f" }, "eu-west-1": { "64": "ami-3367d340" } } }, "Resources": { "ServiceInstance": { "Type": "AWS::EC2::Instance", "Metadata": { "Comment": "Install Service", "AWS::CloudFormation::Init": { "configSets": { "default": [ "ServiceConfig" ] }, "ServiceConfig": { "files": { "c://service//settings.config": { "source": { "Fn::Join": [ "/", [ "https://s3.amazonaws.com", { "Ref": "DeploymentBucketName" }, "deployments/stacks", { "Ref": "AWS::StackName" }, "templates/settings.config.mustache" ] ] }, "context": { "region": { "Ref": "AWS::Region" }, "accesskey": { "Ref": "IAMUserAccessKey" }, "secretkey": { "Fn::GetAtt": [ "IAMUserAccessKey", "SecretAccessKey" ] }, "bucket": { "Ref": "BucketName" } } }, "c://cfn//cfn-hup.conf": { "content": { "Fn::Join": [ "", [ "[main]/n", "stack=", { "Ref": "AWS::StackId" }, "/n", "region=", { "Ref": "AWS::Region" }, "/n", "interval=1" ] ] } }, "c://cfn//hooks.d//cfn-auto-reloader.conf": { "content": { "Fn::Join": [ "", [ "[cfn-auto-reloader-hook]/n", "triggers=post.update/n", "path=Resources.ServiceInstance.Metadata.AWS::CloudFormation::Init/n", "action=cfn-init.exe -v -s ", { "Ref": "AWS::StackName" }, " -r ServiceInstance --region ", { "Ref": "AWS::Region" }, "/n" ] ] } } }, "sources": { "c://tmp//service": { "Fn::Join": [ "/", [ "https://s3.amazonaws.com", { "Ref": "DeploymentBucketName" }, "deployments/stacks", { "Ref": "AWS::StackName" }, "artifacts/Service", { "Ref": "ServicePackageName" } ] ] } }, "commands": { "Install Service": { "command": "call c://tmp//service//install.bat", "ignoreErrors": "false" } }, "services": { "windows": { "cfn-hup": { "enabled": "true", "ensureRunning": "true", "files": [ "c://cfn//cfn-hup.conf", "c://cfn//hooks.d//cfn-auto-reloader.conf" ] } } } } } }, "Properties": { "ImageId": { "Fn::FindInMap": [ "Region2WinAMI", { "Ref": "AWS::Region" }, "64" ] }, "InstanceType": "t2.micro", "KeyName": { "Ref": "KeyPair" }, "SecurityGroupIds" : [{ "Ref": "VPCSecurityGroup" }], "SubnetId" : { "Fn::Select": [ "0", { "Ref": "VPCSubnets" } ] }, "UserData": { "Fn::Base64": { "Fn::Join": [ "", [ "<script>/n", "if not exist /"C://logs/" mkdir C://logs /n", "cfn-init.exe -v -s ", { "Ref": "AWS::StackName" }, " -r ServiceInstance --region ", { "Ref": "AWS::Region" }, " -c default /n", "</script>/n" ] ] } }, "BlockDeviceMappings": [ { "DeviceName": "/dev/sda1", "Ebs": { "DeleteOnTermination": "true", "VolumeSize": "40", "VolumeType": "gp2" } } ], "Tags": [ { "Key": "Name", "Value": { "Fn::Join": [ ".", [ { "Ref": "AWS::StackName" }, "service" ] ] } } ] } }, "BucketName": { "Type": "AWS::S3::Bucket", "Properties": { "AccessControl": "PublicRead" }, "DeletionPolicy": "Retain" }, "IAMUser": { "Type": "AWS::IAM::User", "Properties": { "Path": "/", "Groups": [ "stack-users" ], "Policies": [ { "PolicyName": "giveaccesstobuckets", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:*" ], "Resource": [ { "Fn::Join": [ "", [ "arn:aws:s3:::", { "Ref": "BucketName" }, "/*" ] ] } ] } ] } } ] } }, "IAMUserAccessKey": { "Type": "AWS::IAM::AccessKey", "Properties": { "UserName": { "Ref": "IAMUser" } } } } }

Como puede ver, después de copiar los artefactos, ejecutamos el archivo por lotes install.bat (incluido en el archivo zip) que moverá los archivos a la ubicación correcta y registrará el servicio. Aquí está el contenido del archivo:

@echo off sc query MyService > NUL IF ERRORLEVEL 1060 GOTO COPYANDCREATE sc stop MyService waitfor /T 20 ServiceStop echo D | xcopy "c:/tmp/service" "c:/service/" /E /Y /i GOTO END :COPYANDCREATE echo D | xcopy "c:/tmp/service" "c:/service/" /E /Y /i sc create MyService binpath= "c:/service/MyService.exe" start= "auto" :END sc start MyService

La plantilla también crea un archivo de configuración (desde el archivo settings.config.mustache que también reside en el cubo de artefactos) que contiene información sobre los otros recursos que se han creado para que el servicio lo use. Aquí está:

<appSettings> <add key="AWSAccessKey" value="{{accesskey}}" /> <add key="AWSSecretKey" value="{{secretkey}}" /> <add key="AWSRegion" value="{{region}}" /> <add key="AWSBucket" value="{{bucket}}" /> </appSettings>

Crea y luego actualiza la pila desde la consola web de AWS o la CLI .

Y eso es más o menos. Puede visitar el sitio web de AWS CloudFormation para obtener más información sobre el servicio y cómo trabajar con plantillas.

PD: me di cuenta de que sería mejor si también comparto la plantilla que crea la VPC. Lo mantengo separado ya que tengo una VPC por región. Puede integrarlo con la plantilla de Servicio si lo desea, pero eso significa que cada vez que cree una nueva pila, también se creará una nueva VPC. Aquí está la plantilla de VPC:

{ "AWSTemplateFormatVersion": "2010-09-09", "Description": "VPC stack template.", "Mappings": { "Region2AZ": { "us-east-1": { "AZ": [ "us-east-1a", "us-east-1b", "us-east-1d" ] }, "us-west-1": { "AZ": [ "us-west-1b", "us-west-1c" ] }, "us-west-2": { "AZ": [ "us-west-2a", "us-west-2b", "us-west-2c" ] }, "eu-west-1": { "AZ": [ "eu-west-1a", "eu-west-1b", "eu-west-1c" ] } } }, "Conditions": { "RegionHas3Zones": { "Fn::Not" : [ { "Fn::Equals" : [ { "Ref": "AWS::Region" }, "us-west-1" ] } ] } }, "Resources": { "VPC": { "Type": "AWS::EC2::VPC", "Properties": { "CidrBlock": "10.0.0.0/16", "EnableDnsSupport" : "true", "EnableDnsHostnames" : "true" } }, "VPCSecurityGroup": { "Type": "AWS::EC2::SecurityGroup", "Properties": { "GroupDescription": "Security group for VPC.", "VpcId": { "Ref": "VPC" } } }, "Subnet0": { "Type": "AWS::EC2::Subnet", "Properties": { "VpcId": { "Ref": "VPC" }, "CidrBlock": "10.0.0.0/24", "AvailabilityZone": { "Fn::Select": [ "0", { "Fn::FindInMap": [ "Region2AZ", { "Ref": "AWS::Region" }, "AZ" ] } ] } } }, "Subnet1": { "Type": "AWS::EC2::Subnet", "Properties": { "VpcId": { "Ref": "VPC" }, "CidrBlock": "10.0.1.0/24", "AvailabilityZone": { "Fn::Select": [ "1", { "Fn::FindInMap": [ "Region2AZ", { "Ref": "AWS::Region" }, "AZ" ] } ] } } }, "Subnet2": { "Type": "AWS::EC2::Subnet", "Condition": "RegionHas3Zones", "Properties": { "VpcId": { "Ref": "VPC" }, "CidrBlock": "10.0.2.0/24", "AvailabilityZone": { "Fn::Select": [ "2", { "Fn::FindInMap": [ "Region2AZ", { "Ref": "AWS::Region" }, "AZ" ] } ] } } }, "InternetGateway": { "Type": "AWS::EC2::InternetGateway", "Properties": { } }, "AttachGateway": { "Type": "AWS::EC2::VPCGatewayAttachment", "Properties": { "VpcId": { "Ref": "VPC" }, "InternetGatewayId": { "Ref": "InternetGateway" } } }, "RouteTable": { "Type": "AWS::EC2::RouteTable", "Properties": { "VpcId": { "Ref": "VPC" } } }, "Route": { "Type": "AWS::EC2::Route", "DependsOn": "AttachGateway", "Properties": { "RouteTableId": { "Ref": "RouteTable" }, "DestinationCidrBlock": "0.0.0.0/0", "GatewayId": { "Ref": "InternetGateway" } } }, "SubnetRouteTableAssociation0": { "Type": "AWS::EC2::SubnetRouteTableAssociation", "Properties": { "SubnetId": { "Ref": "Subnet0" }, "RouteTableId": { "Ref": "RouteTable" } } }, "SubnetRouteTableAssociation1": { "Type": "AWS::EC2::SubnetRouteTableAssociation", "Properties": { "SubnetId": { "Ref": "Subnet1" }, "RouteTableId": { "Ref": "RouteTable" } } }, "SubnetRouteTableAssociation2": { "Type": "AWS::EC2::SubnetRouteTableAssociation", "Condition": "RegionHas3Zones", "Properties": { "SubnetId": { "Ref": "Subnet2" }, "RouteTableId": { "Ref": "RouteTable" } } }, "NetworkAcl": { "Type": "AWS::EC2::NetworkAcl", "Properties": { "VpcId": { "Ref": "VPC" } } }, "AllowAllInboundTCPAclEntry": { "Type": "AWS::EC2::NetworkAclEntry", "Properties": { "NetworkAclId": { "Ref": "NetworkAcl" }, "RuleNumber": "100", "Protocol": "6", "RuleAction": "allow", "Egress": "false", "CidrBlock": "0.0.0.0/0", "PortRange": { "From": "0", "To": "65535" } } }, "AllowAllInboundUDPAclEntry": { "Type": "AWS::EC2::NetworkAclEntry", "Properties": { "NetworkAclId": { "Ref": "NetworkAcl" }, "RuleNumber": "101", "Protocol": "17", "RuleAction": "allow", "Egress": "false", "CidrBlock": "0.0.0.0/0", "PortRange": { "From": "0", "To": "65535" } } }, "AllowAllOutboundTCPAclEntry": { "Type": "AWS::EC2::NetworkAclEntry", "Properties": { "NetworkAclId": { "Ref": "NetworkAcl" }, "RuleNumber": "100", "Protocol": "6", "RuleAction": "allow", "Egress": "true", "CidrBlock": "0.0.0.0/0", "PortRange": { "From": "0", "To": "65535" } } }, "AllowAllOutboundUDPAclEntry": { "Type": "AWS::EC2::NetworkAclEntry", "Properties": { "NetworkAclId": { "Ref": "NetworkAcl" }, "RuleNumber": "101", "Protocol": "17", "RuleAction": "allow", "Egress": "true", "CidrBlock": "0.0.0.0/0", "PortRange": { "From": "0", "To": "65535" } } }, "SubnetNetworkAclAssociation0": { "Type": "AWS::EC2::SubnetNetworkAclAssociation", "Properties": { "SubnetId": { "Ref": "Subnet0" }, "NetworkAclId": { "Ref": "NetworkAcl" } } }, "SubnetNetworkAclAssociation1": { "Type": "AWS::EC2::SubnetNetworkAclAssociation", "Properties": { "SubnetId": { "Ref": "Subnet1" }, "NetworkAclId": { "Ref": "NetworkAcl" } } }, "SubnetNetworkAclAssociation2": { "Type": "AWS::EC2::SubnetNetworkAclAssociation", "Condition": "RegionHas3Zones", "Properties": { "SubnetId": { "Ref": "Subnet2" }, "NetworkAclId": { "Ref": "NetworkAcl" } } } }, "Outputs": { "VPC": { "Description": "VPC", "Value": { "Ref": "VPC" } }, "VPCSecurityGroup": { "Description": "VPC Security Group Id", "Value": { "Fn::GetAtt": [ "VPCSecurityGroup", "GroupId" ] } }, "Subnet0": { "Description": "Subnet0 Id", "Value": { "Ref": "Subnet0" } }, "Subnet1": { "Description": "Subnet1 Id", "Value": { "Ref": "Subnet1" } }, "Subnet2": { "Description": "Subnet2 Id", "Condition": "RegionHas3Zones", "Value": { "Ref": "Subnet2" } } } }

Quiero crear una configuración Elastic Beanstalk que me permita implementar un servicio .NET Windows pero sin implementar una aplicación web.

Acabo de leer esta publicación de blog que explica cómo usar .ebextensions para implementar un servicio de Windows junto con su aplicación web, pero ¿hay algún escenario para el cual las .ebextensions se puedan ejecutar sin implementar un paquete de Web Deploy para una aplicación web?

¿Es mi única opción crear una aplicación web vacía que contenga el directorio .ebextensions y luego implementar el paquete Web Deploy?

Las preguntas frecuentes de Elastic Beanstalk mencionan la capacidad de implementar aplicaciones no web ( here ) y he encontrado una pregunta similar (sin respuesta) en los foros de AWS para desarrolladores ( here ).

Actualizar

Debido a la falta de actividad en esta cuestión y a mi incapacidad para encontrar cualquier otra información en Internet, simplemente asumí que la respuesta a esta pregunta es "No" (al menos por ahora).

Terminé creando una aplicación web vacía y la utilicé para implementar mi servicio de Windows a través de la configuración .ebextensions YAML.

Como nota al margen, me gustaría resaltar esta página de la documentación de Amazon, que me pareció una guía muy útil para crear esos archivos de configuración especiales.

Otra actualización

Después de implementar el enfoque mencionado anteriormente, descubrí que Elastic Beanstalk no estaba ejecutando mis scripts .ebextensions para nuevas instancias de Beanstalk. Como resultado, no se pudo instalar el Servicio de Windows cuando se crearon nuevas instancias. Tuve que saltar varios aros más para finalmente llegar a una solución escalable. Por favor, avíseme si quiere los detalles de la solución final.

En última instancia, parece que Elastic Beanstalk no fue diseñado para implementar servicios escalables de Windows.

Solución Básica

No me siento cómodo liberando el código fuente ya que no era para un proyecto personal, pero aquí está la estructura básica de mi solución de implementación actual:

  1. Un AMI EC2 personalizado contiene un programa ''bootstrap'' que se ejecuta al inicio. El programa hace lo siguiente:
    1.1. Descargue un archivo ''zip'' de un contenedor S3 (configurable) ''S3''
    1.2. Extraiga el archivo zip descargado a un directorio temporal
    1.3. Se encuentra / ejecuta un script "install.bat" (el nombre del script también es configurable). Esta secuencia de comandos instala e inicia el servicio de Windows.
  2. El Elastic Beanstalk "Instance AMI" se establece en el AMI personalizado con el programa bootsrap (ver: este artículo )

Para implementar un nuevo código: cargue el archivo .zip de instalación (que contiene el servicio de Windows y el archivo install.bat) en el depósito S3 y finalice todas las instancias EC2 para la aplicación Elastic Beanstalk. A medida que se vuelvan a crear las instancias, el programa de arranque descargará / instalará el código recientemente actualizado.

Por supuesto, si comenzara de nuevo, me saltaría el uso de Elastic Beanstalk y usaré el escalamiento automático estándar de AWS junto con un esquema de implementación similar. La conclusión es que si no tiene una aplicación web, no use Elastic Beanstalk; está mejor con el escalado automático estándar de AWS.

Nuevas herramientas de implementación AWS

Amazon anunció recientemente varios servicios nuevos de implementación / administración de códigos que parecen abordar los problemas de implementación: http://aws.amazon.com/blogs/aws/code-management-and-deployment/

Aún no he usado estos nuevos servicios (ni siquiera estoy seguro de si ya los han lanzado), pero parecen prometedores.


Desde mi propia experiencia, implementar cualquier cosa que use ebextensions aumenta significativamente el tiempo transcurrido para la implementación. Tanto que puede tomar hasta 15 minutos para que una instancia gire cuando escalas automáticamente. Casi derrota el propósito.

En cualquier caso, asegúrese de configurar la propiedad "Período de gracia de control de salud" del Grupo de escalamiento automático en algo significativo. Por ejemplo, usamos 900 (es decir, 15 minutos). Cualquier cosa menos y la instancia nunca pasa el chequeo de estado y el evento de escalamiento falla; que hace una serie interminable de intentos de escalar.