one by one (original) (raw)

DjangoをCloud Runにデプロイした時のメモ。

以下の記事を参考にしました。

Cloud Run での Django | Google Codelabs

今回はSQLiteを使用しているためCloud SQLは除外しています。
また、SQLiteは別で設定予定のため今回の作業には含まれません。

Cloud API を有効化

gcloud services enable
run.googleapis.com
cloudbuild.googleapis.com
secretmanager.googleapis.com
artifactregistry.googleapis.com

環境変数を設定

export PROJECT_ID=$(gcloud config get-value core/project) export REGION=us-central1

サービスアカウントを作成

gcloud iam service-accounts create cloudrun-sa export SERVICE_ACCOUNT=$(gcloud iam service-accounts list
--filter cloudrun-sa --format "value(email)")

Artifact Registry を作成

export ARTIFACT_REGISTRY={$REGION}-docker.pkg.dev/{$PROJECT_ID}/containers

Storage バケットを作成

export GS_BUCKET_NAME={$PROJECT_ID}-media gcloud storage buckets create gs://{$GS_BUCKET_NAME} --location {$REGION}

バケットを管理する権限をサービスアカウントに付与する。

gcloud storage buckets add-iam-policy-binding gs://{$GS_BUCKET_NAME}
--member serviceAccount:{$SERVICE_ACCOUNT}
--role roles/storage.admin

Secret Managerに保存

echo GS_BUCKET_NAME="{$GS_BUCKET_NAME}" >> .env echo SECRET_KEY="$(cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | fold -w 50 | head -n 1)" >> .env echo DEBUG=True >> .env

gcloud secrets create application_settings --data-file .env

サービス アカウントにこのシークレットへのアクセスを許可する。

gcloud secrets add-iam-policy-binding application_settings
--member serviceAccount:{$SERVICE_ACCOUNT} --role roles/secretmanager.secretAccessor

作成されたことを確認。

gcloud secrets versions list application_settings

rm .env

設定ファイルを作成

記事を参考に設定ファイルを作成します。

mv myproject/settings.py myproject/basesettings.py touch myproject/settings.py

myproject/settings.py

import io import os from urllib.parse import urlparse

import environ

from .basesettings import *

env = environ.Env() env.read_env(io.StringIO(os.environ.get("APPLICATION_SETTINGS", None)))

SECRET_KEY = env("SECRET_KEY")

if "myproject" not in INSTALLED_APPS: INSTALLED_APPS.append("myproject")

CLOUDRUN_SERVICE_URLS = env("CLOUDRUN_SERVICE_URLS", default=None) if CLOUDRUN_SERVICE_URLS: CSRF_TRUSTED_ORIGINS = env("CLOUDRUN_SERVICE_URLS").split(",")

ALLOWED_HOSTS = [urlparse(url).netloc for url in CSRF_TRUSTED_ORIGINS]

else: ALLOWED_HOSTS = ["*"]

DEBUG = env("DEBUG", default=False)

DATABASES = {"default": env.db("DATABASE_URL", default="sqlite:///db.sqlite3")}

GS_BUCKET_NAME = env("GS_BUCKET_NAME") STATICFILES_DIRS = [] GS_DEFAULT_ACL = "publicRead" STORAGES = { "default": { "BACKEND": "storages.backends.gcloud.GoogleCloudStorage", }, "staticfiles": { "BACKEND": "storages.backends.gcloud.GoogleCloudStorage", }, }

requirements.txt(追加)

gunicorn django-storages[google] django-environ

Procfileを作成

touch Procfile

Procfile

web: gunicorn --bind 0.0.0.0:$PORT --workers 1 --threads 8 --timeout 0 myproject.wsgi:application

イメージをビルドする

gcloud builds submit --pack image={$ARTIFACT_REGISTRY}/myimage

イメージ名は適宜変更します。

Cloud Runにデプロイ

gcloud run deploy django-cloudrun
--region $REGION
--image {$ARTIFACT_REGISTRY}/myimage
--set-secrets APPLICATION_SETTINGS=application_settings:latest
--service-account $SERVICE_ACCOUNT
--allow-unauthenticated

デプロイが完了して表示されたURLを選択するとDjangoのスタートページが表示されます。

変更の適用

変更があった場合は、以下の操作を行います。

  1. 新しいイメージをビルド
  2. Cloud Run を更新

gcloud builds submit --pack image=${ARTIFACT_REGISTRY}/myimage

gcloud run services update django-cloudrun
--region $REGION
--image ${ARTIFACT_REGISTRY}/myimage

.envを読み込み

ローカル環境で開発する時は、環境変数.envから読み込めるほうが都合がいい場合もあったのでmyproject/settings.pyに以下を追加しました。

from pathlib import Path

BASE_DIR = Path(file).resolve().parent.parent

env_path = os.path.join(BASE_DIR, '.env') if Path(env_path).is_file(): env.read_env(env_path)

前回の続きでYouTubeの動画から字幕テキストを取得するときのメモ。

tsyknsr.hatenablog.com

import csv from youtube_transcript_api import YouTubeTranscriptApi

def get_video_transcript(video_url):

video_id = video_url.split('v=')[-1].split('&')[0]

try:
    
    transcript = YouTubeTranscriptApi.get_transcript(video_id, languages=['ja'])
    
    transcript_text = ' '.join([entry['text'] for entry in transcript])
    return transcript_text
except Exception as e:
    print(f"Error retrieving transcript: {e}")
    return None

def main():

video_transcripts = []
with open('video_list.csv', mode='r', encoding='utf-8') as file:
    reader = csv.reader(file)
    next(reader)  
    for row in reader:
        print(f"Title: {row[0]}")
        video_url = row[1]
        transcript_text = get_video_transcript(video_url)
        video_transcripts.append({'title': row[0], 'url': video_url, 'transcript': transcript_text})


with open('video_transcripts.csv', mode='w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerow(['title', 'url', 'transcript'])
    for video_transcript in video_transcripts:
        writer.writerow([video_transcript['title'], video_transcript['url'], video_transcript['transcript']])

if name == "main": main()

YouTubeAPIを使用して動画のタイトルとURLの一覧を取得するプログラムを作成した時のメモ。

import csv from googleapiclient.discovery import build

API_KEY = ''

CHANNEL_URL=''

def get_channel_videos(channel_url):

youtube = build('youtube', 'v3', developerKey=API_KEY)


channel_id = channel_url.split('/')[-1]


videos = []
next_page_token = None

while True:
    request = youtube.search().list(
        part='snippet',
        channelId=channel_id,
        maxResults=50,
        type='video',
        pageToken=next_page_token
    )
    response = request.execute()
    
    for item in response['items']:
        video_title = item['snippet']['title']
        video_url = f"https://www.youtube.com/watch?v={item['id']['videoId']}"
        videos.append({'title': video_title, 'url': video_url})
    
    next_page_token = response.get('nextPageToken')
    if not next_page_token:
        break

return videos

def main(): video_list = get_channel_videos(CHANNEL_URL)

with open('video_list.csv', mode='w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerow(['title', 'url'])
    for video in video_list:
        writer.writerow([video['title'], video['url']])

if name == "main": main()

iPhone15 Pro

毎年値段が上がるので周囲ではAndroidに転向する人も出てきましたが今年も頑張って購入しました。

Apple iPhone 15 Pro 256GB ナチュラル チタニウム SIMフリー 5G対応 (整備済み品)

Ankerのモバイルバッテリー

サイズ感がちょうど良いのとケーブルを持ち歩く必要がないので最近の普段使いはこれに落ち着きました。

Anker Nano Power Bank (22.5W, Built-In USB-C Connector) (モバイルバッテリー 5000mAh 小型コンパクト)【PowerIQ搭載/USB-C一体型】 iPhone 15シリーズ (ホワイト)

aarkeの炭酸水メーカー

こういったオシャレ家電は使わなくなることのほうが多いのですが、自宅で炭酸水をつくれることが意外と便利で日常的に使う家電の仲間入りをしました。

【国内正規品】 aarke カーボネーター 3 (マットブラック) ソーダストリームガスシリンダー対応 高級ステンレス製炭酸水メーカー 【専用ペットボトル付き】

ダンベル 10kg

体力維持レベルの運動が目的なので10kgでも充分でした。しっかりしたダンベルは数万円するので購入に勇気がいりますが、こちらは1万円台で購入しやすい価格帯ということも満足度につながりました。ダンベルが良かったので調子に乗ってベンチも買いましたが、残念ながら埃をかぶりはじめています。

【公式】Northdeer ダンベル 可変式 小型 スチール製 10kg (5kg/7kg/8kg/10kg) クロームメッキ 家庭用 (通常版 10kg×2個)

耐熱ボウル

耐熱ボウルの5点セットです。最初は欲しいサイズの1点だけを予定していましたが、実際に使ってみるとそれぞれのサイズで使い分けたほうが圧倒的に料理しやすいのでセットを購入して良かったと思います。

iwaki(イワキ) AGCテクノグラス 耐熱ガラス ボウル 丸型 5点セット 電子レンジ/オーブン/食洗器対応 食材を混ぜやすい広口デザイン 安定しやすい低重心設計 PST-BO-40N

マキネッタ

夏場にアイスカフェラテをつくるのに大活躍しました。手入れも大雑把でいいので性分に合っていました。豆を自分で挽くか悩みましたが、面倒になり使用する機会が減ると思ったので、豆を購入した時にお店で挽いてもらいました。

Bialetti (ビアレッティ) モカエキスプレス 3カップ用 直火式 ( コーヒーメーカー エスプレッソメーカー マキネッタ )

RICOH GR IIIx

サイズ感と重さと軽さが自分の生活に合っていました。iPhone15 Pro購入以降は持ち出す回数が減りましたが、旅行に行った時などにGRを使用しています。数年前に思い切って購入したミラーレス一眼は、今年は使用する機会がなかったので来年売却するか悩んでいます。

RICOH GR IIIx デジタルカメラ 【焦点距離 40mm / 24.2M APS-Cサイズ大型CMOSセンサー搭載/最強のスナップシュータ― / 約0.8秒 高速起動/高速ハイブリッドAF/高解像・高コントラスト GRレンズ / 3軸・4段 手ぶれ補正機構 SR】GRIIIx GR3x

漫画

最近読んで面白い作品でした。

獣王と薬草(1) (裏少年サンデーコミックス)

PS5

年末年始のお供にホグワーツ・レガシーと合わせて購入しました。ゲームはエンタメの最先端のひとつであることを学びました。

PlayStation 5(CFI-2000A01)

Rustでdotenvのcrateはメンテナンスが行われていないらしく以下の記事を参考にdotenvyを使用しました。

zenn.dev

github.com

$ cargo add dotenvy

適当に.envを作成

FOO=bar

use dotenvy::dotenv; use std::env;

fn main() { dotenv().expect(".env file not found");

let val = env::var("FOO").expect("FOO not set");
println!("Foo: {val}");

}

$ cargo run Foo: bar

Windows環境での作業が必要になったので、リモートデスクトップMacからWindowsを使用するための準備を行いました。

リモートデスクトップ接続するためのアプリはいくつか候補がありましたが、 今回は用途に合っていた「Microsoft Remote Desktop」を選びました。

Windows 11 Homeを使用していましたが、Microsoft Remote Desktopで接続するにはProエディションにする必要があったため Windows 11 Proを購入しました。

Windows 11 Pro 日本語版

普段Macを使用しているので、Microsoft Remote Desktopで接続した時のWindowsキーIMEの切り替え操作に馴染めず、 Karabinerに以下のルールを追加しました。

{ "title": "Custom rules for Remote Desktop", "rules": [ { "description": "Swap left_control and left_command in Remote Desktop", "manipulators": [ { "type": "basic", "from": { "key_code": "left_command", "modifiers": { "optional": ["any"] } }, "to": [ { "key_code": "left_control" } ], "conditions": [ { "type": "frontmost_application_if", "bundle_identifiers": [ "^com\.microsoft\.rdc\.macos$" ] } ] } ] }, { "description": "Toggle IME with right_command in Remote Desktop", "manipulators": [ { "type": "basic", "from": { "key_code": "right_command", "modifiers": { "optional": ["any"] } }, "to": [ { "key_code": "grave_accent_and_tilde", "modifiers": ["right_alt"] } ], "conditions": [ { "type": "frontmost_application_if", "bundle_identifiers": [ "^com\.microsoft\.rdc\.macos$" ] } ] } ] } ] }

さくらVPSPostgresqlをインストールした時のメモ

yum update -y

yum install -y https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm

yum install -y postgresql96-server postgresql96-contrib postgresql96-devel

psql --version

psql (PostgreSQL) 9.6.11

初期化

export PGSETUP_INITDB_OPTIONS="--encoding=UTF-8 --no-locale"

/usr/pgsql-9.6/bin/postgresql96-setup initdb

systemctl enable postgresql-9.6

systemctl start postgresql-9.6

認証方法を変更

$ sudo vim /var/lib/pgsql/9.6/data/pg_hba.conf

peerをtrustに、identをmd5に変更

"local" is for Unix domain socket connections only

local all all trust

IPv4 local connections:

host all all 127.0.0.1/32 md5

IPv6 local connections:

host all all ::1/128 md5

参考