ヒスねこTechBlog

日々の気になる技術をまとめてます。

FlaskとMySQL触ったメモ

Ubuntu20.04で動かしたときのメモです。

環境構築

pipでFlaskを入れるだけですが、Flaskの拡張機能であるflask-sqlalchemyとflask-migrate、flask-wtfも追加で入れました。

$ pip3 install flask flask-sqlalchemy flask-migrate \
flask-wtf email-validator flask-login PyMySQL

DBとしてMySQLを使いたかったので、必要なものをインストールします。

$ sudo apt install mysql-server mysql-client python3-mysqldb

MySQLの初期設定を行います。好みの設定があれば適宜変更してください。

$ sudo mysql_secure_installation
Press y|Y for Yes, any other key for No: y
Please enter 0 = LOW, 1 = MEDIUM and 2 = STRONG: 2
New password: 

Re-enter new password: 

Estimated strength of the password: 100 
Do you wish to continue with the password provided?(Press y|Y for Yes, any other key for No) : y
 ... Failed! Error: SET PASSWORD has no significance for user 'root'@'localhost' as the authentication method used doesn't store authentication data in the MySQL server. Please consider using ALTER USER instead if you want to change authentication parameters.

途中で↑のように怒られた場合はDo you wish~のところでCtrl+Cを押すと抜け出せます。そして下を実行。最後のyour passwordとしているところは適宜変更してください。

$ sudo mysql
mysql> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your password';
mysql> exit

続きの設定をしていきます。

$ sudo mysql_secure_installation
Change the password for root ? ((Press y|Y for Yes, any other key for No) : no
Remove anonymous users? (Press y|Y for Yes, any other key for No) : y
Disallow root login remotely? (Press y|Y for Yes, any other key for No) : y
Remove test database and access to it? (Press y|Y for Yes, any other key for No) : y
Reload privilege tables now? (Press y|Y for Yes, any other key for No) : y
All done!

設定完了したら次のコマンドでログインして、各種操作ができることを確認しましょう。

$ mysql -u root -p

# ユーザを作る場合
mysql> CREATE USER 'username'@'localhost' IDENTIFIED BY 'password';
# ユーザにデータベースへのアクセスを許可
mysql> GRANT ALL ON databasename.* TO 'username'@'localhost';
データベース初期化・マイグレーション設定
export FLASK_APP=apps.main.py
export FLASK_ENV=development

flask db init
flask db migrate
起動

単にflask runだとlocalhostにバインドされるので、例えばLAN内の他の機器からアクセスしたければ自身のローカルIPやポートをオプションで指定します。

$ export FLASK_APP=main.py
$ export FLASK_ENV=development
$ flask run -h 192.168.0.xxx -p 5000
(おまけ)トラブルシューティング

flask db migrateで以下のエラーに遭遇した。

sqlalchemy.exc.OperationalError: (MySQLdb._exceptions.OperationalError) (1045, "Access denied for user 'user'@'localhost' (using password: NO)")

stackoverflowにはいくつか解決方法が提示されていましたが、自分の場合はSQLALCHEMY_DATABASE_URIで指定したURIが誤っていました。

stackoverflow.com

dockerコンテナとkvm仮想マシンを物理ネットワークに接続

かなり奮闘したので...

ブリッジの作成

物理NICの名称がeth0とします。

nmcliはsudo apt install network-managerすると使用できます。

# ブリッジ(testbr0)作成
$ nmcli con add type bridge ifname testbr0
# ブリッジを物理NIC接続
$ nmcli con add type bridge-slave ifname eth0 master testbr0
# ブリッジ有効化
$ nmcli con up bridge-slave-eth0
dockerコンテナの接続

dockerは比較的すぐに接続できます。

# dockerネットワーク(testbr0-docker)を作成
$ docker network create --driver macvlan --subnet=192.168.0.0/16 -o parent=testbr0 testbr0-docker
# この時点でコンテナは接続できるはず
$ docker run -it --rm --net testbr0-docker alpine /bin/sh
KVM仮想マシンの接続

次にKVM(virsh) の場合、まずネットワークを定義します。

direct.xmlは以下。

<network>
  <name>direct</name>
  <forward mode="bridge">
    <interface dev="testbr0"/>
  </forward>
</network>

ネットワークをdefineしたのち、仮想マシンのネットワーク接続先をdirectになるように編集します。

# libvirtネットワーク作成
$ virsh net-define direct.xml
$ virsh net-start direct
$ virsh edit <仮想マシンの名前>
# xml編集画面が開くので、source network='direct'に変更する
----(省略)----
<interface type='network'>                                                                                                                                                                         
  <mac address='52:54:00:7e:1a:31'/>
  <source network='direct'/>
  <model type='virtio'/>
  <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
</interface>
ブリッジの削除
$ visrh net-undefine direct
$ docker network rm testbr0-docker
$ nmcli con delete bridge-slave-eth0
$ nmcli con delete bridge-testbr0
(おまけ)dockerでブリッジを作成する場合

次のコマンドは、ブリッジの作成とdockerネットワークの作成までやってくれます。KVMがこのネットワーク配下に入れるかは試してませんが、ご参考程度に。

$ docker network create --driver bridge \
    --subnet=192.168.0.0/16 --gateway=192.168.0.2 \
    --opt="com.docker.network.bridge.name"="testbr0" testbr0

Kubernetesのcontrol-planeでpodを動かす

下記のようにkubeadmで設定した場合、control-planeを担うnodeの名称はmainになります。

$ sudo kubeadm init --node-name main --pod-network-cidr=xxx.xxx.xxx.xxx/xx

確認します。もちろんmainはcontrol-planeであると表示されます。

$ kubectl get nodes
NAME   STATUS   ROLES           AGE   VERSION
main   Ready    control-plane   86m   v1.24.4

ただ、このままpodを作成してもPendingしてしまって動きません。

$ kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
nginx-deployment-6595874d85-7mdxv   0/1     Pending   0          82s
nginx-deployment-6595874d85-826tr   0/1     Pending   0          82s

色々調べたところ、Taintが設定されているとそのノードにはpodがスケジューリングされないとのこと。Node affinityの逆の機能にあたるようです。

$ kubectl describe node main | grep Taints
Taints:             node-role.kubernetes.io/control-plane:NoSchedule

そこでTaintを解除してあげるとpodがRunningになりました。ここではnodesに--allを指定していますが、単体のnodeに対して操作する場合は、その名前を代わりに入れてあげます。

$ kubectl taint nodes --all node-role.kubernetes.io/control-plane-
node/main untainted

PythonでDocker操作お試し

Docker SDK for PythonでDockerを操作してみたメモです。

環境構築

今回の環境はUbuntu20.04です。インストールはDocker公式ページを参照します。

docs.docker.com

また、ユーザをグループに属させることでsudoなしでdockerを実行するようにします。

# 以下実行後は再起動のこと。
# すぐにdockerを実行しようとしてもpermission deniedと怒られる。
sudo gpasswd -a <ユーザ名> docker

Docker SDK for Pythonは次のコマンドでインストールします。

pip3 install docker
PythonでDocker

Dockerの扱いに慣れていれば、Docker SDK for Python公式ページを参照するくらいで、基本的にあまり困らなそうです。

docker-py.readthedocs.io

まずはdocker run相当。コンテナの名前をつける&デタッチする場合は下記のような形。

import docker
client = docker.from_env()

# docker run
cont = client.containers.run(
    'Dockerイメージ名', 
    '実行コマンド', 
    name='コンテナの名前', 
    detach=True
)
# コンテナIDを表示
print(cont.id)

コンテナのIDからコンテナを取得し、操作する場合。

import docker
client = docker.from_env()

# IDからコンテナを取得
cont = client.containers.get('コンテナID(SHA256形式)')
# 例) docker startする
cont.start()
# 例) docker stopする
cont.stop()
# 例) docker container rmする
cont.remove()

swarmも扱えるようなので、次回はそれを使って少し実験してみたいと思います。

Flask小技メモ

この記事は随時更新していきます。

WTFormsで動的なSelectField

例えば下記のようなクラスになっていて、データベース上にあるresourceのidとnameを取得してきて、idをフォームの値とし、nameを選択肢として出したい。

from flask_wtf import FlaskForm
from wtforms import SelectField

class SampleForm(FlaskForm):
    resource_id = SelectField(
        "Resource",
        coerce=int
    )
    submit = SubmitField("送信")

この場合、view側としては次のようにすれば、データベースから取得したものをフォームの選択肢として並べることができる。(flaskでのresourceのモデルはResourcesクラスで定義しているものとする)

@app.route("/", methods=["GET", "POST"])
def index():
    form = SampleForm()
    form.resource_id.choices = [
        (r.id, r.name) for r in Resources.query.all()
    ]

参考: WTF SelectField with dynamic choice · GitHub

KVMとOVSでネットワーク接続

N番煎じ感がありますが、OVS(Open vSwitch)とKVMの組み合わせを試してみます。

環境はUbuntu 20.04(x86_64)です。

ブリッジの作成と接続

まずはOVSのパッケージをインストールしておきます。

$ sudo apt install openvswitch-common openvswitch-switch

早速作成していきます。NICがひとつしかない環境なので、ipコマンドで仮想インタフェースを作成してこれとブリッジを結びつけようかと画策していました。ちょっとコマンド調べてみたり。

仮想ネットワーク周りのipコマンドメモ - ヒスねこTechBlog

...がしかし、IPアドレスの割当とリンクアップをしてもパケットが外に出ていかなかったので、また今度チャレンジします...

今回はとりあえず、USB-Ethernetアダプタを使って物理インタフェースを増やしました。構成イメージとしては下の画像のような感じ。

# ブリッジの作成とネットワークインタフェースへの接続
# ブリッジ名はbr0としました
$ sudo ovs-vsctl add-br br0
$ sudo ovs-vsctl add-port br0 インタフェース名
# 既存のVM設定を編集してbr0に接続
$ virsh edit ドメイン名

最後のコマンドでエディタが開くので、下記のinterfaceタグのあたりを変更します。(左側の数字はエディタの行番号なので読み捨ててください)

100     <interface type='network'>
101       <mac address='52:54:00:a5:bb:fa'/>
102       <source network='default'/>                                                                                                                                                                      
103       <model type='virtio'/>
104       <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
105     </interface>

変更点は以下です。

  • interface typeをbirdgeに変更
  • source を作成したブリッジ名に変更
  • virtualport type='openvswitch'を追加
100     <interface type='bridge'>         
101       <mac address='52:54:00:a5:bb:fa'/>
102       <source bridge='br0'/>         
103       <virtualport type='openvswitch'>
104       </virtualport>                                                                                                                                                                 
105       <model type='virtio'/>          
106       <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
107     </interface>

もういちどvirsh editしてみると、virtualportタグ中にinterfaceidが自動で追加されていると思います。

100     <interface type='bridge'>                                                                                                                                                                          
101       <mac address='52:54:00:a5:bb:fa'/>
102       <source bridge='br0'/>
103       <virtualport type='openvswitch'>
104         <parameters interfaceid='565391c0-86a4-4b27-9849-06698f5f8674'/>
105       </virtualport>
106       <model type='virtio'/>
107       <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
108     </interface>

この状態でVMを起動すると、めでたくOVSのブリッジを介してホストの外へ通信できます。

今後試したいリスト...

  • ブリッジ永続化
    • /etc/sysconfig/network-scripts/の下に設定ファイルを置けばよいらしい
  • 仮想インタフェースでの通信
  • VMのdefine時にOVSをインタフェースとして設定

仮想ネットワーク周りのipコマンドメモ

あんまりネットワークをガチガチにやってこなかったのもあって、ipコマンドで色々試したことを備忘録として残します。

以下、権限によってsudo付加が必要。また、リブートすると設定は解除される。永続化したい場合は静的ルーティングを設定してあげなければならない。静的ルーティングに関してはまた別の機会にトライします。

仮想インタフェース

実インタフェースに対して仮想インタフェースを作成する

$ ip link add link 実インタフェース名 address MACアドレス 仮想インタフェース名 type macvlan

仮想インタフェースを削除する場合は以下

$ ip link del link 実インタフェース名 address MACアドレス 仮想インタフェース名
IPアドレス

実(仮想)インタフェースにIPアドレスを割り当てる

$ ip addr add IPアドレス/サブネットマスク dev インタフェース名

割り当てたIPアドレスを削除する

$ ip addr del IPアドレス/サブネットマスク dev インタフェース名
IPエイリアス

同じMACアドレスについて複数IPを割り当てる

$ ip addr add local IPアドレス/サブネットマスク dev インタフェース名 label インタフェース名:ラベル名

割り当てたIPエイリアスを削除する

$ ip addr delete local IPアドレス/サブネットマスク dev インタフェース名 label インタフェース名:ラベル名