Using Chef Solo to Provision a Rails Server

目标:

使用Chef Solo来将一台全新Ubuntu Server配置为Rails App Server。

关于Chef的介绍见这里:

准备工作

1. 设置 Server

如果有一台新的 Ubuntu Server,为了方便,首先将本机SSH 公钥复制到 Server 上。

1
$ ssh-copy-id root@10.0.0.1

2. 本地创建 Chef Solo 仓库

1
2
3
4
5
$ mkdir chef_rails
$ cd chef_repo
# 初始化 bundler
$ bundle init

在刚生成的 Gemfile 文件中写入如下内容:

1
2
3
4
5
6
source 'http://ruby.taobao.org'
gem 'knife-solo'
gem "chef"
gem 'chef-zero'
gem "berkshelf"

然后运行如下命令来安装所需Gems

1
$ bundle install

3. 服务器上安装 Knife Solo

在 Knife & Knife Solo中介绍过 knife solo提供的prepare命令来在Server上安装Chef。

1
2
3
4
5
$ bundle exec knife solo prepare root@115.28.65.119
# 检查安装情况
server# chef-solo -v
Chef: 12.0.0

3.1 中国特色问题及解决方案

这一路就是在服务器端安装chef client。但由于网络原因,会被卡住:

1
2
3
4
5
6
7
8
WARNING: No knife configuration file found
Bootstrapping Chef...
--2014-12-08 22:08:17-- https://www.opscode.com/chef/install.sh
......
downloading https://opscode-omnibus-packages.s3.amazonaws.com/ubuntu/12.04/x86_64/chef_12.0.0-1_amd64.deb
to file /tmp/install.sh.2141/chef_12.0.0-1_amd64.deb
trying wget...
# 不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动不动

3.2 解决办法:

  1. 多试几次,同时ssh上去看有没有进度,如果文件大小有变化说明在下载,只是比较慢,慢慢等总会下完的。
  2. 本地VPN下载,然后SCP到Server上,手工安装:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ scp chef_12.0.0-1_amd64.deb root@115.28.65.119:/tmp
    server~/tmp$ dpkg -i chef_12.0.0-1_amd64.deb
    # 服务器安装
    Selecting previously unselected package chef.
    (Reading database ... 53040 files and directories currently installed.)
    Unpacking chef (from chef_12.0.0-1_amd64.deb) ...
    Setting up chef (12.0.0-1) ...
    Thank you for installing Chef!

新建 Chef Solo 项目

1. Cookbooks

可以自己创建 cookbook:

1
$ knife cookbook create chef-rails

1.1 Berksfile

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
source "https://api.berkshelf.com"
# Basics
cookbook 'apt'
cookbook 'build-essential'
cookbook 'sudo'
cookbook 'chef-solo-search'
cookbook 'users'
cookbook 'openssl'
# Server Utils
cookbook 'ufw'
cookbook 'ntp'
cookbook 'fail2ban'
cookbook 'hostnames'
cookbook 'locales'
cookbook 'welcome-cookbook', github: 'lanvige/welcome-cookbook'
cookbook 'git'
# Server Monit
cookbook 'monit'
# Web Server
cookbook 'nginx'
# DB
cookbook 'redis-server', github: 'TalkingQuickly/redis-server'
# Rails App
# cookbook 'nodejs'
cookbook 'nodejs-cookbook', github: 'lanvige/nodejs-cookbook'
cookbook 'rails-dependencies-cookbook', github: 'lanvige/rails-dependencies-cookbook'
cookbook 'rbenv', github: 'fnichol/chef-rbenv'

因为依赖关系由 Berksfile来接管,所以我们只要运行berks来进行下载。默认下载到 ~/.berkshelf/cookbooks

1
$ berks install

2. Role

这里,我们会需要多个role

  • bootstrap-server 初始化服务器,创建用户,配置安全相关。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    {
    "name": "bootstrap-server",
    "description": "A server of some kind...",
    "default_attributes": {
    "apt" : {
    "unattended_upgrades" : {
    "enable" : true,
    "allowed_origins" : [
    "${distro_id} stable",
    "${distro_id} ${distro_codename}-security"
    ],
    "automatic_reboot" : true
    }
    },
    "authorization": {
    "sudo": {
    // everyone in the group sysadmin gets sudo rights
    "groups": ["sysadmin"],
    // the deploy user specifically gets sudo rights
    "users": ["deploy"],
    // whether a user with sudo rights can execute sudo
    // commands without entering their password.
    "passwordless": true
    }
    },
    "locales" : {
    "packages" : ["locales"],
    "default" : "en_US.utf8"
    },
    "welcome" : {
    // any extra locales we want available. Useful if your
    // local dev machine uses a locale which doesn't match
    // the servers locale.
    "additional_locales" : ["en_US.utf8"]
    },
    "openssh" : {
    "server" : {
    "password_authentication" : "no",
    "challenge_response_authentication" : "no",
    "permit_empty_passwords" : "no",
    "use_pam" : "no",
    "x11_forwarding" : "no",
    "permit_root_login" : "yes"
    }
    }
    },
    "json_class": "Chef::Role",
    "run_list": [
    // required for generating secure passwords
    "recipe[openssl::default]",
    // required for building from source
    "recipe[build-essential::default]",
    // required by the users cookbook when using chef solo
    "recipe[chef-solo-search::default]",
    // setup standard sysadmin users
    "recipe[users::sysadmins]",
    // install and enable ufw
    "recipe[ufw::default]",
    // enable unattended upgrades
    "recipe[apt::unattended-upgrades]",
    // enable automatic time sync
    "recipe[ntp::default]",
    // make sure deploy user has sudo rights
    "recipe[sudo::default]",
    // Make sure we have a valid locale setup
    // "recipe[locales::default]",
    // Block repeated failed login attempts
    "recipe[fail2ban::default]",
    // git
    "recipe[git]",
    // Visual tweaks
    "recipe[welcome-cookbook::default]"
    ],
    "chef_type": "role",
    "override_attributes": {
    }
    }
  • nginx-server Web 服务器。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {
    "name": "nginx-server",
    "description": "Nginx server",
    "default_attributes": {
    "firewall" : {
    "rules" : [
    {"allow http on port 80" : {"port" : 80}}
    ]
    },
    "nginx" : {
    "default_site_enabled" : false
    }
    },
    "json_class": "Chef::Role",
    "run_list": [
    "recipe[nginx::repo]",
    "recipe[nginx::default]",
    // "recipe[monit_configs-tlq::nginx]",
    "recipe[ufw::default]"
    ],
    "chef_type": "role"
    }
  • ruby-server Ruby 运行所需要的所有基础App。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    {
    "name": "rails-app",
    "description": "Something which runs Ruby apps.",
    // 这里的默认选项,可以不要,毕竟在node里会设置的。
    "default_attributes": {
    "rbenv":{
    "rubies": [
    "2.1.5"
    ],
    "global" : "2.1.5",
    "gems": {
    "2.1.5" : [
    {"name":"bundler"}
    ]
    }
    }
    },
    "json_class": "Chef::Role",
    "run_list": [
    "recipe[rails-dependencies-cookbook]",
    "recipe[nodejs-cookbook]",
    // "recipe[nodejs]",
    // "recipe[nodejs::npm]",
    "recipe[ruby_build]",
    "recipe[rbenv::system]"
    ],
    "chef_type": "role",
    "override_attributes": {
    }
    }
  • redis-server Cache Server。

3. 创建 deploy 发布用户

data_bags下添加一个目录users,新建一个用户名的deploy.json文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
user 'deploy' do
password "$1$7ROt/cNu$lCqfOcMWiO2SmP5GIrNm91"
gid "admin"
home "/home/deploy"
supports manage_home: true
end
{
"id": "deploy",
// generate this with: openssl passwd -1 "plaintextpassword"
"password": "",
// cat ~/.ssh/id_rsa.pub
"ssh_keys": [""
],
"groups": [ "sysadmin"],
"shell": "\/bin\/bash"
}

4. 创建 node

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"environment": "production",
"authorization": {
"sudo": {
"users": ["deploy"]
}
},
"rbenv":{
"rubies": [
"2.1.5"
],
"global" : "2.1.5",
"gems": {
"2.1.5" : [
{"name":"bundler"}
]
}
},
"run_list":
[
"role[bootstrap-server]",
"role[nginx-server]",
"role[ruby-server]",
"role[redis-server]"
],
"automatic": {
"ipaddress": "10.0.0.1"
}
}

Cooking

数据,配置都准备齐全了,是时候在真机上RUN了:

1
$ bundle exec knife solo cook root@10.0.0.1

End

示例代码:https://github.com/lanvige/chef-rails

REF::