├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── images ├── WX20190513-185003@2x.png ├── WX20190513-185744@2x.png ├── WX20190514-082743@2x.png ├── WX20190514-082958@2x.png ├── WX20190514-083514@2x.png ├── WX20190514-083629@2x.png ├── WX20190514-105347@2x.png ├── WX20190514-111847@2x.png ├── service-mesh-00.png └── service-mesh-01.png ├── k8s ├── customer │ ├── v1 │ │ └── app.yaml │ ├── v15 │ │ └── app.yaml │ └── v2 │ │ └── app.yaml ├── delete_all.sh ├── deploy_customer_v2.sh ├── deploy_product_v15.sh ├── deploy_v1.sh ├── order │ └── v1 │ │ └── app.yaml └── product │ ├── v1 │ └── app.yaml │ ├── v15 │ └── app.yaml │ └── v2 │ └── app.yaml ├── mesh ├── 01-create_mesh.sh ├── 02-update-mesh-v15.sh ├── 03-deploy-prod-v15-canary.sh ├── 04-deploy-prod-full.sh ├── 05-update-mesh-v2.sh ├── 06-deploy-customer-v2-canary.sh ├── 07-deploy-customer-full.sh ├── 08-rollback-v1.sh ├── 99-delete_mesh.sh ├── v1 │ ├── customer │ │ ├── r.json │ │ ├── vn.json │ │ ├── vr.json │ │ └── vs.json │ ├── order │ │ ├── r.json │ │ ├── vn.json │ │ └── vr.json │ └── product │ │ ├── r.json │ │ ├── vn.json │ │ ├── vr.json │ │ └── vs.json ├── v15 │ ├── product-canary.json │ ├── product-full.json │ └── product-v15-vn.json └── v2 │ ├── customer-canary.json │ ├── customer-full.json │ └── customer-v2-vn.json ├── service ├── .gitignore ├── Dockerfile ├── Gopkg.lock ├── Gopkg.toml ├── Makefile ├── OrderService │ ├── Dockerfile │ ├── Gopkg.lock │ ├── Gopkg.toml │ ├── Makefile │ └── main.go └── main.go └── stepfunc ├── lambda ├── .gitignore ├── Dockerfile ├── Makefile ├── build.sh ├── func.d │ ├── bootstrap │ ├── cli-input.json.template │ └── main.sh └── sam.yaml └── spec.json /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/aws-samples/eks-canary-deployment-stepfunction/issues), or [recently closed](https://github.com/aws-samples/eks-canary-deployment-stepfunction/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws-samples/eks-canary-deployment-stepfunction/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/aws-samples/eks-canary-deployment-stepfunction/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Amazon Canary Deployment with AWS Step Function 2 | 3 | Reference Architecture of Canary Deployment with Amazon EKS and AWS Step Functions 4 | 5 | ## Architecture Overview 6 | 7 | ![](images/service-mesh-01.png) 8 | 9 | (Diagram A) 10 | 11 | ## Main Services 12 | 13 | - **Order Service** - A public-facing service API that query product service and customer service and response to the client 14 | - **Product Service** - Internal service for product info lookup. 15 | - **Customer Service** - Internal service for customer info lookup. 16 | 17 | 18 | 19 | ![](images/service-mesh-00.png) 20 | 21 | (Diagram B) 22 | 23 | ## Step 1 - Creating the Mesh 24 | 25 | ```bash 26 | cd ./mesh 27 | bash 01-create_mesh.sh 28 | ``` 29 | 30 | This will create `demomesh` as the service mesh and `virtual node`, `virtual router`, and `route` for `3` services. And 2 `virtual services` for `customer service` and `product service` will be created as well. 31 | 32 | 33 | 34 | ## Step 2 - Deploy the v1 services into Amazon EKS 35 | 36 | Now we're gonna deploy v1 of the 3 services into the Amazon EKS cluster. 37 | 38 | ``` 39 | cd ../k8s 40 | bash deploy_v1.sh 41 | ``` 42 | 43 | Response 44 | 45 | ``` 46 | deployment.extensions/product created 47 | service/product created 48 | deployment.extensions/customer created 49 | service/customer created 50 | deployment.extensions/order created 51 | service/order created 52 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 53 | service/customer ClusterIP 10.100.113.89 8080/TCP 4s 54 | service/kubernetes ClusterIP 10.100.0.1 443/TCP 23h 55 | service/order LoadBalancer 10.100.27.80 80:31888/TCP 2s 56 | service/product ClusterIP 10.100.230.95 8080/TCP 6s 57 | 58 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 59 | deployment.extensions/customer 1 1 1 0 4s 60 | deployment.extensions/order 1 1 1 0 2s 61 | deployment.extensions/product 1 1 1 1 6s 62 | 63 | NAME READY STATUS RESTARTS AGE 64 | pod/customer-7976f6c89c-snwvg 0/2 PodInitializing 0 4s 65 | pod/order-868bb64dcf-dx6pt 0/2 PodInitializing 0 2s 66 | pod/product-74b96c4f46-g5rwr 2/2 Running 0 6s 67 | ``` 68 | 69 | The `product` and `customer` services will be exposed as `ClusterIP` while `order` service will be exposed with `LoadBalancer` type. It might take a few minutes before the ELB is ready. 70 | 71 | 72 | 73 | ## Step 3 - Test the Services 74 | 75 | Let's test the `product` service with `kubectl port-forward` like this: 76 | 77 | ``` 78 | $ kubectl port-forward deployment/product 8080:8080 79 | Forwarding from 127.0.0.1:8080 -> 8080 80 | Forwarding from [::1]:8080 -> 8080 81 | ``` 82 | 83 | Open another terminal and `cURL` on **localhost:8080** 84 | 85 | ``` 86 | $ curl localhost:8080/ 87 | {"service":"Product","version":"1.0"} 88 | ``` 89 | 90 | `Ctrl-c` to terminate the `port-forward` terminal and test `customer` service 91 | 92 | ``` 93 | $ kubectl port-forward deployment/customer 8080:8080 94 | Forwarding from 127.0.0.1:8080 -> 8080 95 | Forwarding from [::1]:8080 -> 8080 96 | ``` 97 | 98 | ``` 99 | $ curl localhost:8080/ 100 | {"service":"Customer","version":"1.0"} 101 | ``` 102 | 103 | Both `Product` and `Customer` services are running with version `1.0` now. 104 | 105 | Finally, let's test the `Order` service agains the ELB. 106 | 107 | ``` 108 | $ kubectl get svc/order 109 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 110 | order LoadBalancer 10.100.27.80 a8d4ac145757e11e99af00a7d2dd619f-1485101703.ap-south-1.elb.amazonaws.com 80:31888/TCP 6m7s 111 | ``` 112 | 113 | (please note your ELB DNS name should be different from above) 114 | 115 | ``` 116 | $ curl a8d4ac145757e11e99af00a7d2dd619f-1485101703.ap-south-1.elb.amazonaws.com 117 | {"service":Order, "version":1.0} 118 | {"service":"Product","version":"1.0"} 119 | {"service":"Customer","version":"1.0"} 120 | ``` 121 | 122 | Behind the scene, the order service on receiving the request will call `Product` and `Customer` services in parallel and return all the response to the client. Make sure they all return version `1.0` 123 | 124 | 125 | 126 | Let's run this command with `watch -n1` and keep this terminal open. 127 | 128 | ``` 129 | $ watch -n1 curl -s a8d4ac145757e11e99af00a7d2dd619f-1485101703.ap-south-1.elb.amazonaws.com 130 | ``` 131 | 132 | (This will execute the `curl` command every `1` second. You will see the respose is very consistent with all `1.0` version ) 133 | 134 | 135 | 136 | ## Step 4 - Deploy product service to 1.5 137 | 138 | Let's say if we are planing to upgrade the product service to `1.5`. So we deploy another `1.5` deployment: 139 | 140 | Open another terminal and run: 141 | 142 | ``` 143 | cd ../k8s 144 | bash deploy_product_v15.sh 145 | ``` 146 | 147 | Response 148 | 149 | ``` 150 | deployment.extensions/product-v15 created 151 | service/product-v15 created 152 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 153 | service/customer ClusterIP 10.100.113.89 8080/TCP 12m 154 | service/kubernetes ClusterIP 10.100.0.1 443/TCP 23h 155 | service/order LoadBalancer 10.100.27.80 a8d4ac145757e11e99af00a7d2dd619f-1485101703.ap-south-1.elb.amazonaws.com 80:31888/TCP 12m 156 | service/product ClusterIP 10.100.230.95 8080/TCP 12m 157 | service/product-v15 ClusterIP 10.100.82.11 8080/TCP 2s 158 | 159 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 160 | deployment.extensions/customer 1 1 1 1 12m 161 | deployment.extensions/order 1 1 1 1 12m 162 | deployment.extensions/product 1 1 1 1 12m 163 | deployment.extensions/product-v15 1 1 1 0 2s 164 | 165 | NAME READY STATUS RESTARTS AGE 166 | pod/customer-7976f6c89c-snwvg 2/2 Running 0 12m 167 | pod/order-868bb64dcf-dx6pt 2/2 Running 0 12m 168 | pod/product-74b96c4f46-g5rwr 2/2 Running 0 12m 169 | pod/product-v15-6db9ccbc58-tfmz2 0/2 PodInitializing 0 2s 170 | ``` 171 | 172 | A new standalone deployment named `product-v15` is being deployed. 173 | 174 | OK now we have `product-v1` running with live traffic while `product-v15` is also ready. Let's create a `virtual node` for `product-v15` and start the canary deployment on it. 175 | 176 | ``` 177 | cd ../mesh 178 | bash 02-update-mesh-v15.sh 179 | ``` 180 | 181 | (this will create a `virtual node` called `product-v15-vn` for the `product-v15` service) 182 | 183 | 184 | 185 | ## Step 5 - Canary Deployment on Product v1.5 186 | 187 | ``` 188 | bash 03-deploy-prod-v15-canary.sh 189 | ``` 190 | 191 | This will update the product vertiaul route between `v1` and `v15` with different traffic weight. 192 | 193 | ``` 194 | "weightedTargets": [ 195 | { 196 | "virtualNode": "product-vn", 197 | "weight": 75 198 | }, 199 | { 200 | "virtualNode": "product-v15-vn", 201 | "weight": 25 202 | } 203 | ] 204 | ``` 205 | 206 | check `./mesh/v15/product-canary.json` for details. You may update `product-canary.json` and run `bash 03-deploy-prod-v15-canary.sh` agan to apply the new traffic weight. 207 | 208 | ![](images/WX20190513-185003@2x.png) 209 | 210 | 211 | 212 | Now, let's switch 100% traffic to `v1.5`. 213 | 214 | ``` 215 | bash 04-deploy-prod-full.sh 216 | ``` 217 | 218 | Now the `Product` version is being steady at `1.5` because we've change the `virtual route` to switch `100%` traffic onto `product-v15`. 219 | 220 | ## Step 6 - Canary Deployment on Customer v2 221 | 222 | Similarily, let's deploy a `v2` for customer service with `kubectl` and canary deploy on it. 223 | 224 | 225 | 226 | ``` 227 | cd ../k8s 228 | bash deploy_customer_v2.sh 229 | ``` 230 | 231 | Response 232 | 233 | ``` 234 | deployment.extensions/customer-v2 created 235 | service/customer-v2 created 236 | NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE 237 | deployment.extensions/customer 1 1 1 1 28m 238 | deployment.extensions/customer-v2 1 1 1 0 2s 239 | deployment.extensions/order 1 1 1 1 27m 240 | deployment.extensions/product 1 1 1 1 28m 241 | deployment.extensions/product-v15 1 1 1 1 15m 242 | 243 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 244 | service/customer ClusterIP 10.100.113.89 8080/TCP 28m 245 | service/customer-v2 ClusterIP 10.100.102.79 8080/TCP 2s 246 | service/kubernetes ClusterIP 10.100.0.1 443/TCP 23h 247 | service/order LoadBalancer 10.100.27.80 a8d4ac145757e11e99af00a7d2dd619f-1485101703.ap-south-1.elb.amazonaws.com 80:31888/TCP 27m 248 | service/product ClusterIP 10.100.230.95 8080/TCP 28m 249 | service/product-v15 ClusterIP 10.100.82.11 8080/TCP 15m 250 | 251 | NAME READY STATUS RESTARTS AGE 252 | pod/customer-7976f6c89c-snwvg 2/2 Running 0 28m 253 | pod/customer-v2-5648d668df-wfpgc 0/2 PodInitializing 0 2s 254 | pod/order-868bb64dcf-dx6pt 2/2 Running 0 27m 255 | pod/product-74b96c4f46-g5rwr 2/2 Running 0 28m 256 | pod/product-v15-6db9ccbc58-tfmz2 2/2 Running 0 15m 257 | ``` 258 | 259 | Create a `customer-v2` virtual node for it. 260 | 261 | ``` 262 | cd ../mesh 263 | bash 05-update-mesh-v2.sh 264 | ``` 265 | 266 | Update the `virtual route` to canary deploy the `v2`. 267 | 268 | ``` 269 | bash 06-deploy-customer-v2-canary.sh 270 | ``` 271 | 272 | ![](images/WX20190513-185744@2x.png) 273 | 274 | You might also update `./v2/customer-canary.json` to set different weight of traffic and run again. 275 | 276 | When you are ready, switch 100% traffic to customer service v2 now: 277 | 278 | ``` 279 | bash 07-deploy-customer-full.sh 280 | ``` 281 | 282 | Now we have 100% traiffic going to `order` `v1.0`, `customer` `v2.0` and `product` `v1.5` with zero impact on the service. All traffic is being routed as expected. 283 | 284 | 285 | 286 | ## Step. 7 - Immediate Roll Back 287 | 288 | Chances are we might need an immediate rollback to previous all `v1.0` version. We can just do it like this: 289 | 290 | ``` 291 | $ bash 08-rollback-v1.sh 292 | ``` 293 | 294 | This will update the virtual route of `product` and `cutomer` services to set `100%` traffic going to the v1 virtual node. Immediately all traffic goes to `1.0` as below: 295 | 296 | ``` 297 | {"service":Order, "version":1.0} 298 | {"service":"Product","version":"1.0"} 299 | {"service":"Customer","version":"1.0"} 300 | ``` 301 | 302 | 303 | 304 | ## Conclusion 305 | 306 | I hope you enjoy this demo. Moving forward, you may consider using [aws/aws-app-mesh-inject](aws/aws-app-mesh-inject) to auto inject the sidecar proxy into your deployment to simplified the YAML configuration. 307 | 308 | 309 | 310 | ## Clean up 311 | 312 | delete the mesh 313 | 314 | ``` 315 | bash 99-delete_mesh.sh 316 | ``` 317 | 318 | delete the k8s deployment 319 | 320 | ``` 321 | cd ../k8s 322 | bash delete_all.sh 323 | ``` 324 | 325 | Response 326 | 327 | ``` 328 | deployment.extensions "product" deleted 329 | service "product" deleted 330 | deployment.extensions "customer" deleted 331 | service "customer" deleted 332 | deployment.extensions "order" deleted 333 | service "order" deleted 334 | deployment.extensions "product-v15" deleted 335 | service "product-v15" deleted 336 | deployment.extensions "customer-v2" deleted 337 | service "customer-v2" deleted 338 | ``` 339 | 340 | That's all. 341 | 342 | Have fun! 343 | 344 | 345 | 346 | # Step Function Integration for Automated Canary Deployment 347 | 348 | You may build your state machine with AWS Step Function for automated canary deployment. For example, 10% incremental traffic every 10 minutes until 100% or 10% initial canary traffic for 5min and immediately switch 100% after 10 minutes. AWS Step Functions gives you flexibility to define your canary deployment strategy while Lambda function being invoked by AWS Step Function can update the virtual route on AWS App Mesh for you. 349 | 350 | ![](images/WX20190514-082743@2x.png) 351 | 352 | 353 | 354 | ![](images/WX20190514-082958@2x.png) 355 | 356 | 357 | 358 | ## Step 1 - Prepare Your Lambda function 359 | 360 | We are going to build our own [aws-lambda-layer-awscli](https://github.com/aws-samples/aws-lambda-layer-awscli) from scratch 361 | 362 | ``` 363 | cd stepfunc/lambda/ 364 | make build layer-zip layer-upload layer-publish 365 | ``` 366 | 367 | Response 368 | 369 | ``` 370 | { 371 | "LayerVersionArn": "arn:aws:lambda:ap-south-1:903779448426:layer:awscli-layer:23", 372 | "Description": "awscli-layer", 373 | "CreatedDate": "2019-05-14T05:18:40.388+0000", 374 | "LayerArn": "arn:aws:lambda:ap-south-1:903779448426:layer:awscli-layer", 375 | "Content": { 376 | "CodeSize": 22977865, 377 | "CodeSha256": "Tg6Yjr99B1MXRSs0uGhSWaTZKdLtob3nHPugfn0eIAs=", 378 | "Location": "..." 379 | }, 380 | "Version": 23, 381 | "CompatibleRuntimes": [ 382 | "provided" 383 | ], 384 | "LicenseInfo": "MIT" 385 | } 386 | ``` 387 | 388 | Copy the **LayerVersionArn** value string. 389 | 390 | update `sam.yaml` and configure the `Layers` with **LayerVersionArn** provided above. 391 | 392 | ![](images/WX20190514-105347@2x.png) 393 | 394 | OK. Let's publish our Lambda function. 395 | 396 | update the `Makefile` and define your own parameters such as `S3BUCKET` and `LAMBDA_REGION`. Make sure your current IAM identity has read/write to `S3BUCKET` bucket and `LAMBDA_REGION` is the correct AWS region you are deploying to. 397 | 398 | ``` 399 | make sam-package sam-deploy 400 | ``` 401 | 402 | Response 403 | 404 | ``` 405 | Successfully packaged artifacts and wrote output template to file packaged.yaml. 406 | Execute the following command to deploy the packaged template 407 | aws cloudformation deploy --template-file /home/samcli/workdir/packaged.yaml --stack-name 408 | 409 | Waiting for changeset to be created.. 410 | Waiting for stack create/update to complete 411 | Successfully created/updated stack - eks-canary-stack 412 | # print the cloudformation stack outputs 413 | aws --region ap-south-1 cloudformation describe-stacks --stack-name "eks-canary-stack" --query 'Stacks[0].Outputs' 414 | [ 415 | { 416 | "Description": "Function ARN", 417 | "OutputKey": "FunctionArn", 418 | "OutputValue": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-Function-8BYBEJIPXV6D" 419 | } 420 | ] 421 | ``` 422 | 423 | Now your lambda function is deployed. Copy the **FunctionArn** value above. 424 | 425 | 426 | 427 | ## Step 2 - Prepare Your State Machine in Step Function 428 | 429 | Create a new step function with the provided spec JSON [here](https://github.com/pahud/aws-appmesh-eks-refarch/blob/master/stepfunc/spec.json). Make sure update the Task Resource arn to your own Lambda function arn above. 430 | 431 | 432 | 433 | ## Step 3 - Monitoring 434 | 435 | Before we execute the state machine, let's monitor our application with two seperate tabs or windows. 436 | 437 | 438 | 439 | In the 1st tab we monitor the different weighted targets in the product virtual route with `watch -n1`. The result will refresh every second. 440 | 441 | ``` 442 | $ watch -n1 aws appmesh describe-route --mesh-name demomesh --route-name product-r --virtual-router-name product-vr --query "route.spec.httpRoute.action.weightedTargets" --output table 443 | ------------------------------ 444 | | DescribeRoute | 445 | +-----------------+----------+ 446 | | virtualNode | weight | 447 | +-----------------+----------+ 448 | | product-vn | 100 | 449 | | product-v15-vn | 0 | 450 | +-----------------+----------+ 451 | ``` 452 | 453 | In the 2nd tab we keep curling on the ELB of the order service. 454 | 455 | ![](images/WX20190514-111847@2x.png) 456 | 457 | 458 | 459 | 460 | 461 | ## Step 4 - Execute the State Machine 462 | 463 | OK let's execute the state machine in step function. Watch the state machine in the step function console. 464 | 465 | ![](images/WX20190514-082743@2x.png) 466 | 467 | 468 | 469 | And also watch the changes of the two tabs. 470 | 471 | ![](images/WX20190514-083514@2x.png) 472 | 473 | 474 | 475 | ## Conclusion 476 | 477 | In this demo, we walks you through how to build a customized Amaozn EKS canary deployment with AWS Step Function, AWS Lambda and AWS App Mesh. I hope you enjoy it and customize your own deployment logic based on it. 478 | -------------------------------------------------------------------------------- /images/WX20190513-185003@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/WX20190513-185003@2x.png -------------------------------------------------------------------------------- /images/WX20190513-185744@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/WX20190513-185744@2x.png -------------------------------------------------------------------------------- /images/WX20190514-082743@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/WX20190514-082743@2x.png -------------------------------------------------------------------------------- /images/WX20190514-082958@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/WX20190514-082958@2x.png -------------------------------------------------------------------------------- /images/WX20190514-083514@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/WX20190514-083514@2x.png -------------------------------------------------------------------------------- /images/WX20190514-083629@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/WX20190514-083629@2x.png -------------------------------------------------------------------------------- /images/WX20190514-105347@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/WX20190514-105347@2x.png -------------------------------------------------------------------------------- /images/WX20190514-111847@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/WX20190514-111847@2x.png -------------------------------------------------------------------------------- /images/service-mesh-00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/service-mesh-00.png -------------------------------------------------------------------------------- /images/service-mesh-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/eks-canary-deployment-stepfunction/a64b95051ee09901f320d8f08fc8ddc9ca54d896/images/service-mesh-01.png -------------------------------------------------------------------------------- /k8s/customer/v1/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: customer 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | microsvc: customer 11 | spec: 12 | containers: 13 | - name: customer 14 | image: pahud/aws-appmesh-refarch-service:latest 15 | imagePullPolicy: Always 16 | env: 17 | - name: "serviceName" 18 | value: "Customer" 19 | - name: "versionNum" 20 | value: "1.0" 21 | ports: 22 | - name: customer 23 | containerPort: 8080 24 | - name: envoy 25 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.9.1.0-prod 26 | securityContext: 27 | runAsUser: 1337 28 | env: 29 | - name: "APPMESH_VIRTUAL_NODE_NAME" 30 | value: "mesh/demomesh/virtualNode/customer-vn" 31 | - name: "ENVOY_LOG_LEVEL" 32 | value: "info" 33 | - name: "AWS_REGION" 34 | value: "ap-south-1" 35 | initContainers: 36 | - name: proxyinit 37 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v2 38 | securityContext: 39 | capabilities: 40 | add: 41 | - NET_ADMIN 42 | env: 43 | - name: "APPMESH_START_ENABLED" 44 | value: "1" 45 | - name: "APPMESH_IGNORE_UID" 46 | value: "1337" 47 | - name: "APPMESH_ENVOY_INGRESS_PORT" 48 | value: "15000" 49 | - name: "APPMESH_ENVOY_EGRESS_PORT" 50 | value: "15001" 51 | - name: "APPMESH_APP_PORTS" 52 | value: "8080" 53 | - name: "APPMESH_EGRESS_IGNORED_IP" 54 | value: "169.254.169.254" 55 | --- 56 | apiVersion: v1 57 | kind: Service 58 | metadata: 59 | name: customer 60 | labels: 61 | microsvc: customer 62 | spec: 63 | selector: 64 | microsvc: customer 65 | type: ClusterIP 66 | ports: 67 | - port: 8080 68 | name: http 69 | targetPort: 8080 70 | protocol: TCP -------------------------------------------------------------------------------- /k8s/customer/v15/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: customer 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | microsvc: customer 11 | spec: 12 | containers: 13 | - name: customer 14 | image: pahud/aws-appmesh-refarch-service:latest 15 | imagePullPolicy: Always 16 | env: 17 | - name: "serviceName" 18 | value: "Customer" 19 | - name: "versionNum" 20 | value: "1.5" 21 | ports: 22 | - name: customer 23 | containerPort: 8080 24 | - name: envoy 25 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.9.1.0-prod 26 | securityContext: 27 | runAsUser: 1337 28 | env: 29 | - name: "APPMESH_VIRTUAL_NODE_NAME" 30 | value: "mesh/demomesh/virtualNode/customer-vn" 31 | - name: "ENVOY_LOG_LEVEL" 32 | value: "info" 33 | - name: "AWS_REGION" 34 | value: "ap-south-1" 35 | initContainers: 36 | - name: proxyinit 37 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v2 38 | securityContext: 39 | capabilities: 40 | add: 41 | - NET_ADMIN 42 | env: 43 | - name: "APPMESH_START_ENABLED" 44 | value: "1" 45 | - name: "APPMESH_IGNORE_UID" 46 | value: "1337" 47 | - name: "APPMESH_ENVOY_INGRESS_PORT" 48 | value: "15000" 49 | - name: "APPMESH_ENVOY_EGRESS_PORT" 50 | value: "15001" 51 | - name: "APPMESH_APP_PORTS" 52 | value: "8080" 53 | - name: "APPMESH_EGRESS_IGNORED_IP" 54 | value: "169.254.169.254" 55 | --- 56 | apiVersion: v1 57 | kind: Service 58 | metadata: 59 | name: customer 60 | labels: 61 | microsvc: customer 62 | spec: 63 | selector: 64 | microsvc: customer 65 | type: ClusterIP 66 | ports: 67 | - port: 8080 68 | name: http 69 | targetPort: 8080 70 | protocol: TCP -------------------------------------------------------------------------------- /k8s/customer/v2/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: customer-v2 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | microsvc: customer-v2 11 | spec: 12 | containers: 13 | - name: customer 14 | image: pahud/aws-appmesh-refarch-service:latest 15 | imagePullPolicy: Always 16 | env: 17 | - name: "serviceName" 18 | value: "Customer" 19 | - name: "versionNum" 20 | value: "2.0" 21 | ports: 22 | - name: customer 23 | containerPort: 8080 24 | - name: envoy 25 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.9.1.0-prod 26 | securityContext: 27 | runAsUser: 1337 28 | env: 29 | - name: "APPMESH_VIRTUAL_NODE_NAME" 30 | value: "mesh/demomesh/virtualNode/customer-vn" 31 | - name: "ENVOY_LOG_LEVEL" 32 | value: "info" 33 | - name: "AWS_REGION" 34 | value: "ap-south-1" 35 | initContainers: 36 | - name: proxyinit 37 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v2 38 | securityContext: 39 | capabilities: 40 | add: 41 | - NET_ADMIN 42 | env: 43 | - name: "APPMESH_START_ENABLED" 44 | value: "1" 45 | - name: "APPMESH_IGNORE_UID" 46 | value: "1337" 47 | - name: "APPMESH_ENVOY_INGRESS_PORT" 48 | value: "15000" 49 | - name: "APPMESH_ENVOY_EGRESS_PORT" 50 | value: "15001" 51 | - name: "APPMESH_APP_PORTS" 52 | value: "8080" 53 | - name: "APPMESH_EGRESS_IGNORED_IP" 54 | value: "169.254.169.254" 55 | --- 56 | apiVersion: v1 57 | kind: Service 58 | metadata: 59 | name: customer-v2 60 | labels: 61 | microsvc: customer-v2 62 | spec: 63 | selector: 64 | microsvc: customer-v2 65 | type: ClusterIP 66 | ports: 67 | - port: 8080 68 | name: http 69 | targetPort: 8080 70 | protocol: TCP -------------------------------------------------------------------------------- /k8s/delete_all.sh: -------------------------------------------------------------------------------- 1 | kubectl delete -f product/v1/app.yaml 2 | kubectl delete -f customer/v1/app.yaml 3 | kubectl delete -f order/v1/app.yaml 4 | kubectl delete -f product/v15/app.yaml 5 | kubectl delete -f customer/v2/app.yaml 6 | -------------------------------------------------------------------------------- /k8s/deploy_customer_v2.sh: -------------------------------------------------------------------------------- 1 | kubectl apply -f customer/v2/app.yaml 2 | kubectl get deploy,svc,po 3 | # kubectl apply -f customer/v1/app.yaml 4 | # kubectl apply -f order/v1/app.yaml -------------------------------------------------------------------------------- /k8s/deploy_product_v15.sh: -------------------------------------------------------------------------------- 1 | kubectl apply -f product/v15/app.yaml 2 | kubectl get svc,deploy,po 3 | # kubectl apply -f customer/v1/app.yaml 4 | # kubectl apply -f order/v1/app.yaml -------------------------------------------------------------------------------- /k8s/deploy_v1.sh: -------------------------------------------------------------------------------- 1 | kubectl apply -f product/v1/app.yaml 2 | kubectl apply -f customer/v1/app.yaml 3 | kubectl apply -f order/v1/app.yaml 4 | kubectl get svc,deploy,pod -------------------------------------------------------------------------------- /k8s/order/v1/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: order 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | microsvc: order 11 | spec: 12 | containers: 13 | - name: order 14 | image: pahud/aws-appmesh-refarch-service:order-latest 15 | imagePullPolicy: Always 16 | env: 17 | - name: "serviceName" 18 | value: "Order" 19 | - name: "versionNum" 20 | value: "1.0" 21 | - name: "PRODUCT_SVC_URL" 22 | value: "http://product.default.svc.cluster.local:8080" 23 | - name: "CUSTOMER_SVC_URL" 24 | value: "http://customer.default.svc.cluster.local:8080" 25 | ports: 26 | - name: order 27 | containerPort: 8080 28 | - name: envoy 29 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.9.1.0-prod 30 | securityContext: 31 | runAsUser: 1337 32 | env: 33 | - name: "APPMESH_VIRTUAL_NODE_NAME" 34 | value: "mesh/demomesh/virtualNode/order-vn" 35 | - name: "ENVOY_LOG_LEVEL" 36 | value: "debug" 37 | - name: "AWS_REGION" 38 | value: "ap-south-1" 39 | initContainers: 40 | - name: proxyinit 41 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v2 42 | securityContext: 43 | capabilities: 44 | add: 45 | - NET_ADMIN 46 | env: 47 | - name: "APPMESH_START_ENABLED" 48 | value: "1" 49 | - name: "APPMESH_IGNORE_UID" 50 | value: "1337" 51 | - name: "APPMESH_ENVOY_INGRESS_PORT" 52 | value: "15000" 53 | - name: "APPMESH_ENVOY_EGRESS_PORT" 54 | value: "15001" 55 | - name: "APPMESH_APP_PORTS" 56 | value: "8080" 57 | - name: "APPMESH_EGRESS_IGNORED_IP" 58 | value: "169.254.169.254" 59 | --- 60 | apiVersion: v1 61 | kind: Service 62 | metadata: 63 | name: order 64 | labels: 65 | microsvc: order 66 | spec: 67 | selector: 68 | microsvc: order 69 | type: LoadBalancer 70 | ports: 71 | - port: 80 72 | name: http 73 | targetPort: 8080 74 | protocol: TCP -------------------------------------------------------------------------------- /k8s/product/v1/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: product 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | microsvc: product 11 | spec: 12 | containers: 13 | - name: product 14 | image: pahud/aws-appmesh-refarch-service:latest 15 | imagePullPolicy: Always 16 | env: 17 | - name: "serviceName" 18 | value: "Product" 19 | - name: "versionNum" 20 | value: "1.0" 21 | ports: 22 | - name: product 23 | containerPort: 8080 24 | - name: envoy 25 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.9.1.0-prod 26 | securityContext: 27 | runAsUser: 1337 28 | env: 29 | - name: "APPMESH_VIRTUAL_NODE_NAME" 30 | value: "mesh/demomesh/virtualNode/product-vn" 31 | - name: "ENVOY_LOG_LEVEL" 32 | value: "info" 33 | - name: "AWS_REGION" 34 | value: "ap-south-1" 35 | initContainers: 36 | - name: proxyinit 37 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v2 38 | securityContext: 39 | capabilities: 40 | add: 41 | - NET_ADMIN 42 | env: 43 | - name: "APPMESH_START_ENABLED" 44 | value: "1" 45 | - name: "APPMESH_IGNORE_UID" 46 | value: "1337" 47 | - name: "APPMESH_ENVOY_INGRESS_PORT" 48 | value: "15000" 49 | - name: "APPMESH_ENVOY_EGRESS_PORT" 50 | value: "15001" 51 | - name: "APPMESH_APP_PORTS" 52 | value: "8080" 53 | - name: "APPMESH_EGRESS_IGNORED_IP" 54 | value: "169.254.169.254" 55 | --- 56 | apiVersion: v1 57 | kind: Service 58 | metadata: 59 | name: product 60 | labels: 61 | microsvc: product 62 | spec: 63 | selector: 64 | microsvc: product 65 | type: ClusterIP 66 | ports: 67 | - port: 8080 68 | name: http 69 | targetPort: 8080 70 | protocol: TCP -------------------------------------------------------------------------------- /k8s/product/v15/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: product-v15 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | microsvc: product-v15 11 | spec: 12 | containers: 13 | - name: product 14 | image: pahud/aws-appmesh-refarch-service:latest 15 | imagePullPolicy: Always 16 | env: 17 | - name: "serviceName" 18 | value: "Product" 19 | - name: "versionNum" 20 | value: "1.5" 21 | ports: 22 | - name: product 23 | containerPort: 8080 24 | - name: envoy 25 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.9.1.0-prod 26 | securityContext: 27 | runAsUser: 1337 28 | env: 29 | - name: "APPMESH_VIRTUAL_NODE_NAME" 30 | value: "mesh/demomesh/virtualNode/product-vn" 31 | - name: "ENVOY_LOG_LEVEL" 32 | value: "info" 33 | - name: "AWS_REGION" 34 | value: "ap-south-1" 35 | initContainers: 36 | - name: proxyinit 37 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v2 38 | securityContext: 39 | capabilities: 40 | add: 41 | - NET_ADMIN 42 | env: 43 | - name: "APPMESH_START_ENABLED" 44 | value: "1" 45 | - name: "APPMESH_IGNORE_UID" 46 | value: "1337" 47 | - name: "APPMESH_ENVOY_INGRESS_PORT" 48 | value: "15000" 49 | - name: "APPMESH_ENVOY_EGRESS_PORT" 50 | value: "15001" 51 | - name: "APPMESH_APP_PORTS" 52 | value: "8080" 53 | - name: "APPMESH_EGRESS_IGNORED_IP" 54 | value: "169.254.169.254" 55 | --- 56 | apiVersion: v1 57 | kind: Service 58 | metadata: 59 | name: product-v15 60 | labels: 61 | microsvc: product-v15 62 | spec: 63 | selector: 64 | microsvc: product-v15 65 | type: ClusterIP 66 | ports: 67 | - port: 8080 68 | name: http 69 | targetPort: 8080 70 | protocol: TCP -------------------------------------------------------------------------------- /k8s/product/v2/app.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: extensions/v1beta1 2 | kind: Deployment 3 | metadata: 4 | name: product-v2 5 | spec: 6 | replicas: 1 7 | template: 8 | metadata: 9 | labels: 10 | microsvc: product-v2 11 | spec: 12 | containers: 13 | - name: product 14 | image: pahud/aws-appmesh-refarch-service:latest 15 | imagePullPolicy: Always 16 | env: 17 | - name: "serviceName" 18 | value: "Product" 19 | - name: "versionNum" 20 | value: "2.0" 21 | ports: 22 | - name: product 23 | containerPort: 8080 24 | - name: envoy 25 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.9.1.0-prod 26 | securityContext: 27 | runAsUser: 1337 28 | env: 29 | - name: "APPMESH_VIRTUAL_NODE_NAME" 30 | value: "mesh/demomesh/virtualNode/product-vn" 31 | - name: "ENVOY_LOG_LEVEL" 32 | value: "info" 33 | - name: "AWS_REGION" 34 | value: "ap-south-1" 35 | initContainers: 36 | - name: proxyinit 37 | image: 111345817488.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v2 38 | securityContext: 39 | capabilities: 40 | add: 41 | - NET_ADMIN 42 | env: 43 | - name: "APPMESH_START_ENABLED" 44 | value: "1" 45 | - name: "APPMESH_IGNORE_UID" 46 | value: "1337" 47 | - name: "APPMESH_ENVOY_INGRESS_PORT" 48 | value: "15000" 49 | - name: "APPMESH_ENVOY_EGRESS_PORT" 50 | value: "15001" 51 | - name: "APPMESH_APP_PORTS" 52 | value: "8080" 53 | - name: "APPMESH_EGRESS_IGNORED_IP" 54 | value: "169.254.169.254" 55 | --- 56 | apiVersion: v1 57 | kind: Service 58 | metadata: 59 | name: product-v2 60 | labels: 61 | microsvc: product-v2 62 | spec: 63 | selector: 64 | microsvc: product-v2 65 | type: ClusterIP 66 | ports: 67 | - port: 8080 68 | name: http 69 | targetPort: 8080 70 | protocol: TCP -------------------------------------------------------------------------------- /mesh/01-create_mesh.sh: -------------------------------------------------------------------------------- 1 | aws appmesh create-mesh --mesh-name demomesh 2 | 3 | # create virtual node/router for order service 4 | aws appmesh create-virtual-node --cli-input-json file://v1/order/vn.json 5 | aws appmesh create-virtual-router --cli-input-json file://v1/order/vr.json 6 | aws appmesh create-route --cli-input-json file://v1/order/r.json 7 | 8 | # create virtual node/router for product service 9 | aws appmesh create-virtual-node --cli-input-json file://v1/product/vn.json 10 | aws appmesh create-virtual-router --cli-input-json file://v1/product/vr.json 11 | aws appmesh create-route --cli-input-json file://v1/product/r.json 12 | 13 | # create virtual node/router for customer service 14 | aws appmesh create-virtual-node --cli-input-json file://v1/customer/vn.json 15 | aws appmesh create-virtual-router --cli-input-json file://v1/customer/vr.json 16 | aws appmesh create-route --cli-input-json file://v1/customer/r.json 17 | 18 | # create virtual services 19 | aws appmesh create-virtual-service --mesh-name demomesh --cli-input-json file://v1/customer/vs.json 20 | aws appmesh create-virtual-service --mesh-name demomesh --cli-input-json file://v1/product/vs.json 21 | -------------------------------------------------------------------------------- /mesh/02-update-mesh-v15.sh: -------------------------------------------------------------------------------- 1 | aws appmesh create-virtual-node --cli-input-json file://v15/product-v15-vn.json -------------------------------------------------------------------------------- /mesh/03-deploy-prod-v15-canary.sh: -------------------------------------------------------------------------------- 1 | aws appmesh update-route --cli-input-json file://v15/product-canary.json 2 | 3 | -------------------------------------------------------------------------------- /mesh/04-deploy-prod-full.sh: -------------------------------------------------------------------------------- 1 | aws appmesh update-route --cli-input-json file://v15/product-full.json 2 | 3 | -------------------------------------------------------------------------------- /mesh/05-update-mesh-v2.sh: -------------------------------------------------------------------------------- 1 | aws appmesh create-virtual-node --cli-input-json file://v2/customer-v2-vn.json -------------------------------------------------------------------------------- /mesh/06-deploy-customer-v2-canary.sh: -------------------------------------------------------------------------------- 1 | aws appmesh update-route --cli-input-json file://v2/customer-canary.json 2 | 3 | -------------------------------------------------------------------------------- /mesh/07-deploy-customer-full.sh: -------------------------------------------------------------------------------- 1 | aws appmesh update-route --cli-input-json file://v2/customer-full.json 2 | 3 | -------------------------------------------------------------------------------- /mesh/08-rollback-v1.sh: -------------------------------------------------------------------------------- 1 | aws appmesh update-route --cli-input-json file://v1/customer/r.json 2 | aws appmesh update-route --cli-input-json file://v1/product/r.json 3 | 4 | -------------------------------------------------------------------------------- /mesh/99-delete_mesh.sh: -------------------------------------------------------------------------------- 1 | # delete virtual services 2 | aws appmesh delete-virtual-service --mesh-name demomesh --virtual-service-name product.default.svc.cluster.local 3 | aws appmesh delete-virtual-service --mesh-name demomesh --virtual-service-name customer.default.svc.cluster.local 4 | 5 | aws appmesh delete-route --mesh-name demomesh --route-name product-r --virtual-router-name product-vr 6 | aws appmesh delete-route --mesh-name demomesh --route-name customer-r --virtual-router-name customer-vr 7 | aws appmesh delete-route --mesh-name demomesh --route-name order-r --virtual-router-name order-vr 8 | 9 | aws appmesh delete-virtual-router --mesh-name demomesh --virtual-router-name product-vr 10 | aws appmesh delete-virtual-router --mesh-name demomesh --virtual-router-name customer-vr 11 | aws appmesh delete-virtual-router --mesh-name demomesh --virtual-router-name order-vr 12 | 13 | aws appmesh delete-virtual-node --mesh-name demomesh --virtual-node-name product-vn 14 | aws appmesh delete-virtual-node --mesh-name demomesh --virtual-node-name customer-vn 15 | aws appmesh delete-virtual-node --mesh-name demomesh --virtual-node-name order-vn 16 | aws appmesh delete-virtual-node --mesh-name demomesh --virtual-node-name product-v15-vn 17 | aws appmesh delete-virtual-node --mesh-name demomesh --virtual-node-name customer-v2-vn 18 | 19 | # aws appmesh delete-virtual-node --mesh-name demomesh --virtual-node-name product-v2-vn 20 | # aws appmesh delete-virtual-node --mesh-name demomesh --virtual-node-name customer-v15-vn 21 | 22 | aws appmesh delete-mesh --mesh-name demomesh -------------------------------------------------------------------------------- /mesh/v1/customer/r.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "customer-vr", 5 | 6 | "routeName": "customer-r", 7 | 8 | "spec": { 9 | "httpRoute": { 10 | "action": { 11 | "weightedTargets": [ 12 | { 13 | "virtualNode": "customer-vn", 14 | "weight": 100 15 | } 16 | ] 17 | }, 18 | "match": { 19 | "prefix": "/" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mesh/v1/customer/vn.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualNodeName": "customer-vn", 5 | 6 | "spec": { 7 | "listeners": [ 8 | { 9 | "portMapping": { 10 | "port": 8080, 11 | "protocol": "http" 12 | } 13 | } 14 | ], 15 | 16 | "serviceDiscovery": { 17 | "dns": { 18 | "hostname": "customer.default.svc.cluster.local" 19 | } 20 | 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mesh/v1/customer/vr.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "customer-vr", 5 | 6 | "spec": { 7 | "listeners": [ 8 | { 9 | "portMapping": { 10 | "port": 8080, 11 | "protocol": "http" 12 | } 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /mesh/v1/customer/vs.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec": { 3 | "provider": { 4 | "virtualRouter": { 5 | "virtualRouterName": "customer-vr" 6 | } 7 | } 8 | }, 9 | "virtualServiceName": "customer.default.svc.cluster.local" 10 | } 11 | -------------------------------------------------------------------------------- /mesh/v1/order/r.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "order-vr", 5 | 6 | "routeName": "order-r", 7 | 8 | "spec": { 9 | "httpRoute": { 10 | "action": { 11 | "weightedTargets": [ 12 | { 13 | "virtualNode": "order-vn", 14 | "weight": 100 15 | } 16 | ] 17 | }, 18 | "match": { 19 | "prefix": "/" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mesh/v1/order/vn.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualNodeName": "order-vn", 5 | 6 | "spec": { 7 | 8 | "backends": [ 9 | { 10 | "virtualService": { 11 | "virtualServiceName": "product.default.svc.cluster.local" 12 | } 13 | }, 14 | { 15 | "virtualService": { 16 | "virtualServiceName": "customer.default.svc.cluster.local" 17 | } 18 | } 19 | ], 20 | 21 | "listeners": [ 22 | { 23 | "portMapping": { 24 | "port": 8080, 25 | "protocol": "http" 26 | } 27 | } 28 | ], 29 | 30 | "serviceDiscovery": { 31 | "dns": { 32 | "hostname": "order.default.svc.cluster.local" 33 | } 34 | 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /mesh/v1/order/vr.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "order-vr", 5 | 6 | "spec": { 7 | "listeners": [ 8 | { 9 | "portMapping": { 10 | "port": 8080, 11 | "protocol": "http" 12 | } 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /mesh/v1/product/r.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "product-vr", 5 | 6 | "routeName": "product-r", 7 | 8 | "spec": { 9 | "httpRoute": { 10 | "action": { 11 | "weightedTargets": [ 12 | { 13 | "virtualNode": "product-vn", 14 | "weight": 100 15 | } 16 | ] 17 | }, 18 | "match": { 19 | "prefix": "/" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mesh/v1/product/vn.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualNodeName": "product-vn", 5 | 6 | "spec": { 7 | 8 | "listeners": [ 9 | { 10 | "portMapping": { 11 | "port": 8080, 12 | "protocol": "http" 13 | } 14 | } 15 | ], 16 | 17 | "serviceDiscovery": { 18 | "dns": { 19 | "hostname": "product.default.svc.cluster.local" 20 | } 21 | 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mesh/v1/product/vr.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "product-vr", 5 | 6 | "spec": { 7 | "listeners": [ 8 | { 9 | "portMapping": { 10 | "port": 8080, 11 | "protocol": "http" 12 | } 13 | } 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /mesh/v1/product/vs.json: -------------------------------------------------------------------------------- 1 | { 2 | "spec": { 3 | "provider": { 4 | "virtualRouter": { 5 | "virtualRouterName": "product-vr" 6 | } 7 | } 8 | }, 9 | "virtualServiceName": "product.default.svc.cluster.local" 10 | } 11 | -------------------------------------------------------------------------------- /mesh/v15/product-canary.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "product-vr", 5 | 6 | "routeName": "product-r", 7 | 8 | "spec": { 9 | "httpRoute": { 10 | "action": { 11 | "weightedTargets": [ 12 | { 13 | "virtualNode": "product-v15-vn", 14 | "weight": 75 15 | }, 16 | { 17 | "virtualNode": "product-v15-vn", 18 | "weight": 25 19 | } 20 | ] 21 | }, 22 | "match": { 23 | "prefix": "/" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mesh/v15/product-full.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "product-vr", 5 | 6 | "routeName": "product-r", 7 | 8 | "spec": { 9 | "httpRoute": { 10 | "action": { 11 | "weightedTargets": [ 12 | { 13 | "virtualNode": "product-v15-vn", 14 | "weight": 100 15 | } 16 | ] 17 | }, 18 | "match": { 19 | "prefix": "/" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mesh/v15/product-v15-vn.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualNodeName": "product-v15-vn", 5 | 6 | "spec": { 7 | "listeners": [ 8 | { 9 | "portMapping": { 10 | "port": 8080, 11 | "protocol": "http" 12 | } 13 | } 14 | ], 15 | 16 | "serviceDiscovery": { 17 | "dns": { 18 | "hostname": "product-v15.default.svc.cluster.local" 19 | } 20 | 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mesh/v2/customer-canary.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "customer-vr", 5 | 6 | "routeName": "customer-r", 7 | 8 | "spec": { 9 | "httpRoute": { 10 | "action": { 11 | "weightedTargets": [ 12 | { 13 | "virtualNode": "customer-vn", 14 | "weight": 50 15 | }, 16 | { 17 | "virtualNode": "customer-v2-vn", 18 | "weight": 50 19 | } 20 | ] 21 | }, 22 | "match": { 23 | "prefix": "/" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mesh/v2/customer-full.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "customer-vr", 5 | 6 | "routeName": "customer-r", 7 | 8 | "spec": { 9 | "httpRoute": { 10 | "action": { 11 | "weightedTargets": [ 12 | { 13 | "virtualNode": "customer-v2-vn", 14 | "weight": 100 15 | } 16 | ] 17 | }, 18 | "match": { 19 | "prefix": "/" 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /mesh/v2/customer-v2-vn.json: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualNodeName": "customer-v2-vn", 5 | 6 | "spec": { 7 | "listeners": [ 8 | { 9 | "portMapping": { 10 | "port": 8080, 11 | "protocol": "http" 12 | } 13 | } 14 | ], 15 | 16 | "serviceDiscovery": { 17 | "dns": { 18 | "hostname": "customer-v2.default.svc.cluster.local" 19 | } 20 | 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /service/.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | -------------------------------------------------------------------------------- /service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | 3 | RUN apk add --no-cache make git 4 | 5 | WORKDIR /go/src/myapp.github.com 6 | 7 | COPY . . 8 | 9 | RUN make build-linux 10 | 11 | FROM alpine 12 | 13 | RUN apk --no-cache add ca-certificates 14 | 15 | WORKDIR /root/ 16 | 17 | COPY --from=builder /go/src/myapp.github.com/myapp . 18 | 19 | EXPOSE 8080 20 | 21 | CMD ["./myapp"] -------------------------------------------------------------------------------- /service/Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | branch = "master" 6 | digest = "1:a4b5a6b32f33323c7850fae4ec085a0fbac3be93c057b6b8cef5bcabe24645ac" 7 | name = "github.com/gin-contrib/sse" 8 | packages = ["."] 9 | pruneopts = "UT" 10 | revision = "5545eab6dad3bbbd6c5ae9186383c2a9d23c0dae" 11 | 12 | [[projects]] 13 | digest = "1:d8bd2a337f6ff2188e08f72c614f2f3f0fd48e6a7b37a071b197e427d77d3a47" 14 | name = "github.com/gin-gonic/gin" 15 | packages = [ 16 | ".", 17 | "binding", 18 | "internal/json", 19 | "render", 20 | ] 21 | pruneopts = "UT" 22 | revision = "b75d67cd51eb53c3c3a2fc406524c940021ffbda" 23 | version = "v1.4.0" 24 | 25 | [[projects]] 26 | digest = "1:318f1c959a8a740366fce4b1e1eb2fd914036b4af58fbd0a003349b305f118ad" 27 | name = "github.com/golang/protobuf" 28 | packages = ["proto"] 29 | pruneopts = "UT" 30 | revision = "c823c79ea1570fb5ff454033735a8e68575d1d0f" 31 | version = "v1.3.0" 32 | 33 | [[projects]] 34 | digest = "1:f5a2051c55d05548d2d4fd23d244027b59fbd943217df8aa3b5e170ac2fd6e1b" 35 | name = "github.com/json-iterator/go" 36 | packages = ["."] 37 | pruneopts = "UT" 38 | revision = "0ff49de124c6f76f8494e194af75bde0f1a49a29" 39 | version = "v1.1.6" 40 | 41 | [[projects]] 42 | digest = "1:e150b5fafbd7607e2d638e4e5cf43aa4100124e5593385147b0a74e2733d8b0d" 43 | name = "github.com/mattn/go-isatty" 44 | packages = ["."] 45 | pruneopts = "UT" 46 | revision = "c2a7a6ca930a4cd0bc33a3f298eb71960732a3a7" 47 | version = "v0.0.7" 48 | 49 | [[projects]] 50 | digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" 51 | name = "github.com/modern-go/concurrent" 52 | packages = ["."] 53 | pruneopts = "UT" 54 | revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" 55 | version = "1.0.3" 56 | 57 | [[projects]] 58 | digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" 59 | name = "github.com/modern-go/reflect2" 60 | packages = ["."] 61 | pruneopts = "UT" 62 | revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" 63 | version = "1.0.1" 64 | 65 | [[projects]] 66 | digest = "1:d0072748c62defde1ad99dde77f6ffce492a0e5aea9204077e497c7edfb86653" 67 | name = "github.com/ugorji/go" 68 | packages = ["codec"] 69 | pruneopts = "UT" 70 | revision = "2adff0894ba3bc2eeb9f9aea45fefd49802e1a13" 71 | version = "v1.1.4" 72 | 73 | [[projects]] 74 | branch = "master" 75 | digest = "1:4fc43e824f7e546353de724c00507b1d9c0bab56344b10c2330a97f166a0fba7" 76 | name = "golang.org/x/sys" 77 | packages = ["unix"] 78 | pruneopts = "UT" 79 | revision = "a5b02f93d862f065920dd6a40dddc66b60d0dec4" 80 | 81 | [[projects]] 82 | digest = "1:cbc72c4c4886a918d6ab4b95e347ffe259846260f99ebdd8a198c2331cf2b2e9" 83 | name = "gopkg.in/go-playground/validator.v8" 84 | packages = ["."] 85 | pruneopts = "UT" 86 | revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf" 87 | version = "v8.18.2" 88 | 89 | [[projects]] 90 | digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" 91 | name = "gopkg.in/yaml.v2" 92 | packages = ["."] 93 | pruneopts = "UT" 94 | revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" 95 | version = "v2.2.2" 96 | 97 | [solve-meta] 98 | analyzer-name = "dep" 99 | analyzer-version = 1 100 | input-imports = ["github.com/gin-gonic/gin"] 101 | solver-name = "gps-cdcl" 102 | solver-version = 1 103 | -------------------------------------------------------------------------------- /service/Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | name = "github.com/gin-gonic/gin" 30 | version = "1.4.0" 31 | 32 | [prune] 33 | go-tests = true 34 | unused-packages = true 35 | -------------------------------------------------------------------------------- /service/Makefile: -------------------------------------------------------------------------------- 1 | GOOSDEV ?= $(shell uname -s) 2 | GOARCH ?= amd64 3 | GOOS ?= $(shell uname -s | tr '[:upper:]' '[:lower:]') 4 | S3TMPBUCKET ?= tmp.pahud.net 5 | APPNAME = myapp 6 | 7 | .PHONY: build 8 | build: 9 | ifeq ($(GOOS),darwin) 10 | @docker run -ti --rm -v $(shell pwd):/go/src/myapp.github.com -w /go/src/myapp.github.com golang:1.12 /bin/sh -c "make build-darwin" 11 | else 12 | @docker run -ti --rm -v $(shell pwd):/go/src/myapp.github.com -w /go/src/myapp.github.com golang:1.12 /bin/sh -c "make build-linux" 13 | endif 14 | 15 | 16 | .PHONY: stop 17 | stop: 18 | @docker rm -f $(APPNAME) || exit 0 19 | 20 | .PHONY: reload 21 | reload: 22 | @docker kill -s HUP $(APPNAME) 23 | 24 | .PHONY: exec 25 | exec: 26 | @docker run --rm \ 27 | -v $(shell pwd):/go/src/myapp.github.com \ 28 | -p 8080:8080 \ 29 | -e serviceName=foo \ 30 | -e versionNum=1.0 \ 31 | --name $(APPNAME) \ 32 | -w /go/src/myapp.github.com golang:1.12 \ 33 | /bin/sh -c "./myapp" 34 | 35 | .PHONY: run 36 | run: 37 | @docker run --rm \ 38 | -v $(shell pwd):/go/src/myapp.github.com \ 39 | -p 8080:8080 \ 40 | -e serviceName=foo \ 41 | -e versionNum=1.0 \ 42 | --name $(APPNAME) \ 43 | -w /go/src/myapp.github.com golang:1.12 \ 44 | /bin/sh -c "go run main.go" 45 | 46 | .PHONY: build-linux 47 | build-linux: 48 | @go get -u github.com/golang/dep/cmd/dep 49 | @[ ! -f ./Gopkg.toml ] && dep init || true 50 | @dep ensure 51 | @GOOS=linux GOARCH=amd64 go build -o $(APPNAME) main.go 52 | 53 | .PHONY: build-darwin 54 | build-darwin: 55 | @go get -u github.com/golang/dep/cmd/dep 56 | @[ ! -f ./Gopkg.toml ] && dep init || true 57 | @dep ensure 58 | @GOOS=darwin GOARCH=amd64 go build -o $(APPNAME) main.go 59 | 60 | 61 | .PHONY: docker-build 62 | docker-build: 63 | @docker build -t pahud/aws-appmesh-refarch-service:latest . 64 | 65 | .PHONY: docker-push 66 | docker-push: 67 | @docker push pahud/aws-appmesh-refarch-service:latest 68 | 69 | .PHONY: clean 70 | clean: 71 | rm -rf Gopkg.* vendor $(APPNAME) 72 | 73 | 74 | -------------------------------------------------------------------------------- /service/OrderService/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | 3 | RUN apk add --no-cache make git 4 | 5 | WORKDIR /go/src/myapp.github.com 6 | 7 | COPY . . 8 | 9 | RUN make build-linux 10 | 11 | FROM alpine 12 | 13 | RUN apk --no-cache add ca-certificates 14 | 15 | WORKDIR /root/ 16 | 17 | COPY --from=builder /go/src/myapp.github.com/myapp . 18 | 19 | EXPOSE 8080 20 | 21 | CMD ["./myapp"] -------------------------------------------------------------------------------- /service/OrderService/Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | branch = "master" 6 | digest = "1:a4b5a6b32f33323c7850fae4ec085a0fbac3be93c057b6b8cef5bcabe24645ac" 7 | name = "github.com/gin-contrib/sse" 8 | packages = ["."] 9 | pruneopts = "UT" 10 | revision = "5545eab6dad3bbbd6c5ae9186383c2a9d23c0dae" 11 | 12 | [[projects]] 13 | digest = "1:d8bd2a337f6ff2188e08f72c614f2f3f0fd48e6a7b37a071b197e427d77d3a47" 14 | name = "github.com/gin-gonic/gin" 15 | packages = [ 16 | ".", 17 | "binding", 18 | "internal/json", 19 | "render", 20 | ] 21 | pruneopts = "UT" 22 | revision = "b75d67cd51eb53c3c3a2fc406524c940021ffbda" 23 | version = "v1.4.0" 24 | 25 | [[projects]] 26 | digest = "1:318f1c959a8a740366fce4b1e1eb2fd914036b4af58fbd0a003349b305f118ad" 27 | name = "github.com/golang/protobuf" 28 | packages = ["proto"] 29 | pruneopts = "UT" 30 | revision = "c823c79ea1570fb5ff454033735a8e68575d1d0f" 31 | version = "v1.3.0" 32 | 33 | [[projects]] 34 | digest = "1:f5a2051c55d05548d2d4fd23d244027b59fbd943217df8aa3b5e170ac2fd6e1b" 35 | name = "github.com/json-iterator/go" 36 | packages = ["."] 37 | pruneopts = "UT" 38 | revision = "0ff49de124c6f76f8494e194af75bde0f1a49a29" 39 | version = "v1.1.6" 40 | 41 | [[projects]] 42 | digest = "1:e150b5fafbd7607e2d638e4e5cf43aa4100124e5593385147b0a74e2733d8b0d" 43 | name = "github.com/mattn/go-isatty" 44 | packages = ["."] 45 | pruneopts = "UT" 46 | revision = "c2a7a6ca930a4cd0bc33a3f298eb71960732a3a7" 47 | version = "v0.0.7" 48 | 49 | [[projects]] 50 | digest = "1:33422d238f147d247752996a26574ac48dcf472976eda7f5134015f06bf16563" 51 | name = "github.com/modern-go/concurrent" 52 | packages = ["."] 53 | pruneopts = "UT" 54 | revision = "bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94" 55 | version = "1.0.3" 56 | 57 | [[projects]] 58 | digest = "1:e32bdbdb7c377a07a9a46378290059822efdce5c8d96fe71940d87cb4f918855" 59 | name = "github.com/modern-go/reflect2" 60 | packages = ["."] 61 | pruneopts = "UT" 62 | revision = "4b7aa43c6742a2c18fdef89dd197aaae7dac7ccd" 63 | version = "1.0.1" 64 | 65 | [[projects]] 66 | digest = "1:d0072748c62defde1ad99dde77f6ffce492a0e5aea9204077e497c7edfb86653" 67 | name = "github.com/ugorji/go" 68 | packages = ["codec"] 69 | pruneopts = "UT" 70 | revision = "2adff0894ba3bc2eeb9f9aea45fefd49802e1a13" 71 | version = "v1.1.4" 72 | 73 | [[projects]] 74 | branch = "master" 75 | digest = "1:4fc43e824f7e546353de724c00507b1d9c0bab56344b10c2330a97f166a0fba7" 76 | name = "golang.org/x/sys" 77 | packages = ["unix"] 78 | pruneopts = "UT" 79 | revision = "a5b02f93d862f065920dd6a40dddc66b60d0dec4" 80 | 81 | [[projects]] 82 | digest = "1:cbc72c4c4886a918d6ab4b95e347ffe259846260f99ebdd8a198c2331cf2b2e9" 83 | name = "gopkg.in/go-playground/validator.v8" 84 | packages = ["."] 85 | pruneopts = "UT" 86 | revision = "5f1438d3fca68893a817e4a66806cea46a9e4ebf" 87 | version = "v8.18.2" 88 | 89 | [[projects]] 90 | digest = "1:4d2e5a73dc1500038e504a8d78b986630e3626dc027bc030ba5c75da257cdb96" 91 | name = "gopkg.in/yaml.v2" 92 | packages = ["."] 93 | pruneopts = "UT" 94 | revision = "51d6538a90f86fe93ac480b35f37b2be17fef232" 95 | version = "v2.2.2" 96 | 97 | [solve-meta] 98 | analyzer-name = "dep" 99 | analyzer-version = 1 100 | input-imports = ["github.com/gin-gonic/gin"] 101 | solver-name = "gps-cdcl" 102 | solver-version = 1 103 | -------------------------------------------------------------------------------- /service/OrderService/Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | 28 | [[constraint]] 29 | name = "github.com/gin-gonic/gin" 30 | version = "1.4.0" 31 | 32 | [prune] 33 | go-tests = true 34 | unused-packages = true 35 | -------------------------------------------------------------------------------- /service/OrderService/Makefile: -------------------------------------------------------------------------------- 1 | GOOSDEV ?= $(shell uname -s) 2 | GOARCH ?= amd64 3 | GOOS ?= $(shell uname -s | tr '[:upper:]' '[:lower:]') 4 | S3TMPBUCKET ?= tmp.pahud.net 5 | APPNAME = myapp 6 | 7 | .PHONY: build 8 | build: 9 | ifeq ($(GOOS),darwin) 10 | @docker run -ti --rm -v $(shell pwd):/go/src/myapp.github.com -w /go/src/myapp.github.com golang:1.12 /bin/sh -c "make build-darwin" 11 | else 12 | @docker run -ti --rm -v $(shell pwd):/go/src/myapp.github.com -w /go/src/myapp.github.com golang:1.12 /bin/sh -c "make build-linux" 13 | endif 14 | 15 | 16 | .PHONY: stop 17 | stop: 18 | @docker rm -f $(APPNAME) || exit 0 19 | 20 | .PHONY: reload 21 | reload: 22 | @docker kill -s HUP $(APPNAME) 23 | 24 | .PHONY: exec 25 | exec: 26 | @docker run --rm \ 27 | -v $(shell pwd):/go/src/myapp.github.com \ 28 | -p 8080:8080 \ 29 | -e serviceName=foo \ 30 | -e versionNum=1.0 \ 31 | --name $(APPNAME) \ 32 | -w /go/src/myapp.github.com golang:1.12 \ 33 | /bin/sh -c "./myapp" 34 | 35 | .PHONY: run 36 | run: 37 | @docker run --rm \ 38 | -v $(shell pwd):/go/src/myapp.github.com \ 39 | -p 8080:8080 \ 40 | -e serviceName=foo \ 41 | -e versionNum=1.0 \ 42 | -e PRODUCT_SVC_URL=http://myipddd.today \ 43 | -e CUSTOMER_SVC_URL=http://myip.today \ 44 | --name $(APPNAME) \ 45 | -w /go/src/myapp.github.com golang:1.12 \ 46 | /bin/sh -c "go run main.go" 47 | 48 | .PHONY: build-linux 49 | build-linux: 50 | @go get -u github.com/golang/dep/cmd/dep 51 | @[ ! -f ./Gopkg.toml ] && dep init || true 52 | @dep ensure 53 | @GOOS=linux GOARCH=amd64 go build -o $(APPNAME) main.go 54 | 55 | .PHONY: build-darwin 56 | build-darwin: 57 | @go get -u github.com/golang/dep/cmd/dep 58 | @[ ! -f ./Gopkg.toml ] && dep init || true 59 | @dep ensure 60 | @GOOS=darwin GOARCH=amd64 go build -o $(APPNAME) main.go 61 | 62 | 63 | .PHONY: docker-build 64 | docker-build: 65 | @docker build -t pahud/aws-appmesh-refarch-service:order-latest . 66 | 67 | .PHONY: docker-push 68 | docker-push: 69 | @docker push pahud/aws-appmesh-refarch-service:order-latest 70 | 71 | .PHONY: clean 72 | clean: 73 | rm -rf Gopkg.* vendor $(APPNAME) 74 | 75 | 76 | -------------------------------------------------------------------------------- /service/OrderService/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "os" 10 | "strings" 11 | ) 12 | 13 | func HttpGet(url string, c chan<- string) { 14 | resp, err := http.Get(url) 15 | if err != nil { 16 | c <- "error" 17 | log.Println(err) 18 | } else { 19 | defer resp.Body.Close() 20 | body, _ := ioutil.ReadAll(resp.Body) 21 | c <- string(body) 22 | } 23 | 24 | } 25 | 26 | func fetchServices() []string { 27 | // fetch other microservices result 28 | ch := make(chan string, 2) 29 | 30 | microservices := []string{} 31 | buf := []string{} 32 | 33 | if v, ok := os.LookupEnv("PRODUCT_SVC_URL"); ok { 34 | fmt.Println("got PRODUCT_SVC_URL") 35 | PRODUCT_SVC_URL := v 36 | microservices = append(microservices, PRODUCT_SVC_URL) 37 | // go HttpGet(PRODUCT_SVC_URL, ch) 38 | } 39 | if v, ok := os.LookupEnv("CUSTOMER_SVC_URL"); ok { 40 | fmt.Println("got CUSTOMER_SVC_URL") 41 | CUSTOMER_SVC_URL := v 42 | microservices = append(microservices, CUSTOMER_SVC_URL) 43 | // go HttpGet(CUSTOMER_SVC_URL, ch) 44 | } 45 | 46 | for _, url := range microservices { 47 | fmt.Printf("fetching %s\n", url) 48 | go HttpGet(url, ch) 49 | } 50 | 51 | for range microservices { 52 | o := <-ch 53 | buf = append(buf, o) 54 | } 55 | return buf 56 | } 57 | 58 | func main() { 59 | 60 | r := gin.Default() 61 | r.GET("/", func(c *gin.Context) { 62 | serviceName, versionNum := "undefined", "undefined" 63 | 64 | if v, ok := os.LookupEnv("serviceName"); ok { 65 | serviceName = v 66 | } 67 | 68 | if v, ok := os.LookupEnv("versionNum"); ok { 69 | versionNum = v 70 | } 71 | // c.JSON(200, gin.H{ 72 | // "service": serviceName, 73 | // "version": versionNum, 74 | // }) 75 | buf := fetchServices() 76 | c.String(http.StatusOK, "{\"service\":%s, \"version\":%s}\n%s\n", serviceName, versionNum, strings.Join(buf, "\n")) 77 | 78 | }) 79 | r.Run() // listen and serve on 0.0.0.0:8080 80 | } 81 | -------------------------------------------------------------------------------- /service/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | serviceName, versionNum := "undefined", "undefined" 10 | 11 | if v, ok := os.LookupEnv("serviceName"); ok { 12 | serviceName = v 13 | } 14 | 15 | if v, ok := os.LookupEnv("versionNum"); ok { 16 | versionNum = v 17 | } 18 | 19 | r := gin.Default() 20 | r.GET("/", func(c *gin.Context) { 21 | c.JSON(200, gin.H{ 22 | "service": serviceName, 23 | "version": versionNum, 24 | }) 25 | }) 26 | r.Run() // listen and serve on 0.0.0.0:8080 27 | } 28 | -------------------------------------------------------------------------------- /stepfunc/lambda/.gitignore: -------------------------------------------------------------------------------- 1 | *.zip 2 | packaged.yaml 3 | layer 4 | -------------------------------------------------------------------------------- /stepfunc/lambda/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM amazonlinux:latest 2 | 3 | WORKDIR /root 4 | 5 | RUN yum update -y && yum install -y unzip bc 6 | 7 | ADD https://s3.amazonaws.com/aws-cli/awscli-bundle.zip /root 8 | 9 | 10 | RUN unzip awscli-bundle.zip && \ 11 | cd awscli-bundle; 12 | 13 | #RUN ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws 14 | RUN ./awscli-bundle/install -i /opt/awscli -b /opt/awscli/aws 15 | 16 | # install bc 17 | RUN cp /usr/bin/bc /opt/awscli/bin/ \ 18 | && cp /lib64/libncurses.so.6 /opt/awscli/lib64/ \ 19 | && cp /lib64/libtinfo.so.6 /opt/awscli/lib64/ 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /stepfunc/lambda/Makefile: -------------------------------------------------------------------------------- 1 | LAYER_NAME ?= awscli-layer 2 | LAYER_DESC ?= awscli-layer 3 | S3BUCKET ?= pahud-tmp-ap-south-1 4 | LAMBDA_REGION ?= ap-south-1 5 | LAMBDA_FUNC_NAME ?= eks-canary 6 | LAMBDA_FUNC_DESC ?= 'eks canary deployment with app mesh' 7 | 8 | build: 9 | @bash build.sh 10 | 11 | layer-zip: 12 | ( cd layer; zip -r ../layer.zip * ) 13 | 14 | layer-upload: 15 | @aws s3 cp layer.zip s3://$(S3BUCKET)/$(LAYER_NAME).zip 16 | 17 | layer-publish: 18 | @aws --region $(LAMBDA_REGION) lambda publish-layer-version \ 19 | --layer-name $(LAYER_NAME) \ 20 | --description $(LAYER_DESC) \ 21 | --license-info "MIT" \ 22 | --content S3Bucket=$(S3BUCKET),S3Key=$(LAYER_NAME).zip \ 23 | --compatible-runtimes provided 24 | 25 | func-zip: 26 | chmod +x main.sh 27 | zip -r func-bundle.zip bootstrap main.sh; ls -alh func-bundle.zip 28 | 29 | create-func: func-zip 30 | @aws --region $(LAMBDA_REGION) lambda create-function \ 31 | --function-name $(LAMBDA_FUNC_NAME) \ 32 | --description $(LAMBDA_FUNC_DESC) \ 33 | --runtime provided \ 34 | --role $(LAMBDA_ROLE_ARN) \ 35 | --timeout 30 \ 36 | --layers $(LAMBDA_LAYERS) \ 37 | --handler main \ 38 | --zip-file fileb://func-bundle.zip 39 | 40 | update-func: func-zip 41 | @aws --region $(LAMBDA_REGION) lambda update-function-code \ 42 | --function-name $(LAMBDA_FUNC_NAME) \ 43 | --zip-file fileb://func-bundle.zip 44 | 45 | func-all: func-zip update-func 46 | layer-all: build layer-upload layer-publish 47 | 48 | 49 | invoke: 50 | @aws --region $(LAMBDA_REGION) lambda invoke --function-name $(LAMBDA_FUNC_NAME) \ 51 | --payload "" lambda.output --log-type Tail | jq -r .LogResult | base64 -d 52 | 53 | add-layer-version-permission: 54 | @aws --region $(LAMBDA_REGION) lambda add-layer-version-permission \ 55 | --layer-name $(LAYER_NAME) \ 56 | --version-number $(LAYER_VER) \ 57 | --statement-id public-all \ 58 | --action lambda:GetLayerVersion \ 59 | --principal '*' 60 | 61 | 62 | all: build layer-upload layer-publish 63 | 64 | sam-package: 65 | @docker run -ti \ 66 | -v $(PWD):/home/samcli/workdir \ 67 | -v $(HOME)/.aws:/home/samcli/.aws \ 68 | -w /home/samcli/workdir \ 69 | -e AWS_DEFAULT_REGION=$(LAMBDA_REGION) \ 70 | pahud/aws-sam-cli:latest sam package --template-file sam.yaml --s3-bucket $(S3BUCKET) --output-template-file packaged.yaml 71 | 72 | 73 | sam-deploy: 74 | @docker run -ti \ 75 | -v $(PWD):/home/samcli/workdir \ 76 | -v $(HOME)/.aws:/home/samcli/.aws \ 77 | -w /home/samcli/workdir \ 78 | -e AWS_DEFAULT_REGION=$(LAMBDA_REGION) \ 79 | pahud/aws-sam-cli:latest sam deploy --template-file ./packaged.yaml --stack-name "$(LAMBDA_FUNC_NAME)-stack" --capabilities CAPABILITY_IAM 80 | # print the cloudformation stack outputs 81 | aws --region $(LAMBDA_REGION) cloudformation describe-stacks --stack-name "$(LAMBDA_FUNC_NAME)-stack" --query 'Stacks[0].Outputs' 82 | 83 | 84 | sam-destroy: 85 | # destroy the stack 86 | aws --region $(LAMBDA_REGION) cloudformation delete-stack --stack-name "$(LAMBDA_FUNC_NAME)-stack" 87 | 88 | 89 | clean: 90 | rm -rf awscli-bundle* layer layer.zip func-bundle.zip lambda.output 91 | 92 | clean-all: clean 93 | @aws --region $(LAMBDA_REGION) lambda delete-function --function-name $(LAMBDA_FUNC_NAME) 94 | 95 | 96 | -------------------------------------------------------------------------------- /stepfunc/lambda/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | [ ! -d ./layer/awscli ] && mkdir -p ./layer/awscli 4 | [ ! -d ./layer/lib ] && mkdir -p ./layer/lib 5 | 6 | docker build -t awscli:amazonlinux . 7 | CONTAINER=$(docker run -d awscli:amazonlinux false) 8 | docker cp ${CONTAINER}:/opt/awscli/lib/python2.7/site-packages/ layer/awscli/ 9 | docker cp ${CONTAINER}:/opt/awscli/bin/ layer/awscli/ 10 | docker cp ${CONTAINER}:/opt/awscli/lib64/libncurses.so.6 layer/lib/ 11 | docker cp ${CONTAINER}:/opt/awscli/lib64/libtinfo.so.6 layer/lib 12 | 13 | docker rm -f ${CONTAINER} 14 | 15 | 16 | mv layer/awscli/site-packages/* layer/awscli/ 17 | cp layer/awscli/bin/aws layer/awscli/aws 18 | 19 | # bc 20 | cp layer/awscli/bin/bc layer/awscli/bc 21 | 22 | # # extra libs 23 | # mv layer/awscli/lib64/* layer/ 24 | 25 | # remove unnecessary files to reduce the size 26 | rm -rf layer/awscli/pip* layer/awscli/setuptools* layer/awscli/awscli/examples 27 | 28 | # install jq 29 | wget https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 30 | mv jq-linux64 layer/awscli/jq 31 | chmod +x layer/awscli/jq 32 | 33 | # cd layer; zip -r ../layer.zip * 34 | -------------------------------------------------------------------------------- /stepfunc/lambda/func.d/bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -euo pipefail 4 | 5 | # Initialization - load function handler 6 | #source $LAMBDA_TASK_ROOT/"$(echo $_HANDLER | cut -d. -f1).sh" 7 | 8 | export PATH=$PATH:/opt 9 | 10 | # Processing 11 | while true 12 | do 13 | HEADERS="$(mktemp)" 14 | # Get an event 15 | EVENT_DATA=$(curl -sS -LD "$HEADERS" -X GET "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/next") 16 | REQUEST_ID=$(grep -Fi Lambda-Runtime-Aws-Request-Id "$HEADERS" | tr -d '[:space:]' | cut -d: -f2) 17 | 18 | # Execute the handler function from the script 19 | RESPONSE=$(./$(echo "$_HANDLER" | cut -d. -f2).sh "$EVENT_DATA") 20 | 21 | echo "=========[RESPONSE]=======" 22 | echo "$RESPONSE" 23 | echo "=========[/RESPONSE]=======" 24 | 25 | # Send the response 26 | curl -X POST "http://${AWS_LAMBDA_RUNTIME_API}/2018-06-01/runtime/invocation/$REQUEST_ID/response" -d "$RESPONSE" 27 | done -------------------------------------------------------------------------------- /stepfunc/lambda/func.d/cli-input.json.template: -------------------------------------------------------------------------------- 1 | { 2 | "meshName": "demomesh", 3 | 4 | "virtualRouterName": "product-vr", 5 | 6 | "routeName": "product-r", 7 | 8 | "spec": { 9 | "httpRoute": { 10 | "action": { 11 | "weightedTargets": [ 12 | { 13 | "virtualNode": "product-vn", 14 | "weight": #BLUE# 15 | }, 16 | { 17 | "virtualNode": "product-v15-vn", 18 | "weight": #GREEN# 19 | } 20 | ] 21 | }, 22 | "match": { 23 | "prefix": "/" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /stepfunc/lambda/func.d/main.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export PATH=$PATH:/opt/awscli 4 | 5 | get_blue_ratio(){ 6 | aws appmesh describe-route --mesh-name demomesh \ 7 | --route-name product-r \ 8 | --virtual-router-name product-vr \ 9 | --query "route.spec.httpRoute.action.weightedTargets[?virtualNode=='product-vn'].weight" \ 10 | --output text 11 | } 12 | 13 | update_route(){ 14 | # update_route blue_ratio green_ratio 15 | echo "update route now" 16 | blue="$1" 17 | green="$2" 18 | sed -e "s/#BLUE#/$blue/g" -e "s/#GREEN#/$green/g" cli-input.json.template > /tmp/cli-input.json 19 | aws appmesh update-route --cli-input-json file:///tmp/cli-input.json 20 | } 21 | 22 | update(){ 23 | blue_ratio=$(get_blue_ratio) 24 | green_ratio=$((100-blue_ratio)) 25 | echo "current: $blue_ratio $green_ratio" 26 | if [ $(($blue_ratio-10)) -gt 0 ]; then 27 | echo "update to: $(($blue_ratio-10)) $((100-$blue_ratio+10))" 28 | update_route "$(($blue_ratio-10))" "$((100-$blue_ratio+10))" 29 | elif [ $blue_ratio -eq 0 ]; then 30 | echo "done" 31 | elif [ $(($blue_ratio-10)) -lt 0 ]; then 32 | echo "update to: 0 100" 33 | update_route 0 100 34 | else 35 | echo "update to: 0 100" 36 | update_route 0 100 37 | fi 38 | } 39 | 40 | result="$(update)" 41 | 42 | cat << EOF 43 | {"body": "$result", "headers": {"content-type": "text/plain"}, "statusCode": 200} 44 | EOF -------------------------------------------------------------------------------- /stepfunc/lambda/sam.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Transform: AWS::Serverless-2016-10-31 3 | Description: Amazon EKS Canary Deployment Lambda function 4 | Resources: 5 | Function: 6 | Type: AWS::Serverless::Function 7 | Properties: 8 | Handler: main 9 | CodeUri: ./func.d 10 | Runtime: provided 11 | Layers: 12 | - !Sub "arn:aws:lambda:ap-south-1:903779448426:layer:layer-awscli-layer-stack:1" 13 | MemorySize: 512 14 | Policies: 15 | - AWSLambdaBasicExecutionRole 16 | - AWSAppMeshFullAccess 17 | Timeout: 10 18 | 19 | Outputs: 20 | FunctionArn: 21 | Description: Function ARN 22 | Value: !GetAtt Function.Arn -------------------------------------------------------------------------------- /stepfunc/spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "StartAt": "Here We Go", 3 | "States": { 4 | "Here We Go": { 5 | "Type": "Pass", 6 | "Result": "Hello World!", 7 | "Next": "Canary 10" 8 | }, 9 | 10 | "Canary 10": { 11 | "Type": "Task", 12 | "InputPath": null, 13 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 14 | "Next": "Wait10", 15 | "Catch": [{ 16 | "ErrorEquals": [ 17 | "States.ALL" 18 | ], 19 | "Next": "Roll Back" 20 | }] 21 | }, 22 | 23 | "Wait10": { 24 | "Type": "Wait", 25 | "Seconds": 10, 26 | "Next": "Canary 20" 27 | }, 28 | 29 | "Canary 20": { 30 | "Type": "Task", 31 | "InputPath": null, 32 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 33 | "Next": "Wait20", 34 | "Catch": [{ 35 | "ErrorEquals": [ 36 | "States.ALL" 37 | ], 38 | "Next": "Roll Back" 39 | }] 40 | }, 41 | 42 | "Wait20": { 43 | "Type": "Wait", 44 | "Seconds": 10, 45 | "Next": "Canary 30" 46 | }, 47 | 48 | "Canary 30": { 49 | "Type": "Task", 50 | "InputPath": null, 51 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 52 | "Next": "Wait30", 53 | "Catch": [{ 54 | "ErrorEquals": [ 55 | "States.ALL" 56 | ], 57 | "Next": "Roll Back" 58 | }] 59 | }, 60 | 61 | 62 | "Wait30": { 63 | "Type": "Wait", 64 | "Seconds": 10, 65 | "Next": "Canary 40" 66 | }, 67 | 68 | "Canary 40": { 69 | "Type": "Task", 70 | "InputPath": null, 71 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 72 | "Next": "Wait40", 73 | "Catch": [{ 74 | "ErrorEquals": [ 75 | "States.ALL" 76 | ], 77 | "Next": "Roll Back" 78 | }] 79 | }, 80 | 81 | "Wait40": { 82 | "Type": "Wait", 83 | "Seconds": 10, 84 | "Next": "Canary 50" 85 | }, 86 | 87 | "Canary 50": { 88 | "Type": "Task", 89 | "InputPath": null, 90 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 91 | "Next": "Wait50", 92 | "Catch": [{ 93 | "ErrorEquals": [ 94 | "States.ALL" 95 | ], 96 | "Next": "Roll Back" 97 | }] 98 | }, 99 | 100 | "Wait50": { 101 | "Type": "Wait", 102 | "Seconds": 10, 103 | "Next": "Canary 60" 104 | }, 105 | 106 | "Canary 60": { 107 | "Type": "Task", 108 | "InputPath": null, 109 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 110 | "Next": "Wait60", 111 | "Catch": [{ 112 | "ErrorEquals": [ 113 | "States.ALL" 114 | ], 115 | "Next": "Roll Back" 116 | }] 117 | }, 118 | 119 | "Wait60": { 120 | "Type": "Wait", 121 | "Seconds": 10, 122 | "Next": "Canary 70" 123 | }, 124 | 125 | "Canary 70": { 126 | "Type": "Task", 127 | "InputPath": null, 128 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 129 | "Next": "Wait70", 130 | "Catch": [{ 131 | "ErrorEquals": [ 132 | "States.ALL" 133 | ], 134 | "Next": "Roll Back" 135 | }] 136 | }, 137 | 138 | "Wait70": { 139 | "Type": "Wait", 140 | "Seconds": 10, 141 | "Next": "Canary 80" 142 | }, 143 | "Canary 80": { 144 | "Type": "Task", 145 | "InputPath": null, 146 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 147 | "Next": "Wait80", 148 | "Catch": [{ 149 | "ErrorEquals": [ 150 | "States.ALL" 151 | ], 152 | "Next": "Roll Back" 153 | }] 154 | }, 155 | 156 | "Wait80": { 157 | "Type": "Wait", 158 | "Seconds": 10, 159 | "Next": "Canary 90" 160 | }, 161 | 162 | "Canary 90": { 163 | "Type": "Task", 164 | "InputPath": null, 165 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 166 | "Next": "Wait90", 167 | "Catch": [{ 168 | "ErrorEquals": [ 169 | "States.ALL" 170 | ], 171 | "Next": "Roll Back" 172 | }] 173 | }, 174 | 175 | "Wait90": { 176 | "Type": "Wait", 177 | "Seconds": 10, 178 | "Next": "Canary 100" 179 | }, 180 | 181 | 182 | "Canary 100": { 183 | "Type": "Task", 184 | "InputPath": null, 185 | "Resource": "arn:aws:lambda:ap-south-1:903779448426:function:eks-canary-stack-SampleFunction-1GVNOT8HW76IN:$LATEST", 186 | "Next": "DONE", 187 | "Catch": [{ 188 | "ErrorEquals": [ 189 | "States.ALL" 190 | ], 191 | "Next": "Roll Back" 192 | }] 193 | }, 194 | 195 | 196 | "DONE": { 197 | "Type": "Pass", 198 | "Result": "DONE", 199 | "End": true 200 | }, 201 | 202 | "Roll Back": { 203 | "Type": "Pass", 204 | "Result": "DONE", 205 | "End": true 206 | } 207 | 208 | } 209 | 210 | } 211 | --------------------------------------------------------------------------------