paloma blog

NWエンジニアやってます。主に自宅環境のお遊びを書きます。Pythonもちょっと。タイトルは好きなカクテルから。

PayPayの支払いデータを自分で集計したい2

前回PayPayの日毎の集計をSQLで出力させました。
最終目標はスマホアプリとして操作させるということで

  1. ツール単体で集計
  2. 集計結果を可視化
  3. スマホアプリ用にビルド

の1-2としてpythonでの出力をやります。

コード

早速ですが全体。
CSVを引数で読んでsqliteに格納してからsql文で出力します。
dbファイルが作成されるのもなんだかなということでいったんインメモリで動かすようにしました。

sql分を直で書くのはよろしくないかもしれませんが前回の文を流用したのでさくっとできました。
変数埋め込みの書き方が都合上バラけてしまったのが悔しいです。

import sys
import sqlite3
import datetime


def subDateisoformat(d):
    df = datetime.datetime.strptime(d, '%Y/%m/%d')
    sub_y, sub_m, sub_d = df.year, df.month, df.day

    return datetime.date(sub_y, sub_m, sub_d).isoformat()


def main():
    # Delete BOM in file
    with open(sys.argv[1], 'r', encoding='utf-8-sig') as f:
        csvfile = f.readlines()

    con = sqlite3.connect(':memory:')
    cur = con.cursor()

    for n, c in enumerate(csvfile):
        field = c.split(',')
        if n == 0:
            headers = (field[0], field[1], field[6])
            # Create sql statement becouse can't used qmark style
            headers = f'CREATE TABLE pay ({field[0]} TEXT,{field[1]} TEXT, \
                       {field[6]} TEXT)'
            cur.execute(headers)
        else:
            # Delete double quotes
            field[0] = field[0].replace("\"","")
            field[1] = field[1].replace("\"","")
            field[6] = field[6].replace("\"","")

            field[0] = subDateisoformat(field[0])
            insert_row = (field[0], field[1], field[6])
            cur.execute('INSERT INTO pay VALUES (?,?,?)', insert_row)

    cur.execute('SELECT "利用日/キャンセル日", "利用店名・商品名", sum("支払総額"), \
                 count("利用店名・商品名") FROM pay GROUP BY "利用日/キャンセル日", \
                 "利用店名・商品名" ORDER BY "利用日/キャンセル日"')

    for x in cur.fetchall():
        print(*x)

    cur.execute('SELECT sum("支払総額") FROM pay')
    # Use 'format' for use astarisk
    print('Total: {}'.format(*cur.fetchone()))

    con.close()

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print('Be specify csvfile.')
        sys.exit(1)

    main()

解説

難しいことはやっていないので2つだけ。

BOM付きファイル読み込み

PayPayの利用明細はBOM付きでした。
エンコードを指定して解決。

  • 指定なし
>>> with open('detail202311(5569).csv', 'r') as f:
...     csvfile = f.readlines()
... 
>>> csvfile[0]
'\ufeff"利用日/キャンセル日","利用店名・商品名","利用者","支払区分","利用金額","手数料","支払総額","当月支払金額","翌月以降繰越金額","調整額","当月お支払日"\n'
>>> with open('detail202311(5569).csv', 'r', encoding='utf-8-sig') as f:
...     csvfile = f.readlines()
... 
>>> csvfile[0]
'"利用日/キャンセル日","利用店名・商品名","利用者","支払区分","利用金額","手数料","支払総額","当月支払金額","翌月以降繰越金額","調整額","当月お支払日"\n'
日付の修正

明細はyyyy/mm/ddで出るのでISOフォーマットに直します。
1桁日付も0埋めしました。

>>> subDateisoformat('2023/12/1')
'2023-12-01'
>>> subDateisoformat('2023/12/11')
'2023-12-11'

動かしてみる

先月分出してみます。

❯ python3 --version
Python 3.10.12
❯ python3 paycalc.py detail202311\(5569\).csv
2023-09-30 ヤフージャパン 508 1
2023-10-04 PayPay 町かど酒場XXXX 3200 6
2023-10-06 PayPay XXXX Dining 1900 1
2023-10-07 PayPay 町かど酒場XXXX 2150 4
2023-10-08 PayPay 町かど酒場XXXX 2700 6
2023-10-09 PayPay 町かど酒場XXXX 1900 4
2023-10-12 PayPay 町かど酒場XXXX 2300 5
2023-10-13 PayPay 町かど酒場XXXX 1150 2
2023-10-14 PayPay 町かど酒場XXXX 3700 8
2023-10-14 PayPay XXXX Dining 2000 1
2023-10-15 PayPay 町かど酒場XXXX 4000 8
2023-10-16 PayPay 町かど酒場XXXX 1450 2
2023-10-18 PayPay 町かど酒場XXXX 950 2
2023-10-20 PayPay 町かど酒場XXXX 2200 4
2023-10-21 PayPay 町かど酒場XXXX 3500 6
2023-10-22 PayPay 町かど酒場XXXX 3900 7
2023-10-26 PayPay 町かど酒場XXXX 3950 8
2023-10-27 PayPay 町かど酒場XXXX 950 2
2023-10-28 PayPay 町かど酒場XXXX 2700 5
2023-10-29 PayPay ハリケーン 3020 1
2023-10-29 PayPay 町かど酒場XXXX 500 1
Total: 48628

同じく店名マスクしました。
出力もsqliteと同じで日付修正したのでソートも効いているしOKですね。

合計も出すようにしたのですがほぼ飲み屋で5万弱...ちょっと飲みすぎたね。

ちなみに7月の支払時。

❯ python3 paycalc.py detail202307\(5569\).csv
2023-05-31 ヤフージャパン 508 1
2023-06-03 PayPay 町かど酒場XXXX 2200 4
2023-06-03 PayPay XXXX Dining 1800 1
2023-06-09 PayPay 町かど酒場XXXX 900 2
2023-06-10 PayPay XX商店 755 1
2023-06-10 PayPay 町かど酒場XXXX 700 1
2023-06-11 PayPay 町かど酒場XXXX 1000 2
2023-06-18 PayPay 町かど酒場XXXX 2700 5
2023-06-19 PayPay 町かど酒場XXXX 2750 5
2023-06-25 PayPay XXXX Dining 2100 1
2023-06-30 PayPay 町かど酒場XXXX 1350 3
Total: 16763

少ないですね。使う金額はそんなに変わらないですが通いのペースが上がってしまったのがわかりますw
XXXX Diningというのも同じ街にあってたまに行くバーなので一応マスクしてますw

あとはこのコードをKivyに埋め込んで完成です。
が、見た目も少し良くしたいので一回グラフ化もさせます。

PayPayの支払いデータを自分で集計したい1

今年はコロナも落ち着きそうと言うことで引きこもりを脱却すべく外に飲みに行くようになりました。
地元に気軽に行けるちょっとお洒落な立ち飲み屋があり今年は平均週一回以上通ってます。

立ち飲みという形態上キャッシュオンなのですが毎回現金を用意するのが面倒だったので4月にpaypayを導入しました。
今年前半こそ週一ペースだったのですが通っていくうちに知り合いも増えたということもあり、だんだん頻度が上がってしまい今月・先月の請求がビックリするほど増加していました。

生活に困るレベルではないですが出費はなるべく押さえたいところです。
というわけで内訳を出してウォッチしようと思います。

paypayアプリから請求金額や使った店舗の集計は出来ますが、キャッシュオンで都度支払ってかつ同じ店に通っている身としては1日何件決済したかどうかのペースが知りたいところです。

CSV読んでグラフ化して完了!としたいところですがpaypayの気に入らないところで利用明細のダウンロードがアプリからしか出来ません。

スマホでダウンロードしてPCに移してCSVから起こして...って毎月やる?

こんな運用するならスマホから見れるようにしようということでいっちょ集計アプリを作ろうと思います。 

ロードマップ

いきなりアプリを作ろうとすると完成まで時間がかかるので段階を踏みます。

  1. ツール単体で集計
  2. 集計結果を可視化
  3. スマホアプリ用にビルド

というわけでまずは①です。 

集計ツール

月の支払い合計を出すだけなら何を使ってもできるのですが、使いすぎを見つけるため1日毎にどの店でいくら決済したかというのを集計したいです。

pythoncsvモジュールでなんとかできないかと思いましたがすぐにはできなかったのでpandasかSQLあたりを使います。

いずれスマホで触るアプリにしようと考えるとパッケージをボンボン入れてあまりサイズを大きくしたくないのでSQLで考えます。

なのでpythonsqliteのセットくらいになりますね。 

sqliteで集計

まずはsqliteでサクッと集計してみましょう。
csvインポート機能もあります。

とりあえず先月分。 

バージョン
❯ sqlite3 --version

3.37.2 2022-01-06 13:25:41 872ba256cbf61d9290b571c0e6d82a20c224ca3ad82971edc46b29818d5dalt1
csvインポート

型が指定できないのは困りますがインポート機能便利です。

sqlite> .mode csv
sqlite> .import 'detail202311(5569).csv' pay
スキーマ

CSVのヘッダです。  
必要なのは利用日、利用店、支払い総額くらいですかね。

sqlite> .schema
CREATE TABLE IF NOT EXISTS "pay"(
  "利用日/キャンセル日" TEXT,
  "利用店名・商品名" TEXT,
  "利用者" TEXT,
  "支払区分" TEXT,
  "利用金額" TEXT,
  "手数料" TEXT,
  "支払総額" TEXT,
  "当月支払金額" TEXT,
  "翌月以降繰越金額" TEXT,
  "調整額" TEXT,
  "当月お支払日" TEXT
);
1日毎かつ店毎の集計

CSV形式で出力します。  

見にくいですがテーブル形式だとスマホからもっと見にくいですからね。
処理上仕方無いのかも知れませんがデフォルトで全角なの腹立ちますね(笑)
日本の利用明細は何故か全部こうだ。

sqlite> .header on
sqlite> select "利 用 日 /キ ャ ン セ ル 日 ", "利 用 店 名 ・ 商 品 名 ",sum("支 払 総 額 "), count("利 用 店 名 ・ 商 品 名 ") from pay group by "利 用 店 名 ・ 商 品 名 ", "利 用 日 /キ ャ ン セ ル 日 " order by "利 用 日 /キャ ン セ ル 日 " ASC;
"利 用 日 /キ ャ ン セ ル 日 ","利 用 店 名 ・ 商 品 名 ","sum(""支 払 総 額 "")","count(""利 用 店 名 ・ 商 品 名 "")"
2023/10/12,"P a y P a y   町 か ど 酒 場 XXXX ",2300,5
2023/10/13,"P a y P a y   町 か ど 酒 場 XXXX ",1150,2
2023/10/14,"P a y P a y   町 か ど 酒 場 XXXX ",3700,8
2023/10/14,"P a y P a y   XXXX   D i n i n g ",2000,1
2023/10/15,"P a y P a y   町 か ど 酒 場 XXXX ",4000,8
2023/10/16,"P a y P a y   町 か ど 酒 場 XXXX ",1450,2
2023/10/18,"P a y P a y   町 か ど 酒 場 XXXX ",950,2
2023/10/20,"P a y P a y   町 か ど 酒 場 XXXX ",2200,4
2023/10/21,"P a y P a y   町 か ど 酒 場 XXXX ",3500,6
2023/10/22,"P a y P a y   町 か ど 酒 場 XXXX ",3900,7
2023/10/26,"P a y P a y   町 か ど 酒 場 XXXX ",3950,8
2023/10/27,"P a y P a y   町 か ど 酒 場 XXXX ",950,2
2023/10/28,"P a y P a y   町 か ど 酒 場 XXXX ",2700,5
2023/10/29,"P a y P a y   ハ リ ケ ー ン ",3020,1
2023/10/29,"P a y P a y   町 か ど 酒 場 XXXX ",500,1
2023/10/4,"P a y P a y   町 か ど 酒 場 XXXX ",3200,6
2023/10/6,"P a y P a y   XXXX   D i n i n g ",1900,1
2023/10/7,"P a y P a y   町 か ど 酒 場 XXXX ",2150,4
2023/10/8,"P a y P a y   町 か ど 酒 場 XXXX ",2700,6
2023/10/9,"P a y P a y   町 か ど 酒 場 XXXX ",1900,4
2023/9/30,"ヤ フ ー ジ ャ パ ン ",508,1

店名は一応マスクしてます。
他の飲み屋の決済もありますが先月の集計です。

町かど酒場が例の立ち飲み屋です。 
うーん、なかなか通ってますねw

長居して4000円周辺の回が何回かありますね。
これが良くなかったんだな。
フードも入ってるけど1件で7-8回注文してるのでこれも減らさねば。

記事執筆前にpythonでどうにかしようとウンウン唸っていたのですがSQLで一発でした。
SQL文は優秀ですねえ。


日付でソートしたかったのですが上手く並んでいませんね。
日付がTEXT型しかないのは知っていますがISOフォーマットじゃないと日付と認識しないようでこれでは型の変換もできないのでどこかで変換を噛まさないといけませんね。

とりあえず目的の出力は果たせました。
今回はここまでで次回pythonから呼んでみたいと思います。

自宅のグローバルIP変化をslackで通知する

外から家の環境にアクセスする時はSSL VPNを張るのですがある時繋げなくなってしまいました。
思い当たるのはVPN装置のpfsenseが落ちているかグローバルIPが変わるしか心当たりがありません。

帰宅後グローバルIPが変わっていたことが判明したのですが、変わるなんて滅多に無かったのでタイミングがわかりません。

ISPの簡易レンタルルータなのでログみても良くわかりませんでした。
再起動したかキャリア側の期間終了でDHCP再取得したような感じですが... WAN側が同軸ケーブルのルータなので置き換えができないんですよね。

でも急に繋げなくなると困るので監視ツールを作ります。

グローバルIP監視

ルータにログインしてスクレイピングして、とか考えましたがグローバルIP診断サイトにwgetとかでできるじゃんと思いつきました。
診断サイトにcurlでアクセスしてIPを正規表現で抜けばOK。
記事用にマスクします。

❯ curl -s http://www.myglobalip.com/ | grep -oP "(\d{1,3}\.){3}\d{1,3}" | sed -r 's/[0-9]{1,3}/xxx/g'
xxx.xxx.xxx.xxx

これを一次ファイルに書いておいて定期的に上記のシェルを実行したタイミングで突合すればOKですね。

通知ツール

出先でも分かるようにスマホに飛ばしたいです。

上記と同じくshellから飛ばして終わらせたい。
と調べたら

らへんがお手軽に出来そうです。

shellからのmail送信試したのですがgmailは自前MTAとかだとセキュリティを下げないと受信できないらしく、このためにセキュリティ下げるのはなあと思い諦めました。

www.digitalocean.com

後者のchat系は沢山ありますがたまたまスマホにslackが入っていたのでこちらに通知するようにします。

APIの準備

APIのページに行ったらフレームワークやWebソケットがどうのと種類がたくさんあってどれを使えばいいかパッとわかりません。

curlからメッセージを飛ばすチュートリアルがあったのでこちらを参考にします。

Posting messages using curl | Slack

APIはCreate appから連携するworkspaceと権限等設定して完了です。
(この辺の設定は通勤中にスマホからポチポチとやったのでスクショがありません)

Slackにインストール

これも忘れてしまいましたが作成過程のポップアップ等でSlack側にAPIをインストールできたと思います。 調べれば出るので割愛。

APIのテスト

Authenticationのテストをします。 (tokenはマスク)

❯ curl -d "token=xoxb-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" https://slack.com/api/auth.test
{"ok":true,"url":"https:\/\/masashi-qmv6034.slack.com\/","team":"masashi","user":"the_workspace_reporte", () }%    

OKが返ったので成功した様です。

テストメッセージを飛ばします。

 ❯ curl -d "text=Hi I am a bot that can post messages to any public channel." -d "channel=home-lab" -H "Authorization: Bearer xoxb-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" -X POST https://slack.com/api/chat.postMessage
{"ok":true,"channel":"(略)","ts":"(略)","message":{"bot_id":"B06491FEEAV","type":"message","text":"Hi I am a bot that can post messages to any public channel.","user":"(略)}}}%    

slack側を見るとメッセージ受信しました。OKです。
通知ツールの名前がよくわからないデフォルトのやつになってしまった。
どう変更するんだろ。まあこのままでもいいか。

IPアドレス変更メッセージを送信

IPが変わった体でメッセージを飛ばしてみます。

マスク版IPを変数に入れておいて、

IPADDRESS=$(curl -s http://www.myglobalip.com/ | grep -oP "(\d{1,3}\.){3}\d{1,3}" | sed -r 's/[0-9]{1,3}/xxx/g')
❯ curl -d "text=IPアドレスが変更されました。$IPADDRESS" -d "channel=home-lab" -H "Authorization: Bearer $(cat slacktoken)" -X POST https://slack.com/api/chat.postMessage
{"ok":true,"channel":"(略)","ts":"(略)","message":{"bot_id":"B06491FEEAV","type":"message","text":"IP\u30a2\u30c9\u30ec\u30b9\u304c\u5909\u66f4\u3055\u308c\u307e\u3057\u305f\u3002<http:\/\/xxx.xxx.xxx.xxx|xxx.xxx.xxx.xxx>","user":()}}}%   

これも送信OK。

slackでも無事に受信しました。
ちなみにメッセージ内の改行が効かなかったので2回実行してます。

旧IP、新IPの診断もOKです。
globalipaddressファイルに今のIPを書いてます。

test $(echo $IPADDRESS) = $(cat globalipaddress) ; echo $?
0

あとはこれらをスクリプト化してcronに仕込んでおけばOKですね。

まとめ

slack apiの豊富さに驚いて他のツール通知(LINE, Facebook, X等)にすればよかったかなと思いましたが意外と簡単に作れました。
とりあえずグローバルIPなんて能動的に見るものではないので、push通知があるツールで作りたかったのですがなんとかなりそうです。
home-lab用のチャンネルなので他にも家のラボ系の通知にも使えそう。

とりあえずこれでいつIPが変わってもVPNを貼り直すことができます。
まあ、IPの接続先変更はOKとしてもOPENVPN側のconfigを直す必要があるのですが...

linuxのriceターミナルに出てくるアレのパッケージ名

あらまし

今年の前半にunixporn、riceの概念を知って興味を持ち色々調べたのですが、記事や動画を見ていく中でどうしてもわからないものがありました。

それがこのターミナルのアスキーアートです。 サイト内のパックマンのやつね。

terminalroot.com

他にもインベーダーとかいろいろな絵が表示されて、riceの動画なんかだとターミナル起動時に出力されてかっこいいんですよね。

私もインストールしたいと思い「terminal ascii art pacman」とかいろいろなワードで検索したのですがヒット無し。
諦めてしばらく経ち、2代目ubuntuも少しいい感じにしてきたところで(シーズンだし)最近この記事を見つけました。

itsfoss.com

内容はタイトルの通りですが中に探していたパッケージが!

Shell Color Scripts

gitlab.com

Shell Color Scriptsというパッケージ名らしいです。
早速インストールしよう。 ドキュメントに沿ってubuntuはsourceからビルドします。

❯ git clone https://gitlab.com/dwt1/shell-color-scripts.git
❯ cd shell-color-scripts
❯ sudo make install
❯ sudo cp completions/_colorscript /usr/share/zsh/site-functions

実行

実行してみます。(pywal外してます)

これだー! OK!

結構パターン入ってます。
ゼルダのやつかっこいいですね。

あとはターミナル起動時にコマンド実行するようにしてターミナルrice一旦完成です。
ターミナル起動が楽しみだぜ。

2代目UbuntuのGNOMEでrice

ここ最近記事に登場しているNUCのubuntuですがカッコいいデスクトップはテンションが上がるのでこの子もrice化します。
この子はハングアップせず安定しているのでGNOMEのままやろうと思います。

conky

ずっと使ってるリソースモニタツール。
バックアップからconkyrcをコピーして終了。
居住地方の天気情報も取って表示してます。
これも色文字表示とか入れたいのだがなかなか...

zsh

お洒落ターミナルはzsh + oh-my-zsh + powerlevel10kで決まり!
これは何故か設定ファイルをコピーせずに再設定してしまいました。

sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" 
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git ~/powerlevel10k
cp dotfiles/.zshrc .
zsh
p10k configure  

ubuntuはライブラリ系のエラーが出るので追加。

/usr/lib/x86_64-linux-gnu/libgtk3-nocsd.so.0' from LD_PRELOAD cannot be preloaded
sudo apt install gtk3-nocsd

wallpaper

先代と同じサムスピの骸流島です。
SNKのドット本当好き。

日本語でもいいですが「gairyu isle」で検索するとたくさん出てきます。

GNOME Extention

www.youtube.com

youtubeのカスタム動画をみて良さそうなのを選択。
あとはトップバーを半透明にしたい。

有効化しているのはこの辺。

  • Aylur's Widget
    • work spaceの○表示が可愛かったので採用
    • 他の機能はあまり使いこなせていません
  • Caffeine
  • Pop Shell
    • 後述

pop shell

タイルウィンドウにしたかったので導入。
自動サイジング楽で慣れると結構快適です。

先代はインストール失敗して使えなかったのですが何でだろ?
同じバージョンでもアップデートからだとどこかの構成が違うんだろうか。

まあ使えたのでOKです。 タイリングの画像は撮ってませんでした。

というわけで一旦こんな感じです。

今までのデスクトップデザインを踏襲しつついい感じです。
ちなみにサブ機ということで昔の余りディスプレイ使っているので画角は4:3ですw

pywal

背景と同じカラースキームにするツール。

❯ pip3 install pywal

-iで背景のカラースキームを読み込みます。

❯ wal -i ダウンロード/Samsho5sp_bg_gairyu_isle_2.webp
[I] image: Using image bg_gairyu2.jpg.
[I] colors: Generating a colorscheme.
[I] colors: Using wal backend.
[E] wal: Imagemagick wasn't found on your system.
[E] wal: Try another backend. (wal --backend)

パッケージ不足のエラーが出たのでisuueを見つつ以下を追加。

❯ pip3 install --user colorz
❯ sudo apt install imagemagick

再度実行。

これが...

こうなります。
本当はターミナル半透明なんだけどスクショだと地味な感じになってしまいました。

色が変わって雰囲気も変わりました。
色味が合うのはいいけど青系が見にくいな...

変更されるのは現在のターミナルだけっぽいのでzshrcに書かなくては。

まとめ

参考にした動画には遠く及びませんが使っていて楽しい環境にはなってきました。
動かしているところまで取っていないので一枚絵だと微妙かもしれませんが、いい感じになったところでここまでにします。
GTKテーマも変更したい。

いいExtentionとか入れたらまた書きます。

おまけ

  • riceについて

UnixPornをお洒落に楽しむ - おしゃれな気分でプログラミング

KDE環境をrice化する - paloma blog

Home labにGiteaをデプロイする

前回ラボのステージング環境を作りました。
本題のGit管理ツールのデプロイもOKだったので本番のラボにデプロイします。

gitea

オープンソースのGit管理ツールはGitlabくらいしか知らなかったのですが結構リソース要求が高い様です。
色々調べるとGiteaという軽量なツールがあるそうなのでこちらを採用することにしました。

about.gitea.com

composeファイル

Docker installはrootlessとrootあり版がありますが、とりあえずはroot使う用事がないのでrootless版にします。
公式のcomposeファイルで特にカスタムなしでデプロイします。

composeファイルのバージョンが2だとラボに対応していなかったので他のファイルを合わせて3.3に修正しました。

volumeのデータ置き場を統一するべきなのですがあまり設計していないのでとりあえずデフォルトです。

version: "3.3"

services:
  server:
    image: gitea/gitea:1.20.5-rootless
    restart: always
    volumes:
      - ./data:/var/lib/gitea
      - ./config:/etc/gitea
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "3000:3000"
      - "2222:2222"

ステージング環境からファイルを持ってきます。
multipass mountではディレクトリの中身が表示されないという事象に陥ったので一旦transferで持ってきました。

❯ multipass transfer dev-lab:/home/ubuntu/gitea/docker-compose.yml .
❯ ls docker-compose.yml
docker-compose.yml
❯ scp docker-compose.yml masashi@10.0.1.1:/home/masashi/gitea
docker-compose.yml    

デプロイ

ラボ側にディレクトリ準備します。(上のscp時に作成済み)

masashi@lab-docker1:~$ mkdir gitea
masashi@lab-docker1:~$ cd gitea/
masashi@lab-docker1:~/gitea$ mkdir {data,config}
masashi@lab-docker1:~/gitea$ ls
config  data

ステージングにもラボにもdocker-composeコマンドを入れていないのでswarmのserviceコマンドでデプロイ。
restartのエラーは一旦無視します。

masashi@lab-docker1:~/gitea$ docker stack deploy -c docker-compose.yml gitea
Ignoring unsupported options: restart

Creating network gitea_default
Creating service gitea_server

コンテナ起動しましたよ。

masashi@lab-docker1:~/gitea$ docker service ls
ID             NAME                  MODE         REPLICAS   IMAGE                           PORTS
29w5a2b2snfy   dokuwiki_dokuwiki     replicated   1/1        bitnami/dokuwiki:20230404       *:82->8080/tcp
plup1b33qmjy   firefly_app           replicated   1/1        fireflyiii/core:latest          *:80->8080/tcp
u0009w7ryo30   firefly_db            replicated   1/1        mariadb:latest                  
xj6n7hbbhowc   gitea_server          replicated   1/1        gitea/gitea:1.20.5-rootless     *:2222->2222/tcp, *:3000->3000/tcp
luna6ms9v7bi   pi-hole_pihole        replicated   1/1        pihole/pihole:latest            *:53->53/tcp, *:81->80/tcp, *:53->53/udp
fyijaxnx2903   portainer_agent       global       2/2        portainer/agent:2.18.2          
ddrxmnjz9is4   portainer_portainer   replicated   1/1        portainer/portainer-ce:2.18.2   *:8000->8000/tcp, *:9000->9000/tcp, *:9443->9443/tcp

アクセス & 初期設定

Giteaにアクセスします。

初期設定画面が表示されました。
サイト名だけ変えてあとはデフォルト。

ユーザ登録は割愛して、リポジトリを作成します。

リポジトリにpush

ツール置いてあるディレクトリからcommit、pushします。
これはメイン機WindowsのWSL上にあります。

今は中身こんな感じ。
txtファイルはメモ、APIトークン、API投入コマンド系なので.gitignoreに入れておきます。
csvファイルは支払いデータですが一応管理対象にするか。(家の環境だからできることですね)

masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ ls
 ,      202301.csv   202304.csv   202307.csv   Mar.txt   command.txt          jul.txt   post.txt     transactionapi.py
 2021   202302.csv   202305.csv   202308.csv   apr.txt   feb.txt              jun.txt   salary.txt
 2022   202303.csv   202306.csv   202309.csv   aug.txt  'firefly token.txt'   may.txt   sep.txt
初期化

初回に表示されるアナウンスに沿っていきます。

masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ touch README.md
masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ git init
Initialized empty Git repository in /mnt/c/Users/masashi/tools/Household-account/.git/
masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ git checkout -b main
Switched to a new branch 'main'
masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ git status
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        ,
        .gitignore
        2021/
        2022/
        202301.csv
        202302.csv
        202303.csv
        202304.csv
        202305.csv
        202306.csv
        202307.csv
        202308.csv
        202309.csv
        README.md
        transactionapi.py

nothing added to commit but untracked files present (use "git add" to track)
commit

addします。
txtが除外されてるので.gitignoreもOK。

masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ git add *
The following paths are ignored by one of your .gitignore files:
Mar.txt
apr.txt
aug.txt
command.txt
feb.txt
firefly token.txt
jul.txt
jun.txt
may.txt
post.txt
salary.txt
sep.txt
Use -f if you really want to add them.

commitします。

masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ git commit -m "first commit"
[main (root-commit) 6c897cc] first commit
 34 files changed, 1283 insertions(+)
()
リモートリポジトリ設定

giteaを指定。

masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ git remote add origin http://10.0.1.1:3000/masashi/Household-account.git
masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ git remote -v
origin  http://10.0.1.1:3000/masashi/Household-account.git (fetch)
origin  http://10.0.1.1:3000/masashi/Household-account.git (push)
push

これでやっとpush。

masashi@DESKTOP-HBP3520:/mnt/c/Users/masashi/tools/Household-account$ git push -u origin main
Username for 'http://10.0.1.1:3000': masashi
Password for 'http://masashi@10.0.1.1:3000':
Enumerating objects: 38, done.
Counting objects: 100% (38/38), done.
Delta compression using up to 4 threads
Compressing objects: 100% (37/37), done.
Writing objects: 100% (38/38), 17.53 KiB | 326.00 KiB/s, done.
Total 38 (delta 30), reused 0 (delta 0)
remote: . Processing 1 references
remote: Processed 1 references in total
To http://10.0.1.1:3000/masashi/Household-account.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.

OK。

giteaにも登録されました。

執筆中に気づいたのですが謎の,ディレクトリをcommitしてしまいました。
後で消しておこう。

まとめ

ついに家にもgit環境が作成されました!
まあそんなに更新することはなさそうですが家計簿ツールのバージョン管理 + 保存先ができたので一旦安心。
今後はgiteaのデータを守らないといけませんね。

いきなりやらかしましたが自宅環境だからと不要ファイル、大切なファイルまでcommitしない様に意識したいところです。

前回ステージング環境を作ったことでこれのデプロイテストもできて手順が確立できたので簡単にデプロイでしました。
作ってよかった。

コンテナも増えてきたのでそろそろプロキシ導入も検討します。

Home labのステージング環境をmultipassで作成する

自宅で作っているツール類でGithubとかに公開したくないもの(家計簿系)をどうするかなあと考えていたのですが、今更ローカルにGitlabか何か立てればいいじゃんと思い立ちました。

プライベートリポジトリでいいじゃんという話は置いておいて、気軽に使える環境あってもいいかなと思って自宅ラボにも1つ作ることにしました。

ラボのDocker swarmにデプロイすれば終わりの話ですが、まずは本番稼働前にステージング環境で試さないといけません。

Dockerのステージング環境をどうするか

Docker swarm環境をもう1つほしいところですがラボは省スペースPCなのでリソースが潤沢にあるわけではありません。

案としては

  1. Docker swarmにステージング用のNWを作ってそこで動かす
  2. 別の筐体にスタンドアロンDockerを立てる

くらいでしょうか。

ステージング環境もクラスタで動かしたいので案1としたいところですが、本番と同じyamlファイルを使うべきと考えますので案2の1台でswarmを動かしてステージング環境としたいと思います。

ちょうど最近NUCをサブ機デスクトップとしてお迎えしたのでこの機器で稼働させます。

仮想環境としてmultipassを使ってみる

サブ機はUbuntuを使っていて直にインストールしてもいいのですが別の検証用にmicrok8sを入れてしまったのでこれ以上環境を汚したくありません。

なので仮想環境上に作ろうと思います。 先代サブ機ではKVMを使っていましたがmultipassなる管理ツールがあるので今回はこちらの検証も兼ねつつ作成したいと思います。

multipassインストール

ドキュメント通りにインストール。

❯ sudo snap install multipass
[sudo] masashi のパスワード: 
multipass 1.12.2 from Canonical✓ installed

これでOK。

ゲスト作成

OS問わず同じコマンドで仮想環境を管理できるツールなのですが、イメージがUbuntuのみだそうです。
私は普段から使っていますので特に問題はありません。
CLIで仮想環境を管理するとなるとVagrantとかの類ということですね。

ゲストを作成します。

❯ multipass launch --name dev-lab
Launched: dev-lab

❯ multipass list
Name                    State             IPv4             Image
dev-lab                 Running           10.153.193.26    Ubuntu 22.04 LTS

❯ multipass exec dev-lab -- lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.3 LTS
Release:    22.04
Codename:   jammy


❯ multipass info dev-lab
Name:           dev-lab
State:          Running
IPv4:           10.153.193.26
Release:        Ubuntu 22.04.3 LTS
Image hash:     5bed3f233c24 (Ubuntu 22.04 LTS)
CPU(s):         1
Load:           0.01 0.18 0.12
Disk usage:     1.4GiB out of 4.8GiB
Memory usage:   158.7MiB out of 951.9MiB
Mounts:         --

リソース指定もできるのですがデフォルトだと1CPU、メモリ1G、ディスク5Gですね。

LXDの様な使い勝手と聞いていましたがコマンドがまさにまんまですね。
昔はLXD使っていたので楽勝です。

Dockerインストール

仮想マシンに入ってインストール。 ラボとバージョンを合わせるため使用中のバージョンを探して指定します。

これでリポジトリ内のインストールできるバージョンを探して、

sudo apt-cache policy docker-ce
sudo apt-cache policy docker-ce-cli
sudo apt-cache policy containerd.io
sudo apt-cache policy docker-buildx-plugin
sudo apt-cache policy docker-compose-plugin

バージョン指定でインストール。

ubuntu@dev-lab:~$ sudo apt-get install docker-ce=5:23.0.5-1~ubuntu.22.04~jammy docker-ce-cli=5:23.0.5-1~ubuntu.22.04~jammy containerd.io=1.6.20-1 docker-buildx-plugin=0.10.4-1~ubuntu.22.04~jammy docker-compose-plugin=2.17.3-1~ubuntu.22.04~jammy

準備OKです。

ubuntu@dev-lab:~$ docker version
Client: Docker Engine - Community
 Version:           23.0.5
 API version:       1.42
 Go version:        go1.19.8
 Git commit:        bc4487a
 Built:             Wed Apr 26 16:21:07 2023
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          23.0.5
  API version:      1.42 (minimum version 1.12)
  Go version:       go1.19.8
  Git commit:       94d3ad6
  Built:            Wed Apr 26 16:21:07 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.20
  GitCommit:        2806fc1057397dbaeefbea0e4e17bddfbd388f38
 runc:
  Version:          1.1.5
  GitCommit:        v1.1.5-0-gf19387a
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

シングルですがswarmを有効化。

ubuntu@dev-lab:~$ docker swarm init
Swarm initialized: current node (znqyqxen2v6xibh0zhjm44qb1) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-3xgnkwpu6yww50hr5cqrn30kxe7fpmr19uxjv4uxpgci152e13-6jhfcbh93zbs4sgpwguxc6l6a 10.153.193.26:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

ubuntu@dev-lab:~$ docker node ls
ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS   ENGINE VERSION
znqyqxen2v6xibh0zhjm44qb1 *   dev-lab    Ready     Active         Leader           23.0.5

これで一応同じ環境ができました。

まとめ

とりあえず今回はここまでにします。
ただの仮想環境を作っただけなのであまりコメントすることはないですね。
次回Git系ツールをデプロイしたいと思います。

これに絡んだ話でcloud-initというVM作成時にパッケージインストール等行ってくれるツールがあるのですが、multipassと合わせて実行するという合わせ技がありました。
VMを複製せずともコンテナの様に同じ環境の仮想マシンがボンボンと作れるということですね。
ありがたいことにもう何でも自動化の世界です。
cloud-initもいつか試したい。

ラボ

My kickass home labを構築しました - paloma blog