2014年12月26日金曜日

[Python] Fabric を pyenv + virtualenv 環境で使ったら楽しかったよ!


メリークリスマス! なのまる です。

NTPの脆弱性(英語)の対応しよーと思って、手作業はツラすぎるのである程度の自動化をしようと思いました。


もくじ



Fabricが楽しかった理由

そもそもCapistrano (Ruby) を使っていたんですがなんとなく、一度だけ勉強会で使わせてもらったFabricの勉強をしてみたいと思って使いました!

Fabricを使ってみて、楽しいなぁ~って思った理由を自分なりにまとめてみたので書いておきます。
  • Capistranoでは、そのままでは煩わしい部分が簡単に解決できた!(認証関連)
  • 出来上がった、コードが短くて済んだ!
  • 出来上がった、コードがわかりやすい!
  • 上記もあって、使っていて楽しい!w


Fabric を使ってみる!


前提条件

  • OSシステム環境のPythonに影響を及ぼさない
  • pyenv + virtualenv
  • Linux (CentOS 6)
  • fabric
システム環境に初めて入れるので、使い捨てができるようにしておきたかった。
そのため「pyenv + virtualenv」で実装した。

「pyenv + virtualenv」 インストール・設定

ここの部分長いので、わかる人は飛ばしていいと思います!

pyenv インストール

pyenvはinstallerがあって、インストールが簡単です!
以下を実行するだけ!
curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash

インストール状況
  [snickerjp@localhost ~]$ curl -L https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash
    % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                   Dload  Upload   Total   Spent    Left  Speed
  101  2126  101  2126    0     0   6321      0 --:--:-- --:--:-- --:--:--  6858
  Initialized empty Git repository in /home/snickerjp/.pyenv/.git/
  remote: Counting objects: 8056, done.
  remote: Total 8056 (delta 0), reused 0 (delta 0)
  Receiving objects: 100% (8056/8056), 1.44 MiB | 374 KiB/s, done.
  Resolving deltas: 100% (5898/5898), done.
  Initialized empty Git repository in /home/snickerjp/.pyenv/plugins/pyenv-doctor/.git/
  remote: Counting objects: 26, done.
  ~中略~
  Resolving deltas: 100% (8/8), done.
  
  WARNING: seems you still have not added 'pyenv' to the load path.
  
  # Load pyenv automatically by adding
  # the following to ~/.bash_profile:
  
  export PATH="$HOME/.pyenv/bin:$PATH"
  eval "$(pyenv init -)"
  eval "$(pyenv virtualenv-init -)"

~/.bash_profileに以下を追記
export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

かっこよく編集してみたりw
cat << 'EOF' >> ~/.bash_profile
# for pyenv
export PATH="$HOME/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
EOF

localにPythonのインストール

なんとなく2.x系をインストールしました。
    # Pythonの現在のバージョンを確認
    [snickerjp@localhost ~]$ python --version
    Python 2.6.6
 
    [snickerjp@localhost ~]$ pyenv install 2.7.8
    Downloading Python-2.7.8.tgz...
    ~略~
    Installed Python-2.7.8 to /home/snickerjp/.pyenv/versions/2.7.8
 
    [snickerjp@localhost ~]$ pyenv versions
    * system (set by /home/snickerjp/.pyenv/version)
      2.7.8
 
    [snickerjp@localhost ~]$ pyenv install 2.7.9
    Downloading Python-2.7.9.tgz...
    ~略~
 
    [snickerjp@localhost ~]$ pyenv versions
    * system (set by /home/snickerjp/.pyenv/version)
      2.7.8
      2.7.9

Pythonのインストールでエラーが出たので、追加でdevelパッケージを入れました。
    WARNING: The Python readline extension was not compiled. Missing the GNU readline lib?
    WARNING: The Python bz2 extension was not compiled. Missing the bzip2 lib?
    WARNING: The Python sqlite3 extension was not compiled. Missing the SQLite3 lib?
    ERROR: The Python ssl extension was not compiled. Missing the OpenSSL lib?

yumでインストール!
    sudo yum install readline-devel bzip2-devel openssl-devel sqlite-devel


virtualenvのインストール

virtualenvのインストール~
    [snickerjp@localhost fabric-bin]$ pyenv virtualenv 2.7.9 fabric
    Collecting virtualenv
      Downloading virtualenv-12.0.3-py2.py3-none-any.whl (1.8MB)
        100% |################################| 1.8MB 6.2MB/s
    Installing collected packages: virtualenv
   

    Successfully installed virtualenv-12.0.3
    New python executable in /home/snickerjp/.pyenv/versions/fabric/bin/python2.7
    Also creating executable in /home/snickerjp/.pyenv/versions/fabric/bin/python
    Installing setuptools, pip...done.
    Ignoring indexes: https://pypi.python.org/simple/
    Requirement already satisfied (use --upgrade to upgrade): setuptools in /home/snickerjp/.pyenv/versions/fabric/lib/python2.7/site-packages
    Requirement already satisfied (use --upgrade to upgrade): pip in /home/snickerjp/.pyenv/versions/fabric/lib/python2.7/site-packages
    Cleaning up...
    [snickerjp@localhost fabric-bin]$
      
    [snickerjp@localhost fabric-bin]$ pyenv versions
    * system (set by /home/snickerjp/.pyenv/version)
      2.7.8
      2.7.9
      fabric


localのPythonを使う場所を作る

localバージョンのPythonを使う場所を作ります!
    [snickerjp@localhost ~]$ mkdir fabric-bin
    [snickerjp@localhost fabric-bin]$ cd fabric-bin/
    [snickerjp@localhost fabric-bin]$ pyenv local fabric
    pyenv-virtualenv: activate fabric
    (fabric)[snickerjp@localhost fabric-bin]$
    (fabric)[snickerjp@localhost fabric-bin]$ pyenv rehash
    (fabric)[snickerjp@localhost fabric-bin]$ python --version
    Python 2.7.9
    (fabric)[snickerjp@localhost fabric-bin]$ pip list
    pip (6.0.3)
    setuptools (8.2.1)
参考



fabric インストール

やっと、ここでfabricのインストール!
もうvirtualenvでlocal環境にインストールできる状態です!
    (fabric)[snickerjp@localhost fabric-bin]$ pip install fabric
    Collecting fabric
      Downloading Fabric-1.10.1.tar.gz (209kB)
        100% |################################| 212kB 9.3MB/s
    Collecting paramiko>=1.10 (from fabric)
      Downloading paramiko-1.15.2-py2.py3-none-any.whl (165kB)
        100% |################################| 167kB 10.8MB/s
    Collecting ecdsa>=0.11 (from paramiko>=1.10->fabric)
      Downloading ecdsa-0.11.tar.gz (45kB)
        100% |################################| 49kB 10.3MB/s
    Collecting pycrypto!=2.4,>=2.1 (from paramiko>=1.10->fabric)
      Downloading pycrypto-2.6.1.tar.gz (446kB)
        100% |################################| 446kB 8.1MB/s
    Installing collected packages: pycrypto, ecdsa, paramiko, fabric
      Running setup.py install for pycrypto
        checking for gcc... gcc
        checking whether the C compiler works... yes
        checking for C compiler default output file name... a.out
    ~略~
        gcc -pthread -shared -L/home/snickerjp/.pyenv/versions/2.7.9/lib build/temp.linux-x86_64-2.7/src/_counter.o -o build/lib.linux-x86_64-2.    7/Crypto/Util/_counter.so
      Running setup.py install for ecdsa
   

      Running setup.py install for fabric
        Installing fab script to /home/snickerjp/.pyenv/versions/fabric/bin
    Successfully installed ecdsa-0.11 fabric-1.10.1 paramiko-1.15.2 pycrypto-2.6.1

参考



ntp をアップデートしてみる

いよいよ本題ですね!w

ntpup.py
こちらを参考に以下のコードを書いてみました!
(コメント部分は消してください)
    #coding:utf-8
    from fabric.api import env, run, sudo
    from fabric.contrib.console import confirm
   

    env.use_ssh_config = True
    env.key_filename = ['~/.ssh/id_rsa']
   

    def yum_update_ntp():
        sudo('cat /etc/∗-release',user='root', pty=True) # Linuxのディストリビューションの確認
        sudo('yum -q -y update ntp',user='root', pty=True) # sudo で yum update
   

    def run_su(command, user="root"): # sudo が無いサーバーなどの対応
        return run('su %s -c "%s"' % (user, command), pty=True)
   

    def su_update_ntp():
        run_su('cat /etc/∗-release',user='root') 
        run_su('yum -y update ntp',user='root') # sudo がないサーバーで yum update

実行例
    [192.168.82.xxx] Executing task 'yum_update_ntp'
    [192.168.82.xxx] sudo: cat /etc/*-release
    [192.168.82.xxx] Login password for 'snickerjp':
    [192.168.82.xxx] out:
    [192.168.82.xxx] out: We trust you have received the usual lecture from the local System
    [192.168.82.xxx] out: Administrator. It usually boils down to these three things:
    [192.168.82.xxx] out:
    [192.168.82.xxx] out:     #1) Respect the privacy of others.
    [192.168.82.xxx] out:     #2) Think before you type.
    [192.168.82.xxx] out:     #3) With great power comes great responsibility.
    [192.168.82.xxx] out:
    [192.168.82.xxx] out: sudo password:
    [192.168.82.xxx] out: Sorry, try again.
    [192.168.82.xxx] out: sudo password:  ← 公開鍵とPAMで違うパスワードの時は聞いてきてくれる!
    [192.168.82.xxx] out: NAME="Amazon Linux AMI"
    [192.168.82.xxx] out: VERSION="2014.09"
    [192.168.82.xxx] out: ID="amzn"
    [192.168.82.xxx] out: ID_LIKE="rhel fedora"
    [192.168.82.xxx] out: VERSION_ID="2014.09"
    [192.168.82.xxx] out: PRETTY_NAME="Amazon Linux AMI 2014.09"
    [192.168.82.xxx] out: ANSI_COLOR="0;33"
    [192.168.82.xxx] out: CPE_NAME="cpe:/o:amazon:linux:2014.09:ga"
    [192.168.82.xxx] out: HOME_URL="http://aws.amazon.com/amazon-linux-ami/"
    [192.168.82.xxx] out: Amazon Linux AMI release 2014.09
    [192.168.82.xxx] out:
    [192.168.82.xxx] sudo: yum --releasever=2014.09 --disablerepo=epel update ntp
    [192.168.82.xxx] out: sudo password:
    [192.168.82.xxx] out: Loaded plugins: priorities, update-motd, upgrade-helper
    [192.168.82.xxx] out:
    [192.168.82.xxx] out: amzn-main/2014.09                                                        | 2.1 kB     00:00
    [192.168.82.xxx] out:
    [192.168.82.xxx] out: amzn-updates/2014.09                                                     | 2.3 kB     00:00
    [192.168.82.xxx] out: 181 packages excluded due to repository priority protections
    [192.168.82.xxx] out: No packages marked for update
    [192.168.82.xxx] out:
SSH関連について
  • 公開鍵とPAMで違うパスワードでも対話で入力できる!
  • rootパスワードも対話で入力できる!
  • サーバーごとにパスワード違っても対話で入力できる!
  • 前の処理で入れたパスワードは記憶しておいてくれる!
Capistranoでやろうとすると、このあたりが比較的にめんどくさい!
fabricだと、楽だった!のでした!
参考



まとめ


  • Capistranoで不便と思っていた部分が解決できる!(認証関連)
  • コードが短くて、さらに読みやすい!
  • pyenv + virtualenv + fabric でポータビリティもあるんじゃないかと!w


参考サイト


Zenback