<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Vishesh Jindal on Medium]]></title>
        <description><![CDATA[Stories by Vishesh Jindal on Medium]]></description>
        <link>https://medium.com/@vishesh92?source=rss-5d3d78a9658b------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*BEwB3_6Zwo9zS2h_mfES9A.jpeg</url>
            <title>Stories by Vishesh Jindal on Medium</title>
            <link>https://medium.com/@vishesh92?source=rss-5d3d78a9658b------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 15:54:41 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@vishesh92/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Reducing data transfer costs with a Docker registry cache]]></title>
            <link>https://lambda.blinkit.com/reducing-data-transfer-costs-with-a-docker-registry-based-cache-8f93d7e561f3?source=rss-5d3d78a9658b------2</link>
            <guid isPermaLink="false">https://medium.com/p/8f93d7e561f3</guid>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[containers]]></category>
            <category><![CDATA[docker]]></category>
            <category><![CDATA[kubernetes]]></category>
            <dc:creator><![CDATA[Vishesh Jindal]]></dc:creator>
            <pubDate>Tue, 08 Jun 2021 18:10:53 GMT</pubDate>
            <atom:updated>2021-06-09T04:31:35.034Z</atom:updated>
            <content:encoded><![CDATA[<p>Reducing costs has been a focus area for us ever since the COVID pandemic started. We recently published a <a href="https://lambda.grofers.com/response-to-covid-19-lessons-learned-in-aws-cost-optimization-b466f354b123">blog post</a> on this topic. One of the prominent contributors to our AWS costs was the data transfer of our internal Docker registry. We use the Oregon region for our development environment &amp; Singapore region for the production environment because of the cost difference between different AWS regions. This multi-region setup led to high costs of data transfer. In this post, we will talk about how we managed to reduce this cost.</p><h3>Our setup</h3><p>We run the <a href="https://docs.docker.com/registry/">Docker registry</a> on EC2 instances with AWS S3 as a storage backend for images and AWS CloudFront as CDN for caching images. Docker registry container behind an Nginx proxy for authentication. We use the way recommended in the <a href="https://docs.docker.com/registry/recipes/nginx/">documentation</a>.</p><h3>Removing CloudFront middleware</h3><p>To save data transfer costs from outside the VPC, we disabled CloudFront middleware from the configuration. Removing CloudFront from our configuration impacted our Docker pull times in the Oregon region, but there was no impact in pull times for the Singapore region since the S3 bucket storing all the Docker images is in the Singapore region as well.</p><p>We were expecting the data transfer cost to go down but it didn’t create any significant impact since the data transfer happening between VPC &amp; S3 was still over the internet. We decided to set up an S3 VPC endpoint for our VPCs to make data transfer between our VPC &amp; S3 private. After this change, our data transfer costs for the Singapore region reduced quite a bit. This was our first win!</p><p>We were still getting charged for data transfer between S3 &amp; Oregon region because the S3 bucket storing Docker images bucket was in the Singapore region and VPC Endpoints for services work for VPCs and AWS services within the same region.</p><p><strong><em>Pro-tip:</em></strong> If you want to use CloudFront in one region but want to use s3 for some regions, you can use <em>ipfilteredby</em> &amp; <em>awsregion</em> to bypass CloudFront and pull from s3 directly. You can check the documentation <a href="https://docs.docker.com/registry/configuration/#cloudfront">here</a> for more information.</p><h3>Setting up a Docker registry as a pull-through cache</h3><p>After going through the Docker registry’s documentation, we came across <a href="https://docs.docker.com/registry/recipes/mirror/">how to setup Docker registry as a pull-through cache</a>. This was a simple configuration change. If the image earlier was at <em>my-docker-registry.com/my-image</em>, I can also pull the same image through <em>my-docker-registry-cache.com/my-image</em>.</p><p>Docker introduced <a href="https://www.docker.com/blog/what-you-need-to-know-about-upcoming-docker-hub-rate-limiting/">rate-limiting on Docker</a> pulls from their registry in November 2020. We haven’t tried this out, but a private Docker registry as a pull-through cache can be used to avoid getting rate limited as well as improve image pull times. You need not be concerned about pulling stale images since it checks for stale images by making a HEAD request upstream. And <a href="https://docs.docker.com/docker-hub/download-rate-limit/">HEAD requests are not counted by Docker</a> for rate limiting. You need to set <a href="https://registry-1.docker.io/">https://registry-1.docker.io</a> as <em>remoteurl</em> in the configuration for this.</p><p>We used a configuration like <a href="https://gist.github.com/vishesh92/b28afe0904333a5187a1edf923ec72ef.js">this</a>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ebfd7a5b6a2d7dc9b8694959f1539d18/href">https://medium.com/media/ebfd7a5b6a2d7dc9b8694959f1539d18/href</a></iframe><p>Once we had set up the pull-through cache, the next challenge that we faced was how do we change it for applications. Making changes to every codebase and handling a different image path for production and development environments was difficult. We explored two approaches:</p><ol><li>Geolocation based DNS records</li><li>Mutating webhook in Kubernetes</li></ol><h3>Geolocation based DNS records</h3><p>How this approach works is that it will return different responses depending on your location. This approach seemed great but had two issues:</p><ol><li>Not every DNS provider supports geolocation based DNS records (e.g. — AWS Route53 supports this but not Cloudflare which is our primary DNS provider)</li><li>We could pull images from the Oregon region, but it failed when we tried to push images.</li></ol><p>We had some workflows which were building images in the Oregon region and pushing them to our private registry. So we couldn’t go ahead with this approach since it would break existing workflows.</p><h3>Mutating webhook in Kubernetes</h3><p>The majority of the pulls in the Oregon region for us were from our Kubernetes cluster. So, we deployed a <a href="https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#mutatingadmissionwebhook">mutating admission webhook</a>, which replaces my-docker-registry.com with my-docker-registry-cache.com in Docker image URLs.</p><h3>Conclusion</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5e2cq0xhK0V65scy7u8sIA.png" /></figure><p>We deployed this change around May 20. The previous drop was because of image caching on Kubernetes nodes. This setup improved our Docker image pull time and reduced our data transfer costs from our Docker setup by more than 50%. We explored migrating to AWS ECR, but it didn’t support cross-region replication last year when we did this. Since <a href="https://aws.amazon.com/blogs/containers/cross-region-replication-in-amazon-ecr-has-landed/">AWS ECR supports cross-region replication</a> now, it might be a good idea to explore that as well now.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8f93d7e561f3" width="1" height="1" alt=""><hr><p><a href="https://lambda.blinkit.com/reducing-data-transfer-costs-with-a-docker-registry-based-cache-8f93d7e561f3">Reducing data transfer costs with a Docker registry cache</a> was originally published in <a href="https://lambda.blinkit.com/">Lambda by Blinkit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How COVID-19 pushed us to reduce our AWS cost by half]]></title>
            <link>https://lambda.blinkit.com/response-to-covid-19-lessons-learned-in-aws-cost-optimization-b466f354b123?source=rss-5d3d78a9658b------2</link>
            <guid isPermaLink="false">https://medium.com/p/b466f354b123</guid>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[aws-cost-reduction]]></category>
            <category><![CDATA[aws-cost-optimization]]></category>
            <category><![CDATA[infrastructure]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Vishesh Jindal]]></dc:creator>
            <pubDate>Wed, 18 Nov 2020 11:46:25 GMT</pubDate>
            <atom:updated>2020-11-27T05:41:01.987Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/982/0*G-K_FcNMPvIoRW5T" /><figcaption>Reduction by approximately 40%</figcaption></figure><p>We have been extremely focused on growth before COVID started. AWS is central to how we ship fast that helps fuel that growth. Some of our early mistakes resulted in wastage resulting in high costs over time. COVID-19 pushed us to bring more efficiency in our business operations to provide more savings to our customers during these tough times. As a part of that initiative, we started looking at ways to reduce our infrastructure cost.</p><p>In this post, we will try to share tools, our key learnings and strategies to attack the problem of high infrastructure costs.</p><h3>Identifying Wastage and Optimization Opportunities</h3><p>To reduce costs, we needed to look at our biggest contributors to cost. <a href="https://aws.amazon.com/aws-cost-management/aws-cost-explorer/#"><strong>AWS Cost Explorer</strong></a> is a good place to start for this. If you have tagged your resources properly (we tag by service, team, division, etc. There is a nice guideline for this by AWS <a href="https://docs.aws.amazon.com/general/latest/gr/aws_tagging.html">here</a>), Cost Explorer can show you how much a team, application or microservice costs.</p><p>Service-wise and application-wise cost attribution and reports is a good place to start. But as you start to identify the reasons for costs under different heads, you will need to get a more granular view of why a particular service or application is costing you so much. To get data at a granular level such as operation or API level, <a href="https://docs.aws.amazon.com/cur/latest/userguide/what-is-cur.html"><strong>Cost &amp; Usage Reports (CUR)</strong></a> are helpful. To <a href="https://docs.aws.amazon.com/cur/latest/userguide/what-is-cur.html#download-cur">setup CUR Reports</a>, you will have to set up a pipeline to make the CUR data accessible for queries and then query the reports using <a href="https://docs.aws.amazon.com/cur/latest/userguide/cur-ate-run.html"><strong>Athena</strong></a>.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/4713d91a17eed64f6beb62cd7dd28ef9/href">https://medium.com/media/4713d91a17eed64f6beb62cd7dd28ef9/href</a></iframe><h3>Major Contributors to Cost</h3><p>Depending on your business, you will have different things that will be your major cost contributors. However, there are some AWS services that are common amongst most businesses — EC2, RDS, S3, and Data Transfer. This applies to us as well.</p><p>Major contributors to cost for us are:</p><ul><li>Data Transfer</li><li>EC2</li><li>S3</li><li>Reserved Instances</li></ul><p>Let’s look at each of these in detail.</p><h3>Data Transfer Costs</h3><p>Data transfer costs are the ones that get ignored most easily because it is hard to account for data transfer over the network as compared to compute usage. Not to forget that when you are building up your business, the network usage is very hard to estimate. The cost of network early on is usually so small that it doesn’t warrant so much attention. However, some higher level decisions such as avoiding usage of public network routes for accessing internal services or reducing data transfer due to infrastructural components (such as monitoring) can go a long way in saving money. That is as much as you can do. As you scale up, the cost of the network starts to show up.</p><p>Data transfer costs show up in <a href="https://aws.amazon.com/blogs/aws-cost-management/tips-and-tricks-for-exploring-your-data-in-aws-cost-explorer-part-2/#:~:text=The%20EC2%2DOther%20category%20includes,%2C%20data%20transfer%2C%20and%20more."><strong>EC2-Other</strong> service in AWS Cost Explorer</a>. AWS charges you for different kinds of data transfer:</p><ol><li><strong>Data transfer from outside the VPC</strong> — listed as NAT Gateway, PublicIP-In, PublicIP-Out, VPCPeering-In, VPCPeering-Out in AWS Cost Explorer</li><li><strong>Cross-AZ data transfer</strong> — Listed as InterZone-In, InterZone-Out in AWS Cost Explorer</li></ol><h4><strong>Data transfer from outside the VPC (Internet)</strong></h4><p>Our major costs here were <a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-nat-gateway.html">NAT Gateway</a> costs. NAT Gateway is used to enable instances in a private subnet to connect to the internet. AWS Cost Explorer doesn’t provide much information about what data transfer you are being charged for. This <a href="https://aws.amazon.com/premiumsupport/knowledge-center/vpc-reduce-nat-gateway-transfer-costs/">documentation</a> by AWS recommends these solutions to reduce data transfer costs:</p><ol><li><strong>Setup NAT Gateway</strong> per AZ to reduce cross-AZ (or inter-AZ) data transfer.</li><li>If you are using <strong>DynamoDB and S3</strong> in the same region, setup <a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpce-gateway.html"><strong>gateway VPC endpoint</strong></a>.</li><li>If the majority of the traffic is to AWS services supporting VPC endpoints, create one <a href="https://aws.amazon.com/privatelink/pricing/"><strong>interface VPC endpoint</strong></a>.</li><li>If the majority of traffic needs to go to the internet, set up an <a href="https://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Internet_Gateway.html"><strong>internet gateway</strong></a>.</li></ol><blockquote><strong>Pro-tip</strong>: You can also set up a public instance and use that instead of a NAT gateway if you don’t want to pay for NAT Gateway running hours. This can be risky if the instance goes down, needs maintenance or your network bandwidth requirements cannot be satisfied by the network bandwidth cap of EC2, so you can probably try this in your non-prod environments. We didn’t try this but learned from people we know that some companies do this as well.</blockquote><p>We already had a NAT Gateway per AZ so we don’t get charged for cross-AZ data transfer.</p><p><a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpce-gateway.html">VPC endpoint</a> is a hidden gem. Data transfer cost due to S3 is often a big contributor. As soon as you roll out the VPC endpoint, you will see a sudden drop in data transfer cost. Before we set up a VPC endpoint, we needed to know where we were making requests to get a better understanding of our costs and usage. If S3 or other AWS services show up in your analysis, you should look at deploying VPC endpoints immediately.</p><blockquote><a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpce-gateway.html">VPC endpoint</a> is a hidden gem. Data transfer cost due to S3 is often a big contributor. As soon as you roll out the VPC endpoint, you will see a sudden drop in data transfer cost.</blockquote><p>Reducing NAT Gateway charges further would require building a better understanding of the reasons for high NAT Gateway charges. To further drill down into where all we were being charged for NAT Gateway data transfer, we used <a href="https://docs.aws.amazon.com/vpc/latest/userguide/flow-logs.html"><strong>VPC Flow Logs</strong></a> on a log group and <a href="https://meet.google.com/linkredirect?authuser=1&amp;dest=https%3A%2F%2Fdocs.aws.amazon.com%2FAmazonCloudWatch%2Flatest%2Flogs%2FAnalyzingLogData.html"><strong>AWS Insights</strong></a> to find out our biggest contributors. You can find some sample queries <a href="https://www.cloudzero.com/blog/natgateway">here</a> and <a href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_QuerySyntax-examples.html">here</a>. You can also dump VPC Flow Logs to an S3 bucket and later query them using AWS Athena to do custom analysis if your infrastructure and business setup requires you to find deeper answers about your network usage. For details on how to set that up, follow this <a href="https://aws.amazon.com/blogs/mt/analyzing-vpc-flow-logs-got-easier-with-support-for-s3-as-a-destination/">post</a> by AWS. If you need to identify AWS service from IPs, you can check AWS IP Ranges <a href="https://docs.aws.amazon.com/general/latest/gr/aws-ip-ranges.html">here</a>.</p><p>After analyzing our VPC Flow Logs, we found out that most of our NAT Gateway cost is from:</p><ol><li>Our applications sending requests to other applications via public load balancers</li><li>Cost of of data transfer between our own EC2 and S3</li></ol><p><strong><em>Application sending requests to our own public load balancer</em></strong></p><p>Identifying which applications are making requests to public load balancers can get a little tricky because access logs of your receiving load balancer will show your NAT gateway’s public IP. We had to perform the painful process of talking to all service owners who had public load balancers and set up an additional private load balancer, and then have all the applications migrate to using the private load balancer. In some cases, we had to set up two load balancers — one for public travel and another for internal traffic, because our NAT Gateway data transfer costs were higher than the cost of an additional load balancer.</p><p><strong><em>Cost of of data transfer between our own EC2 and S3</em></strong></p><p>We created a <a href="https://aws.amazon.com/blogs/aws/new-vpc-endpoint-for-amazon-s3/">gateway VPC endpoint</a> to make traffic to S3 private. After making this change, our S3 data transfer costs went significantly down, but the NAT Gateway costs due to S3 were still high. This was because we were transferring data from buckets in a different region. We had to migrate some buckets to the same region from where these buckets were being mostly accessed.</p><p>One of the big S3 data transfer expenses was caused by our internal Docker registry. After all these changes, we couldn’t reduce the cost of data transfer because our stage and CI environment is in a different region. Private endpoint was effective only for the environment where the registry’s region was the same as the environment’s region (production in our case). So we set up a docker registry with a proxy in the same region as our stage to cache docker images. We will be publishing details about how we achieved this in a separate blog post soon.</p><h4><strong>Cross-AZ Data Transfer</strong></h4><p>We had significant Cross-AZ data transfer in our production as well as stage environments. There are a few easy things you can do here — like enabling compression, reducing unnecessary data transfer like debug logs, etc. If you have done all the configuration and tuning, the next thing to look at is trying to reduce the data transfer footprint of your services. This is not easy to solve so we decided to take it up as a long term improvement. Depending on your risk appetite, you can also look at running your entire infrastructure out of a single AZ. We migrated our stage infrastructure and migrated large portions of production infrastructure to a single AZ bringing down the inter AZ cost to as less as we could. This was an interesting migration. You can read more about the migration <a href="https://lambda.grofers.com/reducing-aws-data-transfer-cost-kubernetes-from-multi-az-to-single-az-341d890553b6">here</a>.</p><h3>EC2 Costs</h3><p>Decreasing EC2 costs depends a lot on the infrastructure you are running. There were multiple steps we took to reduce our EC2 costs.</p><h4><strong>Resource Optimization</strong></h4><p>The first step is to see which EC2 instances are costing you the most and then work your way from there. Once again our <a href="https://d1.awsstatic.com/whitepapers/aws-tagging-best-practices.pdf">tagging strategy</a> helped us a lot in determining this using AWS Cost Explorer. We made a list where the instance type could be changed or if an auto-scaling group’s minimum size could be reduced. For Kubernetes, we needed something similar to Cost Explorer. After some exploration, we came across <a href="https://codeberg.org/hjacobs/kube-resource-report">kube-resource-reports</a>. This provides the current view of costs and usage by applications, namespaces and pods. We used this to identify resources that were requesting lots of resources but not utilizing them so that we could right-sized them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*BzEKbGF2ih9dhhUB" /><figcaption>Image credits: <a href="https://codeberg.org/hjacobs/kube-resource-report">demo instance of kube-resource-reports</a></figcaption></figure><h4><strong>Utilization of Reserved Instances (RIs)</strong></h4><p>We had bought RIs a couple of months before COVID-19 hit us. After changing instance types and reducing auto-scaling groups’ min size, our RI Utilization went down. This was pure wastage. But fortunately, we bought Convertible RIs so we could convert them to other instance types to make sure that our RI Utilization is 100%. If you are looking to buy EC2 RIs, we would suggest looking at <a href="https://aws.amazon.com/savingsplans/">Savings Plan</a> launched last year which is an offering similar to RIs but you don’t have to worry about converting RIs or purchasing them separately for every regions. One <strong>important caveat with Savings Plan</strong> is that it’s not exactly capacity reservation. It is purely a discounting construct.</p><h4><strong>Spot Instances</strong></h4><p>Spot instances are a great way to save costs. They can be as cheap as 30% of the on-demand cost. Majority of our infrastructure was running on on-demand instances. We had explored spot instances earlier but we had some concerns:</p><p><strong>1. If the spot instance gets terminated due to price issues, how do we ensure application remains available?</strong></p><p>Spot instances are quite easy to manage with AWS’s autoscaling groups if you are using a launch template. In a launch template, you can specify different <a href="https://docs.aws.amazon.com/autoscaling/ec2/userguide/asg-purchase-options.html#asg-allocation-strategies">allocation strategies</a> to ensure high availability of your application. These strategies would ensure that if an instance type is not available, another instance type depending on your allocation strategy would be launched.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/779/0*nQ8pu8-D_SzTuZQt" /><figcaption>Configuration of instance allocation strategy in auto-scaling groups</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/798/0*sxLxW_tvAnAbOMko" /><figcaption>Configuration of instance type mix in auto-scaling groups</figcaption></figure><p><strong>2. Graceful shutdown of an application when the instance is about to terminate so that error rate doesn’t increase.</strong></p><p>You can get to know if a spot instance is about to terminate before 2 minutes by making a simple curl request periodically (read more <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-interruptions.html#spot-instance-termination-notices">here</a>). This was a little tough to solve for existing applications running on EC2 instances because that would have required changes in all of our ASGs. Another option was to use a managed service like <a href="https://spot.io/">SpotInst</a> to do the work for you.</p><p>For Kubernetes (which is most of our production), it turns out that it’s a lot easier to do spot instances than EC2. AWS and the DevOps community has built awesome tools that make it super easy. You can use <a href="https://github.com/aws/aws-node-termination-handler">aws-node-termination-handler</a> or <a href="https://github.com/kube-aws/kube-spot-termination-notice-handler">kube-spot-termination-notice-handler</a>.</p><p>It is a good idea to set up <a href="https://kubernetes.io/docs/concepts/workloads/pods/disruptions/#how-disruption-budgets-work"><strong>Pod Disruption Budgets</strong></a> for your deployments to ensure that there is least possible disruption. Today, we just change the percentage of spot instances in our node group’s ASG as and when we need to tune our spot spread.</p><blockquote>This approach has helped us run all of our stage Kubernetes workloads and a significant portion of our production Kubernetes workloads on spot instances. In production, 99% of our workload is covered under spot, reserved instances and savings plan. Only 1% workload runs on-demand, mostly because of the difficulty of managing spot coverage to avoid RI or savings plan wastage because of our varying workloads.</blockquote><h4><strong>Nightly shutdown of stage infrastructure</strong></h4><p>All of our developers are in the same time zone, so our stage environment is not in use at night. We were already stopping EC2 instances in the stage environment at night. On Kubernetes, we have an internal tool that helps every developer create their own namespace and set up a personal dev environment. We set replicas for all deployments and stateful sets to 0 at night. And if there hasn’t been any change in the namespace for 5 days, we delete them. This saves at least 35% of our stage costs. Also, we get a nice side effect of ensuring that our stage environments are repeatable and re-creatable through code and automation.</p><h4><strong>Nightly scale down in production</strong></h4><p>Since our operations are based only in India, we don’t receive significant traffic at night. We decrease the number of instances that we run for a service at off hours. We were already using scheduled actions on Autoscaling Groups on EC2. On Kubernetes, we needed something similar for HPA for our services running in our Kubernetes cluster. <a href="https://github.com/citizensadvice/kube-schedule-scaler">kube-schedule-scaler</a> is capable of doing this. We use a modified fork of kube-schedule-scaler. Developers can specify the scale down schedule by just putting a simple annotation on HPA objects. When the services scale down, the cluster autoscaler takes care of reducing the size of node groups. You can also explore <a href="https://github.com/hjacobs/kube-downscaler">kube-downscaler</a> for a similar functionality.</p><p>Here is an example of what an HPA with a scheduled action looks like:</p><pre>apiVersion: autoscaling/v1<br>kind: HorizontalPodAutoscaler<br>metadata:<br>  annotations:<br>    kube-schedule-scaler/schedule-actions: &#39;[<br>      {&quot;schedule&quot;: &quot;0 19 * * *&quot;, &quot;minReplicas&quot;: &quot;7&quot;},<br>      {&quot;schedule&quot;: &quot;30 1 * * *&quot;, &quot;minReplicas&quot;: &quot;20&quot;}<br>    ]&#39;<br>  name: my-hpa<br>  namespace: my-namespace<br>spec:<br>  maxReplicas: 100<br>  minReplicas: 20<br>...</pre><h4><strong>Resource optimisation for Kubernetes dev cluster</strong></h4><p>In Kubernetes, you can set requests and limits for a pod. In production, the difference between limits and requests should be small else you might see performance issues (read more on this <a href="https://sysdig.com/blog/kubernetes-limits-requests/">here</a>). But in the case of the stage environment, the workload is generally not CPU intensive. So, it doesn’t make sense to keep resource requests very high. It’s a little tough to do this for all your services, so we created a Kubernetes controller which, depending on namespace labels, decreases the requested CPU of containers but keeps the limits at the same value to allow some peaks for applications.</p><blockquote>Reducing requests to minimum while keeping limits high enough helped us reduce number of cores by 70%.</blockquote><p>We faced a few challenges around resource management — you can read about that <a href="https://lambda.grofers.com/cpu-throttling-in-kubernetes-a-postmortem-b9b433d24b03">here</a>.</p><h4><strong>Reducing EBS cost</strong></h4><p>We use a mix of magnetic and GP2 volumes. We try to use magnetic volumes wherever we know that we won’t need much IO. Most of our workload using EBS volume is stateless and were used as root volumes in EC2. We didn’t have a good policy around EBS size selection while launching new EC2 infrastructure which led to a lot of wastage of EBS (most instances using as high as 20% of provisioned storage). The solution here is simple but needs a lot of manual work (some of it can be scripted out as well):</p><ol><li>Recreate all AMIs and update the launch template to provision less only necessary EBS capacity.</li><li>Fix EBS provisioning policies and set good alerts on usage to solve this in the long term.</li></ol><h4><strong>Stage ELB costs</strong></h4><p>We were using ELB to expose the services and provide an endpoint for local testing. But this resulted in extra ELB costs and it was a big inefficiency in our setup. We decided to replace all ELB with Nginx ingress to expose services. We wrote an in-house Kubernetes controller to create a NodePort service backed by Nginx ingress for every request to create an ELB based service. This did not require any change in our service manifests and drastically reduced our ELB costs.</p><h3>S3 Costs</h3><p>S3 is quite cheap for data storage but without proper handle on how you are using S3, the costs can keep increasing steadily with time if you haven’t configured your lifecycle policies for your buckets. We have hundreds of buckets. To get the cost attribution report for S3, we used AWS Athena to query our CUR reports and to filter out the buckets for which we were getting charged the most for. After identifying the costliest buckets, we found a few common problems:</p><ol><li>No lifecycle policy. We don’t retire/purge any data which adds up over months and years.</li><li>If storing backups, backups were not compressed.</li><li>Logs are dumped but not archived.</li></ol><p>You can set up a lifecycle policy on your bucket to change the storage type of your files or remove them after some time. Read <a href="https://aws.amazon.com/premiumsupport/knowledge-center/s3-reduce-costs/">this</a> post by AWS for more details.</p><p>We’d like to especially call out the <a href="https://aws.amazon.com/about-aws/whats-new/2018/11/s3-intelligent-tiering/"><strong>intelligent tiering</strong></a> option which can help reduce storage costs by a great deal.</p><h3>Reserved Instances</h3><p>AWS offers Reserved Instances (RIs) for many of its services (like RDS, Elasticache, Redshift, etc.). If you know that a resource is going to be there for a year or more and wouldn’t require changes in its instance type, it would be better to buy a RI for that instance. AWS also supports converting RIs before expiry of purchase contract for some of their services. Explore those options before making your purchase.</p><p>Buying a lot of RIs can also be problematic if your RI Utilisation decreases because that would be pure wastage. You can use AWS Cost Explorer to track this. Some 3rd party tools can also be used for setting up alerts.</p><h3>Here is what we learnt</h3><p>By undertaking these initiatives, we reduced our costs by almost 40%. The pandemic made us push our limits to optimise cost. It pushed us to put better governance of infrastructure and engineer solutions that fundamentally changed how we manage infrastructure.</p><p>Here are a few lessons that we want to summarise:</p><ol><li><strong>Resource Tagging</strong> — having resource tagging helped us dive deep into our AWS bills and squeeze out wastage as much as possible. We recommend every team should figure this out early in their journey to avoid this debt</li><li><strong>Partner With The Finance Function</strong> — your company’s finance function can help optimize costs a lot by asking the right questions and providing a business framework to look at cloud costs. Use their expertise to your advantage to model your infrastructure costs better.</li><li><strong>Regular Cost Review</strong> — most teams do once in a blue moon cost optimization activity. That’s fine in the initial stages of your company. In later stages as the business grows, being able to regularly review costs becomes important to prevent slippage and keep costs under check. We do this as a monthly activity jointly with our finance team.</li><li><strong>Monitor Data Transfer Cost</strong> — incorrect decisions can creep in and data transfer costs can keep increasing if you don’t have a handle on it.</li><li><strong>Set Good Governance Policies</strong> — If you put too many restrictions and have your “devops engineers” manage everything, then it’s nothing more than more expensive on-premise infrastructure. The cloud must enable your team to move fast but at the same time how do you control costs when you open it up to your engineering teams? We give a lot of freedom to our developers to use AWS. We missed covering some important aspects of governance such as EBS storage, ELB fixed costs, S3 storage, data transfer, etc. Thinking about governance of some of the most commonly used AWS services will go a long way in controlling cost and keeping a good security posture.</li><li><strong>Kubernetes Governance</strong> — Kubernetes is hard to introduce and migrate to but it’s even harder to govern because all the angles of governance are not fully covered by available solutions today. You can find some open-source solutions or you can build some using APIs. Do not overlook it if you are heavily dependent on it or plan to make it central to your infrastructure strategy. We are also exploring a new tagging strategy for kubernetes resources and <a href="https://kubecost.com/">kubecost</a> to monitor costs.</li></ol><p>There are some initiatives that need their own detailed blog posts. We hope to write about them soon and share with you. We hope you found our experience of cost optimization useful.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-xO0dSUvZe8xxRwdMqCiaA.gif" /></figure><h4><a href="https://www.linkedin.com/in/vishesh-j-60b75682/">Vishesh Jindal</a> is an engineer on the Infrastructure Engineering team at Grofers.</h4><h3>Thanks for reading Lambda.</h3><p>Say hello on <a href="http://www.twitter.com/groferseng">Twitter</a> or follow us on <a href="https://www.linkedin.com/company/grofers/">LinkedIn</a>.</p><h4>We’re hiring!</h4><p>We are<strong> </strong><a href="https://www.linkedin.com/feed/update/urn:li:activity:6723105680967901184/"><strong>hiring</strong></a> across various roles<strong>!</strong> If you are interested in exploring working at Grofers, we’d love to hear from you. You can either <strong>apply on </strong><a href="https://www.linkedin.com/jobs/search?location=India&amp;geoId=102713980&amp;trk=public_jobs_jobs-search-bar_search-submit&amp;f_C=3561164&amp;redirect=false&amp;position=1&amp;pageNum=0"><strong>LinkedIn</strong></a> or directly reach out to the author on <a href="https://twitter.com/visheshj92">Twitter</a> or <a href="https://www.linkedin.com/in/vishesh-j-60b75682/">LinkedIn</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b466f354b123" width="1" height="1" alt=""><hr><p><a href="https://lambda.blinkit.com/response-to-covid-19-lessons-learned-in-aws-cost-optimization-b466f354b123">How COVID-19 pushed us to reduce our AWS cost by half</a> was originally published in <a href="https://lambda.blinkit.com/">Lambda by Blinkit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ansible at Grofers (Part 4) — Manageable auto-scaling with Ansible]]></title>
            <link>https://lambda.blinkit.com/ansible-part-4-manageable-auto-scaling-with-ansible-23fc175f1ae2?source=rss-5d3d78a9658b------2</link>
            <guid isPermaLink="false">https://medium.com/p/23fc175f1ae2</guid>
            <category><![CDATA[ansible]]></category>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[infrastructure]]></category>
            <category><![CDATA[devops]]></category>
            <dc:creator><![CDATA[Vishesh Jindal]]></dc:creator>
            <pubDate>Thu, 20 Sep 2018 05:43:09 GMT</pubDate>
            <atom:updated>2018-09-20T06:00:40.632Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/690/1*A-D6cuz1WofJOR81z1yWJQ.png" /></figure><p>Auto-Scaling Groups (ASG) in AWS EC2 provide a really easy way to scale your services horizontally based on different kinds of alarms. It launches instances using existing AMIs baked by you for your services.</p><p>While ASGs are easy to understand at a high level, they have their own challenges — a fairly difficult one being how to bring the latest code, packages and configuration into the AMI. You can automate building AMIs but it quickly becomes challenging to build AMIs for code bases in a micro-services architecture that can potentially be updated every hour.</p><h3>How did we solve it</h3><p>If you have been <a href="https://lambda.grofers.com/tagged/ansible">following our blog</a>, you would know how much we love Ansible. Our entire engineering team uses Ansible for setting up and deploying services. So our natural inclination was towards using Ansible for simplifying our auto-scaling setup as well.</p><p>Any EC2 instance in our infrastructure goes through at least two separate runs of Ansible:</p><ol><li>First playbook for basic setup of the instance (installing basic common tools like consul agent, rsyslog, collectd, etc.)</li><li>The second one is used to setup the service built or being setup by development teams.</li></ol><p>Having these two run on a new EC2 instance would make sure that the service is ready for use.</p><p>When a new instance is added in an ASG, we want the above two things to happen, but slightly differently to start the services essential for business as fast as possible.</p><h3>Introducing Bootstrap</h3><p>Bootstrap is our internal tooling that developers use to prepare easily maintainable AMIs for their services which helps them bring their services up as fast as possible in the latest desired state.</p><p>Written in Bash, it is basically a wrapper on top of Ansible that gives the flexibility to configure how to execute playbooks using a simple configuration. When AMI is baked with bootstrap configuration for a service, bootstrap will run Ansible on startup to bring up that service in the desired state. This way you have to rebake your AMI only when you want to change your bootstrap configuration. There is no need to rebake AMIs when code or ansible playbooks change.</p><p>A bootstrap configuration file includes information like ansible repository, branch, playbook name, and some other stuff required for us to run execute the ansible playbook. Using this information, the playbook is executed locally on the instance. We have our own ansible module which places the file with the required values. All that needs to be done to setup an AMI is:</p><pre>- name: Add bootstrap file for consumer web<br>  asg_bootstrap:<br>    ansible_git_repo: git@github.com:grofers/ansible-repo.git<br>    ansible_git_branch: master<br>    ansible_playbook: my_playbook.yml<br>    ansible_roles_requirements: roles.yml<br>    ansible_playbook_args: &quot;-t tag1,tag2&quot;<br>    file_name: 00_setup_abc<br>    state: present</pre><ol><li>Create a task in your ansible playbook to create bootstrap’s configuration file using our custom ansible module called asg_bootstrap. The task would look something like above.</li><li>Run ansible playbook on the new instance and create the AMI out of it.</li></ol><h3>How does it work</h3><p>When an instance starts, there are a bunch of tasks that need to be performed like tagging of the instance, configuring rsyslog, collectd, etc. But during traffic peaks, we can’t wait for services like collectd and rsyslog agent to come up before our business critical backend services.</p><p>To solve this, bootstrap allows you to set priority of tasks so that you can choose any critical setup necessary to be done before business facing service can be started. We have a directory structure like this for the configuration files</p><pre>./<br>|── bootstrap.d/<br>|   ├── prerun/<br>|   |   ├── 00_tagging<br>|   |   ├── 01_set_hostname<br>|   ├── 00_setup_abc<br>|   ├── 01_setup_xyz<br>|   └── postrun/<br>|   |   ├── 00_setup_common</pre><p>Configuration files get their name from the file_name argument of the asg_bootrap module as shown in the code snippet above. Bootstrap picks up configuration files in bootstrap.d/prerun directory first, then in bootstrap.d/ directory at the root level and finally in bootstrap.d/postrun directory. We prefix the configuration files with numbers so that we can easily control the order of execution. The order of execution for the configuration files in the above example will be like this:</p><ol><li>prerun/00_tagging — Responsible for setting the Name tag for that instance</li><li>prerun/01_set_hostname — Responsible for setting FQDN on the basis of the Name tag</li><li>00_setup_abc — Responsible for setting FQDN on the basisof the Name tag</li><li>01_setup_abc — Responsible for setting up business facing critical service XYZ</li><li>postrun/00_setup_common — Responsible for setting up common tooling like rsyslog, collectd, etc.</li></ol><h3>Challenges &amp; fixes</h3><h4>Setting dynamic Name tags for identifying instances</h4><p>A single service can have a lot of instances and every instance must be uniquely identifiable, meaning it should have a unique Name tag. ASG doesn’t have support for dynamic Name tags for new instances — every new instance started in an ASG would always get the same Name tag. Our 00_tagging script solves this by querying a the instance’s key in Consul to get the last index number for that service, increments the index number, update the key (using CAS operation) with the new index number in Consul and retag the instance with the appropriate Name tag.</p><h4>Missing Tags</h4><p>Name tags are required by our Ansible’s dynamic inventory script to function properly. We started seeing intermittent failures because AWS’s autoscaling sometimes takes time to tag the instance and is not deterministic. So at the time of Ansible running on instance start, it is possible that the name tag is not present on the instance. To fix this, if the instance doesn’t have a name tag, we check which ASG the instance is attached to, directly get the Name tag from there and forcefully apply it.</p><h4>AWS API Limit Exceeded</h4><p>We have our own dynamic inventory script which gets the instances from AWS and creates groups on the basis of their tags. This resulted in us hitting AWS API’s limit resulting into failures. To reduce the number of AWS API calls, bootstrap script sets some environment variables (like ip address, name tag, etc.) which are used by ansible instead of dynamic inventory script to create groups at its end.</p><h4>Changes in bootstrap script</h4><p>It is a little troublesome if there are changes in bootstrap itself. We have to bake a new AMIs for every service with a new bootstrap script. One of the ways this can be fixed is to keep the script at a remote location which could be fetched and executed when the instance comes up. This problem is still unsolved for us but having iterated on bootstrap enough number of times has got us to a point where this is not a very big problem for us.</p><h3>Conclusion</h3><p>Though we faced some challenges in the beginning, this setup has helped us achieve auto-scaling without the overheads of having an AMI build pipeline and in giving developers the right kind of tooling that enables them to quickly implement auto-scaling with a lot of control, improving the overall DevOps culture in our team.</p><p>If you like to solve similar problems at scale, we are always looking for new talent. Check for open positions <a href="https://grofers.recruiterbox.com/?team=Product%2C%20Design%20and%20Technology#content">here</a>.</p><p><strong>Read our other blog posts in this series:</strong></p><ol><li><a href="https://lambda.grofers.com/ansible-at-grofers-part-1-introduction-52cdc92259f">Ansible at Grofers (Part 1) — Introduction</a></li><li><a href="https://lambda.grofers.com/ansible-at-grofers-part-2-managing-postgresql-c4069ce5855b">Ansible at Grofers (Part 2) — Managing PostgreSQL</a></li><li><a href="https://lambda.grofers.com/ansible-at-grofers-part-3-managing-ec2-instances-887567124257">Ansible at Grofers (Part 3) — Managing EC2 Instances</a></li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=23fc175f1ae2" width="1" height="1" alt=""><hr><p><a href="https://lambda.blinkit.com/ansible-part-4-manageable-auto-scaling-with-ansible-23fc175f1ae2">Ansible at Grofers (Part 4) — Manageable auto-scaling with Ansible</a> was originally published in <a href="https://lambda.blinkit.com/">Lambda by Blinkit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Securing AWS Access Keys]]></title>
            <link>https://lambda.blinkit.com/securing-aws-access-keys-a083ae4a5572?source=rss-5d3d78a9658b------2</link>
            <guid isPermaLink="false">https://medium.com/p/a083ae4a5572</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[vpn]]></category>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[infrastructure]]></category>
            <dc:creator><![CDATA[Vishesh Jindal]]></dc:creator>
            <pubDate>Thu, 05 Apr 2018 17:55:52 GMT</pubDate>
            <atom:updated>2018-04-06T04:30:08.824Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/462/1*S_diBDtF6vTwn5gUUPsTEw.png" /><figcaption>Source: <a href="http://www.blog.labouardy.com/openvpn-server-aws/">http://www.blog.labouardy.com/openvpn-server-aws/</a></figcaption></figure><p>After a Friday night party, we found out that someone had accidentally put AWS Access Keys in a public repository (thanks to AWS Security Team for reporting this). Keys didn’t have much access and were of a test environment, but this could have happened with any other key pair with higher privileges. We immediately deactivated them and got the keys rotated everywhere they were being used.</p><p>This was not the first time. Things like these have happened in the past with us. Asking friends in the industry reveals that this is a common issue with many engineering teams. Analyzing past incidents like these we learned that whenever this has happened at Grofers, they usually happened because we somehow missed the implications of something we did while trying to build a cool experience for our users. And this can happen to anyone who ends up mishandling “secrets” secret because of a mistake or lack of awareness.</p><p>We understood that a big part of the problem is education and started thinking how can we educate our team better but even with all best efforts put in place, things like these can still happen. We realized that education alone is not the solution and we need to put better guards in place — like ones that the AWS Security Team has put for its customers.</p><h3>How we secured it</h3><p>We started trying to figure out a way to make AWS keys more secure. In IAM docs (<a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#policy-vars-infotouse">https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_variables.html#policy-vars-infotouse</a>), we came across sourceIP and ec2:SourceInstanceARN conditions.</p><p>We have two kinds of users on AWS:</p><ol><li>Application users — applications using AWS APIs</li><li>Human users — members of our engineering and product teams using AWS CLI</li></ol><p>The only thing we could use was aws:sourceIP. It would be okay for the developers when they are in office, but that would stop them from using keys from their home or anywhere else. We want our developers to have the flexibility of working from anywhere.</p><p>We already have a VPN setup to access all of our infrastructure. So we thought why not route all requests to AWS APIs through our VPN itself. This will make the requests AWS APIs with our VPN instance’s IP as the source. Our VPN server has support to push routes to clients when a connection is established. So, we route all requests falling in AWS IP range (<a href="https://ip-ranges.amazonaws.com/ip-ranges.json">https://ip-ranges.amazonaws.com/ip-ranges.json</a>) through our VPN. Now, every request that AWS receives comes through our VPN infrastructure and has the aws:sourceIP of our VPN servers. So, we could now use a policy like this:</p><pre>&quot;Condition&quot;: {<br>  &quot;IpAddress&quot;: {<br>    &quot;aws:SourceIp&quot;: [<br>      &quot;203.0.113.0/32&quot;,<br>      &quot;23.0.123.0/32&quot;<br>    ]<br>  }<br>}</pre><p>This restricts usage of AWS Access Key from our VPN only.</p><p>We have more than 100 IAM users and more than 300 IAM policies. Some policies are in use by both human users and application users. It’s cumbersome to create new policies and update all the policies to add aws:sourceIP conditions.</p><p>According to the documentation <a href="http://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_evaluation-logic.html">here</a>, adding an explicit deny will deny all the requests if it matches that condition.</p><pre>{<br>  &quot;Version&quot;: &quot;2012-10-17&quot;,<br>  &quot;Statement&quot;: {<br>    &quot;Effect&quot;: &quot;Deny&quot;,<br>    &quot;Action&quot;: &quot;*&quot;,<br>    &quot;Resource&quot;: &quot;*&quot;,<br>    &quot;Condition&quot;: {&quot;NotIpAddress&quot;: {&quot;aws:SourceIp&quot;: [<br>      &quot;192.0.2.0/24&quot;,<br>      &quot;203.0.113.0/24&quot;<br>    ]}}<br>  }<br>}</pre><p>Ref: <a href="http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_examples.html#iam-policy-example-deny-source-ip-address">AWS IAM Docs</a></p><p>So we created the above policy and mapped it to IAM group which contained all the human users.</p><p>As for application users, we needed a similar thing. Since most of our instances are private, the sourceIP was the internet gateway’s public IP. So we used these IPs for the application users.</p><p>We created two IAM Groups, <strong>human-users</strong> and <strong>application-users</strong>. And added all the IAM users to these groups according to their type.</p><h3>Conclusion</h3><p>Now AWS APIs can be accessed over VPN by human users and from private EC2 instances by application users. This prevents the use of application user’s credentials by human users and vice versa and makes sure that no IAM user’s credentials can be used by anyone outside our private network.</p><p>If you are interested in solving problems like this, we are hiring for all roles. <a href="https://grofers.com/careers/">Come join us</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a083ae4a5572" width="1" height="1" alt=""><hr><p><a href="https://lambda.blinkit.com/securing-aws-access-keys-a083ae4a5572">Securing AWS Access Keys</a> was originally published in <a href="https://lambda.blinkit.com/">Lambda by Blinkit</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>