<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>api gateway on Digi Hunch</title><link>https://static.digihunch.com/tag/api-gateway/</link><description>Recent content in api gateway on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Wed, 23 Apr 2025 13:37:45 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/api-gateway/index.xml" rel="self" type="application/rss+xml"/><item><title>AWS serverless services and developer tools</title><link>https://static.digihunch.com/2022/11/aws-serverless-services-and-developer-tools/</link><pubDate>Wed, 09 Nov 2022 12:19:00 -0400</pubDate><guid>https://static.digihunch.com/2022/11/aws-serverless-services-and-developer-tools/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-server-less-devtools.webp" alt="Featured image of post AWS serverless services and developer tools" /&gt;&lt;p class="wp-block-paragraph"&gt;As &lt;a href="https://static.digihunch.com/2022/10/computing-from-paas-to-serverless/"&gt;discussed&lt;/a&gt;, serverless simply means cloud services that delegate autoscaling management to cloud platform. In my mind, the word &amp;#8220;serverless&amp;#8221; translates into &amp;#8220;managed autoscaling&amp;#8221;. As long as a service&amp;#8217;s capacity is managed automatically, we can consider it as serverless. Given that capacity scaling accounts for a good amount of work in IT operation, moving to serverless significantly reduces operation overhead. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Unlike containers, the serverless ecosystem lacks standard. Since &lt;a href="https://serverlessland.com/"&gt;AWS&lt;/a&gt; is leading the charge in this field, let&amp;#8217;s take a look at its offerings.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Lambda Function&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Lambda functions have triggers. We can configure a trigger from an AWS service, or even a non-AWS service which supports AWS event bridge, to invoke Lambda function. Based on the trigger (e.g. SQS, S3, DynamoDB), you then specify event source mapping. For example, if trigger is SQS, you need to specify the queue name, batch size and batch window in event source mapping. If trigger is S3, the event source mapping needs to specify bucket, S3 action, etc. Event source mappings vary significantly among trigger types.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Depending on the trigger, a Lambda function may be invoked in one of the two ways:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-stripes"&gt;&lt;table class="has-cyan-bluish-gray-background-color has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/invocation-sync.html" target="_blank" rel="noreferrer noopener"&gt;Synchronous&lt;/a&gt; invocation&lt;/th&gt;&lt;th&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/invocation-async.html#async-overview" target="_blank" rel="noreferrer noopener"&gt;Asynchronous&lt;/a&gt; invocation&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Summary&lt;/td&gt;&lt;td&gt;Requestor fires request and waits until it receives response before closing connection.&lt;/td&gt;&lt;td&gt;Lambda places triggering event in a queue and immediately returns a success code (202). Then a separate process reads events off the queue and sends them to your Lambda function. When the function returns a success response or exits without throwing an error, Lambda sends a record of the invocation to an EventBridge event bus.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pros&lt;/td&gt;&lt;td&gt;Requestor get invocation result as soon as function run is complete&lt;/td&gt;&lt;td&gt;The queue decouples the request and invocation.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cons&lt;/td&gt;&lt;td&gt;When load is high the function requires higher capacity for concurrency&lt;/td&gt;&lt;td&gt;Involves more parties at play and can be quite complex to troubleshoot.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://docs.aws.amazon.com/lambda/latest/operatorguide/invocation-modes.html" target="_blank" rel="noreferrer noopener"&gt;Here&lt;/a&gt; is a chart with the triggers that support each invocation mode. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Real-life applications often need to include libraries and dependencies that requires language-specific steps. For example, NodeJS applications need &lt;a href="https://aws.amazon.com/blogs/compute/optimizing-node-js-dependencies-in-aws-lambda/"&gt;bundling&lt;/a&gt; (often using Webpack or esbuild). We can use &lt;a href="https://aws.amazon.com/blogs/compute/using-lambda-layers-to-simplify-your-development-process/"&gt;Lambda layers&lt;/a&gt; to simplify the management. The additional libraries often need to be packaged in S3 bucket or as container image.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Lambda functions are subject to &lt;a href="https://aws.amazon.com/blogs/compute/operating-lambda-performance-optimization-part-1/"&gt;cold start&lt;/a&gt; to &lt;a href="https://betterprogramming.pub/lets-stop-talking-about-serverless-cold-starts-38e4c1fda963"&gt;cope with&lt;/a&gt;. When a lot of invocations occur about the same time, the order of executions might be different than the order of upstream events that invokes the function. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-api-gateway"&gt;API Gateway&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS API Gateway is an &lt;a href="https://microservices.io/patterns/apigateway.html"&gt;API gateway&lt;/a&gt; implementation for REST and WebSocket APIs. It couples with &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/services-apigateway.html"&gt;Lambda&lt;/a&gt; in the classic &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/serverless-multi-tier-architectures-api-gateway-lambda/welcome.html"&gt;multi-tier serverless&lt;/a&gt; architecture pattern. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this classic pattern Lambda function often need to work with a database (e.g. DynamoDB etc), to perform &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete"&gt;CRUD operations&lt;/a&gt; and other custom business logics. The CRUD operations are so commonplace that it makes sense to use mapping template to configure CRUD operation instead of writing similar set of functions for each new data model. API Gateway supports such a &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/models-mappings.html"&gt;mapping template&lt;/a&gt; called &lt;a href="https://velocity.apache.org/engine/devel/vtl-reference.html"&gt;Velocity Template Language&lt;/a&gt; (VTL), a technology from Apache &lt;a href="https://velocity.apache.org/"&gt;Velocity&lt;/a&gt; Project.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;API Gateway integrate with many other AWS services. Here are the available integration types:&lt;/p&gt;&#10;&lt;table id="tablepress-20" class="tablepress tablepress-id-20 tbody-has-connected-cells"&gt;&#10;&lt;thead&gt;&#10;&lt;tr class="row-1"&gt;&#10;&#9;&lt;th class="column-1"&gt;Integration Type&lt;/th&gt;&lt;th class="column-2"&gt;Description&lt;/th&gt;&lt;th class="column-3"&gt;Integration Mode&lt;/th&gt;&lt;th class="column-4"&gt;How it works&lt;/th&gt;&#10;&lt;/tr&gt;&#10;&lt;/thead&gt;&#10;&lt;tbody class="row-striping row-hover"&gt;&#10;&lt;tr class="row-2"&gt;&#10;&#9;&lt;td rowspan="2" class="column-1"&gt;AWS integration&lt;/td&gt;&lt;td rowspan="2" class="column-2"&gt;connects gateway to an AWS service action as the end point.&lt;/td&gt;&lt;td class="column-3"&gt;AWS_PROXY&lt;/td&gt;&lt;td class="column-4"&gt;this mode only supports only one action with one service: the function invoking action for Lambda service. Therefore it is available only for Lambda integration and no other AWS services. For that reason, it is also known as Lambda proxy integration. This mode is recommended for Lambda integration and is the default mode for LambdaIntegrationOption CDK construct. It connects a method (PUT, GET, etc) to a Lambda function and pass along the request on the way in, and the response on the way out. You do not set integration request or integration response. Even if you do, there’s no effect. This “pass-along” mode is easier to understand and configure. &lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-3"&gt;&#10;&#9;&lt;td class="column-3"&gt;AWS&lt;/td&gt;&lt;td class="column-4"&gt;this mode connects an API method to a broad range of supported AWS service action. Function invoking for Lambda service is a common example but not the only service action supported in this mode. When used in Lambda integration, it is also referred to as “Lambda custom integration”, or “normal (request/response mapping) integration”. This mode is good for advanced use cases (e.g. header modification) but involves more management effort. You have to control the mapping between method request and integration request, and between integration response and method response. &lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-4"&gt;&#10;&#9;&lt;td rowspan="2" class="column-1"&gt;HTTP integration&lt;/td&gt;&lt;td rowspan="2" class="column-2"&gt;for generic HTTP service endpoint&lt;/td&gt;&lt;td class="column-3"&gt;HTTP_PROXY&lt;/td&gt;&lt;td class="column-4"&gt;the pass-along mode for upstream HTTP endpoint&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-5"&gt;&#10;&#9;&lt;td class="column-3"&gt;HTTP&lt;/td&gt;&lt;td class="column-4"&gt;the request/response mapping mode for upstream HTTP endpoint&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;tr class="row-6"&gt;&#10;&#9;&lt;td class="column-1"&gt;MOCK integration &lt;/td&gt;&lt;td class="column-2"&gt;the API gateway itself serves as the endpoint.&lt;/td&gt;&lt;td class="column-3"&gt;MOCK&lt;/td&gt;&lt;td class="column-4"&gt;the API gateway itself acts endpoint without an upstream. One example use case is to return CORS-related headers upon a pre-flight OPTIONS query.&lt;/td&gt;&#10;&lt;/tr&gt;&#10;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;!-- #tablepress-20 from cache --&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The table above is a summary of API Gateway integration types as covered &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-api-integration-types.html"&gt;here&lt;/a&gt;. In the AWS context, proxy mode suggests that the request is not being morphed (transformed). In non-proxy mode, request or response may be modified on their ways in or out.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For API gateway to invoke lambda function, synchronous invocation is used by default. You can also configure API gateway to invoke Lambda function &lt;a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-integration-async.html" target="_blank" rel="noreferrer noopener"&gt;asynchronously&lt;/a&gt; by using headers.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As with other API gateway implementations, AWS API Gateway can also connects to an authorizer (either another Lambda function or Cognito service) in order to authorize the incoming request.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;AppSync&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a previous post, I discussed &lt;a href="https://static.digihunch.com/2022/10/graphql-and-grpc/"&gt;GraphQL&lt;/a&gt; as a modern and efficient alternative to REST API. &lt;a href="https://aws.amazon.com/appsync/"&gt;AppSync&lt;/a&gt; to GraphQL is the same as API Gateway to REST API. One of the advantages that GraphQL has over REST API is more information in the response. Oftentimes, we use a proxy that supports GraphQL in front of REST API service. AppSync can act as such proxy. I think of it as a managed &lt;a href="https://www.apollographql.com/docs/intro/platform"&gt;Apollo&lt;/a&gt; since they play the the role in the architecture. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another benefit of GraphQL is the support of subscription, obviating WebSocket configuration. AppSync supports pub/sub API for real-time experience. Client application can get near real-time update as the data on the server is changed.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can configure AppSync to connect to different data sources to formulate GraphQL response. The data source can be an HTTP endpoint, a Lambda function, a database (Relational or DynamoDB), or OpenSearch. &lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="408" src="https://static.digihunch.com/wp-content/uploads/2022/12/appsync-1024x408.png" alt="" class="wp-image-7653" srcset="https://static.digihunch.com/wp-content/uploads/2022/12/appsync-1024x408.png 1024w, https://static.digihunch.com/wp-content/uploads/2022/12/appsync-300x119.png 300w, https://static.digihunch.com/wp-content/uploads/2022/12/appsync-768x306.png 768w, https://static.digihunch.com/wp-content/uploads/2022/12/appsync-1536x612.png 1536w, https://static.digihunch.com/wp-content/uploads/2022/12/appsync-2048x816.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;figcaption class="wp-element-caption"&gt;App Sync&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Between data source and the request, AppSync uses resolver to convert GraphQL payload to the underlying protocols and executes if the caller is authorized to invoke it. Resolvers are comprised of request and response mapping templates, which contain transformation and execution logic. AppSync also uses VTL as the mapping template for resolvers.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Messaging Services&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Messaging services have three patterns: queues, pub/sub, and event buses. In AWS, the corresponding services are SQS, SNS and EventBridge. Here is a good &lt;a href="https://aws.amazon.com/blogs/compute/choosing-between-messaging-services-for-serverless-applications/"&gt;post&lt;/a&gt; about how to choose among them.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Queues are temporary storage to decouple the source and destination systems. The expectation is that the actions in response to the message can be delayed. If that is not the case and the response needs to be immediate, that is by definition an event-driven pattern. A common solution is to have the AWS service invoke Lambda function. If that is not supported, we can use SNS as an intermediary. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;SNS is for simple event-driven pattern. In a more complicated event-driven architecture, we often need an event bus to route events in certain ways. We also need to support various &lt;a href="https://aws.amazon.com/eventbridge/integrations/"&gt;event sources&lt;/a&gt; by different software providers. Moreover, we want the capability to register our own event schema. These are the scenarios where EventBridge can help.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Developer tools&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In application development with serverless stack, the line between application and infrastructure is somewhat blurred. Developers often find themselves making repeated configuration on cloud resources while writing application code. For example, to test Python code (application), developer has to upload the code to S3, create Lambda function referencing the code, etc. This would require too much work with AWS CLI, or CloudFormation. The AWS &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html"&gt;SAM&lt;/a&gt; (AWS Serverless Application Model) is a better utility for serverless development workflow.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;SAM comes with its own CLI and developers can feed it with template that interacts with a number of serverless &lt;a href="https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-specification-resources-and-properties.html"&gt;resources&lt;/a&gt; (e.g. API, SimpleTable, Function, etc). I view SAM as one layer of abstraction on top of CloudFormation that handles some resources in serverless stack. It saves developers from re-writing resources in CloudFormation templates and keeping them consistent, which would have been tedious. Once you deployed your application using SAM, it will appear as a Lambda Application in the console.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;SAM templates are also declarative. It is simple but limited in feature. AWS CDK is also a powerful utility that works with general purpose programming language for IaC and interacts with all AWS resources. It is very powerful for building serverless applications. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;AWS Amplify&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We have CDK, SAM and CloudFormation but we still have to ensure integration between those resources and our applications. For example, when creating a S3 buckets, they have to get the endpoint and reference it in the application code. So is Cognito. As a result, developers still have to spend time on resource integration. They still can&amp;#8217;t focus on business logic. We need a tool that can make opinionated configuration of cloud resources and automatically reference them from the application code. This is the purpose of AWS Amplify.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Amplify not only help application developers create backend resources with opinionated configurations. It also provides libraries for the application to use and connect to those resources seamlessly. Developers will need to include Amplify libraries in the application code. To create cloud resources, they can use Amplify &lt;a href="https://docs.amplify.aws/cli/"&gt;CLI&lt;/a&gt; or &lt;a href="https://docs.amplify.aws/console/uibuilder/figmatocode/#step-1-set-up-figma-file"&gt;Studio&lt;/a&gt; (a web portal from AWS console). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Amplify natively supports a number of serverless resources such as API (using API gateway or AppSync), Storage (S3 and CloudFront), Lambda function, Cognito, etc. Developer may use Amplify CLI command to create such supported resources. Amplify will prompt some guiding questions in order to configure them correctly. In addition to the natively supported resources, developers can also create custom resources. They have to declare those custom resources with CloudFormation or CDK. I came across &lt;a href="https://github.com/aws-samples/amazon-location-samples/tree/main/maplibre-js-react-iot-asset-tracking"&gt;this&lt;/a&gt; project as a good illustration of how Amplify works. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Amplify Studio can save developers from using Amplify CLI commands. It also can integrate with &lt;a href="https://en.wikipedia.org/wiki/Figma_(software)"&gt;Figma&lt;/a&gt;, providing developers with a framework for &lt;a href="https://aws.amazon.com/blogs/mobile/aws-amplify-studio-figma-to-fullstack-react-app-with-minimal-programming/"&gt;frontend development.&lt;/a&gt; However, in my experience, it is still &lt;a href="https://github.com/aws-amplify/amplify-ui/issues"&gt;glitchy&lt;/a&gt;. For now I stick to Amplify CLI.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Amplify also supports &lt;a href="https://docs.aws.amazon.com/amplify/latest/userguide/getting-started.html"&gt;hosting&lt;/a&gt; capability, providing users with opinionated and customizable CI/CD pipeline configuration.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;How about &amp;#8220;clientless&amp;#8221;&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Lambda function can also run client-side logics. System administrators have to create lots of client-side scripting. While Lambda functions can encapsulate those logics, we&amp;#8217;d still need a script orchestrator to invoke those functions. AWS &lt;a href="https://aws.amazon.com/step-functions/"&gt;Step Function&lt;/a&gt; comes to rescue. It was even regarded as the &lt;a href="https://itnext.io/why-step-functions-is-the-best-aws-service-you-are-not-using-4f3c133d7d0d"&gt;most under-utilized service&lt;/a&gt;. As the author states, a state machine (as design pattern) is simply a flow of actions with decision making logics.&amp;nbsp;AWS Step function helps you groom the logic flow of existing actions with &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/concepts-amazon-states-language.html" target="_blank" rel="noreferrer noopener"&gt;Amazon States Language&lt;/a&gt;. There are standard and express workflows. Each step is a state. A state can be of several different types, such as Choice, Task, Succeed, Fail, End, Map, Wait, Parallel. The task can be a Lambda function, and even AWS API calls. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS Step function integrate with Lambda functions. It is typically used for patterns with long process and the need to orchestrate the execution of several Lambda functions. For specific use cases, look at these &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/create-sample-projects.html" target="_blank" rel="noreferrer noopener"&gt;sample projects&lt;/a&gt;. For example, a network professional needs to run a lot of connectivity testing, reusing the same Python script but run it from different subnets. We need to create a Lambda function for commands like &amp;#8220;nc -vz&amp;#8221; then &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/configuration-vpc-endpoints.html"&gt;invoke the lambda function from VPC&lt;/a&gt;, multiple times from different VPCs. We should use step function to drive this. It works like a Makefile on Linux, without requiring your own computer to run.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS Step function also integrates with &lt;a href="https://docs.aws.amazon.com/step-functions/latest/dg/connect-supported-services.html" target="_blank" rel="noreferrer noopener"&gt;other AWS services&lt;/a&gt;, such as SNS, SQS, Dynamo, Batch, Glue, EMR, EKS, API gateway, event bridge.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Whenever we need to create a script, involving custom actions, or AWS API calls, we should consider using AWS step function to organize the actions. The benefits are: it saves you a laptop or bastion host (&amp;#8220;client-less&amp;#8221;), many ways to invoke them (not just cron&amp;#8221;). &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In this post, we started with some serverless services in AWS. For server-side application, we can use API Gateway to invoke Lambda function. For client-side, we can use step function to invoke Lambda function. We then discussed some tools to speed up application development in serverless pattern. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS categorizes both Amplify and AppSync under Front-end Mobile. Amplify does not represent any computing resources in AWS cloud. It is a library and CLI tools to enhance developer experience. AppSync on the other hand is a cloud computing resource acting as GraphQL API for mobile or web application. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/10/computing-from-paas-to-serverless/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Computing services: from PaaS to Serverless&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/11/key-mapping-on-external-pc-keyboard-on-macbook/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Key mapping for external PC keyboard on Mac&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Computing services: from PaaS to Serverless</title><link>https://static.digihunch.com/2022/10/computing-from-paas-to-serverless/</link><pubDate>Fri, 21 Oct 2022 19:31:00 -0400</pubDate><guid>https://static.digihunch.com/2022/10/computing-from-paas-to-serverless/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-computing-paas-serverless.webp" alt="Featured image of post Computing services: from PaaS to Serverless" /&gt;&lt;p class="wp-block-paragraph"&gt;Silicon Valley startups in mid-2000s likely do not run their own IT operations (i.e. renting their own data centre spaces, purchasing their own rack-mounted servers). Since the &lt;a href="https://web.archive.org/web/20070101134207/http://www.amazon.com/aws/"&gt;launch of EC2&lt;/a&gt;, AWS has been renting extra computing capacity to those startups, in the IaaS model. The leased infrastructure requires maintenance work, and AWS realized that many of these customers cannot afford specialized database admins, network admins, storage admins, or even server admins. As a result, they created a handful of managed services aiming to cut out admin overhead and let their customer focus on coding. This is how Platform-as-a-service (PaaS) came about. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s take a look at what are exactly operation activities.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-ops-activities"&gt;Ops activities&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;IT operation team manages server provisioning, installation of operating system, tuning performance, configuring auto scaling and load balancing, configure networking and storage systems, etc. Networking can be so complex that many infrastructure teams have a dedicated &lt;a href="https://en.wikipedia.org/wiki/Network_operations_center"&gt;Network Operation Center&lt;/a&gt; (NOC), who along with security team, manages key aspects of networking, such as segmentation, router configuration, load balancing, firewall configuration.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For client-server application, the client-side code will make outgoing connections, utilizing the TCP/IP stack on the host through an ephemeral port. The server-side code has to be wrapped as a service. A daemon ensures the process running this service stays up and listens to a TCP port in order to respond to request by invoking the functions. Application team usually assumes these activities.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If database is involved, then the patching, upgrade, replication, data protection are all Ops problems. If storage is involved, then Ops has to manage mass data accumulated over years, the integration between storage and database and applications, performance, replication, etc. Some larger organizations have full-time database administrator and storage administrators.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then comes container. Containers have their benefits but it increases the operation overhead by an order of magnitude. Running container application at scale warrants its own platform, most likely a Kubernetes platform, to address all of the problems above again at the cluster level. Some organization created platform team to manage container and VM platforms.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is the Ops, that turns functional code into a running business. It is also the Ops, that becomes a pain point as a startup scales. With IaaS and PaaS models, AWS managed to convince many small businesses to delegate their IT operations to AWS. This is the humble start of cloud computing.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-elastic-beanstalk"&gt;Elastic Beanstalk&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At first, I wasn&amp;#8217;t too impressed with Elastic Beanstalk, since it abstracts away too many details. However, I later realized that it has been surprisingly popular in the developer community, especially with individual developers and SMBs. It simplifies deployment to the point that their users don&amp;#8217;t need to know other AWS services, allowing them to focus on coding application logic.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;You configure Applications and Environments (one application may have multiple environments). In the Environment layer, you can specify code platform (e.g. Python 3.8 on 64bit Amazon Linux 2, Java, Go, PHP, Ruby) and even container platform (Docker on EC2 or ECS). Behind the scene, Elastic Beanstalk configures EC2 instances, Elastic Load Balancers, etc on the selected VPC and integrate with logging and monitoring services. In the console, Elastic Beanstalk exposes a list of configurations options (e.g. AMI, instance type). This centralized configuration page is dummied down for those who don&amp;#8217;t want to deal with Ops. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The downside of Elastic Beanstalk is it takes away a lot of flexibility. Many developers find Elastic Beanstalk limit their choices of deployment, as their applications scale. Elastic Beanstalk does not suit for applications that demand extensive operation efforts. Its niche market is individual developers and SMB. Few enterprise applications run on Elastic Beanstalk.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-containerization-with-ecs-and-eks"&gt;Containerization with ECS and EKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When we containerize an application, we build container images. Then we run these images with container runtimes, is a core feature of container platform. Container platform also provides orchestration engine since we frequently take containers up and down. In addition, container platform provides mechanisms for container networking and storage.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS has a couple options for container platform. ECS (Elastic Container Service) came out earlier. It organizes a group of EC2 instances as a cluster. You can manage autoscaling, &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/bestpracticesguide/networking-networkmode.html"&gt;networking&lt;/a&gt;, and &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/bestpracticesguide/storage.html"&gt;persistent storage&lt;/a&gt; (EFS, FSx etc) on ECS. EKS (Elastic Kubernetes Service) is the managed Kubernetes service by AWS. Just like AKS, it provides a managed control plane along with computing nodes.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I see ECS as a proprietary and simplified container platform, and Kubernetes as an open-source standard for full-fledged container platform with an entire ecosystem. EKS includes an upstream-certified Kubernetes distribution with a set of tools specific to AWS. Since Kubernetes is the de-facto standard container platform, I prefer EKS by default, unless I can justify the use of ECS. In fact, ECS and Kubernetes have many concepts in common. For example, a &amp;#8220;Task&amp;#8221; in ECS is equivalent to a Pod in Kubernetes. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Whether it is ECS or EKS, right-sizing the computing node group is always challenging especially when the application traffic load is irregular. AWS Fargate is a technology that provides on-demand, right-sized compute capacities. It works with ECS and EKS. When integrated with EKS, we delegate the node management (e.g. scaling) to Fargate and forget about sizing the node pool.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Using ECS and Fargate involves quite a bit of configurations. To simplify that, we can use App Runner, which builds ECS cluster and uses Fargate to execute the container behind the scenes. App Runner helps client in a way similar to Elastic Beanstalk, but concentrate on Container workload.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-serverless-with-lambda-and-api-gateway"&gt;Serverless with Lambda and API Gateway&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The services above have their limitations when it comes to scaling capability. First, they cannot scale to zero. You still pay for idling resources. Also, it is not easy to find the optimal autoscaling setting. Lambda and API Gateway together solves these challenges. AWS refers to it as serverless, which has since become a buzzword. To understand what it is, let&amp;#8217;s examine two concepts:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;strong&gt;Function as a Service&lt;/strong&gt;: service with the ability to execute code on demand. Users only pay for code execution time and do not care where the underlying runtime is&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Backend as a Service&lt;/strong&gt;: service with the ability to listen to a port and respond to web request&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Lambda itself is a function as a service. Triggered by events, it only incurs a charge when it&amp;#8217;s invoked. It does not stay up and listening to a TCP port for incoming web request, as does a backend service. In order to act as a backend, Lambda needs to pair up with API gateway. In this configuration, API gateway listens to a web request, and it fires an event to trigger the execution of Lambda function. Lambda and API gateway together makes a backend as a service. In AWS, the coupling of API gateway and Lambda function ensures an idle service does not incur computing cost.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since Lambda supports many types of events as trigger, it is also used in event-driven architecture, either standalone or from a VPC. Under the hood, Lambda runs code in a container (with a quick startup time relative to a VM).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Developers can release Lambda code by uploading zip package to S3 bucket, or just packaging code into container image. For deployment, apart from AWS console and CLI, one can leverage CloudFormation, SAM (serverless application model), or CDK.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Lambda vs Fargate&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both Lambda and Fargate are serverless capabilities, at least from a marketing perspective. Both can be used to back web service but there are differences. They provision computing resource at different granularity. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In a web service, the execution duration of a Lambda function is the response duration to an API request, in terms of seconds. While the server is waiting for a request, there is no usage of the computing resource so you&amp;#8217;re not paying for waiting for a request. However, this also creates the delay of cold-start, especially when the code size is large. There are several ways to optimize the cold start (e.g. &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/snapstart.html"&gt;SnapStart&lt;/a&gt; for Java), but none of those can completely get rid of the cold-start delay with a once-after-a-while request. A light GET call could take 5 seconds with cold start. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;On the Fargate side, the resource provisioning is based on container lifecycle, instead of request lifecycle. As a result, you&amp;#8217;re still paying for wait time, and it is not per-request billing. Since the container remains up, your request is not going to experience the cold-start if it&amp;#8217;s been idle for a while. Although, Fargate saves you from the effort to right-sizing the computing nodes for container execution, it is not quite the idea of &amp;#8220;scale-to-zero when idle&amp;#8221; by itself. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Serverless Architecture&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the white paper &lt;a href="https://docs.aws.amazon.com/whitepapers/latest/serverless-multi-tier-architectures-api-gateway-lambda/welcome.html"&gt;AWS Serverless Multi-Tier Architectures with Amazon API Gateway and AWS Lambda&lt;/a&gt;, AWS advocates the serverless architecture as a modern alternative to the traditional widely adopted three-tier architecture (presentation, logic and data tiers). In the three tier architecture, the scalability of three tier are managed separately. The modern serverless architecture that AWS whitepaper proposes uses API Gateway and Lambda function in place of Load Balancer and EC2 instances (e.g. in an Auto Scaling Group), as illustrated below:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="432" height="345" src="https://static.digihunch.com/wp-content/uploads/2022/08/serverless.png" alt="" class="wp-image-7170"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Both API Gateway and Lambda scale automatically to support the need of application workload. It assumes the role of logic tier in three-tier architecture but requires minimal maintenance work. For presentation tier, AWS has serverless alternatives such as CloudFront, S3. For data tier, AWS has serverless alternatives such as Amazon Aurora for relational database and DynamoDB for NoSQL. However, the &amp;#8220;no request, no pay&amp;#8221; model for Lambda does not apply to the data tier in serverless architecture.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Whilst this paradigm benefits small shop IT who wants to minimize infrastructure cost, it has downsides. There is no ability for infrastructure optimization. Since you do not manage where the code runs, client may have concerns over security (e.g. multi-tenant runtime). As business grows, keep using Lambda can result in technology lock-in. Also, a less used application usually requires warm-up time. A code start (downloading the code and preparing the environment behind the scene) can take 100ms to over a second.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-conclusion"&gt;Conclusion&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;PaaS attempts to help startups simplify the &amp;#8220;grunt work&amp;#8221; of IT operation. Serverless takes it even further. The semantics of serverless computing is confusing and the &lt;a href="https://en.wikipedia.org/wiki/Serverless_computing"&gt;Wikipedia&lt;/a&gt; page acknowledges it as a misnomer. The nature of serverless model, is the cloud users delegate server capacity management to cloud platforms. The users don&amp;#8217;t need to manage servers, VMs, instances, containers, etc on their own. In a &lt;a href="https://static.digihunch.com/2022/04/knative-introduction-serving/"&gt;previous post&lt;/a&gt;, I discussed the ability to scale to zero, which is just one of the many enabling technologies of serverless. Also, &amp;#8220;no request, no pay&amp;#8221; is neither an inherent nature of serverless model. Serverless service may involve storage (e.g. data service, S3, Aurora serverless) which incurs storage cost. There is a &lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/website-deployment-services/welcome.html"&gt;whitepaper&lt;/a&gt; on choosing the right AWS service to deploy your website or web application, with a decision tree. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS pioneered serverless with Lambda release in 2014 but competitor follows. In the Azure landscape, there is an entire suite of computing services from virtual machine to serverless (also with a &lt;a href="https://docs.microsoft.com/en-us/azure/architecture/guide/technology-choices/compute-decision-tree"&gt;decision tree&lt;/a&gt; in documentation). Azure&amp;#8217;s counterpart for &lt;a href="https://azure.microsoft.com/en-ca/solutions/serverless/"&gt;serverless architecture&lt;/a&gt; is Azure Function (released in 2016 for GA) with API Management. As for GCP, the &lt;a href="https://cloud.google.com/serverless"&gt;serverless&lt;/a&gt; suite includes the event-driven Cloud Function (introduced in 2017) and Knative-based FaaS Cloud Run (introduced in 2019).&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are &lt;a href="https://thenewstack.io/serverless-needs-standards-to-be-the-future-of-application-infrastructure"&gt;voices&lt;/a&gt; in advocacy of standardization of serverless model, and CNCF had since made minuscule efforts such as &lt;a href="https://cloudevents.io/?utm_source=thenewstack&amp;amp;utm_medium=website&amp;amp;utm_campaign=platform"&gt;CloudEvents&lt;/a&gt;. The status quo, unfortunately, is anything but standardized.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Lastly, here is a table that summarizes the pros and cons of each computing service model.&lt;/p&gt;&#10;&lt;figure class="wp-block-table"&gt;&lt;table class="has-fixed-layout"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Computing Service Model&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Pro&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;Con&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;EC2&lt;/td&gt;&lt;td&gt;&amp;#8211; Most straightforward and widespread legacy model&lt;br&gt;&amp;#8211; Legacy&lt;/td&gt;&lt;td&gt;&amp;#8211; Ops tasks can be heavy (e.g. patch and vulnerability management of OS)&lt;br&gt;&amp;#8211; Utilization can be low&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;ECS&lt;/td&gt;&lt;td&gt;&amp;#8211; Container orchestration is managed&lt;br&gt;&amp;#8211; Convenient to scale&lt;br&gt;&amp;#8211; Well integrated with other AWS services&lt;/td&gt;&lt;td&gt;&amp;#8211; Limited advanced features&lt;br&gt;&amp;#8211; Vendor lock-in&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;EKS&lt;/td&gt;&lt;td&gt;&amp;#8211; Highly scalable and flexible&lt;br&gt;&amp;#8211; Advanced, platform-neutral deployment tools available (e.g. Helm, ArgoCD, etc)&lt;br&gt;&amp;#8211; Custom configurations (e.g. operators)&lt;/td&gt;&lt;td&gt;&amp;#8211; Significant operation overhead&lt;br&gt;&amp;#8211; Steep learning curve (especially for teams)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Lambda&lt;/td&gt;&lt;td&gt;&amp;#8211; automatically scale&lt;br&gt;&amp;#8211; low ops overhead&lt;br&gt;&amp;#8211; pay per use&lt;/td&gt;&lt;td&gt;&amp;#8211; limited choices of runtime&lt;br&gt;&amp;#8211; subject to latency due to cold start; yet warm start incurs cost&lt;br&gt;&amp;#8211; not suitable for long running tasks (batch processing jobs etc)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In summary, the Lambda-based serverless model is good for stateless server-side workload with short response time (&amp;lt;15s), tolerance of cold-start, no need for portability across platforms, and no complex package dependency. &lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/10/graphql-and-grpc/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;GraphQL and gRPC&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/11/aws-serverless-services-and-developer-tools/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;AWS serverless services and developer tools&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>GraphQL and gRPC</title><link>https://static.digihunch.com/2022/10/graphql-and-grpc/</link><pubDate>Fri, 07 Oct 2022 14:49:00 -0400</pubDate><guid>https://static.digihunch.com/2022/10/graphql-and-grpc/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-graphql-grpc.webp" alt="Featured image of post GraphQL and gRPC" /&gt;&lt;h2 class="wp-block-heading" id="h-big-picture"&gt;Big Picture&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For inter-process communication at a high level, the two styles are asynchronous and synchronous styles:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Asynchronous event-driven style: involving an event broker as a middle man. &lt;/li&gt;&#10;&lt;li&gt;Synchronous request-response style: including several families of technologies:&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Remote_procedure_call"&gt;RPC&lt;/a&gt; (Remote Procedure Call):&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Common_Object_Request_Broker_Architecture"&gt;CORBA&lt;/a&gt; (Common Object Request Broker Architecture)&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Java_remote_method_invocation"&gt;Java RMI&lt;/a&gt; (Remote Method Invocation)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/SOAP"&gt;SOAP&lt;/a&gt; (Simple Object Access Protocol)&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Representational_state_transfer"&gt;REST&lt;/a&gt; (Representational State Transfer)&lt;/li&gt;&#10;&lt;li&gt;gRPC&lt;/li&gt;&#10;&lt;li&gt;GraphQL&lt;/li&gt;&#10;&lt;li&gt;Apache Thrift&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;RPCs, built on top of TCP/UDP, are usually complex to implement. SOAP improved it and can operate on HTTP. Many large companies today still used SOAP for message exchange. However, it has a limitation with complex format and specifications for XML messaging, giving rise to REST. REST is not a standard, but rather a loosely defined architectural style. Its payload can be in any format (XML, JSON, etc) specified in the header. As long as the API conforms to certain guidelines (criteria outlined in &lt;a href="https://www.redhat.com/en/topics/api/what-is-a-rest-api"&gt;this&lt;/a&gt; article), we can consider the API RESTful. REST has been steadily replacing &lt;a href="https://www.redhat.com/en/topics/integration/whats-the-difference-between-soap-rest"&gt;SOAP&lt;/a&gt; in the past few years. In this post, I start with REST, then dive into gRPC and GraphQL.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;REST&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The de facto method of building microservices using REST architectural style is use HTTP protocol with JSON format payload. JSON format is human readable, but not optimized for machine-to-machine communication. So there is some room for &lt;a href="https://nilsmagnus.github.io/post/proto-json-sizes/"&gt;compression&lt;/a&gt;. To emulate a web request in REST, one can use curl, a common utility to emulate any HTTP client.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;REST has its shortcomings. For example, the interface between REST client and server is not strongly typed. You can choose to use &lt;a href="https://swagger.io/specification/"&gt;OpenAPI/Swagger&lt;/a&gt; specification to define types but it is still not tightly integrated. There is no enforcement on the format of the payload either. RESTful services are quite bulky, inefficient, and error-prone. gRPC and GraphQL emerged to address different challenges with REST. GraphQL operates on HTTP and we can view it as a &lt;a href="https://www.apollographql.com/blog/backend/layering-graphql-on-top-of-rest/"&gt;layer&lt;/a&gt; on top of REST in a broad sense. gRPC on the other hand, operates on HTTP /2, and thereby inherits many advantages from it.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;HTTP /2&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;HTTP/2 is the second major version of HTTP. It overcomes some issues with HTTP/1.1 on security, speed, etc. &lt;a href="https://www.digitalocean.com/community/tutorials/http-1-1-vs-http-2-what-s-the-difference"&gt;This&lt;/a&gt; post is a good rundown of the difference between HTTP /2 and HTTP /1.1, which account for many of the advantages of gRPC. A thorough discussion on the differences between the two HTTP versions is beyond what this post can cover. One of the important difference with HTTP/2, is that all communication between a client and server is performed over a single TCP connection that can carry any number of bidirectional flows of bytes. This makes gRPC a high-performance RPC framework. In HTTP/2, the key concepts to understand are: &lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Stream: a bidirectional flow of bytes within an established connection. A stream may carry one or more messages;&lt;/li&gt;&#10;&lt;li&gt;Frame: the smallest unit of communication in HTTP/2. Each frame contains a frame header, which at a minimum identifies the stream to which the frame belongs. &lt;/li&gt;&#10;&lt;li&gt;Message: a complete sequence of frames that map to a logical HTTP message that consists of one or more frames. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The request message is always triggered by the client. During the interaction, the client and server break down the message into frames, interleave them, and then reassemble them on the other side. In this way HTTP /2 multiplex the messages, and enables the following communication patterns:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Simple RPC: a single request and a single response in the communication;&lt;/li&gt;&#10;&lt;li&gt;Server Streaming RPC: a single request and message followed by multiple response messages;&lt;/li&gt;&#10;&lt;li&gt;Client streaming RPC: client sends multiple messages and the server replies with one response message;&lt;/li&gt;&#10;&lt;li&gt;Bi-directional RPC: client setups connection by sending header frames. Once connection is established, both client and server send messages simultaneously without waiting for the other to finish;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The streaming communication patterns fundamentally improves performance, enabling the duplex streaming capability for gRPC.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;gRPC&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;At the protocol level, gRPC has the following advantages over REST:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;well-defined service interface and schema&lt;/li&gt;&#10;&lt;li&gt;strongly typed data&lt;/li&gt;&#10;&lt;li&gt;duplex streaming (thanks to HTTP2)&lt;/li&gt;&#10;&lt;li&gt;built-in commodity features (e.g. authentication, encryption, resiliency, service discovery, etc)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I previously touched on gRPC protocol in the context of &lt;a href="https://static.digihunch.com/2022/03/from-nginx-to-envoy-proxy/"&gt;Envoy&lt;/a&gt; and &lt;a href="https://static.digihunch.com/2022/06/etcd-the-key-value-store-for-kubernetes/"&gt;etcd&lt;/a&gt;. Envoy makes use of gRPC for its control plane, where it&amp;nbsp;&lt;a href="https://www.envoyproxy.io/docs/envoy/latest/configuration/overview/overview#config-overview"&gt;fetches configuration from management server(s)&lt;/a&gt;&amp;nbsp;and in filters, such as for&amp;nbsp;&lt;a href="https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/rate_limit_filter#config-http-filters-rate-limit"&gt;rate limiting&lt;/a&gt;&amp;nbsp;or authorization checks. Etcd store implements gRPC protocol for client utility to communicate with. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since the typical use case of gRPC is internal communication, I have not been able to find a playground online. To get a taste of gRPC client-server interaction, just play with &lt;a href="https://etcd.io/docs/v3.5/tutorials/"&gt;etcd&lt;/a&gt; on MacOS. gRPC is language neutral. To start developing, refer to the tutorial in different languages (e.g. &lt;a href="https://grpc.io/docs/languages/go/basics/"&gt;Golang&lt;/a&gt;, &lt;a href="https://grpc.io/docs/languages/python/quickstart/"&gt;Python&lt;/a&gt;). When developing a gRPC application, the first thing to do is define a service interface in IDL (interface definition language). gRPC uses &lt;a href="https://developers.google.com/protocol-buffers"&gt;protocol buffers&lt;/a&gt; as the IDL to define the service interface. Protocol buffers are a language-agnostic, platform-neutral, extensible mechanism to serializing structured data. Using that service interface definition, we can generate the server-side code known as a server skeleton. Also you can generate the client-side code, known as a client stub. The methods that you specify in the service interface definition can be remotely invoked by the client side as easily as making a local function invocation. &lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1052" height="503" src="https://static.digihunch.com/wp-content/uploads/2022/08/image-5.png" alt="" class="wp-image-6910"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;gRPC also has some disadvantages. Currently, the ecosystem is still small. When we have a service interface, we have to maintain the interface across versions.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;GraphQL&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In most of the use cases of gRPC and GraphQL, GraphQL works for external-facing services/APIs while internal services backing the APIs are implemented using gRPC. Honeypot created good documentary for GraphQL available &lt;a href="https://www.youtube.com/watch?v=783ccP__No8"&gt;here&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We use GraphQL for external facing services because it gives API client the ability to query. An SQL query allows client to filter requested data based on conditions, and define the interested columns in the data return. This capability is missing in the REST style guideline. One may choose to implement their RESTful service to support their client&amp;#8217;s query requirement, GraphQL standardizes this capability with typed data, thereby prevents unnecessary network round trips, over- and under-fetching of data.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;a href="https://graphql.org/learn/"&gt;official site&lt;/a&gt; is a good reference for learning. One needs to know concepts around queries and mutations, schema and types (scalars, variable, fragment, interfaces, unions) to understand how query works.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://ivangoncharov.github.io/graphql-apis/"&gt;This&lt;/a&gt; page lists a number of publicly available services in GraphQL. For example, country information service is available &lt;a href="https://countries.trevorblades.com/"&gt;here&lt;/a&gt;. On the web page you can put in a query like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;query&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;myCountry&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;countries&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;filter&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;code&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;{&lt;span style="color:#66d9ef"&gt;in&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;CA&amp;#34;&lt;/span&gt;}}) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;code&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;capital&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The query (with the name of myCountry) above asks to return entries with countries as type. Then it filters the result by the condition that the country code must include &amp;#8220;CA&amp;#8221;. The return should include code, name and capital columns. The web page looks like this:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1073" height="376" src="https://static.digihunch.com/wp-content/uploads/2022/08/image-3.png" alt="" class="wp-image-6853"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;GraphQL is on HTTP, so I can emulate the call with curl and get the same result:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl --request POST https://countries.trevorblades.com/ &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;--header &lt;span style="color:#e6db74"&gt;&amp;#39;Content-Type: application/json&amp;#39;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;--data-raw &lt;span style="color:#e6db74"&gt;&amp;#39;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;#34;query&amp;#34; : &amp;#34;query myCountry { countries (filter: { code: { in: \&amp;#34;CA\&amp;#34; } }) {code name capital} }&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;}&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The return prints the same result:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="2442" height="318" src="https://static.digihunch.com/wp-content/uploads/2022/08/image-4.png" alt="" class="wp-image-6854"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The document has a &lt;a href="https://graphql.org/code/"&gt;page&lt;/a&gt; on library options for different languages, such as Graphene and Ariadne for Python. You can use Ariadne and Flask to build GraphQL API as &lt;a href="https://www.twilio.com/blog/graphql-api-python-flask-ariadne"&gt;this&lt;/a&gt; post suggests.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The example on &lt;a href="https://www.howtographql.com/basics/1-graphql-is-the-better-rest/"&gt;this&lt;/a&gt; page has a better example that highlights how GraphQL allows client to ask multiple questions at the same time, and how the response includes different pieces of answers. GraphQL backend often needs to connect to different downstream system. This requires a proxy service to connect to disparate data sources. Examples of such services with such capabilities include &lt;a href="https://www.apollographql.com/docs/"&gt;Apollo&lt;/a&gt;, AWS &lt;a href="https://aws.amazon.com/appsync/"&gt;AppSync&lt;/a&gt;, or &lt;a href="https://cloud.google.com/blog/products/api-management/interacting-with-apis-rest-and-graphql"&gt;ApiGee&lt;/a&gt;. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For API protocols, we often compare among REST, gRPC and GraphQL. REST is loosely defined and widely adopted for the past few years. The use case of REST diverged into two areas: external facing API and internal service-to-service communication. gRPC and GraphQL are relatively new and they each are good for one of the use cases.&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1051" height="696" src="https://static.digihunch.com/wp-content/uploads/2022/08/image-6.png" alt="" class="wp-image-6915"/&gt;&lt;figcaption class="wp-element-caption"&gt;Microservice pattern based on gRPC and GraphQL (source: &amp;#8220;gRPC up &amp;amp; running&amp;#8221; by Indrasiri &amp;amp; Kuruppu)&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The gRPC protocol on HTTP2 is often used in internal communication between &lt;a href="https://thenewstack.io/build-real-world-microservices-with-grpc/"&gt;microservices&lt;/a&gt;. GraphQL offers query capability and is therefore often used to face external client.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/09/build-a-kubernetes-cluster/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Build and Manage Kubernetes Clusters&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/10/computing-from-paas-to-serverless/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Computing services: from PaaS to Serverless&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Service Proxy – from Nginx to Envoy</title><link>https://static.digihunch.com/2022/03/from-nginx-to-envoy-proxy/</link><pubDate>Wed, 09 Mar 2022 21:36:00 -0400</pubDate><guid>https://static.digihunch.com/2022/03/from-nginx-to-envoy-proxy/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-nginx2envoy.webp" alt="Featured image of post Service Proxy – from Nginx to Envoy" /&gt;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Update&lt;/strong&gt; (Nov 20, 2022): 1. Envoy&amp;#8217;s configuration schema can be hard to get used to. It is lacking examples because the documentation is mostly generated. Use its &lt;a href="https://github.com/envoyproxy/examples"&gt;examples&lt;/a&gt; directory to find real-life configuration examples. 2. the Envoy implementation in the example project has been reverted in favour of Nginx.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Envoy proxy is the underlying technology for Istio, as well as a number of other service mesh products, such as AppMesh (AWS), Consul (Hashicorp) and OpenServiceMesh (Azure). Most of the capabilities of Isito is ultimately provided by Envoy proxy. Envoy has a &lt;a href="https://www.envoyproxy.io/docs/envoy/v1.10.0/intro/comparison"&gt;page&lt;/a&gt; outlining its differences with similar technologies. I decided to take a look into Envoy by replacing Nginx with it.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-rate-limiting-and-circuit-breaker"&gt;Rate limiting and Circuit Breaker&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In most SDLC, it is application developers that create backend APIs or server applications. Most developers specializes in application features, and cannot fathom all the nuances with the TCP/IP network stack. Nginx allows them to push network concerns (non-business features) to a dedicated proxy to handle the dynamics in network connection. Nginx can be configured as both a reverse proxy (handling incoming connection on behalf of the process) and a forward proxy (handling outgoing connection on behalf of the process). This is the prototype of sidecar pattern, an important idea behind service mesh. For example, when a sudden increase in connection to the server-side application, the server process could be either unresponsive (refer to &amp;#8220;the &lt;a href="https://queue.acm.org/detail.cfm?id=1854041"&gt;queuing knee&lt;/a&gt;&amp;#8220;, and &lt;a href="https://en.wikipedia.org/wiki/Little%27s_law"&gt;Little&amp;#8217;s Law&lt;/a&gt;), or OOM killed. When such interruptions are not automatically recovered, a downtime is caused. This traditionally requires some congestion control strategy for TCP/IP queue but two features provided by a network proxy can help circumvent this situation: rate limiting, and circuit breaking. Rate limiting keeps more requests above threshold from entering the queue. Circuit breaker releases downstream pressure by cutting out existing in-queue request. Nginx added both over the years but &lt;a href="https://www.nginx.com/blog/microservices-reference-architecture-nginx-circuit-breaker-pattern/"&gt;Circuit breaker&lt;/a&gt; still remains a premium feature exclusive to Nginx Plus. Envoy on the other hand has them free when it was launched.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Envoy also supports other advanced traffic management such as traffic shaping, and mirroring. It is on top of those features that Istio introduces its own abstraction such as virtual service, destination rules to its users. In that sense, we can think of Istio as a configurator (control plane) for Envoy proxy (data plane), similar to Ansible to Nginx proxy instances.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-dynamic-configuration-via-api"&gt;Dynamic Configuration via API&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I used Nginx previously with traditional environment and loved its flexibility. As the system grows, I started to feel the pain of management overhead. With one of the production system, there were 25 + instances of Nginx each running on a VM and I managed configuration files with Ansible. Ansible pushes out configuration files and triggers a reload from each Nginx instance. In the cloud-native era where Pods are ephemeral, this kind of overhead would snowball to an unmanageable level. Envoy was designed for cloud-native applications, with all these kinds of problems in mind. Envoy has dynamic configuration. The majority of the configurations can be pulled from &lt;a href="https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/operations/dynamic_configuration"&gt;xDS API&lt;/a&gt;, or file system. Updating configuration drains connections gracefully without runtime having to reload the file. The idea of centrally managing Nginx instances with Ansible, also evolved into the concept of control plane.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-tls-origination"&gt;TLS origination&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the Orthweb project, I used Nginx to proxy TLS and HTTP traffic, and performed TLS termination on both ports. This is know as TLS offloading. The traffic between the proxy and the upstream service takes place in the clear, even though they do not travel across different network interfaces in most cases. For a true end-to-end encryption, it is helpful to also encrypt the traffic between proxy and upstream server. This requires the capability of securing TCP traffic to upstream server. With Nginx, the ability to &lt;a href="https://dzone.com/articles/nginx-rate-limiting"&gt;secure HTTP traffic to upstream&lt;/a&gt; server is offered in open-source. The ability to &lt;a href="https://docs.nginx.com/nginx/admin-guide/security-controls/securing-tcp-traffic-upstream/"&gt;secure TCP traffic&lt;/a&gt; is available in Nginx Plus, or with self-compiled binary. In Envoy, both are available using the UpstreamTlsContext configuration.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-more-pros"&gt;More pros&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In another &lt;a href="https://static.digihunch.com/2020/01/nginx-as-a-reverse-proxy-for-nifi/"&gt;post&lt;/a&gt;, I also discussed Nginx as a LDAP proxy to front services such as Kibana and Nifi. It requires a proxy service (ldap-auth in this case), to defer auth to third party. Envoy has this capability using a &lt;a href="https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter"&gt;filter&lt;/a&gt; with extension for external authorization. Istio also exposes this capability, an enabler for the configuration proposed in my previous &lt;a href="https://static.digihunch.com/2022/02/istio-external-authorization/"&gt;post&lt;/a&gt;. Envoy also uses WebAssembly for its extensibility.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Another useful feature is protocol detection. It can use filters to detect protocol (TLS or regular TCP) and route traffic to predefined destination.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Performance wise, this &lt;a href="https://www.loggly.com/blog/benchmarking-5-popular-load-balancers-nginx-haproxy-envoy-traefik-and-alb/"&gt;benchmark&lt;/a&gt; from 2018 ran a comparison among the popular options where Envoy leads by a margin.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Observability (logging, metrics and tracing) are well supported in Envoy. User can configure format of logs that takes effect immediately. There are many metrics that works with Prometheus and they are expandable using filters. On the tracing side, Envoy supports integration with jaeger, zipkin and datadog.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-basics-of-envoy"&gt;Basics of Envoy&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The configuration of Envoy is more involving. There is an Envoy course by &lt;a href="https://academy.tetrate.io/"&gt;Tetrate&lt;/a&gt;, as well as two blog entries for envoy 101: Envoy as &lt;a href="https://www.tetrate.io/blog/envoy-101-configuring-envoy-as-a-gateway/"&gt;gateway proxy&lt;/a&gt; and File-based &lt;a href="https://www.tetrate.io/blog/envoy-101-file-based-dynamic-configurations/"&gt;dynamic configuration&lt;/a&gt;. Another good way to get started is the Sandboxes &lt;a href="https://www.envoyproxy.io/docs/envoy/latest/start/sandboxes/#start-sandboxes"&gt;projects&lt;/a&gt;, which covers a number of different areas of configuration. The admin port (by default at port 9901. Istio&amp;#8217;s default is 15000) provides helpful information. If we need to turn on debug on some features, we can do so with curl:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X POST http://localhost:9901/logging?client&lt;span style="color:#f92672"&gt;=&lt;/span&gt;debug&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Stats are exposed at the same port:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -X GET http://localhost:9901/stats&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;When packets are received at a listener, the is first processed by listener filters. Then, depending on filter match, one or more network filter chains will further process the packet, including further actions, as illustrated below:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-large"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="726" src="https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-1024x726.png" alt="" class="wp-image-11357" srcset="https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-1024x726.png 1024w, https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-300x213.png 300w, https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-768x544.png 768w, https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters-1536x1089.png 1536w, https://static.digihunch.com/wp-content/uploads/2024/07/envoy-listener-filters.png 1920w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Envoy supports dynamic configuration, which uses a set of discovery services (xDS) APIs. Some of the important xDS APIs include:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;LDS (Listener Discovery Service) &amp;#8211; allows you to add listeners dynamically while Envoy is running&lt;/li&gt;&#10;&lt;li&gt;RDS (Route Discovery Service) &amp;#8211; allows you to dynamically update routes for HTTP connection managers&lt;/li&gt;&#10;&lt;li&gt;CDS (Cluster Discovery Service) &amp;#8211; allows you to update cluster definitions dynamically&lt;/li&gt;&#10;&lt;li&gt;EDS (Endpoint Discovery Service) &amp;#8211; allows you to add or remove endpoints dynamically&lt;/li&gt;&#10;&lt;li&gt;Secret DS&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The relation can be illustrated in this diagram below:&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://i0.wp.com/www.tetrate.io/wp-content/uploads/2020/11/Screen-Shot-2020-11-18-at-12.17.08-PM-1.png?resize=1044%2C638&amp;amp;ssl=1" alt="Envoy - xDS configuration API overview"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This &lt;a href="https://www.tetrate.io/blog/envoy-101-file-based-dynamic-configurations/"&gt;post&lt;/a&gt; from Tetrate has more examples.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-nginx-to-envoy"&gt;Nginx to Envoy &lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For the advantages of Envoy, I decided to migrate from Nginx to Envoy on my &lt;a href="https://github.com/digihunch/orthweb"&gt;Orthweb&lt;/a&gt; project. Using Envoy as service proxy is not where Envoy is mostly used (as sidecar), but it is how Envoy was originally used at Lyft to replace ELB in 2015.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The original Nginx configuration was referenced in this &lt;a href="https://static.digihunch.com/2020/11/medical-imaging-web-server-deployment-pipeline/"&gt;old blog post&lt;/a&gt;. The Envoy setup also covers both TCP (&lt;a href="https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/"&gt;DICOM&lt;/a&gt;) and HTTP (HTTPS) traffic. For HTTP traffic, it also encrypts the traffic to upstream. Below is what it looks like:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;admin&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;: { &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#f92672"&gt;0.0.0.0, port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;9901&lt;/span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;static_resources&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;listeners&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https_listener&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0.0.0.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;443&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;filter_chains&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;filters&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.filters.network.http_connection_manager&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;codec_type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;AUTO&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;stat_prefix&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ingress_http&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;route_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;local_route&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;virtual_hosts&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;app&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;domains&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;routes&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;match&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;prefix&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;route&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;http_filters&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.filters.http.router&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;transport_socket&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.transport_sockets.tls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;common_tls_context&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;tls_certificates&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;certificate_chain&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;private_key&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;dicomtls_listener&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;0.0.0.0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;11112&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;filter_chains&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;filters&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.filters.network.tcp_proxy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;stat_prefix&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;downstream_cx_total&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-dicomtls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;transport_socket&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.transport_sockets.tls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;common_tls_context&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;tls_certificates&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;certificate_chain&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;private_key&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validation_context&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;allow_expired_certificate&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;trusted_ca&lt;/span&gt;: {&lt;span style="color:#f92672"&gt;&amp;#34;filename&amp;#34;: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/etc/ssl/certs/site.pem&amp;#34;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;require_client_certificate&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;clusters&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;STRICT_DNS&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;lb_policy&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ROUND_ROBIN&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;load_assignment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-https&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;endpoints&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;lb_endpoints&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;endpoint&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;orthanc-backend&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;8042&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;transport_socket&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;envoy.transport_sockets.tls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;typed_config&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;@type&amp;#34;: &lt;/span&gt;&lt;span style="color:#ae81ff"&gt;type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-dicomtls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;type&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;STRICT_DNS&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;lb_policy&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ROUND_ROBIN&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;load_assignment&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;cluster_name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;service-dicomtls&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;endpoints&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;lb_endpoints&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;endpoint&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;socket_address&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;address&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;orthanc-backend&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;port_value&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;4242&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layered_runtime&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;layers&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;static_layer_0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;static_layer&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;envoy&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;resource_limits&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;listener&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;https_listener&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;connection_limit&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;overload&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;global_downstream_max_connections&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;5000&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;From Nginx to Envoy, to achieve nearly the same functionalities, it takes 100 lines of configuration instead of less than 30. The configuration also appears more abstract, which is one of the cons of Envoy to consider before the migration.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Update from Jan 2025 &amp;#8211; To align with what most uses in Orthanc community, the project switched back to using Nginx. The envoy configuration is kept &lt;a href="https://gist.github.com/digihunch/e3192481f0a54e018442d9629562d40f"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/02/istio-external-authorization/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio External Authorization via OIDC&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/03/istio-operation-gotchas/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio Operation Gotchas&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Istio Lab – Authentication and Authorization</title><link>https://static.digihunch.com/2022/02/istio-lab-authentication-and-authorization-in-jwt/</link><pubDate>Sun, 13 Feb 2022 13:21:13 -0400</pubDate><guid>https://static.digihunch.com/2022/02/istio-lab-authentication-and-authorization-in-jwt/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-istio-lab.webp" alt="Featured image of post Istio Lab – Authentication and Authorization" /&gt;&lt;p class="wp-block-paragraph"&gt;My previous &lt;a href="https://static.digihunch.com/2022/02/authentication-and-authorization-with-istio/"&gt;blog&lt;/a&gt; discussed as service mesh what Istio can offer in terms of authentication and authorization capabilities. Istio can authenticate an incoming HTTP request, ensuring the JWT issued has not been tampered somewhere in the middle. The fields in the JWT allows for more flexibilities at the point of authorization. This combination allows Istio to integrate with identity providers that can issue JWT.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We&amp;#8217;ve also discussed how JWT works, and pointed out that the two key element of request authentication is the JWT (payload signed with private key) itself as well as the JWK (carrying public key). In this post, we will test it in a lab. To start with this lab, we need a test cluster (e.g. Minikube) with Istio &lt;a href="https://github.com/digihunch/korthweb/tree/main/manual"&gt;installed&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="preparation"&gt;Preparation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I have not find a native Bash way to produce JWT. We will do that with python packages python_jwt and jwcrypto in Python3. Let&amp;#8217;s install the modules and import them in Python environment.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python3 -m pip install python_jwt jwcrypto datetime&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ python3&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt; import python_jwt as jwt, jwcrypto.jwk as jwk, datetime&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we&amp;#8217;re in the Python3 shell with needed modules loaded. We can take the following steps to produce the JWT as well as the JWK:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;RSAkey &lt;span style="color:#f92672"&gt;=&lt;/span&gt; jwk&lt;span style="color:#f92672"&gt;.&lt;/span&gt;JWK&lt;span style="color:#f92672"&gt;.&lt;/span&gt;generate(kty&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;RSA&amp;#39;&lt;/span&gt;, size&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2048&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;private_key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; RSAkey&lt;span style="color:#f92672"&gt;.&lt;/span&gt;export_private()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;public_key &lt;span style="color:#f92672"&gt;=&lt;/span&gt; RSAkey&lt;span style="color:#f92672"&gt;.&lt;/span&gt;export_public()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;raw_payload &lt;span style="color:#f92672"&gt;=&lt;/span&gt; {&lt;span style="color:#e6db74"&gt;&amp;#39;iss&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;digihunch.com&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;sub&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;DIGIHUNCH&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;role&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;reader&amp;#39;&lt;/span&gt;,&lt;span style="color:#e6db74"&gt;&amp;#39;permission&amp;#39;&lt;/span&gt;:&lt;span style="color:#e6db74"&gt;&amp;#39;read&amp;#39;&lt;/span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## use private key to generate jwt token. HTTP request will bear this token &lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jwt_token &lt;span style="color:#f92672"&gt;=&lt;/span&gt; jwt&lt;span style="color:#f92672"&gt;.&lt;/span&gt;generate_jwt(raw_payload, jwk&lt;span style="color:#f92672"&gt;.&lt;/span&gt;JWK&lt;span style="color:#f92672"&gt;.&lt;/span&gt;from_json(private_key), &lt;span style="color:#e6db74"&gt;&amp;#39;RS256&amp;#39;&lt;/span&gt;, datetime&lt;span style="color:#f92672"&gt;.&lt;/span&gt;timedelta(minutes&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;))&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(jwt_token)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## The JWKS keeps public key and is referenced by Istio RequestAuthentication object&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;jwks&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{&amp;#34;keys&amp;#34;:[&amp;#39;&lt;/span&gt;&lt;span style="color:#f92672"&gt;+&lt;/span&gt;public_key&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;]}&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(jwks)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Helpful command to print the key in PEM format:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## RSAkey.export_to_pem(private_key=False)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Helpful command to verify JWT token:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## header, claims = jwt.verify_jwt(jwt_token, jwk.JWK.from_json(public_key), [&amp;#39;RS256&amp;#39;])&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To complete this lab, we need the values of &lt;em&gt;jwt_token&lt;/em&gt; and &lt;em&gt;jwks&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Next in the preparation is a local &lt;a href="https://static.digihunch.com/2021/09/single-node-kubernetes-cluster-minikube/"&gt;cluster&lt;/a&gt;, istio with metallb installed, which is covered in a previous &lt;a href="https://static.digihunch.com/2021/11/istio-ingress-egress/"&gt;post&lt;/a&gt;. We should be able to get the ingress IP address:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ export INGRESS_HOST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl -n istio-system get service istio-ingressgateway -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.status.loadBalancer.ingress[0].ip}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ echo $INGRESS_HOST&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We create our own namespace and use the &lt;a href="https://github.com/istio/istio/tree/master/samples/httpbin"&gt;httpbin&lt;/a&gt; application:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl create ns web &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl label namespace web istio-injection&lt;span style="color:#f92672"&gt;=&lt;/span&gt;enabled&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl apply -n web -f httpbin.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl apply -n web -f httpbin-gateway.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl -I -XGET $INGRESS_HOST/headers&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The curl command above (without any token) should return HTTP 200 code, indicating that no authorization token is needed to connect to the service. Note that the Pod for httpbin has the label &lt;em&gt;app=httpbin&lt;/em&gt; which will be used in the request authentication. Also note that traffic is served over a named port called http in the Service object for http, which will implicitly enable HTTP based conditions for authorization policies we will build later. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="request-authentication"&gt;Request Authentication&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s create a request authentication object with the following manifest:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;security.istio.io/v1beta1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;RequestAuthentication&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;jwt-req-authn&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;web&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;jwtRules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;issuer&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;digihunch.com&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;jwks&lt;/span&gt;: |&lt;span style="color:#e6db74"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; ## jwks output from previous step ##&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Replace the last line with the jwks output from the preparation step and store it to jwt-req-authn.yaml. It should look like this:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="1589" height="386" src="https://static.digihunch.com/wp-content/uploads/2022/02/image-1.png" alt="" class="wp-image-3442"/&gt;&lt;figcaption class="wp-element-caption"&gt;jwt-req-authn.yaml&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Then apply it to the web namespace:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n web apply -f jwt-req-authn.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now the request authentication resource is applied to the httpbin workload. We first test it with a random authentication token and it should be denied of 401 error:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl -I -XGET $INGRESS_HOST/headers --header &lt;span style="color:#e6db74"&gt;&amp;#34;Authorization: Bearer randomstring&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTP/1.1 &lt;span style="color:#ae81ff"&gt;401&lt;/span&gt; Unauthorized&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;www-authenticate: Bearer realm&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;http://192.168.64.16/headers&amp;#34;&lt;/span&gt;, error&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;invalid_token&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-length: &lt;span style="color:#ae81ff"&gt;79&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-type: text/plain&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;date: Sun, &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; Feb &lt;span style="color:#ae81ff"&gt;2022&lt;/span&gt; 16:13:32 GMT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;server: istio-envoy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-envoy-upstream-service-time: &lt;span style="color:#ae81ff"&gt;31&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we take the jwt_token from the preparation step and present it to the request authentication resource by sending an HTTP request with the appropriate authorization token. It should return an HTTP 200 code this time:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl -I -XGET $INGRESS_HOST/headers --header &lt;span style="color:#e6db74"&gt;&amp;#34;Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDQ3NzE0NjUsImlhdCI6MTY0NDc2ODQ2NSwiaXNzIjoiZGlnaWh1bmNoLmNvbSIsImp0aSI6Im1faUhla2pNbWRmUmlsWEdCaTFBR3ciLCJuYmYiOjE2NDQ3Njg0NjUsInBlcm1pc3Npb24iOiJyZWFkIiwicm9sZSI6InJlYWRlciIsInN1YiI6IkRJR0lIVU5DSCJ9.sVppwmvDqKvSsVdB05a_mDHymZq7Okvnwu-caTywXQgsUvOA6HfaySp_WXMyTp1HQ4WcTqKE4frZm7QNtrZsPso4bdD_4mEDYTswTCWhblaPy236NJBEH3ilB2BVySBVQKsjyxd94F1KV24SFWiR6lUxk52wKKE3ipBwR79jPizhAu9xxrfJ2Lfi5ypNa_kjBdJi63KCt2Y0eW94Fjq3PZs4ZalHJyaXYSx5Gxyei5f7QdpEOBpvs13mSdi9RqkgVQOjE0V1uBRpMckMyZs-IijknJcSu4fkrjgfNmXsrm__-vlM9UjUl2Jlj0x8bRC8l20IZ6t1ml-GFkwj39JC0g&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTP/1.1 &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt; OK&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;server: istio-envoy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;date: Sun, &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; Feb &lt;span style="color:#ae81ff"&gt;2022&lt;/span&gt; 16:13:54 GMT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-type: application/json&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-length: &lt;span style="color:#ae81ff"&gt;589&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;access-control-allow-origin: *&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;access-control-allow-credentials: true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-envoy-upstream-service-time: &lt;span style="color:#ae81ff"&gt;17&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the example above, jwtRules can be used with other keys such as jwksUri to reference the jwks by Uri. More fields in JWTRules can be found &lt;a href="https://istio.io/latest/docs/reference/config/security/request_authentication/#JWTRule"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now we have tested three curl commands:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Without authentication token at all: http server (istio-envoy) returns 200 code.&lt;/li&gt;&#10;&lt;li&gt;With an invalid authentication token: http server (istio-envoy) returns 401 code for error.&lt;/li&gt;&#10;&lt;li&gt;with a valid authentication: http server (istio-envoy) returns 200 code.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;So we have the capability to validate token, but it is not yet mandatory to present the token. We can change this behaviour by tweaking authorization policy.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="authorization-policy"&gt;Authorization Policy&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;According to Istio &lt;a href="https://istio.io/latest/docs/reference/config/security/request_authentication/"&gt;documentation&lt;/a&gt;, to restrict access to authenticated requests only, this should be accompanied by an authorization rule. We start with the following policy:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;security.istio.io/v1beta1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;AuthorizationPolicy&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;auth-pol&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;web&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ALLOW&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;from&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;source&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requestPrincipals&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt; Applying the manifest above to namespace web to it applies to workload httpbin. Then we &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ curl -I -XGET $INGRESS_HOST/headers&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HTTP/1.1 &lt;span style="color:#ae81ff"&gt;403&lt;/span&gt; Forbidden&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-length: &lt;span style="color:#ae81ff"&gt;19&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;content-type: text/plain&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;date: Sun, &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt; Feb &lt;span style="color:#ae81ff"&gt;2022&lt;/span&gt; 16:29:58 GMT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;server: istio-envoy&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;x-envoy-upstream-service-time: &lt;span style="color:#ae81ff"&gt;35&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The requestPrincipals clause makes it mandatory to present a token. The RequestAuthentication validates the token. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We can beef up the authorization policies by adding claims to the conditions, for example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;apiVersion&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;security.istio.io/v1beta1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;kind&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;AuthorizationPolicy&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;metadata&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;auth-pol&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;namespace&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;web&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spec&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;selector&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;matchLabels&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;action&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;ALLOW&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;rules&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;from&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;source&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;requestPrincipals&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;to&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;operation&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;methods&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;when&lt;/span&gt;:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;request.auth.claims[iss]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;values&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;digihunch.com&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;request.auth.claims[role]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;values&lt;/span&gt;: [&lt;span style="color:#e6db74"&gt;&amp;#34;reader&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Both the to and when conditions are for HTTP traffic only, and we must tell Istio to inspect the traffic as HTTP, which is done implicitly with named ports on the Service object. Refer to &lt;a href="https://istio.io/latest/docs/ops/common-problems/security-issues/#make-sure-you-are-not-using-http-only-fields-on-tcp-ports"&gt;this&lt;/a&gt; common problem from Istio&amp;#8217;s documentation. The request principals, if a none wildcard value is specified, will be a SPIFFE format identity, the same one used for peer authentication, as discussed in the &lt;a href="https://static.digihunch.com/2022/02/authentication-and-authorization-with-istio/"&gt;previous&lt;/a&gt; post.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;em&gt;when&lt;/em&gt; clause above contains two key-value pairs. The first looks for the value of a standard claim (iss), the second for a custom claim (role). In the preparation step, we created the claims with those claims in Python and they will match the condition here. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Should any of the conditions above not match, a 403 (Forbidden) error code will be returned by the workload&amp;#8217;s istio-envoy proxy.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="validating-mtls"&gt;&lt;a href="#ValidatingMTLS"&gt;Verify mTLS connection&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In some cases, we need to audit whether the TLS traffic between workloads actually take place in mTLS. As I was developing my &lt;a href="https://github.com/digihunch/korthweb"&gt;korthweb&lt;/a&gt; project, I don&amp;#8217;t find a straightforward way of validating TLS. We can validate that mTLS mode on a workload using the following istio CTL command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ istioctl x describe pod my-workload-pod -n &lt;span style="color:#f92672"&gt;[&lt;/span&gt;namespace&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The output shows Effective PeerAuthentication and Applied PeerAuthentication. This verifies configuration. But how can we ensure that traffic are indeed using mTLS? For most of mTLS traffic, we can use Kiali&amp;#8217;s observability feature. In Graph, we need to ensure &amp;#8220;Security&amp;#8221; is checked in the display drop-down. The pad lock will indicate the traffic is mTLS. To get reliable results, we have to artificially create some live traffic between workloads (e.g. curl from one Pod to another) so Kiali can pick up the update.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="1015" height="723" src="https://static.digihunch.com/wp-content/uploads/2022/02/image-3.png" alt="" class="wp-image-3550"/&gt;&lt;figcaption class="wp-element-caption"&gt;Validate mTLS traffic in Kiali&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Unfortunately, even artificial traffic does not make Kiali the most reliable way to detect mTLS. There are three other approach to verify TLS traffic.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The first approach is to let Envoy proxy emit TLS related traffic. We can apply the following annotation line to a Pod, to tell its Envoy proxy to emit measurements related to tls_inspector:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;sidecar&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;statsInclusionPrefixes&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;tls_inspector,listener.0.0.0.0_15006&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The metrics will be exposed to Envoy&amp;#8217;s admin port (15000 on istio-proxy) with the path /stats. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n orthweb exec orthanc-7f4c9b759-lxnrb -c istio-proxy -- curl localhost:15000/stats | grep tls_inspector&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; % Total % Received % Xferd Average Speed Time Time Time Current&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Dload Upload Total Spent Left Speed&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;22194&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;22194&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; 21.1M &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; --:--:-- --:--:-- --:--:-- 21.1M&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.alpn_found: &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.alpn_not_found: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.client_hello_too_large: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.connection_closed: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.read_error: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.sni_found: &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.sni_not_found: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.tls_found: &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tls_inspector.tls_not_found: &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We still need some live traffic to bump up the measurement. Comparing between tls_found and tls_not_found is a good way to determine if a Pod is receiving both TLS and plaintext traffic in PERMISSIVE mode.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The second approach to tell mTLS is via the &lt;strong&gt;connection_security_policy&lt;/strong&gt; metric label. It is set to mtutual_tls if the connection is in mTLS. We need the dashboard for Prometheus:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ istioctl dashboard prometheus&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the prometheus dashboard, click on &amp;#8220;Graph&amp;#8221; at the top, then search for &amp;#8220;&lt;em&gt;istio_tcp_connections_closed_total&lt;/em&gt;&amp;#8221; for &amp;#8220;&lt;em&gt;istio_tcp_connections_opened_total&lt;/em&gt;&amp;#8220;, the result should include a metrics called &lt;em&gt;connection_security_policy&lt;/em&gt; which is labelled as mtutual_tls, as illustrated below:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large"&gt;&lt;img loading="lazy" decoding="async" width="2870" height="754" src="https://static.digihunch.com/wp-content/uploads/2022/02/image-4.png" alt="" class="wp-image-3553"/&gt;&lt;figcaption class="wp-element-caption"&gt;mTLS metric&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For more about collecting and querying metrics from Prometheus, check out Istio&amp;#8217;s documentation &lt;a href="https://istio.io/latest/docs/tasks/observability/metrics/tcp-metrics/"&gt;here&lt;/a&gt; and &lt;a href="https://istio.io/latest/docs/tasks/observability/metrics/querying-metrics/"&gt;here&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The third approach is to utilize the AUDIT feature of Authorization Policy. When a rule in Authorization Policy has a &lt;a href="https://istio.io/latest/docs/reference/config/security/authorization-policy/#Source"&gt;source&lt;/a&gt; with namespace or notNamespace field, it requires the incoming connection to have an SPIFFE identity and use mTLS. We can set the Authorization Policy&amp;#8217;s action to AUDIT and use &lt;a href="https://istio.io/latest/docs/ops/common-problems/security-issues/#ensure-istiod-accepts-the-policies"&gt;RBAC access&lt;/a&gt; logging, the same way we would do to troubleshoot RBAC access issues. From the log we can check RBAC failures due to missing SPIFFE identity.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2022/02/authentication-and-authorization-with-istio/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio Authentication and Authorization&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/02/istio-external-authorization/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Istio External Authorization via OIDC&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>From Ingress to CRD: why my solution needs Istio Gateways on Kubernetes platforms</title><link>https://static.digihunch.com/2021/12/from-ingress-to-gateway-why-you-need-istio-gateways-on-kubernetes-platforms/</link><pubDate>Wed, 29 Dec 2021 22:50:16 -0400</pubDate><guid>https://static.digihunch.com/2021/12/from-ingress-to-gateway-why-you-need-istio-gateways-on-kubernetes-platforms/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-ingress-crd.webp" alt="Featured image of post From Ingress to CRD: why my solution needs Istio Gateways on Kubernetes platforms" /&gt;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Update&lt;/strong&gt;: also read my other article &lt;a href="https://medium.com/slalom-build/managing-ingress-traffic-on-kubernetes-platforms-ebd537cdfb46"&gt;here&lt;/a&gt; on the different generations of ingress technologies.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In my &lt;a href="https://github.com/digihunch/korthweb"&gt;Korthweb&lt;/a&gt; project I was researching for the best ingress mechanism for HTTP and TCP workload, both of which need to be secured. I started with Kubernetes Ingress but eventually decided to go with Istio Gateway. This blog post is about the justification. In this essay, I will make the distinction between Ingress and Gateway and explain why a Kubernetes platform needs the latter going forward.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The word ingress can be used either to indicate ingress resource (in conjunction with ingress controller) in the context of Kubernetes cluster, or more generally to indicate the technology (e.g. provided by service mesh) to direct north-south traffic, originated from the outside into the workload running within the cluster. I will use both meanings of the word ingress throughout the article. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-background"&gt;Background&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In my previous &lt;a href="https://static.digihunch.com/2021/07/traffic-management-in-kubernetes-service-and-ingress/"&gt;post&lt;/a&gt;, I discussed Kubernetes Service object and native Ingress. In short, a Service object addresses the problem of exposing a workload (Pod or Deployment), operating at layer 3-4. A &lt;span style="text-decoration: underline;"&gt;ClusterIP&lt;/span&gt; type of service exposes workload within the cluster only, and therefore is only used by internal services that are consumed by workload in the same cluster. A &lt;span style="text-decoration: underline;"&gt;NodePort&lt;/span&gt; type of service exposes workload to outside of the cluster, using the node&amp;#8217;s IP address and port range available on the node, a great step forward but still quite inconvenient and subject to the limitation of using Node&amp;#8217;s IP and Port. A &lt;span style="text-decoration: underline;"&gt;LoadBalancer&lt;/span&gt; type of Service brings separate IP address and port for running a service. To back up this type of service, the platform has to provide a load balancer with its own IP address and port range to manage. The implementation is platform specific e.g. Metal LB for Minikube, Azure Load Balancer for AKS, NLB for AWS EKS. In this type of service, Node Ports still exists but are not exposed to the outside world. Instead, they are only exposed to the backend of Load Balancer, whose front-end port is exposed to the outside world. In both NodePort and LoadBalancer types of Service, the kube-proxy process plays a role on each node to direct traffic into NodePort to target Pods.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Kubernetes Ingress, on the other hand, targets issues above layer 4, for example, path-based routing and is therefore mostly used for HTTP and HTTPS traffic. It is implemented by an &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/"&gt;Ingress Controller&lt;/a&gt; and now there has been a diverse ecosystem of those ingress controllers. In many implementations (e.g. Azure Application Gateway Ingress Controller, AGIC), the ingress themselves usually come with a load balancer managed by themselves. This eliminates the need for a separate LoadBalancer type of Service just for L4 capability. As a result, Ingress (with both L4 and L7 capabilities) is usually deployed along with ClusterIP type Service. &lt;a href="https://docs.microsoft.com/en-us/azure/aks/ingress-basic"&gt;Here&lt;/a&gt; is an example. The declaration of the Ingress in this case usually contains numerous lines of annotations in order to communicate the specification to the implementation.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-requirement"&gt;Requirement&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Using Kubernetes Ingress along with Service of ClusterIP type seems perfect to address the majority of use cases. However, it has its blind spots. My Korthweb project deals with &lt;a href="https://static.digihunch.com/2020/11/how-imaging-devices-talk-to-each-other-tip-in-dicom/"&gt;DICOM&lt;/a&gt; traffic (a protocol on top of TCP) over TLS as well as HTTP traffic, which can be broken down as:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;The ability to proxy TCP traffic over an arbitrary TCP port&lt;/li&gt;&#10;&lt;li&gt;The ability to terminate TLS/SSL encryption &lt;span style="text-decoration: underline;"&gt;for TCP traffic&lt;/span&gt; &lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Ingress &lt;a href="https://kubernetes.io/docs/concepts/services-networking/ingress/#what-is-ingress"&gt;documentation&lt;/a&gt; of Kubernetes clearly states that: an Ingress does not expose arbitrary ports or protocols. Exposing services other than HTTP and HTTPS to the internet typically uses a service of type&amp;nbsp;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport"&gt;Service.Type=NodePort&lt;/a&gt;&amp;nbsp;or&amp;nbsp;&lt;a href="https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer"&gt;Service.Type=LoadBalancer&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The stance of community-driven Nginx ingress controller can be found in the &lt;a href="https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/"&gt;documentation&lt;/a&gt;, which suggests the use of Service object for arbitrary TCP port. There is some unofficial &lt;a href="https://github.com/kubernetes/ingress-nginx/issues/636"&gt;claims&lt;/a&gt; of workaround available but I&amp;#8217;m not confident.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I also went through a number of other Ingress providers but to my disappointment, the only product that supports it seems to be Traefik:&lt;/p&gt;&#10;&lt;figure class="wp-block-table"&gt;&lt;table class="has-very-light-gray-to-cyan-bluish-gray-gradient-background has-background"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Product&lt;/td&gt;&lt;td&gt;Requirement #1&lt;/td&gt;&lt;td&gt;Requirement #2&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;F5 driven Nginx Ingress Controller&lt;/td&gt;&lt;td&gt;Supported&lt;/td&gt;&lt;td&gt;There have also been &lt;a href="https://github.com/nginxinc/kubernetes-ingress/issues/831#issuecomment-578206759"&gt;requests&lt;/a&gt; for TLS termination for TCP traffic but the request has not been closed as of yet.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;meta charset="utf-8"&gt;HA Proxy Ingress controller&lt;/td&gt;&lt;td&gt;Supported&lt;/td&gt;&lt;td&gt;There&amp;#8217;s no mention of this in &lt;a href="https://haproxy-ingress.github.io/docs/"&gt;documentation&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Kong&amp;#8217;s Kubernetes Controller (with TCPIngress CRD)&lt;/td&gt;&lt;td&gt;Supported&lt;/td&gt;&lt;td&gt;&lt;a href="https://docs.konghq.com/kubernetes-ingress-controller/2.1.x/guides/using-tcpingress/"&gt;Documentation&lt;/a&gt; claims support through SNI-based routing. However, the &lt;a href="https://docs.konghq.com/kubernetes-ingress-controller/2.1.x/guides/using-tcpingress/#tls-sni-based-routing"&gt;example&lt;/a&gt; demonstrates it using self-signed certificate only. BYO cert not supported according to this GitHub &lt;a href="https://github.com/Kong/kong/issues/8151"&gt;issue&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;meta charset="utf-8"&gt;Traefik Lab&amp;#8217;s &lt;meta charset="utf-8"&gt;&lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/#kind-ingressroute"&gt;IngressRoute&lt;/a&gt; (with IngressRouteTCP CRD)&lt;/td&gt;&lt;td&gt;Supported&lt;/td&gt;&lt;td&gt;Documentation seems to suggest that it is supported by &lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/"&gt;IngressRoute&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;My general impression is that Req #2 isn&amp;#8217;t very popular so the providers either don&amp;#8217;t support it or delaying the implementation. Even if they do, the CRD used for TCP ingress is different than HTTP&amp;#8217;s. For example with Traefik Lab, the CRD for TCP is IngressRouteTCP, and for HTTP it is IngressRoute.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-introduction-to-gateway"&gt;Introduction to Gateway&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to functional shortages, according to &lt;a href="https://kubernetes.io/blog/2021/04/22/evolving-kubernetes-networking-with-the-gateway-api/"&gt;this&lt;/a&gt; blog post, there are signs of fragmentation into different but strikingly similar CRDs and overloaded annotations. In Kubecon 2019, a group of contributors discussed the evolution of Ingress into Gateway. Below are the two key slides stolen from their &lt;a href="https://kubernetes.io/blog/2021/04/22/evolving-kubernetes-networking-with-the-gateway-api/"&gt;presentation&lt;/a&gt;:&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://static.digihunch.com/wp-content/uploads/2021/12/image.png" alt="" class="wp-image-3269" width="719" height="280"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-large is-resized"&gt;&lt;img loading="lazy" decoding="async" src="https://static.digihunch.com/wp-content/uploads/2021/12/image-1.png" alt="" class="wp-image-3270" width="716" height="336"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This sums up how the concept of Gateway is different from Ingress. Gateway is an instantiation of a given LB. It works along with Route to achieve the functions brought by an Ingress. Gateway as a resource type of its own makes management easier at L4-L6 by a separate team. Routing at L7 is offloaded to &amp;#8220;Route&amp;#8221; resource type.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This conceptual evolution gives rise to the &lt;a href="https://gateway-api.sigs.k8s.io/"&gt;Gateway API&lt;/a&gt; open source &lt;a href="https://github.com/kubernetes-sigs/gateway-api"&gt;project&lt;/a&gt; managed by the SIG-NETWORK community. &lt;a href="https://github.com/kubernetes-sigs/gateway-api"&gt;Gateway API&lt;/a&gt; is a collection of resources that model service networking in Kubernetes, including GatewayClass, Gateway, HTTPRoute, TCPRoute, Service etc. The aim of this initiative is to evolve Kubernetes service networking through expressive, extensive and role-oriented interfaces that are implemented by many vendors and have broad industry support.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://gateway-api.sigs.k8s.io/images/api-model.png" alt="Gateway API Model"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The diagram above stolen from Gateway API website illustrate the management model for each resource.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-kubernetes-gateway-implementations"&gt;Kubernetes Gateway Implementations&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Gateway API is still a fairly young initiative and all the reference &lt;a href="https://gateway-api.sigs.k8s.io/implementations/"&gt;implementations&lt;/a&gt; are either work-in-progress or in early stages. In the last section, we learned that in the journey from Ingress to Gateway, the standardization initiative comes a bit behind the implementation efforts. For many supporters, the natural strategy is to continue with existing proprietary ingress controller implementation, and retrofit their technology to the emerging Gateway API along the way.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because of that, we can see many market players with multiple flavours of implementations, typically one that evolves from their original product offering, with higher adoption rate and maturity level, and one that conforms to Gateway API specification. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, Traefik Labs has its &lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-gateway/"&gt;Gateway API implementation&lt;/a&gt; in experimental stage. Its own implementation consists of (standard) Kubernetes &lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-ingress/"&gt;Ingress&lt;/a&gt; and Kubernetes &lt;a href="https://doc.traefik.io/traefik/routing/providers/kubernetes-crd/"&gt;IngressRoute&lt;/a&gt; (based on custom resource with support of advanced features such as TCP route). Another example is the HAProxy &lt;a href="https://www.haproxy.com/documentation/kubernetes/latest/usage/ingress/"&gt;Ingress&lt;/a&gt;, a community driven ingress controller implementation for HAProxy. Starting from its version 0.13, it partially supports the Gateway API&amp;#8217;s v1alpha1 specification. &lt;a href="https://haproxy-ingress.github.io/docs/configuration/gateway-api/#conformance"&gt;Here&lt;/a&gt; is the conformance statement. &lt;a href="https://projectcontour.io/getting-started/"&gt;Contour&lt;/a&gt; as a CNCF project for ingress controller has support for Gateway API at &lt;a href="https://projectcontour.io/guides/gateway-api/"&gt;alpha&lt;/a&gt; version. Kong&amp;#8217;s &lt;a href="https://github.com/kong/kubernetes-ingress-controller"&gt;Kubernetes Ingress Controller&lt;/a&gt; follows the same path.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The Gateway functionality is also provided as part of Service Mesh product offering, and we can see some reference implementation by service mesh providers. Istio has its own Gateway implementation but tries to adapt to &lt;a href="https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/#differences-from-istio-apis"&gt;Kubernetes Gateway API&lt;/a&gt;. Hashicorp Consul also claims to be building support for Kubernetes Gateway API.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Until the Gateway API project matures, it is not recommended to use Gateway implementations that conforms to it, unless you intend to be their Guinea Pig. For my project, I chose Istio Gateway.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-istio-gateway-implementation"&gt;Istio Gateway implementation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As discussed, when the Gateway standard is still in its infancy, I chose a non-standard implementation of Gateway, even though it may include some CRDs. Out of the many Gateway implementations, I choose Istio mainly for two reasons.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First, I need a Gateway as part of service mesh because service mesh provides many other features that are needed in the platform. One of the goals of &lt;a href="https://static.digihunch.com/2021/11/from-microservice-to-service-mesh/"&gt;Service Mesh&lt;/a&gt; is to provide commonly used features (traffic management, observability, security, extensibility) in a commodity layer on top of Kubernetes. Gateway is not the only thing I need out of this commodity layer. With one install of Istio, many common platform-level problems are also addressed (e.g. mTLS, traceability, etc). I&amp;#8217;ve compared three major service mesh technologies as below:&lt;/p&gt;&#10;&lt;figure class="wp-block-table is-style-regular"&gt;&lt;table class="has-very-light-gray-to-cyan-bluish-gray-gradient-background has-background"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Linkderd&lt;/th&gt;&lt;th&gt;Istio&lt;/th&gt;&lt;th&gt;Consul&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&amp;#8211; lightweight&lt;br&gt;&amp;#8211; uses linkerd2-proxy&lt;br&gt;&amp;#8211; the original service mesh&lt;br&gt;&amp;#8211; no Gateway implementation (deal breaker in my use case)&lt;/td&gt;&lt;td&gt;&amp;#8211; feature rich&lt;br&gt;&amp;#8211; uses Envoy proxy&lt;br&gt;&amp;#8211; complex but getting better&lt;/td&gt;&lt;td&gt;&amp;#8211; initially a service discovery and distributed key-value store&lt;br&gt;&amp;#8211; lacks observability features but getting better&lt;br&gt;&amp;#8211; uses Envoy proxy&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;It is worth-noting that there are some initiatives to standardize service mesh (e.g. &lt;a href="https://smi-spec.io/"&gt;SMI&lt;/a&gt;) with reference implementation such as &lt;a href="https://openservicemesh.io/"&gt;OpenServiceMesh&lt;/a&gt;. The standard is too weak to be considered important for now compared with maturity and stability. Istio has strong community support. The risk with Istio to acknowledge, is that it is &lt;span style="text-decoration: underline;"&gt;not&lt;/span&gt; following an open-governance model (unlike many other &lt;a href="https://www.cncf.io/news/2020/07/28/the-new-stack-googles-management-of-istio-raises-questions-in-the-cloud-native-community/"&gt;CNCF&lt;/a&gt; projects), which could potentially causes vendor lock-in. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Second, The Istio Gateway features the separation between Gateway and Virtual Service CRDs. The former defines entry point and the latter defines routing rules. This design separates entry points from routing rules, enabling the flexibility of reusing the same Virtual Service for different gateways.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Istio uses Envoy proxy to manage traffic between Pods. Unlike the kube-proxy pattern, the Envoy proxies are side-car containers centrally managed by Istio Control Plane, which also enables other features such as traceability, as illustrated in the diagram below (stolen from &lt;a href="https://thenewstack.io/why-do-you-need-istio-when-you-already-have-kubernetes/"&gt;this&lt;/a&gt; post).&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://cdn.thenewstack.io/media/2021/03/200a2844-image4.png" alt=""/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In addition to Virtual Service, Istio &lt;a href="https://istio.io/latest/docs/reference/config/networking/gateway/"&gt;Gateway&lt;/a&gt; also has the concept of Destination Rules. &lt;a href="https://istio.io/latest/docs/reference/config/networking/virtual-service/"&gt;Virtual Service&lt;/a&gt; defines how to route traffic to different destinations. &lt;a href="https://istio.io/latest/docs/reference/config/networking/destination-rule/"&gt;Destination rule&lt;/a&gt; defines how to split traffic to different subset at the routing destination. The concepts are documented on &lt;a href="https://istio.io/latest/docs/reference/config/networking/"&gt;this&lt;/a&gt; page. For HTTP traffic, the relevant entities can be represented in the diagram below:&lt;/p&gt;&#10;&lt;p class="has-white-background-color has-background wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="661px" viewBox="-0.5 -0.5 661 341" style="max-width:100%;max-height:341px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;path d="M 120 275 L 150 275 L 150 95 L 173.63 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 178.88 95 L 171.88 98.5 L 173.63 95 L 171.88 91.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="0" y="210" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 275px; margin-left: 2px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;hosts&lt;/li&gt;&lt;li&gt;gateways&lt;/li&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;http&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;tls&lt;/li&gt;&lt;li&gt;tcp&lt;/li&gt;&lt;li&gt;exportTo&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="2" y="279" fill="#333333" font-family="Helvetica" font-size="12px"&gt;hostsgatewayshttptls&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 300 95 L 333.63 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 338.88 95 L 331.88 98.5 L 333.63 95 L 331.88 91.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 240 160 L 240 180 L 240 160 L 240 173.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 240 178.88 L 236.5 171.88 L 240 173.63 L 243.5 171.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="180" y="30" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 95px; margin-left: 182px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;match&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;route&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;retries&lt;/li&gt;&lt;li&gt;timeout&lt;/li&gt;&lt;li&gt;rewrite&lt;/li&gt;&lt;li&gt;redirect&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="182" y="99" fill="#333333" font-family="Helvetica" font-size="12px"&gt;matchrouteretriestim&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="180" y="210" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 275px; margin-left: 182px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;uri&lt;/li&gt;&lt;li&gt;scheme&lt;/li&gt;&lt;li&gt;method&lt;/li&gt;&lt;li&gt;headers&lt;/li&gt;&lt;li&gt;port&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="182" y="279" fill="#333333" font-family="Helvetica" font-size="12px"&gt;urischememethodheade&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="180" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 195px; margin-left: 1px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;VirtualService&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="60" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;VirtualService&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="180" y="0" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 15px; margin-left: 181px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;HTTPRoute&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="240" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;HTTPRoute&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="180" y="180" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 195px; margin-left: 181px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;HTTPMatchRequest&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="240" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;HTTPMatchRequest&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 400 160 L 400 180 L 400 160 L 400 173.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 4" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 400 178.88 L 396.5 171.88 L 400 173.63 L 403.5 171.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="340" y="30" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 95px; margin-left: 342px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;destination&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;weight&lt;/li&gt;&lt;li&gt;headers&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="342" y="99" fill="#333333" font-family="Helvetica" font-size="12px"&gt;destinationweighthea&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="340" y="0" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 15px; margin-left: 341px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;HTTPRouteDestionation&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="400" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;HTTPRouteDestionation&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 460 275 L 485 275 L 485 95 L 503.63 95" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 508.88 95 L 501.88 98.5 L 503.63 95 L 501.88 91.5 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="340" y="210" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 275px; margin-left: 342px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;host&lt;/li&gt;&lt;li&gt;&lt;font color="#0000cc"&gt;&lt;b&gt;subsets&lt;/b&gt;&lt;/font&gt;&lt;/li&gt;&lt;li&gt;trafficPolicy&lt;/li&gt;&lt;li&gt;exportTo&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="342" y="279" fill="#333333" font-family="Helvetica" font-size="12px"&gt;hostsubsetstrafficPo&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="340" y="180" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 195px; margin-left: 341px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;DestionationRule&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="400" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;DestionationRule&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 585 160 L 585 180 L 585 160 L 585 173.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 585 178.88 L 581.5 171.88 L 585 173.63 L 588.5 171.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="510" y="30" width="150" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 148px; height: 1px; padding-top: 95px; margin-left: 512px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;name&lt;/li&gt;&lt;li&gt;labels&lt;/li&gt;&lt;li&gt;&lt;b&gt;&lt;font color="#0000cc"&gt;trafficPolicy&lt;/font&gt;&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="512" y="99" fill="#333333" font-family="Helvetica" font-size="12px"&gt;namelabelstrafficPolicy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="525" y="0" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 15px; margin-left: 526px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Subset&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="585" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Subset&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="510" y="210" width="150" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 148px; height: 1px; padding-top: 275px; margin-left: 512px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;loadBalancer&lt;/li&gt;&lt;li&gt;connectionPool&lt;/li&gt;&lt;li&gt;outlierDetection&lt;/li&gt;&lt;li&gt;tls&lt;/li&gt;&lt;li&gt;portLevelSettings&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="512" y="279" fill="#333333" font-family="Helvetica" font-size="12px"&gt;loadBalancerconnectionPoo&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="525" y="180" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 195px; margin-left: 526px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;TrafficPolicy&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="585" y="199" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;TrafficPolicy&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 60 160 L 60 180 L 60 160 L 60 173.63" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="1 4" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 60 178.88 L 56.5 171.88 L 60 173.63 L 63.5 171.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;rect x="0" y="30" width="120" height="130" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 118px; height: 1px; padding-top: 95px; margin-left: 2px;"&gt;&lt;div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: left;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;&lt;ul&gt;&lt;li&gt;selector&lt;/li&gt;&lt;li&gt;servers&lt;/li&gt;&lt;ul&gt;&lt;li&gt;port&lt;/li&gt;&lt;li&gt;hosts&lt;/li&gt;&lt;li&gt;tls&lt;/li&gt;&lt;li&gt;name&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="2" y="99" fill="#333333" font-family="Helvetica" font-size="12px"&gt;selectorservers&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="0" y="0" width="120" height="30" fill="none" stroke="none" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 15px; margin-left: 1px;"&gt;&lt;div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;"&gt;&lt;div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"&gt;Gateway&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="60" y="19" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle"&gt;Gateway&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Viewer does not support full SVG 1.1&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://github.com/digihunch/korthweb/blob/main/manual/orthanc.yaml#L106"&gt;Here&lt;/a&gt; is an example of using Gateway and Virtual Service resources (from Korthweb sample project). Also note that in the Istio literature, there is neither a CRD name called &amp;#8220;Ingress&amp;#8221;, nor a resource type named &amp;#8220;Ingress Gateway&amp;#8221;, although they may be loosely used to refer to &amp;#8220;Gateway resources configured to manage ingress traffic&amp;#8221;. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-istio-gateway-installation"&gt;Istio Gateway Installation&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are more than one ways to install Istio. In the past, Istio Operator was used to install Istio. The Operator calls IstioOperator API. Today the use of Istio Operator is not recommended anymore but the IstioOperator API is used implicitly by Istioctl installer. The two recommended approaches to install Istio today is by &lt;a href="https://istio.io/latest/docs/setup/install/istioctl/"&gt;Istioctl&lt;/a&gt; and &lt;a href="https://istio.io/latest/docs/setup/install/helm/"&gt;Helm&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With istioctl, you can specify an option imperatively, or using an overlay file as did in &lt;a href="https://static.digihunch.com/2021/11/istio-ingress-egress/"&gt;this&lt;/a&gt; lab. With Helm, istio has multiple charts, and requires multiple steps:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;install CRDs using the &lt;a href="https://artifacthub.io/packages/helm/istio-official/base"&gt;base&lt;/a&gt; chart&lt;/li&gt;&#10;&lt;li&gt;install istiod using the &lt;a href="https://artifacthub.io/packages/helm/istio-official/istiod"&gt;istiod&lt;/a&gt; chart. &lt;a href="https://github.com/digihunch/korthweb/blob/main/manual/istio/istiod-values.yaml"&gt;Here&lt;/a&gt; is an example of values provided to Helm installer.&lt;/li&gt;&#10;&lt;li&gt;install ingress &lt;a href="https://istio.io/latest/docs/setup/additional-setup/gateway/"&gt;gateway&lt;/a&gt; using the &lt;a href="https://artifacthub.io/packages/helm/istio-official/gateway"&gt;gateway&lt;/a&gt; chart. &lt;a href="https://github.com/digihunch/korthweb/blob/main/manual/istio/ingress-gateway-values.yaml"&gt;Here&lt;/a&gt; is an example of values provided to Helm installer for ingress gateway. Note that egress gateway also requires the same helm chart with different value definition. &lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://digihunch.github.io/korthweb/deployment/manual/"&gt;This&lt;/a&gt; instruction contains how to manually install Istio gateways using Helm, including installation of multiple Helm charts. I attempted to create a single chart to consolidate the multiple charts required for istio. It was not successful because of an &lt;a href="https://github.com/helm/helm/issues/10392"&gt;error&lt;/a&gt; when trying to reference the same gateway chart dependency for multiple times.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2021/12/aks-lessons-learned-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;AKS Lessons Learned 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2022/01/kubernetes-admission-control/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes Admission Control&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Istio Lab – Ingress and Egress</title><link>https://static.digihunch.com/2021/11/istio-ingress-egress/</link><pubDate>Tue, 16 Nov 2021 21:28:00 -0400</pubDate><guid>https://static.digihunch.com/2021/11/istio-ingress-egress/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-istio-lab-1.webp" alt="Featured image of post Istio Lab – Ingress and Egress" /&gt;&lt;p class="wp-block-paragraph"&gt;Istio is a popular open-source &lt;a href="https://static.digihunch.com/2021/11/from-microservice-to-service-mesh/"&gt;service mesh&lt;/a&gt; implementation using Envoy proxy. One of the benefit of using Istio is the ingress and egress it brings to native Kubernetes platform. This article is a hands-on guide to test Istio ingress and egress gateways on Minikube. It was tested on my MacBook. All the information in this post can be found in several different pages from Istio and Minikube documentation. The purpose of this article is a single cheat sheet for myself to quickly set up Istio ingress and egress testing.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-install-istio-on-minikube"&gt;Install Istio on Minikube&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We start with configuring Minikube on MacBook (or change the command accordingly on other platform), and install metal LB load balancer. The steps with Istio also works with a regular Kubernetes platform.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;minikube start --memory&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;12288&lt;/span&gt; --cpus&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;6&lt;/span&gt; --kubernetes-version&lt;span style="color:#f92672"&gt;=&lt;/span&gt;v1.20.2 --nodes &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; --container-runtime&lt;span style="color:#f92672"&gt;=&lt;/span&gt;containerd --driver&lt;span style="color:#f92672"&gt;=&lt;/span&gt;hyperkit&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;minikube addons enable metallb&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we need to give load balancer an IP address range before setting up the load balancer. Minikube adds an interface named bridge100 to MacOS host environment. The IP address of the host can be found via the following command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;minikube ssh &lt;span style="color:#e6db74"&gt;&amp;#34;ping host.minikube.internal -c 1&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;For example, my host IP address is 192.168.64.1, so I dedicate the IP range 192.168.64.64 to 192.168.64.80 to the MetalLB load balancer. Then I need to provide this range when configuring the load balancer:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;minikube addons configure metallb&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n metallb-system get po&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;After the configuration, make sure the MetalLB pod is up. Then we can install istio to the Kubernetes cluster. The steps are completed using istioctl following the instruction, I created a quick and dirty script to put the steps together:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#! /bin/bash&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -L https://istio.io/downloadIstio | sh -&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export PATH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;realpath istio*/bin&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;:$PATH&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ln -sfn &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;ls -d -- istio-*&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt; istio&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; istioctl x precheck; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo ready to install istio and lable namespace &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; istio-injection&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; istioctl install --set profile&lt;span style="color:#f92672"&gt;=&lt;/span&gt;demo -y --verify --set meshConfig.outboundTrafficPolicy.mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;REGISTRY_ONLY&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; kubectl label namespace default istio-injection&lt;span style="color:#f92672"&gt;=&lt;/span&gt;enabled&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo failed precheck&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; exit &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The script above download the istio zip file, unzips it, installs Istio (in demo mode and set outboundTrafficPolicy to REGISTRY_ONLY), labels default namespace for proxy injection. To use istioctl without spelling out full path, we can run the following command again separately outside of script:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export PATH&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;realpath istio/bin&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;:$PATH&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To validate, check istioctl version command.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-install-sample-application"&gt;Install Sample application&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For simplicity, the rest of this article uses httpbin to test Istio&amp;#8217;s ingress and egress. For completeness, this section gives the steps to install bookinfo application as it is used in other Istio testing as well. It also help understand how to ingress to an application with Istio using an IP provided by the load balancer. Skip this section if just need to test with the simple httpbin application. The Bookinfo application comes within the istio 1.12 directory. In the following steps from the istio directory, we install some observability add-ons as well as the bookinfo application. Then we test access to the website.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f samples/addons/prometheus.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f samples/addons/kiali.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f samples/addons/jaeger.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f samples/addons/grafana.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl exec &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl get pod -l app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;ratings -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.items[0].metadata.name}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -c ratings -- curl -sS productpage:9080/productpage | grep -o &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;title&amp;gt;.*&amp;lt;/title&amp;gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export INGRESS_HOST&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl -n istio-system get service istio-ingressgateway -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.status.loadBalancer.ingress[0].ip}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export INGRESS_PORT&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;kubectl -n istio-system get service istio-ingressgateway -o jsonpath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;{.spec.ports[?(@.name==&amp;#34;http2&amp;#34;)].port}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export GATEWAY_URL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$INGRESS_HOST:$INGRESS_PORT&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s &lt;span style="color:#e6db74"&gt;&amp;#34;http://&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;GATEWAY_URL&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;/productpage&amp;#34;&lt;/span&gt; | grep -o &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;title&amp;gt;.*&amp;lt;/title&amp;gt;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;On my laptop, the value of $GATEWAY_URL is 192.168.64.64:80. Therefore I can browse to site http://&lt;meta charset="utf-8"&gt;192.168.64.64:80/productpage with local browser. We can also check kiali dashboard:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;istioctl dashboard kiali&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we are good to examine ingress and egress. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-prep-work"&gt;Prep Work&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The following custom resources are related to traffic management in istio:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Virtual Service (vs)&lt;/li&gt;&#10;&lt;li&gt;Destination Rule (dr)&lt;/li&gt;&#10;&lt;li&gt;Service Entry (se)&lt;/li&gt;&#10;&lt;li&gt;Gateway (gw, Istio&amp;#8217;s ingress or egress)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We will expand on the role of each in a separate article. To execute command from within a Pod, we also use sleep application which is essentially a Pod with a sleeper thread for the purpose allowing user to run troubleshooting command. The sleep application is provided in istio directory:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f samples/sleep/sleep.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get po -l app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;sleep&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Find out the Pod name to execute command. Then we use httpbin website to emulate an external service, and use the sleep Pod (suppose the name is sleep-557747455f-q99cz) to access the service:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl exec -it sleep-557747455f-q99cz -c sleep -- curl http://httpbin.org/headers&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;As we previously set outboundTrafficPolicy.mode=REGISTRY_ONLY, this step is expected to output nothing. If this is not the case, it means the cluster may have ALLOW_ALL as outbount traffic policy, and you will need to follow &lt;a href="https://istio.io/latest/docs/tasks/traffic-management/egress/egress-control/#controlled-access-to-external-services"&gt;this&lt;/a&gt; guide to set it to REGISTRY_ONLY. With REGISTRY_ONLY, in order to allow access, we need to register a service entry, by applying the document below:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;networking&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1alpha3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ServiceEntry&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hosts&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;org&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;ports&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;number&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;protocol&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;HTTP&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;resolution&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DNS&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Now we should be able to access httpbin service as it is registered:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get se httpbin -o yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Using the exec command above again, we should see output from the curl command.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-ingress-testing"&gt;Ingress Testing&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We deploy an httpbin service in order to test ingress. The YAML declaration is prepared by Istio installation package:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f samples/httpbin/httpbin.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we deploy the ingress:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;networking&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1alpha3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Gateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;gateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;selector&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ingressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;servers&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;number&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;protocol&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;HTTP&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hosts&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;httpbin.digihunch.com&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The selector above indicates that the gateway deployed to a Pod labeled with istio: ingressgateway. Then we create a corresponding virtual service for this gateway:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;networking&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1alpha3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;VirtualService&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hosts&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;httpbin.digihunch.com&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;gateways&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;gateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;match&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;uri&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;prefix&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;/status&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;uri&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;prefix&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;/delay&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;route&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;destination&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;number&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8000&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Note that the hosts setup must match the configuration in gateway. The virtual service defines two services at URIs /status and /delay to expose, and we can confirm the creation of virtual service:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get vs httpbin -o yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can then test with curl command from MacOS host to verify that the ingress is working. We need to use the IP address of the service (192.168.64.64 with port 80 as previously indicated):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -I -HHost:httpbin.digihunch.com http://192.168.64.64/status/200&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -I -HHost:httpbin.digihunch.com http://192.168.64.64/delay/2&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We use -HHost to emulate this request going to httpbin.digihunch.com (otherwise we&amp;#8217;d have to fake DNS entry in /etc/hosts). Both command should give their return without error. The second command will give return after a delay of 2 seconds.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;This validates the success of ingress setup.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-egress-testing"&gt;Egress Testing&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To visit an external service, there are four options:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;Just access external service from Pods, when you have global.outboundTrafficPolicy.mode=ALLOW_ANY&lt;/li&gt;&#10;&lt;li&gt;Use ServiceEntry (as illustrated above in Prep Work)&lt;/li&gt;&#10;&lt;li&gt;Bypass envoy proxy (not recommended, because there is no role of service mesh)&lt;/li&gt;&#10;&lt;li&gt;Configure an Egress Gateway&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the Prep Work, we&amp;#8217;ve seen how to use Service Entry. However, there is no control of how Pods access the external service. We are going to configure an Egress Gateway to manage the outgoing traffic flow. This allows you to manage route, and apply observability features to the outgoing traffic, something the Service Entry alone will not provide. The egress gateway have two typical &lt;a href="https://istio.io/latest/docs/tasks/traffic-management/egress/egress-gateway/#use-case"&gt;use cases&lt;/a&gt; as explained on Istio website.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;First, we want to confirm that the egress pod exists, with a quick command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl get po -n istio-system -l app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;istio-egressgateway &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; kubectl get se&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;It should exist as we installed istio as demo version. We then also need a service entry, as we have already configured in Prep Work. At this point, the egress gateway resource has not been created.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now, in a separate terminal, we monitor the log of egress pod&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl logs -f istio-egressgateway-7d5d69dcfd-kvwxv -n istio-system&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;With the -f (follow) session on, if we visit external service as we did during Prep Work, there is no new entry pop up at the tail of the log. This is because the external access is still not managed by egress. Now we need to add egress, with the following resources applied:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;networking&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1alpha3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Gateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;egressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;selector&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;egressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;servers&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;number&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;protocol&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;HTTP&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hosts&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;org&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Then we setup the virtual service with related rules:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;networking&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1alpha3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;VirtualService&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;vs&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;egressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;hosts&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;httpbin.org&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;gateways&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;egressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mesh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;http&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;match&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gateways&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;mesh&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;route&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;destination&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;egressgateway&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;system&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;svc&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;cluster&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;local&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;subset&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;number&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;weight&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;match&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gateways&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;egressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;route&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;destination&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;org&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;port&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;number&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;weight&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;---&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;networking&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1alpha3&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;DestinationRule&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dr&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;egressgateway&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;host&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;egressgateway&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;system&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;svc&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;cluster&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;local&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;subsets&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;httpbin&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;With all applied, we can repeat the access command using curl, and watch for the log tail. &lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl exec -it sleep-557747455f-q99cz -c sleep -- curl http://httpbin.org/ip&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The log should output a line saying curl command access, which indicates that this outbound access is managed by istio egress gateway. The log line looks like below and in the example the IP address 10.244.2.4 is the Pod IP address.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[2021-11-22T01:22:47.547Z] &amp;#34;GET /ip HTTP/2&amp;#34; 200 - via_upstream - &amp;#34;-&amp;#34; 0 45 818 816 &amp;#34;10.244.2.9&amp;#34; &amp;#34;curl/7.80.0-DEV&amp;#34; &amp;#34;9ec0a193-77f0-90a5-b132-21c3259307da&amp;#34; &amp;#34;httpbin.org&amp;#34; &amp;#34;3.216.167.140:80&amp;#34; outbound|80||httpbin.org 10.244.2.4:42372 10.244.2.4:8080 10.244.2.9:52710 - -&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Looking at the virtual service declaration, you might noticed multiple routes in the virtual service. The first route (mesh) tells sidecars to route request to egress gateway. The second route (istio-egressgateway) tells egress gateway to route traffic to external service as destination.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In real life, there&amp;#8217;s more to consider. For example, we usually want the host to allow on egress to be a wild card host such as *.api.google.com. While the hosts field in virtual service can be wild card, the route destination in virtual service has to be a list of fully qualified domains. The other situation is the destination website may issue an HTTP redirect to a different host, which also needs to be registered as a destination. These details can add up to administrative effort. &lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img decoding="async" src="https://istio.io/latest/blog/2019/egress-performance/case3_egressgw3.png" alt="Introduction of the egress gateway to access MongoDB"/&gt;&lt;figcaption class="wp-element-caption"&gt;Egress example&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;An other real life scenario is the use of authorization policy. Authorization policy rules use SPIFEE identity of a Pod. If a Pod initiates egress connection to destination host in TLS, then the Pod&amp;#8217;s SPIFFEE identity and its identity for external TLS connection are in conflict. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-with-azure-aks"&gt;With Azure AKS&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The steps above also works on other cloud platform such as AKS. &lt;a href="https://github.com/digihunch/cloudkube/tree/main/azure"&gt;This&lt;/a&gt; is an AKS cluster that I tested the steps. The Terraform code provisions a private cluster, as well as a Bastion Host. From the Bastion host, we can perform the same steps as above. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When Istio gets installed, it will created a Service object called istio-ingressgateway in the istio-system namespace. The service is of load balancer type and by default will create a public load balancer in Azure AKS. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The test steps will also work for private load balancers. To tell Azure to create private load balancer on a specific subnet, the Service object needs to include annotation. We have to customize Istio installation in order to do that, using the following overlay file (azoverlay.yaml):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;apiVersion&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;install&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;v1alpha2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;kind&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;IstioControlPlane&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;metadata&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;namespace&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;operator&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;custom&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;istiocontrolplane&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;spec&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;profile&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;demo&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;values&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;gateways&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;istio&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;ingressgateway&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;serviceAnnotations&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;service&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;beta&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;azure&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;load&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;balancer&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;internal&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;service&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;beta&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;kubernetes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;azure&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;load&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;balancer&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;internal&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;subnet&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;suitable-porpoise-lb-subnet&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This is inspired by &lt;a href="https://discuss.istio.io/t/setting-gateways-istio-ingressgateway-serviceannotations/5711/4"&gt;this&lt;/a&gt; post. Then the command to install Istio becomes:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;istioctl install -f azoverlay.yaml -y --verify --set meshConfig.outboundTrafficPolicy.mode&lt;span style="color:#f92672"&gt;=&lt;/span&gt;REGISTRY_ONLY&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This concludes the hand-on guide for Istio ingress and egress labs. I will need to write another post explaining the related concepts.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2021/11/infrastructure-deployment-in-terraform-2-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Infrastructure deployment in Terraform 2/2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2021/11/from-microservice-to-service-mesh/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;From Microservice to Service Mesh&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>AWS Certified DevOps Engineer Exam Tips</title><link>https://static.digihunch.com/2019/08/aws-certified-devops-engineer-exam-tips/</link><pubDate>Sat, 17 Aug 2019 23:07:59 -0400</pubDate><guid>https://static.digihunch.com/2019/08/aws-certified-devops-engineer-exam-tips/</guid><description>&lt;p class="wp-block-paragraph"&gt;The last 30 days have been exhausting for me. I studied hard on the new AWS Certified DevOps Engineer exam and thank goodness I passed (750 out of 1000 is required). This was the hardest professional certification I ever worked on. The exam was re-launched recently in March 2019 so there is still a shortage of information around the community. I was hoping to share my experience to help demystify this new exam. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I had a somewhat solid background to begin with, having taken the AWS Certified Solution Architect Professional exam (before the 2019 update), and worked quite a bit on CloudFormation, automation and Git. However, I still did not anticipate the exam to be this difficult until I was halfway through and already had the exam and materials paid for.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img loading="lazy" decoding="async" width="1469" height="518" src="https://static.digihunch.com/wp-content/uploads/2019/08/image.png" alt="" class="wp-image-127"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Without clear guideline on study material other than the white papers, I first skimmed through the ACloudGuru course, which helped me form a high level sense of exam coverage. Nonetheless it does not cover any topic in-depth and therefore by no means makes an essential part of my study. I checked out &lt;strong&gt;LinuxAcademy course&lt;/strong&gt; and they are much more in-depth for the major topics indeed. I like the course material in Lucid chart. However, the LinuxAcademy course along does not cover everything you need to know.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;What I found extremely helpful is the free training videos from the &lt;a href="https://www.aws.training"&gt;official training website&lt;/a&gt;. I strongly recommend the 7-hour course &lt;strong&gt;Exam Readiness: AWS Certified DevOps Engineer – Professional&lt;/strong&gt;. The instructor did a great job outlining the services and knowledge areas in the assessment . The course also comes with quality sample questions with answers and explanations on what the thinkings are behind the correct answers or why some choices are obviously wrong. I went through these questions twice and feel much better at not only understanding the question, but also understanding the intent of the question.&lt;/p&gt;&#10;&lt;figure class="wp-block-image"&gt;&lt;img loading="lazy" decoding="async" width="833" height="156" src="https://static.digihunch.com/wp-content/uploads/2019/08/image-1.png" alt="" class="wp-image-129"/&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Apart from the Exam Readiness course, other &lt;strong&gt;free introductory courses&lt;/strong&gt; from the official training website are helpful as well especially for those services that you only need to know the basics. Most of those courses are 5 ~ 10 minutes long, with brief but sufficient introduction and a demo session. The other extremely helpful resource is the &lt;strong&gt;official practice questions&lt;/strong&gt;. The practice exam is harder than the actual exam but they closely resemble the actual question style in the exam. Unfortunately, no answer is provided but they made me spend time finding answers across the documentations and blogs. It is worth-noting that the AWS blogs provides plenty of use cases that are covered in the questions.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When it comes to the real exam, it covers a lot more topics than its predecessor. Many questions are long and confusing. And I wish I could run a diff command to highlight the differences between choices. During exam preparation you really need to train yourself on reading efficiently. I found myself sometimes eyeball through all four choices at the same time, which get my mind scattered.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;During my study, I divide all services into three categories based on the level of familiarity, and here is my list:&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Category 1. Know these services very well, in and out:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS ElasticBeanstalk, OpsWorks Stacks, OpsWorks Chef Automate, CloudFormation, CloudWatch, CodeBuild, CodeCommit, CodeDeploy, CodePipeline, CodeStar, Lambda, API Gateway, Config, Trusted Advisor, CloudTrail, Systems Manager, Autoscaling Group in EC2, DynamoDB&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Category 2. Know these services well, but not necessarily down to every single detail:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Amazon Kinesis Firehose, Kinesis Analytics, Kinesis Streams, Step Functions, Elastic Load Balancer, Secrets Manager, Serverless Application Model (SAM), Route53, RDS, Certificate Manager, ElasticSearch, ECS, ECR&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;strong&gt;Category 3. Know about these services at a high level, but do not skip any:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;AWS Organization, X-Ray, GuardDuty, Macie, Inspector, Service Catalog, KMS, Batch, Athena, Single-Sign-On, Data LifeCycle Manager, CloudSearch, Health Dashboard, Glue, QuickSight, LightSail&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although I did not mention much about the white papers, I want to highlight their importance again. I would not attempt the exam without reading and understanding the required white papers. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you aspire to take the AWS certified DevOps Engineer exam I hope this helps you a little bit. Good luck.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2019/07/storage-nitty-gritty-3-of-5-nas-and-object-storage/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Storage Nitty-Gritty 3 of 5 – NAS and Object Storage&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2019/09/cryptographic-concepts-for-busy-it-professionals-2-of-2/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Cryptography basics 2 of 2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>