読者です 読者をやめる 読者になる 読者になる

言語のコミュニティがどうとかどうでもいい

当方、iOSをobjcからやってて、Androidも少し。フロントエンドは、jqueryの頃から、typescriptあたりまでなんとなく。 サーバーは、RailsScala Play frameworkあたりを少々。 今はインフラ周り、AWSGCP、コンテナ周りは、docker、k8s、ECS、Deisなどを、少々かじっております。

で、

Scalaの話でなんかコミュニティがどうこうで揉めてるのを見て、なんかほんと、どうでもいいことで争ってる。言語なんてなんだっていいんだよ。ある言語をわかってる人は、いざやってみたら多分どのパラダイムでも、すぐ理解できる。分からないってなら、多分何やっても分からない。

なんていうか、外野のあーだこーだ、コミュニティがどうこうなんてどうでもいい。第一線の人たちは一般人なんかシカトでいいです。

ほんと、みんなすごい人たちなんだから、一般人がどうこうとか、コミュニティ怖いとかどうでもいい。みなさんは、ソフトウェアを作って欲しい。ぼくたちみたいな一般人なんて相手にしなくていいよ。

日本で有名な人たちは、一般人なんて相手にしなくていいから、日本発、すごいソフトウェアをみんな作ってて欲しい><

外国では次から次へと有名なソフトウェアが生まれて、何も生み出さない自分を悔しく思って。 こんな何もできない自分に比べてもっとすごい人たちが、なんか、しょうもないあーだこーだに巻き込まれてるのはもったいないし、損失だなと。

Objective-Cのallocは何をしてるのか

allocはNSObjectのクラスメソッド

alloc - NSObject | Apple Developer Documentation

+ (instancetype)alloc;

The isa instance variable of the new instance is initialized to a data structure that describes the class; memory for all other instance variables is set to 0.

初期化前にメモリを確保。なのでこの時点で一応インスタンスはできている。でも初期化はされてない。サイズは確保されてない状態ってことだと思う。initして初めてサイズが確保される。なので、allocの返り値の型とinitの返り値の型は同じ。

Swiftの場合、 Hoge() ってやると、[[Hoge alloc] init]; まで呼ばれるのでallocを意識することはない。

allocationの説明

developer.apple.com

isaとは

http://algorithm.com.au/downloads/talks/objective-c-internals/objective-c-internals.pdf

In other words, an NSObject is simply a struct with a single field, named isa (“is a”, as in “a car is a vehicle”) that points to some sort of Class type.

AnyClassの正体

AnyObjectの型

typealias AnyClass = AnyObject.Type

何か分からないものの型を表現してる。

参考

Swifter - Swift Must Know Tips

kubernetesでserviceのselectorはnamespace内に閉じてるのか検証

まとめ

  • 同じlabel名がつけられていてもnamespace内でのみ振り分けられる

検証の構成

  • GKE上で試した
  • Deploymentでただ単にnginxかapacheが動いてるだけ
  • sandbox1ではnginx、sandbox2ではapacheが動いてる
  • Service(LoadBalancer)で接続
  • labelは同名にする
namespace: sandbox1

| Service |
     |
     | app: sandbox-webserver
     ▼
| nginx |

------------------------------
namespace: sandbox2
| Service |
     |
     | app: sandbox-webserver
     ▼
| apache |

ディレクトリ構成

├── sandbox1
│   ├── Chart.yaml
│   └── templates
│       ├── deployment.yaml
│       ├── namespace.yaml
│       └── service.yaml
├── sandbox2
│   ├── Chart.yaml
│   └── templates
│       ├── deployment.yaml
│       ├── namespace.yaml
│       └── service.yaml

検証コード(sandbox1)

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: web
  namespace: sandbox1
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: sandbox-webserver
    spec:
      containers:
      - name: web
        image: bitnami/nginx:latest
        ports:
        - containerPort: 80
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: sandbox1
spec:
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
  type: LoadBalancer
  selector:
    app: sandbox-webserver

検証コード(sandbox2)

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: web
  namespace: sandbox2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: sandbox-webserver
    spec:
      containers:
      - name: web
        image: bitnami/apache:latest
        ports:
        - containerPort: 80
apiVersion: v1
kind: Service
metadata:
  name: web
  namespace: sandbox2
spec:
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
  type: LoadBalancer
  selector:
    app: sandbox-webserver

Swiftで動的にクラス作ってメソッド呼ぶ。しかも全く型情報がない状態で。要はjavascriptみたいなことがしたい

まとめ

  • NSSClassFromString使う
  • 引数が3つ以上だとperformSelectorは使えない
  • SwiftだとNSInvocationは使えないので代わりにIMPを使う
  • Class methodとInstance methodで呼び方ちょっと違う
  • performSelectorの返り値の型はUnmanaged<AnyObject>takeUnretainedValueを使う

引数が多いときのやり方で参考にしたobjcのコード

github.com

+ (id)initGraphRequestWithGraphPath:(NSString *)graphPath{
    Class FBSDKGraphRequestClass = NSClassFromString (@"FBSDKGraphRequest");
    id graph = [FBSDKGraphRequestClass alloc];
    
    SEL aSelector = NSSelectorFromString(@"initWithGraphPath:parameters:");
    IMP imp = [graph methodForSelector:aSelector];
    id (*func)(id, SEL, id, id) = (void *)imp;
    
    graph = func(graph, aSelector, graphPath, @{});
    return graph;
}

www.b2cloud.com.au

Unmanaged<AnyObject>の取り出し方

stackoverflow.com

var unmanagedObject: Unmanaged = someFunctionThatReturnsAnUnmanagedObject() var newValue: Type = unmanagedObject.takeRetainedValue as Type

takeRetainedValueってやつを使うみたい。でも、アプリがEXC_BAD_ACCESSかなんかだったかで落ちた。 takeUnretainedValueだったら大丈夫だった。

ふーむ。allocしたのに、release漏れてるからとかなんだろうか。Swiftでは普通allocなんて書かないからあんまり普段考えないことだけど。

SwiftでIMPを呼ぶ

stackoverflow.com

unsafeBitCastを使う。

NSClassFromStringでClass methodを呼ぶ

stackoverflow.com

kubernetes meetup Tokyo #4に参加してLT発表してきた #k8sjp

k8sjp.connpass.com

ECSからGKEに乗り換えたい

感想

  • Google Japanの食堂でながーい会場で発表した。人めっちゃいた。
  • kubernetesのserviceまわり、OnlyLocalのことが少し理解できた
  • RBACっていうIAMみたいな機能の話がためになった
  • ServiceAccountもなんなのか分かった
  • ただ、歓談の時間がほとんどなくてあんまり人と話すチャンスがなかった
  • 歓談のときに、kubernetesにしようと思ってたけど当時は難しそうすぎて、docker swarmにしたけど、そろそろkubernetesにしようかなーみたいなこと話せてよかった
  • あと、openshift使ってる人の話も聞けて参考になった
  • etcdが落ちた時、復旧する時にどれを神にすればいいか辛そうという話をしました
  • meetupうちでもやりたいなーって話しかけに行ったんですが400人ぐらい毎回集まっちゃうからでかい会場じゃないと厳しいとのこと、残念><
  • オーケストレーションはkubernetesがデファクトではありますが、みたいな雰囲気をなんとなく感じました
  • AWS上にkubernetesたてるにしても、etcdとかmasterのapi-serverも冗長化するみたいな風にしてやれば意外といけそうではって雰囲気。kube-awsってやつで H/A etcdになるように更新してるみたい

GKEからCloud SQLに接続する方法

Deisのpostgresをoff-clusterにする(Cloud SQLを利用) - まーぽんって誰がつけたの?Deisのpostgresをoff-clusterにする(postgresql on GCEを利用) - まーぽんって誰がつけたの?でCloud SQLをdeisのdatabaseとしようともがいてたけど解決した話。

Cloud SQLはRDSと違う

Cloud SQLAWSでいうRDS的なやつ。ただちょっと違うのが、同じVPC的な論理的に同じネットワーク内に存在することができずに、別ネットワークになってしまう。 なので、GKE(Google Container Engine)のネットワークとCloud SQLはインターネットを経由しないとつながらないということになる。

GCPが用意してるCloud SQL接続方法

1. 接続してくるグローバルIPアドレスを許可する

GKEのCluster内に存在するNodeは単なるGCEのインスタンスなので一つ一つはグローバルIPを持っている。なので、このIPを指定してあげる。 ただ、Nodeが増えたり減ったりしたときに常にIPアドレスを管理しないといけずあまりよい方法とはいえない :no_good:

2. SSLで接続する

IPアドレスを許可しない場合、SSL証明書を使って接続する。 基本的にdriverはSSL接続は対応してるからまぁこれでもいいんだけど、たまに対応してないミドルウェアなんかがあって取り扱いがちょっと面倒。特にDeisの場合、そういうオプションは用意されてないので、PRをして変更を受け入れてもらわないといけないという状況だった :rolling_eyes:

3. CloudSQL Proxyを使う

これが一番よい方法。Googleが用意してるProxyで、binaryやDocker Imageでinstallして起動するだけで接続できるようになる。Cloud SQL Proxyを起動する際に、Cloud SQLに接続可能なAPI Key的なものと、Cloud SQLインスタンス名を渡してあげるだけで接続をproxyしてくれる。 あとは、普通にproxyが起動しているインスタンス上で、127.0.0.1に対して接続しにいけばCloud SQLにつながってくれる :100:

ただ、単純なVMインスタンスであればこれでいいが、GKE(Kubernetes)上で使う場合は、ちょっと一工夫がいる :wrench:

公式で紹介されてるGKEからCloud SQLに接続しに行く方法

Kubernetesでは、PodというのがContainerの集まりで、それらを何個起動するかとかRolling Updateリリースとかを管理しているDeploymentというリソースがある。 で、公式で紹介されてるのは、DeploymentのPodにCloudSQL ProxyをDocker Imageからcontainerを立てる方法。

https://cloud.google.com/sql/docs/mysql/connect-container-engine

こんな風に、podの中にwordpressっていうimageでcontainerを起動させてるとしたら、そこに、cloud sqlのproxyのimageを追記する。 そうすると、wordpressのcontainerからは、127.0.0.1で接続しにいける。ただ、これだと、Deis本体に手を入れる必要がある。

- image: wordpress:xxx
  name: wordpers
    ・ 略
    ・
- image: gcr.io/cloudsql-docker/gce-proxy:1.09
  name: cloudsql-proxy
  command: ["/cloud_sql_proxy", "--dir=/cloudsql",
            "-instances=[INSTANCE_CONNECTION_NAME]=tcp:[PORT]",
            "-credential_file=/secrets/cloudsql/credentials.json"]
  volumeMounts:
    - name: cloudsql-instance-credentials
      mountPath: /secrets/cloudsql
      readOnly: true
    - name: ssl-certs
      mountPath: /etc/ssl/certs
    - name: cloudsql
      mountPath: /cloudsql
volumes:
  - name: cloudsql-instance-credentials
    secret:
      secretName: cloudsql-instance-credentials
  - name: ssl-certs
    hostPath:
      path: /etc/ssl/certs
  - name: cloudsql
    emptyDir:

で、実際に、DeisのChartという設定ファイルを書き換えることで、proxy経由で無事接続することができた🎉 これはPRチャンスということで、Contribution.md読むと、PR出す前にSlackで一言相談してみてねとのことで、入ろうとするがlimitエラーかなんかでジョインできない。Deisの問い合わせから頼む入れてくれって送って入れてもらった。

これはDeisにPRチャンス!! 意気揚々とチャンネルに書き込んだ😎

f:id:masato47744:20170422024919p:plain

すると、フィーチャーフラグとかつけてくれたらいいよ!でも、なんでわざわざそんなことするの?みたいに言われて、あっさり別の解決策を教えてもらった。わざわざ同じpod内に作る必要なんてないとのこと。

f:id:masato47744:20170422024931p:plain

Cloud SQL Proxyを単に一つのDeploymentとServiceとして起動する

Kubernetes上で、あるPodから別のDeploymentのPodには接続しにいけないのかなーって思ってたのが間違いだった。実は、Kubernetes上では、Pod間の通信はServiceを利用すれば簡単にできる。しかもNameSpaceをまたがっていても!

KubernetesがCluster内のDNSもいい感じに設定していてくれて、Service名.NameSpace名というホスト名でつながることができる!!

最終的な構成はこういう感じ。

NameSpace deis
                       <host: pg-sqlproxy.sql-proxy>
 | deis-controller |-----------|
                               |
-------------------------------|---
NameSpace sql-proxy            |
                               ▼
                         | Service: pg-sqlproxy |
                               |
                               ▼
                         | Pod: gce-proxy |
                               |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|~~~~~~~~~~~~~~~~
Cloud SQL                      |
                               ▼
                         | PostgreSQL |

この概念を利用すれば、Deisの1アプリとして、Cloud SQL Proxyをdeployしたっていい。Communityではそういうやり方も教えてもらった。

f:id:masato47744:20170422025008p:plain

まとめ

Deisのことを調べていくとKubernetesのことを知っていかないといけないんだけど、知れば知るほど奥が深いというか至れり尽せりな機能が用意されててほんとすごい🚢