5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Kubernetesにいまさら入門した話

Last updated at Posted at 2019-12-30

ローカル環境構築

Minikubeとkubectlをダウンロードする。

kubectlはPATHを通す。Minikubeはインストーラーを実行してインストール。

minikube.exe start
minikube.exe dashboard

ブラウザが立ち上がってKubernetes Dashboardが表示される。
とりあえずノードを作る。

kubectl create deployment hello-node --image=gcr.io/hello-minikube-zero-install/hello-node

Deploymentを確認。

kubectl get deployments

Podを確認する。

kubectl get pods

Kubernetes Dashboardで確認する。

image.png

イベントを確認する。

kubectl get events

設定を確認する。

kubectl config view

設定はこんなん出ます。

apiVersion: v1
clusters:
- cluster:
    certificate-authority: C:\Users\xxxxxx\.minikube\ca.crt
    server: https://192.168.99.101:8443
  name: minikube
contexts:
- context:
    cluster: minikube
    user: minikube
  name: minikube
current-context: minikube
kind: Config
preferences: {}
users:
- name: minikube
  user:
    client-certificate: C:\Users\xxxxxx\.minikube\client.crt
    client-key: C:\Users\xxxxxx\.minikube\client.key

サービスを作る。--type=LoadBalancerフラグはServiceをクラスタ外部に公開するということ。

kubectl expose deployment hello-node --type=LoadBalancer --port=8080

サービスを確認する。

$ kubectl get service
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
hello-node   LoadBalancer   10.96.194.179   <pending>     8080:30610/TCP   3m21s
kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP          25h

ロードバランサーをサポートするクラウドプロバイダーでは、Serviceにアクセスするための外部IPアドレスが提供されます。 Minikube では、LoadBalancerタイプはminikube serviceコマンドを使用した接続可能なServiceを作成します。

ということらしい。以下でブラウザが立ち上がる。

minikube service hello-node

用語の確認

このチュートリアルにしたがってやるよ
https://thinkit.co.jp/series/7342

Workloads…コンテナの実行に関するリソース

Pod

  • Workloads リソースの最小単位
  • Pod は1つ以上のコンテナから構成
  • ネットワークは隔離されておらず、Pod内のコンテナはお互いにlocalhost宛で通信することが可能
  • 補助するサブコンテナのことを「サイドカー」と呼ぶ

ReplicaSetとReplicationController

  • Podのレプリカを生成し、指定した数のPodを維持し続ける
  • ReplicationControllerは今後廃止される
  • 特定のラベルがつけられたPodの数をカウントし、レプリカ数が不足している場合はtemplateからPodを生成する

Deployments ★推奨

  • 複数のReplicaSetを管理しローリングアップデートやロールバックなどを実現する
    1. 新しいReplicaSetを作成
    2. 新しいReplicaSet上のレプリカ数(Pod数)を徐々に増やす
    3. 古いReplicaSet上のレプリカ数(Pod数)を徐々に減らす
    4. 古いReplicaSetはレプリカ数0で保持する
  • ReplicaSetが生成される条件は「生成されるPodの内容の変更」。
  • 正確にはspec.templateに変更があるとReplicaSetを新規で作成し、ローリングアップデートが行われる。
  • ロールバック機能の実体はReplicaSetの切り替えと同義で、ReplicaSetは基本的には履歴としてレプリカ数を0にして形だけは残っている。
  • 実際にはこのロールバック機能を使っている利用者は少なく、古いYAMLファイルを再度kubectl applyして適用したほうが良い。

StatefulSet

  • データベースなどstatefulなワークロードに対応するためのリソース
  • ReplicaSetとの違い
    • 生成されるPod名が数字でindexingされたものになる
    • PersistentVolumeを使っている場合は同じディスクを利用して再作成される
    • Pod名が変わらない
  • 1つずつPodを作成し、Ready状態になってから次のPodを作成し始める。
  • 常にindex:0がMasterとなるような構成を取ることが可能。

DaemonSet

  • 全nodeに1 podずつ配置する

Jobs

  • 停止=正常終了となるようなもので使う。
  • ワークロード別パラメータの使い分け(Think ITより)
ワークロード completions parallelism backoffLimit
1回だけ実行するタスク 1 1 1
N並列で実行させるタスク M N P
1個ずつ実行するワークキュー 未指定 1 P
N並列で実行するワークキュー 未指定 N P

CronJobs

  • Cronのようにスケジュールされた時間にJobを生成する

Service,LB,Network

Kubernetesでクラスタを構築すると、Podのための内部用ネットワークが構成される。Pod内のコンテナはお互いにlocalhost宛で通信することが可能。

サービスディスカバリ(DNS的な仕組み)もサービスが提供する。以下の三種類ある。

  • Aレコード(kube-dnsのAレコード)を利用したサービスディスカバリ。
    • IP単位で対応させるのがAレコード
    • 正式なFQDNは[Service名].[Namespace名].svc.[ClusterDomain名]
    • コンテナ内の/etc/resolv.confで、[Service名].[Namespace名]や[Service名]だけで名前解決できる
  • SRVレコード(kube-dnsのSRVレコード)を利用したサービスディスカバリ
    • IP+Port単位で対応させるのがSRVレコード
    • 正式なFQDNは[**ServiceのPort名].[**Protocol].[Service名].[Namespace名].svc.[ClusterDomain名]
  • 環境変数を利用したサービスディスカバリ
    • 環境変数でも同じNamespaceのサービスが確認できる

サービスのタイプ別の挙動の違い

  • ClusterIP
    • クラスタ内からでないと疎通性のないInternal Networkに作り出されるVIPが割り当てられる
  • ExternalIP
    • type自体はClusterIP
    • externalIPsにnodeのIPを指定することでKubernetesクラスタ外からそのIPでのアクセスができる
    • ExternalIPに利用するIPは全てのnodeのIPでなくて良い
    • Podへのリクエストは分散される
  • NodePort
    • 全てのNodeで受けたトラフィックをコンテナに転送する。ExteralIPの全ノード版
    • ノード上のNodePortに到達したパケットは、さらにノードをまたいだPodへもロードバランシングされる
    • externalTrafficPolicyをデフォルトのClusterからLocalに設定すると同じノードのPodに転送される
  • LoadBalancer
    • コンテナ内からの通信はClusterIPを利用するために、ClusterIPも自動的に確保される
    • 外部のLoadBalancerと連携できるのは、GCP、AWS、Azureを始めとしたCloudProviderのみ
    • 8080:30082/TCPみたいに表示されるのは、左側がVIPにアクセスした時のポートで、右側がノードのIPでアクセスしたときのポート
  • Headless
    • 永続性の高いStatefulSetのみで利用できる。逆に言えばこれを使いたいとき以外は関係ない
    • PodのIP Addressが返ってくるService
    • DNS Round Robin(DNS RR)を使ったエンドポイントを提供
    • 転送先のPodのIPアドレスがクラスタ内DNSから返ってくる形で負荷分散
    • spec.typeはClusterIP
    • metadata.nameはStatefulSetのspec.serviceNameと同じ。
    • spec.clusterIPはNone
    • [Pod名].[Service名].[Namespace名].svc.[domain名]
  • ExternalName
    • Service名の名前解決に対してCNAMEを返す
    • これ、使わない気がします
  • Ingress
    • L7 LoadBalancerを提供する
    • 「Kind: Service」タイプのリソースではなく、「Kind: Ingress」タイプのリソース

Config

環境変数を渡す際にはpodテンプレートにenvまたはenvFromを指定する。下記の5つの情報源から環境変数を埋め込むことができる。

  • 静的設定 値をそのまま書くだけ
  • Podの情報 fieldRefを使う
  • Containerの情報 resourceFieldRefを使う
  • Secretリソースの機密情報 
  • ConfigMapリソースからのKey-Value値。nginx.confやhttpd.confのような設定ファイル自体も保存可能

Storage

Volume

  • 既存のボリューム(ホストの領域、NFS、Ceph、GCP Volume)などをYAML Manifestに直接指定する
  • 利用者が新規でボリュームを作成したり、既存のボリュームを削除したりはできない
  • YAML ManifestからVolumeリソースを作成するといった処理もない

PersistentVolume

  • 新規のボリュームの作成や、既存のボリュームの削除などを行うことが可能
  • YAML ManifestなどからPersistent Volumeリソースを別途作成する形
  • GCPやAWSのボリュームサービスでは、Persistent VolumeプラグインとVolumeプラグインの両方が用意されている
  • Persistent Volumeはクラスタにボリュームを登録するだけ

PersistentVolumeClaim

  • 作成されたPersistentVolumeリソースの中からアサインするためのリソース

  • Dynamic Provisioningを利用した場合は、Persistent Volume Claimが利用されたタイミングでPersistent Volumeを動的に作成することが可能

Volumeプラグイン

  • EmptyDir Pod用の一時的なディスク領域として利用可能です。PodがTerminateされると削除されます

  • HostPath Node上の領域をコンテナにマッピング。

よく使う?kubectlのコマンド

Workload関連のコマンド

2つのコンテナを持つPodを作る。

まずはyamlファイルを作る。

apiVersion: v1
kind: Pod
metadata:
  name: sample-2pod
spec:
  containers:
    - name: nginx-container
      image: nginx:1.12
      ports:
      - containerPort: 80
    - name: redis-container
      image: redis:5.0.5
      ports:
      - containerPort: 6379

以下のコマンドでyamlファイルの内容を反映させる。

kubectl apply -f .\2pod_sample.yaml

コンテナの中に入る

ReplicaSetで作ったPodのコンテナの中に入ることもできるよ。

# sample-podはPodの名前。
# 複数のコンテナがPodの中に存在する場合、何も指定しないとデフォルトのコンテナが選択される。
# 特定のコンテナを指定したい場合は-cでコンテナの名前を指定する。
$ kubectl exec -it sample-2pod -c nginx-container /bin/bash

存在しているPodを確認する

kubectl get pods

Podを消す

kubectl delete pods sample-2pod

ReplicaSetを作る

yamlファイルを作る。

ReplicaSetが生成するPodは、[ReplicaSet名]−[乱数]で命名される。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: sample-rs
spec:
  replicas: 3
  # selectorの下のラベルの追加Podを探しにいって維持しようとする
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
    # Podにつけられるラベルはここで指定して、これは上の指定と一致していないといけない
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
          ports:
            - containerPort: 80

反映させる。これはPodのときと同じコマンド。ファイルが違うだけ

kubectl apply -f .\rs_sample.yaml

存在しているReplicaSetを確認する。

$ kubectl get replicaset
NAME        DESIRED   CURRENT   READY   AGE
sample-rs   3         3         3       19m
$ kubectl get replicaset -o wide
NAME        DESIRED   CURRENT   READY   AGE   CONTAINERS        IMAGES       SELECTOR
sample-rs   3         3         3       38m   nginx-container   nginx:1.12   app=sample-app

オートヒーリングの確認。Podを消してみて、再作成されるのを確認する。

$ kubectl get pods -o wide
NAME              READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
sample-2pod       2/2     Running   2          16h     172.17.0.6   minikube   <none>           <none>
sample-rs-776l8   1/1     Running   0          3h53m   172.17.0.8   minikube   <none>           <none>
sample-rs-dhjdr   1/1     Running   0          3h53m   172.17.0.9   minikube   <none>           <none>
sample-rs-kcbn2   1/1     Running   0          3h53m   172.17.0.7   minikube   <none>           <none>
$ kubectl delete pod sample-rs-dhjdr
pod "sample-rs-dhjdr" deleted
$ kubectl get pods -o wide
NAME              READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
sample-2pod       2/2     Running   2          16h     172.17.0.6   minikube   <none>           <none>
sample-rs-776l8   1/1     Running   0          3h54m   172.17.0.8   minikube   <none>           <none>
sample-rs-kcbn2   1/1     Running   0          3h54m   172.17.0.7   minikube   <none>           <none>
sample-rs-w9h4r   1/1     Running   0          4s      172.17.0.9   minikube   <none>           <none>

ReplicaSetの詳細を確認する。

$ kubectl describe replicaset sample-rs
Name:         sample-rs
Namespace:    default
Selector:     app=sample-app
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"name":"sample-rs","namespace":"default"},"spec":{"replicas":3,"s...
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=sample-app
  Containers:
   nginx-container:
    Image:        nginx:1.12
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age    From                   Message
  ----    ------            ----   ----                   -------
  Normal  SuccessfulCreate  5m24s  replicaset-controller  Created pod: sample-rs-w9h4r

Podのスケーリング

yamlを書き換える以外の方法だと以下のコマンドを使う

$ kubectl scale replicaset sample-rs --replicas 5

Deployment(でPod)を作る

まずyamlファイルを作る

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
          ports:
            - containerPort: 80

読み込ませる

$ kubectl apply -f .\deployment_sample.yaml

存在するDeploymentを確認する

$ kubectl get deployment
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
sample-deployment   3/3     3            3           3m30s
$ kubectl describe deployment sample-deployment
Name:                   sample-deployment
Namespace:              default
CreationTimestamp:      Sat, 28 Dec 2019 15:58:26 +0900
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
                        kubectl.kubernetes.io/last-applied-configuration:
                          {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"sample-deployment","namespace":"default"},"spec":{"replic...
Selector:               app=sample-app
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=sample-app
  Containers:
   nginx-container:
    Image:        nginx:1.12
    Port:         80/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   sample-deployment-c6c6778b4 (3/3 replicas created)
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  5m55s  deployment-controller  Scaled up replica set sample-deployment-c6c6778b4 to 3

Deploymentのイメージは以下で更新できる。

$ kubectl set image deployment sample-deployment nginx-container=nginx:1.13

Jobを作成する。

yamlファイルを作る。

apiVersion: batch/v1
kind: Job
metadata:
  name: sample-job
spec:
  # 実行の回数を指定する
  completions: 1
  # 並列度を指定する
  parallelism: 1
  # 失敗を許容する回数
  backoffLimit: 10
  template:
    spec:
      containers:
      - name: sleep-container
        image: centos:latest
        command: ["sleep"]
        args: ["60"]
      # spec.template.spec.restartPolicyにはOnFailureまたはNeverが指定可能
      # NeverはPodが障害時には新規のPodが作成される
      # OnFailureの場合には、再度同一のPodを利用してJobを再開する
      restartPolicy: Never

反映させる。

$ kubectl apply -f job_sample.yml

Jobの実行状況を確認する。

$ kubectl get jobs
NAME         COMPLETIONS   DURATION   AGE
sample-job   0/1           40s        40s

CronJobを作る。

yamlファイルを作る。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: sample-cronjob
spec:
  schedule: "*/1 * * * *"
  # spec.concurrencyPolicyで指定する同時実行に関するポリシー
  # - Allow(default):同時実行に対して制限を行わない
  # - Forbid:前のJobが終了していない場合は次のJobは実行しない
  # - Replace:前のJobをキャンセルし、新たにJobを開始する
  concurrencyPolicy: Forbid
  # 開始時刻が遅れた場合に許容できる秒数
  startingDeadlineSeconds: 30
  # 保存する成功Jobの数
  successfulJobsHistoryLimit: 5
  # 保存する失敗Jobの数
  failedJobsHistoryLimit: 5
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: sleep-container
            image: centos:latest
            command: ["sleep"]
            args: ["30"]
          restartPolicy: Never

ネットワーク関連のコマンド

サービスを作成する

apiVersion: v1
kind: Service
metadata:
  name: sample-clusterip
spec:
  type: ClusterIP
  # 複数のPortを指定できる
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app

ClusterIPを静的にIPを指定する場合

apiVersion: v1
kind: Service
metadata:
  name: sample-static-clusterip
spec:
  type: ClusterIP
  clusterIP: 10.96.253.80
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app

External IPを指定する

apiVersion: v1
kind: Service
metadata:
  name: sample-static-clusterip
spec:
  type: ClusterIP
  clusterIP: 10.96.253.80
  externalIPs:
    - 192.168.99.101
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app

LoadBalancerを指定した場合

apiVersion: v1
kind: Service
metadata:
  name: sample-lb
spec:
  type: LoadBalancer
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
      nodePort: 30082
  selector:
    app: sample-app

設定を適用する。

kubectl apply -f clusterip_sample.yml

作成したサービスを確認する

kubectl get services
NAME               TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
kubernetes         ClusterIP   10.96.0.1     <none>        443/TCP    6d13h
sample-clusterip   ClusterIP   10.96.64.65   <none>        8080/TCP   23s

サービスの詳細を確認する

kubectl describe services sample-clusterip
Name:              sample-clusterip
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration:
                     {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"sample-clusterip","namespace":"default"},"spec":{"ports":[{"name"...      Selector:          app=sample-app
Type:              ClusterIP
IP:                10.96.64.65
Port:              http-port  8080/TCP
TargetPort:        80/TCP
Endpoints:         172.17.0.11:80,172.17.0.14:80,172.17.0.16:80 + 5 more...
Session Affinity:  None
Events:            <none>

clusteripの動作を確認する。内部から出ないと呼べないので内部から

IPを使ってアクセスする。使えるIPはkubectl get servicesで確認したもの。

kubectl run --image=centos:7 --restart=Never --rm  -ti testpod -- curl -s http://10.96.64.65:8080

Aレコードを使ってアクセスする。[Service名].[Namespace名].svc.[ClusterDomain名]

 kubectl run --image=centos:7 --restart=Never --rm  -ti testpod -- curl -s http://sample-clusterip.default.svc.cluster.local:8080

/etc/resolv.confの[Service名]を使ってアクセスする。

kubectl run --image=centos:7 --restart=Never --rm  -ti testpod -- curl -s http://sample-clusterip:8080

SRVレコードを使ってアクセスする。[**ServiceのPort名].[**Protocol].[Service名].[Namespace名].svc.[ClusterDomain名]

kubectl run --image=centos:7 --restart=Never --rm  -ti testpod -- curl -s http://_http-port._tcp.sample-clusterip.default.svc.cluster.local:8080

環境変数にもレコードと同じ内容が設定される。

$ kubectl run --image=centos:7 --restart=Never --rm  -ti testpod -- env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=testpod
TERM=xterm
KUBERNETES_SERVICE_PORT_HTTPS=443
SAMPLE_CLUSTERIP_SERVICE_HOST=10.96.64.65
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_PORT=tcp://10.96.0.1:443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
SAMPLE_CLUSTERIP_SERVICE_PORT=8080
SAMPLE_CLUSTERIP_PORT_8080_TCP_ADDR=10.96.64.65
KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_PORT=443
SAMPLE_CLUSTERIP_PORT_8080_TCP=tcp://10.96.64.65:8080
SAMPLE_CLUSTERIP_PORT_8080_TCP_PROTO=tcp
SAMPLE_CLUSTERIP_PORT_8080_TCP_PORT=8080
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
SAMPLE_CLUSTERIP_SERVICE_PORT_HTTP_PORT=8080
SAMPLE_CLUSTERIP_PORT=tcp://10.96.64.65:8080

External IPで指定できるノードのIPは以下で取得できる

$ kubectl get node -o custom-columns="NAME:{metadata.name},IP:{status.addresses[].address}"
NAME       IP
minikube   192.168.99.101

External IPの指定した結果を確認する

kubectl get services
NAME                      TYPE        CLUSTER-IP     EXTERNAL-IP      PORT(S)    AGE
kubernetes                ClusterIP   10.96.0.1      <none>           443/TCP    6d16h
sample-clusterip          ClusterIP   10.96.64.65    <none>           8080/TCP   3h48m
sample-static-clusterip   ClusterIP   10.96.253.80   192.168.99.101   8080/TCP   16m

Secretを利用する

ファイルからSecretを登録する

改行が入らないように注意

echo -n "root" > ./username
echo -n "rootpassword" > ./password
kubectl create secret generic sample-db-auth --from-file=./username --from-file=./password

yamlファイルから作成する

値はbase64でエンコード済みのものでないといけない

apiVersion: v1
kind: Secret
metadata:
  name: sample-yaml-auth
type: Opaque
data:
  username: cm9vdA==
  password: cm9vdHBhc3N3b3Jk
kubectl apply -f ./secret_sample.yml

kubectlから直接作成する (--from-literal)

kubectl create secret generic sample-cli-auth --from-literal=username=root --from-literal=password=rootpassword

envfile(dockerの --env-fileオプションと同じ)から作成する

username=root
password=rootpassword
kubectl create secret generic sample-env-file --from-env-file ./env_secret

登録内容を確認する

kubectl describe secrets sample-db-auth
Name:         sample-db-auth
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
password:  8 bytes
username:  9 bytes

中身まで確認する

base64でエンコードされているので、デコードする必要がある

kubectl get secret sample-db-auth -o json
{
    "apiVersion": "v1",
    "data": {
        "password": "UEBzc3cwcmQ=",
        "username": "dXNlci1uYW1l"
    },
    "kind": "Secret",
    "metadata": {
        "creationTimestamp": "2019-12-31T08:01:36Z",
        "name": "sample-db-auth",
        "namespace": "default",
        "resourceVersion": "123932",
        "selfLink": "/api/v1/namespaces/default/secrets/sample-db-auth",
        "uid": "d5e6188d-bbb3-496f-8b95-bb3dfc7fa4ec"
    },
    "type": "Opaque"
}

環境変数として利用する

apiVersion: v1
kind: Pod
metadata:
  name: sample-secret-env
spec:
  containers:
    - name: secret-container
      image: nginx:1.12
      envFrom:
      - secretRef:
          name: sample-db-auth
kubectl apply -f .\secret_env_sample.yml
pod/sample-secret-env created
kubectl exec -it sample-secret-env env
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=sample-secret-env
TERM=xterm
password=P@ssw0rd
username=user-name

ファイルとして利用する

環境変数として設定する場合との違いは以下。

Volumeマウントを利用したSecretに限り一定期間ごと(kubeletのSync Loopのタイミング)にkube-apiserverに変更を確認し、変更がある場合は入れ替えを行います。

デフォルトではSyncLoopの間隔は60秒に設定されています。この頻度を上げる場合には、kubeletのオプションとして--sync-frequencyを設定して下さい。また、環境変数を利用したSecretの場合、動的な更新はできません。

apiVersion: v1
kind: Pod
metadata:
  name: sample-secret-single-volume
spec:
  containers:
    - name: secret-container
      image: nginx:1.12
      volumeMounts:
      - name: config-volume
        mountPath: /config
  volumes:
    - name: config-volume
      secret:
        secretName: sample-db-auth
        items:
        - key: username
          path: username.txt

ConfigMapを利用する

ファイルから登録する

改行が入らないように注意

echo "config=true" > ./sample.conf
kubectl create configmap sample-config --from-file=./sample.conf

yamlファイルから作成する

値はbase64でどうのこうのする必要はない

apiVersion: v1
kind: ConfigMap
metadata:
  name: sample-configmap
data:
  thread: "16"
  connection.max: "100"
  connection.min: "10"
  sample.properties: |
    property.1=value-1
    property.2=value-2
    property.3=value-3
  nginx.conf: |
    user nginx;
    worker_processes auto;
    error_log /var/log/nginx/error.log;
    pid /run/nginx.pid;
kubectl apply -f ./sample-config-map.conf

kubectlから直接作成する (--from-literal)

kubectl create configmap web-config --from-literal=connection.max=100 --from-literal=connection.min=10

環境変数として利用する

環境変数として利用する場合、以下の制限がある。

環境変数で下記の2パターンを表現することができない読み込まれない

**.**が含まれる場合

改行が含まれる場合(**Key: |**で定義されているもの)

apiVersion: v1
kind: Pod
metadata:
  name: sample-configmap-single-env
spec:
  containers:
    - name: configmap-container
      image: nginx:1.12
      env:
        - name: CONNECTION_MAX
          valueFrom:
            configMapKeyRef:
              name: sample-configmap
              key: connection.max
kubectl apply -f configmap_single_env_sample.yml
pod/sample-configmap-single-env created

ファイルとして利用する

環境変数として設定する場合との違いはSecretの時と同じ。

apiVersion: v1
kind: Pod
metadata:
  name: sample-configmap-multi-volume
spec:
  containers:
    - name: configmap-container
      image: nginx:1.12
      volumeMounts:
      - name: config-volume
        mountPath: /config
  volumes:
    - name: config-volume
      configMap:
        name: sample-configmap

Volumeを利用する

emptyDirを使う

apiVersion: v1
kind: Pod
metadata:
  name: sample-emptydir
spec:
  containers:
  - image: nginx:1.12
    name: nginx-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}
kubectl apply -f .\emptydir-sample.yml
kubectl exec -it sample-emptydir /bin/bash
root@sample-emptydir:/# ls -l
total 72
drwxrwxrwx   2 root root 4096 Jan  1 03:41 cache

HostPathを使う

apiVersion: v1
kind: Pod
metadata:
  name: sample-hostpath
spec:
  containers:
  - image: nginx:1.12
    name: nginx-container
    volumeMounts:
    - mountPath: /srv
      name: hostpath-sample
  volumes:
  - name: hostpath-sample
    hostPath:
      path: /data
      type: DirectoryOrCreate
kubectl apply -f .\hostpath-sample.yml
kubectl exec -it sample-hostpath /bin/bash
touch /srv/test
$ minikube.exe ssh
                         _             _
            _         _ ( )           ( )
  ___ ___  (_)  ___  (_)| |/')  _   _ | |_      __
/' _ ` _ `\| |/' _ `\| || , <  ( ) ( )| '_`\  /'__`\
| ( ) ( ) || || ( ) || || |\`\ | (_) || |_) )(  ___/
(_) (_) (_)(_)(_) (_)(_)(_) (_)`\___/'(_,__/'`\____)

$ ls /data
test

Kubernetesを構成しているコンポーネント

主に以下の引用です。

コンポーネント一覧

コンポーネント 種別 説明
etcd master Kubernetesのリソースの永続化に使われる高信頼分散KVS
kube-apiserver master Kubernetesのリソースを管理するAPIサーバー
kube-scheduler master Podのノードへの割り当てを行うスケジューラー
kube-controller-manager master Replication Controllerなどの各種コントローラーを起動し管理するマネージャー
kubelet node Podを起動し管理するエージェント(Nodeのメイン処理)
kube-proxy node KubernetesのServiceが持つ仮想的なIPアドレス(cluster IP)へのアクセスをルーティングする
kubectl client KubernetesのCLIクライアント
hyperkube misc Kubernetes関連のバイナリを1つにまとめたall-in-oneバイナリ
pause misc pod内のネットワークnamespaceを保持するコンテナ
pod-master add-on High-Availability構成時にscheduler, controllerがどのMasterで動くかを調整するコンテナ
kube-dns add-on クラスタ内DNSのPod
SkyDNS add-on クラスタ内DNSのDNSサーバー
kube2sky add-on SkyDNSにKubernetesの情報を反映させるブリッジ
heapster add-on Kuernetesのパフォーマンス情報を集約する仕組み
helm(Deployment Manager) add-on Kubernetesの設定をテンプレート化しデプロイを管理しやすくする仕組み
dashboard UI 新しいKubernetesのダッシュボード
kube-ui UI 古いKubernetesのダッシュボード
kubedash UI パフォーマンス分析のダッシュボード

architecture

コンポーネント同士がどうやって連携しあっているのかの基本的な考え方

各コンポーネントの中でReconciliation Loopというのが動いている。

image.png

Kubernetesの設計原則

API Serverのみがクラスター状態を管理するデータストア(etcd)を扱う 部分的なサーバー障害がクラスター全体のダウンにつながらないよう にする 各コンポーネントへの指示が失われても、直近の指示にもとづいて処 理を継続できるようにする

各コンポーネントは関連する設定や状態をメモリに持つが、永続化は API Serverを通じてデータストア(etcd)へ行う ユーザーや他のコンポーネントがオブジェクトを変化させたことを検 知できるよう、コンポーネントはAPI Serverをウォッチする

イベントチェーンの流れ Deployment作成の例

  1. クライアントがDeployment APIを呼ぶ
  2. Deployment Controllerが検知し、ReplicaSet APIを呼ぶ
  3. ReplicaSet Controllerが検知し、Pod APIを呼ぶ
  4. Schedulerが検知し、配置先Nodeを決定し、Pod APIを呼ぶ
  5. Kubeletが検知し、Pod(Container)を作成

冗長構成を組む

基本的な考え方

またこちらを見ました。https://www.slideshare.net/ToruMakabe/kubernetes-120907020

Controller ManagerとSchedulerはActive/Standby

image.png

台数

Master、Nodeともに3台以上がおすすめ Masterサーバーにetcdを同居させる場合、おすすめは3台以上で奇数 etcdの障害時、処理継続ノードの決定基準が過半数であるため Nodeサーバーは2台以上、必要なリソース量に応じて増やす Nodeサーバーが2台構成だと障害時に使えるリソースが一気に半減す るため、3台以上とするケースが多い

少なくともリージョンをまたがるようなクラスタを組まない方がいいらしい

複数のデータセンターに分散配置する場合、制約条件は遅延 etcdの死活確認、性能の観点から2~3msに抑えるのが一般的 Azureのバックボーンでも東阪は10ms程度かかる 1つのクラスターは1つの都市圏で構成するのが現実的 広域災害対策は1つのクラスターではなく、複数のクラスターで

HPA(Horizontal Pod Autoscaler)の定義

HPAはPodごとに定義できるRequestsを割り当てられるかどうかを判断基準にしていて、実際の使用量をもとにPodの配置を決めるわけではない。

Nodeに空きがないとPodがPendingになる。これを監視しているのがCluster AutoscalerでインフラのAPIを呼んでノードを追加する。

image.png

Helmを導入する

Helmはk8sのパッケージマネージャ的なものらしい。

試してみたけど、公式通りにやってもちゃんと動かないことがあるというか…残念

Goで書かれていて、以下からダウンロードしてパスを通せば導入できる。

$ helm
The Kubernetes package manager

Common actions for Helm:

- helm search:    search for charts
- helm pull:      download a chart to your local directory to view
- helm install:   upload the chart to Kubernetes
- helm list:      list releases of charts

Environment variables:

+------------------+-----------------------------------------------------------------------------+
| Name             | Description                                                                 |
+------------------+-----------------------------------------------------------------------------+
| $XDG_CACHE_HOME  | set an alternative location for storing cached files.                       |
| $XDG_CONFIG_HOME | set an alternative location for storing Helm configuration.                 |
| $XDG_DATA_HOME   | set an alternative location for storing Helm data.                          |
| $HELM_DRIVER     | set the backend storage driver. Values are: configmap, secret, memory       |
| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.                  |
| $KUBECONFIG      | set an alternative Kubernetes configuration file (default "~/.kube/config") |
+------------------+-----------------------------------------------------------------------------+

Helm stores configuration based on the XDG base directory specification, so

- cached files are stored in $XDG_CACHE_HOME/helm
- configuration is stored in $XDG_CONFIG_HOME/helm
- data is stored in $XDG_DATA_HOME/helm

Helmでできること

  • 自分のKubernetesクラスター環境へのデプロイ(インストール)・削除(アンインストール)
  • パッケージを共有する仕組み(リポジトリ)
  • 新規のパッケージを開発する支援機能

Helmの構成要素

構成要素 説明 補足
helm(CLI) go言語で開発されたコマンドラインツール。 ローカルOS環境の$HELM_HOMEでポイントする場所に、設定ファイル群や、「ローカルリポジトリ」を作り出し、その上でtillerと通信してKubernetesクラスタとのやりとりを行います。
tiller(サーバー) Kubernetesクラスタ内にリソースとして定義され、動作するサーバーアプリ。 helmコマンドからの要求に基づき、apiserverを介してKubernetesクラスタ内のリソース作成・削除・情報取得などの操作を行います。
chart(チャート) Helmにおける管理対象としての「パッケージ」 実体は.tgz形式の圧縮ファイル。様々なファイル群で構成され、Kubernetesのリソースを生成するためのテンプレートファイルやデフォルト値の設定ファイルなどが含まれる。
release(リリース) chartを使って実際にデプロイ(インストール)されたクラスタ上のオブジェクトをまとめて表したもの。 リリースは、暗黙にせよ明示的にせよ、「リリース名」が必ず与えられ、識別されます。
chartリポジトリ チャートをhelmクライアントに提供するWebサーバー 仕様として定められたindex.yamlおよびchartファイル(.tgz)群をサービスすれば、それはchartリポジトリとなりえます。

Helmのクイックスタート

MySQLの環境を立ち上げる。

$ helm repo add stable https://kubernetes-charts.storage.googleapis.com/
$ helm repo update
$ helm install stable/mysql --generate-name
NAME: mysql-1577664762
LAST DEPLOYED: Mon Dec 30 09:12:44 2019
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
mysql-1577664762.default.svc.cluster.local

To get your root password run:

    MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default mysql-1577664762 -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)  
To connect to your database:

1. Run an Ubuntu pod that you can use as a client:

    kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il

2. Install the mysql client:

    $ apt-get update && apt-get install mysql-client -y

3. Connect using the mysql cli, then provide your password:
    $ mysql -h mysql-1577664762 -p

To connect to your database directly from outside the K8s cluster:
    MYSQL_HOST=127.0.0.1
    MYSQL_PORT=3306

    # Execute the following command to route the connection:
    kubectl port-forward svc/mysql-1577664762 3306

    mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}

MySQLの環境を調べてみる。

$ kubectl get deployments -o wide
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE     CONTAINERS              IMAGES         SELECTOR
mysql-1577664762        1/1     1            1           5m46s   mysql-1577664762        mysql:5.7.28   app=mysql-1577664762,release=mysql-1577664762 
$ kubectl describe deployments mysql-1577664762
Name:               mysql-1577664762
Namespace:          default
CreationTimestamp:  Mon, 30 Dec 2019 09:12:10 +0900
Labels:             app=mysql-1577664762
                    chart=mysql-1.6.2
                    heritage=Helm
                    release=mysql-1577664762
Annotations:        deployment.kubernetes.io/revision: 1
Selector:           app=mysql-1577664762,release=mysql-1577664762
Replicas:           1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:       Recreate
MinReadySeconds:    0
Pod Template:
  Labels:           app=mysql-1577664762
                    release=mysql-1577664762
  Service Account:  default
  Init Containers:
   remove-lost-found:
    Image:      busybox:1.29.3
    Port:       <none>
    Host Port:  <none>
    Command:
      rm
      -fr
      /var/lib/mysql/lost+found
    Requests:
      cpu:        10m
      memory:     10Mi
    Environment:  <none>
    Mounts:
      /var/lib/mysql from data (rw)
  Containers:
   mysql-1577664762:
    Image:      mysql:5.7.28
    Port:       3306/TCP
    Host Port:  0/TCP
    Requests:
      cpu:      100m
      memory:   256Mi
    Liveness:   exec [sh -c mysqladmin ping -u root -p${MYSQL_ROOT_PASSWORD}] delay=30s timeout=5s period=10s #success=1 #failure=3
    Readiness:  exec [sh -c mysqladmin ping -u root -p${MYSQL_ROOT_PASSWORD}] delay=5s timeout=1s period=10s #success=1 #failure=3
    Environment:
      MYSQL_ROOT_PASSWORD:  <set to the key 'mysql-root-password' in secret 'mysql-1577664762'>  Optional: false
      MYSQL_PASSWORD:       <set to the key 'mysql-password' in secret 'mysql-1577664762'>       Optional: true
      MYSQL_USER:
      MYSQL_DATABASE:
    Mounts:
      /var/lib/mysql from data (rw)
  Volumes:
   data:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  mysql-1577664762
    ReadOnly:   false
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   mysql-1577664762-d75956dbc (1/1 replicas created)
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  6m59s  deployment-controller  Scaled up replica set mysql-1577664762-d75956dbc to 1
$ kubectl get pods
NAME                                     READY   STATUS      RESTARTS   AGE
mysql-1577664762-d75956dbc-bz2fm         1/1     Running     0          4m28s
$ kubectl describe pods mysql-1577664762-d75956dbc-bz2fm
Name:         mysql-1577664762-d75956dbc-bz2fm
Namespace:    default
Priority:     0
Node:         minikube/192.168.99.101
Start Time:   Mon, 30 Dec 2019 09:12:11 +0900
Labels:       app=mysql-1577664762
              pod-template-hash=d75956dbc
              release=mysql-1577664762
Annotations:  <none>
Status:       Running
IP:           172.17.0.13
IPs:
  IP:           172.17.0.13
Controlled By:  ReplicaSet/mysql-1577664762-d75956dbc
Init Containers:
  remove-lost-found:
    Container ID:  docker://2ef1950b5844614c38122e32fa069e03490d8d0a077d0feb94d6b79a740f97da
    Image:         busybox:1.29.3
    Image ID:      docker-pullable://busybox@sha256:8ccbac733d19c0dd4d70b4f0c1e12245b5fa3ad24758a11035ee505c629c0796
    Port:          <none>
    Host Port:     <none>
    Command:
      rm
      -fr
      /var/lib/mysql/lost+found
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Mon, 30 Dec 2019 09:12:18 +0900
      Finished:     Mon, 30 Dec 2019 09:12:18 +0900
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:        10m
      memory:     10Mi
    Environment:  <none>
    Mounts:
      /var/lib/mysql from data (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-jn5v7 (ro)
Containers:
  mysql-1577664762:
    Container ID:   docker://41735ace19ebb2f949d70d40b8b5d7f4a397369a9116230ee53dbe14f51de990
    Image:          mysql:5.7.28
    Image ID:       docker-pullable://mysql@sha256:b38555e593300df225daea22aeb104eed79fc80d2f064fde1e16e1804d00d0fc
    Port:           3306/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Mon, 30 Dec 2019 09:12:57 +0900
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:      100m
      memory:   256Mi
    Liveness:   exec [sh -c mysqladmin ping -u root -p${MYSQL_ROOT_PASSWORD}] delay=30s timeout=5s period=10s #success=1 #failure=3
    Readiness:  exec [sh -c mysqladmin ping -u root -p${MYSQL_ROOT_PASSWORD}] delay=5s timeout=1s period=10s #success=1 #failure=3
    Environment:
      MYSQL_ROOT_PASSWORD:  <set to the key 'mysql-root-password' in secret 'mysql-1577664762'>  Optional: false
      MYSQL_PASSWORD:       <set to the key 'mysql-password' in secret 'mysql-1577664762'>       Optional: true
      MYSQL_USER:
      MYSQL_DATABASE:
    Mounts:
      /var/lib/mysql from data (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-jn5v7 (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  data:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  mysql-1577664762
    ReadOnly:   false
  default-token-jn5v7:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-jn5v7
    Optional:    false
QoS Class:       Burstable
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type     Reason            Age        From               Message
  ----     ------            ----       ----               -------
  Warning  FailedScheduling  <unknown>  default-scheduler  error while running "VolumeBinding" filter plugin for pod "mysql-1577664762-d75956dbc-bz2fm": pod has unbound immediate PersistentVolumeClaims
  Normal   Scheduled         <unknown>  default-scheduler  Successfully assigned default/mysql-1577664762-d75956dbc-bz2fm to minikube
  Normal   Pulling           11m        kubelet, minikube  Pulling image "busybox:1.29.3"
  Normal   Pulled            10m        kubelet, minikube  Successfully pulled image "busybox:1.29.3"
  Normal   Created           10m        kubelet, minikube  Created container remove-lost-found
  Normal   Started           10m        kubelet, minikube  Started container remove-lost-found
  Normal   Pulling           10m        kubelet, minikube  Pulling image "mysql:5.7.28"
  Normal   Pulled            10m        kubelet, minikube  Successfully pulled image "mysql:5.7.28"
  Normal   Created           10m        kubelet, minikube  Created container mysql-1577664762
  Normal   Started           10m        kubelet, minikube  Started container mysql-1577664762
  Warning  Unhealthy         10m        kubelet, minikube  Readiness probe failed: mysqladmin: [Warning] Using a password on the command line interface can be insecure.
mysqladmin: connect to server at 'localhost' failed
error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)'
Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!

MySQLのrootパスワードを取得する。

$ kubectl get secret --namespace default mysql-1577664762 -o jsonpath="{.data.mysql-root-password}"

これであっているはずだけど、ここで取得できるパスワードを使っても繋がらない…

5
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?