Pages

Friday 23 May 2014

Learning Chef - Part - II


some rights reserved by Matt Ray

...continued from  Learning Chef - Part - I

Consider you have to install an application. You 1st install and configure the application on a single server. It could be a developers laptop/workstation. In order to setup the application you have to perform various installation procedures i.e install packages, start services, manage database etc.
After sometime you are going to make the Application available to a larger number of public than your laptop/workstation can handle and so will need to add a database server and will make this a multi tier application. So now we have one server handling the Application request and a separate server for database. To avoid data loss we will add another App server and Database server to keep the data redundant so that data loss is avoided.
As time passes the load increases on the server with more number of people trying to access the server so we may need to add a scaling solution like a load-balancer to the server.
As in how the application usage increase and the amount of users increases we will need to add more and more app servers and add more load balancers for them.
As the database is not able to cope up with the high demand, we need to add a DB cache to the existing solution, Making the infra even more complex.

Chef is Infrastructure as a code. Using Chef you can programmatically provision and configure components. Chef ensures that each node complies to the policy. Policies are determined by the configurations included in each Node's run list. You can define the policy in your Chef configuration. Your policy states what state each resource should be in, but not how to get there. Chef-client will pull the policy from the Chef-server and enforce the policy on the Node. Policy will state what needs to installed but not how it needs to install. Chef is intelligent enough to figure that out. Chef will enforce the policy based on the resource that you specified.

Setup:

Setup a Chef Environment first by setting up Chef workstation, use the following command on Ubuntu,


curl -L https://www.opscode.com/chef/install.sh | sudo bash
 
 Login to manage.opscode.com and download the starter-kit there. Extract the chef-repo to your home directory. It should show the following contents.

cd chef-repo
ls
  .berkshelf
  .chef
  cookbooks
  roles
  .gitignore
  Berksfile
  chefignore
  README.md
  Vagrantfile

Check the .chef file present in the directory, it should show the following content.

cd .chef
  knife.rb
  org-validator.pem
  user.pem

Knife.rb will show your chef-server configuration for the workstation to be identified by the chef-server.

vim knife.rb
  # See http://docs.opscode.com/config_rb_knife.html for more information on knife configuration options

  current_dir = File.dirname(__FILE__)
  log_level                :info
  log_location             STDOUT
  node_name                "user"
  client_key               "#{current_dir}/user.pem"
  validation_client_name   "org-validator"
  validation_key           "#{current_dir}/org-validator.pem"
  chef_server_url          "https://api.opscode.com/organizations/user"
  cache_type               'BasicFile'
  cache_options( :path => "#{ENV['HOME']}/.chef/checksums" )
  cookbook_path            ["#{current_dir}/../chef-repo/cookbooks"]

Writing Recipes:

package "apache2" do
  action :install
end

template "/etc/apache2/apache.conf" do
  source "apache2.conf.erb"
  owner "root"
  group "root"
  mode "0644"
  variable(:allow_override => "All")
  notifies :reload, "service[apache2]

service "apache2" do
  action [:enable,:start]
  supports :reload => true
end

Lets consider the above recipe and understand it.
Each recipe has resources in it. The resources have :
- types -> package, template, service are the types of resources in the code
- names -> apache2, /etc/apache2/apache.conf, apache2(service) are the names of the resources in the code
- parameters ->   source "apache2.conf.erb"   owner "root"   group "root"   mode "0644"   supports :reload => true
- action to put the resource on desired state -> action :install, action [:enable,:start]
- send notification to other resources -> notifies :reload, "service[apache2]


A cookbook can be created by the command
knife cookbook create cookbookname
This will automatically create the cookbook along with all the necessary files inside it. You can delete, check the available cookbooks, delete a cookbook, upload and download a cookbook using different knife commands. You can check all the options by
knife cookbook --help

To bootstrap a new node you need to use the following command:
knife bootstrap hostname --sudo -x username -P password --ssh-port 2222 -N nodename

Creating Environments
Many a times you will want development environment and production environment to have little different configurations. e.g xdebug to be installed on dev but not on production. PayPal enabled on prod but not dev etc. Chef allows you to define different environments and also allows you to assign different nodes to a particular environment.

create a directory called environments in the chef-repo. Add the following content to a file dev.rb in it.

name "dev"
description "The dev environment"

create another file called prod.rb there and add the following content to it.

name "prod"
description "The prod environment"

Upload the environment to the chef server.
You can verify whether the environments are created in the chef server by logging in there.

Creating roles:
Roles provide a way to apply a group of recipes and attributes to all the nodes performing a particular function. e.g all the nodes which would work as db server can be assigned a db server roles and accordingly all the db server specific recipes can be applied to those nodes.

Roles can be created in the following manner. Create a roles directory in the chef-repo if it does not exist. add a file base.rb there with the following content.

name "base"
description "Base role applied to all nodes."
run_list(
  "recipe[users::sysadmins]",
  "recipe[sudo]",
  "recipe[apt]",
  "recipe[git]",
  "recipe[build-essential]",
  "recipe[vim]"
)
override_attributes(
  :authorization => {
    :sudo => {
      :users => ["ubuntu", "vagrant"],
      :passwordless => true
    }
  }
)

Here the runlist method defines a list of recipes to be applied to all the nodes that have base role. The override_attributes method tells lets us override the default attributes used by the recipes in the list. Here we are overriding attributes used by the sudo cookbook so that "vagrant" and "ubuntu" users can run sudo without entering password.

Next create another role Webserver by creating a file webserver.rb in the roles directory with the following content.

name "webserver"
description "Web server role"
all_env = [
  "role[base]",
  "recipe[php]",
  "recipe[php::module_mysql]",
  "recipe[apache2]",
  "recipe[apache2::mod_php5]",
  "recipe[apache2::mod_rewrite]",
]

run_list(all_env)

env_run_lists(
  "_default" => all_env,
  "prod" => all_env,
  #"dev" => all_env + ["recipe[php:module_xdebug]"],
  "dev" => all_env,
)

Here it shows that a method env_run_lists method in a role to define different run lists for different environments. To simplify things we create an all_env array to define the common run list for all environments, and then merge in any additional run list items unique to each environment.

Next create another role db_master.rb file with following contents:

name "db_master"
description "Master database server"

all_env = [
  "role[base]",
  "recipe[mysql::server]"
]

run_list(all_env)

env_run_lists(
  "_default" => all_env,
  "prod" => all_env,
  "dev" => all_env,
)

upload the created roles to chef-server and also verify the same. Upload roles by:
knife role from file roles/base.rb
knife role from file roles/webserver.rb
knife role from file roles/db_master.rbenv


Setting up a user account for sys-admin
Define a user account for yourself on all the nodes with admin privileges. This can be done by defining a data bag for the users cookbook, with attributes that describe the user account to create.

mkdir -p data_bags/users
vim data_bags/users/$USER.json

Add the following to the $USER.json
{
  "id": "jkg",
  "ssh_keys": "ssh-rsa ...SecretKey... roshan4074@gmail.com",
  "groups": [ "sysadmin", "dba", "devops" ],
  "uid": 2001,
  "shell": "\/bin\/bash"
}

Upload the data bag as well to the chef-server and verify
knife data bag create users
knife data bag from file users $USER.json

Sunday 11 May 2014

Meetup.com practices

Some rights reserved by Christain Senger
For quiet sometime I have been attending meetups/sessions organised through meetup.com at different locations. I have seen that the general practice is quiet similar everywhere. 
  • The Agenda is posted on the meetup group by the organizers/speakers . 
  • People mark RSVP(even when most of them wont show up). 
  • Less than 30 percent of attendance is seen.
  • The actual event would start at least 30-40 minutes (or more) late than it was scheduled because of late comers.
  • The speakers and the participants would just socialize or sit idle.
  • The late comers would give the same reason always; couldn't find the location/stuck in traffic.
  • Many 1st time visitors for the meetup who will probably have no clue of what the meetup is all about and will be expecting to have the basics covered 1st.
  • The organizers would probably consider reviewing the basics based on the majority.
  • The meetup will cover most of the times everything as per the agenda.
  • A break with some snacks/refreshments and for socializing.
  • Meetup concludes with an informal planning for the next meetup to be arranged.
  • Feedback mail received for the meetup.
Finally it all goes well here, but I see a few problems that could be avoided. 

Latecomers :  
 This is something that cant be avoided. However I honestly feel that this could be minimized to a certain extent for sure. Also, most of the organisers already practice this and I think it helps them for sure.
  • While giving a time, keep a buffer of at least 15-20 minutes as certain things like traffic cannot be avoided.
  • Provide a google maps link for the users to locate things fast. Also some important landmark nearby could help too.
  • Traffic situation at a particular time in general will also be helpful for people to leave little early. e.g if the meetup is conducted in the evening, at a busy location, it would take hours to reach the location.
  • A way to reach the location would help too. e.g if someone comes by bus, the bus stop he should be informing to the bus-conductor and the bus number he needs to look for. If by rickshaw, the nearest possible landmark to the location and approximate walkable distance from any location if its complicated to find.

Newcomers :
Many meetups will have new faces who expect basics or the introductory things to be covered for them to keep up with the pace. When the meetup starts directly these people don't get most of the things and then they would probably not join in the next meetup as well. Covering the basics time and again will make the regularly coming people bored as it would just eat up their time.
  • Newcomers need to make an attempt to reach the venue as early as possible and ask questions to the organizers or the people present there and make the maximum out of the time to understand the introductory part.
  • The organizers could play slides of the previous meetup or of the introductory meetups for the beginners/newcomers to know what was covered or perhaps covering the basics for the 1st 15-30 minutes buffer can also be a good idea provided the newcomers are coming early.
  • Beginners can also read about the past meetups and check if the slides for the past meetups are available on the page and review them, understand what topics are covered, read them and then join the meetup. Its just like attending the classes in the college and reading a brief info of the topic before the lecture.

Socialize : 
Less participation is been seen in terms of socializing at a few meetups. You never know, who could help you in what kind of problems where you are stuck at office. I have come across so many situations where I don't get what use cases could be followed for a particular problems and the experts I meet in the meetup/conferences have really splendid and simple solutions that I could not have think of. Not only will they give you a solution, they would also explain why would that solution be the best one to be used. Michael Ducy from Chef(old Opscode) provided a really good, simple and descriptive answer for the best use case to be followed for Chef-Docker integration.

Incorrect RSVP :
One practice that has been seen is that people would simply click attending/going and will not turn up for the meetup. In almost every meetup I see an attendance less than 30%. Planning to attend a meetup in advance and marking RSVP is good, however if the plan changes, updating the RSVP is a good practice too. It will keep the organizers updated and help them arrange the function well. It gives me a feeling that people would just RSVP yes to a meetup cos its free. It becomes difficult for the organisers to arrange the meetup because of incorrect RSVPs. I have seen last minute arrangements being made to many meetups and I will surely not blame them for it, its just the RSVP that they cant trust and then have to rely on the last minute attendance.
  • Update your RSVP whenever you change your decision for any reason.
  • Mention reason in the comments if your decision changes due to any reason so that the co-participants will look for you in the next meetup. Everyone is an important member in the meetup.
  • If you plan to bring a friend/colleague along with you, update the RSVP to reflect the change.

Feedback :
The organizers keep looking for feedback for the meetup that they organized voluntarily and selflessly (marketing the brand can be ignored for sometime). The attendance is as low as 30% and the feedback is even lesser, close to around 10%. An honest feedback helps the organizers organize better in future. 
  • Always provide a feedback after the meetup verbally as well as on the meetup page.
  • Let the people know that it was nice to see them at the meetup. This builds a good network, there are high chances that next time they would attend the meetup to meet you and socialize further. 
  • Its all about giving respect and getting it back.

Friday 9 May 2014

Docker - Lightweight Linux Container



Docker: Its a tool that helps you to pack, ship and run any application as a light-weight Linux container. More on https://www.docker.io/

Works best on Linux kernel 3.8 Ubuntu 12.04 precise has 3.2 and needs to be upgraded. 

Install Docker with on Ubuntu 12.04:

sudo apt-get update
sudo apt-get install linux-image-generic-lts-raring linux-headers-generic-lts-raring

sudo reboot

To check docker version:
sudo docker version

Client version: 0.11.1
Client API version: 1.11
Go version (client): go1.2.1
Git commit (client): fb99f99
Server version: 0.11.1
Server API version: 1.11
Git commit (server): fb99f99
Go version (server): go1.2.1
Last stable version: 0.11.1

To check info about docker installed:
sudo docker info

Containers: 1
Images: 9
Storage Driver: aufs
 Root Dir: /var/lib/docker/aufs
 Dirs: 11
Execution Driver: native-0.2
Kernel Version: 3.11.0-20-generic
WARNING: No swap limit support

To pull an existing docker image:
sudo docker pull <imagename>

sudo docker pull busybox

HelloWorld in docker:
sudo docker run busybox echo HelloWorld

Search for an existing image in the index:
docker search <image-name>
sudo docker search stackbrew/ubuntu
NAME                       DESCRIPTION                                     STARS     OFFICIAL   TRUSTED
stackbrew/ubuntu           Barebone ubuntu images                          36                   
jprjr/stackbrew-node       A stackbrew/ubuntu-based image for Docker,...   2                    [OK]
hcvst/erlang               Erlang R14B04 based on stackbrew/ubuntu         0                    [OK]
stackbrew/ubuntu-upstart                                                   0                    


Pull an existing image:
sudo docker pull ubuntu

Pulling repository ubuntu
a7cf8ae4e998: Pulling dependent layers 
3db9c44f4520: Downloading [=================>                                 ] 22.18 MB/63.51 MB 2m19s
74fe38d11401: Pulling dependent layers 
316b678ddf48: Pulling dependent layers 
99ec81b80c55: Pulling dependent layers 
5e019ab7bf6d: Pulling dependent layers 
511136ea3c5a: Download complete 
6cfa4d1f33fb: Download complete 


To the check the available images:
sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
ubuntu              13.10               5e019ab7bf6d        2 weeks ago         180 MB
ubuntu              saucy               5e019ab7bf6d        2 weeks ago         180 MB
ubuntu              12.04               74fe38d11401        2 weeks ago         209.6 MB
ubuntu              precise             74fe38d11401        2 weeks ago         209.6 MB
ubuntu              12.10               a7cf8ae4e998        2 weeks ago         171.3 MB
ubuntu              quantal             a7cf8ae4e998        2 weeks ago         171.3 MB
ubuntu              14.04               99ec81b80c55        2 weeks ago         266 MB
ubuntu              latest              99ec81b80c55        2 weeks ago         266 MB
ubuntu              trusty              99ec81b80c55        2 weeks ago         266 MB
ubuntu              raring              316b678ddf48        2 weeks ago         169.4 MB
ubuntu              13.04               316b678ddf48        2 weeks ago         169.4 MB
busybox             latest              2d8e5b282c81        2 weeks ago         2.489 MB
ubuntu              10.04               3db9c44f4520        2 weeks ago         183 MB
ubuntu              lucid               3db9c44f4520        2 weeks ago         183 MB

To run a command within an image:
docker run<image> command

sudo docker run ubuntu echo HelloWorld
HelloWorld

To install something on an ubuntu image
sudo docker run apt-get install <package>

find ID of the container 
sudo docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
0dac167b178d        ubuntu:14.04        ps aux              12 minutes ago      Exited (0) 12 minutes ago                       goofy_bell

committing changes made to the images:
docker commit 0da firstcommit
723aa6ead77a14ff05cd2c640163345ec5a36fa9a4c757a6872a1ec919ab9345

To get log of the present container:
sudo docker logs 0dac167b178d
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0   7132   644 ?        Rs   09:10   0:00 ps aux

To inpect the details of an image
sudo docker inspect <id: 3-4 characters of id will work too>
sudo docker inspect 0da

<json output>

Push container image to the index
sudo docker push ubuntu


Creating Dockerfile:

All instructions in Dokerfile are in the form of 
INSTRUCTION arguments

Instruction are not case sensitive but CAPS are recommended. The first instruction in any Dockerfile is the FROM instruction. The syntax is:

FROM <image>
FROM ubuntu

This will look for the image in Docker index. You can also search docker index by the command docker search

Next is the RUN instruction. The RUN instruction will execute any commands on the current image. After executing, it will also commit the changes. The committed image can be used for the next instructions from the Dockerfile. This way the committed changes form a layer of changes just like any other source code control system. Syntax of RUN command:
RUN <command>
RUN apt get install -y apache2

Here the RUN command is equivalent to docker run image command + docker commit container_id. Here image will be automatically replaced with the current image and container_id is the result of the previous commit.

Once you have created your Dockerfile you can use docker build to create you image from it. You can use the command in this way.

Create a Dockerfile with the content
FROM ubuntu
RUN apt-get install -y memcached

Save and close the file. If the file is in you r present directory:
docker build .
If the file in in some other location 
docker build path/to/file
If passing through STDIN
docker build - < Dockerfile
If passing through github URL
docker build github.com/roshan4074

you can check the container with the command:
sudo docker images

To apply a tag to an image you the command: docker tag 
sudo docker tag <container_id>

To comment a code use the "#' symbol followed by the text

To specify the contact info of the Maintainer of the Dockerfile:
MAINTAINER Name contact@email

To trigger a command as soon as a container starts, use ENTRYPOINT instruction
ENTRYPOINT echo "Hello, Container Started"

Another Format to use ENTRYPOINT Instrcution is 
ENTRYPOINT ["echo", "Hello, Container Started"]
This is the preferred format

e.g
ENTRYPOINT ["wc", "-l"]

To execute a certain command by a particular user use the command USER
USER roshan

T open a particular port for a process use EXPOSE instruction
EXPOSE 8080