I personally found 3 possible solutions to this problem that work well in different situations:
Option 1 - Set ansible_python_interpreter: /usr/bin/python3
for hosts that have python3
installed by default
I think this is the superior method for solving the problem if you have a way to group your hosts by whether or not they have python3
installed by default. As far as I'm aware, python3
is available on all Ubuntu releases 16.04 and higher.
- If all your hosts definitely have
python3
, you could add the variable to your group_vars/all.yml
(or equivalent):
# group_vars/all.yml
ansible_python_interpreter: /usr/bin/python3
- If some of your hosts don't have
python3
and you have a way to tag them when using dynamic inventory (e.g. AWS tagging for ec2.py
), you could apply the variable to certain hosts like this:
# group_vars/tag_OS_ubuntu1804.yml
ansible_python_interpreter: /usr/bin/python3
- If you use static inventory and are able to group hosts based on whether they have
python3
, you could do something like this:
# inventory/hosts
[python2_hosts]
centos7_server
[python3_hosts]
u1804_server
[python3_hosts:vars]
ansible_python_interpreter=/usr/bin/python3
I like this option the most because it requires no changes on the remote host and only minor changes to variables, as opposed to options 2 and 3, which require additions to every playbook.
Option 2 - Install Python 2 using raw
This option requires putting a play at the top of every playbook with gather_facts: false
that uses raw
to install python
:
- name: install python2 on all instances
hosts: "*"
gather_facts: false
tasks:
- name: run apt-get update and install python
raw: "{{ item }}"
loop:
- sudo apt-get update
- sudo apt-get -y install python
become: true
ignore_errors: true
ignore_errors: true
is required if you plan to run the play on hosts that don't have apt-get
installed (e.g. anything RHEL-based), otherwise they will error out in the first play.
This solution works, but is the lowest on my list for a few reasons:
- Needs to go at the top of every playbook (as opposed to option 1)
- Assumes
apt
is on the system and ignores errors (as opposed to option 3)
apt-get
commands are slow (as opposed to option 3)
Option 3 - Symlink /usr/bin/python -> /usr/bin/python3
using raw
I haven't seen this solution proposed by anyone else. It's not ideal, but I think it's superior to option 2 in a lot of ways. My suggestion is to use raw
to run a shell command to symlink /usr/bin/python -> /usr/bin/python3
if python3
is on the system and python
is not:
- name: symlink /usr/bin/python -> /usr/bin/python3
hosts: "*"
gather_facts: false
tasks:
- name: symlink /usr/bin/python -> /usr/bin/python3
raw: |
if [ -f /usr/bin/python3 ] && [ ! -f /usr/bin/python ]; then
ln --symbolic /usr/bin/python3 /usr/bin/python;
fi
become: true
This solution is similar to option 2 in that we need to put it at the top of every playbook, but I think it's superior in a few ways:
- Only creates the symlink in the specific case that
python3
is present and python
is not -- it won't override Python 2 if it's already installed
- Does not assume
apt
is installed
- Can run against all hosts without any special error handling
- Is super fast compared to anything with
apt-get
Obviously if you need Python 2 installed at /usr/bin/python
, this solution is a no go and option 2 is better.
Conclusion
- I suggest using option 1 in all cases if you can.
- I suggest using option 3 if your inventory is really large/complex and you have no way to easily group hosts with
python3
, making option 1 much more difficult and error-prone.
- I only suggest option 2 over option 3 if you need Python 2 installed at
/usr/bin/python
.
Sources