システムデザイン

Python覚書集 – 第2回 ssh接続まとめ

*本記事は旧TechblogからCOLORSに統合した記事です。

エムフィールド エンジニアリングソリューション事業部のエムシバです。
今回はPython覚書集として、ssh接続で使用されるpython ライブラリを纒めて紹介したいと思います。

ssh接続用ライブラリ

  1. paramiko
  2. fabric
  3. sshtunel

まずは簡単にそれぞれどういったライブラリなのかを紹介致します。

1. paramiko
公式page : http://www.paramiko.org/

ParamikoはPython(2.7、3.4+)におけるSSHv2プロトコルの実装で、クライアントとサーバーの両方の機能を提供します。
これは低レベルの暗号処理(Cryptography)のためのPython C拡張を利用していますが、
Paramiko自体はSSHネットワークの概念を中心とした純粋なPythonインターフェースです。

説明は不要ですね。そのまんま、ssh接続を行いshell コマンドを行う為のpythonライブラリになります。

2. fabric
公式page : https://fabric-ja.readthedocs.io/ja/latest/tutorial.html

FabricはSSH経由でリモートでシェルコマンドを実行するように設計された高レベルのPython(2.7、3.4+)ライブラリです。 もっと具体的に言うと、Fabricとは:
・コマンドライン 経由で 任意の Python 関数 を実行するツールです。
・(低レベルライブラリの上に構築された)サブルーチンのライブラリで、
  SSH経由で 簡単に かつ Python風に シェルコマンドを実行します。

よくpythonでのデプロイツールとして紹介されます。
自動化のためのPython+SSH+コマンド実行ライブラリとして使えるます。
使い方としては下記の2通りに分けれると思います。
・Djangoなどと合わせて、サーバーサイドでのコマンド実行
・ SSHを利用してのリモートサーバからのコマンド実行

今回としてはssh接続として紹介致します。


3. sshtunel

github : https://github.com/pahaz/sshtunnel/


外から直接アクセス出来ないDBに 、踏み台サーバーを利用して接続する場合などに、利用する事が出来ます。
また、proxyサーバー等に遮られ、対象サーバーにssh等で接続出来ない場合など、トンネルさせる事で、対象サーバーに接続させる事が出来ます。



上記 1. ~ 3. を通して行う事
 全体を通してssh接続を行い、サーバーサイドでls コマンドを実行し、
 ls の結果を表示する方法を記載してみます。
また 3. sshtunelでは踏み台サーバー経由でmysqlの接続方法の例も記載してみます。

1.paramiko


# -*- coding: utf-8 -*-
from paramiko import SSHClient, AutoAddPolicy

HOST = 'mfield.com'
PORT = 22
USER = 'ec2-user'
PRIVATE_KEY = './ssh_test.pem'

ssh = SSHClient()
ssh.set_missing_host_key_policy(AutoAddPolicy())
#接続
ssh.connect(HOST, PORT, USER, key_filename=PRIVATE_KEY)
#cmdの実行
stdin, stdout, stderr = ssh.exec_command('ls -m')
#readlinesでリスト化
print(stdout.readlines())
ssh.close()

結果

[test_ssh.txt, test.txt\n']

解説

・3行目 : paramikoモジュールのインポート
・5行目 ~ 8行目 : 接続先情報を記載
・11行目:
set_missing_host_key_policyは接続しようとしたホストに対するホストキーがなかった場合の振る舞いを設定している。
今回はホストキーを自動追加とした。オプションは他にWarningPolicy、RejectPolicyなどもある。
・13行目 : ホストサーバーへ接続
・15行目:コマンドを実行 正常終了の場合は標準出力のstdoutで結果を受け取ります。
・18行目: コネクションをクローズ。

2.fabric


# -*- coding: utf-8 -*-
from fabric.api import run
from fabric.api import env

env.hosts = ["mfield.com"]
env.port = '22'
env.user = "ec2-user"
env.key_filename = "./ssh_test.pem"

run("ls -m")

結果

[mfield.com] out: test_ssh.txt, test.txt

解説

・ 3行目 ~ 4行目 : fabricモジュールをインポートします。
 なお今回は、python 2.7 を利用しました。
 最近fabricはver.2としてpython 3の対応を行っておりますが、
 3.7では 2019/5時点では動かない模様。
 fabric3というライブラリもあるようですが、今回は2.7を利用しました。
・6行目 ~ 9行目 : 接続先情報を記載
・13行目 : run()でコマンド実行します。

これだけでは、paramikoとfabircの違いが殆どわからないですね。
fabricが デプロイツール として良く利用されているのは
下記の様な、複数のhostに対し処理を行ってくれる為、
gitなどと連携して複数サーバーに対するデプロイ処理を並行して実行する事ができます。

複数hostへの接続


# -*- coding: utf-8 -*-
from fabric.api import run
from fabric.api import env
from fabric.api import sudo

env.hosts = ["ec2-user@mfield.com", "ec2-user2@mfield-test.com"]
env.key_filename = {"ec2-user@mfield.com": "./ssh_test.pem",
                 "ec2-user2@mfield-test.com": "./ssh_dev.pem" }

sudo(touch fabric.txt)
run("ls -m")
run("hostname")
run("whoami")
run("git pull origin master")

解説

・7行目 ~ 9行目 : 接続先情報を記載
今回は上記と異なり、複数の接続先をhostsに記載
また接続ユーザをhost毎にuser@hostで変えています。
  key_fileも環境によって異なる場合は、上記と同様 user@host に対になるように記載すれば、環境毎に設定が出来ます。
・12行目 : sudo でroot権限が必要な場合、sudo()で権限を変更し、コマンドを実行なども出来ます。

3.sshtunel


import paramiko
from sshtunnel import SSHTunnelForwarder

#REMOTE_SERVER
REMOTE_SERVER_IP = 'mfield.com'
PORT = 22
REMOTE_SERVER_USER = 'ec2-user'
REMOTE_SERVER__KEY = './ssh_test.pem'

#PRIVATE_SERVER
PRIVATE_SERVER_IP = '172.0.0.0'
PORT = 22
PRIVATE_SERVER_USER = 'ec2-user2'
PRIVATE_SERVER_KEY = './sshtunnel.pem'

with SSHTunnelForwarder(
  (REMOTE_SERVER_IP, PORT),
  ssh_username = REMOTE_SERVER_USER ,
  ssh_pkey = REMOTE_SERVER__KEY ,
  remote_bind_address=(PRIVATE_SERVER_IP, PORT)
) as ec2instace:
  client = paramiko.SSHClient()
  client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
  ssh.connect(PRIVATE_SERVER_IP, PORT, PRIVATE_SERVER_USER, key_filename=PRIVATE_SERVER_KEY)
  
  stdin, stdout, stderr = ssh.exec_command('ls -m')
  print(stdout.readlines())
  client.close()

結果

 [private_server.txt,  private_server_test.txt\n'] 

解説

踏み台サーバーを REMOTE_SERVER
接続先サーバーを PRIVATE_SERVER_IP としています。
・5行目 ~ 8行目 : REMOTE_SERVER の接続先情報を記載
・12行目 ~ 15行目 : PRIVATE_SERVER_IP の接続先情報を記載
・17行目 ~ 21行目 : REMOTE_SERVER に接続
・23行目 ~ 25行目 : 先述のparamikoを使用してPRIVATE_SERVER に接続

sshtunnelを利用したmysqlへの接続

mysqlに接続して、以下のデータ取得を行いJSONで出力を行います。

Id firstName lastName employeeCode region
1001 Christian Bale E1 JP
1002 Cillian Murphy E2 CA
1003 Aaron Eckhart E3 EN

# -*- coding: utf-8 -*-
import json

import mysql.connector
from sshtunnel import SSHTunnelForwarder

#REMOTE_SERVER
REMOTE_SERVER_IP = 'mfield.com'
REMOTE_SERVER_USER = 'ec2-user'
REMOTE_SERVER__KEY = './ssh_test.pem'

#DB_HOST
DB_HOST='mf-db.cskbsd1wzlua.ap-northeast-1.rds.amazonaws.com'
DB_PORT=3306
DB_NAME=mf_testdb
ROOT_USER=root
ROOT_PASSWORD

# SSH接続の設定
with SSHTunnelForwarder(
  (REMOTE_SERVER_IP, 22),
  ssh_username=REMOTE_SERVER_USER ,
  ssh_pkey=REMOTE_SERVER__KEY ,
  remote_bind_address=(DB_HOST, DB_PORT)
) as ec2instace:
    con = MySQLdb.connect(
    host=DB_HOST,
    port=DB_PORT,
    db=DB_NAME,
    user=ROOT_USER,
    passwd=ROOT_PASSWORD,
    charset = 'utf8',)

      query = "select * from emp"
      cursor = con.cursor()
      
      cursor = con.cursor(MySQLdb.cursors.DictCursor)
      cursor.execute(query)
      data = cursor.fetchall()

      print(json.dumps(data, ensure_ascii=False, encoding='utf8', indent=4))
      con.close()

解説

踏み台サーバーを REMOTE_SERVER
DBサーバーを DB_HOST としています。
・9行目 ~ 11行目 : REMOTE_SERVER の接続先情報を記載
・14行目 ~ 18行目 : 接続先mysqlの接続先情報を記載
・22行目 ~ 25行目 : REMOTE_SERVER に接続
・27行目 ~ 33行目 : mysqlに接続
・36行目 : mysqlからコネクションカーソルを取得
・39行目 : 35行目に記載したselect 文を実行
・40行目: 今回は1件ずつデータ取得とせず、fetchallとして全件取得を行っています。
・42行目: jsonで 取得結果を表示

結果


{
    "emp": [
        {
            "Id": "1001",
            "firstName": "Christian",
            "lastName": "Bale",
            "employeeCode": "E1",
            "region": "JP"
        },
        {
            "Id": "1002",
            "firstName": "Cillian",
            "lastName": "Murphy",
            "employeeCode": "E2",
            "region": "CA"
        },
        {
            "Id": "1003",
            "firstName": "Aaron",
            "lastName": "Eckhart",
            "employeeCode": "E3",
            "region": "EN"
        }
    ]
}

まとめ

今回、python 覚書シリーズとして、python ライブラリによるssh接続を纒めてみました。
接続用途によって様々なライブラリが用意されており、使用方法も難しい事はなく、コードもかなりスッキリしたものが記載できました。
今回書いた中でもfabricは自動化 ツールとして、かなり利用出来るんじゃないかと思っております。
今後はfabicに関して、新たに記載もしてみたいと考えております。

以上で python 覚書シリーズ第2段 ssh接続 の紹介はおしまいです。
お付き合いいただきありがとうございました。