2025年7月3日木曜日

MySQL8.4でレプリケーション構成を試してみた

MySQL8.4でレプリケーション構成を試してみた

概要

検証なので docker 上で構築しています
パラメータなどはそのまま使えると思います

8.4 から Master/Slave は Source/Replica となりコマンドを完全に変わっているので注意しましょう

環境

  • Ubuntu 24.04
  • docker 28.2.2
  • MySQL 8.4.5

compose.yaml

基本的には Source/Replica に必要な設定を指定して起動するだけです
mysql-native-password に関しては後述しますが指定不要にもできます

healthcheck はいらないかもです

services:
  mysql-master:
    image: mysql:8.4.5
    container_name: mysql-master
    environment:
      - MYSQL_ROOT_PASSWORD=rootpassword
    command:
      - --mysql-native-password=ON
      - --server-id=1
      - --log-bin=mysql-bin
      - --gtid-mode=ON
      - --enforce-gtid-consistency=ON
    ports:
      - "3306:3306"
    volumes:
      - master_data:/var/lib/mysql
      - ./master-init/:/docker-entrypoint-initdb.d/
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  mysql-slave:
    image: mysql:8.4.5
    container_name: mysql-slave
    depends_on:
      - mysql-master
    environment:
      - MYSQL_ROOT_PASSWORD=rootpassword
    command:
      - --mysql-native-password=ON
      - --server-id=2
      - --log-bin=mysql-bin
      - --gtid-mode=ON
      - --enforce-gtid-consistency=ON
      - --relay-log=relay-log
      - --read-only=ON
    ports:
      - "3307:3306"
    volumes:
      - slave_data:/var/lib/mysql
      - ./slave-init/:/docker-entrypoint-initdb.d/
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

volumes:
  master_data:
  slave_data:

./master-init/init-master.sql

公式の MySQL8.4.5 のイメージでは MYSQL_REPLICATION_USER と MYSQL_REPLICATION_PASSWORD の環境変数は使えないので初期化スクリプトでレプリケーション用のユーザを作成します

CREATE USER 'replicator'@'%' IDENTIFIED BY 'replicatorpassword';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

./slave-init/init-replica.sql

スレーブ側ではマスターに接続する設定を初期化スクリプトで行います
MASTER_HOST などは使えず SOURCE_HOST になっているので注意しましょう

CHANGE REPLICATION SOURCE TO
  SOURCE_HOST='mysql-master',
  SOURCE_USER='replicator',
  SOURCE_PASSWORD='replicatorpassword',
  SOURCE_AUTO_POSITION=1;
START REPLICA;

動作確認

  • docker compose up -d

であとは Source/Replica の状態を確認します

Source (マスター)

mysql -h 127.0.0.1 -P 3306 -u root -p

mysql>  SHOW BINARY LOG STATUS\G
*************************** 1. row ***************************
             File: mysql-bin.000003
         Position: 198
     Binlog_Do_DB:
 Binlog_Ignore_DB:
Executed_Gtid_Set: 705ee183-57b5-11f0-b5ae-2a2407db9148:1-8
1 row in set (0.00 sec)

Replica (スレーブ)

mysql> SHOW REPLICA STATUS\G
*************************** 1. row ***************************
             Replica_IO_State: Waiting for source to send event
                  Source_Host: mysql-master
                  Source_User: replicator
                  Source_Port: 3306
                Connect_Retry: 60
              Source_Log_File: mysql-bin.000003
          Read_Source_Log_Pos: 198
               Relay_Log_File: relay-log.000004
                Relay_Log_Pos: 415
        Relay_Source_Log_File: mysql-bin.000003
           Replica_IO_Running: Yes
          Replica_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Source_Log_Pos: 198
              Relay_Log_Space: 2985967
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Source_SSL_Allowed: No
           Source_SSL_CA_File:
           Source_SSL_CA_Path:
              Source_SSL_Cert:
            Source_SSL_Cipher:
               Source_SSL_Key:
        Seconds_Behind_Source: 0
Source_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Source_Server_Id: 1
                  Source_UUID: 705ee183-57b5-11f0-b5ae-2a2407db9148
             Source_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
    Replica_SQL_Running_State: Replica has read all relay log; waiting for more updates
           Source_Retry_Count: 10
                  Source_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Source_SSL_Crl:
           Source_SSL_Crlpath:
           Retrieved_Gtid_Set: 705ee183-57b5-11f0-b5ae-2a2407db9148:1-8
            Executed_Gtid_Set: 705ee183-57b5-11f0-b5ae-2a2407db9148:1-8,
708f9d45-57b5-11f0-b04a-d263a8c2e257:1-5
                Auto_Position: 1
         Replicate_Rewrite_DB:
                 Channel_Name:
           Source_TLS_Version:
       Source_public_key_path:
        Get_Source_public_key: 0
            Network_Namespace:
1 row in set (0.00 sec)

あとはデータベースやテーブルを作成して Replica 側でデータがあることを確認しましょう

mysql_native_password を使わないほうがいい

sha256_password も deprecated なので caching_sha2_password を使いましょう

基本的には起動オプションを変更してレプリケーション用のユーザのパスワードを設定する際に caching_sha2_password を使えば OK です

  • vim compose.yaml
services:
  mysql-master:
    image: mysql:8.4.5
    container_name: mysql-master
    environment:
      - MYSQL_ROOT_PASSWORD=rootpassword
    command:
      - --server-id=1
      - --log-bin=mysql-bin
      - --gtid-mode=ON
      - --enforce-gtid-consistency=ON
    ports:
      - "3306:3306"
    volumes:
      - master_data:/var/lib/mysql
      - ./master-init/:/docker-entrypoint-initdb.d/
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

  mysql-slave:
    image: mysql:8.4.5
    container_name: mysql-slave
    depends_on:
      - mysql-master
    environment:
      - MYSQL_ROOT_PASSWORD=rootpassword
    command:
      - --server-id=2
      - --log-bin=mysql-bin
      - --gtid-mode=ON
      - --enforce-gtid-consistency=ON
      - --relay-log=relay-log
      - --read-only=ON
    ports:
      - "3307:3306"
    volumes:
      - slave_data:/var/lib/mysql
      - ./slave-init/:/docker-entrypoint-initdb.d/
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: always

volumes:
  master_data:
  slave_data:
  • vim ./master-init/init-master.sql
CREATE USER 'replicator'@'%'
IDENTIFIED WITH caching_sha2_password BY 'replicatorpassword';
GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

最後に

MySQL8.4.5 で Source/Replica 構成を構築してみました
構築自体は非常に簡単にできます
MySQL8.4.5で非推奨になっていたり使えないコマンドが増えているのでそこは注意しましょう

参考サイト

2025年7月1日火曜日

管理者権限のない環境でCygwinをlocalにインストールする方法

管理者権限のない環境でCygwinをlocalにインストールする方法

概要

管理者権限があればグローバルな環境にインストールできますがない場合は自分のローカル領域にだけインストールすることができます

環境

  • Windows11
  • Cygwin 3.6.3

setup-x86_64.exe をダウンロード

公式から最新をダウンロードします

コマンドラインを起動

管理者権限でなくて OK です

管理者権限なしで起動

  • cd C:\Users\username\app
  • setup-x86_64.exe -B

-B オプションは --no-admn オプションの短縮形です

あとは UI が起動するのでインストールを進める

「Choose Installation Directory」では Root Directory に自分の領域を選択することと「Install For」で「Just Me」を選択しましょう

最後に

Cygwin 自体をアップグレードしたい場合は最新版の setup-x86_64.exe からダウンロードし同じように実行すれば OK です

パッケージ自体のアップグレードがある場合もデフォルトは自動で最新版にあげてくれます
もし手動で行いたい場合は Select Packages の一覧で「Picked」を選択しインストール済みパッケージのみを表示したあとで New の絡むで Keep もしくはインストールしたいバージョンを選択しましょう

2025年6月30日月曜日

Blogger API v3 で OAuth2.0 の認証トークンをリフレッシュする方法

Blogger API v3 で OAuth2.0 の認証トークンをリフレッシュする方法

概要

毎回ブラウザ認証する or トークンが期限切れになったらブラウザ認証は面倒なので refresh_token を使って認証トークンを再取得できます

環境

  • macOS 15.5
  • Python 3.12.11
  • google-api-python-client 2.174.0

Python スクリプト

  • vim app.py
import os

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build


class BloggerClient:
    # スコープ(Blogger のフルアクセス)
    SCOPES = ["https://www.googleapis.com/auth/blogger"]
    # 対象のブログID
    BLOG_ID = "1234567890"

    def __init__(
        self, client_secrets_file="client_secrets.json", token_file="token.json"
    ):
        self.client_secrets_file = client_secrets_file
        self.token_file = token_file
        self.creds = self._authenticate()
        self.service = self._build_service()

    def _authenticate(self):
        # 認証情報の取得
        if os.path.exists("token.json"):
            creds = Credentials.from_authorized_user_file("token.json", self.SCOPES)
            if not creds.valid:
                if creds.expired and creds.refresh_token:
                    # トークンが有効期限切れの場合は更新
                    creds.refresh(Request())
                else:
                    # トークンが無効な場合は再認証
                    os.remove("token.json")
                    return self._authenticate()
        else:
            # token.json がない場合はブラウザを使って初回認証する
            flow = InstalledAppFlow.from_client_secrets_file(
                "client_secrets.json", self.SCOPES
            )
            creds = flow.run_local_server(port=0, access_type="offline")
            with open("token.json", "w") as token:
                token.write(creds.to_json())
        return creds

    def _build_service(self):
        # Blogger API サービスのビルド
        return build("blogger", "v3", credentials=self.creds)

    def post_to_blog(self, title, content) -> str:
        # ブログに投稿する
        body = {
            "kind": "blogger#post",
            "title": title,
            "content": content,
        }
        # 投稿の実行
        result = self.service.posts().insert(blogId=self.BLOG_ID, body=body).execute()
        return result["url"]

ちょっと解説

ポイントは _authenticate メソッドです
token.json がある場合はそれを再利用しますが token.json にあるトークンがすでに期限切れの場合には refresh メソッドをコールします
こうすることで毎回ブラウザで認証する必要がなくなります

最後に

OAuth2.0 は大抵の場合 refresh_token があるので CLI などの環境ではこれを使ってトークンを更新するようにしましょう

参考サイト

2025年6月29日日曜日

Blogger API v3 を使って記事を更新する方法

Blogger API v3 を使って記事を更新する方法

概要

前回は記事を投稿する方法を紹介しました
今回は更新する方法を紹介します

環境

  • macOS 15.5
  • Python 3.12.11
  • google-api-python-client 2.174.0

Python スクリプト

  • vim app.py
import os

from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

# スコープ(Blogger のフルアクセス)
SCOPES = ["https://www.googleapis.com/auth/blogger"]


if os.path.exists("token.json"):
    creds = Credentials.from_authorized_user_file("token.json", SCOPES)
else:
    flow = InstalledAppFlow.from_client_secrets_file("client_secrets.json", SCOPES)
    creds = flow.run_local_server(port=8080, access_type="offline")
    with open("token.json", "w") as token:
        token.write(creds.to_json())

# 以降のコードで自動リフレッシュ対応済み
service = build("blogger", "v3", credentials=creds)

# 対象のブログID
blog_id = "1234567890"

# ブログの一覧を取得したい場合は以下のコードを使用
# 対象ブログの取得(複数ある場合はリストされる)
# blogs = service.blogs().listByUser(userId='self').execute()
# for blog in blogs['items']:
#     print(f"Blog ID: {blog['id']}, Title: {blog['name']}")
#
# blog_id = blogs['items'][0]['id']  # 1つ目のブログを使う

body = {
    "kind": "blogger#post",
    "title": "テスト投稿",
    "content": "<p>これは Python からの Blogger API を使ったテスト投稿です。</p>",
}

# 投稿の実行
result = service.posts().insert(blogId=blog_id, body=body).execute()

print(f"Post published: {result['url']}")

# 投稿IDを取得
post_id = result["id"]

body = {
    "kind": "blogger#post",
    "title": "テスト投稿(更新)",
    "content": "<p><b>これは Python からの Blogger API を使ったテスト投稿です。</b></p>",
}

# 投稿の更新
result = service.posts().update(blogId=blog_id, postId=post_id, body=body).execute()

print(f"Post updated: {result['url']}")

動作確認

  • pipenv run python app.py

記事が更新されていることを確認しましょう

最後に

投稿した記事に付与される permalink は Blogger API v3 では更新できないので GUI から手動でやるしかないようです

参考サイト

2025年6月28日土曜日

Blogger API v3 を使って Python スクリプトから記事を投稿する方法

Blogger API v3 を使って Python スクリプトから記事を投稿する方法

概要

前回 OAuth2.0 を実装しました
今回は認証後に記事を投稿する部分を実装します

環境

  • macOS 15.5
  • Python 3.12.11
  • google-api-python-client 2.174.0

Python スクリプト

blog_id の部分は適宜変更してください

  • vim app.py
import os

from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

# スコープ(Blogger のフルアクセス)
SCOPES = ["https://www.googleapis.com/auth/blogger"]


if os.path.exists("token.json"):
    creds = Credentials.from_authorized_user_file("token.json", SCOPES)
else:
    flow = InstalledAppFlow.from_client_secrets_file("client_secrets.json", SCOPES)
    creds = flow.run_local_server(port=8080, access_type="offline")
    with open("token.json", "w") as token:
        token.write(creds.to_json())

# 以降のコードで自動リフレッシュ対応済み
service = build("blogger", "v3", credentials=creds)

# 対象のブログID
blog_id = "1234567890"

# ブログの一覧を取得したい場合は以下のコードを使用
# 対象ブログの取得(複数ある場合はリストされる)
# blogs = service.blogs().listByUser(userId='self').execute()
# for blog in blogs['items']:
#     print(f"Blog ID: {blog['id']}, Title: {blog['name']}")
#
# blog_id = blogs['items'][0]['id']  # 1つ目のブログを使う

body = {
    "kind": "blogger#post",
    "title": "テスト投稿",
    "content": "<p>これは Python からの Blogger API を使ったテスト投稿です。</p>",
}

# 投稿の実行
result = service.posts().insert(blogId=blog_id, body=body).execute()

print(f"Post published: {result['url']}")

動作確認

  • pipenv run python app.py

で記事を投稿し問題なく記事があることを確認しましょう
記事の URL などを変更したい場合は post では行えず put する必要があります

最後に

Blogger API v3 を使って記事を投稿する方法を紹介しました
Blogger API v3 では投稿した記事の URL 部分 (permalink) を変更することはできないようです
permalink の変更は GUI からのみ行えます

参考サイト

2025年6月26日木曜日

Blogger API v3 を使うために OAuth2.0 認証を Python スクリプトで行う方法

Blogger API v3 を使うために OAuth2.0 認証を Python スクリプトで行う方法

概要

Blogger API v3 を使って記事を投稿するためには OAuth2.0 認証を使わなければなりません
OAuth の場合一度ブラウザで認証しトークンを取得する必要がありますがそうしないと記事の投稿はできないようです

今回は前準備として Python スクリプトで Google API を使うための OAuth2.0 認証を行う方法を紹介します

環境

  • macOS 15.5
  • Python 3.12.11
  • google-api-python-client 2.174.0

インストール

  • pipenv install google-api-python-client google_auth_oauthlib

client_secrets.json の取得

API とサービス -> 認証情報 -> 認証情報の作成 -> OAuth クライアント ID

  • アプリケーションの種類 -> ウェブアプリケーション
  • 承認済みのリダイレクト URI http://localhost:8080/

で作成し client_secret_1234567890-xxx.apps.googleusercontent.com.json というファイルをダウンロードします
以下のコードでは client_secrets.json に名前を変更しています

Python スクリプト

認証し成功したらトークンをファイルに保存します
トークンファイルが既にローカルにある場合はそれを使うためブラウザで認証するのは初回のみになります

  • vim app.py
import os

from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

# スコープ(Blogger のフルアクセス)
SCOPES = ['https://www.googleapis.com/auth/blogger']


if os.path.exists("token.json"):
    creds = Credentials.from_authorized_user_file("token.json", SCOPES)
else:
    flow = InstalledAppFlow.from_client_secrets_file("client_secrets.json", SCOPES)
    creds = flow.run_local_server(port=8080, access_type='offline')
    with open("token.json", "w") as token:
        token.write(creds.to_json())

# 以降のコードで自動リフレッシュ対応済み
service = build("blogger", "v3", credentials=creds)

動作確認

  • piepnv run python app.py

実行するとブラウザが起動し Google アカウントでログインすることになります
アプリが Blogger にアクセスすることを許可する画面になるので許可します

GCP の画面でブランディングなど設定するとアプリ名などを設定できます

認証後に Python スクリプト側に token.json があれば認証成功です
再度実行してみるとブラウザは起動せずにクライアントオブジェクトが設定できることを確認できるはずです

最後に

Blogger API v3 で記事を投稿するための準備段階として OAuth2.0 認証を Python スクリプトで行う方法を紹介しました
OAuth2.0 はセキュアなのですがブラウザ認証しなければならないので面倒です

参考サイト

2025年6月25日水曜日

Youtube のチャンネル登録を一括で削除する方法

Youtube のチャンネル登録を一括で削除する方法

概要

Google のマイアクティビティから削除します
API などはないようです

環境

  • Youtube (2025/06/25 時点)
  • Google マイアクティビティ (2025/06/25 時点)

方法

ここ https://myactivity.google.com/page?page=youtube_subscriptions にアクセスし削除します
おそらく現状はこれが一番簡単です