AppEngineでcomposerが更新されないとき

Google AppEngine

 最近になってようやくGoogle AppEngine (以下GAE)を使いだし、自サイトをその上で動かせるよう試行錯誤しています。

前置き

私の主サイトは古くからPHPで動かしていて(初期の頃はLegacy ASPで動かしていたこともありました)、GAEでPHPがサポートされてからいつか移植して動かしてみたい。と思っていたのですが、ベースとしていたフレームワークが古く、composerも使用していない状態のものだったため、最近になり、重い腰を上げてLaravelに置き換えるまでなかなか移植できずにいました。

GAEで開発していると、自分で好き勝手にモジュールをインストールして動かしていた環境と異なり、ある程度の制約を受けつつその中で作るようになります。幸いにも既存機能については代替の外部APIなどに置き換える事でほとんど移植可能になりそうとなりました。

本題

前置きが長くなりましたが、開発している中で、composerでインストールしたパッケージが、GAE上では何故かインストールされないことがありました。

具体定期には、GAEは実質Dockerコンテナなので、永続的なファイル管理はできません。そこで、内部で使用するストレージ部分はGoogle Cloud Storageに置き換えようと思い、google/cloud-storageを使用して通信する処理に置き換えるなどしたのですが、何度デプロイしてもこのパッケージがデプロイされず、参照できずにエラーとなっていました。


デプロイコマンド

GAEでデプロイする際は、通常下記のコマンドを使用します。

gcloud app deploy

または

gcloud app --project <project_id> deploy

基本的には上記でコードをデプロイできますが、composer取得などを毎回行うとビルドに時間がかかるため、composerのパッケージ取得は内部でキャッシュしているそうです。
このキャッシュが強く残り、composer.jsonとcomposer.lockが更新されていても、パッケージがインストールされていなかったようです。

デプロイ時にcomposerを更新させるには下記のコマンドで実行します。

gcloud beta app deploy --no-cache

これにより、デプロイ時ビルドでキャッシュを使用せず、composerパッケージもインストールしなおしてからデプロイされるようになります。

minikubeでGoogle Container Registry (gcr)からイメージを取得する

最近は今ごろになってKubernetesの勉強をしています。
概念は大まかに理解した。つもり。けど全然分からん。

Zennで書いた記事のミラー。なので、以下同じ内容。

前置き

現在Google Cloudの無料期間を利用しつつ色々試しているのですが、Google Container Registry(gcr.io)にホストしたコンテナイメージをminikubeで構築しているローカルの環境から取得する際に、プライベートリポジトリだと認証処理が必要になります。

Kubernetesでもこのあたりはもちろん考慮されていて、Dockerレジストリで使用されるログイン認証に準拠した仕組みが準備されています。

が、如何せんうまく認証されない。普通にリポジトリパスを指定しても、PullImageErrorとなってしまう。

エラーログを見ると、認証方法を確認しろ(意訳)というメッセージとともに GCRでの認証方法を案内するページ へのリンクが出ているので確認し、手順に則って見るも、うまくできない🤔

前提条件

  • GCPのGoogle Container Registoryにホストしているイメージを使用したい
  • コンテナレジストリはプライベートのため認証が必要
  • docker コマンドを利用したレジストリへのイメージのPush/Pullは問題なく行える

こんな感じで2週間くらいハマっていたのですが、情報を探してもなかなか今回のケースを解消できるものがなかなか見つからない…😞

マニフェストファイル

マニフェストファイルは下記の様にdeploymentを記述してました。

apiVersion: apps/v1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  name: app
  namespace: default
spec:
  selector:
    matchLabels:
      name: app
  replicas: 2
  template:
    spec:
      containers:
        - name: app-php
          image: gcr.io/<GCP_PROJECT_ID>/<IMAGE_NAME>:latest
          ports:
            - containerPort: 9000
          imagePullPolicy: IfNotPresent
      imagePullSecrets:
          - name: gcr-secret
    metadata:
      labels:
        name: app
      namespace: default

結論

こちらの記事を参考にしました。

Using Google Container Registry with Kubernetes
https://blog.container-solutions.com/using-google-container-registry-with-kubernetes

この記事内の「Access Token」での取得でこの問題をクリアできました。

kubectl create secret docker-registry gcr-access-token \
 --docker-server=gcr.io \
 --docker-username=oauth3accesstoken \
 --docker-password="$(gcloud auth print-access-token)" \
 --docker-email=your-email@example.com
kubectl patch serviceaccount default \
 -p '{"imagePullSecrets": [{"name": "gcr-json-key"}]}'

2つめの kubectl patch serviceaccount はこれまで実行してなかったので、これをしていないことで認証できていなかった可能性があります。

上記の後、 kubectl apply -f ./deployment.yaml を実行すると、無事すべてのコンテナのステータスがRunningになりました🎉



また、今回の問題を調べていた中で、プライベートコンテナレジストリから取得するための認証情報もKubernetes上のsecretsを使用するんだな、ということが新たな学びがあり、さらに興味を引きつけられました。
このままでもまだアプリは動いて無くて、 まだ一歩前進 でしかないですが、これからもハマりつつ使えるよう勉強していきます。

フリーWi-Fiに接続したらサイトに接続できない場合の話


 スマートフォンが普及してからだいぶ経ち、国外からの旅行者向け、観光誘致も込めてまちなかで使用できるフリーWi-Fiもだいぶ普及が進んできました。

おかげで飲食店などに入り、その店内でWi-Fi経由で調べ物をしたりするのにはとても便利だったりします。

しかし、フリーWi-Fiに接続した途端にサイトに接続できなくなることありますよね。あれ鬱陶しいですね。

これは少し仕方がない部分があって、これもまた最近広まっている、常時HTTPS通信によるものが影響してます。

何故繋がらなくなるのか

HTTPSの通信によって通信の内容は暗号化されています。その内容は利用者以外からは読み取れません。また、その通信内容を改ざんすることもできません。

フリーWi-Fiで繋がらなくなるのはこの通信に介入(実質的には一部改竄)し、Wi-Fiの事前認証情報にログインしていない場合はログイン画面に移動させようとします。
また認証されていない場合、ログイン画面と、一部の許可されたサイト(例えば店舗のウェブサイトなど)以外は通信を制限するようにしています。

この機能はフリーWi-Fiのルーターの機能によるもので、これをキャプティブポータル(Captive Portal)といいます。

そのためログイン画面に遷移させることもできず、外部のサイトへの接続も制限された結果、フリーWi-Fiにつないだ途端にウェブサイトにつながらない。という状態が発生します。

どうしたらいいの?

一度HTTPで通信できるページに移動しましょう。最近のスマートフォンOSであれば、別の画面が立ち上がり、専用のログイン画面に遷移したりしますが、そうでない場合は自分でそれを行わなければなりません。

でも最近のサイトはみんなHTTPSだし、HTTPのページってどこよ?

多くのサイトがHTTPS化しているため、HTTPで通信可能なサイトが少ないです。現在はHTTPのみで通信していても、いつかHTTPSにってしまうかもしれません。

さて、先程スマートフォンOSは自動的にログイン画面を出してくれると書きました。OSはWi-Fi接続を検知すると、そのネットワークでキャプティブポータルが有効かどうか確認します。

確認された場合、Wi-Fi側で指定された画面に遷移しますが、そのときに使用する確認用のページを各OSメーカーは持っています。

Apple iOS/macOSの場合

Google Android OSの場合

http://connectivitycheck.gstatic.com/generate_204

これらのページは接続状態の確認以外には特に意味を持たない無害なページですが、このURLに接続しようとしたときに指定のURLにリダイレクトされようとした場合はキャプティブポータルだと検知し、ログイン画面を表示させています。OS内部に組み込まれているため、各開発元がページを変更しない限り上記は有効です。

そのため、OSで自動検知されていなくてもブラウザからこのURLにアクセスすれば同じ状態が作れます。

Androidのgstatic.com ってドメイン怪しくない?

ところで、感のいい人はAndroidのgstatic.comってドメインって怪しくないの?なんでgoogle.comじゃないの?って思うかもしれません。gstatic.comはGoogleが以前から所有しているドメインで、(最近はあまり使われていないようですが)以前は彼らのサイト内で使用する画像などの呼び出し元としてよく使われていたドメインです。(現在でもマップなどで一部使用されています)

ちなみに、google.comのドメインを使おうとした場合、このドメインでの通信はすべてHTTPSで強制的通信するように、ブラウザ内に組み込まれる仕組みを適用してしまっているため、キャプティブポータルの検出には利用できなくなっています。

ところで、常時HTTPS通信って?

別名、常時SSLともいわれている常時HTTPS通信は、その名の通りすべてのページにおいてHTTPSで通信するもので、URLはhttps://で始まり、ブラウザで見たときにはアドレスバーはの横に鍵マークが付きます。

この状態での通信の場合、ブラウザとサーバーの間の通信は暗号化されていて、悪意ある第三者や他のアプリケーションが通信内容を読み取ったり、改竄しようとすることはできません。*1

なぜ常時HTTPSにする必要があるのか

少し前までは重要な情報、例えばログイン認証時、個人情報を入力する場合、銀行の口座情報を閲覧するとき……など、プライベートな情報を送信、閲覧するときなどに限定した利用の仕方が一般的でした。*2

しかし昨今では、前述のフリーWi-Fi経由に接続した人を対象に、悪意を持って別の偽サイトに誘導して情報を抜き取られる恐れがあるなど、セキュリティ上の問題を孕んでいるケースがあります。

特定の国家、組織からインターネットの自由を守る

フリーWi-Fiでなくても、中国国内からインターネット通信をする際に必ず通過する、中国政府によるグレート・ファイアウォール(金盾)は、建前上、中国国民を国外からのインターネット攻撃から守るということになっていますが、実質的な運用としてはよく知られている通り、中国共産党に不都合な情報を自国民に閲覧制限させるためのものとなっています。これも、暗号化されていない通信の内容を金盾が読み取り、それによって通信を遮断させているからです。

技術的な理由

上記のようなリスク回避の他にも、この通信技術を応用することで通信や表示を高速化*3できたり、HTTPSでしか利用することができない機能*4などもあることから、多くのウェブサイトで常時HTTPSを用いて通信を暗号化することが一般的になっています。


1実際にはゼロではないですが、かなり高度な計算が必要なため、通常の通信の流れでは非常に難しく、現実的ではないです

2: これは昔のサーバーの性能は現在ほど高くなく、暗号化処理を常時利用するには負荷が高すぎたため

3このページで体験できます。またはこちらの動画でも確認できます。HTTP/2やHTTP/3は、HTTPSでの通信技術をベースに実現しています

4: 例えば、ブラウザから現在の位置情報を取得する機能はHTTPSのページからしか利用できないようになっています

AppEngineでcomposerが更新されないとき

Google AppEngine  最近になってようやくGoogle AppEngine (以下GAE)を使いだし、自サイトをその上で動かせるよう試行錯誤しています。 前置き 私の主サイトは古くからPHPで動かしていて(初期の頃はLegacy ASPで動かしていたこともありました...