mike-neckのブログ

Java or Groovy or Swift or Golang

周回遅れ(3周くらい)で Kubernetes 入門してみた

Kubernetes の名前は 2年ちょっと前くらいから耳にはしていたけど、全然触ったことないわりに、最近方方から聞こえてくるので触ってみることにした。

f:id:mike_neck:20180712010003p:plain

なお、基本的な内容はこちらのものを、ただコピペしてくだけの作業…

docs.microsoft.com


いくつかチュートリアルを続ける際に困った点

Azure を操作するために Azure-cliMac にインストールしていたのですが、新しいマシンを購入したので移行したところ、 az コマンドで Azure にログインできなくなりました。

$ az login
Note, we have launched a browser for you to login. For old experience with device code, use "az login --use-device-code"
Please ensure you have network connection. Error detail: HTTPSConnectionPool(host='login.microsoftonline.com', port=443): Max retries exceeded with url: /common/oauth2/token (Caused by SSLError("Can't connect to HTTPS URL because the SSL module is not available."))

これと同じメッセージを調べてみたところ、エラーメッセージにて SSL module is not available とある SSL module が標準でついているとのことで、 python を起動してインポートしてみたところ…

python
Python 3.7.0 (default, Jun 29 2018, 20:14:27) 
[Clang 9.0.0 (clang-900.0.39.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ssl.py", line 98, in <module>
    import _ssl             # if we can't import it, let the error propagate
ImportError: dlopen(/usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload/_ssl.cpython-37m-darwin.so, 2): Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib
  Referenced from: /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload/_ssl.cpython-37m-darwin.so
  Reason: image not found

とインポートに失敗しました。そこで、以前、 Mac OSX High Sierra では openssl という名前のパッケージが実際は libressl に変わっているということを思い出し、 pythonSSL モジュールは利用できないのではないかと考えました。

この件については brew でインストールすれば解決するわけですが、せっかく Apple(BSD?) が openssl をやめて libressl にしたのに、 openssl を入れるのは負けだなと思ったので、docker でコンテナを立ち上げて、そちらで az コマンドを使うようにしました。


dind

Kubernetes を扱うということは、 Docker もあつかうということで、 az コマンドだけでなくて、 docker コマンドも同じコンテナで使えないとうまくいかなそうです。たいして Docker を勉強していなかったので、 dind(Docker in Docker) というのがあるらしいので、そちらのイメージをベースに azure-cli を入れられるようにコンテナを作りました。

FROM docker:18.03.1-dind

RUN mkdir /root/.azure /azure-cli
VOLUME /root/.azure
WORKDIR /azure-cli

RUN \
  apk add --no-cache curl libffi python3-dev bash openssh ca-certificates jq openssl && \
  apk add --no-cache --virtual .build-deps gcc make openssl-dev libffi-dev musl-dev && \
  update-ca-certificates && \
  ln -s /usr/bin/python3.6 /usr/bin/python && \
  ln -s /usr/bin/pip3 /usr/bin/pip && \
  pip install --no-cache-dir --upgrade jmespath-terminal && \
  curl -L https://aka.ms/InstallAzureCli | \
    sed -e "s/tmp_XXXX/tmp_XXXXXX/g" \
      -e "s/\/dev\/tty/config.txt/g" > install.sh && \
  chmod +x install.sh && \
  echo /azure-cli/lib >> config.txt && \
  echo /azure-cli/bin >> config.txt && \
  echo y >> config.txt && \
  ./install.sh && \
  cat /azure-cli/lib/az.completion > ~/.bashrc && \
  runDeps="$( \
    scanelf --needed --nobanner --recursive /usr/local \
        | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
        | sort -u \
        | xargs -r apk info --installed \
        | sort -u \
    )"  && \  
  apk add --virtual .rundeps $runDeps && \
  apk del .build-deps

WORKDIR /
ENV PATH $PATH:/azure-cli/bin

CMD bash

なお、作ったコンテナはこちらから入手可能

azure-cli-dind


入門できたこと

  • Kubernetes でアプリケーションをデプロイ
  • Pod の数を増やす
  • ローリングアップデート
  • Kubernetes クラスタのアップグレード
    • 実行したらサービス落ちた

入門できなかったこと

  • volume などのデータの永続化
  • Istio などの周辺プロダクト

以上、特に見どころのないエントリー

reactor-netty コールドリーディング(4)

ChannelOperationsHandler

  • reactor-netty が提供している ChannelHandler の実装クラス
  • 内部に ContextHandler を持ち、これを通じて処理の登録などを行っている模様

channelActive(ChannelHandlerContext)

  • ContextHandler#createOperations(Channel, Object) を通じて ChannelOperationsChannelAttribute に保存する
    • ChannelOperations#autoCreateOperations の値が true の場合のみだが、このフィールドのデフォルト値は true となっている
    • ChannelOperations.OnNew<Channel>ChannelOperations を生成するが、これは ContextHandler#newClientContext で渡されてくる
    • ContextHandler#newClientContext に渡す ChannelOperations.OnNew<Channel>HttpClient#doHandler に渡される BiFunction<NettyInbound,NettyOutbound, Publisher<Void>> をラップして作られる
    • HttpClientOperations#bindHttpHttpClientOperations を生成するファクトリーメソッド
    • HttpClientOperationsChannelOperations のサブクラス。 ChannelOperationsInboundHandler のラッパー NettyInboundOutboundHandler のラッパー NettyOutbound を実装したクラス
handler != null ? (ch, c, msg) -> {
    if(onSetup != null){
        onSetup.accept(ch);
    }
    return HttpClientOperations.bindHttp(ch, handler, c);
} : EMPTY);

channelRead

  • channelActive で作った ChannelOperations#onInboundNext に処理を委譲する

reactor-netty コールドリーディング(3)

HttpRequest の組み立て

  1. HttpClient#get などのメソッドから HttpClient#request メソッドを呼び出し
  2. HttpClient#request にて Mono<HttpClientResponse> を返すが、実体は MonoHttpClientResponse
  3. 2.の際に HttpClient#handler というメソッドが呼び出され、 Function<HttpClientRequest,Publisher<Void>>MonoHttpClientResponse に渡される
  4. 3.はユーザーのハンドリングにて Mono<SomeRequestBaseModel> が生成されることを予期している
  5. MonoHttpClientResponse#subscribe が呼び出されると、 Mono#defer にて Mono<NettyContext> が生成されて、 HttpClientResponse にキャストされたものが subscribe される
  6. 5.の defer の中では HttpClient#newHandler が呼び出される
  7. 6.の HttpClient#newHandlerMono<NettyContext> を返すメソッド
  8. 6.の HttpClient#newHandler に渡す HttpClientHandlerBiFunction<NettyInbound,NettyOutbound,Publisher<Void>> を実装するクラスで、ここの apply メソッドにて NettyInbound (= HttpClientOperations)をもとに リクエストを組み立てる
  9. HttpClient#newHandler は実際には TcpBridgeClient#newHandler に委譲していて、更に TcpClient#newHandler に委譲している
  10. TcpClient#newHandler では Bootstrap の取得が行われる
  11. TcpClient#newHandler の最中に呼び出される TcpClient#doHandler メソッドの戻り値 ContextHandler<SocketChannel>ChannelInitializer<SocketChannel> を継承したクラス
  12. ContextHandler<SocketChannel> は抽象クラスで実装クラスは PooledClientContextHandler(ChannelPool が利用可能な場合) または ClientContextHandler
  13. ContextHandlerinitChanneldoPipeline(Channel) を読んで各サブクラスで ハンドラーを登録したあとに ChannelOperationsHandler を登録する

reactor-netty コールドリーディング(2)

Bootstrap の組み立て

Bootstrap は以下の2箇所のいずれかで組み立てる

  • PoolResources#selectOrCreate(SocketAddress,Supplier<? extends Bootstrap>,Consumer<? super Channel>,EventLoopGroup)
    • PoolResources が利用できる場合のみ
    • PoolResources では接続先アドレス(ポート含む)ごとに Channel をプールしている
  • TcpClient#newHandler(BiFunction<? super NettyInbound, ? super NettyOutbound, ? extends Publisher<Void>>, InetSocketAddress, boolean, Consumer<? super Channel>)