まーぽんって誰がつけたの?

iOS→Scala→インフラなおじさん技術メモ

Deis/routerでwhitelistでIP制限する方法

Security Considerations - Deis Workflow Documentationで公式にIP制限する方法が紹介されてるが、403が出る。

なんで403になっちゃうのかをここを調べてみた。

公式通りにやっても403

公式ページにもあっさりこうすればできると書いてあるが、確かに403になってしまう。

$ kubectl --namespace=deis annotate deployments/deis-router router.deis.io/nginx.enforceWhitelists=true
$ kubectl --namespace=deis annotate deployments/deis-router router.deis.io/nginx.defaultWhitelist="0.0.0.0/0"

deis/routerが生成するnginx.conf

deis/routerくんはただのnginxなので、生成されたnginx.confを見てみる。

・・中略
    server {
        listen 8080;
        server_name deis.35.189.144.204.nip.io;
        server_name_in_redirect off;
        port_in_redirect off;
        set $app_name "deis/deis-controller";




        allow 1.2.3.4;

        deny all;


        vhost_traffic_status_filter_by_set_key deis/deis-controller application::*;

        location / {



            proxy_buffering off;
            proxy_buffer_size 4k;
            proxy_buffers 8 4k;
            proxy_busy_buffers_size 8k;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $remote_addr;
            proxy_set_header X-Forwarded-Proto $access_scheme;
            proxy_set_header X-Forwarded-Port $forwarded_port;
            proxy_redirect off;
            proxy_connect_timeout 10;
            proxy_send_timeout 1200;
            proxy_read_timeout 1200;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;






            proxy_pass http://10.3.243.160:80;
        }

    }
・・・略

単に、virtual hostでallow 1.2.3.4;してるだけ。 アクセスログを見てみると、clientのIPがこちらのIPになってない。ここが問題のようだ。

2017/04/03 10:40:27 [error] 81#0: *739 access forbidden by rule, client: 10.146.0.2, server: deis.35.189.144.204.nip.io, request: "GET /v2/ HTTP/1.1", host: "deis.35.189.144.204.nip.io"
[2017-04-03T10:40:27+00:00] - deis/deis-controller - 10.146.0.2 - - - 403 - "GET /v2/ HTTP/1.1" - 406 - "-" - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36" - "deis.35.189.144.204.nip.io" - - - deis.35.189.144.204.nip.io - - - 0.000

Client IPを保存するには??

kubenetes v1.5から、beta機能として、loadbalancerがclient IPをpreserveするような機能がついたので、その設定をいけば、client ipがrealなものになる。 やり方は、

$ kd annotate services/deis-router service.beta.kubernetes.io/external-traffic="OnlyLocal"

403になる原因としては、client ipがLoadbalancerのものか、kubernetesの何かのものになってしまったため。Deisの問題でもなく、GCPの問題でもなく、kubernetesの問題だった。以前からissueにあがっており、1.5からGCPとAzureのみ対応できた模様。AWSのELBに関しては、proxy protocolが使えるのであまり問題になっていなかった模様。

LoadbalancerにGCPのfirewall指定すればいいのでは?

この方法もkubernetesのannotateをすることで実施することができた。

単にfirewall設定をするだけでいけた。 https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/ をみると、loadBalancerSourceRanges で設定できるとある。

When using a Service with spec.type: LoadBalancer, you can specify the IP ranges that are allowed to access the load balancer by using spec.loadBalancerSourceRanges.

annotateで指定してあげると、即座にfirewallにsource rangeが反映される。

$ kd annotate services/deis-router service.beta.kubernetes.io/load-balancer-source-ranges="5.6.7.8/32"

これを打つと、annotationsにこれが追加される。

f:id:masato47744:20170422024306p:plain

そうすると、もともとあったfirewall ruleが更新される。

試しに、この状態で、service.beta.kubernetes.io/external-traffic=“OnlyLocal"を削除したら、接続できなくなった。 これがないとどちらにしろ、LBでfirewallを指定しても、deis-routerのnginxでIP制限したとしてもIP制限は働かないということになる。

まとめ

  • GKE上のDeisでIP制限するにはGCPのfirewallを使うか、deis/routerのnginxでIP制限するかの2通りのやり方がある
  • service.beta.kubernetes.io/external-traffic="OnlyLocalこれがないとClientIPが保存されない

deis-routerなしでKubernetesのIngressでDeisを動かす

Clusterを作る時にデフォルトでついてくるLBを外す

gloucd container clusters createをするときに、--disable-addons "HttpLoadBalancing"をつける。これをしないとGLBCがIngress Controllerとなってしまうため。コマンドラインからしか外せない。

cluster作成後どうなってるか

このときはkube-systemのnamesapce配下にkubernetesが動くために必要なものが起動されてる。addonを外したので、l7-default-backendなどは存在しない。

フラグ付きでDeisをインストール

$ helm install deis/workflow --namespace deis \
 --set global.experimental_native_ingress=true \
 --set controller.platform_domain=example.com

global.experimental_native_ingress=trueがポイント。

deis/routerの代わりにingressが作成されている。

$ kd describe ing
Name:           controller-api-server-ingress-http
Namespace:      deis
Address:
Default backend:    default-http-backend:80 (<none>)
Rules:
  Host              Path    Backends
  ----              ----    --------
  deis.example.com
                    /   deis-controller:80 (<none>)
Annotations:
Events: <none>

この段階でGCP側にこの辺が追加になる。

Ingress Controllerをインストール

今は、addonで外しているのでIngress Controllerがkubernetes上にいない状態。helmコマンドでIngress controllerをインストールする。今回は、treafikというLBのソフトウェアを利用する。

$ helm install stable/traefik --name deis-ingress-001 --namespace kube-system

これをすると

  • GCP上に80,443のHTTPを通すFirewallが作られ
  • TCP:80,443のL4ロードバランサーが作られ
  • treafikのimageがdeployされてそのL4 LBと結びつく

あとは、このとき作成されたLBのExternal IPとdeis.example.comをひけるAレコードを作成する。

Deisでアプリケーションをdeploy

通常通りregisterして、アプリケーションをdeployしたりすると以下のような動きをしてルーティングが行われる。

  • アプリケーションのnamespaceが追加
  • 該当のnamespaceにアプリケーションのdeploymentが追加
  • 該当のnamespaceにClusterIP型のserviceが追加
  • 該当のnamespaceにIngressも追加されて、そのClusterIP型のserviceを向く
     internet
        |
     [ LB ]
        |
   [ Traefik ]
   ____|____________________
  |                        |
[ deis/controller   ]     [ App ]

cluster作成時に作られるaddonのGLBCでやってみたが

これがうまくいかなかった。 まず、さっきと同じようにhelm installしたあとのFirewallとIngressの状態がちょっと違う。 まず、FirewallはGCP内のロードバランサーたちが所属するCIDRから、Nodeへの特定TCPポートを開くルールが作られる。これはhealthcheck用のようだ。

で、Ingressはdeis-controllerサービスが見つかりませんよーというエラーが出てくる。

$ kd describe ing
Name:           controller-api-server-ingress-http
Namespace:      deis
Address:        130.211.37.103
Default backend:    default-http-backend:80 (10.0.2.3:8080)
Rules:
  Host              Path    Backends
  ----              ----    --------
  deis.example.com
                    /   deis-controller:80 (<none>)
Annotations:
  forwarding-rule:  k8s-fw-deis-controller-api-server-ingress-http--90b3b4269724c70
  target-proxy:     k8s-tp-deis-controller-api-server-ingress-http--90b3b4269724c70
  url-map:      k8s-um-deis-controller-api-server-ingress-http--90b3b4269724c70
  backends:     {"k8s-be-31752--90b3b4269724c7ab":"Unknown"}
Events:
  FirstSeen LastSeen    Count   From            SubObjectPath   Type        Reason  Message
  --------- --------    -----   ----            -------------   --------    ------  -------
  3m        3m      1   loadbalancer-controller         Normal      ADD deis/controller-api-server-ingress-http
  2m        2m      1   loadbalancer-controller         Normal      CREATE  ip: 130.211.37.103
  2m        2m      3   loadbalancer-controller         Warning     Service Could not find nodeport for backend {ServiceName:deis-controller ServicePort:{Type:0 IntVal:80 StrVal:}}: Could not find matching nodeport from service.
  2m        2m      3   loadbalancer-controller         Normal      Service no user specified default backend, using system default

deis-controllerはtype:ClusterIPというクラスター内部IPのみで接続できるサービスとして公開されるようになってるが、このClusterIPとIngressとGLBCだと相性が悪く接続に失敗するようだった。ドキュメントにちゃんと書いとけよ的なissueもあがってた。

なので、現状は、GLBCをIngress ControllerとしたGKE上ではNative Ingressの利用は難しそうだった。。

まとめ

やってみるとまだ対応されてない(grafanaとかのルーティングがない)ところがあって、フラグについてるExperimentalは伊達じゃないと感じた。なので、やっぱりdeis/routerを使う方法にすることにした。

GKEだとデフォルトでIngress Controller(=GLBC)がいる

おまえ、そこにいたのか

kubernetesでIngressでやる場合、Ingress Controllerが必要なんだけど、GKEの場合はマスターノード、つまりGCPのマネージドな領域でクラスター作成時に一緒に作られるようになってた。

で、これをGLBC(GCE Load-Balancer Controller) Cluster Addonという。

Ingress Controllerを作るときはgcenginxのclassが標準で用意されてたけど、このgceっていうやつはGLBCのことで、ただ、GLBCGKEだと最初からaddonされるから作らなくてよいということだった。

自分で作りたい場合は、cluster作成時に、コマンドとかAPI経由でのみ、外せる。GUIコンソールからだと無理だった。

GLBCとは

なんとなく、GCPにおいてあるロードバランサーのことだろって思ってたけど違う

これはkubernetes側が勝手に名前をつけたやつで、GCPのリソースを組み合わせてL7のロードバランサー相当のものを作る命令のあつまりみたいなもの。まさにcontroller。あと、ルーティングしたときにどこにもあてはまらないリクエストがきたときにデフォルトの404ページを出してくれるだけのバックエンドのセット。

Ingressを作ると、GLBCがたたかれて、GLBCがGCPAPIをたたいて、L7のロードバランサーを実現するために、GCPでL7ロードバランサーをするための設定をしてくれる。

f:id:masato47744:20170422023108p:plain

で、そのGLBCはどこにいるかっていうとGKEの場合は、cluster作成時にマスターノードに作られていたって話でした。kube-systemっていうnamespaceが基本的にcluster作成時にできるものなんだけど、ここにGLBC相当のものがいないのではまった。ただ、さっき言った404出してくれるl7-default-backendはNodeの方にいるのでこの管理画面に表示されてるのが分かる。

f:id:masato47744:20170422023135p:plain

Deis Workflow v2.13.0でKubernetesのIngressがサポートされた件

Deisがverupした

本日、Deis Workflowがverupしたんですが、Changelogを見てるとIngressというものがなんか導入されてDeis/routerなくてもいけるみたいなことが。

f:id:masato47744:20171110022324p:plain

そもそもWhat is Ingress?

Kubernetesのたくさんあるリソースのタイプのうちの一つのIngress。 これについて学習したその記録を残す。

Kubernetesのリソース Service

まず、Ingressを理解する前にKubernetesにおけるServiceを理解する必要がある。ServiceもKubernetesのリソースのタイプのうちの一つで、簡単に言うとclusterと外部を接続を抽象化したもの。例えば、このserviceのtypeをLoadBalancerと書けば、kubernetesがいい感じにバックエンドのクラウドプロバイダーを見て勝手にLBを作ってくれる。 バックエンドがAWSならELB、GCPならGoolge Load Balancingという具合に。

こういう概念になる。

    internet
        |
  ------------
  [ Service ]  (例: ELBなど)
        |
  [ App     ] 

:point_up: ここでは分かりやすさのためにAppって書いてるけど、本当はKubernetesの場合、コンテナのセットのPodという単位が基本。本当はそのPodを集合したReplicaSet、それを司るDeploymentとかがいるんだけど、その辺は割愛。まぁアプリケーションが動いているコンテナがいると思ってくれればOK。

他に増えたらルーティングどうするの

さっきの例だと一つのServiceとあるバックエンドのアプリケーションという感じだった。これを複数のアプリケーション立ち上げようとするとどうなるかっていうと、Serviceをたくさん増やしてあげればいいんだけど、そうするとその分、外部から接続するためのIPアドレスがたくさん生成されるし、DNSレコードの管理とか面倒になってくる。DNSだとキャッシュとかあるし、迅速で柔軟な変更が難しいよね(って話がどっかに書いてあったんだけど忘れてしまった・・)

            internet
                |
              [ dns ]
     |-----------|-----------|
  ------------------------------------
   IP:1.2.3.4   IP:5.6.7.8  IP:9.1.2.3
  [ Service ]  [ Service ] [ Service ]
        |          |            |
  [   App  ]   [   App  ]   [   App  ]

じゃあDeis君はルーティングどうしてたのか

Deis/routerっていうcomponentがあって、それがLBと振り分けを行うnginxで構成されてた。GCP上で作るとこうなる。Load Balancerは1つなのでIPアドレスも一つ。

            internet
                |
             [ dns ]
                |
  -------------------------------------
           IP:1.2.3.4
           [ Service ] <- Google LoadBalnacing
                |
          [ Deis/router ] <- 簡単に言えばただのnginx。virtual hostでbackendに流す
                |
       ------------------------
      |          |            |
 [   App  ]   [   App  ]   [   App  ]

Deis/routerの代わりにKubernetesのリソース Ingress

Ingressは各Serviceへのルーティングのルールセットを抽象化したようなもの。L7のルーティングができるやつ。そのルールの書き方は、host baseだったり、pathベースだったりで書ける。また、SSL終端などの設定も書くことができる。 例:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        backend:
          serviceName: test
          servicePort: 80

ただ、Ingressはルールを抽象化しただけのobjectなのでこいつ自身がルーティングとか何かの処理を受けるということではなく、ルーティングしたりする実際の処理を行うIngress Controllerという実体へ命令するだけである。 なので、Ingressへルールを書くと、何が起きるかというと、そのルールを見て、バックエンドにいるIngress ControllerへAPIを発行するなり、configを突っ込んだりするなどの命令を出すだけの存在。なので、terraform的なものに近いのかもしれない。

Ingress Controllerとは

github.com

で、Ingress Controllerってなんなのかっていうと、結局は、LoadBalancerの実装である。標準で用意されているIngress ControllerはNginxとGCEという2つがあり、Nginxは単にnginxでできたLBの実装である。 もちろん、自分でIngress Controllerを作ることもできる。例として、traefikというGolangでできたReverse Proxy的なやつをIngress Controllerとする例がDeisの公式にはのっていた。

GCEの場合は、Google Loadbalancing、URL MapなどGCPのリソースでそれを実現するようになってる。

なので、Ingressを使った場合はこのようになる。下の図の外部と接続するServiceからAppと接続するための内部用Serviceあたりまでを多分管理してくれる。

            internet
                |
             [ dns ]
                |
  -------------------------------------
           IP:1.2.3.4
           [ Service ] <- Google LoadBalnacing
                |
  [ HTTP TargetProxy や URL map ] <- GCPの振り分けしてくれるやーつ
                |
       ------------------------
      |          |            | 
 [ Service ]  [ Service ] [ Service ] <- 内部IPだけのNodePortというtypeのサービス
      |          |            |
 [   App  ]   [   App  ]   [   App  ]

まとめ

なので、Ingressを使えば、Deis/routerはいらなくなるっていう話。Deis/routerは設定が色々面倒なので、Ingressを利用した方が色々とすっきりしそうだし、わざわざこれが作られるってことは今後はDeis/routerからIngressベースのものになっていくのではと予想している。 というか、Deis君が実装しているものはどんどんKubernetesが吸収していき、いつかDeis君はいなくなるのではという未来が見えてきた。

あと、KubernetesはIngressやServiceのように現実世界のある何かを一つのものに抽象化して、裏側でよしなに実装がせっせと書いてあるという仕組みになっている。ほんと発想がすごい なので、Kubernetesやるときは、あんまりTerraformの出番がない。ないけど、あんまり困らない。なぜかっていうとKubernetesもTerraformみたいなものでもあるから。

【AWSしかやったことない人向け】AWSとGCPのネットワークの違いを理解してみよう

背景

  • AWSでVPC作ったりしたことあるけど、GCPやったことないって人は色々違いに戸惑う
  • アカウントの関係性、ネットワークの概念の違いなどを理解したのでまとめた

AWSとGCPのアカウントの考え方の違い

AWSの場合、ある人間に対して色々なAWSアカウントが付与される。

(人間)  ->  個人で発行されたアカウント
        ->  会社で発行されたアカウント

GCPの場合、ある人間は色々なプロジェクトというものに属することができる。 そして、その人間が使うアカウントはGmailとかで使ってるようなGoogleアカウントである。

(人間 with Googleアカウント)  ->  プロジェクト 会社のやつ
                           ->  プロジェクト 何の関係もない個人のやつ

GCPの方が始めるのはすごく簡単な気がする。アカウント管理もGoogleアカウントさえやっときゃいいみたいな感じなので、Googleにロックインされる感はある。

まずはAWSのネットワークについて説明するよ

VPCとsubnetについて

AWSではこの2つが基本となる考え方。VPCは各region内における仮想的なネットワークで、subnetはそれらをさらに分割した論理的な単位。 VPCはregionをまたぐことはできず、regionごとに作ることしかできない。region内で複数VPCを作ることは可能。

subnetはVPCに属しているのでVPCで決めたネットワーク範囲内(下の図だと172.16.0.0/16)の範囲でのみ作成できる。

f:id:masato47744:20170422015409j:plain

この上の図は、AWSでインフラ作るときに、ごく一般的な構成を図示している。各regionごとに、VPCを作り物理的にはavailability-zoneで分かれていてその中にsubnetを作っていくみたいなやつ。 subnetをpublic subnetprivate subnetと便宜的に呼び、public subnetには、NAT GatewayとpublicなIPアドレスを持ったインスタンスを置く。private subnetにはpublicなIPアドレスを持たないインスタンスをおいておく。 private subnetが外部と通信する場合は、NAT Gateway経由で外に出て行き、private subnet内のインスタンスに接続するには、public subnetにある踏み台インスタンスなど経由で接続をする。

security groupについて

AWSでは、各インスタンスの通信の制限を基本的にsecurity groupで行う。ACLもあるけどね

security groupの考え方は、特定のインスタンスや、特定のIPアドレスからの特定のプロトコルを受け付けるというものを作成して、それをインスタンスに付与していく。

f:id:masato47744:20170422015425p:plain

上の図でいえば、例としてsg-1とsg-2、sg-3が作成されている。

sg-1はpublic subnetにあるi-01のインスタンスからのみsshを受けるというルールで、private subnet内にあるi-02とi-03にルールを付与するというやり方をとる。俗に言う踏み台とかbastionと呼ばれるインスタンスのやり方。 こうすると、private subnet内のインスタンスは踏み台経由でないとsshログインできないということになる。

sg-2も同様に別のavailability zoneにおける踏み台のsecurity groupを表現している。 sg-3は、i-01とi-04のインスタンスへ外部からsshアクセスできるということ。

こんな風にまずは、セキュリティグループを作り、それを各インスタンスにつけていくという流れをとるのがAWS流の基本的なやり方。

ここまでを踏まえて、次でGCPのネットワークについて説明します。

GCPのネットワーク

ネットワークサブネットワークという概念

GCPにおいて、各プロジェクトはネットワークというものを複数作成することができる。さらに、ネットワークは複数のサブネットワークを持つことができる。

まず、ネットワークという単位には、CIDRはない。なんとなくVPCと対比で考えるとCIDRがあって欲しいところだけどない。あくまでも論理的なオブジェクトでしかない。

このネットワークに対して、サブネットワークというものを切ることができる。このサブネットワークはCIDRを持っている。ここがポイント。

この辺を図で説明してみる。

f:id:masato47744:20170422015716j:plain

この図はGCPにおけるネットワーク構成。AWSのVPC的なネットワーク構成とだいぶ異なっている。なおこの図では、プロジェクトを作ると必ず作成されるデフォルトネットワークと呼ばれるものを示している。

デフォルトネットワークだと、必ずリージョンごとにサブネットワークが一つ作成される。GCPだと、AWSのVPCのようにregionごとのネットワークではなく、なんと、最初から全リージョンをまたいだネットワークが作成されてるのだ。つまり、全リージョンまたいだインスタンスそれぞれに内部IPアドレスでアクセスできるということを示している。

じゃあ、なんでサブネットワークなんてものがあるのかというと、単に名前をつけたいというぐらいのものであると思って欲しい。 たとえば、全世界のリージョンにインスタンスがあるけれど、sshをするのは日本のリージョンだけとしたいみたいなときに、CIDRをつけられるサブネットワークがないと、日本のインスタンスたちが属するIPアドレスの範囲が分からず、IPアドレスガチャ状態になってしまう。

Nat Gatewayなんてものは基本的にない

GCPはとても男らしくて、基本的に全インスタンスに外部IPをつけようとする。 で、外部IPがつけられたインスタンスが外部に通信しにいくときは、そのインスタンスにつけられた外部IPをsourceIPとして接続しにいく。 逆に外部からそのインスタンスに通信しにいくときもこの外部IPを使って接続しにいく。

GCPの場合、sshなどもGoogleアカウントと統合されてるので基本的に自分で.ssh/authorized_keysに書き込んだりはしない。Googleアカウント側を2段階認証などで守ればそれでOKみたいという考えだろうか。まぁどんな環境だろうと必ずどこかは穴を開けないといけないので同じっちゃ同じ。

もちろん、外部IPをなしにすることもできる。その場合、このインスタンスは外部への通信も外部からの通信も受けることができない。ただ、内部IPはあるので、ネットワーク内のインスタンスであれば通信することは可能である。

どうしても、NAT Gatewayのようなことがやりたければ、通常のインスタンスをたててNATの役割をさせて、すべてのインスタンスの外部IPをなくして、ルートテーブルを修正して、NATインスタンス経由で外に出て行くようにすればいい。 NATの役割をする場合は、インスタンスを作成するときにルーティング可能にできるオプションを選択するだけでよい。

Firewall

GCPにはFirewallと呼ばれるAWSのSecurityGroup相当のものがある。このFirewallという機能がGCPとAWSで随分と違う部分。 GCPの場合は、各インスタンスにタグと呼ばれるものを好きなだけ貼ることができる。このタグをベースにネットワークの制限をつけていく。

ここで再びさっきの上の図を詳しく見ていく。

f:id:masato47744:20170422020010p:plain

firewallルールのrule2を見ると、3.4.5.6からのTag:aへのHTTP接続を受け付けるというルールを作成してある。この場合、Tag:aがついているすべてのインスタンスは、外部からhttp接続できるということである。

rule3を見ると、10.146.0.0/20からTag:bへのHTTP接続を受け付けるというルールを作成してある。この場合、tokyo regionのサブネットワーク内にあるVMインスタンスすべて(VM1,2,3)からus regionにあるTag:bがついているインスタンスはHTTP接続を受け付けるということである。

さらに、rule1で、すべてのインスタンスは10.0.0.0/8からのSSH接続を受け付けるというルールを設定しておく。そうすると、このネットワーク内のインスタンスは10.0.0.0/8に属する(10.0.0.1 ~ 10.255.255.254)ので、お互いにssh接続ができるという仕組み。このように特定のタグではなく、すべてのターゲットという指定もできる。

Firewallのソースフィルタ

rule1のような、特定のIPアドレス範囲から受け付けるという部分をソースフィルタという。これも結構変わった考え方で面白い。どういうことかというと、CIDRで例えばクラスBとクラスCのアドレス同士でも設定できるということ。

ここで出てくるのがさっき説明したサブネットワークだが、デフォルトネットワーク以外であれば、サブネットワークには好きなCIDRをつけられる。

|サブネットワークA| 172.20.0.0/20

                |インスタンス A1| 172.20.10.5   <------  
                                                                               |
|サブネットワークB| 192.168.100.0/24                |  HTTP
                                                                               |
                |インスタンス B1| 192.168.100.99 -----         

とした場合、通常は別ネットワークとなるがFirewallのソースフィルタでは単なる名前でしかないので関係なく指定することができる。 サブネットワークAにインスタンスA1、サブネットワークBにインスタンスB1がいたとして、Firewallとしてはソースフィルタに、192.168.100.0/24を指定してhttpを許可したとしてタグを作成して、そのタグをA1につければ、A1はB1からhttp接続を受け付けられるようになる。

これはAWSにはなかった概念だと思う。

まとめ

AWSとGCPの違いを人に説明しようと思ってみたけど説明しようとすると自分でもよくわかってない部分にぶちあたるという感じになった。

GCPも調べてみると色々利点があり、(安い、全世界またがったネットワークが強い、googleアカウントとの統合がうまい、LBの温め不要)ちょっと好きになってきた

両者の違いをかなり個人的な感想だけどこんなイメージ。

  • AWSさんはベーシックなネットワーク構成をクラウド上に再現して顧客のためにどんどん使いやすいようにがんばってくれる優等生
  • GCPはVPC作って、subnet作ってとかそういうのめんどくさいから、もうVMインスタンス主役でちゃっちゃとグーグルアカウントでやっちゃおうぜ、あ、俺らが使ってるplatformそのまま貸してあげるけど俺らの新しい概念についてきてね!

みたいなスタンスの差を感じた。

Kubernetsに出てくる用語:PVCとは?

PVCとは?

KubernetesのPersistentVolumeClaimsの略である。 https://kubernetes.io/docs/user-guide/persistent-volumes/#persistentvolumeclaims

A persistentVolumeClaim volume is used to mount a PersistentVolume into a pod. PersistentVolumes are a way for users to “claim” durable storage (such as a GCE PersistentDisk or an iSCSI volume) without knowing the details of the particular cloud environment.

KubernetesのVolume

まず、PVCを理解するには、そもそもkubernetesにはVolumeという概念があることを知らないといけない。 https://kubernetes.io/docs/concepts/storage/volumes/ Volumeはdockerの-vでvolumeをマウントするような感じで、色々なVolume typeをサポートしていて、例として、nodeのDiskを利用してpodが削除されたら一緒に消えるemptyDir、GCEのpersistent diskを利用するgcePersistentDiskAWSのEBSを利用するawsElasticBlockStore、gitのrepositoryを利用するgitRepoなどがある。

例えば、volume typeでGCPgcePersistentDiskを利用したければ、先にgcloudコマンドなどでGoogle persistent diskを作成しておいて、このvolume typeでgcePersistentDiskを指定してあげるという流れになるが、それをkubernetesがdiskも作成してくれるという方法がある。

PersistentVolume

それがPersistentVolumeという仕組み。podと同じような感じでyamlで記述できて、容量(10GB)とか、読み書き権限とか、どこのプロバイダーで作るかみたいなものを記述する。こんな感じ。kindがPersistentVolume

  apiVersion: v1
  kind: PersistentVolume
  metadata:
    name: pv0003
  spec:
    capacity:
      storage: 5Gi
    accessModes:
      - ReadWriteOnce
    persistentVolumeReclaimPolicy: Recycle
    storageClassName: slow
    nfs:
      path: /tmp
      server: 172.17.0.2

やっとPersistentVolumeClaimsにたどり着いた

でも、PersistentVolumeだと容量を動的に変えたいとか、GCPじゃなくてAWSにやっぱり作りたいとかそもそもいらない、みたいな感じで動的に作成することはできない。 そのためにあるのが、PersistentVolumeClaimsです。これは最近できた?やつっぽくて、StorageClassGCPとかAWSとか指定しておけば、あとは、設定値とかで必要になったタイミングで作成してくるというもの。

Deisの場合、install時にあるcomponentをcluster外に自分で作るか、cluster内に作ってもらうか選べる。そんなときに、PersistentVolumeClaimsで指定しておけば、ユーザーがcluster内にあるcomponetをたてたい、しかも、diskをpersistentしたいってときに、動的にサイズを指定しつつそのproviderにあったディスクをたて、mountまでしてくれるというもの。

なので、多分、podのmountのとこに、volumeType: PersistentVolumeClaimsみたいに書いてあるはず。

claimは苦情をつける、注文をつけるみたいな意味なので、Persistent Volumeを注文するみたいな風にとらえればよい。

まとめ

何か分からない用語が出てくると、deisの概念なのか、kubernetesの概念なのか、GCPの概念なのかで小一時間理解に時間がかかる:joy:

Deisのpostgresをoff-clusterにする(postgresql on GCEを利用)

やること

mpon.hatenablog.com

前回はCloud SQLを使ったが、Cloud SQLはデフォルトネットワーク外にあるので、Cloud SQL Proxyをたてて通信するか、外部IPを許可するしかなかった。Deisの場合だと、proxyたてるパターンを使えず、外部IPを許可する方法にしたけど、これだとNodeのIPが変わったり増えたときに対応できない。 なので、素直にGCE上にpostgresqlをたてることにした。

Cloud Launcher

postgreSQLをinstallとかめんどくさいなーって思ってたら、Cloud Launcherなるものを発見した。ぽちぽちっとやるだけで、いい感じにインストールまでしてくれる。本当の本当の本番ならこれはよくないかもしれないけど、今回はこれを使うことにした。

こんな感じで選んで、

f:id:masato47744:20170422013707p:plain

できた。

f:id:masato47744:20170422013800p:plain

単なるVMインスタンスとして出来上がっているので、まぁあとから修正は好きなように加えられるだろう。

f:id:masato47744:20170422013848p:plain

sshするには

外部IPが振られているNode経由で、ログインできる。

$ gcloud compute ssh --ssh-flag="-A" <NODE_INSTANCE_NAME>
# gcloudコマンドでmponユーザーとしてGoogleの認証がうまいことやってくれてる
# さらに内部のインスタンスへはインスタンス名を入れるだけでつなげる。
# この辺がGCPのすごいところ。
mpon@gke-xxxxxxx-p-default-pool-d02cceda-15xb ~ $ ssh deis-postgres-vm
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
       ___ _ _                   _
      | _ |_) |_ _ _  __ _ _ __ (_)
      | _ \ |  _| ' \/ _` | '  \| |
      |___/_|\__|_|_|\__,_|_|_|_|_|

  *** Welcome to the Bitnami PostgreSQL 9.6.2-0 ***
  *** Service accessible using hostname 10.146.0.5 port 5432 ***
  *** Documentation:  https://docs.bitnami.com/google/infrastructure/postgresql/ ***
  ***                 https://docs.bitnami.com/google/ ***
  *** Bitnami Forums: https://community.bitnami.com/ ***
mpon@deis-postgres-vm:~$

あとは、configuring-postgresの内容でdeis君用のdatabaseとuserを作るだけ。

helm install

deis-postgres-vmの内部IPは、例によってコマンドでとれる。

  def sql_instance_address
    res = `gcloud compute instances describe deis-postgres-vm`
    YAML.load(res)["networkInterfaces"].first["networkIP"]
  end

これで持って、helm installすると・・・・

$ kd get pods
NAME                                     READY     STATUS    RESTARTS   AGE
deis-builder-1364651073-9kcjd            1/1       Running   0          28m
deis-controller-2711454920-5v8p5         1/1       Running   0          28m
deis-logger-176328999-qmdqv              1/1       Running   5          28m
deis-logger-fluentd-9frz3                1/1       Running   0          28m
deis-logger-fluentd-hkkcl                1/1       Running   0          28m
deis-logger-fluentd-xlrfz                1/1       Running   0          28m
deis-logger-redis-304849759-wqb0f        1/1       Running   0          28m
deis-monitor-grafana-432627134-1f8qr     1/1       Running   0          28m
deis-monitor-influxdb-2729788615-qlmc4   1/1       Running   0          28m
deis-monitor-telegraf-l8ttq              1/1       Running   2          28m
deis-monitor-telegraf-sscvx              1/1       Running   1          28m
deis-monitor-telegraf-svpxc              1/1       Running   1          28m
deis-nsqd-3597503299-rcjr8               1/1       Running   0          28m
deis-registry-169338602-2jkwm            1/1       Running   0          28m
deis-registry-proxy-6m6fx                1/1       Running   0          28m
deis-registry-proxy-mqwm8                1/1       Running   0          28m
deis-registry-proxy-vtn3v                1/1       Running   0          28m
deis-router-2126433040-9jrs7             1/1       Running   0          28m
deis-workflow-manager-2528409207-jt8fq   1/1       Running   0          28m

無事動いたでござる

まとめ

GCPのネットワークは、AWSVPC的な考えとは結構違うのでそこに慣れる必要がある。

追記:これで解決

mpon.hatenablog.com

Deisのpostgresをoff-clusterにする(Cloud SQLを利用)

やること

postgresをoff-clusterする。 で、そのpostgresをどこに立てるかというとGCEにたてるかGCPのmangaedなCloud SQL(RDSみたいなやつ)があるので、どっちにするかということで、今回はCloud SQLを使ってみた。

Cloud SQL PostgreSQL beta

Cloud SQLのポスグレはまだbeta版。なので、terraformにも来てない。けど、GCPは全体的に、公式docにGUIでconsoleでやるか、CLIでやるかっていうのが並列で揃ってて、とても親切。AWSみたいに、AWS Consoleで突然何かが色々できてしまうってことがない感じ。

f:id:masato47744:20170422013302p:plain

作成は、rakeタスクにまとめるとこんな感じ。簡単

namespace :gcp do
  task :create_postgresql do
    sh %Q(gcloud beta sql instances create deis-cloudsql-postgres \
          --database-version=POSTGRES_9_6 \
          --activation-policy=ALWAYS \
          --backup-start-time=16:00 \
          --gce-zone=asia-northeast1-a \
          --maintenance-window-day=SUN \
          --maintenance-window-hour=17 \
          --tier=db-n1-standard-1 \
          --storage-type=SSD \
          --storage-size=10GB \
          --storage-auto-increase \
          --region=asia-northeast1
          )
  end
end

deis君からCloud SQLに接続するには

Cloud SQLに接続するには、何通りか方法があるけど、基本的にcloud sql proxyというのをたてて、それ経由でアクセスするっていうのがいい方法のようだ。 関係ないけど、Kubernetsとかにも言えるけどこういうのうまいなって思う。

cloud sql proxyを起動したマシン上で、localhostに対してDB接続しにいくと、つながるようになってる。

で、GKEの場合どうするかっていうと、sidecarパターンというやり方で、同一Pod内にcloud sql proxyのdocker image(imageが用意されてるところも憎い!)を起動させてそれをmountしてlocalhostをproxyとして接続できるようになる。うまい。

ただ、Deis君の場合は既にpodは固定されていて、ちょっと個別にpodを書き換えたりするのは面倒・・・っていうかやり方分からない。forkして、自分で追加したりしないといけないのかもしれない。なので、cloud sql proxyのパターンは使えない。もしかしたら、clusterたてるときに各nodeに初期処理入れてcloud sql proxyのinstallとかできるのかな。。

Cloud SQLは外部からアクセスする際に、IPアドレスを指定しておけば、そこからのアクセスは受け付けるようになる。なので、今回は、GKEのnodeのIPアドレスを指定してあげたら接続できた。 AWSだとNAT Gatewayがいたりするんだけど、GCPの場合はそういうのなさそう?やるなら、自分でNAT instance立てろ的な感じかもしれない。

nodeのIPアドレスたちは、kubectlコマンド使ってとれるようになってる。便利。この情報は、kubectl Cheat Sheetに書いてあった。

kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'

deis用のユーザーとdatabaseの作成

GCPは、cloud shellっていうコンソールからssh接続できる風なやつが用意されてて、それ経由で作成してもOK。(AWSにも欲しい・・)

f:id:masato47744:20170422013525p:plain

ただ、これもCLIが用意されてるのでそっちのがいい。ので、そうした。

  task :create_database do
    sh %Q(gcloud beta sql users set-password postgres no-host \
          --instance=deis-cloudsql-postgres --password=#{ENV["POSTGRES_PASSWORD"]}
          )
    sh %Q(gcloud beta sql users create deis no-host \
          --instance=deis-cloudsql-postgres --password=#{ENV["POSTGRES_DEIS_PASSWORD"]}
          )
    sh %Q(gcloud beta sql databases create deis \
          --instance=deis-cloudsql-postgres
          )
  end

off-clusterにしてhelm install

さっき作ったdbの情報をvalues.yamlに書く。

cloud sqlIPアドレスは、gcloudコマンドでとれる。結果はYAML形式でかえってくるので、rubyだといい感じに処理できる。それを--set database.postgres.hostで渡してあげればOK。passwordとかも、環境変数に入れておいてinstallをする。

namespace :helm do
  task :install do
    sh %Q(helm install deis/workflow --namespace deis -f values.yml \
          --set gcs.key_json="$(cat gcloud-service-key.json | base64)" \
          --set database.postgres.password=#{ENV["POSTGRES_DEIS_PASSWORD"]} \
          --set database.postgres.host=#{sql_instance_address}
        )
  end
  
  def sql_instance_address
    res = `gcloud sql instances describe deis-cloudsql-postgres`
    YAML.load(res)["ipAddresses"].first["ipAddress"]
  end
end

動いているようだ

$ kd get pods
NAME                                     READY     STATUS    RESTARTS   AGE
deis-builder-1364651073-83r7x            1/1       Running   0          43m
deis-controller-2711454920-g32nn         1/1       Running   0          43m
deis-logger-176328999-06nwq              1/1       Running   2          43m
deis-logger-fluentd-fbg4m                1/1       Running   0          43m
deis-logger-fluentd-nt0tq                1/1       Running   0          43m
deis-logger-fluentd-ttlwl                1/1       Running   0          43m
deis-logger-fluentd-zc6hd                1/1       Running   0          43m
deis-logger-redis-304849759-njc4w        1/1       Running   0          43m
deis-monitor-grafana-432627134-b12zt     1/1       Running   0          43m
deis-monitor-influxdb-2729788615-6pl28   1/1       Running   0          43m
deis-monitor-telegraf-5rhmv              1/1       Running   1          43m
deis-monitor-telegraf-ckfvc              1/1       Running   1          43m
deis-monitor-telegraf-pdhj8              1/1       Running   1          43m
deis-monitor-telegraf-q0d2h              1/1       Running   1          43m
deis-nsqd-3597503299-sxcss               1/1       Running   0          43m
deis-registry-169338602-4hzmk            1/1       Running   0          43m
deis-registry-proxy-7h5f2                1/1       Running   0          43m
deis-registry-proxy-k8vv4                1/1       Running   0          43m
deis-registry-proxy-np9ks                1/1       Running   0          43m
deis-registry-proxy-xcsx8                1/1       Running   0          43m
deis-router-2126433040-7js0d             1/1       Running   0          43m
deis-workflow-manager-2528409207-55vjg   1/1       Running   0          43m

うまくいってないと、STATUSがCrashになってたり、RESTARTSが増え続ける。 ちなみに、kdkubectl --namespace=deisのalias。Tipsが公式に書いてあた

追記:これで解決

mpon.hatenablog.com

deisでminioを置き換えるだけをやってみる

公式ページを参考に

Configuring Object Storage

本当にここを変えただけ。bucketはGCSであらかじめ作っておく。権限とかもなんもしてない。

diff --git a/values.yml b/values.yml
index c25cfb2..1d94aca 100644
--- a/values.yml
+++ b/values.yml
@@ -7,7 +7,7 @@ global:
   # - azure: Store persistent data in Azure's object storage
   # - gcs: Store persistent data in Google Cloud Storage
   # - minio: Store persistent data on in-cluster Minio server
-  storage: minio
+  storage: gcs

   # Set the location of Workflow's PostgreSQL database
   #
@@ -75,9 +75,9 @@ gcs:
   # key_json is expanded into a JSON file on the remote server. It must be
   # well-formatted JSON data.
   key_json: <base64-encoded JSON data>
-  registry_bucket: "your-registry-bucket-name"
-  database_bucket: "your-database-bucket-name"
-  builder_bucket: "your-builder-bucket-name"
+  registry_bucket: "my-registry"
+  database_bucket: "my-database"
+  builder_bucket: "my-builder"

deisをinstallする

適当にRakeタスク作って、コマンド実行。--setでsecrety keyを渡せるのいいね:100:

namespace :helm do
  task :install do
    sh %Q(helm install deis/workflow --namespace deis -f values.yml \
          --set gcs.key_json="$(cat gcloud-service-key.json | base64)"
        )
  end
end

状態

確かにminioはいなくなった。

$ helm status nobby-gibbon
LAST DEPLOYED: Tue Mar 28 20:41:00 2017
NAMESPACE: deis
STATUS: DEPLOYED

RESOURCES:
==> v1/ServiceAccount
NAME                   SECRETS  AGE
deis-nsqd              1        9m
deis-workflow-manager  1        9m
deis-router            1        9m
deis-registry          1        9m
deis-database          1        9m
deis-builder           1        9m
deis-logger-fluentd    1        9m
deis-controller        1        9m
deis-logger            1        9m
deis-monitor-telegraf  1        9m

==> v1/Service
NAME                    CLUSTER-IP    EXTERNAL-IP     PORT(S)                                                   AGE
deis-monitor-influxui   10.3.254.23   <none>          80/TCP                                                    9m
deis-logger-redis       10.3.249.48   <none>          6379/TCP                                                  9m
deis-registry           10.3.250.48   <none>          80/TCP                                                    9m
deis-database           10.3.242.10   <none>          5432/TCP                                                  9m
deis-monitor-influxapi  10.3.241.35   <none>          80/TCP                                                    9m
deis-workflow-manager   10.3.242.232  <none>          80/TCP                                                    9m
deis-builder            10.3.244.107  <none>          2222/TCP                                                  9m
deis-logger             10.3.253.37   <none>          80/TCP                                                    9m
deis-monitor-grafana    10.3.245.167  <none>          80/TCP                                                    9m
deis-router             10.3.252.162  35.190.228.247  80:32439/TCP,443:30793/TCP,2222:30898/TCP,9090:30627/TCP  9m
deis-nsqd               10.3.246.21   <none>          4151/TCP,4150/TCP                                         9m
deis-controller         10.3.241.43   <none>          80/TCP                                                    9m

==> extensions/v1beta1/Deployment
NAME                   DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
deis-monitor-grafana   1        1        1           1          9m
deis-controller        1        1        1           1          9m
deis-logger            1        1        1           1          9m
deis-nsqd              1        1        1           1          9m
deis-database          1        1        1           1          9m
deis-builder           1        1        1           1          9m
deis-workflow-manager  1        1        1           1          9m
deis-router            1        1        1           1          9m
deis-registry          1        1        1           1          9m
deis-logger-redis      1        1        1           1          9m
deis-monitor-influxdb  1        1        1           1          9m

==> extensions/v1beta1/DaemonSet
NAME                   DESIRED  CURRENT  READY  NODE-SELECTOR  AGE
deis-monitor-telegraf  4        4        4      <none>         9m
deis-registry-proxy    4        4        4      <none>         9m
deis-logger-fluentd    4        4        4      <none>         9m

==> v1/Secret
NAME                   TYPE    DATA  AGE
deis-router-dhparam    Opaque  1     9m
objectstorage-keyfile  Opaque  4     9m
minio-user             Opaque  2     9m

==> v1/ConfigMap
NAME                  DATA  AGE
dockerbuilder-config  2     9m
slugbuilder-config    2     9m
slugrunner-config     1     9m

GCSのbucketの中になんか入ってる

databaseにだけ入ってた。これがなんか状態を保存してるやつなんだろうな。

f:id:masato47744:20170422013114p:plain

deisのアンインストール

なんとなく分かったのでアンインストール

$ helm list
NAME            REVISION    UPDATED                     STATUS      CHART               NAMESPACE
nobby-gibbon    1           Tue Mar 28 20:41:00 2017    DEPLOYED    workflow-v2.12.0    deis
$ helm delete nobby-gibbon

これだけだと、secret情報が消えなかったので次に再度helm installするときに、エラーになってしまう。こんな感じで残っちゃうようだ。

$ kubectl get secrets -n deis
NAME                       TYPE                                  DATA      AGE
builder-key-auth           Opaque                                1         18m
builder-ssh-private-keys   Opaque                                3         18m
database-creds             Opaque                                2         18m
default-token-vdgrd        kubernetes.io/service-account-token   3         19m
deis-workflow-manager      Opaque                                1         17m
deploy-hook-key            Opaque                                1         18m
django-secret-key          Opaque                                1         18m
logger-redis-creds         Opaque                                1         18m

なので、コマンドラインで一気に消す。今までのぼくなら1個ずつ手動入力してたけどlazy nightに参加したのでがんばって怠惰になるためにワンライナーでやる。

$ kubectl get secrets -n deis | awk '{print $1}' | tail -n +2 | xargs kubectl delete secret -n deis

追記

ただ、helm deleteだけだと、完全には消えなかったので、helm delete --purgeをつける必要があった。

Deisをproduction運用するための設定の仕方調査

背景

Deisはhelmコマンドでインストールすれば簡単に入るけど、そのままだとデータ保存先がephemeralなものになってしまうのでダメ。他にもproduction運用のためにした方が設定があるのでまとめる。

調べ方

変えなきゃいけなさそうなところ

  • kubernetesのバージョン
  • クラスタインスタンスの推奨スペック
  • object storage はminioからGCS
  • postgres は Cloud SQL for postgresql or postgresql on GCE
  • docker registryはGCR
  • firewallの設定。そのままだと0.0.0.0/0からつながってしまう。
  • deis registerできるのをadminだけにする。controllerの設定
  • Grafanaのsign upをdisableにする。
  • SSLの設定。そのままだとオレオレ認証のまま?
  • monitorのinfluxDBの設定。そのままだとmetricsが永続化されない。
  • DNS
  • routerをscaleさせた方がいいとある -> 今回はいらないかも?

変え方

kubernetsの色んなリソースをまとめたものをChartという。まぁざっくりいうとyumとかhomebrew的なもの。 実はChartには海図という意味があり、kubernetesがギリシャ語で船長みたいな意味で、helmが舵という意味なので、kubernetesがchartをもとにhelmをとると考えればなんとなく意味が分かる。

Chartはある決まったstructureをしてるんだけど、それのうちパラメータを設定できるのがvalues.ymlというもの。CLI経由で--setで一つ一つパラメータを渡すこともできるけどめんどいから基本やらない。ただ、--setでoverrideするとか追加するとかはやりそうな気がする。 で、DeisもChartで管理されてるので、当然values.yml経由でパラメータを変更する。

values.ymlの持ってきかたは、helm inspectサブコマンドで取得できる。valuesをつけなければ、values以外の基本的な情報を取得できたりする。ちなみに、当然だけど、deis/workflow以外のものの場合も同様で、deis/workflowの部分をstable/mysqlとかにすれば、情報がとれる。

helm inspect values deis/workflow | sed -n '1!p' > values.yml

values.ymlの内容はこれ

global:
  # Set the storage backend
  #
  # Valid values are:
  # - s3: Store persistent data in AWS S3 (configure in S3 section)
  # - azure: Store persistent data in Azure's object storage
  # - gcs: Store persistent data in Google Cloud Storage
  # - minio: Store persistent data on in-cluster Minio server
  storage: minio

  # Set the location of Workflow's PostgreSQL database
  #
  # Valid values are:
  # - on-cluster: Run PostgreSQL within the Kubernetes cluster (credentials are generated
  #   automatically; backups are sent to object storage
  #   configured above)
  # - off-cluster: Run PostgreSQL outside the Kubernetes cluster (configure in database section)
  database_location: "on-cluster"

  # Set the location of Workflow's logger-specific Redis instance
  #
  # Valid values are:
  # - on-cluster: Run Redis within the Kubernetes cluster
  # - off-cluster: Run Redis outside the Kubernetes cluster (configure in loggerRedis section)
  logger_redis_location: "on-cluster"

  # Set the location of Workflow's influxdb cluster
  #
  # Valid values are:
  # - on-cluster: Run Influxdb within the Kubernetes cluster
  # - off-cluster: Influxdb is running outside of the cluster and credentials and connection information will be provided.
  influxdb_location: "on-cluster"
  # Set the location of Workflow's grafana instance
  #
  # Valid values are:
  # - on-cluster: Run Grafana within the Kubernetes cluster
  # - off-cluster: Grafana is running outside of the cluster
  grafana_location: "on-cluster"

  # Set the location of Workflow's Registry
  #
  # Valid values are:
  # - on-cluster: Run registry within the Kubernetes cluster
  # - off-cluster: Use registry outside the Kubernetes cluster (example: dockerhub,quay.io,self-hosted)
  # - ecr: Use Amazon's ECR
  # - gcr: Use Google's GCR
  registry_location: "on-cluster"
  # The host port to which registry proxy binds to
  host_port: 5555
  # Prefix for the imagepull secret created when using private registry
  secret_prefix: "private-registry"


s3:
  # Your AWS access key. Leave it empty if you want to use IAM credentials.
  accesskey: ""
  # Your AWS secret key. Leave it empty if you want to use IAM credentials.
  secretkey: ""
  # Any S3 region
  region: "us-west-1"
  # Your buckets.
  registry_bucket: "your-registry-bucket-name"
  database_bucket: "your-database-bucket-name"
  builder_bucket: "your-builder-bucket-name"

azure:
  accountname: "YOUR ACCOUNT NAME"
  accountkey: "YOUR ACCOUNT KEY"
  registry_container: "your-registry-container-name"
  database_container: "your-database-container-name"
  builder_container: "your-builder-container-name"

gcs:
  # key_json is expanded into a JSON file on the remote server. It must be
  # well-formatted JSON data.
  key_json: <base64-encoded JSON data>
  registry_bucket: "your-registry-bucket-name"
  database_bucket: "your-database-bucket-name"
  builder_bucket: "your-builder-bucket-name"

swift:
  username: "Your OpenStack Swift Username"
  password: "Your OpenStack Swift Password"
  authurl: "Swift auth URL for obtaining an auth token"
  # Your OpenStack tenant name if you are using auth version 2 or 3.
  tenant: ""
  authversion: "Your OpenStack swift auth version"
  registry_container: "your-registry-container-name"
  database_container: "your-database-container-name"
  builder_container: "your-builder-container-name"

# Set the default (global) way of how Application (your own) images are
# pulled from within the Controller.
# This can be configured per Application as well in the Controller.
#
# This affects pull apps and git push (slugrunner images) apps
#
# Values values are:
# - Always
# - IfNotPresent
controller:
  app_pull_policy: "IfNotPresent"
  # Possible values are:
  # enabled - allows for open registration
  # disabled - turns off open registration
  # admin_only - allows for registration by an admin only.
  registration_mode: "admin_only"

database:
  # The username and password to be used by the on-cluster database.
  # If left empty they will be generated using randAlphaNum
  username: ""
  password: ""
  # Configure the following ONLY if using an off-cluster PostgreSQL database
  postgres:
    name: "database name"
    username: "database username"
    password: "database password"
    host: "database host"
    port: "database port"

redis:
  # Configure the following ONLY if using an off-cluster Redis instance for logger
  db: "0"
  host: "redis host"
  port: "redis port"
  password: "redis password" # "" == no password

fluentd:
  syslog:
    # Configure the following ONLY if using Fluentd to send log messages to both
    # the Logger component and external syslog endpoint
    # external syslog endpoint url
    host: ""
    # external syslog endpoint port
    port: ""

monitor:
  grafana:
    user: "admin"
    password: "admin"
    # Configure the following ONLY if you want persistence for on-cluster grafana
    # GCP PDs and EBS volumes are supported only
    persistence:
      enabled: false # Set to true to enable persistence
      size: 5Gi # PVC size

  influxdb:
    # Configure the following ONLY if using an off-cluster Influx database
    url: "my.influx.url"
    database: "kubernetes"
    user: "user"
    password: "password"
    # Configure the following ONLY if you want persistence for on-cluster influxdb
    # GCP PDs and EBS volumes are supported only
    persistence:
      enabled: false # Set to true to enable persistence
      size: 20Gi # PVC size

registry-token-refresher:
  # Time in minutes after which the token should be refreshed.
  # Leave it empty to use the default provider time.
  token_refresh_time: ""
  off_cluster_registry:
    hostname: ""
    organization: ""
    username: ""
    password: ""
  ecr:
    # Your AWS access key. Leave it empty if you want to use IAM credentials.
    accesskey: ""
    # Your AWS secret key. Leave it empty if you want to use IAM credentials.
    secretkey: ""
    # Any S3 region
    region: "us-west-2"
    registryid: ""
    hostname: ""
  gcr:
    key_json: <base64-encoded JSON data>
    hostname: ""

router:
  dhparam: ""
  # Any custom router annotations(https://github.com/deis/router#annotations)
  # which need to be applied can be specified as key-value pairs under "deployment_annotations"
  deployment_annotations:
    #<example-key>: <example-value>

  # Any custom annotations for k8s services like http://kubernetes.io/docs/user-guide/services/#ssl-support-on-aws
  # which need to be applied can be specified as key-value pairs under "service_annotations"
  service_annotations:
    #<example-key>: <example-value>

  # Enable to pin router pod hostPort when using vagrant
  host_port:
    enabled: false

  # Service type default to LoadBalancer
  # service_type: LoadBalancer

workflow-manager:
  versions_api_url: https://versions.deis.com
  doctor_api_url: https://doctor.deis.com

このvalues.ymlの内容をproductionのための設定に変えていって、helm installとやる予定。

Terraform0.9.0からremote configがdeprecatedになる

remote configがdeprecatedになる

terraform0.9.0からterraformコマンド実行するとdeprecated warningがでてきます。

💁 3行

  • remote configコマンドがなくなってinitコマンドが導入される
  • backendという概念が導入されterraform.tfstateファイルを保存する設定をtfファイル側に書くようになってlocalのtfstateは更新されなくなる
  • ざっくりmigrationする際は、tfstateは一応バックアップしといて、tfファイルにbackendの設定書いて terraform initすればOK

設定の書き方

https://www.terraform.io/docs/backends/types/index.html ここの左ペインにStandard Backendsに色々なタイプがあるのでそこを見る。

例: S3

terraform {
  backend "s3" {
    bucket = "mybucket"
    key    = "path/to/my/key"
    region = "us-east-1"
  }
}

例: local

terraform {
  backend "local" {
    path = "relative/path/to/terraform.tfstate"
  }
}

そもそもなんでbackendというものを導入しようとしたのか、メリット的なものについて詳しく :pencil2:

該当のPR#11286に詳しく書いてある

  • remote configをより一般的な考え方にするために、backendという概念を導入した
  • 今ってterraform applyってやるタイミングでインフラ担当一人って感じだけど本当はいろんな箇所から複数同時に実行されることだってあるはず、将来はそうなるはず、そんな時のために、stateをlockしてくれる機能も自動的についてくる
  • remote configはコマンドラインベースでオプションを渡す形で設定していたのでわかりにくい、それをファイルベースで明示的に書くことでより基本的な設定とした
  • initコマンドに統合してmoduleのdownloadとかも一気にやってくれるようになる
  • 今まではremote configを見に行くコマンドと見に行かないコマンドがあったけど全部共通してbackendを通してコマンドを実行するようになる
  • localにキャッシュされてたtfstateファイルにsecret情報が書かれてしまってたがそもそもlocalにキャッシュされてたtfstateファイルはもう更新されなくなるのでgitに含めなくてOK
  • なので.gitignoreに.terraform/を追加しよう:exclamation:
  • localのtfstateのserialがremoteより高いとダメみたいなことが起きなくなる
  • LineageというUUID的なものが導入されてこの値が変わると別のinfraとみなされエラーが出るようになる。具体的なユースケースは間違って別環境のものでapplyしようとしたときとかだろうか 😐

terraform0.9.1からplanレベルでsecurity_groupのcidr_blocksのvalidateが入るようになった

0.9.1 changlog :tada:

https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md#091-march-17-2017

provider/aws: Validate CIDR Blocks in SG and SG rule resources (#12765)

planで未然に防げる

つい間違ってcidr_blocksの方にsecurity_groupのidを指定しまったことがあったけどそれが防げるようになる💯

mpon.hatenablog.com

terraform consoleっていうREPLみたいなやつがあるの知らなかった

v0.8から導入された模様。俗に言うREPL。今のtfstateをもとにsyntaxを試すことができる。planやapplyなどを発行することはできないので安全。単にこういう時はterraformではどう書くんだろう??みたいなのを試したいときに便利。

例1: moduleのoutputを呼び出して要素を取得する

> "${module.base_network.private_subnet_001_ids}"
[
  subnet-xxxxxxxxx,
  subnet-yyyyyyyyy
]
> "${module.base_network.private_subnet_001_ids[0]}"
subnet-xxxxxxxxx
> "${module.base_network.private_subnet_001_ids[1]}"
subnet-yyyyyyyyy

例2: 組み込み関数が何を返すか調べる

> cidrsubnet("172.24.0.0/16", 10, 1)
172.24.0.64/26
> cidrsubnet("172.24.0.0/16", 10, 0)
172.24.0.0/26
> cidrsubnet("172.24.0.0/16", 26, 0)
cidrsubnet: insufficient address space to extend prefix of 16 by 26 in:
> cidrsubnet("172.24.0.0/16", 10, 4)
172.24.1.0/26
> cidrsubnet("172.24.0.0/16", 10, 5)
172.24.1.64/26
> cidrsubnet("172.24.0.0/16", 10, 6)
172.24.1.128/26
> cidrsubnet("172.24.0.0/16", 10, 8)
172.24.2.0/26

自分で立てたdeisにアプリをdeployするまで

quickstartのConfigure DNSregister an admin user and deploy your first app.をやっただけ。

ロードバランサーの確認

$ kubectl --namespace=deis describe svc deis-router | grep LoadBalancer
Type:           LoadBalancer
LoadBalancer Ingress:   104.198.90.182

これがロードバランサーのIP。

でも、これがいつできたのがわからない :scream:

On Google Container Engine, Deis Workflow will automatically provision and attach a Google Cloud Loadbalancer to the router copmonent.

Deis Workflowは自動的にGCPロードバランサーを作ってアタッチするっていってるけど、一体これはいつ行われたんだ・・ :thinking: helm install したときか?該当のコードをdeisのorganization配下でgcpとかload balancerとかgclbとかいろいろ検索してみたけど、わからず・・

kubernetesのserviceでtype:LoadBalancerとしてるから

そもそも、k8sにserviceというcomponentがあって、serviceは色んなタイプがあるけれどLoadBalancerを選ぶと、k8sがよしなにバックエンドのcloud providerを見てロードバランサーを作ってくれるってことですね。GKEの場合は、それは、Google cloud load balancerを使って実現すると。 なので、多分k8sのコードにそういうことが書いてあるはず(まだ見てないけど)。

deis/workflowがやるのはpodを作成するってことだけだと思いこんでしまっていて、podはcontainerを包含した単位なのに、なぜ上位のGCPの操作の仕方まで知っているんだろう?というところがつまづいてしまった原因でした:scream: そもそもk8sは何ができるのかを分かったようで分かってなかったってことですね。

ロードバランサーのIPをもとにnip.ioというサービスを利用したときのhostを確認する

$ host 104.198.90.182.nip.io
104.198.90.182.nip.io has address 104.198.90.182

これで deis.<GCPロードバランサーIPアドレス>.nip.io で手元でアクセスできるようになる。

$ curl http://deis.104.198.90.182.nip.io/v2/ && echo
{"detail":"Authentication credentials were not provided."}

手元のdeisコマンドでユーザー登録する

$ deis register http://deis.104.198.90.182.nip.io
username: admin
password:
password (confirm):
email: xx@bb
Registered admin
Logged in as admin
Configuration file written to /Users/mpon/.deis/client.json

既存のやつが上書きされちゃうっぽい。ってことは複数のdeis workflowに対して何かやりたいときは、deis registerをやり直さないといけないのか??ちょっとめんどいかもしれない :rolling_eyes:

$ deis register --help
Registers a new user with a Deis controller.

Usage: deis auth:register <controller> [options]

Arguments:
  <controller>
    fully-qualified controller URI, e.g. 'http://deis.local3.deisapp.com/'

Options:
  --username=<username>
    provide a username for the new account.
  --password=<password>
    provide a password for the new account.
  --email=<email>
    provide an email address.
  --login=true
    logs into the new account after registering.
  --ssl-verify=true
    enables/disables SSL certificate verification for API requests

deis registerのhelpを見てみても複数に切り替える的なやつはなさげだ。まぁいっか :dancer:

アプリケーションの器をdeis上に作る

$ deis create --no-remote
Creating Application... done, created jangly-sailfish
If you want to add a git remote for this app later, use `deis git:remote -a jangly-sailfish`

作ったとこにサンプルアプリケーションをpullする

$ deis pull deis/example-go -a jangly-sailfish
Creating build... done

アクセスする

$ curl http://jangly-sailfish.104.198.90.182.nip.io
Powered by Deis

コンフィグを上書きする

この間、勝手にローリングデプロイが行われるのでdown timeは0 💯

$ deis config:set POWERED_BY="Docker Images + Kubernetes" -a jangly-sailfish
Creating config... done

=== jangly-sailfish Config
POWERED_BY      Docker Images + Kubernetes
$ curl http://jangly-sailfish.104.198.90.182.nip.io
Powered by Docker Images + Kubernetes

スケーリング

コマンドでさくっとできる。

$ deis scale cmd=2 -a jangly-sailfish
Scaling processes... but first, coffee!
done in 10s
=== jangly-sailfish Processes
--- cmd:
jangly-sailfish-cmd-2108971595-1vxqg up (v3)
jangly-sailfish-cmd-2108971595-b7xhf up (v3)