一つのdocker-compose.ymlで2つの環境用の設定をする
あまり真剣にdocker-composeを使うことがなかったけど、フロントエンドとバックエンドとDBを同じdocker-composeで立ててるとき、リクエストが遅くなります。
そのため、フロントはnginxで動かそうかなとか考えるのですが、いちいちビルドする必要あるし、めんどくさいなーと思ってました。
ローカルを汚したくないからとりあえず実行環境はdockerで考えてる人は多いかと思います。
ビルドとかするときはどうするかまだ迷ってますが、profilesを書くことで、普段はprofilesをセットすることで、
$ docker-compose --profile dev up --build
のように --profile オプションをつけることで自動で切り替えられます。
今回はdevとprodと2つのprofilesを設定しています。
docker-compse.ymlも以下のような書き方で起動するコンテナを設定できます。
services:
front:
profiles: ["dev"]
container_name: "front"
depends_on:
- api
volumes:
- ./front:/app/front
- ./scripts:/app/scripts:cached
build:
context: ./docker/front
dockerfile: Dockerfile
working_dir: /app/scripts
command: bash -c "sh front.sh"
ports:
- 5173:5173
env_file:
- .env_front
nginx:
profiles: ["prod"]
container_name: "nginx"
depends_on:
- api
volumes:
- ./front/dist:/var/www/html/
- ./docker/nginx/server.conf:/etc/nginx/nginx.conf
build:
context: ./docker/nginx
dockerfile: Dockerfile
ports:
- 80:80
api:
profiles: ["dev","prod"]
container_name: "api"
depends_on:
- db
volumes:
- ./app:/workspace/app
- ./scripts:/workspace/scripts:cached
build:
context: ./docker/api
dockerfile: Dockerfile
working_dir: /workspace/scripts
command: bash -c "sh run.sh"
ports:
- 8080:8080
networks:
- fastapi_network
environment:
APP_ENV: "development"
TZ: "Asia/Tokyo"
env_file:
- .env
中学受験 ~算数の逆算~
小4の息子が中学受験したいと言い出した(それ自体は良いこと)ので、塾を探して塾に入って勉強を始めた。
夏休み明けからということで、たぶん受験勉強開始したのは遅い方だろう。それは仕方ないが本人のやる気もあり、それを止める理由もそこまでないなと思っている。
そして、塾に入って宿題がたくさん出てたのでと、中学生以降の知識をフル活用すればさほど難しくない計算も小学生の知識でどうやって解くか?を親として考えてしまう問題も多々ある。そんな感じなので、子供が勉強してる横で算数についてはどう解くのか勉強することにしたのでやったことをまとめます。
逆算
逆算ってなに?って思うかともいるかもしれませんが、 □が入った計算を四則演算の優先度を逆から順番にやっていくものです。
例えば、5+(10-□)×2=21 とあった場合、
最初に ()内の 10-□が優先度的に最初で、次に(10-□)×2、最後に5を足すという四則演算がありますが、□があるので計算ができません。
ついでに、小学生は左辺を右辺に移すという概念ではないので、
- 四則演算の優先順を逆からやっていく
- 記号は反転(✖️↔︎➗, ➕↔︎マイナス)と置き換える
ここでは、
1.21-5=16
2.16÷2=8
3 10-8=2
となります。さて、ここで疑問に持たれる方もいますが、
引き算についても基本的には➖をプラスにしますが、そうでないケースがあります。
それは、 10-□=8 のようなケースです。これは 10-2=8となるのですが、□を求めるだけなら
10-8=2と簡単にできます。
しかし、ここでルールにのっとってやろうとすると、
10+8=18となります。
これでは間違った答えが出てしまいます。
なので、引き算で引かれる数がわかっているときに限り、□と=の右にある数字を入れ替えます。
また、□-2=8の引かれる数がわからないケースは、8+2=10と、基本のルールに則った計算をします。
ルール
- 引かれる数がわかってる時は、□と答えを入れ替える
- 引かれる数がわからない時は、➖を➕にする。
これらのルールを守れば基本的に間違わないはずです。
子供と算数の復習しつつ、勉強するのは面白いですね。
それはそうと、最近の小学校の算数の比例・反比例でxを使うようになったそうですね。
□もxに置き換えて色々できると便利そうですが、そうはなってないので、縛りがあって面白いなと思いました。
dockerにmysqlclientを入れようとするとエラーになる
fastapiとmysqlをdocker-composeで起動しようとしてたら以下のようなエラーがでた。 mysqlclientをインストールする時のエラーだ。
5.468 Collecting sqlalchemy_utils (from -r requirements.txt (line 6)) 5.475 Downloading sqlalchemy_utils-0.42.0-py3-none-any.whl.metadata (4.6 kB) 5.494 Collecting mysqlclient (from -r requirements.txt (line 7)) 5.500 Downloading mysqlclient-2.2.7.tar.gz (91 kB) 5.513 Installing build dependencies: started 7.912 Installing build dependencies: finished with status 'done' 7.912 Getting requirements to build wheel: started 8.046 Getting requirements to build wheel: finished with status 'error' 8.048 error: subprocess-exited-with-error 8.048 8.048 × Getting requirements to build wheel did not run successfully. 8.048 │ exit code: 1 8.048 ╰─> [35 lines of output] 8.048 /bin/sh: 1: pkg-config: not found 8.048 /bin/sh: 1: pkg-config: not found 8.048 /bin/sh: 1: pkg-config: not found 8.048 /bin/sh: 1: pkg-config: not found 8.048 Trying pkg-config --exists mysqlclient 8.048 Command 'pkg-config --exists mysqlclient' returned non-zero exit status 127. 8.048 Trying pkg-config --exists mariadb 8.048 Command 'pkg-config --exists mariadb' returned non-zero exit status 127. 8.048 Trying pkg-config --exists libmariadb 8.048 Command 'pkg-config --exists libmariadb' returned non-zero exit status 127. 8.048 Trying pkg-config --exists perconaserverclient 8.048 Command 'pkg-config --exists perconaserverclient' returned non-zero exit status 127. 8.048 Traceback (most recent call last): 8.048 File "/usr/local/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 389, in <module> 8.048 main() 8.048 ~~~~^^ 8.048 File "/usr/local/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 373, in main 8.048 json_out["return_val"] = hook(**hook_input["kwargs"]) 8.048 ~~~~^^^^^^^^^^^^^^^^^^^^^^^^ 8.048 File "/usr/local/lib/python3.13/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 143, in get_requires_for_build_wheel 8.048 return hook(config_settings) 8.048 File "/tmp/pip-build-env-bl45y4e9/overlay/lib/python3.13/site-packages/setuptools/build_meta.py", line 331, in get_requires_for_build_wheel 8.048 return self._get_build_requires(config_settings, requirements=[]) 8.048 ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 8.048 File "/tmp/pip-build-env-bl45y4e9/overlay/lib/python3.13/site-packages/setuptools/build_meta.py", line 301, in _get_build_requires 8.048 self.run_setup() 8.048 ~~~~~~~~~~~~~~^^ 8.048 File "/tmp/pip-build-env-bl45y4e9/overlay/lib/python3.13/site-packages/setuptools/build_meta.py", line 317, in run_setup 8.048 exec(code, locals()) 8.048 ~~~~^^^^^^^^^^^^^^^^ 8.048 File "<string>", line 156, in <module> 8.048 File "<string>", line 49, in get_config_posix 8.048 File "<string>", line 28, in find_package_name 8.048 Exception: Can not find valid pkg-config name. 8.048 Specify MYSQLCLIENT_CFLAGS and MYSQLCLIENT_LDFLAGS env vars manually 8.048 [end of output] 8.048 8.048 note: This error originates from a subprocess, and is likely not a problem with pip. 9.171 error: subprocess-exited-with-error 9.171 9.171 × Getting requirements to build wheel did not run successfully. 9.171 │ exit code: 1 9.171 ╰─> See above for output.
とエラーでてた。 原因調べると、パッケージが足りないようなので、
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
pkg-config \
default-libmysqlclient-dev
mysqlは以上のようなパッケージが必要になる。
alembicで生成したマイグレーションファイルにsqlmodelがimportされてなかった
alembic で マイグレーションファイルを作成した後、データベースを反映させようとしたところ、以下のようなエラーが出た。 fastapi + alembic + sqlmodel + sqlalchemyと使っていますが、sqlmodelがimportされず、実行時にエラーが出てた。
# python -m alembic upgrade head
mysql+mysqldb://{username}:{password@{hostname}:3306/{database}?charset=utf8mb4
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running upgrade -> 88e53880a27c, create todos table
Traceback (most recent call last):
File "<frozen runpy>", line 198, in _run_module_as_main
File "<frozen runpy>", line 88, in _run_code
File "/root/.local/lib/python3.13/site-packages/alembic/__main__.py", line 4, in <module>
main(prog="alembic")
~~~~^^^^^^^^^^^^^^^^
File "/root/.local/lib/python3.13/site-packages/alembic/config.py", line 1022, in main
CommandLine(prog=prog).main(argv=argv)
~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^
File "/root/.local/lib/python3.13/site-packages/alembic/config.py", line 1012, in main
self.run_cmd(cfg, options)
~~~~~~~~~~~~^^^^^^^^^^^^^^
File "/root/.local/lib/python3.13/site-packages/alembic/config.py", line 946, in run_cmd
fn(
~~^
config,
^^^^^^^
*[getattr(options, k, None) for k in positional],
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**{k: getattr(options, k, None) for k in kwarg},
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/root/.local/lib/python3.13/site-packages/alembic/command.py", line 483, in upgrade
script.run_env()
~~~~~~~~~~~~~~^^
File "/root/.local/lib/python3.13/site-packages/alembic/script/base.py", line 549, in run_env
util.load_python_file(self.dir, "env.py")
~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
File "/root/.local/lib/python3.13/site-packages/alembic/util/pyfiles.py", line 116, in load_python_file
module = load_module_py(module_id, path)
File "/root/.local/lib/python3.13/site-packages/alembic/util/pyfiles.py", line 136, in load_module_py
spec.loader.exec_module(module) # type: ignore
~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^
File "<frozen importlib._bootstrap_external>", line 1026, in exec_module
File "<frozen importlib._bootstrap>", line 488, in _call_with_frames_removed
File "/workspace/app/db/migration/env.py", line 68, in <module>
run_migrations_online()
~~~~~~~~~~~~~~~~~~~~~^^
File "/workspace/app/db/migration/env.py", line 63, in run_migrations_online
context.run_migrations()
~~~~~~~~~~~~~~~~~~~~~~^^
File "<string>", line 8, in run_migrations
File "/root/.local/lib/python3.13/site-packages/alembic/runtime/environment.py", line 946, in run_migrations
self.get_context().run_migrations(**kw)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
File "/root/.local/lib/python3.13/site-packages/alembic/runtime/migration.py", line 627, in run_migrations
step.migration_fn(**kw)
~~~~~~~~~~~~~~~~~^^^^^^
File "/workspace/app/db/migration/versions/88e53880a27c_create_todos_table.py", line 26, in upgrade
sa.Column('title', sqlmodel.sql.sqltypes.AutoString(length=200), nullable=False),
^^^^^^^^
NameError: name 'sqlmodel' is not defined
結局そういうこういうエラーなので、importしてあげるしかないので以下のような修正を行なった
# diff -u app/db/migration/versions/88e53880a27c_create_todos_table.py.org app/db/migration/versions/88e53880a27c_create_todos_table.py --- app/db/migration/versions/88e53880a27c_create_todos_table.py.org 2025-09-09 21:54:23 +++ app/db/migration/versions/88e53880a27c_create_todos_table.py 2025-09-09 21:50:55 @@ -9,6 +9,7 @@ from alembic import op import sqlalchemy as sa +import sqlmodel # revision identifiers, used by Alembic.
Ansibleのfirewalldで特定ホストのIPをセットする
firewalldで制限をかける設定をするとき、addressはhostnameに対応してません。(ufwは確認してないですが、hostname対応してないっぽいので同じ方法で対応できるかと思います。)
送信元アドレスを指定することにより、接続試行の発信元を送信元アドレスに制限できます。ソースアドレスまたはアドレス範囲は、IPv4 または IPv6 のマスクを持つ IP アドレスまたはネットワーク IP アドレスのいずれかです。IPv4 の場合、マスクはネットワークマスクまたは単純な番号になります。IPv6 の場合、マスクは単純な番号です。ホスト名の使用はサポートされていません。NOT キーワードを追加することで、source address コマンドの意味を反転させることができます。指定されたアドレス以外はすべて一致します。
引用元: docs.redhat.com
環境が1つだったらIPを直書きできますが、複数環境ある場合はそうはいきません。
そのため、digを使用します。digは手元で動くので、dnspythonをインストールしておく必要があります。 その上で以下のように書くとhostnameでもセットできるようになります。
- name: host AとB からsshを許可
ansible.posix.firewalld:
zone: public
rich_rule: "{{ item }}"
permanent: true
immediate: true
state: enabled
with_items:
- rule family="ipv4" source address="{{ lookup('dig', 'hostA') }}" port port="22" protocol="tcp" accept
- rule family="ipv4" source address="{{ lookup('dig', 'hostB') }}" port port="22" protocol="tcp" accept
このhostAやhostBは {{ host_something }} のように変数を入れることも可能です。 これでhostnameに環境依存のところを環境の部分だけ変数にしたりして対応してかけますね。
PDF パスワード解除スクリプトの作成
最近パスワードのかかったpdfファイルをよく受け取るので、pdfのパスワードを削除するスクリプトを書いた。
import pypdf
SRC_PDF = 'password.pdf' #パスワードがかかったPDF
DST_PDF = 'non_password.pdf' #パスワードを解除したPDF
PASSWORD = '' #PDFのパスワード
def delete_password(src_pdf, dst_pdf, src_password):
src_pdf_file = pypdf.PdfReader(src_pdf)
src_pdf_file.decrypt(src_password)
dst_pdf_file = pypdf.PdfWriter()
dst_pdf_file.clone_reader_document_root(src_pdf_file)
d = {key: src_pdf_file.metadata[key] for key in src_pdf_file.metadata.keys()}
dst_pdf_file.add_metadata(d)
dst_pdf_file.write(dst_pdf)
delete_password(SRC_PDF, DST_PDF, PASSWORD)
pypdfでパスワードのかかったファイルのパスワードを解除します。
パスワードのかかったpdfのファイル名をSRC_PDFにセットし、
新しいファイル名をDST_PDF、
パスワードをPASSWORDにセットすれば、パスワードのかかってないファイルがDST_PDFにセットしたファイル名で出力されます。
フロントエンドでVueを勉強するときにChatGPTにエラー対応させたらだいぶ楽だった
フロントエンドはとくに流れが早く、全然わからん!という感じだったので、とりあえずなんか昔同僚が触ってたVue.jsで使って何か作ろうと思いました。
最近はChatGPTなど便利なツールもあるので、やらない理由がどんどんなくなってきていて素晴らしいなーと思っています。
怠け者の自分でも、めんどくささがなくなったなーと感心してます。
それでも新しいこと始めるのはめんどくさいのでガンガンChatGPTを使っていきました。新しいこと始めるコストはないに等しいなと思います。 でも何かしらのプログラミング言語をやったことない人が頼ると少し壁はあるのかな?という感じです。
トラップを回避してく感があるぐらいの人ならガンガンできるでしょう。
言語とかのセットアップも最近はめんどくさいので Dockerに乗っけてガンガン動かしてます。
言語のバージョンとかパッケージの管理とか毎回スクラップアンドビルドで綺麗にしていけばいいんじゃないかーと思ってます。
今回勉強にはこのブログを参考にさせていただきました。
ただ、実際 フロント動かすに、axiosをfetchで書き換えたり、element plusをvuetifyに書き換えたりしてます。
axiosは脆弱性など報告が多く最近の流れ的にもfetchの方が良いかな?と思ったり、
vuetifyはレスポンシブデザインっぽいこと書かれてたので、今後もフロントエンドを自分で書くときは極力手間を減らしたいのでなんとなく良い感じになるよう選んでます。
(エラーが出たらガンガンChatGPTに聞きましょう。ググる時間がもったいないです。)
あと、バックエンドはflaskに書き換えてます。modelかけば falsk db init, migrate, upgradeとさっとできるよう flask-migrateを使うのが楽な気がしてるからです。

fetchの書き換えで、以下のような違いがあってだいぶ最初エラーが出てたけど、ChatGPT先生に聞いて、とりあえず動く状態まで持ってくと「じゃぁなんでこれがこうなってるの?」を調べたり色々試すのが簡単でした。
axios
getAll(): Promise<any> {
return http.get("/api/item");
}
create(name: string, price: number): Promise<any> {
return http.post(
`/api/item`,
{
name: `${name}`,
price: `${price}`
}
);
}
fetch
async getAll(): Promise<any> {
return httpClient("/api/item", { method: "GET" });
}
async create(name: string, price: number): Promise<any> {
const res = httpClient(
"/api/item",
{
method: "POST",
body: JSON.stringify({name: name, price: price})
});
return res
}
そして、コードを検証するため、docker-composeを準備しました。
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "8080:80"
depends_on:
- backend
networks:
- app-network
backend:
build: ./backend
ports:
- "5000:5000"
networks:
- app-network
networks:
app-network:
driver: bridge
とdocker-composeを準備して、めでたく起動はdocker-compose upだけで動くようになりました。
たまに npm run buildでエラーが出るから調べる
> [frontend build-stage 6/6] RUN npm run build: 0.146 0.146 > front-sample@0.0.0 build 0.146 > vue-tsc -b && vite build 0.146 0.148 sh: 1: vue-tsc: Permission denied ------ failed to solve: process "/bin/sh -c npm run build" did not complete successfully: exit code: 127
と権限んがなさそうなので、
対処としては、
$ chmod 755 frontend/node_modules/vue-tsc/bin/vue-tsc.js chmod: Invalid file mode: 755
と対処してあげれば動きます。
実際のコード

