Heroku で Google Cloud API の認証を通す方法

GCP のサービスアカウントで Google Cloud API を使うためには、環境変数 GOOGLE_APPLICATION_CREDENTIALS にクレデンシャルファイルのパスを与える必要がある。これが正しく設定されていない場合、GCP の認証エラーが発生する。

さて、Heroku でクレデンシャルファイルを扱うにはどうすればよいのだろうか。安全に 管理したいので、クレデンシャルファイルは Git リポジトリにはコミットしない。どうにかして Heroku の Dyno 内にファイルを置く方法を模索することになる。

僕が取った手段は次の通りである。

  1. クレデンシャルファイルの 中身 を環境変数に渡す
  2. 上記の環境変数を .procfile の中でファイルにダンプする
  3. ダンプしたファイルのパスを GOOGLE_APPLICATION_CREDENTIALS に指定する

具体的な手順

次のコマンドでクレデンシャルファイルの中身を環境変数 GOOGLE_CREDENTIALS に入れる。

heroku config:set GOOGLE_CREDENTIALS="$(< /PATH/TO/credentials.json)"

次の内容の .profile ファイルを作成する。このファイルはコンテナ起動時に実行される Bash シェルスクリプトである。

echo ${GOOGLE_CREDENTIALS} > /app/google-credentials.json

最後に環境変数 GOOGLE_APPLICATION_CREDENTIALS を Heroku アプリに設定する。

heroku config:set GOOGLE_APPLICATION_CREDENTIALS=/app/google-credentials.json

これで Google Cloud API の認証は成功する。

LeetCode の Number of Matching Subsequences の解答例 (Python 3)

LeetCode の Number of Matching Subsequences を Python 3 で解いてみた。説明の一部が Python 風の擬似コードになってしまったものの、雰囲気で読めるはず。 直感的なアイディアの説明 words を「先頭文字をキー、残りの文字列」をバリューとして辞書型のオブジェクトに保存する。問題文のサンプルである words = ["a", "bb", "acd", "ace"] の場合は、次の辞書オブジェクトを作る (以下 dic と呼ぶ)。 { 'a': ['', 'cd', 'ce'], 'b': ['b'] } 対応する S = "abcde" を走査しながら words 中の Subsequences を数えるにはどうすればよいだろうか。ひとつの案として dic を作り変えながら Subsequence を数え上げる方法がある。具体的には次のようにする。 前準備として ans = 0 と初期化する (Subsequence を数え上げるために使う変数) S[0] で dic を引くと ['', 'cd', 'ce'] というリストが返る (リストを取得後に dic[S[0]] をいったん削除する) 取得したリストに含まれる文字列を順に調べていく (以下、リストに含まれるそれぞれの文字列を word と呼ぶ): word が空文字であれば Subsequence を発見したことと等しいので ans += 1 する word が空文字でなければ dic[word[0]] に word[1:] を追加する S[1] で dic を引くと ['b'] というリストが返る (リストを取得後に dic[S[1]] をいったん削除する) 取得したリストに含まれる文字列を順に調べていく (以下、リストに含まれるそれぞれの文字列を word と呼ぶ): word が空文字であれば Subsequence を発見したことと等しいので ans += 1 する word が空文字でなければ dic[word[0]] に word[1:] を追加する S[2] で dic を引くと ['d', 'e'] というリストが返る (リストを取得後に dic[S[2]] をいったん削除する) … 以下略 (S[len(S)-1] まで同様の処理を繰り返す) コード 次のようにコードを書いた。このコードは Runtime: 576 ms, faster than 61. [Read More]

LeetCode の Shortest Way to Form String の解答例 (Python 3)

LeetCode の Shortest Way to Form String を Python 3 で解いてみた。なお、基本的なアイディアは Twoho さんの Discussion へのポスト を参考にしている。 直感的なアイディアの説明 次の入力を考えてみよう。 source: "abcab" target: "aabbaac" まず source から、次のデータ構造を作り出す。 { 'a': [0, 3], 'b': [1, 4], 'c': [2] } これは「キーがアルファベット、値が出現位置のリスト」な辞書である。 次に具体的な処理について考えよう。まず source のどこに注目しているかを示すインデックスを i とする (初期値: 0)。また source を何巡したか示す変数を cnt_loop とする (初期値: 1)。cnt_loop が最終的な解となる。なぜなら、source を巡回する数が minimum number of subsequences そのものになるからである。 ここまでを前準備として、target を走査しながら次の処理をしていく。target 中で注目している文字を c とする。 最初の c は target[0] すなわち 'a' である。これで辞書を引くと [0, 3] である。このリストで i 以上の最小の値 は 0 なのでインデックスをひとつ進めて (0 + 1 して) i = 1 とする。 次の c は target[1] すなわち a である。これで辞書を引くと [0, 3] である。このリストで i 以上の最小の値 は 3 なのでインデックスをひとつ進めて (3 + 1 して) i = 4 とする。 次の c は target[2] すなわち b である。これで辞書を引くと [1, 4] である。このリストで i 以上の最小の値 は 4 なのでインデックスをひとつ進めて (4 + 1 して) i = 5 とする。 次の c は target[3] すなわち b である。これで辞書を引くと [1, 4] である。このリストで i 以上の最小の値 は存在しないので、cnt_loop += 1 しつつ i をリストの最小の要素 + 1 (つまり 1 + 1 = 2) にする。 次の c は target[4] すなわち b である。これで辞書を引くと [1, 4] である。このリストで i 以上の最小の値 は 4 なのでインデックスをひとつ進めて (4 + 1 して) i = 5 とする。 … (省略) … このようにしていくと、最終的に cnt_loop は 3 となる。 [Read More]

LeetCode の Delete Nodes And Return Forest の解答例 (Python 3)

LeetCode の Delete Nodes And Return Forest を Python 3 で解いてみた。 アルゴリズム 僕は木を探索しながら返り値を作り上げていくような実装をした。 具体的には、再帰関数 (walk_tree) に木の頂点を渡し、そこから子ノードがある限り自分自身をコールしていく。 再帰関数には has_parent という引数を設けて、親ノードがある場合とない場合で処理を分岐させる。親ノードがない場合 (i.e., has_parent = False の場合) はルートノードなので、返り値に自分自身を追加する。 木をたどる中で、ノードの値が to_delete に含まれる場合は has_parent = False として再帰関数をコールする。また 親を持たないノードはルート なので、そのノードは返り値のリストに append する。 コード 具体的なコードは次の通り。この実装で Runtime: 68 ms, faster than 66.80% of Python3 online submissions for Delete Nodes And Return Forest. とのこと。なお、同じ実装で faster than 85.44% of Python3 online submissions になることもあるので、多くの そこそこ速い 実装は似ているのだと思う。 ブログの読者向けに詳細にコメントを書き込んだコードを貼り付けておく。 from typing import List class TreeNode: def __init__(self, x): self. [Read More]

LeetCode の Decode String の解答例 (Python 3)

LeetCode の Decode String を Python 3 で解いてみた。 アルゴリズム tl;dr スタックを使って与えられた文字列をパースしながら、出力文字列を組み立てた。具体的には次の通りである。 与えられた文字列を左から走査する。[ を発見するたび、そこまでに現れた数字とアルファベットをスタックにプッシュする。] を見つけたらスタックから値をふたつポップして (それぞれ prev_num と prev_str という名前と仮定する) cur_str = prev_str + cur_str * int(prev_num) として文字列を組み立てる。ここでの右辺の cur_str は現時点までに現れたアルファベットである。 時間計算量は O(n) と言ってよいはず。与えられた文字列を走査 (O(n)) しながら処理 (O(1)) をするため。 具体的なコードは次の通り。この実装で Runtime: 24 ms, faster than 84.71% of Python3 online submissions for Decode String. とのこと。 #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Difficulty: Medium # https://leetcode.com/problems/decode-string/ class Solution: def decodeString(self, string: str) -> str: stack = [] cur_num = '' cur_str = '' for char in string: # 入力文字は '[', ']', 数字, アルファベットの 4 パターンに分けて考える if char == '[': stack. [Read More]

Disqus の導入

このブログに Disqus を導入した。

昔はブログにコメント機能をつけることには反対していた。なぜなら、一般的にブログ記事のコメント欄にコメントを書くのは 簡単すぎる からである。「何を言っているのか分からない…」という声が聞こえてきそうだ。

ブログを書くのは割と大変だ。苦労して書いた記事なのに、タイトルだけ読んで脊髄反射したようなコメントをつけられたり、一部分を都合よく解釈して批判をするようなコメントをつけられることが嫌だったのだ。

昔はブログにメールアドレスを公開して「記事に意見があるときはメールをください」と書いていた。メールを書くのも割と大変なので、脊髄反射的な内容を送られることは少ないだろうという意図だ。

これはそれなりにうまく行っていたけど、コメントするハードルが高すぎると、フィードバックを得づらいというデメリットもある。今は積極的に人からのフィードバックをもらいたいという気持ちである。そこで、本記事のタイトルの通り Disqus を導入したという次第だ。

そもそも脊髄反射的なつまらないコメントをつけられてしまうというのは、記事が大衆向けすぎるということなのかもしれない。自転車置き場の議論に供されるようなことしか書いていないのかも。高度な記事を書けば、しょうもないコメントはつかないのかもしれない。そうだとすると「よいコメントが欲しい」という気持ちは、高度な内容を書くためのよいドライバなのかもしれない。

追記 (2022-10-27)

残念ながら、これまで Disqus 経由でフィードバックをもらえることはあまりなかった。

最近のエントリ“自分が、自分のために、ブログを書いている、というスタンスは大事にしていきたい” という気持ちに立ち戻ったので、このタイミングで Disqus を廃止することにする。

プライベートサブネットの Locust クラスタで動いているウェブインタフェースに接続する

Locust のようなウェブアプリケーションに対するベンチマークツール (負荷試験ツール) を使うのであれば、アプリケーションが動いている場所と同じネットワークで起動するべきである。ベンチマークを取るときにネットワークの影響を軽減するためだ。

多くの場合はウェブアプリケーションはプライベートサブネット内で動くだろう。公開されたロードバランサが、ウェブアプリケーションにリクエストをフォワードするのである。この場合、Locust も同じプライベートサブネットで動かすべきである。しかし、Locust のウェブインタフェースに手元からアクセスしたい場合はどうすればよいだろう?

この場合、SSH のポートフォワードを使うのが最も手っ取り早い。

Locust のウェブインタフェース (通常はマスター) が ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com のポート 8089 (デフォルト) で動いているものとする。また、このプライベートサブネットに接続できる踏み台サーバが bastion.example.com と仮定する。その場合、次のコマンドでリモートで動いている Locust のウェブインタフェースに対してポートフォワードできる。

ssh -fNL 8081:ec2-xx-xx-xx-xx.ap-northeast-1.compute.amazonaws.com:8089 bastion.example.com

このコマンドを叩いた後で localhost:8081 をブラウザで開けば、手元のブラウザから Locust を管理できる。

日本語圏のユーザーには DuckDuckGo よりも startpage.com がオススメ

プライバシ意識に目覚めて Google のプロダクトを使いたくなくなることもあるでしょう。人間だもの。

“検索エンジン 匿名” などのキーワードで Google 検索すると、最初に出てくるのが DuckDuckGo だ。 The search engine that doesn't track you. というメッセージとシンプルな検索文字列の入力フォームがトップページにある。そうそう、こういうのが欲しかったのだ。

しかし、残念ながら、僕はこのプロダクトに満足することはできなかった。日本語検索の結果が非常に悪い。古かったり関係なかったりする記事がずらーっと並んでしまう。

DuckDuckGo は検索エンジンそのものを開発しておらず、エンジンは Microsoft Bing を使っている。DuckDuckGo は仲介者であり、検索クエリをユーザーに代わって Bing に投げるため、Bing にはユーザー情報が渡らない、という塩梅である。そして Microsoft Bing の日本語の検索結果はいまいちよくない。結果、DuckDuckGo も満足いく結果を返せない。

DuckDuckGo と似たコンセプトでサービスを提供している Startpage.com という検索サービスがある。こちらは The world's most private search engine をトップページで謳っている。

Startpage.com は DuckDuckGo と似た仕組みだが、裏側の検索エンジンは Google である。したがって、日本語の検索結果もそれなりに満足いくものだ。そして DuckDuckGo と同様、Startpage.com が検索クエリを投げるため、ユーザーのプライバシは守られる (Google からは誰のクエリかわからない)。一年くらい Startpage.com を使っているけれど、とても満足している。あえて不満をあげると検索ページが少し重いくらいだろうか。

Startpage.com は DuckDuckGo と比べるといまいち知名度が高くないので紹介してみた。日本語ユーザーであれば試してみる価値があると思う。

追記: 2024年1月5日

Startpage.com を便利に使うための Chrome 拡張を作りました。Zenn で紹介しているので、ぜひお試しください。

Python で Socks プロキシを経由して目的とするサーバにアクセスする方法

この記事は何か?

Python で Socks プロキシを経由して目的とするサーバにアクセスする方法について書いた記事である。わたしは Python 3.8 環境で実験したけれど、Python 3 系であればバージョンを問わず同じように動かせるはずである。

具体的なコード

requestsPySocks が導入された状態で、次のようにプロキシの設定をする。なお、Socks プロキシそのものを作成する方法はこちらの記事を参考にしてほしい。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import requests

proxies = {
    'http' : "socks5h://localhost:1080",
    'https' : "socks5h://localhost:1080"
}

requests.get('http://somewhere.example.internal/ping', proxies=proxies)

何がハマりどころかというと、Socks プロキシの記述で socks5://localhost:1080 ではなく、socks5h://localhost:1080 としなければならない点である。そうでない場合、DNS での名前解決をローカルで行うため、Failed to establish a new connection: [Errno 8] nodename nor servname provided, or not known のようなエラーメッセージを見ることになる。

ブザーとともに生きる

少し前に『プリズナートレーニング』が出版されたとき、「読んでみたいな」と思って図書館に予約リクエストを出した。予約リクエストを出してから順番が回ってくるまでに時間がかかったけど、今も変わらず健康的な体作りに興味があるので読んでみた。

プリズナートレーニング

僕が最も感銘を受けたのは筋トレのテクニックそのものではなく、筋トレを日常生活に組み入れる手法について書かれた「ブザーとともに生きる」という節である。著者は監獄で囚人として暮らしながら筋トレをしていた経験があり、「ブザーでスケジュールが区切られる」監獄生活が筋トレのルーチン化に役立ったと述べている。

この節は試し読みできる。

監獄で生活する。それは、規律がすべてであることを学ぶ生活だ。食べるための時間、眠るための時間、他者とふれ合うための時間、作業するための時間が決まっている。タイムテーブルに従ってすべてが行われ、自分でスケジュールをコントロールする自由がほとんどない。ブザー人生と呼び習わす監獄もあった。ある日のある行為がブザーの音で始まり、そして終わるからだ。

 強制的なタイムテーブルの下での生活を何年間も続けたことで、わたしは、時間を尊重するようになっていった。入獄してしばらくすると、だれもが、ルーチンを上手にこなしていけるようになるが、それは、監獄暮らしが長引くと一般社会で生きていけなくなる理由のひとつにもなる。外の世界に出ると、タイムテーブルを失うからだ。何をすべきか、いつ行うべきかを教えてくれる人がいなくなり、迷子になる。自分でタイムテーブルをつくってそれに従う賢い〝元囚人〟もいる。そうできれば外の世界でのサバイバルが可能になり、レールから外れることを避けることができる。

僕はいわゆる「外の世界」で生きているわけだが、それでもブザーが必要なのかもしれない。自らタイムテーブルを作り、それを強制することでルーチンを作るのだ。

様々な要因から、外の世界にブザーを持ち出すのは難しい。僕の場合は「幼い息子」や「仕事の割り込み」がブザー導入の障害になる。次のようにするのはどうだろうか。

  1. 息子が必ず寝ている時間帯に、ルーチンを組み込む
  2. 仕事に関する情報をオフィスの外で読まない (緊急時を除いて)

数ヶ月後に「望ましい行動」を日々の生活に組み入れられているかどうか、チェックしてみようと思う。