Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sunzi run #22

Closed
wants to merge 2 commits into from
Closed

Sunzi run #22

wants to merge 2 commits into from

Conversation

hopsoft
Copy link

@hopsoft hopsoft commented Nov 30, 2013

Added a sunzi.run function as a simple idempotent solution for running bash functions. Simply define a specific provisioning operation in a bash function & then call sunzi.run like so.

# install.sh

function pf.update_sources() {
  apt-get install -y lsb-release
  echo deb http://archive.ubuntu.com/ubuntu $(lsb_release -cs) main universe > /etc/apt/sources.list.d/universe.list
  apt-get -qq update
  apt-get -qq -y dist-upgrade
}

sunzi.run "pf.update_sources"

Invoking a fuction with sunzi.run only executes the function once regardless of how many times sunzi deploy is run. The implementation simply touches a file at /etc/sunzi_run/FUNCTION_NAME & then checks for the existence of this file to determine whether or not to run the function.

This simple idempotency solution has proved invaluable for me, making Sunzi a viable alternative to heavier tools like Chef & Puppet.

sunzi.run is an idempotent solution for running bash functions.
This allows someone to isolate provisioning concerns into
distinct functions which can be safely executed via sunzi.run
over N number of sunzi deploys to the same server.
@kenn
Copy link
Owner

kenn commented Dec 1, 2013

I'm not sure if having a persistent state at a remote server is a good idea. It can make things harder - you'd need to ssh to the server when something went wrong. For instance,

# install.sh

function my.custom_recipe() {
  A
  B
  C
}

sunzi.run "my.custom_recipe"

What would you do if A and B passed, but C failed? If it was my responsibility to ensure the atomicity of my.custom_recipe, which I think is impossible because most of provisioning-related problems are external (forward incompatibility, etc.), I would rather tentatively comment out as follows:

# install.sh

# A
# B
C

and re-run. Of course, I don't commit those changes to install.sh on git. What kind of scenario do you have in mind that your approach works better?

Also, leaving the state on the server is incompatible with erase_remote_folder setting in sunzi.yml - scripts can contain sensitive information, and some people prefer to erase them.

@hopsoft
Copy link
Author

hopsoft commented Dec 1, 2013

I have scenarios where some of my provisioning functions run once, some every time, & others where I frequently add new functions. Some of my use cases are complex enough that commenting just doesn't scale well. It's nice to simply use the sunzi.run in my scripts & repeatedly call sunzi deploy whenever I've added a function or have modified a file whose canonical source is in the sunzi project. i.e. nginx.conf, pg_hba.conf, authorized_keys, etc...

@kenn
Copy link
Owner

kenn commented Dec 13, 2013

OK, now that's related to the topic we talked on a separate ticket - I'd use Capistrano for that kind of repetitive tasks.

For instance, I have cap config that deploys

  • Configs for MySQL, logrotate, monit, etc.
  • Init scripts for nginx, unicorn, resque, etc.
namespace :config do
  task :default do
    ...
    unicorn
    ...
  end

  task :unicorn do
    run "cp -f #{current_release}/ops/config/unicorn/unicorn.sh /etc/init.d/unicorn && chmod +x /etc/init.d/unicorn"
  end

Because capistrano fits better for those tasks, when you want to copy and replace those files from the git repository.

Sunzi basically only runs once at the provisioning phase, and all the rest are handled by capistrano. Sunzi shouldn't be a swiss army knife that does everything... Does that make sense?

@hopsoft
Copy link
Author

hopsoft commented Dec 18, 2013

I agree with the idea of limiting Sunzi's scope to the narrowest set. I concede that for some environments copying config files & init scripts might be app specific. Using a tool like Capistrano for such cases is probably more appropriate than using Sunzi.

Having said that... a common use case that I use "re-provisioning" (with Sunzi) for is to copy our canonical authorized_keys file to the "developer" user account onto my servers. All of our developers ssh to our servers with this account. By re-provisioning with Sunzi (using a sunzi.run force) I can easily keep the security tightened up. I consider such use cases to be outside the scope of app deployment and best suited to a tool like Sunzi.

I suppose my Chef knowledge is skewing my opinion on this. I think of provisioning as a tool to manage the server's evolution (independent of app deployment concerns) rather than a 1 time thing that runs on first boot. This is why idempotency is an important goal for me.

It has been very helpful to get your insight on what Sunzi is intended to be. I prefer it's simplicity to other tools like Chef, but I'm currently using it to accomplish some of the same goals. I'm really hoping there is a middle ground that will let me have my cake & eat it too.

@kenn
Copy link
Owner

kenn commented Dec 22, 2013

Having said that... a common use case that I use "re-provisioning" (with Sunzi) for is to copy our canonical authorized_keys file to the "developer" user account onto my servers.

Actually, I use Capistrano for that too.

task :ssh do
  require 'open-uri'

  keys = ['kenn', 'hopsoft'].map do |name|
    open("https://github.com/#{name}.keys").read
  end.join("\n") << "\n"

  put keys, '/root/.ssh/authorized_keys', mode: 0600
end

By directly retrieving SSH keys from Github, authorized_keys gets overwritten entirely every time, so it's idempotent and feel safer running it anytime.

Keeping everything idempotent has become perceived as too much effort, and now I'm more leaning toward Immutable Infrastructure, where every infrastructure is provisioned once and only once. Disposing it upon each app deploy sounds too extreme, but I get the idea very well - I've been always doing "create a new app server then add to the cluster, then eliminate the old one" kind of approach.

For that pattern, Sunzi works perfectly - better at purchasing new instances rather than maintaining the old instances.

@hopsoft
Copy link
Author

hopsoft commented Aug 15, 2014

I'm sold on the immutable infrastructure approach & have been experimenting with Docker for some of my personal projects to accomplish this. Thanks for your insights on this pull request.

@hopsoft hopsoft closed this Aug 15, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants