How to create new system service by ansible-playbook - ansible

I have created a script to start/stop my application. Now I want to add it as a centos system service. First I created a task to create a link from my script to /etc/init.d/service_name as below.
---
- name: create startup link
file: src={{ cooltoo_service_script }} dest={{ cooltoo_service_init }} state=link
After create the service, I want to add it to system service. The command used to do that is "chkconfig --add service_name". I wonder whether there is a ansible module to do that instead of hardcoded the command in ansible-playbook file. I have looked at this page http://docs.ansible.com/ansible/service_module.html but it only shows how to manage a service not create a new one.

The below code snippet will create Service in CentOS 7.
Code
Tasks
/tasks/main.yml
- name: TeamCity | Create environment file
template: src=teamcity.env.j2 dest=/etc/sysconfig/teamcity
- name: TeamCity | Create Unit file
template: src=teamcity.service.j2 dest=/lib/systemd/system/teamcity.service mode=644
notify:
- reload systemctl
- name: TeamCity | Start teamcity
service: name=teamcity.service state=started enabled=yes
Templates
/templates/teamcity.service.j2
[Unit]
Description=JetBrains TeamCity
Requires=network.target
After=syslog.target network.target
[Service]
Type=forking
EnvironmentFile=/etc/sysconfig/teamcity
ExecStart={{teamcity.installation_path}}/bin/teamcity-server.sh start
ExecStop={{teamcity.installation_path}}/bin/teamcity-server.sh stop
User=teamcity
PIDFile={{teamcity.installation_path}}/teamcity.pid
Environment="TEAMCITY_PID_FILE_PATH={{teamcity.installation_path}}/teamcity.pid"
[Install]
WantedBy=multi-user.target
\templates\teamcity.env.j2
TEAMCITY_DATA_PATH="{{ teamcity.data_path }}"
Handlers
\handlers\main.yml
- name: reload systemctl
command: systemctl daemon-reload
Reference :
Ansible playbook structure: http://docs.ansible.com/ansible/playbooks_intro.html
SystemCtl: https://www.digitalocean.com/community/tutorials/how-to-use-systemctl-to-manage-systemd-services-and-units

The 'service' module supports an 'enabled' argument.
Here's an example part of a playbook, which I will freely admit does look like a newbie attempt. This assumes RHEL/CentOS 6.x, which uses SysV, not systemd.
- name: install rhel sysv supervisord init script
copy: src=etc/rc.d/init.d/supervisord dest=/etc/rc.d/init.d/supervisord owner=root group=root mode=0755
- name: install rhel sysv supervisord sysconfig
copy: src=etc/sysconfig/supervisord dest=/etc/sysconfig/supervisord owner=root group=root mode=0640
- name: enable sysv supervisord service
service: name=supervisord enabled=yes
- name: start supervisord
service: name=supervisord state=started
IMPORTANT A lot of custom init scripts WILL FAIL with Ansible and SysV init; the reason being that the 'status' option (service supervisord status) needs to a return an LSB-compliant return code. Otherwise, Ansible will not know if a service is up or down, and idempotency will fail (restart will still work because that is unconditional)
Here's part of a script, which I've just rewritten to make use of the 'status' function within /etc/init.d/functions (you'll notice this same pattern in other Red Hat provided init-scripts in /etc/init.d/
status)
/usr/bin/supervisorctl $OPTIONS status
status -p $PIDFILE supervisord
# The 'status' option should return one of the LSB-defined return-codes,
# in particular, return-code 3 should mean that the service is not
# currently running. This is particularly important for Ansible's 'service'
# module, as without this behaviour it won't know if a service is up or down.
RETVAL=$?
;;
Reference: http://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
If the status action is requested, the init script will return the
following exit status codes.
0 program is running or service is OK 1 program is dead and /var/run
pid file exists 2 program is dead and /var/lock lock file exists
3 program is not running 4 program or service status is unknown
5-99 reserved for future LSB use 100-149 reserved for distribution use
150-199 reserved for application use 200-254 reserved

Indeed the service module only manages already registered services as you have figured out. To my knowledge there is no module to register a service.
Are you aware this step can be skipped with some modifications to your init.d script? If the script follows those rules you can just use the service module to enable/start the service.

For RedHat/CentOS 7 (using systemd/systemctl), the equivalent of chkconfig --add ${SERVICE_NAME} is systemctl daemon-reload [via fedoraproject.org].
Then, using the systemd module of Ansible 2.2 or greater, you may start a service with a preceding systemctl daemon-reload like so [via docs.ansible.com]:
# Example action to restart service cron on centos, in all cases, also issue daemon-reload to pick up config changes
- systemd:
state: restarted
daemon_reload: yes
name: crond
Based on my experience, the daemon_reload parameter can also be used within the generic service module as well, although it isn't documented, and might fail on non-systemd systems:
- service:
state: restarted
daemon_reload: yes
name: crond

Related

Run Ansible playbook from Cloud-Init

I have been learning Cloud-Init for several days to do an automatic deployment. To achieve this, and apply certain configurations, I am using Ansible playbooks. The problem that I have found is that I am not able to make the playbook run directly on the operating system that is being installed.
I leave you the user-data file that I am using.
#cloud-config
autoinstall:
version: 1
identity:
hostname: hostname
password: "$6$cOciYeIErEet80Rv$YX8qt6vizXgcUkgIPSKD1qNZNxe77tSWOY3k/0.i8D8EpApaGNuyucxJvONmZiRj4rVM3L6EE4sLKcnzYVcMj/ "
username: ubuntu
storage:
layout:
name: direct
locale: es_ES
timezone: "Europe/Madrid"
keyboard:
layout: es
packages:
- sshpass
- ansible
- git
late-commands:
- git clone https://github.com/MarcOrfilaCarreras/dotfiles /target/root/dotfiles
- ansible-playbook -i inventory-test /root/dotfiles/ansible/playbooks/docker.yml -u ubuntu -e "ansible_password=ubuntu" -e "ansible_become_pass=ubuntu"
PS: I am using Ubuntu Server 22.04, the Ansible command is temporary and only for testing and I know that I have to change the identity fields.
If you want to configure localhost, it's better to use local transport (which is -c local in command line).
Basically, change ansible call to:
ansible-playbook -i inventory-test /root/dotfiles/ansible/playbooks/docker.yml -c local
This will bypass all SSH things and run locally.

Ansible having problem authenticating with Google Cloud Platform

We are using Ansible to deploy an image to Google Kubernetes Cluster (GKE).
We have setup Ubuntu 20.04 and Python 3.8.5.
playbook.main.yml:
---
- hosts: localhost
vars:
k8s_file_path: /home/pesinn/Documents/...
become: yes
become_method: sudo
roles:
- k8s
main.yml:
- name: First Deployment
k8s:
kubeconfig: /home/pesinn/.kube/config
src: "{{k8s_file_path}}/deployment.yml"
When trying to deploy the image defined in deployment.yml file, by running the playbook, we get this error:
kubernetes.config.config_exception.ConfigException: cmd-path: process returned 1
Cmd: /home/pesinn/y/google-cloud-sdk/bin/gcloud config config-helper --format=json
Stderr: WARNING: Could not open the configuration file: [/root/.config/gcloud/configurations/config_default].
ERROR: (gcloud.config.config-helper) You do not currently have an active account selected.
Please run:
$ gcloud auth login
What we've already done
Initialized the cloud: gcloud init
Logged in and chosen a project gcloud auth login
Run export GOOGLE_APPLICATION_CREDENTIALS="path_to_service_account_key.json"
Run gcloud container clusters get-credentials {gke_project} --region {region}
Run the playbook sudo ansible-playbook playbook.main.yml -vvv
Run gcloud config config-helper --format=json on the local machine without any problems
What is very strange here is that we're logged in for sure. We can access the GKE cluster through kubectl command on the local machine. However, Ansible complains about us not being logged in. Also, in the error logs, we see that it is trying to open /root/.config/gcloud/configurations/config_default. Our default config file is, on the other hand, located in the home folder.
This error occurs randomly. Sometimes Ansible can detect our login and deploys the image, but sometimes it gives us this error. Both scenarios can happens without any code changes being made.
For some reason, ansible does not use GCP's default environment variables for authentication.
You can set
GCP_AUTH_KIND
GCP_SERVICE_ACCOUNT_EMAIL
GCP_SERVICE_ACCOUNT_FILE
GCP_SCOPES
GCP_SERVICE_ACCOUNT_FILE is the equivalent of GOOGLE_APPLICATION_CREDENTIALS
Reference: https://docs.ansible.com/ansible/latest/scenario_guides/guide_gce.html#providing-credentials-as-environment-variables

Ansible module: should I configure using systemd or service?

I want to configure the following linux command using Ansible:
sudo systemctl enable XXX.service
should I use:
systemd:
name: XXX.service
enabled: yes
or
service:
name: XXX.service
enabled: yes
And what are the difference among using systemctl, systemd and service?
Referring to ansible systemd module and ansible service module I think you should use the systemd module.
It's designed to control systemd. systemd is designed to replace service, so you can see xxx.service in systemctl command, but it's systemd system, it's different than service.
use "service", it will systemd's systemctl command anyway.

How can I install a systemd service using Ansible?

I want to install a systemd service from a Jinja2 template. How do I do this?
Do I have to use copy module to copy the file to /lib/systemd/system and then use systemd module to enable it?
Is there a better way?
I use the template module to install the .service file into the /etc/systemd/system. According to this digital ocean blog post /lib/systemd/system should be reserved for packages bundled with the OS itself, and third party services should be defined in /etc/systemd/system.
With ansible's systemd module I'd start the service with daemon_reload=yes.
Prior to Ansible 2.2: I do a systemctl daemon-reload afterward (can use an ansible handler for this if appropriate) to prod systemd to pick up the new file.
- name: install myservice systemd unit file
template: src=myservice.j2 dest=/etc/systemd/system/myservice.service
- name: start myservice
systemd: state=started name=myservice daemon_reload=yes
# For ansilble < 2.2 only
#- name: reload systemd unit configuration
# command: systemctl daemon-reload

Ansible: wait_for in with_items

I have a set of web server processes that I wish to restart one at a time. I want to wait for process N to be ready to service HTTP requests before restarting process N+1
The following works:
- name: restart server 9990
supervisorctl: name='server_9990' state=restarted
- wait_for: port=9990 delay=1
- name: restart server 9991
supervisorctl: name='server_9991' state=restarted
- wait_for: port=9991 delay=1
etc.
But I'd really like to do this in a loop. It seems that Ansible doesn't allow multiple tasks inside a loop (in this case, I need two tasks: supervisorctl and wait_for)
Am I missing a way to do this or is replicating these tasks for each instance of the server really the way to go?
I believe that's not possible with Ansible default functionality. I think your best bet would be to create your own module. I had seen that modules can call other modules, so you might be able to have a slim module which simply calls the supervisorctl module and then waits for the port to be ready. This module then could be called with with_items.
Another idea is to not use supervisorctl in the first place. You could run a shell or script task which does then manually call supervisorctl and waits for the port to open.

Resources