.gitconfig
[alias]
co = checkout
ci = commit
st = status
br = branch
hist = log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short
type = cat-file -t
dump = cat-file -p
[user]
name = Igor Grinkin
email =
Saturday, January 11, 2020
Wednesday, August 01, 2018
Friday, August 04, 2017
Docker
Installation
https://docs.docker.com/engine/getstarted/step_one/Examples
Run nginx containerdocker run -d -p 80:80 --name webserver nginx
List all docker images
docker images
docker ps
docker cp html.tar 213389b682d6:/tmp/
docker exec -it 213389b682d6 /bin/bash
docker commit -m "Adding ops runbook" -a "Igor" 213389b682d6 runbook
docker run -d -p 80:80 -it runbook /bin/bash
docker exec -it 7e7d9c35a216 bash
Ctrl + p , Ctrl + q
Restart nginx inside your container
docker exec -it 213389b682d6 /etc/init.d/nginx restart
root@ecs00-us-west-2b:~# docker start ecs-agent
Logging
It seems like docker puts logs in two places:- Actual container logs
/var/lib/docker/containers
- docker system log
/var/log/docker.log
/etc/default/docker
DOCKER_OPTS="--pidfile=/var/run/docker.pid --log-level=warning --log-opt max-size=1g --storage-driver=zfs --storage-opt=zfs.fsname=zd0/containers"
Terraform notes
This
section serves as a guide line for some usage of terraform to make life
easier. This is built from hard experience using terraform in a
multi-member team and across multiple environments in production.
In addition to reuse, modules provide a repeatable structure and versioning mechanism.
For instance, let’s look at our VPC module. It does the following:
Additionally, as this is a git repo for this module, you can get VERSIONED environments. Consider the following module declaration in terraform code:
By specifically putting the ref in there, you can be sure
that FUTURE changes to the module will not unintentionally propogate
down to existing environments. You can easily test changes by spinning
up the old version, bumping the rev, calling
While terraform TRIES to do the right thing here, being strict in your versions lets you know for sure that even if you unintentially issued
Looking back out our pushbutton setup, we leverage remote state to help isolate independent infrastucture components. There is no reason creating a new instance needs to modify a VPC. There is no reason, creating an RDS instance needs to modify dhcp options.
You _can_ use non-remote state files but those are not currently allowed in terraform for reuse. We use artifactory as our universal remote state mechanism because it provides built in versioning for us.
In our pushbutton runs, EACH subdirectory pushes its state up to artifactory at the end of the run. That makes it usable for downstream modules. The following is an example of using the VPC remote state for an environment downstream:
This allows us to reference outputs from that remote state:
Use consistent variable input names across modules to help minimize confusion. A good example is our
Also, modules to not natively export all outputs from a given resource. For this reason and for future unknowns, you should export ALL outputs from a resource inside a module. Consider the following resource and output in a module:
In this module, we’re ONLY exporting the vpc id. However,
what we should be doing is exporting everything that terraform returns
as an output in case we need that information in future reuses of the
module (originally this resource did not support the additional outputs.
They were added to terraform later. This code only exports older
outputs):
You can find the exported attributes of a given resource at the bottom of any resource’s page on the terraform website
Be careful with
Using
the count trick is really handy in terraform when you need multiple
copies of something. Be aware, however, that counts “break” the
dependency graph. For instance let’s assume you have an
When you increase the count, the planner recomputes EVERYTHING using that count variable.
If you must use a count, you should ensure that the count is not used in ANY other resources unless you can safely update those resources as well - this includes passing the same _count_ to a template that you use in the instance resource.
Terraform is a really poor fit for application deployment and volatile lifecycle components.
My general rule is if something feels hard in terraform it probably is.
https://github.com/dtan4/terraforming
Disable remote state
Modules
Modules are the reusable construct in terraform. In most cases, we want to make a module out of ANY provisioning process that we expect to reuse.In addition to reuse, modules provide a repeatable structure and versioning mechanism.
For instance, let’s look at our VPC module. It does the following:
- Creates a VPC
- Creates two subnets in that VPC in each AZ - one public/one private
- Creates a private Route53 zone for that VPC
- Creates basic security groups for that VPC
- Creates dhcp options for nodes provisioned in that VPC
- Provisions NAT nodes in each AZ for outbound traffic from the private subnets
Additionally, as this is a git repo for this module, you can get VERSIONED environments. Consider the following module declaration in terraform code:
module "vpc" {
source = "git::ssh://git@opsgit.i.stormpath.com/tf-modules/vpc?ref=894abd9"
terraform get --update=true and then terraform plan.While terraform TRIES to do the right thing here, being strict in your versions lets you know for sure that even if you unintentially issued
--update=true, you would not accidentally tear down a stack.
Note
Always use versioned module sources in terraform. Always. This is required for determinism and repeatability.
Use remote state
Terraform provides another mechanism for a form of reusability - remote state.Looking back out our pushbutton setup, we leverage remote state to help isolate independent infrastucture components. There is no reason creating a new instance needs to modify a VPC. There is no reason, creating an RDS instance needs to modify dhcp options.
You _can_ use non-remote state files but those are not currently allowed in terraform for reuse. We use artifactory as our universal remote state mechanism because it provides built in versioning for us.
In our pushbutton runs, EACH subdirectory pushes its state up to artifactory at the end of the run. That makes it usable for downstream modules. The following is an example of using the VPC remote state for an environment downstream:
resource "terraform_remote_state" "vpc" {
backend = "artifactory"
config {
url = "https://artifactory.i.stormpath.com/artifactory"
repo = "terraform-state"
subpath = "customers/${var.orgname}/vpc"
}
}
module "frontend_subnet_c" {
source = "git::ssh://git@opsgit.i.stormpath.com/tf-modules/frontend_node?ref=60e6ecd"
orgname = "${var.orgname}"
gateway_host = "${terraform_remote_state.vpc.output.nat_c_public_ip}"
.......
}
Note
In terraform 0.7 (unreleased), the syntax for using values from remote state is changing slightly. Instead of terraform_remote_state.vpc.output.some_output, the extra output is being dropped:terraform_remote_state.vpc.some_output
Additionally, 0.7 is adding a new construct called data sources which
are immutable, read-only sources of data than can be used in terraform
runs - i.e. a json file with a list of current amis or versions of
software.
Passing variables and outputs
One area that is painful in terraform is passing variables from modules and other things. This leads to a bit of code duplication however the tradeoffs for safety are worth it.Use consistent variable input names across modules to help minimize confusion. A good example is our
orgname variable.
This is used in EVERY module. This provides a programatic way to build
tags and names for AWS resources as well as ensures a factor of
uniqueness during provisioning. When you have multiple “stacks” in a
single AWS account, it’s very easy to see _WHICH_ environment a resource
goes with as all names are prefixed with the orgname (i.e. nat-a.orgname.local)Also, modules to not natively export all outputs from a given resource. For this reason and for future unknowns, you should export ALL outputs from a resource inside a module. Consider the following resource and output in a module:
resource "aws_vpc" "default" {
cidr_block = "${var.vpc_cidr}"
enable_dns_hostnames = true
enable_dns_support = true
tags { Name = "${var.orgname}-secure"}
}
output "vpc_id" { value = "${aws_vpc.default.id}" }
output "vpc_cidr_block" { value = "${aws_vpc.default.cidr_block}" }
output "vpc_main_route_table_id" { value = "${aws_vpc.default.main_route_table_id}" }
output "vpc_default_network_acl_id" { value = "${aws_vpc.default.default_network_acl_id}" }
output "vpc_default_security_group_id" { value = "${aws_vpc.default.default_security_group_id}" }
Note
Doing a terraform apply will
update any remote state with new outputs from modules used assuming a
new version of the module was pulled in. Let’s say we needed to get
those new attributes for a new terraform use elsewhere. Following our
best practices we would:- Make changes to the git repo of the module and note the new hash
- Update the rev in our downstream use of the module
- call
terraform get --update=trueto pull in the new version of the module - run
terraform planto ensure that we aren’t actually CHANGING anything (or maybe your module allows that - it’s situational) - run
terraform applywhich will grab the new exported outputs from the module and shove them into the current terraform run’s state.
Now we have a remote state file with additional information that we require when using remote state.
Be careful with count
Using
the count trick is really handy in terraform when you need multiple
copies of something. Be aware, however, that counts “break” the
dependency graph. For instance let’s assume you have an aws_ec2_instance resource and you use a count to provision 3 of
them. Now let’s assume that you ALSO use that count inside a dependent
resource (like a broker id in kafka) and that is a dependency on that
instance resource.When you increase the count, the planner recomputes EVERYTHING using that count variable.
If you must use a count, you should ensure that the count is not used in ANY other resources unless you can safely update those resources as well - this includes passing the same _count_ to a template that you use in the instance resource.
Scope creep
Terraform brings a much needed true infrastructure-as-code approach to things. It is important, however, to not try and do too much in terraform. This is the reason we wrap terraform in Rundeck. Rundeck allows us to wrap terraform with steps that don’t FIT in terraform to create a final deliverable.Terraform is a really poor fit for application deployment and volatile lifecycle components.
My general rule is if something feels hard in terraform it probably is.
Recreating resource
taint will destroy and re-create resourceterraform taint aws_instance.nat
Terraforming
Terraforming is a discovery utility to build terraform tf and state files from a running AWS infrastructure.https://github.com/dtan4/terraforming
Installation
gem install terraforming gem install io-console
Usage
terraforming elb --profile fitch > elb.tf terraforming elb --profile fitch --tfstate > terraform.tfstate
"terraform plan" should show you no changes after that.
Merge
Example of merging terraform state with existing infrastructureDisable remote state
terraform remote config -disable
Pull routing data
terraforming rt > route.tf
Update current state file
terraforming rt --tfstate --merge=terraform.tfstate --overwrite
Upload state to remote location
terraform remote config -backend=artifactory -backend-config="repo=terraform-state" -backend-config="subpath=customers/enterpriseeu/vpn"
Wednesday, July 26, 2017
Terraform encryption
How to encrypt db password
1. Create KMS master key from AWS GUI console.
http://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html
(be careful about the Region! Make sure the key is created in the right region)
2. Create a text file with your password in it
echo "mypassword" > /home/igorg/db.txt
3. Encrypt your plain text password with aws key
aws kms encrypt --key-id 3fb2...c7f3 --plaintext fileb:///home/igorg/db.txt --output text --query CiphertextBlob
the output would be an encrypted string.
4. Put your encrypted password in terraform definition
data "aws_kms_secret" "db" {
secret {
name = "master_password"
payload = "AQICAHg...C0rTg="
}
}
resource "aws_db_instance" "krdb1" {
allocated_storage = 5
storage_type = "gp2"
engine = "mysql"
engine_version = "5.6.35"
instance_class = "db.t2.micro"
name = "krdb1"
username = "admin"
password = "${data.aws_kms_secret.db.master_password}"
db_subnet_group_name = "krdb_group"
vpc_security_group_ids = ["${aws_security_group.db.id}"]
}
1. Create KMS master key from AWS GUI console.
http://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html
(be careful about the Region! Make sure the key is created in the right region)
2. Create a text file with your password in it
echo "mypassword" > /home/igorg/db.txt
3. Encrypt your plain text password with aws key
aws kms encrypt --key-id 3fb2...c7f3 --plaintext fileb:///home/igorg/db.txt --output text --query CiphertextBlob
the output would be an encrypted string.
4. Put your encrypted password in terraform definition
data "aws_kms_secret" "db" {
secret {
name = "master_password"
payload = "AQICAHg...C0rTg="
}
}
resource "aws_db_instance" "krdb1" {
allocated_storage = 5
storage_type = "gp2"
engine = "mysql"
engine_version = "5.6.35"
instance_class = "db.t2.micro"
name = "krdb1"
username = "admin"
password = "${data.aws_kms_secret.db.master_password}"
db_subnet_group_name = "krdb_group"
vpc_security_group_ids = ["${aws_security_group.db.id}"]
}
Thursday, June 02, 2016
Storing aws credentials in S3 bucket
Make a script aws_init.sh on your dedicated server with admin AWS privileges
#! /bin/bash
set -e
mkdir -p ~/.ssh
aws s3 cp s3://keys/chef-client ~/.ssh/chef-client
mkdir -p ~/.chef/
aws s3 cp s3://keys/validation-prod.pem ~/.chef
aws s3 cp s3://keys/validation-stag.pem ~/.chef
Initialize the key by running:
#! /bin/bash
set -e
mkdir -p ~/.ssh
aws s3 cp s3://keys/chef-client ~/.ssh/chef-client
mkdir -p ~/.chef/
aws s3 cp s3://keys/validation-prod.pem ~/.chef
aws s3 cp s3://keys/validation-stag.pem ~/.chef
Initialize the key by running:
eval "`aws s3 cp s3://keys/aws-access-keys.txt -`"
~/src/bin/aws_init.sh
Internal private SSL Certificate Authority
Internal SSL Certificate Authority
Intermediate CA
root@devops:~/ca# openssl genrsa -aes256 -out private/intermediate.key.pem 4096 root@devops:~/ca# openssl req -new -sha256 -key private/intermediate.key -out requests/intermediate.csr -config config.txt -subj "/C=US/ST=California/L=San\ Francisco/O=Company/OU=DevOps/CN=Company Intermediate CA/emailAddress=devops@company.com" |
IT signed this request with Company Root CA
Creating server cert based on Intermediate CA
root@devops:~/ca# openssl req -new -nodes -keyout newcerts/wild.company.com.key -out requests/wild.company.com.csr -config config.txt -subj "/C=US/ST=California/L=San\ Francisco/O=Company/OU=DevOps/CN=*.company.com/emailAddress=devops@company.com" root@devops:~/ca# openssl ca -batch -notext -config config.txt -in requests/wild.company.com.csr -cert certs/intermediate_ca.crt -keyfile private/intermediate_ca.key -out newcerts/wild.company.com.crt root@devops:~/ca# cat certs/intermediate_ca.crt >> newcerts/wild.company.com.crt |
See your cert
openssl x509 -in wild.company.com.crt -text |
Revocation list
root@devops:~/ca# echo "01" > crlnumber root@devops:~/ca# openssl ca -config config.txt -gencrl -out crl/certificate.crl |
CA location
In order for Ubuntu system to trust your CA certificate add it to
/usr/local/share/ca-certificates/companyca.crt update-ca-certificates |
Upload new cert to AWS
aws iam upload-server-certificate --server-certificate-name $hostname --certificate-body file://$hostname.crt --private-key file://$hostname.key |
Wednesday, June 01, 2016
Two factor authentication with ssh
Two factor definition:
1. something you have (ssh private key)
2. something you know (your account password)
On Ubuntu
/etc/ssh/sshd_config
# Require public key and password by default
PasswordAuthentication yes
AuthenticationMethods publickey,password
# Allow deploy and git groups to log in without password
Match Group deploy,git
PasswordAuthentication no
AuthenticationMethods publickey
Restart sshd
When you ssh to the server you'll be prompted for your passphrase and then again for your password.
1. something you have (ssh private key)
2. something you know (your account password)
On Ubuntu
/etc/ssh/sshd_config
# Require public key and password by default
PasswordAuthentication yes
AuthenticationMethods publickey,password
# Allow deploy and git groups to log in without password
Match Group deploy,git
PasswordAuthentication no
AuthenticationMethods publickey
Restart sshd
When you ssh to the server you'll be prompted for your passphrase and then again for your password.
Thursday, May 26, 2016
Chef recipe how to start and use custom service or script
Here is an example of a custom service for ubuntu:
package 'push-jobs-client' do
action :install
end
template '/etc/chef/push-jobs-client.rb' do
source 'push-jobs-client.rb.erb'
owner 'root'
group 'root'
mode '644'
end
template '/etc/init/chef-push-jobs.conf' do
source 'push-jobs-client-run.erb'
owner 'root'
group 'root'
mode '644'
end
service 'chef-push-jobs' do
supports [:stop, :start, :restart, :status]
action [ :enable, :start]
end
cat templates/default/push-jobs-client.rb.erb
# Generated by Chef for <%= node[:fqdn] %>
# Local modifications will be overwritten!
# Added this wrapper for community cookbook to allow_unecrypted; encryption is done over SSL
LC_ALL='en_US.UTF-8'
# Chef server connect options
chef_server_url '<%= Chef::Config[:chef_server_url] %>'
node_name '<%= Chef::Config[:node_name] %>'
client_key '/etc/chef/client.pem'
trusted_certs_dir '/etc/chef/trusted_certs'
verify_api_cert false
allow_unencrypted true
# The whitelist comes from default attributes
whitelist<%= node['base']['push-jobs']['whitelist'] %>
# We're under runit, so don't output timestamp
Mixlib::Log::Formatter.show_time = false
cat templates/default/push-jobs-client-run.erb
start on runlevel [2345]
stop on runlevel [!2345]
script
exec /opt/push-jobs-client/bin/pushy-client -l info -c /etc/chef/push-jobs-client.rb
end script
package 'push-jobs-client' do
action :install
end
template '/etc/chef/push-jobs-client.rb' do
source 'push-jobs-client.rb.erb'
owner 'root'
group 'root'
mode '644'
end
template '/etc/init/chef-push-jobs.conf' do
source 'push-jobs-client-run.erb'
owner 'root'
group 'root'
mode '644'
end
service 'chef-push-jobs' do
supports [:stop, :start, :restart, :status]
action [ :enable, :start]
end
cat templates/default/push-jobs-client.rb.erb
# Generated by Chef for <%= node[:fqdn] %>
# Local modifications will be overwritten!
# Added this wrapper for community cookbook to allow_unecrypted; encryption is done over SSL
LC_ALL='en_US.UTF-8'
# Chef server connect options
chef_server_url '<%= Chef::Config[:chef_server_url] %>'
node_name '<%= Chef::Config[:node_name] %>'
client_key '/etc/chef/client.pem'
trusted_certs_dir '/etc/chef/trusted_certs'
verify_api_cert false
allow_unencrypted true
# The whitelist comes from default attributes
whitelist<%= node['base']['push-jobs']['whitelist'] %>
# We're under runit, so don't output timestamp
Mixlib::Log::Formatter.show_time = false
cat templates/default/push-jobs-client-run.erb
start on runlevel [2345]
stop on runlevel [!2345]
script
exec /opt/push-jobs-client/bin/pushy-client -l info -c /etc/chef/push-jobs-client.rb
end script
Tuesday, March 15, 2016
AWS Terraform
If you ever had to manage AWS Amazon environment you probably start by clicking through AWS console - Web GUI, then you quickly realize that it won't scale, because all your clicks are not repeatable and there is no code review of some sort.
Next logical step it to use aws cli, but then you'd need to create a bunch of scripts to manage it. There are a couple of options for scalable and systematic approach - one is Cloud Formation from Amazon, another cool solution is Terraform.
Terraform allows you to manage pretty much any AWS resource from a single configuration file that could be sitting in your GIT repo. This is truly "infrastructure as a code" solution.
Here is an example of how to create a simple ec2 instance with your parameters.
resource "aws_instance" "my-server" {
ami = "ami-93742ea3"
availability_zone = "us-west-2a"
ebs_optimized = false
instance_type = "t2.micro"
monitoring = false
key_name = "your-ssh-access-key"
subnet_id = "subnet-99999999"
security_group_ids = ["sg-ae33333", "sg-bb33333"]
associate_public_ip_address = true
private_ip = "10.10.10.50"
source_dest_check = true
root_block_device {
volume_type = "gp2"
volume_size = 10
iops = 30
delete_on_termination = true
}
tags {
"Name" = "my-server"
}
}
As you can see above, some values like subnet and security group are hardcoded since we are dealing with existing infrastructure, and those resources were created by hand.
It's easy enough to refactor those into a separate file called "variables.tf", so that you could at least use them by readable human names.
variable "access_key" {}
variable "secret_key" {}
# Default AWS region is West
variable "aws_region" {
default = "us-west-2"
}
# Security Groups
variable "sg" {
default = {
internal_ping = "sg-0444444"
internal_ssh = "sg-444444444"
}
}
Now you can start using those variables and you terraform template will be more readable
security_group_id = "${var.sg.internal_ping},${var.sg.internal_ssh},${var.sg.internal_http}"
Here is another example on how Terraform can manage a single DNS entry assuming that you have all your zones in that variables.tf file
resource "aws_route53_record" "my-server-A" {
zone_id = "${var.dns_zone.company_com}"
name = "my-server.company.com"
type = "A"
records = ["10.10.10.50"]
ttl = "600"
}
Next logical step it to use aws cli, but then you'd need to create a bunch of scripts to manage it. There are a couple of options for scalable and systematic approach - one is Cloud Formation from Amazon, another cool solution is Terraform.
Terraform allows you to manage pretty much any AWS resource from a single configuration file that could be sitting in your GIT repo. This is truly "infrastructure as a code" solution.
Here is an example of how to create a simple ec2 instance with your parameters.
resource "aws_instance" "my-server" {
ami = "ami-93742ea3"
availability_zone = "us-west-2a"
ebs_optimized = false
instance_type = "t2.micro"
monitoring = false
key_name = "your-ssh-access-key"
subnet_id = "subnet-99999999"
security_group_ids = ["sg-ae33333", "sg-bb33333"]
associate_public_ip_address = true
private_ip = "10.10.10.50"
source_dest_check = true
root_block_device {
volume_type = "gp2"
volume_size = 10
iops = 30
delete_on_termination = true
}
tags {
"Name" = "my-server"
}
}
As you can see above, some values like subnet and security group are hardcoded since we are dealing with existing infrastructure, and those resources were created by hand.
It's easy enough to refactor those into a separate file called "variables.tf", so that you could at least use them by readable human names.
variable "access_key" {}
variable "secret_key" {}
# Default AWS region is West
variable "aws_region" {
default = "us-west-2"
}
# Security Groups
variable "sg" {
default = {
internal_ping = "sg-0444444"
internal_ssh = "sg-444444444"
}
}
Now you can start using those variables and you terraform template will be more readable
security_group_id = "${var.sg.internal_ping},${var.sg.internal_ssh},${var.sg.internal_http}"
Here is another example on how Terraform can manage a single DNS entry assuming that you have all your zones in that variables.tf file
resource "aws_route53_record" "my-server-A" {
zone_id = "${var.dns_zone.company_com}"
name = "my-server.company.com"
type = "A"
records = ["10.10.10.50"]
ttl = "600"
}
Friday, May 15, 2015
Active Directory authentication for Linux
Add the following lines to your kickstart config.
# Join AD domain
yum -y install pbis-open
/usr/bin/domainjoin-cli setname ${hostname}.corp.yourdomain.com
/usr/bin/domainjoin-cli join --ou OU=LinuxServers,OU=Internal,DC=corp,DC=yourdomain,DC=com corp.yourdomain.com joinaccount joinpassword
/opt/pbis/bin/config AssumeDefaultDomain true
/opt/pbis/bin/config LoginShellTemplate /bin/bash
/opt/pbis/bin/config HomeDirTemplate %H/%D/%U
Simple shell script to do the same:
joinad.sh
#!/bin/bash
if [ -z "$1" ]
then
echo "Usage: joinad.sh yourservername"
exit 1
fi
echo "Joining $1"
ssh -t $1 "domainjoin-cli join --disable hostname --ou OU=LinuxServers,OU=Internal,DC=corp,DC=yourdomain,DC=com corp.yourdomain.com joinaccount joinpassword; /opt/pbis/bin/config AssumeDefaultDomain true; /opt/pbis/bin/config LoginShellTemplate /bin/bash; /opt/pbis/bin/config HomeDirTemplate %H/%U"
# Join AD domain
yum -y install pbis-open
/usr/bin/domainjoin-cli setname ${hostname}.corp.yourdomain.com
/usr/bin/domainjoin-cli join --ou OU=LinuxServers,OU=Internal,DC=corp,DC=yourdomain,DC=com corp.yourdomain.com joinaccount joinpassword
/opt/pbis/bin/config AssumeDefaultDomain true
/opt/pbis/bin/config LoginShellTemplate /bin/bash
/opt/pbis/bin/config HomeDirTemplate %H/%D/%U
Simple shell script to do the same:
joinad.sh
#!/bin/bash
if [ -z "$1" ]
then
echo "Usage: joinad.sh yourservername"
exit 1
fi
echo "Joining $1"
ssh -t $1 "domainjoin-cli join --disable hostname --ou OU=LinuxServers,OU=Internal,DC=corp,DC=yourdomain,DC=com corp.yourdomain.com joinaccount joinpassword; /opt/pbis/bin/config AssumeDefaultDomain true; /opt/pbis/bin/config LoginShellTemplate /bin/bash; /opt/pbis/bin/config HomeDirTemplate %H/%U"
Terminal/Console
Screen
Below is some customization for screen utility.cat .screenrc
# SSH agent link
setenv SSH_AUTH_SOCK $HOME/.ssh/ssh_auth_sock
# Number of lines
defscrollback 10000
# for ctrl right and left arrows
bindkey ^[[1;5D prev
bindkey ^[[1;5C next
bindkey "^[[D" prev # ctrl-left
bindkey "^[[C" next # ctrl-right
# To remove splits
bind X remove
# Window list at the bottom. hostname, centered tabs and redmarked active windows:
#hardstatus alwayslastline
#hardstatus string '%{= kG}[ %{G}%H %{g}][%= %{= kw}%?%-Lw%?%{r}(%{W}%n*%f%t%?(%u)%?%{r})%{w}%?%+Lw%?%?%= %{g}][%{B} %d/%m %{W}%c %{g}]'
#caption always "%{+b rk}%H%{gk} |%c %{yk}%d.%m.%Y | %72=Load: %l %{wk}"
hardstatus alwayslastline "%?%{yk}%-Lw%?%{wb}%n*%f %t%?(%u)%?%?%{yk}%+Lw%?"
To keep your ssh-agent running in screen session add rc file:
cat .ssh/rc
if test "$SSH_AUTH_SOCK" ; then
ln -sf $SSH_AUTH_SOCK ~/.ssh/ssh_auth_sock
fi
Monday, May 11, 2015
VMware CLI management
I'm trying to use command line interface as much as I can. Below are some examples of VMware CLI management.
Get a list of all VMs:
vim-cmd vmsvc/getallvms
Take a look at template:
/vmfs/volumes/541abff4-d8f5aafc-5d95-002590e90bb0 # /vmfs/volumes/datastore1/vmwa re-ovftool/ovftool prve-loadtest.ova
Deploy VM from Windows ovf tool:
C:\Program Files (x86)\VMware\VMware OVF Tool>ovftool --disableVerification --no SSLVerify -dm=thick --datastore=datastore1 --name=loadtest1 --net:"VM Net work"="VM Network" c:\Users\igrinkin\loadtest.ova vi://root:root_password@hypervisor1 Opening OVA source: c:\Users\igrinkin\loadtest.ova Opening VI target: vi://root@10.107.130.29:443/ Deploying to VI: vi://root@10.107.130.29:443/ Transfer Completed The manifest validates Warning: - No manifest entry found for: 'loadtest1-disk1.vmdk'. Completed successfully
Copy VM to another ESX server
ssh hypervisor1 cd /vmfs/volumes/datastore1/ scp -r /vmfs/volumes/datastore1/testvm/ hypervisor2:/vmfs/volumes/datastore1/
Open hypervisor2 in vSphere
Configuration - Storage - right-click on datastore1 - Browse to "testvm" folder - click on testvm.vmx - Add to inventory
Remove VM from hypervisor1 to cleanup
You can easily script it to emulate what Vcenter does.
Add a second hard drive to VM without reboot
Add second drive to live server.
Run
echo "- - -" > /sys/class/scsi_host/host2/scan
fdisk should show you the second drive. Create partition and format.
mkfs.ext3 -m0 /dev/sdb1 tune2fs -c0 -i0 /dev/sdb1
Using mysql module in puppet manifest
I downloaded mysql module from Puppet Labs.
Here is an example of using it in graphite module.
class graphite {
$graphite_packages = ["graphite-web","MySQL-python","python-carbon","python-whisper"]
package { $graphite_packages: ensure => installed }
$graphite_services = ["httpd","carbon-cache"]
service { $graphite_services: ensure => running, enable => true }
file { "/etc/graphite-web/local_settings.py":
owner => "root",
group => "root",
mode => 644,
source => "puppet:///modules/graphite/local_settings.py",
notify => Service["httpd"],
}
file { "/etc/carbon/storage-schemas.conf":
owner => "root",
group => "root",
mode => 644,
source => "puppet:///modules/graphite/storage-schemas.conf",
notify => Service["httpd"],
}
file { "/root/graph.sql":
owner => "root",
group => "root",
mode => 644,
source => "puppet:///modules/graphite/graph.sql",
notify => Service["mysqld"],
}
include '::mysql::server'
mysql::db { 'mydb':
dbname => 'graphite',
user => 'graphite',
password => 'graphitepassword',
host => 'localhost',
grant => ['ALL'],
sql => '/root/graph.sql',
import_timeout => 900,
}
}
Here is an example of using it in graphite module.
class graphite {
$graphite_packages = ["graphite-web","MySQL-python","python-carbon","python-whisper"]
package { $graphite_packages: ensure => installed }
$graphite_services = ["httpd","carbon-cache"]
service { $graphite_services: ensure => running, enable => true }
file { "/etc/graphite-web/local_settings.py":
owner => "root",
group => "root",
mode => 644,
source => "puppet:///modules/graphite/local_settings.py",
notify => Service["httpd"],
}
file { "/etc/carbon/storage-schemas.conf":
owner => "root",
group => "root",
mode => 644,
source => "puppet:///modules/graphite/storage-schemas.conf",
notify => Service["httpd"],
}
file { "/root/graph.sql":
owner => "root",
group => "root",
mode => 644,
source => "puppet:///modules/graphite/graph.sql",
notify => Service["mysqld"],
}
include '::mysql::server'
mysql::db { 'mydb':
dbname => 'graphite',
user => 'graphite',
password => 'graphitepassword',
host => 'localhost',
grant => ['ALL'],
sql => '/root/graph.sql',
import_timeout => 900,
}
}
Friday, May 08, 2015
How to: puppet - hiera - foreman enc
Here is an example of a puppet setup with hiera and Foreman as ENC (external node classifier)
Let's say I want to manage my /etc/resolv.conf using puppet. Since I have multiple datacenters, I want to point linux clients to the closest DNS server.
I want my puppet templates to be generic, so that I don't have to touch it again. All hard-coded data (like IP addresses) goes into hiera.
cat puppet/modules/dns/manifests/init.pp
class dns ( $dns_search = hiera("dns::search"),
$dns_servers = hiera("dns::servers")) {
file { "/etc/resolv.conf":
owner => "root",
group => "root",
mode => 644,
content => template("dns/resolv.conf.erb"),
}
}
Basically, I'm saying that "dns_search" and "dns_servers" variables will come from hiera data.
Here is the template that puppet will apply:
cat puppet/modules/dns/templates/resolv.conf.erb
# This file is controlled by Puppet
search <%= dns_search %>
<% @dns_servers.each do |server| -%>
nameserver <%= server %>
<% end -%>
cat /etc/puppet/hiera.yaml
---
:backends:
- yaml
:yaml:
:datadir: /etc/puppet/hieradata
:hierarchy:
- "%{::clientcert}"
- "nodes/%{::fqdn}"
- "%{::environment}"
- "location/%{::location}"
- defaults
Location hiera file for Redwood City will look like:
cat puppet/hieradata/location/rwc.yaml
---
dns::search: rwc.mycompany.com mycompany.com
dns::servers:
- 192.168.0.2
- 192.168.0.3
- 10.10.0.2
Foreman installation was pretty straightforward from:
http://theforeman.org/manuals/1.1/quickstart_guide.html
foreman-installer --foreman-db-type=mysql
I used mysql database and my own certificate that was signed by my own CA.
cat /etc/puppet/foreman.yaml
---
:url: "https://foreman.mycompany.com"
:ssl_ca: "/etc/pki/tls/certs/mycompanyca.crt"
:ssl_cert: "/etc/pki/tls/certs/foreman.crt"
:ssl_key: "/etc/pki/tls/private/foreman.key"
:user: ""
:password: ""
:puppetdir: "/var/lib/puppet"
:puppetuser: "puppet"
:facts: true
:timeout: 10
:threads: null
Let's say I want to manage my /etc/resolv.conf using puppet. Since I have multiple datacenters, I want to point linux clients to the closest DNS server.
I want my puppet templates to be generic, so that I don't have to touch it again. All hard-coded data (like IP addresses) goes into hiera.
Puppet
I separate puppet classes by modules for convenience. Here is how puppet manifest looks like:cat puppet/modules/dns/manifests/init.pp
class dns ( $dns_search = hiera("dns::search"),
$dns_servers = hiera("dns::servers")) {
file { "/etc/resolv.conf":
owner => "root",
group => "root",
mode => 644,
content => template("dns/resolv.conf.erb"),
}
}
Basically, I'm saying that "dns_search" and "dns_servers" variables will come from hiera data.
Here is the template that puppet will apply:
cat puppet/modules/dns/templates/resolv.conf.erb
# This file is controlled by Puppet
search <%= dns_search %>
<% @dns_servers.each do |server| -%>
nameserver <%= server %>
<% end -%>
Hiera
Hiera configuration file goes by location:cat /etc/puppet/hiera.yaml
---
:backends:
- yaml
:yaml:
:datadir: /etc/puppet/hieradata
:hierarchy:
- "%{::clientcert}"
- "nodes/%{::fqdn}"
- "%{::environment}"
- "location/%{::location}"
- defaults
Location hiera file for Redwood City will look like:
cat puppet/hieradata/location/rwc.yaml
---
dns::search: rwc.mycompany.com mycompany.com
dns::servers:
- 192.168.0.2
- 192.168.0.3
- 10.10.0.2
Foreman
You don't have to use Foreman but it gives you a nice GUI, dashboard and can easily be used as ENC to create puppet host groups and configuration groups.Foreman installation was pretty straightforward from:
http://theforeman.org/manuals/1.1/quickstart_guide.html
foreman-installer --foreman-db-type=mysql
I used mysql database and my own certificate that was signed by my own CA.
cat /etc/puppet/foreman.yaml
---
:url: "https://foreman.mycompany.com"
:ssl_ca: "/etc/pki/tls/certs/mycompanyca.crt"
:ssl_cert: "/etc/pki/tls/certs/foreman.crt"
:ssl_key: "/etc/pki/tls/private/foreman.key"
:user: ""
:password: ""
:puppetdir: "/var/lib/puppet"
:puppetuser: "puppet"
:facts: true
:timeout: 10
:threads: null
Wednesday, April 15, 2015
GIT
Create new GIT repo on the server:
add user git
create athorized_keys for git
mkdir /data/git
ln -s /data/git /git
cd /git
mkdir ops.git
cd ops.git
git --bare init
add user git
create athorized_keys for git
mkdir /data/git
ln -s /data/git /git
cd /git
mkdir ops.git
cd ops.git
git --bare init
Friday, April 10, 2015
Puppet notes
Built-in puppet variables in templates:
# This file is controlled by Puppet
# /etc/puppet/puppet.conf
# <%= scope.lookupvar('::osfamily') %> <%= scope.lookupvar('::operatingsystemmajrelease') %>
Example of template and file:
file { "/etc/puppet/puppet.conf":
owner => "root",
group => "root",
mode => 644,
content => template("puppet/puppet.conf.erb"),
notify => Service["puppet"],
}
file { "/etc/sysconfig/puppet":
owner => "root",
group => "root",
mode => 644,
source => "puppet:///modules/puppet/puppet",
notify => Service["puppet"],
}
Hieradata
cat hiera.yaml
---
:backends:
- yaml
:yaml:
:datadir: /etc/puppet/hieradata
:hierarchy:
- "%{::clientcert}"
- "nodes/%{::fqdn}"
- "%{::environment}"
- "location/%{::location}"
- defaults
Example of location yaml
cat location/mylocation.yaml
---
dns::search: myl.location.com location.com
dns::servers:
- 192.168.0.100
- 192.168.0.101
ssh::group_key: AAAAB3NzaC1yc........
# This file is controlled by Puppet
# /etc/puppet/puppet.conf
# <%= scope.lookupvar('::osfamily') %> <%= scope.lookupvar('::operatingsystemmajrelease') %>
Example of template and file:
file { "/etc/puppet/puppet.conf":
owner => "root",
group => "root",
mode => 644,
content => template("puppet/puppet.conf.erb"),
notify => Service["puppet"],
}
file { "/etc/sysconfig/puppet":
owner => "root",
group => "root",
mode => 644,
source => "puppet:///modules/puppet/puppet",
notify => Service["puppet"],
}
Hieradata
cat hiera.yaml
---
:backends:
- yaml
:yaml:
:datadir: /etc/puppet/hieradata
:hierarchy:
- "%{::clientcert}"
- "nodes/%{::fqdn}"
- "%{::environment}"
- "location/%{::location}"
- defaults
Example of location yaml
cat location/mylocation.yaml
---
dns::search: myl.location.com location.com
dns::servers:
- 192.168.0.100
- 192.168.0.101
ssh::group_key: AAAAB3NzaC1yc........
Monday, March 16, 2015
How to setup E-mail relay
Gmail rejects e-mail from my server.
Solution: E-mail relay through Mandrill (MailChimp service)
1. Create account with Mandrill (it's free)
2. Generate some API key on Mandrill website
3. Setup your Postfix
yum -y install postfix cyrus-sasl-plain cyrus-sasl-md5
4. Modify /etc/postfix/main.cf
relayhost = smtp.mandrillapp.com
smtp_sasl_auth_enable=yes
smtp_sasl_password_maps=hash:/etc/postfix/sasl_passwd
smtp_sasl_mechanism_filter = AUTH LOGIN
smtp_sasl_security_options =
5. Put your credentials in /etc/postfix/sasl_passwd file
smtp.mandrillapp.com yourusername@yourdomain.com:yourapikeyhere
6. Create sasl db readable by postfix
postmap sasl_passwd
chmod 600 sasl_passwd
chown postfix:postfix sasl_passwd.db
Your maillog should look similar to this:
Mar 15 23:45:05 tm1 postfix/master[29546]: daemon started -- version 2.6.6, configuration /etc/postfix
Mar 15 23:45:41 tm1 postfix/pickup[29548]: 34B98580496: uid=500 from=<user>
Mar 15 23:45:41 tm1 postfix/cleanup[29678]: 34B98580496: message-id=<20150316064541.34B98580496@mail.tagmap.me>
Mar 15 23:45:41 tm1 postfix/qmgr[29549]: 34B98580496: from=<user@mail.tagmap.me>, size=419, nrcpt=1 (queue active)
Mar 15 23:45:42 tm1 postfix/smtp[29680]: 34B98580496: to=<user@gmail.com>, relay=smtp.mandrillapp.com[54.70.134.182]:25, delay=1.3, delays=0.02/0.01/1.1/0.24, dsn=2.0.0, status=sent (250 2.0.0 Ok: queued as 5A3F038028A)
Mar 15 23:45:42 tm1 postfix/qmgr[29549]: 34B98580496: removed
Tuesday, December 23, 2014
LVM
Logical Volume Manager
How to expand existing root partition using LVM
Add a second physical drive. Scan the system, no reboot needed:
echo "- - -" > /sys/class/scsi_host/host1/scan
Create Logic Group and Volume:
pvcreate datavg /dev/sdb1
vgcreate datavg /dev/sdb1
lvcreate -l 100%FREE -n lvdata datavg
# vgdisplay
--- Volume group ---
VG Name datavg
System ID
Format lvm2
Metadata Areas 1
Metadata Sequence No 2
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 1
Open LV 0
Max PV 0
Cur PV 1
Act PV 1
VG Size 59.99 GiB
PE Size 4.00 MiB
Total PE 15358
Alloc PE / Size 15358 / 59.99 GiB
Free PE / Size 0 / 0
VG UUID zWlhGf-YDZa-27xv-aH0t-F6fW-Hs1e-TY3d8t
mkfs.ext3 -m 0 /dev/datavg/lvdata
tune2fs -c0 /dev/datavg/lvdata
Expand existing root LVM partition
pvdisplay
pvcreate /dev/sdb1
vgextend root_partition_name /dev/sdb1
lvextend -l +100%FREE /dev/root_partition_name/root
resize2fs /dev/root_partition_name/root
If you need to reduce it back:
vgreduce -a root_partition_name
Thursday, September 11, 2014
HTTP POST into a form from curl
I want to post some data from file.txt into a form on the website. Curl can do it from command line:
curl -X POST -d @filename.txt -u usernamehere:passwordhere https://server.company.com/session/sync.jspThe content of filename.txt is pairs of name=value separated by &. E.g.
id=1&options=doit&field3=Submit
Subscribe to:
Posts (Atom)