<< Photos

Infrastructure as code: running microservices on AWS using Docker, Terraform, and ECS - Comment

This is a talk about managing your software and infrastructure-as-code that walks through a real-world example of deploying microservices on AWS using Docker, Terraform, and ECS.

Topics covered in this presentation slides:


1. INFRASTRUCTURE as CODE Running Microservices on AWS with Docker, Terraform, and ECS
2. Why infrastructure-as-code matters: a short story.
3. You are starting a new project
4. I know, I’ll use Ruby on Rails!
5. > gem install rails
6. > gem install rails Fetching: i18n-0.7.0.gem (100%) Fetching: json-1.8.3.gem (100%) Building native extensions. This could take a while... ERROR: Error installing rails: ERROR: Failed to build gem native extension. /usr/bin/ruby1.9.1 extconf.rb creating Makefile make sh: 1: make: not found
7. Ah, I just need to install make
8. > sudo apt-get install make ... Success!
9. > gem install rails
10. > gem install rails Fetching: nokogiri-1.6.7.2.gem (100%) Building native extensions. This could take a while... ERROR: Error installing rails: ERROR: Failed to build gem native extension. /usr/bin/ruby1.9.1 extconf.rb checking if the C compiler accepts ... yes Building nokogiri using packaged libraries. Using mini_portile version 2.0.0.rc2 checking for gzdopen() in -lz... no zlib is missing; necessary for building libxml2 *** extconf.rb failed ***
11. Hmm. Time to visit StackOverflow.
12. > sudo apt-get install zlib1g-dev ... Success!
13. > gem install rails
14. > gem install rails Building native extensions. This could take a while... ERROR: Error installing rails: ERROR: Failed to build gem native extension. /usr/bin/ruby1.9.1 extconf.rb checking if the C compiler accepts ... yes Building nokogiri using packaged libraries. Using mini_portile version 2.0.0.rc2 checking for gzdopen() in -lz... yes checking for iconv... yes Extracting libxml2-2.9.2.tar.gz into tmp/x86_64-pc-linux- gnu/ports/libxml2/2.9.2... OK *** extconf.rb failed ***
15. nokogiri y u never install correctly?
16. (Spend 2 hours trying random StackOverflow suggestions)
17. > gem install rails
18. > gem install rails ... Success!
19. Finally!
20. > rails new my-project > cd my-project > rails start
21. > rails new my-project > cd my-project > rails start /source/my-project/bin/spring:11:in `': undefined method `path_separator' for Gem:Module (NoMethodError) from bin/rails:3:in `load' from bin/rails:3:in `'
22. Eventually, you get it working
23. Now you have to deploy your Rails app in production
24. You use the AWS Console to deploy an EC2 instance
25. > ssh ec2-user@ec2-12-34-56-78.compute-1.amazonaws.com __| __|_ ) _| ( / Amazon Linux AMI ___|___|___| [ec2-user@ip-172-31-61-204 ~]$ gem install rails
26. > ssh ec2-user@ec2-12-34-56-78.compute-1.amazonaws.com __| __|_ ) _| ( / Amazon Linux AMI ___|___|___| [ec2-user@ip-172-31-61-204 ~]$ gem install rails ERROR: Error installing rails: ERROR: Failed to build gem native extension. /usr/bin/ruby1.9.1 extconf.rb
27. Eventually you get it working
28. Now you urgently have to update all your Rails installs
29. > bundle update rails
30. > bundle update rails Building native extensions. This could take a while... ERROR: Error installing rails: ERROR: Failed to build gem native extension. /usr/bin/ruby1.9.1 extconf.rb checking if the C compiler accepts ... yes Building nokogiri using packaged libraries. Using mini_portile version 2.0.0.rc2 checking for gzdopen() in -lz... yes checking for iconv... yes Extracting libxml2-2.9.2.tar.gz into tmp/x86_64-pc-linux- gnu/ports/libxml2/2.9.2... OK *** extconf.rb failed ***
31. The problem isn’t Rails
32. > ssh ec2-user@ec2-12-34-56-78.compute-1.amazonaws.com __| __|_ ) _| ( / Amazon Linux AMI ___|___|___| [ec2-user@ip-172-31-61-204 ~]$ gem install rails The problem is that you’re configuring servers manually
33. And that you’re deploying infrastructure manually
34. A better alternative: infrastructure- as-code
35. In this talk, we’ll go through a real-world example:
36. We’ll configure & deploy two microservices on Amazon ECS
37. With two infrastructure-as-code tools: Docker and Terraform TERRAFORM
38. I’m Yevgeniy Brikman ybrikman.com
39. Co-founder of Gruntwork gruntwork.io
40. gruntwork.io We offer DevOps as a Service
41. gruntwork.io And DevOps as a Library
42. PAST LIVES
43. Author of Hello, Startup hello-startup.net
44. And Terraform: Up & Running terraformupandrunning.com
45. Slides and code from this talk: ybrikman.com/speaking
46. 1. Microservices 2. Docker 3. Terraform 4. ECS 5. Recap Outline
47. 1. Microservices 2. Docker 3. Terraform 4. ECS 5. Recap Outline
48. Code is the enemy: the more you have, the slower you go
49. Project Size Lines of code Bug Density Bugs per thousand lines of code < 2K 0 – 25 2K – 6K 0 – 40 16K – 64K 0.5 – 50 64K – 512K 2 – 70 > 512K 4 – 100
50. As the code grows, the number of bugs grows even faster
51. “Software development doesn't happen in a chart, an IDE, or a design tool; it happens in your head.”
52. The mind can only handle so much complexity at once
53. One solution is to break the code into microservices
54. In a monolith, you use function calls within one process moduleA.func() moduleB.func() moduleC.func() moduleD.func() moduleE.func()
55. http://service.a http://service.b http://service.c http://service.d http://service.e With services, you pass messages between processes
56. Advantages of services: 1. Isolation 2. Technology agnostic 3. Scalability
57. Disadvantages of services: 1. Operational overhead 2. Performance overhead 3. I/O, error handling 4. Backwards compatibility 5. Global changes, transactions, referential integrity all very hard
58. For more info, see: Splitting Up a Codebase into Microservices and Artifacts
59. For this talk, we’ll use two example microservices
60. require 'sinatra' get "/" do "Hello, World!" end A sinatra backend that returns “Hello, World”
61. class ApplicationController < ActionController::Base def index url = URI.parse(backend_addr) req = Net::HTTP::Get.new(url.to_s) res = Net::HTTP.start(url.host, url.port) {|http| http.request(req) } @text = res.body end end A rails frontend that calls the sinatra backend
62. Rails Frontend Response from the backend: And renders the response as HTML
63. 1. Microservices 2. Docker 3. Terraform 4. ECS 5. Recap Outline
64. Docker allows you to build and run code in containers
65. Containers are like lightweight Virtual Machines (VMs)
66. VMs virtualize the hardware and run an entire guest OS on top of the host OS VM Hardware Host OS Host User Space Virtual Machine Virtualized hardware Guest OS Guest User Space App VM Virtualized hardware Guest OS Guest User Space App VM Virtualized hardware Guest OS Guest User Space App
67. This provides good isolation, but lots of CPU, memory, disk, & startup overhead VM Hardware Host OS Host User Space Virtual Machine Virtualized hardware Guest OS Guest User Space App VM Virtualized hardware Guest OS Guest User Space App VM Virtualized hardware Guest OS Guest User Space App
68. Containers virtualize User Space (shared memory, processes, mount, network) Container VM Hardware Host OS Host User Space Virtual Machine Virtualized hardware Guest OS Guest User Space App Hardware Host OS Host User Space Container Engine Virtualized User Space VM Virtualized hardware Guest OS Guest User Space App VM Virtualized hardware Guest OS Guest User Space App App Container Virtualized User Space App Container Virtualized User Space App
69. Container VM Hardware Host OS Host User Space Virtual Machine Virtualized hardware Guest OS Guest User Space App Hardware Host OS Host User Space Container Engine Virtualized User Space VM Virtualized hardware Guest OS Guest User Space App VM Virtualized hardware Guest OS Guest User Space App App Container Virtualized User Space App Container Virtualized User Space App Isolation isn’t as good but much less CPU, memory, disk, startup overhead
70. > docker run –it ubuntu bash root@12345:/# echo "I'm in $(cat /etc/issue)” I'm in Ubuntu 14.04.4 LTS Running Ubuntu in a Docker container
71. > time docker run ubuntu echo "Hello, World" Hello, World real 0m0.183s user 0m0.009s sys 0m0.014s Containers boot very quickly. Easily run a dozen at once.
72. You can define a Docker image as code in a Dockerfile
73. FROM gliderlabs/alpine:3.3 RUN apk --no-cache add ruby ruby-dev RUN gem install sinatra --no-ri --no-rdoc RUN mkdir -p /usr/src/app COPY . /usr/src/app WORKDIR /usr/src/app EXPOSE 4567 CMD ["ruby", "app.rb"] Here is the Dockerfile for the Sinatra backend
74. FROM gliderlabs/alpine:3.3 RUN apk --no-cache add ruby ruby-dev RUN gem install sinatra --no-ri --no-rdoc RUN mkdir -p /usr/src/app COPY . /usr/src/app WORKDIR /usr/src/app EXPOSE 4567 CMD ["ruby", "app.rb"] It specifies dependencies, code, config, and how to run the app
75. > docker build -t brikis98/sinatra-backend . Step 0 : FROM gliderlabs/alpine:3.3 ---> 0a7e169bce21 (...) Step 8 : CMD ruby app.rb ---> 2e243eba30ed Successfully built 2e243eba30ed Build the Docker image
76. > docker run -it -p 4567:4567 brikis98/sinatra-backend INFO WEBrick 1.3.1 INFO ruby 2.2.4 (2015-12-16) [x86_64-linux-musl] == Sinatra (v1.4.7) has taken the stage on 4567 for development with backup from WEBrick INFO WEBrick::HTTPServer#start: pid=1 port=4567 Run the Docker image
77. > docker push brikis98/sinatra-backend The push refers to a repository [docker.io/brikis98/sinatra- backend] (len: 1) 2e243eba30ed: Image successfully pushed 7e2e0c53e246: Image successfully pushed 919d9a73b500: Image successfully pushed (...) v1: digest: sha256:09f48ed773966ec7fe4558 size: 14319 You can share your images by pushing them to Docker Hub
78. Now you can reuse the same image in dev, stg, prod, etc
79. > docker pull rails:4.2.6 And you can reuse images created by others.
80. FROM rails:4.2.6 RUN mkdir -p /usr/src/app COPY . /usr/src/app WORKDIR /usr/src/app RUN bundle install EXPOSE 3000 CMD ["rails", "start"] The rails-frontend is built on top of the official rails Docker image
81. No more insane install procedures!
82. rails_frontend: image: brikis98/rails-frontend ports: - "3000:3000" links: - sinatra_backend:sinatra_backend sinatra_backend: image: brikis98/sinatra-backend ports: - "4567:4567" Define your entire dev stack as code with docker-compose
83. rails_frontend: image: brikis98/rails-frontend ports: - "3000:3000" links: - sinatra_backend sinatra_backend: image: brikis98/sinatra-backend ports: - "4567:4567" Docker links provide a simple service discovery mechanism
84. > docker-compose up Starting infrastructureascodetalk_sinatra_backend_1 Recreating infrastructureascodetalk_rails_frontend_1 sinatra_backend_1 | INFO WEBrick 1.3.1 sinatra_backend_1 | INFO ruby 2.2.4 (2015-12-16) sinatra_backend_1 | Sinatra has taken the stage on 4567 rails_frontend_1 | INFO WEBrick 1.3.1 rails_frontend_1 | INFO ruby 2.3.0 (2015-12-25) rails_frontend_1 | INFO WEBrick::HTTPServer#start: port=3000 Run your entire dev stack with one command
85. Advantages of Docker: 1. Easy to create & share images 2. Images run the same way in all environments (dev, test, prod) 3. Easily run the entire stack in dev 4. Minimal overhead 5. Better resource utilization
86. Disadvantages of Docker: 1. Maturity. Ecosystem developing very fast, but still a ways to go 2. Tricky to manage persistent data in a container 3. Tricky to pass secrets to containers
87. 1. Microservices 2. Docker 3. Terraform 4. ECS 5. Recap Outline
88. Terraform is a tool for provisioning infrastructure
89. Terraform supports many providers (cloud agnostic)
90. And many resources for each provider
91. You define infrastructure as code in Terraform templates
92. provider "aws" { region = "us-east-1" } resource "aws_instance" "example" { ami = "ami-408c7f28" instance_type = "t2.micro" } This template creates a single EC2 instance in AWS
93. > terraform plan + aws_instance.example ami: "" => "ami-408c7f28" instance_type: "" => "t2.micro" key_name: "" => "" private_ip: "" => "" public_ip: "" => "" Plan: 1 to add, 0 to change, 0 to destroy. Use the plan command to see what you’re about to deploy
94. > terraform apply aws_instance.example: Creating... ami: "" => "ami-408c7f28" instance_type: "" => "t2.micro" key_name: "" => "" private_ip: "" => "" public_ip: "" => "” aws_instance.example: Creation complete Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Use the apply command to apply the changes
95. Now our EC2 instance is running!
96. resource "aws_instance" "example" { ami = "ami-408c7f28" instance_type = "t2.micro" tags { Name = "terraform-example" } } Let’s give the EC2 instance a tag with a readable name
97. > terraform plan ~ aws_instance.example tags.#: "0" => "1" tags.Name: "" => "terraform-example" Plan: 0 to add, 1 to change, 0 to destroy. Use the plan command again to verify your changes
98. > terraform apply aws_instance.example: Refreshing state... aws_instance.example: Modifying... tags.#: "0" => "1" tags.Name: "" => "terraform-example" aws_instance.example: Modifications complete Apply complete! Resources: 0 added, 1 changed, 0 destroyed. Use the apply command again to deploy those changes
99. Now our EC2 instance has a tag!
100. resource "aws_elb" "example" { name = "example" availability_zones = ["us-east-1a", "us-east-1b"] instances = ["${aws_instance.example.id}"] listener { lb_port = 80 lb_protocol = "http" instance_port = "${var.instance_port}" instance_protocol = "http” } } Let’s add an Elastic Load Balancer (ELB).
101. resource "aws_elb" "example" { name = "example" availability_zones = ["us-east-1a", "us-east-1b"] instances = ["${aws_instance.example.id}"] listener { lb_port = 80 lb_protocol = "http" instance_port = "${var.instance_port}" instance_protocol = "http” } } Terraform supports variables, such as var.instance_port
102. resource "aws_elb" "example" { name = "example" availability_zones = ["us-east-1a", "us-east-1b"] instances = ["${aws_instance.example.id}"] listener { lb_port = 80 lb_protocol = "http" instance_port = "${var.instance_port}" instance_protocol = "http" } } As well as dependencies like aws_instance.example.id
103. resource "aws_elb" "example" { name = "example" availability_zones = ["us-east-1a", "us-east-1b"] instances = ["${aws_instance.example.id}"] listener { lb_port = 80 lb_protocol = "http" instance_port = "${var.instance_port}" instance_protocol = "http" } } It builds a dependency graph and applies it in parallel.
104. After running apply, we have an ELB!
105. > terraform destroy aws_instance.example: Refreshing state... (ID: i-f3d58c70) aws_elb.example: Refreshing state... (ID: example) aws_elb.example: Destroying... aws_elb.example: Destruction complete aws_instance.example: Destroying... aws_instance.example: Destruction complete Apply complete! Resources: 0 added, 0 changed, 2 destroyed. Use the destroy command to delete all your resources
106. For more info, check out The Comprehensive Guide to Terraform
107. Advantages of Terraform: 1. Concise, readable syntax 2. Reusable code: inputs, outputs, modules 3. Plan command! 4. Cloud agnostic 5. Very active development
108. Disadvantages of Terraform: 1. Maturity 2. Collaboration on Terraform state is hard (but terragrunt makes it easier) 3. No rollback 4. Poor secrets management
109. 1. Microservices 2. Docker 3. Terraform 4. ECS 5. Recap Outline
110. EC2 Container Service (ECS) is a way to run Docker on AWS
111. ECS Overview EC2 Instance ECS Cluster ECS Scheduler ECS Agent ECS Tasks ECS Task Definition { "cluster": "example", "serviceName": ”foo", "taskDefinition": "", "desiredCount": 2 } ECS Service Definition { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, }
112. ECS Cluster: several servers managed by ECS EC2 Instance ECS Cluster
113. Typically, the servers are in an Auto Scaling Group EC2 Instance Auto Scaling Group
114. Which can automatically relaunch failed servers EC2 Instance Auto Scaling Group
115. Each server must run the ECS Agent EC2 Instance ECS Cluster ECS Agent
116. ECS Task: Docker container(s) to run, resources they need EC2 Instance ECS Cluster ECS Agent ECS Task Definition { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, }
117. ECS Service: long-running ECS Task & ELB settings EC2 Instance ECS Cluster ECS Agent ECS Task Definition { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } { "cluster": "example", "serviceName": ”foo", "taskDefinition": "", "desiredCount": 2 } ECS Service Definition
118. ECS Scheduler: Deploys Tasks across the ECS Cluster EC2 Instance ECS Cluster ECS Agent ECS Task Definition { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } { "cluster": "example", "serviceName": ”foo", "taskDefinition": "", "desiredCount": 2 } ECS Service Definition ECS Scheduler ECS Tasks
119. It will also automatically redeploy failed Services EC2 Instance ECS Cluster ECS Agent ECS Task Definition { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } { "cluster": "example", "serviceName": ”foo", "taskDefinition": "", "desiredCount": 2 } ECS Service Definition ECS Scheduler ECS Tasks
120. You can associate an ALB or ELB with each ECS service EC2 Instance ECS Cluster ECS Agent ECS Tasks User
121. This lets you distribute traffic across multiple ECS Tasks EC2 Instance ECS Cluster ECS Agent ECS Tasks User
122. Which allows zero-downtime deployment EC2 Instance ECS Cluster ECS Agent ECS TasksUser v1 v1 v1 v2
123. As well as a simple form of service discovery EC2 Instance ECS Cluster ECS Agent ECS Tasks
124. You can use CloudWatch alarms to trigger auto scaling EC2 Instance ECS Cluster ECS Agent ECS Tasks CloudWatch
125. You can scale up by running more ECS Tasks EC2 Instance ECS Cluster ECS Agent ECS Tasks CloudWatch
126. And by adding more EC2 Instances EC2 Instance ECS Cluster ECS Agent ECS Tasks CloudWatch
127. And scale back down when load is lower EC2 Instance ECS Cluster ECS Agent ECS Tasks CloudWatch
128. Let’s deploy our microservices in ECS using Terraform
129. Define the ECS Cluster as an Auto Scaling Group (ASG) EC2 Instance ECS Cluster
130. resource "aws_ecs_cluster" "example_cluster" { name = "example-cluster" } resource "aws_autoscaling_group" "ecs_cluster_instances" { name = "ecs-cluster-instances" min_size = 3 max_size = 3 launch_configuration = "${aws_launch_configuration.ecs_instance.name}" }
131. Ensure each server in the ASG runs the ECS Agent EC2 Instance ECS Cluster ECS Agent
132. # The launch config defines what runs on each EC2 instance resource "aws_launch_configuration" "ecs_instance" { name_prefix = "ecs-instance-" instance_type = "t2.micro" # This is an Amazon ECS AMI, which has an ECS Agent # installed that lets it talk to the ECS cluster image_id = "ami-a98cb2c3” } The launch config runs AWS ECS Linux on each server in the ASG
133. Define an ECS Task for each microservice EC2 Instance ECS Cluster ECS Agent ECS Task Definition { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, }
134. resource "aws_ecs_task_definition" "rails_frontend" { family = "rails-frontend" container_definitions = "" container_definitions: "bb5352f" => "2ff6ae" (forces new resource) revision: "3" => "” Plan: 1 to add, 1 to change, 1 to destroy. Use the plan command to verify the changes
154. Apply the changes and you’ll see v2.
155. Advantages of ECS: 1. One of the simplest Docker cluster management tools 2. Almost no extra cost if on AWS 3. Pluggable scheduler 4. Auto-restart of instances & Tasks 5. Automatic ALB/ELB integration
156. Disadvantages of ECS: 1. UI is so-so 2. Minimal monitoring built-in 3. ALB is broken
157. 1. Microservices 2. Docker 3. Terraform 4. ECS 5. Recap Outline
158. Benefits of infrastructure-as-code: 1. Reuse 2. Automation 3. Version control 4. Code review 5. Testing 6. Documentation
159. Slides and code from this talk: ybrikman.com/speaking
160. For more info, see Hello, Startup hello-startup.net
161. And Terraform: Up & Running terraformupandrunning.com
162. gruntwork.io For DevOps help, see Gruntwork
163. Questions?

Posted by :  peter88 Post date :  2020-01-06 14:22
Category :  Technology Views :  390

Comment - Previous - Next - Bookmark This
Social Bookmark or Share this page

peter88 last post Image
- Ab exercises you should be doing
- Build every muscle in the body
- Wallpaper dump that wont break your computer
- My personal wallpaper collection
- My Wallpapers - enjoy them as I have!
- Wallpaper Drop! Get'em while they're hot!
- Big-ass Wallpaper Dump 1000 Images
- 1234 High Resolution Backgrounds
- 4,333 Desktop Backgrounds 1920x1080 or Higher
- 3176 Images of High Resolution Wallpaper Dump
- 575 Wallpapers (All 1080p, No watermarks)
- Small Map Dump
- Map Dump based on Survey
- Interesting maps/information dump (Roman Edition)
- Sizeable Map Dump


New Comment