So, Iâll split my answer into multiple posts as itâs going to be a long one. Also not guaranteed to address the whole point in one sitting, so I might get back to you for the rest a bit later.
My artifacts are in the jenkins workspace in the filesystem.
Ok, so we either deploy them from the same job or get them out of the workspace (which would by default be deleted after IIRC), then run another pipeline to deploy. Iâm more used to have build pipelines separated from deploy ones, though thereâs nothing wrong here.
I am using the Jenkins assistant but it gives me several options, I understand that one would be to execute the shell command line with the ansible program and the modules that you have mentioned and another would be to execute an ansible playbook file, which is better?
Ok let me explain. First, Ansible has two execution âmodesâ: adhoc commands (the ansible
binary) and playbooks (ansible-playbook
binary). Adhoc commands lets you run a single task at a time on targets, with all task args on command line, while playbooks are yaml files including tasks written in a declarative fashion and executed procedurally (well, tasks are arranged in one or multiple âplayâ blocks, though thatâs of no importance to understand the concepts).
In some cases, adhoc commands are fine, but youâll usually want to use playbooks instead for multiple reasons I wonât expand on as this post will already be long enough :p.
Now whether you want to use adhoc commands or playbooks, Jenkins lets you either use a plugin, like this one, or run your commands from a script block. I personally prefer not to be bothered with another plugin, especially since all logic is in your playbook, but linked plugin should work just fine if thatâs more your thing.
Here is an example step (declarative pipeline) from the project I mentioned earlier; you can see how I used ansible-playbook
in an inline script block:
steps {
withCredentials ([
string(credentialsId: 'ansible_become_pass_zzzic-jenkins', variable: 'ANSIBLE_BECOME_PASS_ZZZICJENKINS'),
file(credentialsId: 'ansible_vault_pwd_ec', variable: 'ANSIBLE_VAULT_PWD_EC')]
) {
sshagent(credentials: ['gitlab-ec_zzzIC-Jenkins', 'psgservers_zzzic-jenkins']) {
sh label:'Deploy PSG stack', script:'''
unset GIT_SSH # Puisque l'envvar pointe par dĂŠfaut sur le path d'un binaire Windows -_-; liĂŠ Ă la conf du plugin
cd ${_sourceFolder}
# VĂŠrification des hostkeys
for host in <redacted> $(ansible -i ./inventories/ all --list-hosts | tail -n+2 | sed -e 's/\\s\\+//g'); do
$(which ssh-keygen) -t rsa -F "${host}" || $(which ssh-keyscan) -t rsa "${host}" >> ~/.ssh/known_hosts
done
ansible --version
# Installation des requirements et de leurs dĂŠpendances (rĂ´les / collections)
if '''+params.FORCE_INSTALL_REQS+'''; then
GALAXY_CMD_ARGS="${GALAXY_CMD_ARGS} --force"
fi
ansible-galaxy install -r requirements.yml ${GALAXY_CMD_ARGS:-}
# Set des params optionnels sur la commande ansible
if [ -n "'''+params.FORCE_RESTART_SVCS.replace('\n', ',')+'''" ]; then
ANSIBLE_CMD_ARGS="${ANSIBLE_CMD_ARGS} --extra-vars gbt_psg_force_restart_containers=$(echo -n '''+params.FORCE_RESTART_SVCS.replace('\n', ',')+''')"
fi
if [ -n "'''+params.FORCE_RECREATE_SVCS.replace('\n', ',')+'''" ]; then
ANSIBLE_CMD_ARGS="${ANSIBLE_CMD_ARGS} --extra-vars gbt_psg_force_recreate_containers=$(echo -n '''+params.FORCE_RECREATE_SVCS.replace('\n', ',')+''')"
fi
if '''+params.CONTAINERS_TASK_ONLY+'''; then
ANSIBLE_CMD_ARGS="${ANSIBLE_CMD_ARGS} --tags t_gbt_psg_create_containers"
fi
if '''+params.DRYRUN_MODE+'''; then
ANSIBLE_CMD_ARGS="${ANSIBLE_CMD_ARGS} --check"
fi
if '''+params.SKIP_EXEC_DEPS+'''; then
ANSIBLE_CMD_ARGS="${ANSIBLE_CMD_ARGS} --extra-vars skip_deps=true"
fi
ansible-playbook \
--inventory-file ./inventories/hosts_psg_'''+params.TARGET_ENV+''' \
--extra-vars "ansible_become_pass=${ANSIBLE_BECOME_PASS_ZZZICJENKINS}" \
--vault-id "ec@${ANSIBLE_VAULT_PWD_EC}" \
--extra-vars "gbt_psg_backend_image_version='''+params.TAG_BACKEND+'''" \
--extra-vars "gbt_psg_frontend_image_version='''+params.TAG_FRONTEND+'''" \
${ANSIBLE_CMD_ARGS:-} \
main.yml
'''
}
}
}
In the tutorial it shows me how to use the copy, synchronize and recover commands but I donât understand the use of the main.yml file, is it the name of the module to use when using a playbook? itâs right?
No :D. First articleâs section shows you how to 1) Copy war file with copy module, and 2) Deploy JBoss app with jboss module, both of them using ansible adhoc commands.
Second part is to do the same thing using a playbook instead, but writer did something needlessly complicated here; Iâll explain a bit below, but just know you can put both tasks (written in declarative form) in a simple yaml file (called whatever you want), inside a âplayâ block. This file is now a playbook that you can run with ansible-playbook
command. Here is what it would look like:
---
- hosts: jboss
remote_user: jota
tasks:
- name: copy package to remote server
ansible.builtin.copy:
src: /repository/apps/example.war
dest: /tmp
- name: deploy application
community.general.jboss:
deploy_path: /middleware/jboss-eap-7.1/node1/deployments
src: /tmp/example.war
deployment: example.war
state: present
Now, for a quick explanation; writer used a role, which you can see as a library, containing scattered parts of a playbook: tasks, handlers, vars, ⌠each of them stored in a specific folder, so tasks in âtasksâ folder, vars in âvarsâ folder (also âdefaultâ folder, but anyways), etcâŚ
The tasks file is called âmain.ymlâ per convention, as roles only evaluate âmain.ymlâ files under its folders. It goes the same for every folder (except âfilesâ and âtemplatesâ ones, which acts as stores).
There are no playbooks in a role, only parts of. And roles canât function on their own, they need to be called from a playbook (here â deploy-example.ymlâ, which is not in the role, but calling (aka reusing) their role called âcommonâ).
Anyways, roles are super useful, though it only bring extra complexity in this case, so I suggest you donât bother with it. I hope my explanations are clear enough.
Stay tuned for the next post !