「Container」カテゴリーアーカイブ

IstioのSubsetでバージョン毎に流量制御を行う

以下の記事でIstioによる流量制御を行いました。しかし、本来ABテストというのはバージョン毎に流量制御を行うことが多いので、Subsetを使って異なるバージョンのコンテナアプリケーションに対して行います。

MicroK8sでistioによるABテストを試してみる(1/2)
MicroK8sでistioによるABテストを試してみる(2/2)

  • コンテナイメージを用意する
    • v1コンテナイメージの作成
    • v2コンテナイメージの作成
  • コンテナアプリケーションをデプロイする
    • v1 Deploymentを作成する
    • v2 Deploymentを作成する
    • Serviceを作成する
  • Istioの設定を行う
    • VirtualServiceを作成する
    • DestinationRuleを作成する
  • 設定を確認する

コンテナイメージを用意する


以下の記事で作成したhelloaを使ってコンテナイメージを作成します。

MicroK8sでistioによるABテストを試してみる(1/2)

v1コンテナイメージの作成


以下のコマンドで、v1タグのhelloaイメージを作成し、レジストリに登録します。

$ docker build -t helloa:v1 helloA/
$ docker tag helloa:v1 192.168.64.2:32147/helloa:v1
$ docker push 192.168.64.2:32147/helloa:v1

v2コンテナイメージの作成


httpリクエストを受け付けたら”Hello A v2″を表示されるように、以下のコマンドでhtmlファイルを編集します。

$ echo "Hello A v2" > helloA/src/templates/index.html 

以下のコマンドで、v2タグのhelloaをイメージを作成してレジストリに登録します。

$ docker build -t helloa:v2 helloA/
$ docker tag helloa:v2 192.168.64.2:32147/helloa:v2
$ docker push 192.168.64.2:32147/helloa:v2

コンテナアプリケーションをデプロイする


v1 Deploymentを作成する


以下のファイルを使って$ microk8s.kubectl create -f [ファイル名]を実行し、helloa-v1 Deploymentを作成します。hello-v1は、タグv1のhelloaから実行されるPodを管理します。

重要なのは、version: v1 ラベルが付与されていることです。このラベルをSubsetとして使い、流量制御を行うことになります。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloa-v1
  labels:
    app: helloa
    version: v1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloa
      version: v1
  template:
    metadata:
      labels:
        app: helloa
        version: v1
    spec:
      containers:
      - name: helloa 
        image: localhost:32147/helloa:v1 
        ports:
        - containerPort: 8080

v2 Deploymentを作成する


同じように、以下のファイルを使って$ microk8s.kubectl create -f [ファイル名]を実行し、helloa-v2 Deploymentを作成します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloa-v2
  labels:
    app: helloa
    version: v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloa
      version: v2
  template:
    metadata:
      labels:
        app: helloa
        version: v2
    spec:
      containers:
      - name: helloa 
        image: localhost:32147/helloa:v2 
        ports:
        - containerPort: 8080

Serviceを作成する


以下のファイルを使って$ microk8s.kubectl create -f [ファイル名]を実行し、helloa-v1/v2 DeploymentのPodをエンドポイントとするServiceを作成します。

apiVersion: v1
kind: Service
metadata:
  name: helloa
  labels:
    app: helloa
    service: helloa
spec:
  ports:
  - port: 8080
    name: http
  selector:
    app: helloa

Istioの設定を行う


Istioの設定を行い、流量制御をします。Ingressgatewayについては、以下の記事で作成したものがあるのを前提に行います。

MicroK8sでistioによるABテストを試してみる(1/2)

VirtualServiceを作成する


バージョン毎に流量制御を行うVirtualServiceを作成します。

Subset v2に25%、Subset v1に75%の通信を割り振るhello-ab-route VirtualServiceを作成します。

以下のファイルを使って$ microk8s.kubectl create -f [ファイル名]を実行します。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: hello-ab-route
spec:
  hosts:
  - hello.example.com
  gateways:
  - hello-gateway
  http:
  - route:
    - destination:
        host: helloa
        subset: v2
        port: 
          number: 8080
      weight: 25
    - destination:
        host: helloa
        subset: v1
        port: 
          number: 8080
      weight: 75

DestinationRuleを作成する


Subsetを使う場合に必要となるDestinationRuleを作成します。

以下はhelloaのSubsetをversionラベルで区別しています。この定義によってラベルによる通信制御が行えます。

以下のファイルを使って$ microk8s.kubectl create -f [ファイル名]を実行します。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: helloa-destination-rule
spec:
  host: helloa
subsets: - name: v1 labels: version: v1 - name: v2 labels: version: v2

設定を確認する


以下のコマンドで、helloaに対して10回httpリクエストを行って設定を確認します。

$ for i in {0..9};do curl -s -HHost:hello.example.com "http://192.168.64.2:31380"; done

しかし、以下のエラーが出てしまいました。

upstream connect error or disconnect/reset before headers. reset reason: connection termination

mutual TLSを有効にしている場合は、DestinationRuleにおけるtlsのモードをISTIO_MUTUALにする必要があるそうです。従って、DestinationRuleのyamlを以下に編集し、$ microk8s.kubectl apply -f [ファイル名]を実行する必要がありました。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: helloa-destination-role
spec:
  host: helloa
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2

改めて実行したところ、うまく流量制御が行えていました。

[walker:~/Desktop/hello]$ for i in {0..9};do curl -s -HHost:hello.example.com "http://192.168.64.2:31380"; done
Hello A
Hello A
Hello A
Hello A
Hello A v2
Hello A
Hello A
Hello A v2
Hello A
Hello A v2

せっかくなのでKialiで見てみたところ、実際にリクエストがきた通信をパーセント表示しているので定義した値と多少異なりますが、近い値が出ています。

以上です!

Jeagerって何?

Jeagerの概要を調査したので、記事を描いてみました。

  • 分散トレーシング
    • 分散トレーシングとは
    • SpanとTraceの関係
    • Propagation
  •  Jeager
    • Jeagerとは 
    • Jeagerを操作してみる 

分散トレーシング


分散トレーシングとは


分散トレーシングとは、マイクロサービスのような分散されたシステムにおいて、障害の検知や原因特定を迅速化するために考えられた、サービス間通信の監視を行う概念です。

SpanとTraceの関係


分散トレーシングの基本概念であるSpanとTraceの関係について以下に示します。

  • Span・・・サービス単位の処理
  • Trace・・・任意のリクエストにおけるSpanの集合体

Propagation


分散トレーシングでは、Span同士でメタデータを伝播(Propagation)させています。以下のように、HttpヘッダーにSpanのメタデータを埋め込み、Spanの関係を把握しています。

参照元)https://github.com/openzipkin/b3-propagation#overall-process

Jeager


Jeagerとは


分散トレーシングを体現したツールは、Zipkin、Hawkularなど様々あります。Jeagerはその中の一つで、Uber社がGo言語で開発した分散トレーシングシステムです。

CNCFにホストされたのがきっかけで、分散トレーシングシステムの代表例でもあります。IstioをOperatorでインストールする場合は、Jeagerも一緒にインストールすることができ、Operatorによって構成されます。

アーキテクチャは以下の通りです。jeager-clientが取得したログをjeager-agentを経由してjeager-collectorが受け取り、DBにログを保管します。ユーザーはGUIを通して、このDBを参照することができます。

参照元)https://www.jaegertracing.io/docs/1.8/architecture/

Jeagerを操作してみる 


以下の記事で、MicroK8sにIstioをインストールした時に、一緒にインストールされていたJeagerを使って簡単に確認したいと思います。

MicroK8sでistioによるABテストを試してみる(1/2)
MicroK8sでistioによるABテストを試してみる(2/2)

アクセスするとこんな画面でした。キャラクターが可愛い件について。

適当に検索してみると、トレースログ毎に発生日時とレスポンスタイムのグラフに表示されていました。また、それぞれのトレースログの詳細が確認できますし、トレースログ同士を比較する機能までついていました。

トレースログをクリックすると、上で説明したようなTraceとSpanが確認できました。

Httpリクエストヘッダーも確認することができました。

右上の「Trace Timeline」を「Trace Graph」に変更すると、以下のように依存関係が確認できました。

以上です。本当に概要だけですが、参考になれば幸いです。

MicroK8sでKialiを使ってみる

以下の記事でMicroK8sにIstioを使ってABテストを実施しました。Kialiを使って発生した通信をGUIで確認してみましたので投稿します。

MicroK8sでistioによるABテストを試してみる(1/2)
MicroK8sでistioによるABテストを試してみる(2/2)

  • Kialiにログインする
    • Kiali Serviceの設定を変更する
    • Kialiのログインユーザーとパスワードを確認する
    • Kialiにログインする
  • KialiでGraphを確認する
    • 正常な状態でGraphを確認する
    • helloaを通信不可な状態にして確認する

Kialiにログインする


MicroK8sでIstioをデプロイすると、Kialiも一緒にデプロイされます。ここではそのKialiにログインします。

Kiali Serviceの設定を変更する


KialiはGUIで表示されるので、ローカルPCからブラウザでアクセスする必要があります。従って、KialiのServiceを公開する必要があります。(通常$ microk8s istioctl dashboard kialiでブラウザが開くはずなのですが、私はエラーで開ませんでした。)

以下のコマンドでKialiのServiceのタイプをNodePortにします。spec.typeをClusterIPからNodePortにするだけで変更できます。

ubuntu@microk8s-vm:~$ microk8s.kubectl get svc -n istio-system
NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
kiali                    ClusterIP   10.152.183.201   <none>        20001/TCP       2d1h
ubuntu@microk8s-vm:~$ microk8s.kubectl edit svc kiali -n istio-system
service/kiali edited
ubuntu@microk8s-vm:~$ microk8s.kubectl get svc kiali -n istio-system
NAME    TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)           AGE
kiali   NodePort   10.152.183.201   <none>        20001:31793/TCP   2d1h

Kialiのログインユーザーとパスワードを確認する


Kialiの初期ユーザーと初期パスワードがわからなかったので、以下のコマンドで確認しました。結果、admin/adminでした。

$ microk8s kubectl get secret kiali -n istio-system -o json | jq -r '.data.username' | base64 --decode
$ microk8s kubectl get secret kiali -n istio-system -o json | jq -r '.data.passphrase' | base64 --decode

Kialiにログインする


http://[multipass-vmのIPアドレス]:[NodePortの公開ポート]/kiali/にアクセスすると、以下の画面が表示されるので、admin/adminを入力して「Log in」ボタンをクリックします。

KialiでGraphを確認する


正常な状態でGraphを確認する


左の「Graph」タブをクリックし、まずは正常な状態でGraphを確認します。

以下のように、ServiceとPodがバラバラな状態になっていました。

以下のコマンドで100回httpリクエストを行うと、正しく通信経路が表示されました。

$ for i in {0..99}; do curl -s -HHost:hello.example.com "http://192.168.64.2:31380"; done

左上のプルダウンメニューより、「No edge labels」を「Requests percentage」に変更すると、以下のように流動制御を行う割合が表示されました。

左上のプルダウンメニューより、「Requests percentage」を「Response time」に変更すると、以下のように通信経路ごとのレスポンスタイムが表示されました。

helloaを通信不可な状態にして確認する


コンテナが異常だった場合の表示も見てみたいと思います。

以下のコマンドでhelloa Podを削除します。

ubuntu@microk8s-vm:~$ microk8s.kubectl delete pod helloa -n istio-app

以下のyamlファイルから$ microk8s.kubectl create -f [ファイル名]でPodを作成します。readinessProbeを定義していて、/tmp/healthyが存在しない間は閉局します。

apiVersion: v1
kind: Pod
metadata:
  creationTimestamp: null
  labels:
    run: helloa
  name: helloa
spec:
  containers:
  - image: localhost:32147/helloa
    name: helloa
    ports:
    - containerPort: 8080
    resources: {}
    readinessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
  dnsPolicy: ClusterFirst
  restartPolicy: Always
status: {}

閉局するといってもPodは存在しますので、envoyコンテナが起動していて、helloaコンテナが起動していない状態になります。

ubuntu@microk8s-vm:~$ microk8s.kubectl get pods -n istio-app
NAME     READY   STATUS    RESTARTS   AGE
helloa   1/2     Running   0          47m
hellob   2/2     Running   0          83m

この状態で再度以下のコマンドを実行します。

$ for i in {0..99}; do curl -s -HHost:hello.example.com "http://192.168.64.2:31380"; done

100回は大分時間かかるので、途中でキャンセルしても大丈夫です。完了後にGUIを確認すると以下の画面になりました。helloaコンテナへのhttpリクエストが失敗した割合が分かりやすく表示されていました。右側には失敗したステータスコードも確認できるようになっていました。

以上です。2つ程度では価値が伝わりにくいですが、マイクロサービスなどコンテナの数が膨大になればなるほど貴重になるツールなので、皆様も是非お試しあれ。

MicroK8sでIstioによるABテストを試してみる(2/2)

本記事は以下の続きです。Istioを使って流量制御を行います。

MicroK8sでistioによるABテストを試してみる(1/2)

  • アプリケーションのServiceを作成する
  • Istioの設定をする
    • Istio Ingressgatewayの設定を変更する
    • Gatewayを作成する
    • Virtual Serviceを作成する
  • 設定を確認する

アプリケーションのServiceを作成する


MicroK8sでistioによるABテストを試してみる(1/2)で作成したアプリケーションに、helloa, hellobで名前解決できるServiceを付与します。

ubuntu@microk8s-vm:~$ microk8s.kubectl expose pod helloa --name=helloa --port=8080 -n istio-app
service/helloa exposed
ubuntu@microk8s-vm:~$ microk8s.kubectl expose pod hellob --name=hellob --port=8080 -n istio-app
service/hellob exposed
ubuntu@microk8s-vm:~$ microk8s.kubectl get svc -n istio-app
NAME     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
helloa   ClusterIP   10.152.183.63    <none>        8080/TCP         7m23s
hellob   ClusterIP   10.152.183.36    <none>        8080/TCP         7m11s

Istioの設定をする


Istio Ingressgatewayの設定を変更する


Istio Ingressgatewayの設定を変更します。

Istio IngressgatewayはIstioで構成されたアプリケーションの入り口となるServiceです。istio-system namespaceで稼働していて、Service TypeがLoadBalancerです。IngressIPを設定できる場合はLoadBalancerIPとして設定すれば問題ありませんが、MicroK8sはデフォルトでは使えないのでNodePortに変更しておきます。

この設定は、Istioのドキュメントにも記載があります。

以下のコマンドでspec.typeをLoadBalancerからNodePortに変更します。

ubuntu@microk8s-vm:~$ microk8s.kubectl edit svc istio-ingressgateway -n istio-system

Gatewayを作成する


IstioでGatewayを作成します。

Gatewayは、サービスメッシュのエッジで動作するロードバランサーで、http/tcp接続を受信します。Istioで構成されたアプリケーションを外部に公開するには、Ingressgatewayに加えてGatewayが必要になります。

https://istio.io/docs/reference/config/networking/gateway/

ホスト名”hello.example.comyaml”に対して、httpプロトコルを受け付けるhello-gatewayのyamlを以下に示します。このyamlをoc create -f [ファイル名]で作成します。

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: hello-gateway
spec:
  selector:
    istio: ingressgateway # use Istio default gateway implementation
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "hello.example.com"

Virtual Serviceを作成する


IstioでVirtual Serviceを作成します。

Virtual Serviceはトラフィックルーティングを制御して、Istioで構成されたアプリケーションの振る舞いを変えられます。

https://istio.io/docs/reference/config/networking/virtual-service/

hello.example.comへのhttpリクエストに対して、helloa Serviceに25%、hellob Serviceに75%のトラフィックをルーティングするhello-ab-route Virtual Serviceのyamlを以下に示します。こちらもoc create -f [ファイル名]で作成します。

spec.http.route.destination.hostでServiceを指定し、weightでルーティングするトラフィックの割合を定義しています。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: hello-ab-route
spec:
  hosts:
  - hello.example.com
  gateways:
  - hello-gateway
  http:
  - route:
    - destination:
        host: helloa
        port: 
          number: 8080
      weight: 25
    - destination:
        host: hellob
        port: 
          number: 8080
      weight: 75

設定を確認する


最後に設定した流動制御を確認します。

設定したホスト名hello.exapmle.comに対して、Istio Ingressgatewayを経由するcurlコマンドを10回実行してみます。補足すると、Gatewayとして指定している192.168.64.2:31380は、[multipass-vmのIPアドレス]:[NodePortで80番を公開しているポート]です。

$ for i in {0..9}; do curl -s -HHost:hello.example.com "http://192.168.64.2:31380"; done
Hello B
Hello A
Hello B
Hello A
Hello B
Hello B
Hello B
Hello A
Hello B
Hello B

これは綺麗に7:3ぐらいの割合で出力されていますが、何度も繰り返すと、10:0の時もありました。但し、Hello AがHello Bの回数を越すということはなかったので、正しくルーティングされていることが伺えました。

以上です!全体を通して考えると、MicroK8s特有の部分はインストールのところだけでしたね。Istioの勉強をしてみたいと思っていたので、いい機会になりました。

MicroK8sでIstioによるABテストを試してみる(1/2)

以下の記事でMicroK8sをインストールしてみたので、IstioをインストールしてABテストを試します。今回は長くなってしまったので、インストール編とABテスト編に分けてお送りします。

MicroK8sをMacOSにインストールしてみる

  • Istioをインストールする
  • Istio Injection用のnamespaceを作成する
  • テスト用アプリケーションを実行する
    • アプリケーションソースコードを作成する
    • コンテナイメージを作成する
    • コンテナイメージをdocker registryに登録する
    • コンテナアプリケーションを実行する

Istioをインストールする


以下のコマンドでMicroK8sにIstioをインストールします。Istioを有効化するだけで必要なyamlをダウンロードしてリソースを作成します。

ubuntu@microk8s-vm:~$ microk8s.status | grep istio
istio: disabled
ubuntu@microk8s-vm:~$ microk8s enable istio
Enabling Istio
Fetching istioctl version v1.3.4.
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   635  100   635    0     0    444      0  0:00:01  0:00:01 --:--:--   444
100 36.3M  100 36.3M    0     0  2553k      0  0:00:14  0:00:14 --:--:-- 5118k
・
・
省略
・
・
Istio is starting
ubuntu@microk8s-vm:~$ microk8s.status | grep istio
istio: enabled

インストール完了後、istio-systemというnamespaceに色んなpodがデプロイされていました。

ubuntu@microk8s-vm:~$ microk8s.kubectl get ns
NAME              STATUS   AGE
default           Active   43h
istio-system      Active   119s
kube-node-lease   Active   43h
kube-public       Active   43h
kube-system       Active   43h
ubuntu@microk8s-vm:~$ microk8s.kubectl get pods -n istio-system 
NAME                                      READY   STATUS      RESTARTS   AGE
grafana-78758665bb-tbdn8                  1/1     Running     0          7m4s
istio-citadel-799bf7ddb9-hlzjw            1/1     Running     0          7m3s
istio-egressgateway-76d7cf9c58-dlwnx      1/1     Running     0          7m4s
istio-galley-54cb4b4bcd-ptcmx             1/1     Running     0          7m5s
istio-grafana-post-install-1.3.4-hcbgl    0/1     Completed   0          7m6s
istio-ingressgateway-785cd6c5cc-lj7md     1/1     Running     0          7m4s
istio-pilot-5fdd79bcb5-rzfmn              2/2     Running     0          7m3s
istio-policy-7dc9dbf6bc-jrvht             2/2     Running     5          7m4s
istio-security-post-install-1.3.4-bhctr   0/1     Completed   0          7m6s
istio-sidecar-injector-66bc6cf76b-rc868   1/1     Running     0          7m2s
istio-telemetry-66786bfd8f-km8jh          2/2     Running     5          7m3s
istio-tracing-6cb4f885d4-275sq            1/1     Running     0          7m2s
kiali-68d8fc4d54-66s7r                    1/1     Running     0          7m4s
prometheus-5c8fb8f746-m8kc8               1/1     Running     0          7m3s

Istio Injection用のnamespaceを作成する


以下のコマンドでIstio Injection用のnamespaceを作成します。

このnamespace上にデプロイされたpodは、Envoyがサイドカーとして一緒にデプロイされるようになります。

ubuntu@microk8s-vm:~$ microk8s.kubectl create ns istio-app
namespace/istio-app created
ubuntu@microk8s-vm:~$ microk8s.kubectl label ns istio-app istio-injection=enabled
namespace/istio-app labeled
ubuntu@microk8s-vm:~$ microk8s.kubectl get ns -L istio-injection
NAME              STATUS   AGE   ISTIO-INJECTION
default           Active   43h   
istio-app         Active   53s   enabled
istio-system      Active   10m   disabled
kube-node-lease   Active   43h   
kube-public       Active   43h   
kube-system       Active   43h   
ubuntu@microk8s-vm:~$

テスト用アプリケーションを実行する


アプリケーションソースコードを作成する


明日はABテストを試してみる予定なので、二つのGoアプリケーションを作成します。httpリクエストをするとそれぞれ”Hello A”, “Hello B”と表示する簡単なサンプルアプリケーションです。

階層構造は以下の通りです。

$ tree hello
hello
├── helloA
│   ├── Dockerfile
│   └── src
│       ├── main.go
│       └── templates
│           └── index.html
└── helloB
    ├── Dockerfile
    └── src
        ├── main.go
        └── templates
            └── index.html

ポート8080を受け付けるとindex.htmlを表示するmain.goを以下に示します。これはhelloA, helloB共に同じです。

package main

import (
    "log"
    "net/http"
)

func main() {
    //templatesフォルダ配下を読み込む
    http.Handle("/", http.FileServer(http.Dir("templates")))
    //サーバー起動
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal("ListenAndServe:", nil)
    }
}

helloAのindex.htmlは以下の通りです。

Hello A

helloBのindex.htmlは以下の通りです。

Hello B

helloA, helloB共通のDockerfileは以下の通りです。

FROM golang:latest

# コンテナ作業ディレクトリの変更
WORKDIR /go/src/web
# ホストOSの ./src の中身を作業ディレクトリにコピー
COPY ./src .
RUN go build -o web
# ウェブアプリケーション実行コマンドの実行
CMD ["./web"]

コンテナイメージを作成する


以下のコマンドでそれぞれのコンテナイメージを作成します。

$ docker build -t hellob:latest helloA/
$ docker build -t hellob:latest helloB/

コンテナイメージをdocker registryに登録する


コンテナイメージをMicroK8s上で動かすにはdocker registryに一度登録します。登録方法は以下の記事をご参照ください。

MicroK8sでアプリケーションを動かす

以下のコマンドでdocker registryに登録します。

$ docker tag helloa 192.168.64.2:32147/helloa
$ docker tag hellob 192.168.64.2:32147/hellob
$ docker push 192.168.64.2:32147/helloa
The push refers to repository [192.168.64.2:32147/helloa]
8b8cbdb9bcec: Pushed 
0b5b80fff15e: Pushed 
6510ca638cc9: Pushed 
6e69dbdef94b: Pushed 
f0c38edb3fff: Pushed 
ef234633eec2: Pushed 
8967306e673e: Pushed 
9794a3b3ed45: Pushed 
5f77a51ade6a: Pushed 
e40d297cf5f8: Pushed 
latest: digest: sha256:f55138ac329f19a2602f1059a1e62b3b3915b256f42938cb19aadd5e24b9567a size: 2421
$ docker push 192.168.64.2:32147/hellob
The push refers to repository [192.168.64.2:32147/hellob]
475d9ae2ebee: Pushed 
c4f195275b83: Pushed 
6510ca638cc9: Layer already exists 
6e69dbdef94b: Layer already exists 
f0c38edb3fff: Layer already exists 
ef234633eec2: Layer already exists 
8967306e673e: Layer already exists 
9794a3b3ed45: Layer already exists 
5f77a51ade6a: Layer already exists 
e40d297cf5f8: Layer already exists 
latest: digest: sha256:e625cd9ac7996901c3b74ff463ee58a7187fbb456f2ea136a482d0ea86c2756a size: 2421

コンテナアプリケーションを実行する


以下のコマンドで、Istio Injection用のnamespaceにサンプルアプリケーションをデプロイします。

ubuntu@microk8s-vm:~$ microk8s.kubectl run helloa --image=localhost:32147/helloa --port=8080 -n istio-app
pod/helloa created
ubuntu@microk8s-vm:~$ microk8s.kubectl run hellob --image=localhost:32147/hellob --port=8080 -n istio-app
pod/hellob created

デプロイした後にpodを確認するとコンテナの数が二つになっています。これはIstioのEnvoyがpodの中で一緒に稼働しているからです。

ubuntu@microk8s-vm:~$ microk8s.kubectl get pods -n istio-app
NAME     READY   STATUS    RESTARTS   AGE
helloa   2/2     Running   0          22s
hellob   2/2     Running   0          10s

今日は以上です!明日は実際にIstioを設定してABデプロイをする方法について書きます。

MicroK8sでアプリケーションを動かす

以下の記事でMicroK8sをインストールしてみたので、MicroK8s上でアプリケーションを動かしてみたいと思います。

MicroK8sをMacOSにインストールしてみる

  • docker.ioのコンテナイメージを実行する
  • オリジナルコンテナイメージを実行する
    • docker registryを構築する
    • docker registryにオリジナルコンテナイメージを登録する
    • オリジナルコンテナイメージを実行する

docker.ioのコンテナイメージを実行する


最初にdocker.ioのコンテナイメージからアプリケーションを実行します。

以下のコマンドで、ベースイメージbusyboxを使って”Hello World”を出力するPodを作成します。

root@microk8s-vm:~# microk8s kubectl run hello --restart=Never --image=busybox -- echo "Hello World"

確認してみると、以下のように”Hello World”を出力していることがわかります。

root@microk8s-vm:~# microk8s kubectl get pods
NAME    READY   STATUS      RESTARTS   AGE
hello   0/1     Completed   0          65s
root@microk8s-vm:~# microk8s kubectl logs hello
Hello World

ということで、kubernetesと全く同じようにアプリケーションを実行できることが確認できました。

オリジナルコンテナイメージを実行する


次に自身で作成したオリジナルコンテナイメージを以下の構成でMicroK8sで動かします。

docker registryを構築する


以下のコマンドでdocker registryコンテナを作成します。今回は永続ボリュームは付与しません。

root@microk8s-vm:~# microk8s.kubectl run registry --image=registry --port=5000
pod/registry created
root@microk8s-vm:~# microk8s.kubectl get pods
NAME       READY   STATUS      RESTARTS   AGE
hello      0/1     Completed   0          6h56m
registry   1/1     Running     0          93s

作成したら、MacPCからアクセスできるようにNodePortで公開します。

root@microk8s-vm:~# microk8s.kubectl expose pod registry --type=NodePort --name=registry-service
service/registry-service exposed
root@microk8s-vm:~# microk8s.kubectl get svc
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes         ClusterIP   10.152.183.1    <none>        443/TCP          20h
registry-service   NodePort    10.152.183.23   <none>        5000:32147/TCP   8s

docker registryにオリジナルコンテナイメージを登録する


構築したdocker registryにオリジナルコンテナイメージを登録します。オリジナルコンテナイメージは、golangでcssファイルを読み込むで作成したwebコンテナイメージを利用します。

ifconfigコマンドでmultipassのVM IPアドレスを調べて、~/.docker/daemon.jsonに以下の内容を追記します。ポート番号はNodePortで公開したポートです。

"insecure-registries":["192.168.64.2:32147"]

dockerを起動し、以下のコマンドでdocker registryにオリジナルコンテナイメージを登録します。

$ docker push 192.168.64.2:32147/web
The push refers to repository [192.168.64.2:32147/web]
aa26d6c54ca2: Pushed 
51b5da98010b: Pushed 
6510ca638cc9: Pushed 
6e69dbdef94b: Pushed 
f0c38edb3fff: Pushed 
ef234633eec2: Pushed 
8967306e673e: Pushed 
9794a3b3ed45: Pushed 
5f77a51ade6a: Pushed 
e40d297cf5f8: Pushed 
latest: digest: sha256:860250e45f5851832e2c7632b3e6e1b2c7b89319fdd521bc99262baad6b992cc size: 2421

オリジナルコンテナイメージを実行する


以下のコマンドで登録したオリジナルコンテナイメージを実行します。

root@microk8s-vm:~# microk8s.kubectl run web --image=localhost:32147/web --port=8080 --insecure-skip-tls-verify
pod/web created
root@microk8s-vm:~# microk8s.kubectl get pods
NAME       READY   STATUS      RESTARTS   AGE
hello      0/1     Completed   0          7h28m
registry   1/1     Running     0          33m
web        1/1     Running     0          6s

NodePortで公開し、Mac PCからアクセスできるようにします。

root@microk8s-vm:~# microk8s.kubectl expose pod web --type=NodePort --name=web
service/web exposed
root@microk8s-vm:~# microk8s.kubectl get svc
NAME               TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes         ClusterIP   10.152.183.1    <none>        443/TCP          20h
registry-service   NodePort    10.152.183.23   <none>        5000:32147/TCP   26m
web                NodePort    10.152.183.60   <none>        8080:30162/TCP   29s

192.168.64.2:30162でアクセスしたところ、以下の画面になりました。

冷静に考えると、バックエンドのsearcherとdbを動かしてなかったので、エラーになってました。。。もっと早く気づくべきだった。。

# microk8s.kubectl logs web
2020/06/07 13:19:35 http: panic serving 10.1.2.1:51940: runtime error: invalid memory address or nil pointer dereference
goroutine 18 [running]:
net/http.(*conn).serve.func1(0xc000096000)
    /usr/local/go/src/net/http/server.go:1772 +0x139
panic(0x7993a0, 0xb09820)
    /usr/local/go/src/runtime/panic.go:975 +0x3e3
main.glob..func1(0x890ac0, 0xc0000c0000, 0xc0000a0300)
    /go/src/web/main.go:36 +0xc6
net/http.HandlerFunc.ServeHTTP(0x81cc60, 0x890ac0, 0xc0000c0000, 0xc0000a0300)
    /usr/local/go/src/net/http/server.go:2012 +0x44
net/http.(*ServeMux).ServeHTTP(0xb19820, 0x890ac0, 0xc0000c0000, 0xc0000a0300)
    /usr/local/go/src/net/http/server.go:2387 +0x1a5
net/http.serverHandler.ServeHTTP(0xc000136000, 0x890ac0, 0xc0000c0000, 0xc0000a0300)
    /usr/local/go/src/net/http/server.go:2807 +0xa3
net/http.(*conn).serve(0xc000096000, 0x891440, 0xc00009c000)
    /usr/local/go/src/net/http/server.go:1895 +0x86c
created by net/http.(*Server).Serve
    /usr/local/go/src/net/http/server.go:2933 +0x35c

まぁ、今回はKubernetesと同じように動かせることが分かっただけで良しとします。

中途半端になってしまいましたが、以上です!

MicroK8sをMacOSにインストールしてみる

最近MicroK8sがMacに対応されたので、インストールしてみたいと思います。

Canonicalの軽量Kubernetes「MicroK8s」がWindowsとMacに対応。インストーラーで簡単に導入可能に

  • MicroK8sとは
  • MicroK8sをMacにインストールする

MicroK8sとは


MicroK8sとは、ワークステーションやエッジデバイスで実行できる軽量Kubernetesです。必要なパッケージやライブラリが一つのスナップとして提供されているため、インストールが簡単です。ただし、シングルノードでのみ稼働するなど制約はあります。

Linuxで稼働しますが、WindosとMacでは仮想ソフトウェアで専用のLinux仮想マシンを起動して稼働します。

MicroK8sをMacにインストールする


以下のリンク先を参考に、インストールします。

https://ubuntu.com/tutorials/install-microk8s-on-mac-os#1-overview

Homebrewをインストールする


以下のコマンドでhomebrewをインストールします。私は既にインストール済みなので、スキップします。

$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"

MicroK8sの前提パッケージをインストールする


まずは以下のコマンドで前提パッケージをインストールします。

$ brew install ubuntu/microk8s/microk8s

インストールしたところ、以下のエラーが発生しました。Xcodeのバージョンが古いみたいです。

Error: Your Xcode (10.0) is too outdated.
Please update to Xcode 11.3.1 (or delete it).
Xcode can be updated from the App Store.

Error: Xcode alone is not sufficient on Mojave.
Install the Command Line Tools:
  xcode-select --install

私のMacOSはバージョンがMojaveで、Xcodeのバージョンアップができなかったため、一度MacOSのバージョンをCatalina(10.15.5)にしてXcodeのバージョンを11.5にしました。

再度実行すると、以下のエラーが表示されます。

Error: You have not agreed to the Xcode license. Please resolve this by running:
  sudo xcodebuild -license accept

促されているコマンドをそのまま実行して解消しました。

$ sudo xcodebuild -license accept

改めて以下のコマンドを実行します。今度はエラーなしに完了しました。

$ brew install ubuntu/microk8s/microk8s

MicroK8sをインストールする


以下のコマンドでmicrok8sをインストールします。

$ microk8s install
VM disk size requested exceeds free space on host.
Support for 'multipass' needs to be set up. Would you like to do that it now? [y/N]: y
Updating Homebrew...
==> Tapping homebrew/cask
Cloning into '/usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask'...
remote: Enumerating objects: 35, done.
remote: Counting objects: 100% (35/35), done.
remote: Compressing objects: 100% (35/35), done.
remote: Total 443429 (delta 16), reused 0 (delta 0), pack-reused 443394
Receiving objects: 100% (443429/443429), 200.17 MiB | 1.50 MiB/s, done.
Resolving deltas: 100% (314065/314065), done.
Tapped 1 command and 3570 casks (3,687 files, 214.8MB).
==> Downloading https://github.com/CanonicalLtd/multipass/releases/download/v1.2.1/multipass-1.2.1+mac-Darwin.pkg
==> Downloading from https://github-production-release-asset-2e65be.s3.amazonaws.com/114128199/fcb43c80-861f-11ea-90e8-08d13101e315?X-Am
######################################################################## 100.0%
==> Verifying SHA-256 checksum for Cask 'multipass'.
==> Installing Cask multipass
==> Creating Caskroom at /usr/local/Caskroom
==> We'll set permissions properly so we won't need sudo in the future.
Password:
==> Running installer for multipass; your password may be necessary.
==> Package installers may write to any location; options such as --appdir are ignored.
installer: Package name is multipass
installer: Installing at base path /
installer: The install was successful.
🍺  multipass was successfully installed!
Waiting for multipass...
Launched: microk8s-vm                                                           
2020-06-07T01:23:36+09:00 INFO Waiting for restart...
microk8s (latest/stable) v1.18.2 from Canonical✓ installed
MicroK8s is up and running. See the available commands with `microk8s --help`.

これでインストールが完了しました。kubectlコマンドを実行したら動きました。

$ microk8s kubectl get nodes
NAME          STATUS   ROLES    AGE    VERSION
microk8s-vm   Ready    <none>   4m9s   v1.18.2-41+b5cdb79a4060a3

ちなみにmicrok8sを止めるときは以下のコマンドを実行します。

$ microk8s stop
Stopped.

 

以上です。なんて簡単なんでしょうか。。。

LinuxでDockerのネットワークサブネットを変更する

LinuxでDockerのネットワークサブネットを変更する方法について紹介します。

  • Dockerのネットワークサブネットを考慮する理由

  • Dockerのネットワークサブネットを変更する方法

Dockerのネットワークサブネットを考慮する理由


dockerサブネットを気にしたことはありますか?
仮想的にアサインされるので外の機器に影響を与えることはないのですが、外のサブネットと重複する場合は少し考慮が必要です。

企業のシステムだと、Proxyサーバーを経由してインターネットへアクセスすることがあります。その時、docker.ioなどインターネットからベースイメージを取得したい場合、dockerでもproxyを指定することになります。しかし、以下のようにdockerサブネットとProxyサーバーのIPアドレスが重複する場合、docker.ioへ到達することができません。ProxyサーバーのIPアドレスを変える訳にもいかないので、dockerのサブネットを変更しなければなりません。

Dockerのネットワークサブネットを変更する方法


ここからDockerのネットワークサブネットを変更する方法について紹介します。と言ってもすぐできます。

以下のコマンドで/lib/systemd/system/docker.serviceを開き、オプション--bip=XXX.XXX.XXX.XXX/XXを追記した後、dockerをリスタートします。

$ vi /lib/systemd/system/docker.service
→「--bip=XXX.XXX.XXX.XXX/XX」を追記
$ systemctl daemon-reload
$ systemctl restart docker

 

以上です!私が実際に仕事で経験した小ネタでした。皆さんもご注意ください。

GoとMongoDBのコンテナでフロントとバックエンドを分けてみる

GoとMongoDBを使って、DBのデータを表示する簡単なWebアプリケーションを作成します。本記事は、過去に投稿したコンテナでフロントとバックエンドを分けてみるで作成したWebアプリケーションのデータ格納先をMongoDBにしたものです。

以下の流れで紹介します。

  • dbコンテナを作成する
  • searcherコンテナを作成する
    • ソースコードを作成する
    • コンテナイメージを作成する
  • webコンテナを作成する
  • アプリケーションを起動する

dbコンテナを作成する


以下の記事で作成したMongoDBのコンテナを利用します。

mongodbコンテナの起動時に初期データを登録してみる

コンテナ起動時に、recommendデータベースcategoriesコレクションに以下のデータが保管されています。

[
  { "name": "スポーツ" },
  { "name": "文化" },
  { "name": "娯楽" },
  { "name": "食事" },
  { "name": "お酒" },
  { "name": "イベント" },
  { "name": "ドライブ" },
  { "name": "文化" }
]

searcherコンテナを作成する


dbコンテナからデータを取得し、json形式で出力するsearcherコンテナを作成します。

ソースコードを作成する


MongoDBからデータを取得し、json形式で出力するgolangを作成します。
MongoDBからデータを取得する方法は、mongodbからデータを抽出するgolangを書くで解説しています。

package main

import (
    "fmt"
    "log"
    "net/http"
    "encoding/json"
    "time"
    "context"
    "os"
    "github.com/gorilla/mux"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "go.mongodb.org/mongo-driver/bson"
)

type category struct {
    Categories []struct{
      Name string `json:"name"`
    }`json:"categories"`
}

func main() {
    r := mux.NewRouter()
    // localhost:8082/publicでpublicハンドラーを実行
    r.Handle("/public", public)

    //サーバー起動
    if err := http.ListenAndServe(":8082", r); err != nil {
        log.Fatal("ListenAndServe:", nil)
    }
}

var public = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    //MongoDBの認証情報
    credential := options.Credential{
      Username: "root",
      Password: "password",
    }

    //MongoDBへの接続
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    connect, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://db:27017").SetAuth(credential))
    defer connect.Disconnect(ctx)

    //collectionの取得
    collection, err := connect.Database("recommend").Collection("categories").Find(context.Background(), bson.M{})
    if err != nil { log.Fatal(err) }
    var results []bson.M
    if err = collection.All(context.TODO(), &results); err != nil {
      log.Fatal(err)
    }

    //取得したコレクションの_idエントリを削除
    for _, entry_map := range results {
      delete(entry_map, "_id")
    }

    //bson.Mのデータをjson形式に変換
    json_result, err := json.Marshal(results)
    if err != nil {
        fmt.Println("JSON marshal error: ", err)
        return
    }

    //category構造体にJson形式の値を代入
    var output category
    if err := json.Unmarshal(json_result, &output.Categories); err != nil {
        log.Fatal(err)
        os.Exit(1)
    }

    //読み込んだJsonの表示
    json.NewEncoder(w).Encode(output)

})

コンテナイメージを作成する


Dockerfileを書いてコンテナイメージをビルドします。

ディレクトリ構造は以下の通りです。

$ tree .
.
├── Dockerfile
└── src
    └── main.go

Dockerfileは以下の通りです。

FROM golang:latest

# コンテナ作業ディレクトリの変更
WORKDIR /go/src/searcher
# ホストOSの ./src の中身を作業ディレクトリにコピー
COPY ./src .
RUN go get -u github.com/gorilla/mux
RUN go get -u "go.mongodb.org/mongo-driver/mongo"
RUN go build -o searcher
# ウェブアプリケーション実行コマンドの実行
CMD ["./searcher"]

以下のコマンドで作成します。

$ docker build -t searcher:latest .

webコンテナを作成する


webコンテナは以下の記事で作成したwebコンテナを流用します。以下の記事でフロントとバックエンドを分けていたので、修正する必要がありません。

goコンテナでフロントとバックエンドを分けてみる

アプリケーションを起動する


docker-composeを記述してコンテナアプリケーションを実行します。

docker-composeは以下の通りです。

version: '3'
services:
  db:
    container_name: db
    image: mongo-db:latest
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: password
    ports:
      - "27017:27017"
  searcher:
    container_name: searcher
    image: searcher:latest
    depends_on:
      - db
    links:
      - db
    ports:
      - "8082:8082"
  web:
    container_name: web
    image: web:latest
    depends_on:
      - searcher
    links:
      - searcher
    ports:
      - "8080:8080"

以下のコマンドを実行して起動します。

$ docker-compose up -d

localhost:8080にアクセスすると以下の結果が得られます。

以上です!

コピーしておくと便利なコマンド OpenShift編 Podにファイルを転送する

コピーしておくと便利なコマンド第三弾です。今回はOpenShift編ということで、Podにファイルを転送するコマンドをご紹介したいと思います。

ちょっとした時に、ローカルにあるファイルをコンテナに転送したいと思うことよくありませんか?例えば問題判別の時に定義ファイルを置き換えたいとき。永続ボリューム領域のファイルを書き換えようにも権限がなかったとき。再ビルドしようにも権限を付けてもらうにしても、結構面倒くさいです。

そんなときはコンテナにファイルを転送するコマンドを使って、ファイルを転送するのがオススメです。

Podの確認
$ oc get pods php-apache-1-zxxqd
NAME                 READY     STATUS    RESTARTS   AGE
php-apache-1-zxxqd   1/1       Running   1          11d

Pod内のファイル確認
$ oc exec -it  php-apache-1-zxxqd -- cat /test
cat: /test: No such file or directory
command terminated with exit code 1

ファイル作成
$ echo "Hello World" > test

ファイルの転送
$ oc cp test php-apache-1-zxxqd:/test

Pod内のファイル確認
$ oc exec -it  php-apache-1-zxxqd -- cat /test
Hello World
$

 

本来コンテナはImmutableな性質なので、このような操作はよくありませんが、ちょっとした問題判別や検証に使えるコマンドとして紹介しました。

以上です!皆さんも是非使ってみてください!