まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

2021/06

かなり以前に Bluemix(IBM Cloud) の CloudFoundry ランタイム環境でカスタムドメインを運用する手順を紹介したことがありました。今回はその IKS(IBM Kubernetes Services) 版とも言える内容で、特にカスタムドメイン管理を適用する部分については IBM Cloud から提供されるドメイン単位でのオール・イン・ワン・セキュリティーサービスでもある CIS(Cloud Internet Services) を使った場合の手順を確認する機会があったので、その流れを紹介します。

なお、今回紹介する手順を実行するための前提条件として (0) カスタムドメインを所有していること、(1) ベーシックプラン以上の IBM Cloud アカウントを取得していること、(2) IKS(+ Ingress)サービスが利用できること、そして (3) CIS サービスが利用できるという3点が必要です。(0) は今回の作業では当然必要です。また (1) がないと (2) も (3) も利用できないので IBM Cloud アカウントは取得しているとして、このアカウントは無料のライトプランではなく、ベーシックプラン以上でないと (2) も (3) も作成できない点にご注意ください。

また (2) について、IKS は1ヶ月間無料の Free プランもありますが、今回紹介する内容では(Free プランでは使えない) Ingress によるサーバー名管理が必要なため、事実上この (2) は無料では試せない点をご了承ください。シングルゾーンの最弱スペックで1ワーカーノード環境(1時間で約13円)でも構いませんが、IKS を利用するための料金が必要です。なお IKS ではなく上位版の OpenShift 環境でも大丈夫です:
2021062501


(3) の CIS は1アカウントにつき1回だけ、30日間無料プランを申し込むことが可能です。このプランを利用せずにスタンダードプランを利用する場合は、ドメイン1つにつき1ヶ月約30000円です:
2021062502



では以下で具体的な手順を追いながら説明します。

【IKS + Ingress を使ってアプリケーションをデプロイする】
まずは IKS + Ingress 環境で動くウェブアプリケーションを用意します。この時点ではカスタムドメインを意識せずに環境を作ります。

自分で IKS にデプロイしたアプリケーションがあればそれを使っていただいても構いませんが、動作確認用のサンプルとして、以前のブログエントリでも紹介したこのアプリケーションを使う方法も紹介します(アクセスすると、サーバーの /etc/hostname ファイルの内容を表示するだけのアプリケーションです)。ソースコード内に IKS + Ingress 環境を想定したデプロイ用の yaml ファイルが含まれているのでこれを自分の環境向けに編集してデプロイに使うことにします。

まずはソースコードを git clone するかダウンロードしてください:
$ git clone https://github.com/dotnsf/hostname

厳密には今回の作業で最低限必要になるのは yaml/app_deployment_ingresssample.yaml ファイル1つだけなので、ソースコード等に興味なく単に動作確認するだけであれば、このファイルをダウンロードしていただくだけでも構いません:
https://raw.githubusercontent.com/dotnsf/hostname/master/yaml/app_deployment_ingresssample.yaml


次にダウンロードした (yaml/)app_deployment_ingresssample.yaml ファイルを自分の環境向けに編集するのですが、まずは自分の Ingress サブドメインを確認しておく必要があります。IBM Cloud にログインし、利用中の IKS のダッシュボードを開いて Overview メニューから Ingress Subdomain 欄を参照して、その値を確認します:
2021062403


ダウンロードした app_deployment_ingresssample.yaml ファイルをテキストエディタで開き、以下の部分を上で参照した自分の Ingress Subdomain の値に書き換えます。また k8s クラスターを東京( jp-tok )以外のリージョンで作成した場合はサブドメイン情報に続くリージョン情報も自分のリージョンに書き換えてください。最後に保存します:
apiVersion: v1
kind: Service
metadata:
  name: hostname
spec:
  selector:
    app: hostname
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hostname
  template:
    metadata:
      labels:
        app: hostname
    spec:
      containers:
      - name: hostname
        image: dotnsf/hostname
        ports:
        - containerPort: 8080
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
spec:
  tls:
  - hosts:
    - hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    secretName: yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000
  rules:
  - host: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    http:
      paths:
      - path: /
        backend:
          serviceName: hostname
          servicePort: 8080


これでデプロイの準備が完了したので、実際に IKS へデプロイします:
$ kubectl apply -f yaml/app_deployment_ingresssample.yaml 

デプロイが成功すると Ingress が有効になり、https://(上述の spec.tls.hosts の値)/ でアプリケーションにアクセスできるようになります:
2021062401


IKS 上にデプロイされたアプリケーションの稼働までができました。この時点では IBM Cloud のドメインによるサーバー名でアクセスされていますが、引き続いて CIS を使い、このアプリケーションをカスタムドメインのサーバー名でアクセスできるようにしていきます。


【CIS にカスタムドメインを登録する】
次の手順は CIS 側の設定です。CIS はドメイン単位で設定を行います。したがってカスタムドメインを使う場合はそのカスタムドメインを登録した上で証明書等を発行する必要があります。

今回は私個人が所有する pi314.jp というドメインを使って説明します。最終的には上述の IKS 上で動く hostname アプリケーションを https://hostname.pi314.jp/ という URL で利用できるように設定していくことにします。以下、"pi314.jp" 部分を皆さんが所有するドメイン名に置き換えて参照してください。

カスタムドメインを CIS に登録する際の最初の手順は利用ドメインの登録ネームサーバーの置き換えです。まず何はともあれ自分のドメインを CIS に管理させるために登録を行います。自分の CIS サービスを開き、Overview メニューから Add domain ボタンをクリックします:
2021062401


画面右にウィザード形式のダイアログが現れるので、指示に従って入力していきます。まずは利用するドメイン(下図では pi314.jp)を入力して Next を選択します:
2021062402


次の DNS レコード登録画面はスキップします。そのまま Next を選択:
2021062403


最後にドメイン管理のためのネームサーバーを変更しろ、という以下のような画面が現れます:
2021062404


DNS で利用するネームサーバーを CIS が管理するネームサーバーに変更する必要があります。この具体的な手順は独自ドメインを取得した業者によって異なりますが、プライマリネームサーバーとセカンダリネームサーバーをそれぞれ以下の内容に変更してください(画面は GoDaddy での DNS 管理画面のものです):
2021062401

 プライマリネームサーバー: ns012.name.cloud.ibm.com
 セカンダリネームサーバー: ns091.name.cloud.ibm.com


変更が完了したら、少し待ってから上図の Next を選択します。変更内容がネット上に反映されるまで少し時間が必要なので、すぐに実行すると「1時間後に再実行しろ」といった内容のエラーになるかもしれません。その場合は少し待ってから再実行してください。

無事に変更内容が確認できた場合は以下のような画面になります。最後に Done を選択:
2021062405


CIS のコンソール画面に移動します。この時点で登録したカスタムドメインが Active になっていることが確認できます。これでドメインの登録とネームサーバーの置き換え作業は完了しました:
2021062406


【CIS に DNS を登録し、エッジとサーバーの接続方法の選択】
ネームサーバーの置き換え後に行う必要があるのは、DNS 設定でカスタムドメインを使ったサーバー名(今回は hostname.pi314.jp)で実際に動いているサーバー(IKS の yaml ファイルで spec.tls.hosts に書かれた値、つまりウェブブラウザで動作確認した時の URL のサーバー名)にアクセスさせるための CNAME の登録と、カスタムドメインのエッジ証明書の作成(確認)、そして CIS のエッジサーバーから IKS のサーバーへどのような方法で接続するかの接続方法の選択です。すべて CIS コンソールから行います。

まずは CNAME を登録します。"Reliability" メニューから "DNS" タブを選択します。画面下部に "DNS records" 欄がありますが、おそらく最初は中身が空のはずです。ここに新しいレコードを作成するため、Add ボタンを選択します:
2021062407


画面右に "Add record" ダイアログが表示されるので、ここで以下のように入力します:
 Type: "CNAME" を選択
 TTL: "Automatic" のまま
 Name: カスタムドメインを使ってアクセスさせるホスト名のドメインを抜いた部分
  (例えば hostname.pi314.jp でアクセスさせたい場合であれば hostname)
 Alias domain name: 現在動いているサーバー名
  (上述のウェブサーバーで動作確認した時に入力した IBM Cloud ドメインのサーバー名)

最後に Add ボタンをクリックします:
2021062408


1つ前の画面に戻り、DNS records が1件追加されていることを確認し、Proxy を ON の状態にしておきます。これで DNS の登録も完了して、hostname.pi314.jp というホスト名で IKS で稼働中のアプリケーションを指し示すようになりました:
2021062409


続けて https 接続のためのエッジ証明書を確認します。CIS コンソールから "Security" メニューを選び、画面下部に Edge certificates 欄を参照します。ここにはカスタムドメインとそのワイルドカード名(今回の例であれば pi314.jp と *.pi314.jp)のエッジ証明書が入力されていればそのままで大丈夫です(2021/06/23 時点の仕様では自動的に作成されているはずです)。もしもここが空になっていたら、右上の Add ボタンを押して、この2つを指定してエッジ証明書を追加してください:
2021062401


最後に CIS のエッジサーバーと、実際のサーバーとの間の通信手段を選択します。同じ "Security" メニューの画面内の TLS mode を確認します(デフォルトは End-to-end CA signed)。ここでセキュリティ要件に合わせて通信方法を選択します。最もセキュリティが高いのは End-to-end CA signed ですが、この場合は別途ドメインのワイルドカード証明書を取得する必要があります。今回は1つ下の End-to-end flexible を選択します(自動発行する自己証明書を使った暗号化通信を行うモードです)。なおこれらの選択肢の違いについて、詳しくはこちらを参照ください:
2021062401


以上で CIS コンソールでの作業も完了です。


【デプロイ設定を変更して IKS にアプリケーションを再デプロイ】

最後にデプロイ設定を変更してアプリケーションを再デプロイします。現在は IBM Cloud ドメイン名でアクセスされる前提でデプロイしていますが、今後はカスタムドメインを使ったアクセスを想定する必要があり、そのための変更を app_deployment_ingressample.yaml ファイルに反映させた上で再デプロイします。

この方法には2つあります。1つは旧サーバー名でのアクセスはできないようにした上でカスタムドメインでの新サーバー名のアクセスを許可する場合、もう1つは旧サーバー名でのアクセスを許可しながら(残しながら)カスタムドメインでの新サーバー名でもアクセスを許可する場合です。両方のケースを順に説明します。

まず旧サーバー名でのアクセスが不要になる場合は前者の(旧サーバー名ではアクセスできないようにする)ケースになります。この場合は app_deployment_ingressample.yaml ファイルの2箇所を以下のように書き換えます:
apiVersion: v1
kind: Service
metadata:
  name: hostname
spec:
  selector:
    app: hostname
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hostname
  template:
    metadata:
      labels:
        app: hostname
    spec:
      containers:
      - name: hostname
        image: dotnsf/hostname
        ports:
        - containerPort: 8080
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
spec:
  tls:
  - hosts:
#   - hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    - hostname.pi314.jp
    secretName: yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000
  rules:
# - host: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
  - host: hostname.pi314.jp
    http:
      paths:
      - path: /
        backend:
          serviceName: hostname
          servicePort: 8080

"#" で始まる行はコメントなので無くても構いません。要するに spec.tls.hosts と spec.rules.hosts の値を旧サーバー名ではなく新サーバー名に変更する、という作業が必要になります。

もう一方の、旧サーバー名でのアクセスを残しながら新サーバー名でもアクセスできるようにする場合は app_deployment_ingressample.yaml ファイルに新しい Ingress 設定を書き足す形で以下のように書き換えます:
apiVersion: v1
kind: Service
metadata:
  name: hostname
spec:
  selector:
    app: hostname
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 8080
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hostname
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hostname
  template:
    metadata:
      labels:
        app: hostname
    spec:
      containers:
      - name: hostname
        image: dotnsf/hostname
        ports:
        - containerPort: 8080
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
spec:
  tls:
  - hosts:
    - hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    secretName: yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000
  rules:
 - host: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
    http:
      paths:
      - path: /
        backend:
          serviceName: hostname
          servicePort: 8080
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: hostname.yourcluster-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-0000.jp-tok.containers.appdomain.cloud
spec:
  tls:
  - hosts:
    - hostname.pi314.jp
  rules:
 - host: hostname.pi314.jp
    http:
      paths:
      - path: /
        backend:
          serviceName: hostname
          servicePort: 8080

目的に応じた変更をした上で、改めて新しい yaml ファイルを使ってアプリケーションを再デプロイします:
$ kubectl apply -f yaml/app_deployment_ingresssample.yaml

再デプロイ後に新しいホスト名で https アクセスができるようになったことを確認します:
2021062401


以上、CIS + IKS( + Ingress ) の環境でカスタムドメインを使った https アクセスを実現するための設定手順でした。CIS は今回紹介した DNS 関連の機能だけでなく、本来得意とするセキュリティ強化の機能が簡単に使える強みもあるので、IBM Cloud を使って k8s 環境のアプリケーションを安全に使う上での非常に便利なコンパニオンサービスだと感じています。



Tips 的な小ネタです。

Node.js + Express によるウェブアプリケーションコードの中で、何らかの URL への GET リクエスト(POST とかでもいいですが、処理内容は GET の時と同じなので GET で考えることにします)を受けて処理している時の、アクセス時のフル URL をサーバー側で知る方法です。 なお、ここでの「フル URL 」とは、プロトコル+ホスト名+(デフォルトと異なる場合は)ポート番号+アクセスパス+実行時のURLパラメータ のこととします。

この値はクライアント側の JavaScript を使えば windows.location オブジェクトを参照することで取得できます。ただこちらはあまり意味がないというか、アクセスしたユーザーは自分のブラウザのアドレス欄を見れば URL を確認できるのでわざわざ別途必要になるケースが珍しいはずです。このクライアントサイドでの話ではなく、サーバーサイドの処理内で知る方法、という意味です。

結論としては以下のようなコードで取得することが可能です:
//.  app.js
var express = require( 'express' ),
    app = express();

app.get( '/*', function( req, res ){
  res.contentType( 'text/plain; charset=utf-8' );
  var url = req.protocol + '://' + req.get( 'host' ) + req.originalUrl;
  res.write( url );
  res.end();
});

var port = process.env.PORT || 8080;
app.listen( port );
console.log( "server starting on " + port + " ..." );

まずルーティングのパス定義部分を '/*' としています。これによってルートパス以下のすべてのパスへの GET リクエストをこのハンドラが受け持つ、と宣言します。

肝心のフル URL ですが、このハンドラ実行時のパラメーター: req(リクエストオブジェクト)を使って、以下のように求めることができます:
 var url = req.protocol + '://' + req.get( 'host' ) + req.originalUrl;

req.protocol にはプロトコル("http" または "https")、req.get( 'host' ) でポート番号まで含めたアクセス時のホスト名、そして req.originalUrl にはアクセス時のフルパスが URL パラメータまで含めた形で取得できます。これらをつなぎ合わせることでアクセス時のフル URL が取得できるので、これをレスポンスで返す、という処理をしています。

試しに実行していくつかの URL パターンでアクセスしてみた所、以下のようにいずれも期待通りの結果になりました:
(http://localhost:8080/)
2021062301


(http://localhost:8080/abc/hello?x=100)
2021062302


(http://localhost:8080/abc/hello?x=100&y=200)
2021062303


1つの環境で複数のサーバー名を持って稼働するサーバーの場合に、「何というホスト名でアクセスされているのか」をサーバー側からも知ることができる、という情報でした。

なおこのアプリケーションのソースコードはこちらで公開しています:
https://github.com/dotnsf/access_url


どれだけ需要があるかわかりませんが、docker イメージ(dotnsf/access-url)の形で以下からも公開しています:
https://hub.docker.com/r/dotnsf/access_url


利用可能な Kubernetes クラスタ環境(と接続設定などが済んだ kubectl コマンド)があれば、以下の手順でコマンドを実行することで Deployment と(spec.type = "NodePort" の) Service を作成できます:
$ git clone https://github.com/dotnsf/access_url

$ cd access_url

$ kubectl -f yaml/app_deployment.yaml

IBM Cloud の無料版 IKS(IBM Kubernetes Services) で、上記コマンドを実行して作成したアプリケーションにアクセスした時の様子が以下になります。アクセスした URL が正しくサーバーサイドで取得できている様子が確認できます:
2021062401

 
最後に余談を。上述のクライアントサイド JavaScript による(window.location オブジェクトを用いた)取得方法との取得できる情報の違いについて補足します。

クライアントサイドで取得する場合、サーバーサイドで取得できない情報が1つ取得できます。それが「ハッシュ」と呼ばれる情報で例えば、
 http://xxx.xxx.xxx.xxx/abc/hello?x=1&y=2#here
という URL アドレスの最後の "#here" 部分の情報です。

この情報はクライアントサイドであれば window.location.search を参照することで取得することが可能ですが、サーバーサイドでは取得する方法がありません。ただハッシュ情報はクライアントサイドで(HTML 内の特定位置を参照するなど)利用するためのものであって、サーバーサイドで生成する情報としてはハッシュによる差異はありません。要はサーバーサイドでは意味のない情報であるためサーバー側では取得できなくなっている、ものと思われます。



IBM Cloud コンソール(ダッシュボード)へのログインを(ID とパスワードでの認証に加えて)多要素認証化する手順を確認できたので紹介します。

なお、ログインを多要素認証化するには組織オーナー権限が必要です。自分がオーナーである自分自身の組織に対しては設定可能ですが、他の組織に対する権限があるかどうかは別途設定が必要なためご注意ください。

多要素認証を有効にするには、まず通常のログイン後に自分の組織を選択した上で、画面上部のメニューから「管理」ー「アクセス(IAM)」を選択します:
2021061701


画面左のメニューから「設定」を選択し、「多要素認証(MFA)」と書かれた欄にある「編集」ボタンをクリックします:
2021061702


多要素認証の設定を選択します。ここではスマートフォンの各種認証アプリを使ったワンタイム・パスコードによる認証を設定してみましょう。「すべてのユーザー用の MFA」を選択し、メソッドに「TOTP MFA」を指定して「更新」ボタンをクリックします。これでログイン方法を多要素認証にするためのダッシュボードでの作業ができました:
2021061703


引き続き、認証アプリ側の設定を行います。いったん IBM Cloud からログアウトして少し(5分程度)待ってから再ログインします:
2021061710


多要素認証が有効になっている場合、パスワードの指定画面が少し変わります(黒い背景の画面ではないページでのパスワード入力画面になっています):
2021061704


多要素認証が有効になっている場合、初回ログイン時に認証アプリのセットアップを促す以下のような画面になります。「開始」ボタンをクリックします:
2021061705


多要素認証をアプリにセットアップするには SMS / メール / 音声電話 の3つの中の2つで検証を行う必要があります。自由に2つ選択いただいて構いませんが、以下の例では (1) SMS と (2) メールの2つで検証する手順として紹介します。 まずは検証方法で SMS を選択し、SMS を受け取ることのできる携帯電話番号を +81 から入力して「送信」します。すると指定した番号の携帯電話にワンタイムパスワードが記された SMS が届くので、そこに書かれた内容を入力して「完了」ボタンをクリックします:
2021061706


続けて2つ目の検証を行います。今回は「Eメール」を選択し、メールを受け取ることのできるメールアドレスを入力して「送信」します。同様に指定したアドレスにワンタイムパスワードが記載されたメールが届くので、そこに書かれた内容を入力して「完了」ボタンをクリックします:
2021061707


2種類の認証が完了すると、認証アプリに IBM Cloud を登録することができるようになります。Google AuthenticatorIBM Security Verify などの各種認証アプリをスマホにインストールした上で、認証要素として「TOTP」を選びます。するとアプリで読み取る QR コードが表示されるので、この QR コードを認証アプリで読み取ります(または共有シークレットを認証アプリに入力します)。すると認証アプリに6桁の検証コードが表示されるので、そのコードを画面内に入力して「完了」をクリックします:
2021061708


すると以下のような画面になり、IBM Cloud 多要素認証の認証アプリへのセットアップが完了しました:
2021061709


スマホの認証アプリ側にも IBM Cloud が登録(追加)されているはずです:
2021061713


では改めて多要素認証が有効になった IBM Cloud にログインしてみます。まずは https://cloud.ibm.com/ で IBM ID を入力します:
2021061710


続けてログインパスワードを入力します。これまでの設定であれば、これでログイン作業は完了ですが・・・:
2021061711


多要素認証が有効になっていると、続けて設定した認証アプリによるワンタイムパスワード認証が必要です。認証アプリに表示されている6桁の検証コードを(有効なうちに)入力して「検証」をクリックします:
2021061712


ここまでの手順がすべて正しく入力できていれば、IBM Cloud ダッシュボードにログインできます。ダッシュボードへの多要素認証ログインが有効になりました:
2021061714


なお IBM Cloud への多要素認証を有効にした場合、CLI でのログイン時には SSO オプションを指定する必要があります:
$ ibmcloud login -u username@test.com --sso

このオプションを指定すると指定した URL からワンタイムパスワードを取得して入力するよう指示されます。指示通りの URL を参照してワンタイムパスワードを入手して入力することでログインできます:
$ ibmcloud login -u username@test.com --sso

API endpoint: https://cloud.ibm.com
Region: us-south

Get a one-time code from https://identity-2.ap-north.iam.cloud.ibm.com/identity/passcode to proceed.
Open the URL in the default browser? [Y/n] > n
One-time code >


詳しい内容や手順についてはオンラインドキュメントも参照ください:
https://cloud.ibm.com/docs/account?topic=account-enablemfa
 

こんなのを作ってみました。これ自体がそのまま直接役立つとは思ってませんが、cron などのスケジュールジョブと合わせて使うことで時系列データを簡単に入手できるので、「生の解析用サンプルデータを用意する」のが比較的容易にできちゃうと思っています。

文字通りの「為替取得 REST API」です。エンドポイント URL はこちらです:
http://fx.mybluemix.net/


REST API なので、何らかの(機械的な)HTTP クライアントからアクセスされることを想定しています。とりあえずデータ・フォーマットを確認する目的で、ウェブブラウザでアクセスしてみると、このような JSON テキストが得られるはずです:
2021061501


JSON テキストを整形するとこんな感じの内容です:
{
 "status":true,
 "result":{
  "datetime":"2021-06-16 14:48:27+0",
  "rate":{
   "USDJPY":109.961,
   "EURJPY":133.339,
   "EURUSD":1.21255,
   "AUDJPY":84.707,
   "GBPJPY":155.129,
   "NZDJPY":78.49,
   "CADJPY":90.334,
   "CHFJPY":122.216,
   "HKDJPY":14.158,
   "GBPUSD":1.4107,
   "USDCHF":0.89958,
   "ZARJPY":7.99,
   "AUDUSD":0.77031,
   "NZDUSD":0.71376,
   "EURAUD":1.57405,
   "TRYJPY":12.872,
   "CNHJPY":17.186,
   "NOKJPY":13.116,
   "SEKJPY":13.137,
   "MXNJPY":5.465
  }
 }
}

status が API の実行結果(true/false)で、成功した場合は result がその結果です。result.datetime が取得したタイミングの GMT 日時です( API を実行した瞬間の日時になっているはずです)。そして result.rate 内にそのタイミングでの(リアルタイムの)各通貨ペアの為替情報が格納されています。例えば上の例では result.rate.USDJPY = 109.961 となっていますが、これはこのタイミングで「1米ドル(USD)=109.961日本円(JPY)」だったことを表しています。同様にして result.rate.EURJPY = 133.339 なので「1ユーロ(EUR)=133.339日本円(JPY)」です。他の通貨表記についてはこのあたりを参照してください:
https://www.gaitame.com/gaitame/gaika/gaika_index.html


このように上記 URL に GET リクエストを発行するだけでリアルタイムな為替情報 20 ペア分が取得できるものです。後はこれを1分おきとか1時間おきに取得して、その結果を RDB なり、JSON DB なりに格納する、という処理を1~10日くらい動かしっぱなしにしておけば、それなりにまとまったデータが取得できるはずです。一応数値は本物の為替情報で、深く考えなくてもただ GET リクエストを実行すれば結果が取得できるので、解析元となるデータを集める上では比較的便利かな、と思っています。

これはあくまで一例ですが、Node-RED のインジェクションノードを定期実行する設定にした上でこんな感じのフローを作るだけで定期的な為替情報を集めて DB に格納するまでが(簡単に)できちゃいます:
2021061502


後はここで集めたデータを使って解析学習時のデータにするもよし、予測機能を作るもよし、ご自由にお使いください。

タイトルどおりの内容のエラーが発生したので、その原因と解決策を探った記録をブログにまとめました。

環境としては以下の図のようなものです。1台の Windows 10 PC の中に Docker Desktop を導入・起動し、MySQL サーバーのイメージからコンテナを作って 3306 番ポートで公開起動しました。この環境に WSL(2) の MySQL CLI を使って、localhost:3306 の MySQL サーバーにログインする、というだけの内容なのですが、ログイン時にコネクションエラーが発生してしまう、という症状が出ていました:
20210612


具体的に紹介します。今回使った MySQL のコンテナイメージはこちらです。厳密には mariadb を使っています。同様の現象が発生すると思うので、他の MySQL 系イメージでも構いません:
https://hub.docker.com/r/linuxserver/mariadb


Docker Desktop を使ってこのイメージからコンテナを作成します。WSL を起動するなどして docker コマンドが使える状態で以下のコマンドを実行します(目的は Docker Desktop 内のコンテナとして起動することなので、このコマンド自体は WSL から実行しなくても構いません):
$ docker run -d --name=mariadb -e PUID=1000 -e PGID=1000 -e MYSQL_ROOT_PASSWORD=root -e TZ=Asia/Tokyo -e MYSQL_DATABASE=mydb -e MYSQL_USER=user -e MYSQL_PASSWORD=P@ssw0rd -p 3306:3306 --restart unless-stopped linuxserver/mariadb

こんな感じで、Docker Desktop 内のコンテナとして MySQL が起動します:
2021061301


指定したオプションはコンテナイメージのドキュメントを参考に指定しています。上の例では root のパスワードは root 、利用ユーザーは user 、利用ユーザーのパスワードは P@ssw0rd 、利用データベース名を mydb、コンテナ名は mariadb、などを指定しています(適当に変更いただいても構いません)。ポート番号に -p 3306:3306 を指定しているので、ローカルホストの 3306 番ポートからも(公開されているので)アクセスできるはずです。事前準備はここまで。

ではこの方法で Docker Desktop 内に起動した MySQL(mariadb) に、WSL から MySQL CLI で接続してみます。上述のオプションであれば以下のコマンドで接続できるはずです:
$ mysql -u user -pP@ssw0rd -h localhost mydb

しかし実際には以下のようなエラーが表示されてしまいます:
$ mysql -u user -pP@ssw0rd -h localhost mydb
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2 "No such file or directory")

ユーザー名やパスワード、データベース名は間違っておらず、接続先も localhost だから間違えようがないはずです。ポート番号もデフォルトの 3306 のままなので特別なオプションも不要のはず・・・ ではなぜエラーになってしまうのでしょうか?


改めてエラーメッセージをよく見ると "Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock'" と表示されています。ちなみにこのファイルは存在していません(/var/run/mysqld というディレクトリが存在していません):
$ ls -la /var/run/sqld/mysqld.sock
ls: cannot access '/var/run/sqld/mysqld.sock': No such file or directory

このエラーメッセージでわかる人もいると思うのですが、MySQL コマンドは localhost が対象の場合はデフォルトで「ソケット接続」という方法で接続を試みます。そしてこのソケット接続をする場合の情報を /var/run/sqld/mysqld.sock というファイルで管理しているため、このファイルを読み込もうとしているのでした。

しかし、今回の環境では WSL 内には MySQL サーバーは起動していません。WSL 内からの docker コマンドで起動してはいますが、実体は Windows にインストールされた Docker Desktop のコンテナとして起動しています。要は「WSL と同じマシン上で動いてはいて、ポートも公開されているけど、WSL から直接ソケット接続できる状態で動いていない」のです。これがエラーの原因で、エラーメッセージの理由でもあります。

では、エラーなしに WSL から MySQL に接続するにはどのようなコマンドにすればいいでしょうか? 答はシンプルで「ソケット接続ではなく、TCP 接続するようなオプションを指定」することで解決できます。具体的には以下のように --protocol=TCP オプションを付けて実行します:
$ mysql -u user -pP@ssw0rd --protocol=TCP -h localhost mydb
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 11
Server version: 10.4.18-MariaDB-1:10.4.18+maria~bionic-log mariadb.org binary distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [mydb]>

今度は無事に接続できました。「localhost の罠」にかかった時の話でした。


このページのトップヘ