CKADを受けてみた

冬休みの宿題ということでCKADを受けてみることにした。こういう資格は初めて、かつ、最近Kubernetesに触れていなかったので、テスト対策としてお勉強。

テスト向けにやったこと

昔やったこと

  • 入門kubernetes読む
  • 公式ドキュメントやTutorialを読む/試す

結果

  • 合格できました

反省

  • JobとかProbeとかLog周りを勉強しておけばよかった
    • うる覚えで調べながら進めたので時間がかかってしまった
  • 当初、yamlの編集にemacs使ってたけど、ctrl-sがブラウザ側でハンドルされてしまうみたいで、コンソールが反応/表示しなくなること多数。コンソールをリセットしたり、相当時間を無駄にした。素直にvi使っておくべきだった。

gcloudコマンドでgcpに作成したインスタンスにアクセスする

直に忘れるからメモを残しておく。公式ドキュメントはこちら

gcloudコマンドは、Google Cloud SDKの一部で、GCPのいろいろなサービスをこのコマンドで管理できる。 OpenStackにおけるopenstackコマンドみたいなもの。

インストール

gcloudコマンドはGoogle Cloud SDKの一部なので、このSDKをダウンロードし、インストールする。 fedoraの場合はyumコマンドで入れられる

$ sudo tee -a /etc/yum.repos.d/google-cloud-sdk.repo << EOM
[google-cloud-sdk]
name=Google Cloud SDK
baseurl=https://packages.cloud.google.com/yum/repos/cloud-sdk-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
       https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOM
$ sudo dnf install google-cloud-sdk

初期化

$ gcloud init

いろいろと聞かれるので、適宜設定していく。

使ってみる

$ gcloud compute ssh <instance名>
[hoge@instance  ~]$  

GCPUG Tokyo June 2018

抽選に初当選したので行ってみた。メモ。

はじめに

GCPUGについて紹介

How to utilize GKE for QA environment / @masudak (mercari)

  • mercariでは、QA環境の中で1年近く前からGKEつかってるらしい。今日はそのお話。
  • 発表者:@masudak
    • SET (Software Engineer in Test)
      • SETは2011年にgoogleが提唱(GoogleはSETからSETI(Software Engineer, Tools & Infrastructure)に移っているというもある模様)
      • mercariは2016/10から導入している --> エンジニアblogに記事あり
  • 開発フロー
    • PMが仕様をJIRAに書く
    • エンジニア・デザイナーがローカルで実装
    • レビュー/
    • QA環境でQAエンジニアがテスト
  • QA環境
    • QAエンジニア/エンジニアがQAする環境
    • 共通のDB、検索データをもつ
    • API/Frontend/Admin/Webなどが動く
      • これらは独立してて、他の人に影響を与えないでテストできる
    • 要件
      • 自分専用/WebUIでデプロイ/スケール/可能な限り本番構成反映
  • 2017春
    • 1年前くらいにmonolithic --> microservice (PHP --> Go)
    • CircleCIの中でデプロイ
      • CircleCIのなかでごにょごにょ
      • PR番号とってきてk8s.yaml(テンプレ)の中の変数をsedで置き換え
      • 外部からのアクセスはリバプロ(Goで実装)でhttp header見て適切なPodにrouting
      • わりと地道なことをやってたみたい(Pod消す時とか)
    • 力技 --> めんてつらい
  • 2017後半
    • Spinnaker
      • CircleCIからはGCRにイメージをpush
      • それをトリガにSpinnakerがデプロイ(pollingしてるらしい)
      • WebUI中心で微妙
      • PR Podsに対応できない = PRごとの環境が作れない
    • CRDを作成(k8sのreconciliation loopを利用)
      • k8s controller for PR
        • ループしてPRの状況を確認する
        • Openだったら既存のRSを複製
        • 環境変数などを書き換え
        • desired stateを書き換え
        • PRのRS完成
        • ひつように応じてexternal-dnsドメイン付与
        • ---> ひょっとしてkustomizeで置き換え可能かも
  • まとめ
    • 開発生産性も品質もまだ追求できる
    • たった1つのVM時代(virtualenvとか使って環境分離??)から2年でここまできた
  • Q&A
    • databaseとかのスキーマ変わるような場合のテストはどうするのか?
      • API gatewayの開発の話なので、DBのスキーマが変わるような変更には対応していない
    • SETの役割
      • テストのやり方とか、こうテストするといいよとか考えているの?
      • CaosEngineeringとか考えているけど、チームとしてはまだ取り組めていない
      • SETは4人

GCPの新機能の話もあったけど、App Engineはちゃんとつかっていないので、メモはなし ;p

感想

  • SETというroleを作り、QAにもちゃんとコストかけて改善しているのは良いというか羨ましいですね。
  • CircleCIのながでごにょごにょの部分が今ひとつ何やってるかわからず。PRごとにReplicaSet作ってデプロイしているのかな。そのうち資料が公開されたら改めて確認する。
  • ここでもCRDだった。やはりCRD化(k8s Native化)が流れなの?
  • マイクロサービス化に取り組んでいるのここ1年ぐらいなんですね。もっと前からやっているのかと勝手に思ってた。
  • たった1つのVM時代とはどんな感じでQA環境として使っていたのか気になった。
  • 開発はローカルでやっているみたいだけど、やはり開発はローカルでやるのが普通なのかな。GKE上とかでやらないのか気になった。

kubeadmで構築した環境のkube-apiserverを再起動する

kube-apiserverを設定変更して再起動したくなったのだけど、 kubeadmでデプロイした場合どうするのか知らなかったので調べた結果をメモ。

まあ、全く同じ質問がstack overflowにあったので、それを読めば良いです。

まとめると下記。

  • kubeadmはkube-apiserverをstatic Podとしてデプロイする
  • static Podとは
    • kubeletが直接管理しているPodで、Podがcrashとかするとkubeletが勝手に再起動したりしてくれる
    • apiサーバからは"mirror pod"が見えるけど、apiサーバ経由で操作することは不可能
  • そのため、kube-apiserverを再起動したい場合、kube-apiserverが動いているホストに ログインしてdockerコマンドでコンテナを止める
  • するとkubeletが再起動してくれる

ちなみに、kube-apiserverのmanifestはcentosの場合、/etc/kubernetes/manifestsにあるので、設定を変更する場合はこれを修正する。 (ちゃんと確認していないけど、設定変更したらkubeletがそれを検知してくれてコンテナ再起動するかも --> 今度調べる)

参考

minikubeでheapsterを使う

minikubeの環境でHPA試してみようかなと思って、とりあえずheapsterが動いているか確認したらいなかったので動かしてみました。

とりあえずminikube環境で、heapsterが動いているか確認。

$ kubectl get pod --namespace=kube-system
NAME                                    READY     STATUS    RESTARTS   AGE
kube-addon-manager-minikube             1/1       Running   18         68d
kube-dns-54cccfbdf8-dwv75               3/3       Running   54         68d
kubernetes-dashboard-77d8b98585-b4fhq   1/1       Running   18         68d
storage-provisioner                     1/1       Running   18         68d

ないですね。 とりあえず、グーグルさんに聞いてみたら、minikubeはaddonのシステムがあって、heapsterもアドオンとして実装されているので、有効にすれば良いらしい。

手元のminikube にあるaddonのリストを見てみる。

$ minikube addons list
- addon-manager: enabled
- coredns: disabled
- dashboard: enabled
- default-storageclass: enabled
- efk: disabled
- freshpod: disabled
- heapster: disabled
- ingress: disabled
- kube-dns: enabled
- registry: disabled
- registry-creds: disabled
- storage-provisioner: enabled

heapsterはdisabledになってますね。有効化。

$ minikube addons enable heapster
heapster was successfully enabled

kubectlでheapsterがデプロイされたか確認。

 kubectl get pod --namespace=kube-system
NAME                                    READY     STATUS    RESTARTS   AGE
heapster-nbt68                          1/1       Running   0          3s
influxdb-grafana-6zm66                  2/2       Running   0          3s
kube-addon-manager-minikube             1/1       Running   18         68d
kube-dns-54cccfbdf8-dwv75               3/3       Running   54         68d
kubernetes-dashboard-77d8b98585-b4fhq   1/1       Running   18         68d
storage-provisioner                     1/1       Running   18         68d

すばらしい。 とても簡単にheapsterがデプロイできました。

[参考]

github.com

DNSについて (続き)

kubernetesのdocumentのDNS for Services and Podsでは、次のような記述がある。

Pod’s hostname and subdomain fields

Currently when a pod is created, its hostname is the Pod’s metadata.name value.

The Pod spec has an optional hostname field, which can be used to specify the Pod’s hostname. When specified, it takes precedence over the Pod’s name to be the hostname of the pod. For example, given a Pod with hostname set to “my-host”, the Pod will have its hostname set to “my-host”.

The Pod spec also has an optional subdomain field which can be used to specify its subdomain. For example, a Pod with hostname set to “foo”, and subdomain set to “bar”, in namespace “my-namespace”, will have the fully qualified domain name (FQDN) “foo.bar.my-namespace.svc.cluster.local”.

一例として、headlessサービスでhostname、subdomainの両方を設定しているものが書いてあるけど、普通のサービスだったらとか、hostname指定しなかった場合とかの挙動が分からなかったので、試してみた。

先に結論から言うと、headlessサービスでhostonameとsubdomainの両方の設定をしてある必要がありました。

以下、メモ。

普通のサービス + hostname指定 + subdomain指定

マニフェストは下記。

$ cat normal_hostname_subdomain.yaml
apiVersion: v1                                                                  
kind: Service                                                                   
metadata:                                                                       
  name: my-service                                                              
spec:                                                                           
  selector:                                                                     
    name: busybox                                                               
  ports:                                                                        
  - name: foo                                                                   
    port: 1234                                                                  
    targetPort: 5678                                                            
---                                                                             
apiVersion: v1                                                                  
kind: Pod                                                                       
metadata:                                                                       
  name: busybox1                                                                
  labels:                                                                       
    name: busybox                                                               
spec:                                                                           
  hostname: busybox-1                                                           
  subdomain: my-service                                                         
  containers:                                                                   
  - name: busybox                                                               
    image: busybox                                                              
    command:                                                                    
    - sleep                                                                     
    - "3600"                                                                    
---                                                                             
apiVersion: v1                                                                  
kind: Pod                                                                       
metadata:                                                                       
  name: busybox2                                                                
  labels:                                                                       
    name: busybox                                                               
spec:                                                                           
  hostname: busybox-2                                                           
  subdomain: my-service                                                         
  containers:                                                                   
  - name: busybox                                                               
    image: busybox                                                              
    command:                                                                    
    - sleep                                                                     
    - "3600" 

デプロイ。

$ kubectl apply -f normal_hostname_subdomain.yaml
service "my-service" created
pod "busybox1" created
pod "busybox2" created

$ kubectl get pod
NAME       READY     STATUS    RESTARTS   AGE
busybox1   1/1       Running   0          54s
busybox2   1/1       Running   0          54s

$ kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP    12d
my-service   ClusterIP   10.101.236.136   <none>        1234/TCP   1m

同じクラスタ内の別Podで名前解決してみる。

$ kubectl run -it --rm --restart=Never --image=busybox nslookup -- /bin/sh
If you don't see a command prompt, try pressing enter.
/ # nslookup my-service
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      my-service
Address 1: 10.101.236.136 my-service.default.svc.cluster.local
/ # nslookup busybox1.my-service
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'busybox1.my-service'
/ # nslookup busybox-1.my-service
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'busybox-1.my-service'

ということで、名前解決はできません。

headlessサービス + hostname指定なし + subdomain指定

利用したマニフェスト

$ cat headless_no_subdomain.yaml
File Edit Options Buffers Tools Text Help                                       
apiVersion: v1                                                                  
kind: Service                                                                   
metadata:                                                                       
  name: my-service                                                              
spec:                                                                           
  selector:                                                                     
    name: busybox                                                               
  clusterIP: None                                                               
  ports:                                                                        
  - name: foo                                                                   
    port: 1234                                                                  
    targetPort: 5678                                                            
---                                                                             
apiVersion: v1                                                                  
kind: Pod                                                                       
metadata:                                                                       
  name: busybox1                                                                
  labels:                                                                       
    name: busybox                                                               
spec:                                                                           
  subdomain: my-service                                                         
  containers:                                                                   
  - name: busybox                                                               
    image: busybox                                                              
    command:                                                                    
    - sleep                                                                     
    - "3600"                                                                    
---                                                                             
apiVersion: v1                                                                  
kind: Pod                                                                       
metadata:                                                                       
  name: busybox2                                                                
  labels:                                                                       
    name: busybox                                                               
spec:                                                                                                                              
  subdomain: my-service                                                         
  containers:                                                                   
  - name: busybox                                                               
    image: busybox                                                              
    command:                                                                    
    - sleep                                                                     
    - "3600"      

同じように、デプロイして別のPodから名前解決をしてみる。

$ kubectl apply -f headless_no_subdomain.yaml
service "my-service" created
pod "busybox1" created
pod "busybox2" created

$ kubectl get pod
NAME       READY     STATUS    RESTARTS   AGE
busybox1   1/1       Running   0          17s
busybox2   1/1       Running   0          17s

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP    12d
my-service   ClusterIP   None         <none>        1234/TCP   22s

$ kubectl run -it --rm --restart=Never --image=busybox nslookup -- /bin/sh
If you don't see a command prompt, try pressing enter.

/ # nslookup my-service
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      my-service
Address 1: 172.17.0.4
Address 2: 172.17.0.5

/ # nslookup busybox1.my-service
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

nslookup: can't resolve 'busybox1.my-service'

ということで、busybox1.my-serviceは名前解決できず。 同様に、hostnameを指定して、subdomainを指定しない場合もだめ。hostnameもsubdomainも指定しない場合もだめでした。

以上より、kubernetesのドキュメントにあるように*1名前解決可能にする条件は以下となる。

  1. headlessサービスであること
  2. Podのhostnameとsubdomainを設定すること

*1:The Pod spec also has an optional subdomain field which can be used to specify its subdomain. For example, a Pod with hostname set to “foo”, and subdomain set to “bar”, in namespace “my-namespace”, will have the fully qualified domain name (FQDN) “foo.bar.my-namespace.svc.cluster.local”