web-dev-qa-db-ja.com

WebアプリケーションなしでAmazon Elastic Beanstalkを使用して.NET Windowsサービスをデプロイする

Webアプリケーションをデプロイせずに.NET WindowsサービスをデプロイできるElastic Beanstalk設定を作成したい。

このブログ投稿 を読んだばかりです。これは、.ebextensionsを使用してWebサービスと一緒にWindowsサービスを展開する方法を説明していますが、Web Deployパッケージを展開せずに.ebextensionsを実行できるシナリオがありますWebアプリケーションの場合

.ebextensionsディレクトリを含む空のWebアプリケーションを作成し、Web配置パッケージを配置する唯一のオプションはありますか?

Elastic Beanstalk FAQ=ウェブ以外のアプリケーションをデプロイする機能について言及しています( here )、AWS開発者フォーラムで同様の(未回答の)質問を見つけました(- ここ )。

更新

この質問に対する活動が不足しており、インターネット上で他の情報を見つけることができないため、この質問に対する答えは「いいえ」であると仮定しました(少なくとも今のところ)。

最終的に空のWebアプリケーションを作成し、それを使用して.ebextensions YAML構成を介してWindowsサービスを展開しました。

サイドノートとして、 Amazonのドキュメントのこのページ を強調したいと思います。これは、これらの特別な構成ファイルを作成するのに非常に役立つガイドであることがわかりました。

別の更新

上記のアプローチを実装した後、Elastic Beanstalkが.ebextensions新しいBeanstalkインスタンスのスクリプト。その結果、新しいインスタンスが作成されたときにWindowsサービスをインストールできませんでした。最終的にスケーラブルなソリューションにたどり着くには、さらにいくつかのフープを飛び越えなければなりませんでした。最終的なソリューションの詳細が必要な場合はお知らせください。

最終的には、Elastic BeanstalkはスケーラブルなWindowsサービスをデプロイするためのものではなかったようです。


基本的な解決策

ソースコードは個人的なプロジェクト用ではなかったため、リリースするのは苦手ですが、現在の展開ソリューションの基本構造は次のとおりです。

  1. カスタムEC2 AMIには、起動時に実行される「ブートストラップ」プログラムが含まれています。このプログラムは次のことを行います。
    1.1。 「Zip」アーカイブをダウンロードする (設定可能な)「展開」S3バケットから
    1.2。ダウンロードしたZipファイルを一時ディレクトリに抽出します
    1.3。 「install.bat」スクリプトが配置/実行されます(スクリプトの名前も構成可能です)。このスクリプトは、Windowsサービスをインストールして開始します。
  2. Elastic Beanstalkの「インスタンスAMI」は、ブートストラッププログラムでカスタムAMIに設定されます(参照: この記事

新しいコードをデプロイするには:インストール.Zipアーカイブ(windowsサービスとinstall.batファイルを含む)をS3バケットにアップロードし、Elastic BeanstalkアプリケーションのすべてのEC2インスタンスを終了します。インスタンスが再作成されると、ブートストラッププログラムは新しく更新されたコードをダウンロード/インストールします。

もちろん、最初からやり直す場合は、Elastic Beanstalkの使用をスキップし、同様のデプロイスキームとともに標準のAWS自動スケーリングを使用します。 一番下の行は、Webアプリケーションがない場合はElastic Beanstalkを使用しないことです。標準のAWS自動スケーリングを使用する方が良いでしょう。

新しいAWSデプロイメントツール

Amazonは最近、展開の問題に対処しているように見えるいくつかの新しいコード展開/管理サービスを発表しました。 http://aws.Amazon.com/blogs/aws/code-management-and-deployment/

これらの新しいサービスはまだ使用していません(まだリリースされているかどうかはわかりません)が、有望に見えます。

55
skeryl

この質問はしばらくの間存在し、まだ答えがありませんが、引き続き関心を引くので、非常に似た問題-EC2インスタンスへのWindowsサービスのインストール-のソリューションを共有させてください。ただし、BeanstalkはWebアプリケーションの迅速なデプロイ用に設計されているため、Beanstalkは使用していません。代わりに、BeanstalkがWebアプリケーションに関連するリソースをデプロイするために使用するCloudFormationを直接使用しています。

スタックは、既存のVPC(複数の可用性ゾーンにまたがる)、すべてのサービスビルドアーティファクトを格納するS3バケット、およびEC2キーペアを想定しています。テンプレートは、サービスに必要な追加リソースを作成する方法を示すために、Windows AMIと、アクセスキーと動作するS3バケットを持つIAMユーザーのような他のリソースを使用してEC2インスタンスを作成します。テンプレートは、パラメータとして、ビルドアーティファクトS3バケットにアップロードされたすべてのサービスバイナリと構成ファイルを含む圧縮されたパッケージの名前も使用します(TeamCityビルドサーバーを使用しますが、手動でパッケージを作成およびアップロードできます)コース)。サービスの新しいバージョンをビルドするときは、新しいパッケージ(service.v2.Zipなど)を作成し、新しい名前でスタックを更新するだけで、サービスは自動的に更新されます。テンプレートには4つの異なる地域のAMIのIDが含まれていますが、必要に応じて他の地域をいつでも追加できます。スタックテンプレートは次のとおりです。

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

ご覧のとおり、アーティファクトをコピーした後、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を作成するテンプレートも共有する方が良いことに気付きました。リージョンごとに1つの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" }
        }
    }
}
1
nanestev

私自身の経験から、ebextensionsを使用して何かを実装すると、デプロイメントの経過時間が大幅に長くなります。自動スケーリング時にインスタンスがスピンアップするのに最大15分かかることがあります。目的をほぼ打ち負かします。

いずれにしても、Auto Scaling Groupの「Health Check Grace Period」プロパティを重要なものに設定してください。たとえば、900(つまり15分)を使用します。それ以下の場合、インスタンスはヘルスチェックに合格せず、スケールアップイベントは失敗します。これにより、スケールアップの終わりのない一連の試行が行われます。

1
Dave