Подтвердить что ты не робот

Разверните службу .NET Windows с помощью Amazon Elastic Beanstalk без веб-приложения

Я хочу создать конфигурацию Elastic Beanstalk, которая позволяет мне развернуть службу .NET Windows, но без развертывания веб-приложения.

Я только что прочитал это сообщение в блоге, в котором объясняется, как использовать .ebextensions для развертывания службы Windows рядом с вашим веб-приложением, но есть сценарий, для которого. ebextensions можно запустить без развертывания пакета Web Deploy для веб-приложения?

Является единственной возможностью создать пустое веб-приложение, содержащее каталог .ebextensions, а затем развернуть пакет Web Deploy?

В разделе "Часто задаваемые вопросы" в разделе "Эластичные бобы" упоминается возможность развертывания не-веб-приложений (здесь), и я нашел аналогичный (не отвеченный) вопрос на форумах разработчиков AWS (здесь).

Update

Из-за отсутствия активности по этому вопросу и моей неспособности найти какую-либо другую информацию в Интернете, я просто предположил, что ответ на этот вопрос "Нет" (по крайней мере, на данный момент).

В итоге я создал пустое веб-приложение и использовал его для развертывания моей Службы Windows через конфигурацию .ebextensions YAML.

В качестве примечания я хотел бы выделить эту страницу из документации Amazon, которую я нашел очень полезным руководством для создания этих специальных файлов конфигурации.

Другое обновление

После реализации упомянутого выше подхода я обнаружил, что Elastic Beanstalk не выполнял мои сценарии .ebextensions для новых экземпляров Beanstalk. В результате служба Windows не смогла быть установлена ​​при создании новых экземпляров. Мне пришлось перепрыгнуть через еще несколько обручей, чтобы наконец прийти к масштабируемому решению. Пожалуйста, дайте мне знать, хотите ли вы детали окончательного решения.

В конечном итоге это похоже на то, что Elastic Beanstalk не предназначался для развертывания масштабируемых служб Windows.


Основное решение

Мне не удобно выпускать исходный код, поскольку он не для личного проекта, а вот базовая структура моего текущего решения для развертывания:

  • Пользовательский EC2 AMI содержит программу "bootstrap", которая запускается при запуске. Программа выполняет следующие действия:
    1.1. Загрузите 'zip' архив из (настраиваемого) 'развертывания' S3-ведра
    1.2. Извлеките загруженный zip файл во временный каталог
    1.3. Установлен/выполнен" install.bat" script (также можно настроить имя script). Этот script устанавливает и запускает службу Windows.
  • Упругий бобовый знак "Instance AMI" установлен в пользовательский AMI с программой bootsrap (см. в этой статье)

Чтобы развернуть новый код: загрузите архив .zip установки (содержащий службу Windows и файл install.bat) в ведро S3 и завершите все экземпляры EC2 для приложения Elastic Beanstalk. По мере повторного создания экземпляров программа начальной загрузки будет загружать/устанавливать обновленный код.

Конечно, если бы я начинал, я бы просто пропустил использование Elastic Beanstalk и использовал стандартное автомасштабирование AWS вместе с аналогичной схемой развертывания. Нижняя строка заключается в том, что если у вас нет веб-приложения, не используйте Elastic Beanstalk; вам лучше со стандартным автомасштабированием AWS.

Новые средства развертывания AWS

Недавно Amazon анонсировала несколько новых служб развертывания/управления кодом, которые, по-видимому, затрагивают проблемы с развертыванием: http://aws.amazon.com/blogs/aws/code-management-and-deployment/

Мне еще нужно использовать эти новые сервисы (я даже не уверен, что они еще выпущены), но они выглядят многообещающими.

4b9b3361

Ответ 1

Поскольку этот вопрос существует некоторое время и до сих пор не получил ответа, но продолжает интересоваться, позвольте мне поделиться своим решением с очень похожими проблемами - установкой службы Windows на экземпляр EC2. Однако я не использую Beanstalk, поскольку эта служба больше предназначена для быстрого развертывания веб-приложений. Вместо этого я использую непосредственно CloudFormation, который Beanstalk использует для развертывания ресурсов, связанных с веб-приложением.

Стек ожидает существующий VPC (наши интервалы через несколько зон доступности), ведро S3, в котором хранятся все артефакты сборки и пара ключей EC2. Шаблон создает экземпляр EC2 с использованием Windows AMI и нескольких других ресурсов, таких как пользователь IAM с ключами доступа и рабочим ведром S3, только для иллюстрации того, как создавать дополнительные ресурсы, которые могут понадобиться вашей службе. Шаблон также принимает в качестве параметра имя зашифрованного пакета со всеми служебными двоичными файлами и конфигурационными файлами, которые были загружены в ведро сборки артефактов S3 (мы используем сервер сборки TeamCity, который делает это для нас, но вы можете создавать и загружать пакет вручную из курс). Когда вы создаете новую версию сервиса, вы просто создаете новый пакет (например, service.v2.zip), обновляете стек с новым именем, и служба автоматически обновляется. Шаблон содержит идентификаторы AMI в 4 разных регионах, но вы всегда можете добавить другие регионы, если хотите. Здесь шаблон стека:

{
    "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 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" }
            }
        }
    }
}

Как вы можете видеть, после копирования артефактов мы выполняем пакетный файл install.bat(входит в zip файл), который перемещает файлы в нужное место и регистрирует службу. Вот содержимое файла:

@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

Шаблон также создает файл конфигурации (из файла settings.config.mustache, который также находится в веществе артефактов), содержащий информацию о других ресурсах, которые были созданы для используемой службы. Вот он:

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

Вы создаете и позже обновляете стек либо с веб-консоли AWS, либо CLI.

И это в значительной степени. Вы можете посетить веб-сайт AWS CloudFormation, чтобы получить дополнительную информацию об услуге и о том, как работать с шаблонами.

P.S.: Я понял, что было бы лучше, если бы я также использовал шаблон, который создает VPC. Я держу его отдельно, поскольку у меня есть один VPC для каждого региона. Вы можете интегрировать его с шаблоном Service, если хотите, но это будет означать, что каждый раз, когда вы создаете новый стек, будет создан новый VPC. Вот шаблон 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" }
        }
    }
}

Ответ 2

Из моего собственного опыта, реализация чего-либо с использованием ebextensions значительно добавляет к прошедшему времени для развертывания. Настолько, что может потребоваться до 15 минут, чтобы экземпляр развернулся при автомасштабировании. Почти побеждает цель.

В любом случае обязательно настройте свойство Auto Scaling Group "Срок гарантии на проверку работоспособности" на что-то значительное. Например, мы используем 900 (т.е. 15 минут). Все, что меньше, и экземпляр никогда не проходит проверку работоспособности, а событие масштабирования не выполняется; что приводит к бесконечной серии попыток масштабирования.