/var/log/study

つまり雑記

python fabric2 の話

先日、おもむろに pipenv install fabric したら、fabricのver 2が落ちてきた。

今までのfabricのインターフェースとは大きく変わっていたのでざっくりとまとめておく。

TL;DL

  • fabric2は良い
    • oopなインターフェースを備えた
    • 1ライブラリというスタンスが明確になった
  • fabric1のユーザーは http://docs.fabfile.org/en/2.1/upgrading.html に目を通しましょう。
    • デグレっぽいときはだいたい機能を削った理由が書いてある

Fabric

  • 自動化のためのツール
  • ShellScriptでは微妙だけど、Ansibleを持ち出すほどでもないときによく使われる印象

公式ドキュメント

どのような意図なのか?はわからないが、公式ドキュメントは2ドメインある

また2018年6月16日の段階では fabricの日本語ドキュメントは2対応していないので注意

Fabric ver 2 ?

  • fabric
    • もともとあったfabric
    • python3は未対応
    • 今となってはバージョン指定をして pip インストールをする必要がある
  • fabric 2
    • 先日リリースされたやつ
    • もともとあったfabricのインターフェースを洗練させたため、fabricとは互換性がない
    • python3対応
    • pipenv install fabric で落ちてくる
  • fabric 3
    • fabricが長い間 python3 に未対応だったためのfork
    • fabric の ver 1のインターフェースと ほぼ 互換性がある
      • コードを読むとわかるけど、一部非互換のところもあったよ
    • python3対応
    • pipenv install fabric3 で落ちてくる

ver 1 と ver 2 のgetting start 的な比較

すごく雑だが、パスワード認証でログインできるサーバーにrootで hello world を出力するサンプル

ver 1

from fabric.api import run, env

env.hosts = ["192.168.0.1"]
env.user = "root"

def hello():
    run("echo hello world")

fabfile.pyのあるディレクトリで fab hello で実行可能

ver 2 を ver 1っぽい書き方で

from invoke import task

@task
def hello(c):
    c.run("echo hello world")

fabfile.pyのあるディレクトリで fab --host root@192.168.0.1 --prompt-for-login-password hello

ver 2 を ver 2っぽい書き方で

from fabric import Connection
c = Connection(host="192.168.0.1", user="root", connect_kwargs={"password": "xxxxxxx"})
c.run("echo hello world")

何が変わったのか?

  • ver 1
    • fab コマンドがメイン
    • fabfile.py で、関数の中に fabric.api.run を書いて、 fab 関数名
    • 改めて考えると、マジックな感じが多く、fabricに対する知識が求められる感じ
  • ver 2
    • fab コマンドは使えるが、 invoke の機能を拡張した形
    • fabric.Connection.run を使えばよく、標準のpythonスクリプトとして実行可能
    • API的に、OOP的な感じで、直感的にわかりやすいインターフェースになった

fabricの1と2では、機能的側面で互換性があるので、fabric 1の利用者は http://docs.fabfile.org/en/2.1/upgrading.html を読むことをおすすめする。自分が気になった非互換は以下

fabric ver 1 と ver 2での非互換

http://docs.fabfile.org/en/2.1/upgrading.html からの抜粋して雑に意訳とその感想

  • role がなくなった
    • Group がいい感じで使えるようになったからそっちで賄えそう
  • --shellenv.shell で シェルを指定できなくなった
    • fabric1のときはいろいろと指定する方法があったが面倒なので詳しくは調べていない
    • fabric2では http://www.fabfile.org/upgrading.html#run にremoteでの実行のことが書いてある
    • remoteに対しては runメソッドの引数などを利用せず、commandで明示的に指定するようにしてほしい感じだと思う
  • ディレクトリごとファイルを送れなくなった
    • http://www.fabfile.org/upgrading.html#file-transfer の一番下に書いてある
    • rsync + zip or tar みたいなコードをメンテするのが大変だったからやめたとのこと
    • 自分はローカルでzipにして、リモートに送ってunzipするようにした

雑な感想

fabric ver 2では直感的にわかりやすいし、個人的には良いと思えるインターフェース設計になった印象があるので、積極的にfabric1から移行したい。

mambaを利用してpythonでBDD

pythonで利用できるBDDのツールに良いのが無いよなぁと思っていたら、きちんと awesome-python に記載されていた。

Mamba — mamba 0.9.2 documentation

個人的な所感としては割と良い気がする。RubyRspecの雰囲気を感じ取れる。

インストール

pipenv install mamba

書き方

with 句と mamba で用意されている description もしくは describe , context , it と、 expects というパッケージに用意されている expect などを利用して普通のBDDがpythonでも書ける感じ。

_context などのアンダースコアで始まるのはペンディングの印

全く意味がないテストコードだけど、以下の様。

from mamba import describe, context, it, _context, _it
from expects import expect, be_true, equal
from faker import Faker # サンプルのデータを作るやつ
fake = Faker() 

fake.name()

with describe("New topi") as self:
    with context("Bool"):
        with it("True"):
            expect(True).to(be_true)

with describe("Pendins") as self:
    with _context("penndins"):
        with it("pending"):
            pass

with describe("Pendins") as self:
    with context("penndins"):
        with _it("pending"):
            pass

class Human(object):
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

with describe("Human") as self:
    with before.each:
        self.human = Human(fake.name())

    with it("has name"):
        expect(type(self.human.name)).to(equal(str))

ライフサイクル

ライフサイクルとしては以下のようになっている

  • before.all
    • (describe内の) テストを実行する前に1度だけ
  • before.each
    • (describe内の) 各テストを実行する前に毎回
  • after.each
    • (describe内の) 各テストを実行したあとに毎回
  • after.all
    • (describe内の) テストを実行した後に1度だけ

参考は以下

Hooks — mamba 0.9.2 documentation

実行方法

pipenv run mamba **/*_spec.py とかで実行可能

また pipenv run mamba --enable-coverage と実行した後に、 pipenv run coverage htmlカバレッジを見ることができる。

VMware Photon OS で構築するK8Sクラスタ

Photon OS とは ?

github.com

VMware さんが作っているOSSで、コンテナをホストするためのOSです。

特徴は以下

  • おそらくRedHatベースのOS
  • tdnf (tiny dnf) という独自のパッケージ管理ツールが用いられている
  • systemd
  • network周りはsystemd-networkd

モダンなLinuxの普通構成? なのかな。

K8Sクラスタを作る方針

自分自身は過去にkubeadmでしかクラスタを作ったことが無いので、kubeadmで作りたいと思います。

kubernetes.io

で、kubeadmがサポートしているOSの中に、 Photon OS なんて無いので、 パッケージ管理ツールが絡んできそうな場所は Container Linux, それ以外のところは CentOS などを利用していくつもりです。

注意点

  • 今回はクラスタリングしたいVMがReady状態になるまでしか試していません
  • なので、クラスタが正常に動かない可能性が大いにあります
  • 個人的な不安点は、特にiptablesのルール周りです。

Photon OSの準備

github.com

上のwikiovaを使って、vSphereにデプロイ。

その後にやったことは以下

  • hostnamectl set-hostname name で適当にhostnameを変更
  • 100GBのディスクを /var/lib/docker に追加
  • echo 'export PATH="${PATH}:/opt/bin"' >> ~/.bash_profile
  • tdnf install -y tar wget ebtables ethtool socat

K8S向けの特別な対応

何もせずに、kubeadmでセッティングしていくと、 kube-dns というやつがクラッシュして再起動しまくるので、issueを参考に設定を入れる

cat << EOF > /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=cgroupfs"]
}
EOF

github.com

また、PhotonOSのデフォルト状態だと、k8sAPIを叩くのに必要な6443ポートへの通信をDROPし、後の kubeadm join がfailedするのでポートを開けておく

iptables -I INPUT -p tcp --dport 6443 -m state --state NEW -j ACCEPT

一旦は検証なので、上記の感じで適当に開けるが、必要があればもう少し賢いルールを設定して、以下のissueを参考に永続化すると良い

github.com

kubeadmのインストール

基本的には以下の手順通りすすめる

kubernetes.io

上記の Installing kubeadm, kubelet and kubectl の項目は Container Linux の全部と CentOS の以下のコマンドを実行した

cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

kubernetes のインストール

ここまでの状態のPhotonOSのVMを複数台用意すること

あとは、以下に沿ってインストールを進めるだけ。

kubernetes.io

[WARNING FileExisting-crictl]: crictl not found in system path 

というWARNINGが出るが、 2018/06/24 時点では、crictlをインストールするとk8sのインストール段階でエラーになって結局ignore-errorするハメになるので無視で良さそう

自分は3VM用意して kubectl get nodes の結果は以下のような状態になった。

root@k8s-photon-001-v [ ~ ]# kubectl get nodes
NAME               STATUS    ROLES     AGE       VERSION
k8s-photon-001-v   Ready     master    34m       v1.10.5
k8s-photon-002-v   Ready     <none>    31m       v1.10.5
k8s-photon-003-v   Ready     <none>    31m       v1.10.5

検証中はちょいちょいハマりどころはあったものの、特に問題なくPhotonOSとkubeadmでK8Sクラスタを作ることができました。

後日確認したいこと

kubernetes.io

上記のページに、 Check required ports があるので、そのポートが空いているか否かを確認しつつ、正常にクラスタが動いているか?を確認する必要がありそうです。

Hashicorp Nomad で タスクにクライアント側の変数を利用する

Nomad とは

Nomad というのはおしゃれな緑色のHashicorpプロダクト

www.nomadproject.io

同じくHashicorpのConsulというツールと連動してジョブのスケジューラーとして動作してくれる製品です。

アーキテクチャ

NomadにはServerというユーザーからジョブを受け付けるヤツと、Clientというジョブを実際に実行するヤツとがいるっぽいです。

あまりきちんとドキュメントを読み込んでいないので、正しくないかもしれないですが。

実現したいしたいこと

あるジョブを実行した際、ホストごとに設定される環境変数を読み取り、挙動を変えたいと思いました。

以下のようなコードの中の DOMAIN をクライアントごとに切り替えたいという話です。

from flask import Flask
import os

app = Flask(__name__)

@app.route("/", methods=["GET"])
def root():
    return os.getenv("DOMAIN", "None")

if __name__ == "__main__":
    app.run(host="0.0.0.0")

ざっくりとした図は以下です。

f:id:yaaamaaaguuu:20180624120632p:plain

実現する方法

まず、クライアント側のサーバーを動かす設定ファイルの定義に meta というセクションを指定し、その中で適当に変数を宣言します

client {
    enabled = true
    servers = [ "ipaddress" ]
    meta {
        domain="This is domain value."
    }
}

次に、ジョブの定義の中で、クライアントのmetaを拾えるようにします。

以下の設定の中の、 envというところで、 DOMAIN="${meta.domain}" を指定しています。

こうすることで、上で示した実行したいコードは DOMAIN という環境変数がセットされた上で実行されるようになります。

job "web-app"{
    datacenters = ["dc1"]
    type = "system"
    update {
        stagger = "10s"
        max_parallel = 1
    }
    task "webservice" {
        driver = "docker"

        config {
            image = "flask-app:1.2.0"
            port_map {
                web = 5000
            }
        }
        resources {
            network {
                port "web" {}
            }
        }
        env {
            DOMAIN = "${meta.domain}"
        }
    }
}

以下の話は、ドキュメント に以下のように記載されていました。

${meta.key} | Metadata value given by key on the client | ${meta.foo} => bar

気づくのに時間がかかったので、覚書として。

全部ではないが、コードは以下

[Nomad] Use client side variables. · GitHub

pecoがxterm-256colorの際に文字化けるときの対処

ふとしたことがきっかけで、xterm-256colorのときにpecoが文字化けるようになってしまった。

github.com

自分と同じ症状が↑にまさしく出ていて、pecoの問題ではなく、pecoが利用しているtermbox-goとncursesとの問題とのことらしい。

自分の環境の $TERM あたりを中心に疑って見たが、pecoとncursesをアップデートしたらあっさりと治った。

Dockerを利用してgitlab pagesを試す

ためのdocker-compose.ymlを書いた。

このdocker-composeを動かすのに必要なのは以下

  • VM
    1. 4GB以上のメモリが搭載されたVM
    2. docker
    3. docker-compose
    4. gitlab-runner
  • DNS
    1. VMのIPに対してgitlab用のDNSレコードが設定されていること
    2. VMのIPに対してgitlab pages 用にワイルドカードなレコードが登録されていること

自宅のプライベートなネットワークでの利用の都合上、SSLの設定は切っている。

このymlで大事なのは13行目の gitlab_pages['inplace_chroot'] = true でコンテナ内でのpagesの利用を可能にしていること。

参考にしたissueは以下。

gitlab.com

以下がそのdocker-compose.yml

gist.github.com

systemd環境でDNSの向き先が変わらなかった

2018年の6月9日時点での話です。

Ubuntu18.04でいくら設定を変更しようとしてもDNSの向き先が変わらず若干悩みました。

結論から書くと、/etc/resolve.conf/run/systemd/resolve/resolv.confシンボリックリンクになっていることが期待されているのですが、シンボリックリンクになっていなかっただけです。

以下のリンクの質問と回答がドンピシャでした。

askubuntu.com

ubuntu 18.04 ではnetplanが導入されたので、その設定を変更しても、systemd-networkdの設定をいじっても、頑なに /etc/resolve.conf のnameserver は 127.0.0.53 を向いていたので何事だろうな?と思ったらsystemdのバグとのことでした。