{"id":87393,"date":"2020-07-08T01:42:02","date_gmt":"2020-07-07T23:42:02","guid":{"rendered":"https:\/\/prohoster.info\/blog\/administrirovanie\/sozdanie-masshtabiruemogo-api-na-spotovyh-instansah-aws"},"modified":"2020-07-08T01:42:02","modified_gmt":"2020-07-07T23:42:02","slug":"sozdanie-masshtabiruemogo-api-na-spotovyh-instansah-aws","status":"publish","type":"post","link":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sozdanie-masshtabiruemogo-api-na-spotovyh-instansah-aws","title":{"rendered":"Creating a Scalable API on AWS Spot Instances","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p>Hello everyone! My name is Kirill, I am the CTO at Adapty. A large part of our architecture is hosted on AWS, and today I will share how we reduced our server costs by three times by using spot instances in our production environment, as well as how to set up their auto-scaling. First, there will be an overview of how this works, followed by detailed instructions on how to get started.<\/p>\n<p><\/p>\n<h2 id=\"chto-takoe-spotovye-instansy\">What are spot instances?<\/h2>\n<p><\/p>\n<p><noindex><a rel=\"nofollow\" href=\"https:\/\/aws.amazon.com\/ru\/ec2\/spot\/\">Spot<\/a><\/noindex> instances are AWS servers from other users that are currently idle, and they are offered at a significant discount (Amazon states up to 90%, but according to our experience, it averages around 3x, varying by region, AZ, and instance type). The main difference from regular instances is that they can be terminated at any moment. Therefore, we used to think they were fine for development environments or tasks that involve calculations while saving intermediate results to S3 or a database, but not for production. There are third-party solutions that allow using spots in production, but for our case, they had many complications, which is why we didn't implement them. The approach described in this article fully utilizes standard AWS functionality, without additional scripts, cron jobs, etc.<\/p>\n<p><noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/p>\n<p>Next, I will provide several screenshots that show the price history of spot instances.<\/p>\n<p><\/p>\n<p>m5.large in the eu-west-1 region (Ireland). The price has remained mostly stable over the past 3 months, with current savings of 2.9x.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/901a259955c386493557280489f582fe.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>m5.large in the us-east-1 region (N. Virginia). The price constantly fluctuates over the past 3 months, with current savings ranging from 2.3x to 2.8x depending on the availability zone.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/49c89430f03b2c7025355562d2873b6b.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>t3.small in the us-east-1 region (N. Virginia). The price has remained stable over the past 3 months, with current savings of 3.4x.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/2e139dcc6d44bd37caf9d75eb6ec4835.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<h2 id=\"arhitektura-servisa\">Service Architecture<\/h2>\n<p><\/p>\n<p>The basic architecture of the service we will discuss in this article is illustrated in the diagram below.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/d89bd595675400861360670adaacaf03.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<h3 id=\"application-load-balancer--ec2-target-group--elastic-container-service\">Application Load Balancer \u2192 EC2 Target Group \u2192 Elastic Container Service<\/h3>\n<p><\/p>\n<p>The Application Load Balancer (ALB) is used as the load balancer, directing requests to the EC2 Target Group (TG). TG is responsible for opening ports for the ALB on the instances and linking them to the ports of the Elastic Container Service (ECS) containers. ECS is similar to Kubernetes in AWS, managing Docker containers.<\/p>\n<p><\/p>\n<p>Multiple running containers with the same ports can exist on a single instance, so we cannot set them fixed. ECS informs the TG that it is launching a new task (referred to as a pod in Kubernetes terminology), it checks for available ports on the instance, and assigns one of them for the task being launched. Additionally, the TG regularly checks whether the instance and API on it are functioning through health checks, and if any issues are detected, it stops forwarding requests to that instance.<\/p>\n<p><\/p>\n<h3 id=\"ec2-auto-scaling-groups--ecs-capacity-providers\">EC2 Auto Scaling Groups + ECS Capacity Providers<\/h3>\n<p><\/p>\n<p>The EC2 Auto Scaling Groups (ASG) service is not shown in the diagram above. From the name, it is clear that it is responsible for scaling instances. Until recently, AWS did not have a built-in capability to manage the number of running machines from ECS. ECS allowed for scaling the number of tasks based on CPU, RAM utilization, or the number of requests. However, if tasks occupied all available instances, new machines would not be automatically launched.<\/p>\n<p><\/p>\n<p>This changed with the advent of ECS Capacity Providers (ECS CP). Now each service in ECS can be linked to an ASG, and if tasks do not fit on the running instances, new ones will be launched (within the established ASG limits). It also works in reverse; if ECS CP sees idle instances without tasks, it will instruct the ASG to shut them down. ECS CP has the capability to specify a target utilization percentage for instances, ensuring that a certain number of machines are always available for rapid task scaling, which I will discuss further shortly.<\/p>\n<p><\/p>\n<h3 id=\"ec2-launch-templates\">EC2 Launch Templates<\/h3>\n<p><\/p>\n<p>The last service I will describe before moving on to a detailed explanation of creating this infrastructure is the EC2 Launch Templates. It allows you to create a template that will be used to launch all machines so that you do not have to repeat this from scratch every time. Here, you can select the type of machine to be launched, the security group, the disk image, and many other parameters. You can also specify user data that will be uploaded to all launched instances. User data can be used to run scripts, for example, you can modify the contents of a file <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/AmazonECS\/latest\/developerguide\/ecs-agent-config.html\">of the ECS agent configuration<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>One of the most important parameters of the configuration in this article is <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/AmazonECS\/latest\/developerguide\/container-instance-spot.html\">ECS_ENABLE_SPOT_INSTANCE_DRAINING<\/a><\/noindex>=true. If this parameter is enabled, as soon as ECS receives a signal that the spot instance is being reclaimed, it changes the status of all tasks running on it to Draining. No new tasks will be assigned to this instance; if there are tasks currently scheduled to be deployed on it, they will be canceled. Requests from the load balancer will also cease. A notification of instance termination is sent 2 minutes before the actual event. Therefore, if your service does not perform tasks longer than 2 minutes and does not save anything to disk, you can use spot instances without data loss.<\/p>\n<p><\/p>\n<p>Regarding the disk \u2014 AWS recently <noindex><a rel=\"nofollow\" href=\"https:\/\/aws.amazon.com\/about-aws\/whats-new\/2020\/04\/amazon-ecs-aws-fargate-support-amazon-efs-filesystems-generally-available\/#:~:text=To%20use%20EFS%20with%20ECS,or%20TLS%20encryption%20in%20transit.\">made<\/a><\/noindex> using Elastic File System (EFS) with ECS possible; with this setup, even the disk is not a barrier, but we haven't tried it since we generally do not need a disk for state storage. By default, after receiving SIGINT (sent at the moment the task is transitioned to Draining status), all running tasks will be stopped after 30 seconds, even if they have not completed; this time can be changed using the parameter <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/AmazonECS\/latest\/developerguide\/ecs-agent-config.html\">ECS_CONTAINER_STOP_TIMEOUT<\/a><\/noindex>. It is important not to set it to more than 2 minutes for spot machines.<\/p>\n<p><\/p>\n<h2 id=\"sozdanie-servisa\">Creating a service<\/h2>\n<p><\/p>\n<p>Let's move on to creating the service described. In the process, I will outline some additional useful points that were not mentioned earlier. Overall, this is a step-by-step guide, but I will not cover some very basic or very specific cases. All actions are performed in the AWS visual console, but they can also be reproduced programmatically using CloudFormation or Terraform. At Adapty, we use Terraform.<\/p>\n<p><\/p>\n<h3 id=\"ec2-launch-template\"><strong>EC2 Launch Template<\/strong><\/h3>\n<p><\/p>\n<p>In this service, a configuration of machines that will be used is created. Template management occurs in the EC2 -&gt; Instances -&gt; Launch templates section.<\/p>\n<p><\/p>\n<p><strong>Amazon machine image (AMI)<\/strong> \u2014 we specify the disk image that will be used to launch all instances. For ECS, in most cases, it is advisable to use the Amazon optimized image. It is regularly updated and contains everything necessary for ECS to function. To find out the current AMI ID, we visit the <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/AmazonECS\/latest\/developerguide\/ecs-optimized_AMI.html\">Amazon ECS-optimized AMIs<\/a><\/noindex>, select the region being used, and copy the AMI ID for it. For example, for the us-east-1 region, the current ID at the time of writing this article is <em>ami-00c7c1cf5bdc913ed<\/em>. This ID needs to be entered in the Specify a custom value field.<\/p>\n<p><\/p>\n<p><strong>Instance type<\/strong> \u2014 specify the instance type. Choose the one that best fits your task.<\/p>\n<p><\/p>\n<p><strong>Key pair (login)<\/strong> \u2014 specify the certificate through which you can connect to the instance via SSH, if necessary.<\/p>\n<p><\/p>\n<p><strong>Network settings<\/strong> \u2014 specify the network parameters. <strong>Networking platform<\/strong> in most cases should be Virtual Private Cloud (VPC). <strong>Security groups<\/strong> \u2014 security groups for your instances. Since we will be using a load balancer in front of the instances, I recommend specifying a group here that allows incoming connections only from the load balancer. This means you will have 2 security groups: one for the load balancer that allows incoming (inbound) connections from everywhere on ports 80 (http) and 443 (https), and another for the machines that allows incoming connections on any ports from the load balancer group. Outgoing (outbound) connections in both groups need to be opened via TCP protocol to all ports on all addresses. You can restrict the ports and addresses for outgoing connections, but then you need to monitor constantly that you aren\u2019t trying to access anywhere on a closed port.<\/p>\n<p><\/p>\n<p><strong>Storage (volumes)<\/strong> \u2014 specify the disk parameters for the machines. The disk size cannot be less than what is defined in the AMI, for ECS Optimized \u2014 30 GiB.<\/p>\n<p><\/p>\n<p><strong>Advanced details<\/strong> \u2014 specify additional parameters.<\/p>\n<p><\/p>\n<p><strong>Purchasing option<\/strong> \u2014 do we want to purchase spot instances. We do, but here we will not check this box, we will configure it in the Auto Scaling Group, where there are more options.<\/p>\n<p><\/p>\n<p><strong>IAM instance profile<\/strong> \u2014 specify the role under which the instances will be launched. For the instances to work in ECS, they need permissions that are usually in the role <em>ecsInstanceRole<\/em>. In some cases, it can be created; if not, here <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/AmazonECS\/latest\/developerguide\/instance_IAM_role.html\">the instruction<\/a><\/noindex> on how to do that. After creation, we specify it in the template.<br \/>\nNext comes a lot of parameters; in most cases, you can leave the default values, but each of them has a clear description. I always include the EBS-optimized instance and T2\/T3 Unlimited parameters if they use <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/AWSEC2\/latest\/UserGuide\/burstable-performance-instances.html\">burstable<\/a><\/noindex> instances.<\/p>\n<p><\/p>\n<p><strong>User data<\/strong> \u2014 specify user data. We will edit the file <code>\/etc\/ecs\/ecs.config<\/code>, which contains the ECS agent configuration.<br \/>\nAn example of what user data may look like:<\/p>\n<p><\/p>\n<pre><code class=\"bash\">#!\/bin\/bash\necho ECS_CLUSTER=DemoApiClusterProd &gt;&gt; \/etc\/ecs\/ecs.config\necho ECS_ENABLE_SPOT_INSTANCE_DRAINING=true &gt;&gt; \/etc\/ecs\/ecs.config\necho ECS_CONTAINER_STOP_TIMEOUT=1m &gt;&gt; \/etc\/ecs\/ecs.config\necho ECS_ENGINE_AUTH_TYPE=docker &gt;&gt; \/etc\/ecs\/ecs.config\necho &quot;ECS_ENGINE_AUTH_DATA={&quot;registry.gitlab.com&quot;:{&quot;username&quot;:&quot;username&quot;,&quot;password&quot;:&quot;password&quot;}}&quot; &gt;&gt; \/etc\/ecs\/ecs.config<\/code><\/pre>\n<p><\/p>\n<p><code>ECS_CLUSTER=DemoApiClusterProd<\/code> \u2014 this parameter indicates that the instance belongs to a cluster with the specified name, meaning this cluster will be able to place its tasks on this server. We haven't created a cluster yet, but we will use this name when we do.<\/p>\n<p><\/p>\n<p><code>ECS_ENABLE_SPOT_INSTANCE_DRAINING=true<\/code> \u2014 this parameter indicates that upon receiving a shutdown signal for the spot instance, all tasks on it should transition to the Draining status.<\/p>\n<p><\/p>\n<p><code>ECS_CONTAINER_STOP_TIMEOUT=1m<\/code> \u2014 this parameter indicates that after receiving a SIGINT signal, all tasks have 1 minute before they are killed.<\/p>\n<p><\/p>\n<p><code>ECS_ENGINE_AUTH_TYPE=docker<\/code> \u2014 this parameter indicates that the docker schema is used as the authorization mechanism.<\/p>\n<p><\/p>\n<p><code>ECS_ENGINE_AUTH_DATA=...<\/code> \u2014 connection parameters to the private container registry where your Docker images are stored. If it is public, you do not need to specify anything.<\/p>\n<p><\/p>\n<p>In this article, I will use a public image from Docker Hub, so I won't specify parameters. <code>ECS_ENGINE_AUTH_TYPE<\/code> and <code>ECS_ENGINE_AUTH_DATA<\/code> are not needed.<\/p>\n<p><\/p>\n<p><strong>Good to know<\/strong>: it is recommended to regularly update the AMI, because new versions update the versions of Docker, Linux, ECS agent, etc. To remember this, you can <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/AmazonECS\/latest\/developerguide\/ECS-AMI-SubscribeTopic.html\">set up notifications<\/a><\/noindex> for new version releases. You can receive notifications via email and update manually, or you can write a Lambda function that will automatically create a new Launch Template version with the updated AMI.<\/p>\n<p><\/p>\n<h3 id=\"ec2-auto-scaling-group\"><strong>EC2 Auto Scaling Group<\/strong><\/h3>\n<p><\/p>\n<p>The Auto Scaling Group is responsible for launching and scaling instances. Management of the groups is done in the EC2 -&gt; Auto Scaling -&gt; Auto Scaling Groups section.<\/p>\n<p><\/p>\n<p><strong>Launch template<\/strong> \u2014 select the template created in the previous step. Leave the version as default.<\/p>\n<p><\/p>\n<p><strong>Purchase options and instance types<\/strong> \u2014 specify the instance types for the cluster. Adhere to launch template uses the instance type from the Launch Template. Combine purchase options and instance types allows flexible configuration of the instance types. We will use it.<\/p>\n<p><\/p>\n<p><strong>Optional On-Demand base<\/strong> \u2014 the number of regular, non-spot instances that will always be running.<\/p>\n<p><\/p>\n<p><strong>On-Demand percentage above base<\/strong> \u2014 the percentage ratio of regular instances to spot instances; a 50-50 distribution will balance them equally, while a 20-80 will raise 4 spot instances for each regular instance. For this example, I will set 50-50, but in reality, we often do 20-80, and in some cases, 0-100.<\/p>\n<p><\/p>\n<p><strong>Instance types<\/strong> \u2014 here you can specify additional instance types that will be used in the cluster. We have never used this because I don\u2019t quite understand the point of it. It might be about limits on specific instance types, but they can be easily increased through support. If you know of an application, I\u2019d love to read about it in the comments)<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/c1ee742d2059674f39a53cac6b9f2347.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><strong>Network<\/strong> \u2014 network settings, choose VPC and subnets for the machines; in most cases, it's best to select all available subnets.<\/p>\n<p><\/p>\n<p><strong>Load Balancing<\/strong> \u2014 load balancer settings, but we will do this separately; here we leave everything as is. <strong>Health checks<\/strong> will also be configured later.<\/p>\n<p><\/p>\n<p><strong>Group size<\/strong> \u2014 we set limits on the number of machines in the cluster and the desired number of machines at startup. The number of machines in the cluster will never be less than the minimum specified and more than the maximum, even if scaling based on metrics should occur.<\/p>\n<p><\/p>\n<p><strong>Scaling policies<\/strong> \u2014 scaling parameters, but we will scale based on the running ECS tasks, so we will configure scaling later.<\/p>\n<p><\/p>\n<p><strong>Instance scale-in protection<\/strong> \u2014 protection of instances from deletion during scaling down. We enable this so that the ASG does not delete the machine running active tasks. The ECS Capacity Provider will disable protection for instances without tasks.<\/p>\n<p><\/p>\n<p><strong>Add tags<\/strong> \u2014 tags can be specified for instances (for this, the Tag new instances checkbox must be checked). I recommend specifying the Name tag, so all instances launched within the group will have the same name, making them easy to view in the console.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/61fa0ccb38c11f61431db0938140efb7.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>After creating the group, open it and go to the Advanced configurations section, as not all options are visible in the console at the creation stage.<\/p>\n<p><\/p>\n<p><strong>Termination policies<\/strong> \u2014 rules considered when deleting instances. They are applied in order. We usually use rules like those in the picture below. Instances with the oldest Launch Template are deleted first (for example, if we updated the AMI and created a new version, but all instances were able to switch to it). Then, instances closest to the next billing hour are selected. Lastly, the oldest by launch date are chosen.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/8236624fe08a03f36aedc7665e18b060.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><strong>Good to know<\/strong>: to update all machines in the cluster, it's convenient to use <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/autoscaling\/ec2\/userguide\/asg-instance-refresh.html\">Instance Refresh<\/a><\/noindex>If you combine this with the Lambda function from the previous step, you will have a fully automated instance update system. Before updating all the machines, it is necessary to disable instance scale-in protection for all instances in the group. Not the setting in the group, but specifically the protection on the machines themselves, which can be done on the Instance management tab.<\/p>\n<p><\/p>\n<h3 id=\"application-load-balancer-i-ec2-target-group\">Application Load Balancer and EC2 Target Group<\/h3>\n<p><\/p>\n<p>The load balancer is created in the EC2 \u2192 Load Balancing \u2192 Load Balancers section. We will use the Application Load Balancer; a comparison of different types of load balancers can be read on <noindex><a rel=\"nofollow\" href=\"https:\/\/aws.amazon.com\/elasticloadbalancing\/features\/#compare\">the service page<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p><strong>Listeners<\/strong> \u2014 it makes sense to set up ports 80 and 443 and redirect from 80 to 443 later using load balancer rules.<\/p>\n<p><\/p>\n<p><strong>Availability Zones<\/strong> \u2014 in most cases, we choose all availability zones.<\/p>\n<p><\/p>\n<p><strong>Configure Security Settings<\/strong> \u2014 here you specify the SSL certificate for the load balancer, the most convenient option is to <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/acm\/latest\/userguide\/gs-acm-request-public.html\">create a certificate<\/a><\/noindex> in ACM. You can read about the differences in <strong>Security Policy<\/strong> in <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/elasticloadbalancing\/latest\/classic\/elb-security-policy-table.html\">the documentation<\/a><\/noindex>, the default selected can be kept <code>ELBSecurityPolicy-2016-08<\/code>. After creating the load balancer, you will see its <strong>DNS name<\/strong>, to which you need to set up a CNAME for your domain. For example, this is how it looks in Cloudflare.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/899ff0f30df4117fc0ccd91af135def3.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><strong>Security Group<\/strong> \u2014 create or choose a security group for the load balancer, more about this was mentioned earlier in the EC2 Launch Template \u2192 Network settings section.<\/p>\n<p><\/p>\n<p><strong>Target group<\/strong> \u2014 create a group that is responsible for routing requests from the load balancer to the machines and checking their accessibility to replace them in case of problems. <strong>Target type<\/strong> should be Instance, <strong>Protocol<\/strong> and <strong>Port<\/strong> any if you are using HTTPS for communication between the load balancer and the instances; you need to upload the certificate to them. For this example, we won't do this and will simply leave port 80.<\/p>\n<p><\/p>\n<p><strong>Health checks<\/strong> \u2014 health check parameters for the service. In a real service, this should be a separate request that implements important parts of the business logic; for this example, I will leave the default settings. Next, you can choose the request interval, timeout, success response codes, etc. In our example, we will specify Success codes 200-399 because the Docker image that will be used returns a 304 code.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/e295ab1c3bf1dfe13c76b4942a31c7ee.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><strong>Register Targets<\/strong> \u2014 this is where you select machines for the group, but in our case, this will be handled by ECS, so we just skip this step.<\/p>\n<p><\/p>\n<p><strong>Good to know<\/strong>: at the load balancer level, you can enable logs that will be saved in S3 in a specific <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/elasticloadbalancing\/latest\/application\/load-balancer-access-logs.html\">format<\/a><\/noindex>From there, you can export them to third-party analytics services, or you can make SQL queries directly on the data in S3 using <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/athena\/latest\/ug\/application-load-balancer-logs.html\">Athena.<\/a><\/noindex>This is convenient and works without any additional code. I also recommend setting up log deletion from the S3 bucket after a specified period.<\/p>\n<p><\/p>\n<h3 id=\"ecs-task-definition\">ECS Task Definition<\/h3>\n<p><\/p>\n<p>In the previous steps, we created everything related to the service infrastructure, and now we move on to describing the containers that we will be launching. This is done in the ECS \u2192 Task Definitions section.<\/p>\n<p><\/p>\n<p><strong>Launch type compatibility<\/strong> \u2014 we select EC2.<\/p>\n<p><\/p>\n<p><strong>Task execution IAM role<\/strong> \u2014 we select <code>ecsTaskExecutionRole.<\/code>This role writes logs, provides access to secret variables, and more.<\/p>\n<p><\/p>\n<p>In the Container Definitions section, click Add Container.<\/p>\n<p><\/p>\n<p><strong>Image<\/strong> \u2014 the link to the image with the project code; for this example, I will use a public image from Docker Hub <noindex><a rel=\"nofollow\" href=\"https:\/\/hub.docker.com\/r\/bitnami\/node-example\">bitnami\/node-example:0.0.1.<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p><strong>Memory Limits<\/strong> \u2014 memory limits for the container. <strong>Hard Limit<\/strong> \u2014 the hard limit; if the container exceeds the specified value, the command docker kill will be executed, and the container will die immediately. <strong>Soft Limit<\/strong> \u2014 the soft limit; the container may exceed the specified value, but this parameter will be taken into account when placing tasks on machines. For example, if a machine has 4 GiB of RAM and the container's soft limit is 2048 MiB, then at most 2 tasks with this container can be running on that machine. In reality, 4 GiB of RAM is slightly less than 4096 MiB, which can be seen on the ECS Instances tab in the cluster. The soft limit cannot exceed the hard limit. It is important to understand that if there are multiple containers in one task, their limits are summed up.<\/p>\n<p><\/p>\n<p><strong>Port mappings<\/strong> \u2014 in <strong>Host port<\/strong> we specify 0, which means the port will be assigned dynamically, and it will be monitored by the Target Group. <strong>Container Port<\/strong> \u2014 the port on which your application runs, often specified in the execution command or assigned in your application's code, Dockerfile, etc. For our example, we use 3000 because it is specified in the <noindex><a rel=\"nofollow\" href=\"https:\/\/github.com\/bitnami\/bitnami-docker-node\/blob\/master\/example\/Dockerfile\">Dockerfile<\/a><\/noindex> used image.<\/p>\n<p><\/p>\n<p><strong>Health check<\/strong> \u2014 parameters for checking the container's health; do not confuse it with the one configured in the Target Group.<\/p>\n<p><\/p>\n<p><strong>Environment<\/strong> \u2014 environment settings. <strong>CPU units<\/strong> \u2014 similar to Memory limits, but for the processor. Each CPU core represents 1024 units, so if the server has a dual-core processor and the container is set to 512, then up to 4 tasks can run on that server with this container. CPU units always correspond to the number of cores; there cannot be slightly fewer, as in the case with memory.<\/p>\n<p><\/p>\n<p><strong>Command<\/strong> \u2014 command to start the service inside the container, all parameters are specified through a comma. This can be gunicorn, npm, etc. If not specified, the value of the CMD directive from the Dockerfile will be used. We specify <code>npm,start<\/code>.<\/p>\n<p><\/p>\n<p><strong>Environment variables<\/strong> \u2014 the environment variables of the container. These can be simple text data or secret variables from <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/AmazonECS\/latest\/developerguide\/specifying-sensitive-data-secrets.html\">Secrets Manager<\/a><\/noindex> or <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/AmazonECS\/latest\/developerguide\/specifying-sensitive-data-parameters.html\">Parameter Store<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p><strong>Storage and Logging<\/strong> \u2014 here we will set up logging to CloudWatch Logs (the logging service from AWS). To do this, simply check the Auto-configure CloudWatch Logs box. After creating the Task Definition, a log group in CloudWatch will automatically be created. By default, logs are stored there indefinitely, I recommend changing the Retention period from Never Expire to the required duration. This is done in CloudWatch Log groups, where you need to click on the current period and select a new one.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/c9ad7ca39dfde9343be89eb9ab1aef10.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<h3 id=\"ecs-cluster-i-ecs-capacity-provider\">ECS Cluster and ECS Capacity Provider<\/h3>\n<p><\/p>\n<p>Go to the ECS \u2192 Clusters section to create a cluster. As a template, select EC2 Linux + Networking.<\/p>\n<p><\/p>\n<p><strong>Cluster name<\/strong> \u2014 very important, make sure to give it the same name as specified in the Launch Template under the <code>ECS_CLUSTER<\/code>, in our case \u2014 <code>DemoApiClusterProd<\/code>. Check the Create an empty cluster box. Optionally, you can enable Container Insights to view metrics for services in CloudWatch. If you've done everything correctly, you should see the machines created in the Auto Scaling group in the ECS Instances section.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/cd70062c9783ae359a2b27cbfc66cbe3.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p>Go to the <strong>Capacity Providers<\/strong> tab and create a new one. Let me remind you that it is needed to control the creation and shutdown of machines depending on the number of running ECS tasks. It is important to note that the provider can only be linked to one group.<\/p>\n<p><\/p>\n<p><strong>Auto Scaling group<\/strong> \u2014 select the previously created group.<\/p>\n<p><\/p>\n<p><strong>Managed scaling<\/strong> \u2014 enable it so the provider can scale the service.<\/p>\n<p><\/p>\n<p><strong>Target capacity %<\/strong> \u2014 what percentage of machine load tasks we need. If you set 100%, all machines will always be occupied with running tasks. If you set 50%, half of the machines will always be free. In this case, if there is a sudden surge in load, new tasks will immediately go to the free machines without having to wait for instance deployment.<\/p>\n<p><\/p>\n<p><strong>Managed termination protection<\/strong> \u2014 if enabled, this parameter allows the provider to remove instance deletion protection. This happens when there are no active tasks on the machine and allows for Target capacity %.<\/p>\n<p><\/p>\n<h3 id=\"ecs-service-i-nastroyka-masshtabirovaniya\">ECS Service and scaling configuration<\/h3>\n<p><\/p>\n<p>Final step :) To create a service, you need to go to the previously created cluster and the Services tab.<\/p>\n<p><\/p>\n<p><strong>Launch type<\/strong> \u2014 you need to click on Switch to capacity provider strategy and select the previously created provider.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/9d9dfdd8e333cce24bfb048ac84097e4.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><strong>Task Definition<\/strong> \u2014 select the previously created Task Definition and its revision.<\/p>\n<p><\/p>\n<p><strong>Service name<\/strong> \u2014 to avoid confusion, we always specify the same name as the Task Definition.<\/p>\n<p><\/p>\n<p><strong>Service type<\/strong> \u2014 always Replica.<\/p>\n<p><\/p>\n<p><strong>Number of tasks<\/strong> \u2014 the desired number of active tasks in the service. This parameter is controlled by scaling, but it still needs to be specified.<\/p>\n<p><\/p>\n<p><strong>Minimum healthy percent<\/strong> and <strong>Maximum percent<\/strong> \u2014 determine the behavior of tasks during deployment. The default values are 100 and 200, indicating that during deployment the number of tasks will double and then return to the desired state. If you have 1 task, min=0, and max=100, then during deployment it will be killed, and after that a new one will be launched, which means there will be downtime. If you have 1 task, min=50, max=150, then deployment won't happen at all because 1 task cannot be split in half or increased by one and a half times.<\/p>\n<p><\/p>\n<p><strong>Deployment type<\/strong> \u2014 leave as Rolling update.<\/p>\n<p><\/p>\n<p><strong>Placement Templates<\/strong> \u2014 placement rules for tasks on machines. By default, it is set to AZ Balanced Spread \u2014 which means that each new task will be placed on a new instance until machines are up in all availability zones. We usually do BinPack \u2014 CPU and Spread \u2014 AZ, under this policy tasks are placed as densely as possible on one machine by CPU. If a new machine needs to be created, it is created in a new availability zone.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/6430bdf046fc5b970e0afbbf0d9ef65e.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><strong>Load balancer type<\/strong> \u2014 choose Application Load Balancer.<\/p>\n<p><\/p>\n<p><strong>Service IAM role<\/strong> \u2014 choose <code>ecsServiceRole<\/code>.<\/p>\n<p><\/p>\n<p><strong>Load balancer name<\/strong> \u2014 select the previously created load balancer.<\/p>\n<p><\/p>\n<p><strong>Health check grace period<\/strong> \u2014 pause before executing health checks after rolling out a new task, we usually set it to 60 seconds.<\/p>\n<p><\/p>\n<p><strong>Container to load balance<\/strong> In the Target group name field, select the previously created group, and everything will be filled in automatically.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/c067868ff73c25c3d9b65e02be64f5c9.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<p><strong>Service Auto Scaling<\/strong> Scaling parameters for the service. Select Configure Service Auto Scaling to adjust your service's desired count. Set the minimum and maximum task count for scaling.<\/p>\n<p><\/p>\n<p><strong>IAM role for Service Auto Scaling<\/strong> \u2014 choose <code>AWSServiceRoleForApplicationAutoScaling_ECSService<\/code>.<\/p>\n<p><\/p>\n<p><strong>Automatic task scaling policies<\/strong> There are two types:<\/p>\n<p><\/p>\n<ol>\n<li><strong>Target tracking<\/strong> Tracking a target metric (CPU\/RAM usage or the number of requests per task). For example, we want the average CPU load to be 85%. If it goes above this, new tasks will be added until it reaches the target value. If the load is below, tasks will be removed unless scale-in protection is enabled (<strong>Disable scale-in<\/strong>).<\/li>\n<li><strong>Step scaling<\/strong> A response to arbitrary events. Here you can configure a response to any event (CloudWatch Alarm); when it occurs, you can add or remove the specified number of tasks, or specify an exact number of tasks.<\/li>\n<\/ol>\n<p><\/p>\n<p>The service can have multiple scaling rules, which can be useful; just ensure they do not conflict with each other.<\/p>\n<p><\/p>\n<h2 id=\"zaklyuchenie\">Conclusion<\/h2>\n<p><\/p>\n<p>If you followed the instructions and used the same Docker image, your service should return this page.<\/p>\n<p><\/p>\n<p><img decoding=\"async\" alt=\"Creating a Scalable API on AWS Spot Instances\" src=\"\/wp-content\/uploads\/2020\/07\/ac592e3194480ac8b347a3a1faf0cee0.png\" style=\"display:block;margin: 0 auto;\" \/><\/p>\n<p><\/p>\n<ol>\n<li>We created a template for launching all machines in the service. We also learned how to update machines when the template changes.<\/li>\n<li>We set up handling for the spot instance termination signal, so within a minute after receiving it, all running tasks are removed from the machine, thus nothing is lost or interrupted.<\/li>\n<li>We set up a load balancer to distribute the load evenly across machines.<\/li>\n<li>We created a service that runs on spot instances, which reduces machine costs by about three times.<\/li>\n<li>We configured auto-scaling in both directions to handle increased loads while also avoiding payments for idle time.<\/li>\n<li>We use Capacity Provider to allow the application to manage the infrastructure (machines), rather than the other way around.<\/li>\n<li>We did great.<\/li>\n<\/ol>\n<p><\/p>\n<p>If you have predictable spikes in load, for example, if you are running a large email campaign, you can set up scaling based on <noindex><a rel=\"nofollow\" href=\"https:\/\/docs.aws.amazon.com\/autoscaling\/application\/userguide\/application-auto-scaling-scheduled-scaling.html\">a schedule.<\/a><\/noindex>.<\/p>\n<p><\/p>\n<p>You can also scale based on data from different parts of your system. For example, we have the functionality <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.adapty.io\/promo-campaigns\">of sending personalized promotional offers<\/a><\/noindex> to users of the mobile application. Sometimes a campaign is sent to over 1 million people. After such a mailing, there is always a significant increase in API requests, as many users access the application simultaneously. So if we notice that the queue for sending promotional push notifications has significantly exceeded standard parameters, we can immediately launch several additional machines and tasks to be ready for the load.<\/p>\n<p><\/p>\n<p>I would be glad if you could share interesting use cases of spot instances and ECS or anything related to scaling in the comments.<\/p>\n<p><\/p>\n<p>Soon there will be articles on how we process thousands of analytical events per second on a predominantly serverless stack (with money) and how we deploy services using GitLab CI and Terraform Cloud.<\/p>\n<p><\/p>\n<p>Follow us, it will be interesting!<\/p>\n<p class=\"for_users_only_msg\">Only registered users can participate in the survey. <noindex><a rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/auth\/login\/\">Please log in<\/a><\/noindex>, please.<\/p>\n<h2 class=\"default-block__polling-title\">Do you use spot instances in production?<\/h2>\n<ul class=\"poll-result\">\n<li class=\"poll-result__item\">\n<p>                <strong class=\"poll-result__data-percent\">22,2%<\/strong>Yes6<\/p>\n<\/li>\n<li class=\"poll-result__item\">\n<p>                <strong class=\"poll-result__data-percent  poll-result__data-percent_winner\">66,7%<\/strong>No18<\/p>\n<\/li>\n<li class=\"poll-result__item\">\n<p>                <strong class=\"poll-result__data-percent\">11,1%<\/strong>I learned about them from an article, I plan to use them3<\/p>\n<\/li>\n<\/ul>\n<p>    27 users voted. 5 users abstained.<br \/>\n<br \/>Source: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/509790\/\">habr.com<\/a> <\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u041a\u0438\u0440\u0438\u043b\u043b, \u044f CTO \u0432 Adapty. \u0411\u043e\u043b\u044c\u0448\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043d\u0430\u0448\u0435\u0439 \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u044b \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u043d\u0430 AWS, \u0438 \u0441\u0435\u0433\u043e\u0434\u043d\u044f \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043c\u044b \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u043b\u0438 \u0440\u0430\u0441\u0445\u043e\u0434\u044b \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0430 \u0432 3 \u0440\u0430\u0437\u0430 \u0437\u0430 \u0441\u0447\u0451\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0441\u043f\u043e\u0442\u043e\u0432\u044b\u0445 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432 \u043d\u0430 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u043d \u043e\u043a\u0440\u0443\u0436\u0435\u043d\u0438\u0438, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u0445 \u0430\u0432\u0442\u043e\u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435. \u0421\u043d\u0430\u0447\u0430\u043b\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0437\u043e\u0440 \u0442\u043e\u0433\u043e, \u043a\u0430\u043a \u044d\u0442\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442, \u0430 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":87394,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-87393","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.2 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u041a\u0438\u0440\u0438\u043b\u043b, \u044f CTO \u0432 Adapty.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sozdanie-masshtabiruemogo-api-na-spotovyh-instansah-aws\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.2\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e API \u043d\u0430 \u0441\u043f\u043e\u0442\u043e\u0432\u044b\u0445 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430\u0445 AWS | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u041a\u0438\u0440\u0438\u043b\u043b, \u044f CTO \u0432 Adapty.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sozdanie-masshtabiruemogo-api-na-spotovyh-instansah-aws\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2020-07-07T23:42:02+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-07-07T23:42:02+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Creating a Scalable API on AWS Spot Instances | ProHoster","description":"Hello everyone! My name is Kirill, I am the CTO at Adapty.","canonical_url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sozdanie-masshtabiruemogo-api-na-spotovyh-instansah-aws","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"en_US","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043c\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e API \u043d\u0430 \u0441\u043f\u043e\u0442\u043e\u0432\u044b\u0445 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430\u0445 AWS | ProHoster","og:description":"\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u041a\u0438\u0440\u0438\u043b\u043b, \u044f CTO \u0432 Adapty.","og:url":"https:\/\/prohoster.info\/en\/blog\/administrirovanie\/sozdanie-masshtabiruemogo-api-na-spotovyh-instansah-aws","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2020-07-07T23:42:02+00:00","article:modified_time":"2020-07-07T23:42:02+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"87393","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":null,"breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 12:44:41","updated":"2022-09-28 14:12:20","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/87393","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/comments?post=87393"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/posts\/87393\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media\/87394"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/media?parent=87393"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/categories?post=87393"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/en\/wp-json\/wp\/v2\/tags?post=87393"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}