Modern DevOps eszközök és technikák

Continuous Delivery

Our highest priority is to satisfy the customer

through early and continuous delivery of valuable software.

Create a repetable, reliable process for releasing software

Automate almost everything

Keep everything in version control

If it hurts, do it more frequently, and bring the pain forward

Build quality in

Done means released

Continuous Improvement

Build Pipeline

DEV + OPS + QA

Infrastructure as Code

Infrastructure as Code

  • Code is version controlled
  • Code changes can be reviewed by anyone
  • Code is repeatable
  • Code is testable

Immutable infrastructure 

(Phoenix Server Pattern)

variables = JSON.parse(File.read("variables.json"))
args_name = ""
args_value = []
for i in 2..variables.count-1
    args_name = args_name + "${#{i-1}} "
    args_value.push(variables.values[i])
end

Vagrant.configure("2") do |config|
  config.vm.box = "woohoolabs/api"
  config.ssh.forward_agent = true
  config.ssh.port = variables["ssh_port"]

  config.exec.commands %w(go), directory: "/website"

  config.vm.provider :virtualbox do |vb, override|
    override.vm.network "private_network", ip: variables["private_ip"]
    override.vm.network "forwarded_port", guest: 22, host: variables["ssh_port"], protocol: "tcp"
    override.vm.synced_folder "./", "/vagrant", disabled: true
    override.vm.synced_folder "./", "/website", owner: "vagrant", group: "www-data", mount_options: ["dmode=775,fmode=775"]
  end

  config.vm.provision :shell do |sh|
    sh.inline = "go cd provision #{args_name}| tee -a ~/provision.log"
    sh.args = args_value
    sh.privileged = false
  end
end
{
  "variables": {
    "api_box_name": "api",
    "api_box_version": "2.7",
    "api_web_project_root_path": "/website",
    "api_web_project_web_absolute_path": "/website/web",
    "api_web_project_var_absolute_path": "/etc/website/var",

    "api_dev_web_box_name": "api-dev",
    "api_dev_web_build_env": "dev",
    "api_dev_web_ssh_user": "vagrant",
    "api_dev_web_ssh_password": "vagrant",

    "api_prod_web_box_name": "api-prod-web",
    "api_prod_web_build_env": "prod",
    "api_prod_web_ssh_user": "ubuntu",
    "api_prod_web_ssh_port": "22",
    "api_prod_web_aws_access_key": "{{env `AWS_ACCESS_KEY`}}",
    "api_prod_web_aws_secret_key": "{{env `AWS_SECRET_KEY`}}",
    "api_prod_web_aws_region": "eu-central-1",
    "api_prod_web_aws_source_ami": "ami-70f9cb6d",
    "api_prod_web_aws_instance_type": "t2.micro"
  },
  "builders": [
    {
      "name": "api-prod-web",
      "ami_name": "{{user `api_prod_web_box_name`}}-{{user `api_box_version`}}-{{ timestamp }}",
      "ami_description": "Ubuntu 14.04 64 bit web server for Woohoo Labs. API",
      "type": "amazon-ebs",
      "access_key": "{{user `api_prod_web_aws_access_key`}}",
      "secret_key": "{{user `api_prod_web_aws_secret_key`}}",
      "region": "{{user `api_prod_web_aws_region`}}",
      "source_ami": "{{user `api_prod_web_aws_source_ami`}}",
      "instance_type": "{{user `api_prod_web_aws_instance_type`}}",
      "ssh_username": "{{user `api_prod_web_ssh_user`}}",
      "ssh_port": "{{user `api_prod_web_ssh_port`}}",
      "enhanced_networking": true,
      "tags": {
        "build_env": "prod",
        "build_type": "web",
        "build_version": "{{user `api_box_version`}}",
        "build_time": "{{ timestamp }}"
      }
    },
    {
      "name": "api-dev",
      "type": "virtualbox-iso",
      "boot_command": [
        "<esc><wait><esc><wait><enter><wait>",
        "/install/vmlinuz",
        " auto",
        " console-setup/ask_detect=false",
        " console-setup/layoutcode=hu",
        " console-setup/modelcode=pc105",
        " debconf/frontend=noninteractive",
        " debian-installer=en_US",
        " fb=false",
        " initrd=/install/initrd.gz",
        " kbd-chooser/method=hu",
        " keyboard-configuration/layout=HUNGARY",
        " keyboard-configuration/variant=HUNGARY",
        " locale=en_US",
        " hostname={{user `api_box_name`}}.{{user `api_dev_web_build_env`}}",
        " netcfg/get_domain=site",
        " netcfg/get_hostname=vagrant",
        " noapic",
        " preseed/url=http://{{ .HTTPIP }}:{{ .HTTPPort }}/preseed.cfg",
        "<enter><wait>",
        " -- <wait>"
      ],
      "boot_wait": "10s",
      "disk_size": 8000,
      "guest_additions_path": "VBoxGuestAdditions_{{.Version}}.iso",
      "guest_os_type": "Ubuntu_64",
      "http_directory": "provisioners",
      "iso_urls": [
        "http://releases.ubuntu.com/14.04/ubuntu-14.04.2-server-amd64.iso",
        "http://hu.releases.ubuntu.com/14.04/ubuntu-14.04.2-server-amd64.iso"
      ],
      "iso_checksum": "3bfa6eac84d527380d0cc52db9092cde127f161e",
      "iso_checksum_type": "sha1",
      "output_directory": "{{user `api_dev_web_box_name`}}",
      "shutdown_command": "echo 'vagrant'|sudo -S -E shutdown -P now",
      "ssh_username": "{{user `api_dev_web_ssh_user`}}",
      "ssh_password": "{{user `api_dev_web_ssh_password`}}",
      "ssh_port": 22,
      "ssh_wait_timeout": "10000s",
      "headless": true,
      "hard_drive_interface": "sata",
      "vboxmanage": [
        [
          "modifyvm",
          "{{user `api_dev_web_box_name`}}",
          "--memory",
          "1024"
        ],
        [
          "modifyvm",
          "{{user `api_dev_web_box_name`}}",
          "--cpus",
          "2"
        ]
      ],
      "virtualbox_version_file": ".vbox_version",
      "vm_name": "{{user `api_dev_web_box_name`}}"
    }
  ],
  "post-processors": [
    {
      "type": "vagrant",
      "output": "{{user `api_box_name`}}-{{user `api_box_version`}}-{{ .Provider }}.box"
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "scripts": [
        "provisioners/project/setup-envvars-build.sh",
        "provisioners/server-web/add-dependencies.sh",
        "provisioners/os/setup-os.sh",
        "provisioners/os/setup-sshd.sh",
        "provisioners/os/setup-sudoers.sh"
      ],
      "execute_command": "echo 'vagrant'|{{.Vars}} sudo -S -E bash '{{.Path}}' 2>&1 | tee -a ~/build.log",
      "override": {
        "api-prod-web": {
          "environment_vars": [
            "BUILD_NAME={{user `api_prod_web_box_name`}}",
            "BUILD_ENV={{user `api_prod_web_build_env`}}",
            "BUILD_VERSION={{user `api_box_version`}}",
            "SSH_USER={{user `api_prod_web_ssh_user`}}",

            "PROJECT_DIR={{user `api_web_project_root_path`}}",
            "WEB_DIR={{user `api_web_project_web_absolute_path`}}",
            "VAR_DIR={{user `api_web_project_var_absolute_path`}}"
          ]
        },
        "api-dev": {
          "environment_vars": [
            "BUILD_NAME={{user `api_dev_web_box_name`}}",
            "BUILD_ENV={{user `api_dev_web_build_env`}}",
            "BUILD_VERSION={{user `api_box_version`}}",
            "SSH_USER={{user `api_dev_web_ssh_user`}}",

            "PROJECT_DIR={{user `api_web_project_root_path`}}",
            "WEB_DIR={{user `api_web_project_web_absolute_path`}}",
            "VAR_DIR={{user `api_web_project_var_absolute_path`}}"
          ]
        }
      }
    },
    {
      "type": "shell",
      "override": {
        "api-prod-web": {
          "scripts": [
            "provisioners/os/setup-amazon.sh",
            "provisioners/server-web/install-mysql-client.sh"
          ]
        },
        "api-dev": {
          "scripts": [
            "provisioners/os/setup-vagrant.sh",
            "provisioners/server-web/install-mysql-server.sh"
          ]
        }
      },
      "execute_command": "echo 'vagrant'|{{.Vars}} sudo -S -E bash '{{.Path}}' 2>&1 | tee -a ~/build.log"
    },
    {
      "type": "shell",
      "scripts": [
        "provisioners/server-web/install-locales.sh",
        "provisioners/server-web/install-web.sh",
        "provisioners/server-web/install-beanstalkd.sh",
        "provisioners/server-web/install-redis.sh",
        "provisioners/server-web/install-mail.sh",
        "provisioners/server-web/install-dev.sh",
        "provisioners/project/setup-project.sh",
        "provisioners/os/minimize.sh",
        "provisioners/project/get-build-version.sh"
      ],
      "execute_command": "echo 'vagrant'|{{.Vars}} sudo -S -E bash '{{.Path}}' 2>&1 | tee -a ~/build.log"
    }
  ]
}
provider "aws" {
  access_key = "${var.aws_access_key}"
  secret_key = "${var.aws_secret_key}"
  region = "${var.aws_region}"
}

resource "aws_security_group" "web" {
  name = "api_staging_security_group"
  description = "Default security group for the API"

  # SSH access from anywhere
  ingress {
    from_port = 22
    to_port = 22
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # HTTP access from anywhere
  ingress {
    from_port = 80
    to_port = 80
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # HTTPS access from anywhere
  ingress {
    from_port = 443
    to_port = 443
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # MySQL access from anywhere
  ingress {
    from_port = 3306
    to_port = 3306
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # HTTP access to anywhere
  egress {
    from_port = 80
    to_port = 80
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # HTTPS access to anywhere
  egress {
    from_port = 443
    to_port = 443
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # MySQL access to anywhere
  egress {
    from_port = 3306
    to_port = 3306
    protocol = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_instance" "web" {
  connection {
    user = "ubuntu"
    key_file = "${var.aws_key_path}"
  }
  instance_type = "${var.aws_instance_type}"
  ami = "${var.aws_ami}"
  key_name = "${var.aws_key_name}"
  security_groups = ["${aws_security_group.web.name}"]
  tags {
    Name = "api-${var.app_env}-web-${var.deploy_time}"
    App_Env = "${var.app_env}"
    Deploy_Time = "${var.deploy_time}"
    Deploy_Commit = "${var.deploy_commit}"
  }

  # Copies the Go script to the machine - cwd must be in the Terraform config folder
  provisioner "file" {
    source = "../../../../go"
    destination = "~/go"
  }

  provisioner "remote-exec" {
    inline = [
      "chmod 750 ~/go",
      "~/go cd checkout ${var.deploy_commit} /website ${var.bitbucket_username} ${var.bitbucket_password} | tee -a /home/ubuntu/provision.log",
      "chmod 750 /website/go & rm ~/go & chmod 750 -R /website/app/build/images/provisioners",
      "go cd provision ${var.app_env} ${var.beanstalkd_host} ${var.beanstalkd_port} ${aws_db_instance.web.address} ${aws_db_instance.web.port} ${var.db_common_name} ${var.db_common_user} ${var.db_common_password} ${var.redis_scheme} ${var.redis_host} ${var.redis_port} ${var.open_weather_map_api_key} ${var.blackfire_server_id} ${var.blackfire_server_token} ${var.blackfire_client_id} ${var.blackfire_client_token} | tee -a /home/ubuntu/provision.log",
      "go cd auto-migrate '${var.db_root}' '${var.db_root_password}' | tee -a /home/ubuntu/provision.log"
    ]
  }
}

resource "aws_db_instance" "web" {
  identifier = "api-staging-rds"
  allocated_storage = 5
  engine = "mysql"
  engine_version = "5.6.22"
  instance_class = "db.t2.micro"
  username = "${var.db_root}"
  password = "${var.db_root_password}"
  parameter_group_name = "default.mysql5.6"
  availability_zone= "${var.aws_region}b"
  backup_retention_period = 0
  maintenance_window = "fri:08:22-fri:08:52"
  multi_az = false
  port = "${var.db_port}"
}

output "web-zone" {
  value = "${aws_instance.web.availability_zone}"
}

output "web-ip" {
  value = "${aws_instance.web.public_ip}"
}

output "web-dns" {
  value = "${aws_instance.web.public_dns}"
}

output "db-address" {
  value = "${aws_db_instance.web.address}"
}

People don't scale - machines do

Jeff Atwood

Források

  • http://www.amazon.com/Continuous-Delivery-Deployment-Automation-Signature/dp/0321601912
  • http://slideshare.net/PuppetLabs/infrastructure-as-code-why-it-matters-51737991
  • https://thoughtworks.com/insights/blog/infrastructure-code-reason-smile
  • https://thoughtworks.com/insights/blog/moving-to-phoenix-server-pattern-introduction
  • http://mitchellh.com/comparing-filesystem-performance-in-virtual-machines

Modern DevOps Eszközök és Technikák

By Máté Kocsis

Modern DevOps Eszközök és Technikák

  • 387