An intro to Docker, Terraform, and Amazon ECS - Comment
This talk is a very quick intro to Docker, Terraform, and Amazon's EC2 Container Service (ECS). In just 15 minutes, you'll see how to take two apps (a Rails frontend and a Sinatra backend), package them as Docker containers, run them using Amazon ECS, and to define all of the infrastructure-as-code using Terraform. Topics covered: 1. A quick intro to Docker, Terraform, and Amazon ECS TERRAFORM Amazon ECS 2. In this talk, we’ll show how to deploy two apps: 3. A Rails Frontend and a Sinatra Backend 4. Slides and code from this talk: ybrikman.com/speaking 5. require 'sinatra' get "/" do "Hello, World!" end The sinatra backend just returns “Hello, World”. 6. 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 The rails frontend calls the sinatra backend… 7. Rails Frontend Response from the backend: And renders the response as HTML. 8. We’ll package the two apps as Docker containers… 9. Amazon ECS Deploy those Docker containers using Amazon ECS… 10. TERRAFORM And define our infrastructure-as- code using Terraform. 11. I’m Yevgeniy Brikman ybrikman.com 12. Co-founder of Gruntwork gruntwork.io 13. gruntwork.io We offer DevOps as a Service 14. gruntwork.io And DevOps as a Library 15. PAST LIVES 16. Author of Hello, Startup hello-startup.net 17. And Terraform: Up & Running terraformupandrunning.com 18. 1. Docker 2. Terraform 3. ECS 4. Recap Outline 19. 1. Docker 2. Terraform 3. ECS 4. Recap Outline 20. Docker allows you to build and run code in containers 21. Containers are like lightweight Virtual Machines (VMs) 22. Like an isolated process that happens to be an entire OS 23. > docker run –it ubuntu bash root@12345:/# echo "I'm in $(cat /etc/issue)” I'm in Ubuntu 14.04.4 LTS Running an Ubuntu image in a Docker container 24. > time docker run ubuntu echo "Hello, World" Hello, World real 0m0.183s user 0m0.009s sys 0m0.014s Containers boot quickly, with minimal CPU/memory overhead 25. You can define a Docker image as code in a Dockerfile 26. 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 27. 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 28. > docker build -t gruntwork/sinatra-backend . Step 0 : FROM gliderlabs/alpine:3.3 ---> 0a7e169bce21 (...) Step 8 : CMD ruby app.rb ---> 2e243eba30ed Successfully built 2e243eba30ed Build the Docker image 29. > docker run -it -p 4567:4567 gruntwork/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 30. > docker push gruntwork/sinatra-backend The push refers to a repository [docker.io/gruntwork/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 31. Now you can reuse the same image in dev, stg, prod, etc 32. > docker pull rails:4.2.6 And you can reuse images created by others. 33. 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 34. rails_frontend: image: gruntwork/rails-frontend ports: - "3000:3000" links: - sinatra_backend sinatra_backend: image: gruntwork/sinatra-backend ports: - "4567:4567" Define your entire dev stack as code with docker-compose 35. rails_frontend: image: gruntwork/rails-frontend ports: - "3000:3000" links: - sinatra_backend sinatra_backend: image: gruntwork/sinatra-backend ports: - "4567:4567" Docker links provide a simple service discovery mechanism 36. > 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 37. 1. Docker 2. Terraform 3. ECS 4. Recap Outline 38. Terraform is a tool for provisioning infrastructure 39. Terraform supports many providers (cloud agnostic) 40. And many resources for each provider 41. You define infrastructure as code in Terraform templates 42. 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 43. > 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 44. > 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 45. Now our EC2 instance is running! 46. 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 47. > 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 48. > 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 49. Now our EC2 instance has a tag! 50. 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). 51. 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 52. 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 53. 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. 54. After running apply, we have an ELB! 55. > 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 56. For more info, check out The Comprehensive Guide to Terraform 57. 1. Docker 2. Terraform 3. ECS 4. Recap Outline 58. EC2 Container Service (ECS) is a way to run Docker on AWS 59. 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, } 60. ECS Cluster: several servers managed by ECS EC2 Instance ECS Cluster 61. Typically, the servers are in an Auto Scaling Group Auto Scaling Group EC2 Instance 62. Each server must run the ECS Agent ECS Agent EC2 Instance ECS Cluster 63. ECS Task: Docker container(s) to run, resources they need { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } ECS Agent EC2 Instance ECS Task Definition ECS Cluster 64. ECS Service: long-running ECS Task & ELB settings { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } { "cluster": "example", "serviceName": ”foo", "taskDefinition": "", "desiredCount": 2 } ECS Agent EC2 Instance ECS Task Definition ECS Service Definition ECS Cluster 65. ECS Scheduler: Deploys Tasks across the ECS Cluster { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } { "cluster": "example", "serviceName": ”foo", "taskDefinition": "", "desiredCount": 2 } ECS Agent ECS Tasks EC2 Instance ECS Task Definition ECS Service Definition ECS Scheduler ECS Cluster 66. You can associate an ALB or ELB with each ECS service { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } { "cluster": "example", "serviceName": ”foo", "taskDefinition": "", "desiredCount": 2 } ECS Agent ECS Tasks EC2 Instance ECS Task Definition ECS Service Definition ECS Cluster 67. This allows you to distribute load across your ECS Tasks { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } { "cluster": "example", "serviceName": ”foo", "taskDefinition": "", "desiredCount": 2 } ECS Agent ECS Tasks EC2 Instance ECS Task Definition ECS Service Definition ECS Cluster 68. You can also use it as a simple form of service discovery { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } { "cluster": "example", "serviceName": ”foo", "taskDefinition": "", "desiredCount": 2 } ECS Agent ECS Tasks EC2 Instance ECS Task Definition ECS Service Definition ECS Cluster 69. Let’s deploy our apps on ECS using Terraform 70. Define the ECS Cluster as an Auto Scaling Group (ASG) EC2 Instance ECS Cluster 71. resource "aws_ecs_cluster" "example_cluster" { name = "example-cluster" } resource "aws_autoscaling_group" "ecs_cluster_instances" { name = "ecs-cluster-instances" min_size = 5 max_size = 5 launch_configuration = "${aws_launch_configuration.ecs_instance.name}" } 72. Ensure each server in the ASG runs the ECS Agent ECS Agent EC2 Instance ECS Cluster 73. # 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 74. Define an ECS Task for each microservice { "name": "example", "image": "foo/example", "cpu": 1024, "memory": 2048, "essential": true, } ECS Agent EC2 Instance ECS Task Definition ECS Cluster 75. resource "aws_ecs_task_definition" "rails_frontend" { family = "rails-frontend" container_definitions = |
|||
Posted by : peter88 | Post date : 2020-01-06 15:43 | ||
Category : Technology | Views : 378 | ||
New Comment