僕とコードとブルーハワイ

omega (@equal_001) の日記

Rundeck 基礎編 インストールからジョブ実行までのメモ

最近触る機会があったので、その記録として導入方法から少しずつ使用方法をまとめていく。

Rundeckとは

とても雑にいうとcron機能の進化版みたいなもの。(とか言ったら各所から怒られそう)
時間毎にジョブを実行させるのはもちろん、複数サーバへのパラレル実行、WebUI操作、外部サービスへの通知連携など、多機能なサービスです。


インストール

環境:Ubuntu 14.04
Rundeckバージョン:2.6.2


公式サイト( Rundeck.org - Downloads )にOSごとのパッケージ取得方法が書かれているので、これを参考に取得する。

$ apt-get install openjdk-7-jdk 
$ dpkg -i rundeck-2.6.2-GA.deb


上記でローカルでRundeckを使う環境が整うので、早速Rundeckを起動。

$ service rundeckd start


localhost:8000にアクセスすると以下のページが表示される。この画面が表示されれば正常に起動している。
デフォルトはadmin:admin
ユーザとパスワードの設定は後述する設定ファイルで変更可能。

f:id:equal_001:20160326023136p:plain

プロジェクト作成

ログイン後、以下の画面に遷移するのでNew Project+を押す。
f:id:equal_001:20160326023132p:plain

New Project+を押すと、プロジェクトのConfig画面に遷移するので、Project Nameを入力する。秘密鍵を登録済みなら、Default Node Executor のSSH、 Default Node File CopierのSCP(画像では切れてるがSSHの下にある) を設定する。
f:id:equal_001:20160326023538p:plain

ジョブの作成

プロジェクトを作成したらプロジェクト一覧画面に遷移するので、New Jobを押す。
f:id:equal_001:20160326024317p:plain
ジョブの設定画面では最低限以下の項目を入力する。

  • Job Name: ジョブの名前。どういうコマンドを実行させるジョブかわかる名前をつける。
  • Description: このジョブの説明を書く。
  • Workflow: コマンドまたはスクリプト実行を設定する。Commandはワンラインコマンド、ScriptはShellスクリプトを登録できる。このプロジェクトではScriptを使用している。

今回はhelloといいう文字列を出力するhello.pyを実行するジョブを作成する。
f:id:equal_001:20160326025340p:plain


ローカルで実行するなら必要はないが、リモートサーバでジョブを実行させる場合は以下項目の設定が必要となる。

  • Nodes: 実行するリモート環境を選択する。リモート環境を選択するには、/var/rundeck/projects/${project.name}/etc/resources.xml を編集する必要がある。(Webからは設定できないので、Rundeckサーバに入って直接編集する)

f:id:equal_001:20160326030542p:plain

resources.xml の設定
  • node nage: ジョブ作成ページの Nodes のNode Filterで入力する名前。
  • hostname: リモート先のホスト名
  • username: リモート先に入るときのユーザ名

以下の例だと、ジョブ作成時にNode Filterでexampleと入力して設定しておくことで、プロジェクト作成時に設定した秘密鍵を使用してRundeckがSSHしてジョブを実行する。ssh -i id_rsa example-user@example.com というようにしているのと同じ。

<?xml version="1.0" encoding="UTF-8"?>

<project>
  <node name="OMEGA.local"
    description="Rundeck server node"
    tags=""
    hostname="OMEGA.local"
    osArch="amd64"
    osFamily="unix"
    osName="Linux"
    osVersion="3.13.0-76-generic"
    username="omega"/>
  <node name="example"
    description="example node"
    tags=""
    hostname="example.com"
    osArch="amd64"
    osFamily="unix"
    osName="Linux"
    osVersion="3.13.0-76-generic"
    username="example-user"/>
</project>

その他、オプションで細かな設定ができる。よく使用するものを挙げる。

  • Thread Count: 並列数の選択。
  • Send Notification: ジョブ結果の通知設定。通知するタイミングは成功・失敗・スタートの3種類選べる。通知方法はEmail、Webhook(Slackも可能)の方法を選べる。

Schedule to run repeatedly: ジョブの実行周期を設定する。

  • Log level: 実行結果のログレベルの設定。Nomalならコマンド結果の標準出力、Debugなら- - - Rundeckが実行しているより詳細なログを出力する。Debugにすると、実行終了までの時間が多少遅くなる。

ジョブの実行

Saveボタンを押してジョブを保存すると、登録したジョブの管理画面になる。
ジョブを実行するには、Run job Now を押す。

f:id:equal_001:20160326031116p:plain

実行状況を確認する管理画面に遷移する。Log Outputタブを押してみると、「hello.pyの実行」ジョブのタスクによりhelloという表示がされている。成功。
f:id:equal_001:20160326031438p:plain

秘密鍵の登録方法

2通りの登録方法がある。

WebUIからの秘密鍵を登録する

Rundeck全体の管理画面に遷移して、Key Storageを選択、Add or Upload a Key を押して鍵を登録する。

f:id:equal_001:20160326032010p:plain

任意の場所に鍵を置いてresources.xmlでPATH指定して読み込む

所定のディレクトリに設置した秘密鍵を、以下の例のようにresources.xml でPATH定義して読み込める。
鍵のパーミッションの設定を忘れずに。
パーミッション・・・.ssh 700、rundeck-key 600 chown rundeck:rundeck

例)秘密鍵が/etc/rundeck/.ssh/rundeck-keyにある場合
  <node name="example"
    description="example node"
    tags=""
    hostname="example.com"
    osArch="amd64"
    osFamily="unix"
    osName="Linux"
    osVersion="3.13.0-76-generic"
    ssh-authentication="privateKey"
    ssh-keypath="/etc/rundeck/.ssh/rundeck-key"
    username="example-user"/>

こっちの方法でやったほうが色々楽だと思う。
また別に書くけど、AnsibleでRundeck環境を諸々構築するときに鍵を配置できるので、基本WebUIからの登録はしない。

slackへ通知するプラグインの追加

デフォルトでは使用できないが便利なプラグインを公開してくれている方がいるので、それを利用させて頂く。
使用方法も以下のページに書いてるのでここを読めばOK。
github.com

rundeck-slack-incoming-webhook-plugin-0.5.jar をダウンロードしてきて、/var/lib/rundeck/libext/配下に設置するだけ。

$ sudo mv rundeck-slack-incoming-webhook-plugin-0.5.jar /var/lib/rundeck/libext/

2015年振り返りと2016年の抱負を軽く

2015年お世話になった皆様ありがとうございました

暦の上で節目ということで、タイトル通り、2015年の振り返りと2016年の抱負を軽く。


技術編
PythonDjangoについて日々勉強というか、触ってました。あとパフォーマンスについて色々考える機会が多くなりました。
2016年はアルゴリズムリファクタリング機械学習あたりをなんかやりつつ生きます。Darkのrayをkillすることに夢中になったとき、やっぱりインフラ周りちゃんと使えるようになりてぇなと再度強く感じたのでそっちもなんかぼちぼち。
コードを書くスピードが遅いという自己評価と、仕様決めたりモノゴト話進めるのが遅いという率直なご意見もいただいたので仕事を進めるスピードをもっと上げれるように頑張るぞい、という心です。


生活編
今年は東京へ渡り、知らない人間に囲まれて暮らして大変気疲れしたり、良い歳した大人でも想像以上に腹の中ドロドロしてる人たちを見かけたりして、ストレスで脳が萎縮したなぁという一年でした。
2016年からは、ストレスから自分を守る為にも、アレな人間は雑草か何かと思って生きる事にします。いちいち雑草が喚いてることを気にしても時間の無駄です。
陰でコソコソいう女子特有のアレとかも気にしないで、「花の実も付かない枯れた雑草が日陰で落ちぶれているわ」くらいに思っておきます。

はぁ、清々しい世界で生きたいですね

勿論、マイナスな部分だけでなく、Darkやバイク乗りの皆さんや今住んでる地域の良い人達と出会えたし総合的にみて良い一年でした。

僕は(過激でない)耽美主義者なので、僕の中では美しいものは正義であり美しくないものを見続けると僕の戦闘力が減るのですが、2015年はあまり美しいものを見なかったためにそろそろ精神の限界が来つつあるので、2016年は美しいものを目に入れる機会をもっと増やす所存です。


まとめ
これより、美しいものを見ながら直感の赴くままに学び、遊び、清々しく生きます。
今年もよろしくお願いします。



Django 1.9で追加されたメジャー機能の紹介

この記事は 2015 tech-yuruyuru アドベントカレンダー - connpass 14日目のものです。


Django 1.9 がリリースされたので、メジャー機能だけでもチェックしようということで、ドキュメントとコードみつつ調べたことを簡単に書いてきます。
ここに書いてあるコードは本家documentから引用してます。(わかりやすいので)


今回追加されたメジャー機能

  • Performing actions after a transaction commit
  • Password validation
  • Permission mixins for class-based views
  • Running tests in parallel

Performing actions after a transaction commit

  • トランザクションが正常にコミットされた後に実行するアクションのフックが追加されました
from django.db import transaction

def do_something():
    pass  # send a mail, invalidate a cache, fire off a Celery task, etc.

transaction.on_commit(do_something)

(引用: Database transactions | Django documentation | Django)


Password validation

パスワードのいろんなバリデーション機能がDjango側でできるようになった
settingsのAUTH_PASSWORD_VALIDATORSにバリデーションの種類をいろいろ追加できる
defalutは []、設定しなければ全てのパスワードは受理される

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        'OPTIONS': {
            'min_length': 9,
        }
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

(引用: Password management in Django | Django documentation | Django)

  • UserAttributeSimilarityValidator

Userの入力したパスワードが、username, first_name, last_name, email と類似しているかどうかを検証してくれるclass
lower()で小文字にしたあとSequenceMatcherで文字列比較、quick_ratio()でシーケンスの同一性を測り、類似度が高い場合にエラーメッセージを返す。

  • CommonPasswordValidator

よくあるパスワードのリスト.txtと一致したらエラー出すclass
common-passwords.txt.gzというのが、よくあるパスワードリスト。これを解凍してこのパスワードリストの中に入力されたパスワードがあればエラーメッセージを返す。

  • MinimumLengthValidator

パスワードの最小の長さを設定できる

  • NumericPasswordValidator

数値のみでパスワードが構成されていないかチェックしてくれる

  • 実装みれば何やってるかよくわかります

django.contrib.auth.password_validation | Django documentation | Django


Permission mixins for class-based views

Django-bracesにあるアクセス制御関連と似た機能を追加したよという話
requestを受け取ってViewに処理を渡す前にアクセス権限のチェックとかしたりするのを、今まではdecoratorを作ったりDjango-bracesを入れてやっていたけど、これがDjango側でできるようになった。

  • 今回追加されたMixin
    • AccessMixin
    • LoginRequiredMixin
    • PermissionRequiredMixin
    • UserPassesTestMixin
  • LoginRequiredMixin

非認証ユーザをログインページ または 403ページ へリダイレクト
このMixinはUser.is_active フラグはチェックしないというのは覚えていた方が良いかも。

from django.contrib.auth.mixins import LoginRequiredMixin

class MyView(LoginRequiredMixin, View):
    login_url = '/login/'
    redirect_field_name = 'redirect_to'

(引用: Using the Django authentication system | Django documentation | Django)

  • PermissionRequiredMixin

ビューにアクセスするユーザが指定の権限を持っているかチェックする
権限のないユーザの取り扱いをまとめてカスタマイズできる。便利だと思う。

from django.contrib.auth.mixins import PermissionRequiredMixin

class MyView(PermissionRequiredMixin, View):
    permission_required = 'polls.can_vote'
    # Or multiple of permissions:
    permission_required = ('polls.can_open', 'polls.can_edit')

(引用: Using the Django authentication system | Django documentation | Django)


Running tests in parallel

別のプロセスで平行してテストを実行できるよという話

    • pararel=n でプロセス数の調整もできる
  • 実行方法
    • python manage.py test --pararel=5
    • DJANGO_TEST_PROCESSESでプロセス数を設定可能
  • 注意点
    • pdb仕込むときは並列化を無効にしないとpdb落ちる
    • テスト失敗時は例外のトレースバックが表示されないこともある(デバッグするの困難)
    • 各プロセスのテストで独自DBを使用するので、同じリソースにアクセスしないように気をつけろとのこと

(資料: [django-admin and manage.py | Django documentation | Django)



以上です。
他にもいっぱい追加されてたので、少しずつ調べる。


ちなみに (だいたい)新卒エンジニア向け技術交流会 vol.5 - Dark - Developers at Real Kommunity | Doorkeeper で発表した内容とほぼ一緒なので、スライドも置いておく。

www.slideshare.net

本と対話する

この記事は2015 tech-yuruyuru アドベントカレンダー - connpassの6日目です。

tseで遊んだ何か書こうと思ったけど気分乗らなかったので、19日に書こうと思ってたエモい(?)話を書く。(エモい話でもいいよって言ってたから!)

続きを読む

メモ: Pythonのパフォーマンスチューニング cProfile

=================
環境:MacOS X 10.4

Python -V:3.4.2
=================

最近パフォーマンスチューニングをすることが多いのでメモ。

ドキュメント: 27.4. Python プロファイラ — Python 3.4.3 ドキュメント



良い例が思いつかなくてアレだけど、とりあえず呼び出し回数と実行時間の違いがわかるものを書いた。
100万回join_textを呼び出して、渡ってきた数字が偶数ならBlue、奇数ならRedのNo.として返してひたすらtext変数にstrでつなげるやつ。

 1  def main():
 2     text = ''
 3     num_list = range(1000000)
 4     for num in num_list:
 5         text += join_text(num)
 6
 7  def join_text(num):
 8      if num % 2 == 0:
 9          return "Blue_No.{}".format(str(num))
 10     else:
 11         return "Red_No.{}".format(str(num))
 12 
 13  if __name__ == '__main__':
 14     import cProfile
 15     cProfile.runctx("main()", globals(), locals())

実行結果

         2000004 function calls in 1.396 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.001    0.001    1.396    1.396 <string>:1(<module>)
        1    0.414    0.414    1.395    1.395 profile_test.py:1(main)
  1000000    0.646    0.000    0.981    0.000 profile_test.py:7(join_text)
        1    0.000    0.000    1.396    1.396 {built-in method exec}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
  1000000    0.335    0.000    0.335    0.000 {method 'format' of 'str' objects}


なんかもっと複雑なやつにcProfile通してみると冗長な書き方とかいっぱいでてきて面白い。

toxコマンドでInvocationError食らったときの対処法

=================

環境:MacOS X 10.4

Python -V:3.4.2

=================

 

問題:

toxでUnitTest実行しようとしたら"InvocationError"になった。

 エラートレース

py34 installed: py==1.4.30,pytest==2.8.1,UNKNOWN==0.0.0,wheel==0.24.0
py34 runtests: PYTHONHASHSEED='1572653641'
py34 runtests: commands[0] | py.test tests
========================= test session starts =========================
platform darwin -- Python 3.4.2, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
rootdir: /Users/OMEGA/toxtest, inifile: tox.ini
collected 0 items

=========================== in 0.00 seconds =========================
ERROR: InvocationError: '/Users/OMEGA/toxtest/.tox/py34/bin/py.test tests'
______________________________ summary ____________________________
ERROR: py34: commands failed

  

原因 :

  • tox実行時にテストファイルが一つも無い場合、InvocationErrorになる
  • pytestのテストファイルの読み込み規則の問題でtestファイルが読み込まれない罠

 

調査と解決方法:

今回の調査環境ディレクトリ構成

toxtest

├── setup.py
├── tox.ini
└── tests
    ├── check_hoge.py

    └── tests.py

 

tox.ini

[tox]

  envlist = py27, py34

[testenv]

  basepython = python3.4

  commands = py.test tests

  deps = pytest

 

この構成でtoxを実行してみたが、上記のエラートレースが発生した。

どうやらテストファイルを認識してくれていないようだ。

というかtoxってテストケースが一つも無い時ってエラー吐くんだね。。

 

というわけで調べてみたところ、そもそもpytestではデフォルトtest_*.pyしかテスト時ファイルとして読み込まないようになっていた。

以下のドキュメントを参考に、任意のテストファイル名でも認識してくれるように設定した。

Changing standard (Python) test discovery

 

以下の設定をtox.iniへ追記する。

; [pytestの設定で読み込むファイル形式を定義する。

; デフォルトはtest_*.pyのみだが、python_filesで定義するときはtest_*.pyも記述しないと省かれてしまう。

[pytest]

python_files = check_*.py test_*.py tests.py

 

tox実行結果

py34 installed: py==1.4.30,pytest==2.8.1,UNKNOWN==0.0.0,wheel==0.24.0
py34 runtests: PYTHONHASHSEED='3739634378'
py34 runtests: commands[0] | py.test tests
==================== test session starts ====================
platform darwin -- Python 3.4.2, pytest-2.8.1, py-1.4.30, pluggy-0.3.1
rootdir: /Users/OMEGA/toxtest, inifile: tox.ini
collected 2 items

tests/check_hoge.py .
tests/tests.py .

================== 2 passed in 0.03 seconds =================
____________________________ summary __________________________

py34: commands succeeded
congratulations :)

 

tests/配下のテストファイル二つが認識されて、テストが実行された。ほっ。 

 

このエラー見たとき、何故テストが認識されないのかわからなくて悩んだ。

なかなかマイナーなハマりポイントな気がする。