Ansible fails with /bin/sh: 1: /usr/bin/python: not found

后端 未结 19 1061
执念已碎
执念已碎 2020-11-29 15:15

I\'m running into an error I\'ve never seen before. Here is the command and the error:

$ ansible-playbook create_api.yml

PLAY [straw] **********************         


        
19条回答
  •  旧巷少年郎
    2020-11-29 15:54

    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:

    1. Needs to go at the top of every playbook (as opposed to option 1)
    2. Assumes apt is on the system and ignores errors (as opposed to option 3)
    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

    • Fix the /usr/bin/python: not found error in Ansible
    • Python 3 Support on Ansible Docs
    • Ansible raw Module on Ansible Docs
    • Error handling in playbooks on Ansible Docs

提交回复
热议问题