【Python】smtplibでメール送信
Pythonでメール通知の実装メモ。
ホスト名などは適宜変更してください。
実装
ライブラリは不要です。
# -*- coding: utf-8 -*- import getpass import platform import smtplib from datetime import datetime from email.mime.text import MIMEText from email.utils import formatdate def send_mail(to: str, mail_text: str): """ メール送信 :param to: 送信先 :param mail_text: 本文 """ now = datetime.now().strftime('%Y-%m-%d %H:%M:%S') host_name = platform.uname()[1] message = MIMEText(mail_text) message['Subject'] = f'[{now}][{host_name}]' message['From'] = f'{getpass.getuser()}@{host_name}' message['To'] = to message['Date'] = formatdate() with smtplib.SMTP('localhost') as smtp: smtp.send_message(message)
【Kubernetes】kubectl の便利コマンド
使わなくなると忘れそうなので、kubectlコマンドをメモ。
alias k=kubectl
実行済の想定です。
随時、追加します。
入門ならこの一冊😊
トークン取得
$ (k describe secret $(k get secrets | grep <サービスアカウント名> | cut -f1 -d ' ') | grep -E '^token' | cut -f2 -d':' | tr -d '\t')
ネームスペース切り替え
$ k config set-context $(k config current-context) --namespace=<ネームスペース> # namespace一覧 $ k config view | grep namespace
tls用secret作成
base64デコード時のオプションを--decode
とすると、macでも動きます。
$ k create secret tls <secret名> --key=xxx.key --cert=xxx.crt # 確認コマンド $ k get secret/<secret名> -o yaml # jqが使える場合、項目指定も $ k get secret/<secret名> -o json | jq -r '.data."<項目名>" | base64 --decode' # historyに出力させたくない場合 $ k get secret/<secret名> -o yaml > secret.yaml $ grep "^data:" -A <secretの件数> secret.yaml | awk -F '{print $2}' | base64 --decode
リソースを更新して再起動
# コンフィグマップの編集 $ k edit cm <ConfigMap名> # 変更を反映させる為、再起動 $ k rollout restart <deployment名>
ポートフォワードでデバッグ
以下のコマンドで対象Podの80番ポートがlocalhost:8080
でアクセス可能となる。
$ k port-forward <pod名> 8080:80
【Python】FastAPIのディレクトリ構成
クラウドソーシングでFastAPI
の実装を行う機会があったので、調べた事などをメモ。
作成したソースコードはGit Hubに上げてあります。
ドキュメント
公式ドキュメントは分かりやすく書かれていた印象です。
ディレクトリ構成
公式ドキュメントや現場のPJを参考に、以下の構成にしました。
※ Docker関連ファイルなどは除外してあります。
. ├── __init__.py ├── app.py ├── cli.py ├── database.py ├── db_config.py ├── dependencies │ ├── __init__.py │ ├── auth.py │ └── request.py ├── exception.py ├── gunicorn_conf.py ├── handlers │ ├── __init__.py │ └── exception.py ├── midlewares │ ├── __init__.py │ └── timer.py ├── model │ ├── __init__.py │ └── users.py ├── plugins │ └── __init__.py ├── requirements.txt ├── response.py └── routers ├── __init__.py ├── index.py └── users.py
app.py
create_app
でルーターやDB、ミドルウェア、エラーハンドリングの設定を行っています。
uvicorn
やgunicorn
からこちらのモジュールを指定して起動します。
cli.py
開発時の動作確認用。
uvicorn.run
で起動する他、ArgumentParser
でホストとポートを指定可能です。
起動コマンドはpython cli.py
database.py
SQLite、MySQLと単体テスト用のデータベース設定を定義しています。
ORMはsqlalchemy
。
exception.py
独自のExceptionクラスを定義しています。
response.py
JSONResponse
を継承した、APIのレスポンスクラスを定義しています。
orjson
を組み込むだけならORJSONResponse
クラスを直接利用すれば良いと思います。
gunicorn_conf.py
本番起動時のgunicorn
に関する設定を記載しています。
詳細は以下を参照してください。
dependencies/*
fastapi.Depends
でrouter
モジュール内のエンドポイント定義関数の引数に設定するモジュール群です。
公式で言うと以下の辺りです。
handlers/exception.py
handlers
配下に定義したエラーハンドリング関数をapp.py
でadd_exception_handler
する事により、エラーハンドリングを集約する事ができます。
midlewares/*
ミドルウェア用関数を定義しています。
app.py
でadd_middleware
する事により使用可能となります。
各エンドポイントのレスポンス返却前に、何かしらの処理を追加できます。
公式に記載があるのですが、yield
を使った依存関係をもつ場合の終了コードはミドルウェアの後に実行されるようです。
model/*
model
配下にsqlalchemy
のモデルクラスを定義します。
plugins/*
PJ独自のライブラリを使用した共通処理がある場合などで、実装モジュールを分けられる場合に使用します。
routers/*
fastapi.APIRouter
で各エンドポイントを定義します。
app.py
でinclude時にsqlalchemy.orm.Session
を注入してエンドポイントで利用できるようにしています。
まとめ
FastAPI
を利用したい時にサッと引っ張ってこれる雛形が欲しいという事もあり、この記事の執筆に至りました。
おかしな点などありましたら、教えて頂けると嬉しいです😊
関係ないのですが、クラウドソーシングをしていると動けば良いというスタンスのコードをよく見かけるので、こういった記事が増える事により、〇〇コードが少しでも減ってくれれば良いなと思います。
(構成以前に謎文法な事も多いので、まずは公開されているpythonコードで自分が実装する分野と近しい実装を見て欲しいです、、、😂 )
【Python】pyhive で Presto 接続
PyHiveでPresto接続方法のメモ。
認証方法はプロダクト毎に違うと思いますので、適宜変更。
この辺りを見れば実装方法が分かると思います。
インストール
$ pip3 install pyhive[presto]==0.6.4
実装
# -*- coding: utf-8 -*- import base64 from pyhive import presto def execute(): cursor = presto.connect( '<ホスト名>', port=443, catalog='<カタログ名>', username='<ユーザー名>', protocol='https', ).cursor() # パラメータは辞書・リスト・tupleのいずれか cursor.execute('SELECT * FROM sample WHERE test_column = %s', parameters=('param_value',)) print(cursor.fetchall()) if __name__ == '__main__': execute()
参考
逐次処理等がしたい場合はこちらを参考にすると良いかも。
機会があれば実装しようと思います。
オススメ書籍
色々と考えさせられる一冊です!
ページ数も少なく、時間がない人でも読める量だと思います(`・∀・´)
【Kubernetes】Job・CronJobの設定方法
Job・CronJobの実装方法をメモ。
kubernetes環境上で実行するjobは並列起動してもエラーとならない様な設計にすると良いそうです。
後はdockerイメージ更新はタグをベースに行う事により、imagePullPolicy: IfNotPresent
で更新の必要がない時はpullをskipさせる事ができます。
タグがlatest
だと更新漏れ等事故の元となる為、お勧めしません。
Jobの定義
マニフェストに定義してkubectl apply -f job.yaml
で実行できます。
jobの確認方法はkubectl get job
など、他のリソースと同じです。
apiVersion: batch/v1 kind: Job metadata: name: test-job spec: completions: 5 parallelism: 3 backoffLimit: 1 activeDeadlineSeconds: 3600 # この秒数を超える処理はエラーとなるので注意 template: spec: restartPolicy: Never containers: - name: test-job-container image: test-image:0.0.1 imagePullPolicy: IfNotPresent command: - sh - -c - "python3 main.py" env: - name: MYSQL_PASSWORD #jsonをsecretに格納した場合 valueFrom: secretKeyRef: name: test.secret.mysql key: passwd
CronJobの定義
実行環境によりますが毎時起動などで0分もしくは5の倍数分を指定するPJが多い為、若干ずらして指定すると環境に優しいです。
実行後のPOD保管数をsuccessfulJobsHistoryLimit
とfailedJobsHistoryLimit
で指定できます。
もしPOD内にクレデンシャル情報などが生成される場合、不要になった時点で削除しておくと安全かもしれません。
apiVersion: batch/v1beta1 kind: CronJob metadata: name: test-cronjob spec: schedule: "02 * * * *" concurrencyPolicy: Allow startingDeadlineSeconds: 2000 # 開始時刻 + startingDeadlineSeconds までの間に起動可能 successfulJobsHistoryLimit: 3 # 成功したジョブのPOD保管数 failedJobsHistoryLimit: 3 # 失敗したジョブのPOD保管数 jobTemplate: spec: backoffLimit: 1 activeDeadlineSeconds: 3600 # この秒数を超える処理はエラーとなるので注意 template: spec: containers: - name: test-cronjob-container image: test-image:0.0.1 imagePullPolicy: IfNotPresent command: - sh - -c - "python3 main.py" env: - name: MYSQL_PASSWORD valueFrom: secretKeyRef: name: test.secret.mysql key: passwd
【Kubernetes】PODの安全な起動・終了について
KubernetesのPODを安全に起動・終了する方法をメモ。
アプリケーションのGraceful shutdownは言語によって異なる為、都度実装する事になります。
The Twelve-Factor App
現在のPJではThe Twelve-Factor Appに沿った開発が推奨されています。
PODの安全な起動・停止に関する考慮も重要です。
12factor.net
安全に停止させるには
deploymentのマニフェストにPreStop
を入れる事で、PODの破棄命令〜SIGTERMまでの時間を稼ぐ事ができます。
下記は単純にsleepしているだけですが、何かしらの処理を入れる事も可能です。
deployment.yaml
lifecycle: preStop: exec: command: ["/bin/sh", "-c", "sleep 20"]
起動時にアプリケーションのスタンバイが間に合わない場合
アプリケーションがReady状態になったかを確認するエンドポイントを設定する事ができます。
こちらもマニフェストに入れる事が可能です。
deployment.yaml
readinessProbe: initialDelaySeconds: 10 timeoutSeconds: 1 httpGet: path: /status port: 443 scheme: HTTPS
ローリングアップデート時のPOD数
PodDisruptionBudget
を作成する事により、クラスタ内に必ずPODが残るように退避しながらノードが入れ替わっていきます。
これにより、ローリングアップデート時にクラスタ内にPODが存在しないという状態を回避できます。
PDB設定例
apiVersion: policy/v1beta1 kind: PodDisruptionBudget metadata: name: <任意のPDB名> spec: maxUnavaliable: 1 selector: matchLabels: app: <PODのラベル名>
【curl】通信時間の計測
curlのオプションで通信時にかかる時間の計測ができます。
どの時間帯が長引いているか等で、不具合の原因切り分けができるかもしれません。
manページ
コマンド
$ curl -w "\ntime_namelookup: %{time_namelookup}\ntime_connect: %{time_connect}\ntime_appconnect: %{time_appconnect}\ntime_pretransfer: %{time_pretransfer}\ntime_redirect: %{time_redirect}\ntime_starttransfer: %{time_starttransfer}\ntime_total: %{time_total}\n" https://curl.se/docs/manpage.html <レスポンス内容...> time_namelookup: 0.004318 time_connect: 0.061376 time_appconnect: 0.180256 time_pretransfer: 0.180681 time_redirect: 0.000000 time_starttransfer: 0.217821 time_total: 0.375091
参考
time_namelookup
が大きい場合...名前解決に時間がかかっている
time_pretransfer
とtime_starttransfer
の差が大きい場合...サーバー側の処理に時間がかかっている
time_namelookup: <開始から名前解決完了までにかかった時間> time_connect: <開始からTCP接続完了までにかかった時間> time_appconnect: <開始からSSLハンドシェイク成功までにかかった時間> time_pretransfer: <開始からファイル転送開始までにかかった時間> time_redirect: <リダイレクトにかかった時間> time_starttransfer: <開始からレスポンスの最初の1byteが到達するまでにかかった時間> time_total: <開始から終了まで>