{"id":617,"date":"2017-02-09T00:49:38","date_gmt":"2017-02-08T23:49:38","guid":{"rendered":"https:\/\/www.dereckson.be\/blog\/?p=617"},"modified":"2017-03-01T22:22:08","modified_gmt":"2017-03-01T21:22:08","slug":"deploy-a-darkbot-or-a-simple-generic-service-with-saltstack-part-2","status":"publish","type":"post","link":"https:\/\/www.dereckson.be\/blog\/2017\/02\/09\/deploy-a-darkbot-or-a-simple-generic-service-with-saltstack-part-2\/","title":{"rendered":"Deploy a darkbot or a simple generic service with SaltStack (part 2)"},"content":{"rendered":"<p><em>This is the part 2 of our service deployment walkthrough.<br \/>\nSee also: <a href=\"https:\/\/www.dereckson.be\/blog\/2017\/01\/25\/deploy-a-darkbot-or-a-simple-generic-service-with-saltstack-part-1\/\">Part 1 \u2014 Service accounts, sudo capabilities<\/a><\/em><\/p>\n<p>In <a href=\"https:\/\/www.dereckson.be\/blog\/2017\/01\/25\/deploy-a-darkbot-or-a-simple-generic-service-with-saltstack-part-1\/\">the first part<\/a>, we&#8217;ve seen how to create an user account for the service, a group to put users with access to the service into, and sudo capabilities to allow full control on the service account, some control as root to interact with systemd or another service manager.<\/p>\n<h2>Deploy an application through package<\/h2>\n<p>If your application is packaged or you package it, something we recommend hearthily, you can simply the <a href=\"https:\/\/docs.saltstack.com\/en\/latest\/ref\/states\/all\/salt.states.pkg.html#salt.states.pkg.installed\">pkg.installed<\/a> state.<\/p>\n<p>For example if you wish to deploy with this state configuration:<\/p>\n<pre class=\"lang:yaml decode:true\" title=\"Install packages\">odderon_packages:\r\n  pkg:\r\n    - installed\r\n    - pkgs:\r\n      - darkbot\r\n      - darkbot-backup-manager\r\n<\/pre>\n<p>If you only have one application you installed, you can omit pkgs, it will then take the state name:<\/p>\n<pre class=\"lang:yaml decode:true\">darkbot:\r\n  pkg.installed\r\n<\/pre>\n<p>If you want to force the last version to be reinstalled when you run again the state, you can instead use <a href=\"https:\/\/docs.saltstack.com\/en\/latest\/ref\/states\/all\/salt.states.pkg.html#salt.states.pkg.latest\">pkg.latest<\/a>:<\/p>\n<pre class=\"lang:yaml decode:true\">darkbot:\r\n  pkg.latest\r\n<\/pre>\n<p>Salt can then take care of your upgrade process.<\/p>\n<h2>Deploy an application fetching and building it<\/h2>\n<p>In this sample, we&#8217;ll fetch the source code from the last version of the <code>production<\/code> branch of a Git repository cloned at <code>\/usr\/local\/src\/darkbot<\/code>, and we&#8217;ll install the software to <code>\/opt\/odderon<\/code>. To use <code>\/opt<\/code> will allow us to perform an installation process running as a non privileged user.<\/p>\n<h3>Fetch the source code<\/h3>\n<p>Sometimes, you want a workflow to fetch, build, install, to have a better control on the compilation. Docker image creators tend to like to automate build processes.<\/p>\n<p>For that, you need two things:<\/p>\n<ol>\n<li>A directory where to clone the software<\/li>\n<li>To actually clone the repository<\/li>\n<\/ol>\n<p>A same Salt state can call several functions, here one from <strong>file<\/strong> to create a directory (<strong>file.directory<\/strong>) and one from <strong>git<\/strong> to clone (<strong>git.latest<\/strong>):<\/p>\n<pre class=\"lang:yaml decode:true\">darkbot_repo:\r\n  file.directory:\r\n    - name: \/usr\/local\/src\/darkbot\r\n    - user: odderon\r\n    - group: nasqueron-irc\r\n    - dir_mode: 755\r\n  git.latest:\r\n    - name: https:\/\/devcentral.nasqueron.org\/source\/darkbot.git\r\n    - branch: production\r\n    - target: \/usr\/local\/src\/darkbot\r\n    - user: odderon\r\n<\/pre>\n<p>We&#8217;ve reused the user and group created in previous part.<\/p>\n<p>To clone the repository, we recommend to do the clone from a source you can trust (e.g. PHP provides GPG signed packages) or better, a source you control.<\/p>\n<p>Note it&#8217;s currently not possible to call two functions from the same module, e.g. two git or two file wouldn&#8217;t work.<\/p>\n<p>If you automate the upgrade process (something best to do only if your CI\/CD infrastructure tests the Salt deployment), provide your deployers a quick way to stop the update process.<\/p>\n<p>For example, you can provide a lock file in the application directory (here \/opt\/odderon):<\/p>\n<pre class=\"lang:yaml decode:true\">\u2026:\r\n  \u2026\r\n  git.latest:\r\n    - name: https:\/\/devcentral.nasqueron.org\/source\/darkbot.git\r\n    - branch: production\r\n    - target: \/usr\/local\/src\/darkbot\r\n    - user: odderon\r\n    - unless: test -f \/opt\/odderon\/LOCKED\r\n<\/pre>\n<p>If the file exists, state will be skipped, so it&#8217;s as simple as <code>touch \/opt\/odderon\/LOCKED<\/code> to pause deployment, <code>rm \/opt\/odderon\/LOCKED<\/code> to resume it. SaltStack has a <a href=\"https:\/\/docs.saltstack.com\/en\/latest\/ref\/states\/requisites.html\">good documentation about requisites<\/a>, requisites not always intuitive.<\/p>\n<h3>Compile<\/h3>\n<p>Let&#8217;s start with a simple case where you only have one command to write to configure, compile, install, for example here: <code>.\/build.sh --with-sleep=0 --with-add=0 --with-del=0 --with-random=0 --destdir=\/opt\/odderon<\/code>.<\/p>\n<p>For that, you only need <a href=\"https:\/\/docs.saltstack.com\/en\/latest\/ref\/states\/all\/salt.states.cmd.html#salt.states.cmd.run\">cmd.run<\/a>:<\/p>\n<pre class=\"lang:yaml decode:true\">darkbot_build:\r\n  cmd.run:\r\n    - name: .\/build.sh\r\n    - args: \"--with-sleep=0 --with-add=0 --with-del=0 --with-random=0 --destdir=\/opt\/odderon\"\r\n    - cwd: \/usr\/local\/src\/darkbot\r\n    - runas: odderon\r\n<\/pre>\n<p>The <code>cwd<\/code> parameter allows to change the working directory (cd \/usr&#8230; ; .\/build.sh&#8230;) and <code>runas<\/code> to run the command as another user than root.<\/p>\n<p>If you don&#8217;t have such script available, <a href=\"https:\/\/github.com\/nasqueron\/operations\/commit\/1cd1e43522cce26c577d3da26f1d0a5dea4fa489#diff-16ee6aaa207e72a82bd071ed3774c535\">just create it<\/a>.<\/p>\n<p>Salt allows to deploy a custom script before to run it in one step with the <a href=\"https:\/\/docs.saltstack.com\/en\/latest\/ref\/states\/all\/salt.states.cmd.html#salt.states.cmd.script\">cmd.script<\/a> command:<\/p>\n<pre class=\"lang:yaml decode:true\">darkbot_build:\r\n  cmd.script:\r\n    - source: salt:\/\/roles\/shellserver\/odderon\/files\/build.sh\r\n    - args: \"--with-sleep=0 --with-add=0 --with-del=0 --with-random=0\"\r\n    - cwd: \/usr\/local\/src\/darkbot\r\n    - runas: odderon\r\n<\/pre>\n<p>The <code>roles\/shellserver\/odderon\/files\/build.sh<\/code> file will be copied on the server, and run, and yes you can also send arguments to the script like with cmd.run.<\/p>\n<p>This process isn&#8217;t useful if Git failed, so we can require another state succeeded:<\/p>\n<pre class=\"lang:yaml decode:true\">darkbot_build:\r\n  cmd.script:\r\n    - source: salt:\/\/roles\/shellserver\/odderon\/files\/build.sh\r\n    - args: \"--with-sleep=0 --with-add=0 --with-del=0 --with-random=0\"\r\n    - cwd: \/usr\/local\/src\/darkbot\r\n    - runas: odderon\r\n    - require:\r\n        - git: darkbot_repo\r\n<\/pre>\n<p>The same process can be used to provide the service configuration. For example, to copy a hierarchy of files from your Salt root directory to the server:<\/p>\n<pre class=\"lang:yaml decode:true\">darkbot_configuration:\r\n  file.recurse:\r\n    - name: \/opt\/odderon\/dat\r\n    - source: salt:\/\/roles\/shellserver\/odderon\/files\/dat\r\n    - dir_mode: 770\r\n    - file_mode: 640\r\n<\/pre>\n<p>The 770 directory mode will allow the service and the deployers to access it, files by default read only for deployers to encourage to use Salt to edit them.<\/p>\n<p>By the way, you probably also want a warning like this:<\/p>\n<pre class=\"lang:default highlight:0 decode:true \">#   &lt;auto-generated&gt;\r\n#       This file is managed by our rOPS repository\r\n#       and deployed through SaltStack.\r\n#\r\n#       Changes to this file may cause incorrect behavior\r\n#       and will be lost if the state is redeployed.\r\n# \r\n#       Source directory: roles\/shellserver\/odderon\/files\/dat\r\n#   &lt;\/auto-generated&gt;<\/pre>\n<p>It&#8217;s then clear when you&#8217;re on the production server a file opened is managed.<\/p>\n<h3>Upgrade the code<\/h3>\n<p>So, what can you do with that?<\/p>\n<p>First, it can provision your service to a new server, but you can also use it for updat. For example, if you&#8217;ve your states in roles\/shellserver\/odderon\/init.sls, you can upgrade with:<\/p>\n<p><code>salt eglide state.apply roles\/shellserver\/odderon<\/code><\/p>\n<p>It will then upgrade your package or fetch the latest Git commit and recompile your code.<\/p>\n<p>If you compile manually, note the build script doesn&#8217;t especially need a clean step. For example, if you use a Makefile, make will detect your file has been modified (looking the timestamp) and so should be upgraded.<\/p>\n<h2>Create a service<\/h2>\n<p><em>Thanks to <a href=\"https:\/\/agora.nasqueron.org\/User:Sandlayth\">Sandlayth<\/a> to have prepared the Salt configuration for this part.<\/em><\/p>\n<p>If your application doesn&#8217;t provide a service, it&#8217;s valuable to create one.<\/p>\n<p>Two steps are needed: deploy the service file, ensure the service runs. For systemd, a third step is needed to force reload configuration.<\/p>\n<p>The first part is service manager dependant, the second part is service manager agnostic.<\/p>\n<pre class=\"lang:yaml decode:true\">odderon_service:\r\n  file.managed:\r\n    - name: \/usr\/local\/etc\/rc.d\/odderon\r\n    - source: salt:\/\/roles\/shellserver\/odderon\/files\/odderon.rc\r\n    - mode: 0755\r\n<\/pre>\n<p>We told you there is an extra step for systemd. This one is tricky: as this is a command to refresh an application knowledge, that&#8217;s not something we can declare and check, so it&#8217;s not available as a state. SaltStack also allows to run commands, and provide modules for that. For example, you can do <code>salt eglide service.force_reload odderon<\/code> (see <a href=\"https:\/\/docs.saltstack.com\/en\/latest\/ref\/modules\/all\/salt.modules.systemd.html#salt.modules.systemd.force_reload\">systemd module doc<\/a>, right menu offers the same for other systems).<\/p>\n<pre class=\"lang:yaml decode:true\">odderon_service:\r\n  file.managed:\r\n    - name: \/etc\/systemd\/system\/odderon.service\r\n    - source: salt:\/\/roles\/shellserver\/odderon\/files\/odderon.service\r\n    - mode: 0644\r\n  module.run:\r\n    - name: service.force_reload\r\n    - m_name: odderon\r\n    - onchanges:\r\n       - file: odderon_unit\r\n<\/pre>\n<p>Parameters for the module you run are directly put after the name. But here <code>service.force_reload<\/code> has also a <code>name<\/code> parameter. To disambiguate, you prepend <code>m_<\/code> and get <code>m_name<\/code>.<\/p>\n<p>There is currently in Salt a work of progress to abstract more the services.<\/p>\n<p>Finally, whatever the service manager, you want to <a href=\"https:\/\/docs.saltstack.com\/en\/latest\/ref\/states\/all\/salt.states.service.html\">ensure the service runs<\/a>:<\/p>\n<pre class=\"lang:yaml decode:true\">odderon_running:\r\n  service.running:\r\n    - name: odderon\r\n    - enable: true\r\n    - watch:\r\n      - module: odderon_service\r\n<\/pre>\n<p>The <code>enable<\/code> parameter is useful if you use a systemd-like service, as the service must be explicitly enabled to be automatically launched at server start time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is the part 2 of our service deployment walkthrough. See also: Part 1 \u2014 Service accounts, sudo capabilities In the first part, we&#8217;ve seen how to create an user account for the service, a group to put users with access to the service into, and sudo capabilities to allow full control on the service [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[50],"tags":[294,296,221,298],"class_list":["post-617","post","type-post","status-publish","format-standard","hentry","category-sysadmin","tag-darkbot","tag-eglide","tag-saltstack","tag-systemd"],"_links":{"self":[{"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/posts\/617","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/comments?post=617"}],"version-history":[{"count":17,"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/posts\/617\/revisions"}],"predecessor-version":[{"id":658,"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/posts\/617\/revisions\/658"}],"wp:attachment":[{"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/media?parent=617"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/categories?post=617"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.dereckson.be\/blog\/wp-json\/wp\/v2\/tags?post=617"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}