thirose’s blog

openstackやpythonなどなど

2023年の健康面の振り返りと2024年の目標

2023/01/01から始めたダイエット生活、当初は76.8kgだったが、年間を通して、最後は66.4kgで終わることができました。 ここ数年健康診断ドリブンでダイエットはするけど、健康診断が終わると体重がV字回復してましたが、2023年はそれを阻止できたことは大きかったです。

2023年の減量は、毎年運動となりがちだったけど、食事を気をつけることをかなり意識し、運動は、日曜日に走るか自転車に乗るだけにしました。会社のヘルシーなお弁当とサラダに感謝です。

なぜ運動をあまりやらなかったかというと、2023年の年初というか2022年ぐらいから四十肩っぽく肩が痛く回せないなど運動できる状態ではありませんでした。そして、体重も増えて体重で走れない身体だったので、まずは病院に行き肩のリハビリ治療をして、週末だけ軽いジョギングや負荷が少ないサイクリングなどから始めました。
最終的に12月には10kmを平均5分45秒/kmぐらいで走れることができる程度まで回復したのですが、年初はほとんどリハビリとストレッチに費やしてました。
2016年にハーフマラソンに出場した時は1時間51分(平均5分15秒/kmのペース)で走れてたし、せっかく減った体重をキープする目標を持つだけだと辛いので、ハーフマラソンやフルマラソンなどをやることで間接的にキープすることを2024年はやってけたらなと思ってます。
2023年の運動はrunnkeeperでみると、
ランニング: 238.28km
サイクリング: 229.38km
来年はサイクリング比率は落ちるかもしれませんが、マラソンの総距離を2倍か3倍ぐらいにできるとフルマラソンは見えてきそうですね。

そんな2023年だったので、2024年は食に気をつけつつ、身体を動かし健康に過ごせる1年になるようにしたいです。

ansible-vaultで作ったpasswordに改行が入るときの対応

ansible-vaultでパスワードを暗号化してセットしたつもりで展開されたら改行が入ってしまっていて、
以下のようなことになっていることがあります。

mysql_endpoint = mysql+pymysql://USER:PASSWORD
@HOSTNAME:3306/database

@から改行されてしまって、確認しようとしたらmysqlへの接続エラーみたいな...

そう言った時大体、

echo 'password' | ansible-vault encrypt

とやってしまっていますが、

echo -n 'password' | ansible-vault encrypt

を入れると改行を取り除いてくれますね。

echo の -n オプションは、man echo を読んでいただけるとわかると思いますが、 -n do not output the trailing newline
と書かれてて、改行を出力しないようにしてくれます。

よく忘れて、ansible実行すると大体これにハマるので注意したいです。

json.dumpsした値が期待した通りか確認するunittest

python3.7以前の辞書(dict)とJSONオブジェクトは順序立てられてません。
今回はだいぶ古いpythonなため、順序立てられていませんでした。
そのため、dictをjson.dumpsしたりすると、順番が崩れたりします。
オプションとして、 json.dumps(dict, sort_keys=True) のような
sort_keysを付けられるようなオプションが存在しています。

今回自分がハマったのはunittest引数として、json.dumpsがセットされたときにその順序をどう検証するのか?
というところで少し手間取ったので、まとめていこうと思います。

m_put.assert_called_once_with(data=json_string, headers=headers)

と、いうメソッドが正しい引数で呼ばれているかの確認ですね。
unittestがなく怖かったのですが、元のコードの挙動が正確にわからないため、
元のコードは極力いじりたくないという思いもありました。

当然コードの方には、その直前に json_string = json.dumps(raw_data) のようなことが書かれています。

call = m_put.call_args
call_args, call_kwargs = call
self.assertEqual(json.loads(call_kwargs['data']), json_string)
self.assertEqual(call_kwargs['headers'], headers)

のように、call_argsで引数を取得し、
jsonの部分は json.loads() して比較することで、問題ないことを確認しました。

docs.python.org

無意識に頭の中でdataの中身がdictと考えてしまい、無駄に時間すごしたし、ランダムに変わってしまう値にどうすべきか、
時間を使ってしまいましたが、 callで取れるという学びがありました。
これで、json.dumps() するときに sort_keys しても大丈夫な確認もとれました。

テスト駆動Python

テスト駆動Python

  • 作者:Brian Okken
  • 発売日: 2018/08/29
  • メディア: 単行本(ソフトカバー)

keyやクラス変数の確認

pythonにて、dictに特定のkeyあるかなーとか、クラス変数に期待した変数はあるかなーという確認する方法をたまに忘れるからメモです。

dict型

>>> dict = {'ptyhon_key': 'python_value', 'ruby_key': 'ruby_value'}
>>> if 'java_key' in dict:
...     print(dict['java_key'])
... 
>>> if 'ruby_key' in dict:
...     print(dict['ruby_key'])
... 
ruby_value

クラス変数

>>> class Valiable(object):
...     def __init__(self):
...         self.python_key = 'python_value'
...         self.ruby_key = 'ruby_value'
... 
>>> val = Valiable()
>>> if hasattr(val, 'ruby_key'):
...     print(val.ruby_key)
... 
ruby_value
>>> if hasattr(val, 'java_key'):
...     print(val.ruby_key)
... 
>>>

のように確認する。

パーフェクト Python [改訂2版] (PERFECT SERIES 5)

パーフェクト Python [改訂2版] (PERFECT SERIES 5)

Drone CIで Python-MySQLを使おうとしたら少しハマったのでその対応

今までunittestとかなかったpython2で動いてたスクリプトがあって、あまりにも変更するのが怖いからとりあえずテスト書いて、CIで動くと便利だよねーって思い DroneCIの設定を始めました。 まずはこんな感じかなとdrone.ymlを準備しました。

---
kind: pipeline
name: Unit tests

steps:
  - name: execute unit test
    image: python:2
    commands:
    - pip install --upgrade pip
    - pip install MySQL-Python sqlparse pings paramiko cryptography mock
    - python -m unittest discover .
    when:
      event: [push, pull_request]

そのとき、MySQL-Pythonでこけました。以下のようななんかパッケージがたりてないようなエラーにはまりました。

Failed to build MySQL-Python
94  Installing collected packages: MySQL-Python, sqlparse, pings, ipaddress, pycparser, cffi, enum34, cryptography, pynacl, bcrypt, paramiko, funcsigs, mock
95      Running setup.py install for MySQL-Python: started
96      Running setup.py install for MySQL-Python: finished with status 'error'
97      ERROR: Command errored out with exit status 1:
98       command: /usr/local/bin/python -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-MVeMp_/mysql-python/setup.py'"'"'; __file__='"'"'/tmp/pip-install-MVeMp_/mysql-python/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-avyllU/install-record.txt --single-version-externally-managed --compile --install-headers /usr/local/include/python2.7/MySQL-Python
99           cwd: /tmp/pip-install-MVeMp_/mysql-python/
100     Complete output (38 lines):
101     running install
102     running build
103     running build_py
104     creating build
105     creating build/lib.linux-x86_64-2.7
106     copying _mysql_exceptions.py -> build/lib.linux-x86_64-2.7
107     creating build/lib.linux-x86_64-2.7/MySQLdb
108     copying MySQLdb/__init__.py -> build/lib.linux-x86_64-2.7/MySQLdb
109     copying MySQLdb/converters.py -> build/lib.linux-x86_64-2.7/MySQLdb
110     copying MySQLdb/connections.py -> build/lib.linux-x86_64-2.7/MySQLdb
111     copying MySQLdb/cursors.py -> build/lib.linux-x86_64-2.7/MySQLdb
112     copying MySQLdb/release.py -> build/lib.linux-x86_64-2.7/MySQLdb
113     copying MySQLdb/times.py -> build/lib.linux-x86_64-2.7/MySQLdb
114     creating build/lib.linux-x86_64-2.7/MySQLdb/constants
115     copying MySQLdb/constants/__init__.py -> build/lib.linux-x86_64-2.7/MySQLdb/constants
116     copying MySQLdb/constants/CR.py -> build/lib.linux-x86_64-2.7/MySQLdb/constants
117     copying MySQLdb/constants/FIELD_TYPE.py -> build/lib.linux-x86_64-2.7/MySQLdb/constants
118     copying MySQLdb/constants/ER.py -> build/lib.linux-x86_64-2.7/MySQLdb/constants
119     copying MySQLdb/constants/FLAG.py -> build/lib.linux-x86_64-2.7/MySQLdb/constants
120     copying MySQLdb/constants/REFRESH.py -> build/lib.linux-x86_64-2.7/MySQLdb/constants
121     copying MySQLdb/constants/CLIENT.py -> build/lib.linux-x86_64-2.7/MySQLdb/constants
122     running build_ext
123     building '_mysql' extension
124     creating build/temp.linux-x86_64-2.7
125     gcc -pthread -fno-strict-aliasing -g -O2 -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -fPIC -Dversion_info=(1,2,5,'final',1) -D__version__=1.2.5 -I/usr/include/mariadb -I/usr/include/mariadb/mysql -I/usr/local/include/python2.7 -c _mysql.c -o build/temp.linux-x86_64-2.7/_mysql.o
126     In file included from _mysql.c:44:
127     /usr/include/mariadb/my_config.h:3:2: warning: #warning This file should not be included by clients, include only <mysql.h> [-Wcpp]
128      #warning This file should not be included by clients, include only <mysql.h>
129       ^~~~~~~
130     In file included from _mysql.c:46:
131     /usr/include/mariadb/mysql.h:444:3: warning: function declaration isn’t a prototype [-Wstrict-prototypes]
132        MYSQL_CLIENT_PLUGIN_HEADER
133        ^~~~~~~~~~~~~~~~~~~~~~~~~~
134     _mysql.c: In function ‘_mysql_ConnectionObject_ping’:
135     _mysql.c:2005:41: error: ‘MYSQL’ {aka ‘struct st_mysql’} has no member named ‘reconnect’
136       if ( reconnect != -1 ) self->connection.reconnect = reconnect;
137                                              ^
138     error: command 'gcc' failed with exit status 1
139     ----------------------------------------
140 ERROR: Command errored out with exit status 1: /usr/local/bin/python -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-MVeMp_/mysql-python/setup.py'"'"'; __file__='"'"'/tmp/pip-install-MVeMp_/mysql-python/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-avyllU/install-record.txt --single-version-externally-managed --compile --install-headers /usr/local/include/python2.7/MySQL-Python Check the logs for full command output.

普通に調べると、CentOSなら yum -y install python-devel mysql-devel gcc をインストールする必要があるというのですが、dockerのpython:2のイメージでは、それは無理。
ということで、centos:centos7に変更して実行することにしました。

そして、centos:centos7のイメージでは python-pipをインストールできないので、epel-releaseを事前にインストールする必要があります。

---
kind: pipeline
name: Unit tests

steps:
  - name: execute unit test
    image: centos:centos7
    commands:
      - yum -y install python-devel mysql-devel gcc 
      - yum -y install epel-release && yum clean all 
      - yum -y install python2-pip
      - pip install --upgrade pip 
      - pip install MySQL-Python sqlparse pings paramiko cryptography mock
      - python -m unittest discover .
    when:
      event: [push, pull_request]

早くpython3で動くように書き換えたいですね。その前にlintチェックも入れてこれもdroneでチェックできるようにする番だ。

特定のIPだけ許可したい時の判定方法

プライベートクラウドを運用していると、当然 DNS as a Serviceなんかも提供することになるのですが、OpenStackでは、Designateが第一選択肢として上がると思います。

そして、プライベートクラウドで外部IPを登録されてしまっては困るので、そういった部分は制限したいという要望がでてきます。

また、社内システムなんかでも特定のIPレンジだけ許可したいということもあるでしょう。そういったときにどうすべきか考えなくてはいけなかったので、考えてみました。

pythonのipaddressというモジュールを使います。

ipaddress.ip_networkで、networkを出します。  WHITE_LISTに登録された、IPレンジ、IPアドレスを一つずつチェックしていきます。 20.20.20.20みたいIP アドレスを直に指定すると、 20.20.20.20/32とされます。

そして、そのレンジに対象のIPがマッチするかどうかチェックしていくと簡単にマッチするかどうか判定できます。 

deny list等を作るとレンジが大きすぎるため、IPをどうにか制限したいと言う場合はallow listとした方が 管理も簡単になります。

スクリプト

#/usr/bin/env python
# coding: utf-8

import ipaddress

ALLOW_LIST  = ['10.0.0.0/8', '192.168.0.0/16', '20.20.20.20']


def checker(ip):
  for wl in ALLOW_LIST:
    ip_nw = ipaddress.ip_network(wl)
    if ip in ip_nw:
      result = True
      break
    else:
      result = False
  return result


if __name__ == "__main__":
  ip = ipaddress.ip_address('10.10.10.10')
  print('result: ', checker(ip))
  ip = ipaddress.ip_address('1.10.10.10')
  print('result: ', checker(ip))
  ip = ipaddress.ip_address('192.168.0.5')
  print('result: ', checker(ip))
  ip = ipaddress.ip_address('20.20.20.20')
  print('result: ', checker(ip))

実行結果

$ python ip_checket.py
result:  True
result:  False
result:  True
result:  True

「ジェームズ・クリアー式複利で伸びる1つの習慣」を読んで習慣の作り方を考える

今回読んだのは、「ジェームズ・クリアー式複利で伸びる1つの習慣」という本で、読むきっかけは知人がよかった言っていたからだ。
自分は、飽きやすい方だというのを自覚していて何かとやめがちで、習慣とか作る前に大体飽きるか他のことに興味をもってしまうかで長続きしにくい方なのかなと思っていました。

 

なので、今回自分にとって今まで何がいけなかったのかを考える良いきっかけになりました。

そして、コロナ禍で自分と向き合う良い機会で近年気づいててもなかなか出来ていなかったダイエットに挑戦しました。

その経験を踏まえ、紹介していければと思います。


 

 

まず初めに、 複利で伸びるとはどういうことかというと、人間毎日1%の習慣の改善をおこなっていくと、最初の1%は対したこないかもしれないが、1年間、毎日自分の習慣を1%よくする生活していけば、1年後に37倍になります。

そういった意味でも本書籍では習慣づけるためには習慣は小さいものから始めるとかいてあります。

 

自分のケースで話せば、最初に自分の習慣を変えたのは、朝寝起きに体重計に乗ることでした。

それまでは寝起きにスマートフォンSNSを見たりダラダラしていましたが、その悪習慣を断ち、まず体重計にのることから始めました。

本書には最初の習慣に2分やることと書かれいますが、1分もかかりません。そして、乗ったらfitbitに体重と体脂肪率を記録する。それだけです。

 

毎日計測することで、一喜一憂もしますが、自分の体重が減ったか増えたか日々確認できます。0.1kgでも減った日はもちろん嬉しいしそれがうれしく、走るモチベーションにつながるし、増えた日は食事も気をつけようと気が引き締まりましたね。

 

ここで、本書に書かれていた習慣化に役立つtipsを紹介すると、

  1. 最初の習慣はなるべく小さく、本当に習慣化するための動機付けになることをする
  2. 2分だけやってみる
  3. 記録をつける

です。僕が本当にしたかったことは言うまでもなく、ダイエットをすることです。

そのために、走ろうと考えてました。

そして、2分だけやってみるのは、2分もかからない体重計にのること。

最後に記録をつけること。体重計に乗って記録をつけて推移を確認するので2分程度かもしれません。もっといえば、fitbitの体重計を買ってしまえば記録をつける必要もなくなり手間と感じる必要もないかもしれません。

 

 

 

そして、朝子供を見送ったら後、走りに行きます。

体重計にのったことで、嬉しい日は足取りも軽いし、増えた日は普段より負荷を高くするためにちょっと距離伸ばそうかな。とかスピード上げようかな。とか考えたりしました。

 

ここで、もう一つ習慣について紹介すると、

本書で触れられてたのですが、習慣には、良い習慣、悪い習慣、どっちでもない習慣と3つの習慣があります。悪い習慣は直すべき習慣で、どっちでもない習慣というものがあります。

例えば、「朝N時に起きる」とか「ご飯をたべる」とか悪くもよくもない習慣です。
新しい習慣を始めたいのならば、まずはそういった習慣の後につけるのが一番やりやすいと紹介されています。

 

ここまで2つのことをはじめましたが、どちらも特定の習慣の後です。
朝起きたら -> 体重計

子供を見送ったら -> 走りに行く

どちらも日常的にほぼ 必ずと言っていいほどある習慣です。もちろん、見送る必要がなくなったら、また変わってくると思いますが、しばらくはそのままです。


そして、毎朝走るわけですが、毎日できるわけではありません。何かしら予定が入ることもしばしあります。そういったときにやめないこと、それが大事です。出来る限り早く再開することをめざします。9月は雨も多かったです。雨の日は外に出れません。でも雨が止んでる日は走ります。時には時間を変えて走りました。

 

そして、8月から始めた習慣をベースにしたダイエットは74.7から始まり、10/14時点で70.8kgまで減らすことに成功しました。

まだまだこれからも続けて、68kgまで落とすのが目標ですが、それ以降もキープする必要があります。そういったときに、今回始めた 体重計に乗る習慣は自分にとって良い薬となるでしょう。

今回本を読んで挑戦し始めて2.5ヶ月ほどですが、習慣化できてきてるんじゃないかなーと思っているし、常にモチベーションを保てているのは本のおかげじゃないのかなと思うので、これからも小さい改善から大きい目標が達成できるよう習慣の見直していきたいですね。


f:id:hirosetakahito:20201014233645j:image

 

まとめ

  1. 習慣は小さく始める
  2. 2分だけやってみる
  3. 記録をつける
  4. 良い習慣/どちらでもない習慣の直後に新しい習慣を取り入れる
  5. サボってもすぐに始める

今回ダイエットに関して意識した、習慣化するために本から学んで実践したことは上記5つのことになります。

もし興味を持たれた方がいましたが、まだまだ本書には色んなことが書かれているので、読んでみてください。