chefでprovisioningする
今は下火なのかもだけど、chef周りのメモ。案件によってはまだまだ使うので。
インストール
chefをインストールする方法は昔から乱立しててカオスだった記憶があるけど、最近はchefdkで一本化の方向?みたいな雰囲気を感じた。
インストールはhomebrewでやっておくのがよさそう
➜ ~ brew cask install chefdk ➜ ~ chef -v Chef Development Kit Version: 2.5.3 chef-client version: 13.8.5 delivery version: master (73ebb72a6c42b3d2ff5370c476be800fee7e5427) berks version: 6.3.1 kitchen version: 1.20.0 inspec version: 1.51.21
リポジトリを作る
リポジトリの作り方も何通りかあるみたいで統一されてないみたい。たとえばgeneratorが面倒みてくれるものでも下記のようなものがある。
➜ ~ chef generate --help Usage: chef generate GENERATOR [options] Available generators: app Generate an application repo cookbook Generate a single cookbook recipe Generate a new recipe attribute Generate an attributes file template Generate a file template file Generate a cookbook file helpers Generate a cookbook helper file in libraries lwrp Generate a custom resource resource Generate a custom resource repo Generate a Chef code repository policyfile Generate a Policyfile for use with the install/push commands generator Copy ChefDK's generator cookbook so you can customize it build-cookbook Generate a build cookbook for use with Delivery -h, --help Show this message -v, --version Show chef version
どれ使えばええねん。。。app
とrepo
がそれっぽい。repo
は昔からよく見るdirectory構成になってるけど、謎のexampleが入ってる。app
は最近の構成?らしい。よくわからん。
とりあえずやってみる
以降の構成はベスト・プラクティスなのかはわからないが、とりあえず動くので細かいことは考えないようにしておく。
➜ mkdir my_app_server_repo
➜ cd my_app_server_repo
➜ vi knife.rb
knife.rbの内容は以下の通り
local_mode true chef_repo_path File.expand_path('../' , __FILE__) knife[:ssh_attribute] = "knife_zero.host" knife[:use_sudo] = true ## SSHエージェントを使ってないなら、SSHログイン用の鍵へのファイルパスを指定しましょう。 # knife[:identity_file] = "~/.ssh/id_rsa" ## Nodeの各種属性(attributes)はローカルにJSONファイルとして保存されていきます。 ## automatic_attribute_whitelist オプションは、Nodeから収集したAttributeのうち、保存する対象を絞ることができます。 ## NodeもGitで管理する場合は、現状に依存するディスク使用量などは無視してよいでしょう。 knife[:automatic_attribute_whitelist] = %w[ fqdn os os_version hostname ipaddress roles recipes ipaddress platform platform_version cloud cloud_v2 chef_packages ] cookbook_path [ File.expand_path('../berks-cookbooks', __FILE__), "cookbooks", ] knife[:before_bootstrap] = "chef exec berks vendor" knife[:before_converge] = "chef exec berks vendor"
cookbooks
ディレクトリを作って、Berksfile
をリポジトリの直下に持ってくる。
➜ mkdir cookbooks ➜ chef generate cookbook cookbooks/app_server Generating cookbook app_server - Ensuring correct cookbook file content - Ensuring delivery configuration - Ensuring correct delivery build cookbook content Your cookbook is ready. Type `cd cookbooks/app_server` to enter it. There are several commands you can run to get started locally developing and testing your cookbook. Type `delivery local --help` to see a full list. Why not start by writing a test? Tests for the default recipe are stored at: test/integration/default/default_test.rb If you'd prefer to dive right in, the default recipe can be found at: recipes/default.rb ➜ mv cookbooks/app_server/Berksfile ./
Berksfile
の中身は例として以下のようにしておく。
# frozen_string_literal: true source 'https://supermarket.chef.io' cookbook 'nginx', '~> 8.1.2' cookbook 'ruby_rbenv', '~> 2.0.7'
recipeをかく
cookbooks/app_server/recipes/default.rb
を以下のように1行加える
# # Cookbook:: app_server # Recipe:: default # # Copyright:: 2018, The Authors, All Rights Reserved. # install nginx from source include_recipe "nginx::default" ## ←ここを追加
cookbooks/app_server/metadata.rb
を以下のように1行加える
name 'app_server' maintainer 'The Authors' maintainer_email 'you@example.com' license 'All Rights Reserved' description 'Installs/Configures app_server' long_description 'Installs/Configures app_server' version '0.1.0' chef_version '>= 12.14' if respond_to?(:chef_version) depends 'nginx' ## ←ここを追加
recipeを実行
➜ knife zero bootstrap app-server-config --node-name my-app-server1 WARN: Starting local-mode server in deprecated socket mode (CHEF-18) at /opt/chefdk/embedded/lib/ruby/gems/2.4.0/gems/chef-13.8.5/bin/knife:25:in `<top (required)>'. Please see https://docs.chef.io/deprecations_local_listen.html for further details and information on how to correct this problem. Doing old-style registration with the validation key at ... Delete your validation key in order to use your user credentials instead Connecting to montblanc-intg app-server-config -----> Installing Chef Omnibus (-v 13) ... app-server-config Chef Client finished, 0/0 resources updated in 02 seconds
app-server-ssh-config
は~/.ssh/config
に記載されているHost名を入れる想定
➜ knife search node "name:*" 1 items found Node Name: my-app-server1 Environment: _default FQDN: ip-172-23-7-74.ap-northeast-1.compute.internal IP: 172.23.7.74 Run List: Roles: Recipes: Platform: amazon 2017.09 Tags: ➜ knife node run_list add my-app-server1 app_server my-app-server1: run_list: recipe[app_server] ➜ knife zero converge "name:my-app-server1" Execute command hook in before_converge. Resolving cookbook dependencies... Using build-essential (8.1.1) Using mingw (2.0.2) Using ruby_rbenv (2.0.7) ... app-server-config Running handlers: app-server-config Running handlers complete app-server-config Chef Client finished, 28/43 resources updated in 23 seconds
ブラウザでserverインスタンスのipを打ち込んでnginxの404 Not foundが表示されればとりあえずの成功。
改良する
上記のログを見るとわかるが、package版がインストールされている。最近はだいぶマシになっているようだが、package版だと最新のstable版のnginxが入らない。最新のstable版を入れる場合は相変わらずソースからコンパイルする必要がある。なのでそのように調整する。
ついでにrubyもいれて、environmentを使ってstagingやproductionにも対応できるようにしておく。
➜ mkdir attributes ➜ mkdir templates ➜ mkdir templates/default ➜ knife environment create integration --disable-editing ## これはmy_app_server_repo直下でやる
attributes/default.rb
に以下の内容を記載する。checksumの調べた方どうやるんだったかな。思い出したら後で追記する。
default['nginx']['install_method'] = 'source' default['nginx']['source']['version'] = '1.12.2' default['nginx']['source']['url'] = "http://nginx.org/download/nginx-#{node['nginx']['source']['version']}.tar.gz" default['nginx']['source']['checksum'] = '305f379da1d5fb5aefa79e45c829852ca6983c7cd2a79328f8e084a324cf0416' default['nginx']['default_site_enabled'] = false ## ←後で自分が作ったsite_fileをenableにするので default['rbenv']['version'] = '2.5.0'
このままconvergeするとエラーになることがあるようなので、その場合はサーバごと新しいインスタンスを作り直したほうが良さそう。
templates/default/my-app
とかのnginxのsite fileを作る
upstream unicorn_server { server unix:/tmp/unicorn.socket ## 本当はpumaとかでやりたい fail_timeout=0; } server { listen 80; client_max_body_size 4G; server_name my-app; keepalive_timeout 120; # Location of our static files root /srv/www/my-app/<%= node.chef_environment %>/current/public; location ~ ^/assets/ { root /srv/www/my-app/<%= node.chef_environment %>/current/shared/public; } location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; if (!-f $request_filename) { proxy_pass http://unicorn_server; break; } } error_page 500 502 503 504 /500.html; location = /500.html { root /srv/www/my-app/<%= node.chef_environment %>/current/public; } }
recipes/default.rb
は最終的には以下のようにした。とりあえずここまでやっておけば十分かと。
# # Cookbook:: app_server # Recipe:: default # # Copyright:: 2018, The Authors, All Rights Reserved. # 作成したのはintegrationだけだけど、こんな感じで指定可能なenvironmentのチェックを入れておくと良いかも if !['integration', 'staging', 'production'].include?(node.chef_environment) fail "!!SET chef_environment!" end include_recipe "nginx::default" # locate site conf template "#{node['nginx']['dir']}/sites-available/my-app" do source "my-app.erb" owner 'root' group node['root_group'] mode '0644' notifies :reload, 'service[nginx]' end nginx_site "my-app" do enable true end # install packages package 'Install packages' do package_name ['mysql-devel'] end ## install rbenv and ruby rbenv_system_install 'system_install_foo' rbenv_plugin 'ruby-build' do git_url 'https://github.com/rbenv/ruby-build' end rbenv_ruby node['rbenv']['version'] rbenv_global node['rbenv']['version'] ## install bundler rbenv_gem 'bundler' do rbenv_version node['rbenv']['version'] end rbenv_rehash 'rehash' ## create deploy user user 'deploy' do manage_home true end ## create deploy directory directory '/srv' do owner 'root' group 'root' mode '0755' action :create end %W[ /srv/www /srv/www/my-app /srv/www/my-app/#{node.chef_environment} /srv/www/my-app/#{node.chef_environment}/shared ].each do |path| directory path do owner 'deploy' group 'deploy' mode '0755' action :create end end
metadata.rb
を以下のようにしておくのをお忘れなく。
depends 'nginx' depends 'ruby_rbenv' ## ←ここを追加
再度convergeしようとするとエラーになることがあるようなので、その場合はサーバごと新しいインスタンスを作り直したほうが良さそう。
chefつらい。。。やはり今の時代、サーバを直でprovisioningするのはansibleのほうがナウでヤングでホットな気がする。