LNDのGKE化がうまくいかない
LND-BTCD環境をGKE上に構築し、lndのpeer(port:9375)の接続が可能な状態とすることを目的とする。
最終目的としては、BTCD-LND-LNCLIのnetworkはセキュアな状態で、lndのpeer(port:9375)だけをinternetに公開したい。
アーキテクチャ
前提
- GCPプロジェクト準備済
- GCLOUDをinstall済
- dockerをinstall済
- kubectlをinstall済
クラスタのセットアップ
まずはクラスタを作る。 対象のプロジェクトが存在するconfigurationを指定する。
gcloud config configurations activate CONFIGURATION_NAME
続いて、空のクラスタを作成する。
gcloud container clusters create lnd-simnet --machine-type=n1-standard-1 --num-nodes=3 --region asia-northeast1-a
gcloud container clusters describe lnd-simnet
続いて、今後のローカルでのkubectlとの連携のために、credentialを渡す。
$ gcloud container clusters get-credentials lnd-simnet --zone asia-northeast1-a
Fetching cluster endpoint and auth data.
kubeconfig entry generated for test-cluster.
対象のコンテキストが指定されていることを確認する。
$ kubectl config get-contexts
~/G/s/LND_container ❯❯❯ kubectl config get-contexts 2019/10/13 20:22:05 +[master]
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
docker-desktop docker-desktop docker-desktop
docker-for-desktop docker-desktop docker-desktop
* gke_asia-northeast1-a_lnd-simnet gke_asia-northeast1-a_lnd-simnet gke_asia-northeast1-a_lnd-simnet
docker imageのpush
LND、BTCDともに、最新のgithubのソースを利用する。
上記のイメージを構築し、事前にGCR上に保存する必要がある。 まず、Container Registry を認証するには、gcloud を Docker 認証ヘルパーとして使用する。
gcloud auth configure-docker
これができたら、imageをbuildし、gcrにpushする。 ホスト名と Google Cloud Platform Console のプロジェクト ID とイメージ名を組み合わせたものを指定する必要がある。
[HOSTNAME]/[PROJECT-ID]/[IMAGE]
docker build . -t gcr.io/<PROJECT_NAME>/lnd:latest
docker build . -t gcr.io/<PROJECT_NAME>/btcd:latest
下記を実行すると、下記の用にイメージが表示されるはずだ。
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
gcr.io/<project>/btcd latest aaaaaaaaaaaa 25 hours ago 59.7MB
gcr.io/<project>/lnd latest aaaaaaaaaaaa 25 hours ago 72.7MB
続いて、このイメージをGCRにプッシュする。
docker push gcr.io/<PROJECT_NAME>/btcd:latest
docker push gcr.io/<PROJECT_NAME>/lnd:latest
GCPコンソールでGCRを確認すると、イメージが存在する。
コマンドでも良い。
gcloud container images list
node・podの構築
上記のimageを使ってpodを構築する。
kubectl apply -f LND.yaml
kubectl apply -f service.yaml
上記コマンドにより、pod、serviceが構築される。
~/G/s/LND_container ❯❯❯ kubectl get pod 2019/10/13 20:03:17 +[master]
NAME READY STATUS RESTARTS AGE
lnd-btcd-0 2/2 Running 1 3h53m
~/G/s/LND_container ❯❯❯ kubectl get service 2019/10/13 20:03:41 +[master]
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.43.240.1 <none> 443/TCP 10d
lnd-btcd-s LoadBalancer 10.43.253.226 35.221.78.222 8080:32059/TCP,9735:30447/TCP 5h16m
動作確認(エラーあり)
この状態で、LNDのpodに入り、LNDの情報を確認したい。
kubectl exec -it lnd-btcd-0 -c lnd bash
これでコンテナにログインできる。
bash-5.0 lncli unlock
Input wallet password:
lnd successfully unlocked!
bash-5.0 lncli --network=simnet getinfo
[lncli] rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: Error while dialing dial tcp 127.0.0.1:10009: connect: connection refused"
ここで、connectionがrefuseされる。
どこのコネクションが拒否されているかというと、LND serverに対するlncliのrpc接続だ。cli toolにrpcサーバをローカルで接続させるのはよくある構成である。イメージとしては、下記の構成が同一サーバ上に存在しているのと等しい。
エラー原因調査
エラーメッセージ"transport: Error while dialing dial tcp 127.0.0.1:10009: connect: connection refused"を素直に見れば、lncliからlndに接続しようした時に、エラーが発生していることになる。
lndとlncliは同じコンテナ上に存在するアプリケーションだが、tls証明書をつかった接続をしていおり、tls証明書がうまく作用していないように見える。
細かい挙動を今追っているが、serviceでロードバランサが構築される際に、IPアドレスが定まらないことが原因ではないか。
対応
tls証明書は、LNDのbootstrap時に実行されるが、このtls証明書は使えない。なので、LightningPeach/lnd-gc-deployを参考に、tls証明書の再作成をしてみる。
LightningPeachの考え方は、CFSSL (CloudFlare's PKI/TLS toolkit) を使って、 LNDがbootstrup時に実行する操作をなぞり、tls証明書を作り直すアプローチだ。
CFSSLは、自己署名認証局(CA)で証明書を作成するコンテナで、証明書作成用のコマンドを内包した、非常駐のDockerコンテナである。
利用するconfigは下記の通り。
{
"signing": {
"default": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
このコンフィグを、コンテナにコピーする。
kubectl cp config.json lnd-btcd-0:/config.json -c lnd
続いて、tlsを作成するために必要なserver.json
を作成する。
{
"CN": "lnd",
"hosts": [
"address",
"localhost",
"0.0.0.0",
"127.0.0.1"
],
"key": {
"algo": "ecdsa",
"size": 256
},
"names": [
{
"C": "EU"
}
]
}
ここにあるaddress
を、serviceのロードバランサが作成するexternalIPに置き換えることで作成し、GKEにコピーする。
IP=$(kubectl get services | grep lnd-pod | awk '{print $4}')
sed -i ".bak" "s/address/$IP/g" server.json
kubectl cp server.json lnd-pod:/server.new.json
続いて、コンテナにログインして、証明書を作成し直す。
kubectl exec -it lnd-btcd-0 -c lnd bash
cfssl gencert -initca server.new.json | cfssljson -bare ca -
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=config.json server.new.json | cfssljson -bare server
mv /server.pem /root/.lnd/tls.cert
mv /server-key.pem /root/.lnd/tls.key
これでしっかり動く!と思いきや、エラーがでる・・・
bash-5.0 lncli --network=simnet getinfo
[lncli] rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: x509: certificate signed by unknown authority"
違うエラーではあるものの・・・。 LightningPeach/lnd-gc-deployを参考に、podを作り直してみる。
~/G/s/LND_container ❯❯❯ kubectl apply -f LND.yaml 2019/10/13 15:09:24 +[master]
statefulset.apps/lnd-btcd created
~/G/s/LND_container ❯❯❯ kubectl get pods 2019/10/13 15:10:06 +[master]
NAME READY STATUS RESTARTS AGE
lnd-btcd-0 2/2 Running 0 36s
~/G/s/LND_container ❯❯❯ kubectl exec -it lnd-btcd-0 -c lnd bash
bash-5.0 lncli --network=simnet getinfo
[lncli] rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: Error while dialing dial tcp 127.0.0.1:10009: connect: connection refused"
同じエラー。なぜだ!