Kubernetesお試し on GCP

概要

Web+DB Press vol.99の特集、
『実践Kubernetes Google Container Engineではじめる新時代のコンテナ管理!』
を試した際のメモです。


gcloud toolのインストール

コンポーネントのインストール

https://cloud.google.com/sdk/docs/quickstart-mac-os-x

google-cloud-sdk-158.0.0-darwin-x86_64.tar.gzをDLし、
/Users/kowloon/google-cloud-sdk/
に解凍後、
install.sh
を実行

MacBook-Pro-4:google-cloud-sdk kowloon$ ./install.sh
Welcome to the Google Cloud SDK!

Your current Cloud SDK version is: 158.0.0
The latest available version is: 163.0.0
<snip>
Modify profile to update your $PATH and enable shell command
completion?

Do you want to continue (Y/n)?  Y

The Google Cloud SDK installer will now prompt you to update an rc
file to bring the Google Cloud CLIs into your environment.

Enter a path to an rc file to update, or leave blank to use
[/Users/kowloon/.bash_profile]:
Backing up [/Users/kowloon/.bash_profile] to [/Users/kowloon/.bash_profile.backup].
[/Users/kowloon/.bash_profile] has been updated.

==> Start a new shell for the changes to take effect.


For more information on how to get started, please visit:
  https://cloud.google.com/sdk/docs/quickstarts

bash_profileの末尾に以下が追加されている。

# The next line updates PATH for the Google Cloud SDK.
if [ -f '/Users/kowloon/google-cloud-sdk/path.bash.inc' ]; then source '/Users/kowloon/google-cloud-sdk/path.bash.inc'; fi

# The next line enables shell command completion for gcloud.
if [ -f '/Users/kowloon/google-cloud-sdk/completion.bash.inc' ]; then source '/Users/kowloon/google-cloud-sdk/completion.bash.inc'; fi

環境変数を再読み込みして、gcloudコマンドへのパスを通す

source ~/.bash_profile

コンポーネントのアップデート

$ cd ~/
$ gcloud components update

Your current Cloud SDK version is: 158.0.0
You will be upgraded to version: 163.0.0
<snip>
Update done!

To revert your SDK to the previously installed version, you may run:
  $ gcloud components update --version 158.0.0

kubectlのインストール

初期コンポーネントにはkubectlが入っていないため、k8sクラスタ作成時にエラーが出た

$ gcloud container clusters create wdpress \
> --cluster-version=1.6.1 \
> --machine-type=g1-small
WARNING: Accessing a Container Engine cluster requires the kubernetes commandline
client [kubectl]. To install, run
  $ gcloud components install kubectl

なので、kubectlをインストールする

$ gcloud components install kubectl

┌─────────────────────────────────────────────────┐
│       These components will be installed.       │
├────────────────────────────┬─────────┬──────────┤
│            Name            │ Version │   Size   │
├────────────────────────────┼─────────┼──────────┤
│ kubectl                    │         │          │
│ kubectl (Mac OS X, x86_64) │   1.7.0 │ 16.0 MiB │
└────────────────────────────┴─────────┴──────────┘
<snip>
Update done!

$ kubectl version --short
Client Version: v1.7.0
Server Version: v1.6.4

gcloudの初期設定

GCP側でのAPI有効化

プロジェクトに対して、利用するAPIを明示的に有効化しておかないとエラーになる

API [compute-component.googleapis.com] not enabled on project
[1084890XXXXX]. Would you like to enable and retry?  (Y/n)?  Y

GCPコンソールで対象のプロジェクトを選択した状態で、
以下のAPIを有効化する

  • Google Compute Engine API
  • Google Container Engine API
  • Google Cloud Container Builder API

initの実行

初期化スクリプトを通して、対象のプロジェクトID、リージョンを指定する

$ gcloud init

Welcome! This command will take you through the configuration of gcloud.

Your current configuration has been set to: [default]

<snip>

You must log in to continue. Would you like to log in (Y/n)?  Y

Your browser has been opened to visit:

(ブラウザが起動し、認証を求められるので許可)

You are logged in as: [XXXXXXX@gmail.com].

Pick cloud project to use:
 [1] focus-dragon-xxxxx
 [2] wdpress-17XXXX
 [3] Create a new project
Please enter numeric choice or text value (must exactly match list
item):  2

Your current project has been set to: [wdpress-17XXXX].

Do you want to configure Google Compute Engine
(https://cloud.google.com/compute) settings (Y/n)?  Y

Which Google Compute Engine zone would you like to use as project
default?
If you do not specify a zone via a command line flag while working
with Compute Engine resources, the default is assumed.
[1] asia-east1-c
[2] asia-east1-b
[3] asia-east1-a
[4] asia-northeast1-a
[5] asia-northeast1-c
[6] asia-northeast1-b
[7] asia-southeast1-b
[8] asia-southeast1-a
<snip>
Please enter numeric choice or text value (must exactly match list
item):  4

Your project default Compute Engine zone has been set to [asia-northeast1-a].
You can change it by running [gcloud config set compute/zone NAME].

Your project default Compute Engine region has been set to [asia-northeast1].
You can change it by running [gcloud config set compute/region NAME].

Created a default .boto configuration file at [/Users/kowloon/.boto]. See this file and
[https://cloud.google.com/storage/docs/gsutil/commands/config] for more
information about configuring Google Cloud Storage.
Your Google Cloud SDK is configured and ready to use!

設定が入ったことを確認

$ gcloud config list
[compute]
region = asia-northeast1
zone = asia-northeast1-a
[core]
account = XXXXXXX@gmail.com
disable_usage_reporting = False
project = wdpress-17XXXX

Kubernetesクラスタの作成

参考記事に従いver1.6.1を指定したらエラー

$ gcloud container clusters create wdpress \
> --cluster-version=1.6.1 \
> --machine-type=g1-small
ERROR: (gcloud.container.clusters.create) ResponseError: code=400, message=Version is invalid.

利用可能なバージョンを確認

$ gcloud container get-server-config
Fetching server config for asia-northeast1-a
defaultClusterVersion: 1.6.4
defaultImageType: COS
validImageTypes:
- CONTAINER_VM
- COS
validMasterVersions:
- 1.7.1
- 1.6.7
- 1.6.4
validNodeVersions:
- 1.7.1
- 1.7.0
- 1.6.7
- 1.6.6
- 1.6.4
- 1.5.7
- 1.4.9

1.6.1は利用可能ではなくなっていたので、
1.6.4に指定し直して実行

$ gcloud container clusters create wdpress \
> --cluster-version=1.6.4 \
> --machine-type=g1-small

Creating cluster wdpress..

VMが3台デプロイされ、Kubernetes Clusterが作成された。

VM インスタンス

k8s クラスタ

認証情報のダウンロード

$ gcloud container clusters get-credentials wdpress
Fetching cluster endpoint and auth data.
kubeconfig entry generated for wdpress.

kubectlでクラスタの情報を取得

$ kubectl cluster-info
Kubernetes master is running at https://35.190.xxx.xxx
GLBCDefaultBackend is running at https://35.190.xxx.xxx/api/v1/namespaces/kube-system/services/default-http-backend/proxy
Heapster is running at https://35.190.xxx.xxx/api/v1/namespaces/kube-system/services/heapster/proxy
KubeDNS is running at https://35.190.xxx.xxx/api/v1/namespaces/kube-system/services/kube-dns/proxy
kubernetes-dashboard is running at https://35.190.xxx.xxx/api/v1/namespaces/kube-system/services/kubernetes-dashboard/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

コンテナのデプロイ

サポートサイトからソースをDL

デスクトップに”Kubernetes”フォルダとして展開

$ cd ~/Desktop/Kubernetes/
$ ls
Dockerfile      cloudbuild.yaml     deployment.yaml     main.go         pod.yaml        service.yaml
README.md       configmap.yaml      ingress.yaml        pod-configmap.yaml  replicaset.yaml

ビルドリクエストファイルである、cloudbuild.yamlを編集

編集前

$ cat cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/go:alpine'
 env: ['PROJECT_ROOT=foo']
 args: ['build', '-o', 'hello-world', 'main.go']
- name: 'gcr.io/cloud-builders/docker'
 env: ['PROJECT_ROOT=foo']
 args: ['build', '--tag=asia.gcr.io/$PROJECT_ID/wdpress/hello-world:latest', '.']
images: ['asia.gcr.io/$PROJECT_ID/wdpress/hello-world:latest']

編集後
PROJECT_ROOTに”wdpress”を指定

steps:
- name: 'gcr.io/cloud-builders/go:alpine'
  env: ['PROJECT_ROOT=wdpress']
  args: ['build', '-o', 'hello-world', 'main.go']
- name: 'gcr.io/cloud-builders/docker'
  env: ['PROJECT_ROOT=wdpress']
  args: ['build', '--tag=asia.gcr.io/$PROJECT_ID/wdpress/hello-world:latest', '.']
images: ['asia.gcr.io/$PROJECT_ID/wdpress/hello-world:latest']

ビルドリクエストファイルとは:
https://cloud.google.com/container-builder/docs/quickstart-gcloud?hl=ja

ビルド リクエストとは、Container Builder がコンテナ イメージを作成するのに必要な指示を含んだ YAML ドキュメントのことです。ビルド リクエストは Container Builder API の Build リソースを使用してモデル化されています。

[トラブル] コンテナイメージのビルドエラー

最初、
gcloud container builds submit --config=cloudbuild.yaml
を実行するとエラーになった

$ gcloud container builds submit --config=cloudbuild.yaml
ERROR: (gcloud.container.builds.submit) Invalid value for [--no-source]: To omit source, use the --no-source flag.
$

ソースを無視したければ、–no-source フラグをつけて再実行せよ、と言っている。
今からビルドするのにソースを無視して良いのか?という気もするが、試しにやってみる

→今度はmain.goが無いとエラー


$ gcloud container builds submit --config=cloudbuild.yaml --no-source Created <snip> ------------------------------------------------------------- REMOTE BUILD OUTPUT -------------------------------------------------------------- starting build "45308308-3023-41cc-b1a2-XXXXXXXXXXXX" FETCHSOURCE BUILD Step #0: Already have image (with digest): gcr.io/cloud-builders/go:alpine Starting Step #0 Step #0: Creating shadow workspace and symlinking source into "./gopath/src/foo". Step #0: Documentation at https://github.com/GoogleCloudPlatform/cloud-builders/blob/master/go/README.md Step #0: Running: go build -o hello-world main.go Step #0: stat main.go: no such file or directory Finished Step #0 ERROR

切り分け1

推定:
gcloudがmain.goを見つけられていない。

現状:
カレントディレクトリにmain.goは配置されている

$ pwd
/Users/kowloon/Desktop/Kubernetes
$ ls
Dockerfile      cloudbuild.yaml     deployment.yaml     main.go         pod.yaml        service.yaml
README.md       configmap.yaml      ingress.yaml        pod-configmap.yaml  replicaset.yaml

GCPのヘルプを改めて読む。
カスタム ビルドステップ
コマンド例から、末尾にパス指定の引数があることに気付く。

$ gcloud container builds submit –config=cloudbuild.yaml ./src

main.goをカレント指定して再実行
→エラー

$ gcloud container builds submit --config=cloudbuild.yaml ./main.go
ERROR: (gcloud.container.builds.submit) Local file [{src}] is none of .zip, .tgz, .gz

原因

テキストをもう一度よく見ると、最後にドットでカレント指定されていた。
これが抜けていたのが原因。
気づくのに2日かかった。

誤:
gcloud container builds submit --config=cloudbuild.yaml

正:
gcloud container builds submit --config=cloudbuild.yaml .

今度は通った。

$ gcloud container builds submit --config=cloudbuild.yaml .
Creating temporary tarball archive of 14 file(s) totalling 6.2 MiB before compression.
Uploading tarball of [.] to [gs://wdpress-XXXXX_cloudbuild/source/1501223067.18-8ca30f93049142e3a4d5ed94a1c22470.tgz]
Created [https://cloudbuild.googleapis.com/v1/projects/wdpress-xxxxxx/builds/2833b147-8cf5-4634-9cec-xxxxxxxxxxxx].
Logs are available at [https://console.cloud.google.com/gcr/builds/2833b147-8cf5-4634-9cec-xxxxxxxxxxxx?project=wdpress-xxxxxx].
-------------------------------------------------------- REMOTE BUILD OUTPUT --------------------------------------------------------
starting build "2833b147-8cf5-4634-9cec-xxxxxxxxxxxx"

FETCHSOURCE
Fetching storage object: gs://wdpress-xxxxxx_cloudbuild/source/1501223067.18-8ca30f93049142e3a4d5ed94a1c22470.tgz#1501223071985640
Copying gs://wdpress-xxxxxx_cloudbuild/source/1501223067.18-8ca30f93049142e3a4d5ed94a1c22470.tgz#1501223071985640...
/ [1 files][  1.9 MiB/  1.9 MiB]
Operation completed over 1 objects/1.9 MiB.
BUILD
Step #0: Already have image (with digest): gcr.io/cloud-builders/go:alpine
Starting Step #0
Step #0: Creating shadow workspace and symlinking source into "./gopath/src/wdpress".
Step #0: Documentation at https://github.com/GoogleCloudPlatform/cloud-builders/blob/master/go/README.md
Step #0: Running: go build -o hello-world main.go
Finished Step #0
Step #1: Already have image (with digest): gcr.io/cloud-builders/docker
Starting Step #1
Step #1: Sending build context to Docker daemon  5.907MB
Step #1: Step 1/4 : FROM alpine:3.6
Step #1: 3.6: Pulling from library/alpine
Step #1: Digest: sha256:1072e499f3f655a032e88542330cf75b02e7bdf673278fxxxxxxxxxxx
Step #1: Status: Downloaded newer image for alpine:3.6
Step #1:  ---> 7328f6f8b418
Step #1: Step 2/4 : EXPOSE 8080
Step #1:  ---> Running in 6b3de25fcf30
Step #1:  ---> 176f8938c96a
Step #1: Removing intermediate container 6b3de25fcf30
Step #1: Step 3/4 : ADD hello-world /hello-world
Step #1:  ---> de36051c069d
Step #1: Removing intermediate container 0b869b69922f
Step #1: Step 4/4 : CMD /hello-world
Step #1:  ---> Running in ef43f27b8d04
Step #1:  ---> bf58524d8862
Step #1: Removing intermediate container ef43f27b8d04
Step #1: Successfully built bf58524d8862
Step #1: Successfully tagged asia.gcr.io/wdpress-xxxxxx/wdpress/hello-world:latest
Finished Step #1
PUSH
Pushing asia.gcr.io/wdpress-xxxxxx/wdpress/hello-world:latest
The push refers to a repository [asia.gcr.io/wdpress-xxxxxx/wdpress/hello-world]
6a3806bdcffd: Preparing
5bef08742407: Preparing
5bef08742407: Layer already exists
6a3806bdcffd: Pushed
latest: digest: sha256:2af131b46f9a8d6b2c0acedd6966a6bb873c43d24xxxxxxxxxxx size: 739
DONE
-------------------------------------------------------------------------------------------------------------------------------------

ID                                    CREATE_TIME                DURATION  SOURCE                                                                                    IMAGES                                                    STATUS
2833b147-8cf5-4634-9cec-xxxxxxxxxxxx  2017-07-28T06:24:33+00:00  26S       gs://wdpress-xxxxxx_cloudbuild/source/1501223067.18-8ca30f93049142e3a4d5ed94a1c22470.tgz  asia.gcr.io/wdpress-xxxxxx/wdpress/hello-world (+1 more)  SUCCESS

GCRにコンテナイメージが登録された。
GCR コンテナイメージ

cloudbuild.yaml内で指定した$PROJECT_IDに正しくプロジェクト名が反映されている。

コンテナのデプロイ

$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-world
spec:
  containers:
    - image: asia.gcr.io/$PROJECT_ID/wdpress/hello-world
      imagePullPolicy: Always
      name: hello-world

$ kubectl create -f pod.yaml
pod "hello-world" created

[トラブル] Podのデプロイが失敗:InvalidImageName

Podの状態を取得すると、ステータスが”InvalidImageName”

$ kubectl get pods
NAME          READY     STATUS             RESTARTS   AGE
hello-world   0/1       InvalidImageName   0          18s

GCR POD(失敗状態)

GCR POD(詳細)

couldn't parse image reference "asia.gcr.io/$PROJECT_ID/wdpress/hello-worldと言っているので、イメージを参照できていないことが直接原因。

エラーログに”asia.gcr.io/$PROJECT_ID/wdpress/hello-world”とあることから,変数$PROJECT_IDの中身が正しく展開されて渡されていないように見える。

原因の追求はひとまず置いておいて、
pod.yamlを編集し、$PROJECT_IDの部分をプロジェクト名の直書きで対処。

$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-world
spec:
  containers:
    - image: asia.gcr.io/wdpress-17xxxx/wdpress/hello-world
      imagePullPolicy: Always
      name: hello-world

既存のPodを削除

$ kubectl delete pod hello-world
pod "hello-world" deleted
$ kubectl get pods
No resources found.

再チャレンジ →今度は作成された

$ kubectl create -f pod.yaml
pod "hello-world" created

$ kubectl get pods
NAME          READY     STATUS    RESTARTS   AGE
hello-world   1/1       Running   0          11s

GCR POD(成功状態)

GCR POD(詳細)

しかしながら、名前指定でPodの内訳は取れず

$ kubectl get pods -l name=hello-world
No resources found.

原因の追求はひとまず置いておいて、先に進む。

Podへのアクセス設定

port-forwardの設定を行い、ローカルホストの8080宛てをGCP上のPodにフォワード

$ kubectl port-forward hello-world 8080
Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080
Handling connection for 8080
Handling connection for 8080
Handling connection for 8080

ブラウザで”localhost:8080″にアクセス。

hello-worldの画面が表示されたので、Podは動いていることが確認できた。

Podへのアクセス確認

確認後、Ctrl+Cで停止。

設定の適用

$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-world
spec:
  containers:
    - image: asia.gcr.io/wdpress-xxxxxx/wdpress/hello-world
      imagePullPolicy: Always
      name: hello-world
$ cat pod-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    name: hello-world
spec:
  containers:
    - image: asia.gcr.io/$PROJECT_ID/wdpress/hello-world
      imagePullPolicy: Always
      name: hello-world
      env:
        - name: WDPRESS_MESSAGE
          valueFrom:
            configMapKeyRef:
              name: hello-world-config
              key: hello-world.message

実行すると、metadata.nameを指定するようエラー

$ kubectl create -f pod-configmap.yaml
The Pod "" is invalid: metadata.name: Required value: name or generateName is required

既に同じエラーでつまずいた方がいらっしゃったので、参考にする。

WEB+DB PRESS Vol.99の「実践Kubernetes」の第4章でつまづいたメモ

上記を受けて、修正

The Pod "" is invalid: metadata.name: Required value: name or generateName is required


apiVersion: v1
kind: Pod
metadata:
#  labels:                   ←コメントアウト
#    name: hello-world       ←コメントアウト
  name: hello-world          ←nameを追加
spec:
  containers:
    - image: asia.gcr.io/wdpress-17xxxx/wdpress/hello-world  ←PROJECTIDを直指定
      imagePullPolicy: Always
      name: hello-world
      env:
        - name: WDPRESS_MESSAGE
          valueFrom:
            configMapKeyRef:
              name: hello-world-config
              key:  hello-world.message

まだエラー

$ kubectl create -f pod-configmap.yaml
Error from server (AlreadyExists): error when creating "pod-configmap.yaml": pods "hello-world" already exists

hello-worldは既存であるよ、と言っている。

既存のpodを削除してから、改めてpod-configmap.yamlを指定してデプロイし直す

$ kubectl delete pod hello-world  # Podの削除
pod "hello-world" deleted

$ kubectl get pods
No resources found.    # Podが削除された

$ kubectl create -f pod-configmap.yaml
pod "hello-world" created

$ kubectl get pods
NAME          READY     STATUS    RESTARTS   AGE
hello-world   1/1       Running   0          25s  # Podが作成された

port-forwardの設定を行い、ローカルホストの8080宛てをGCP上のPodにフォワード

$ kubectl port-forward hello-world 8080

ブラウザで”localhost:8080″にアクセス。

hello-worldの画面が表示され、かつ、configmap.yamlの内容を
反映した文言に更新されていることを確認できた。

Podへのアクセス確認

確認後、Ctrl+Cで停止。
Podを削除。

$ kubectl delete pod hello-world
pod "hello-world" deleted
$ kubectl get pods
No resources found.

Replicasetを使ったデプロイ

“replicas”でPodが3つ指定されている。

$ cat replicaset.yaml
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  name: hello-world
spec:
  replicas: 3
  template:
    metadata:
      labels:
        name: hello-world
    spec:
      containers:
        - image: asia.gcr.io/wdpress-xxxxxx/wdpress/hello-world
          imagePullPolicy: Always
          name: hello-world

Podをデプロイ

$ kubectl create -f replicaset.yaml
replicaset "hello-world" created

$ kubectl get replicaset
NAME          DESIRED   CURRENT   READY     AGE
hello-world   3         3         3         3m

$ kubectl get pods
NAME                READY     STATUS    RESTARTS   AGE
hello-world-9jz80   1/1       Running   0          3m
hello-world-dnh0q   1/1       Running   0          3m
hello-world-l0lb6   1/1       Running   0          3m

Podが3つ作成された。

3つのうち、1つのPodを削除。

$ kubectl delete pod hello-world-l0lb6
pod "hello-world-l0lb6" deleted
$ kubectl get pods
NAME                READY     STATUS        RESTARTS   AGE
hello-world-56dkc   1/1       Running       0          6s  ←Podが1つ補充
hello-world-9jz80   1/1       Running       0          5m
hello-world-dnh0q   1/1       Running       0          5m  
hello-world-l0lb6   0/1       Terminating   0          5m  ←削除中のPod

$ kubectl get pods
NAME                READY     STATUS    RESTARTS   AGE
hello-world-56dkc   1/1       Running   0          15s
hello-world-9jz80   1/1       Running   0          5m
hello-world-dnh0q   1/1       Running   0          5m
↑
Pod3個に戻った

レプリカ数を3→4に変更。

$ vim replicaset.yaml

apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  name: hello-world
spec:
  replicas: 4

kubectl replaceで更新

$ kubectl replace -f replicaset.yaml
replicaset "hello-world" replaced

$ kubectl get pods
NAME                READY     STATUS    RESTARTS   AGE
hello-world-56dkc   1/1       Running   0          4m
hello-world-9jz80   1/1       Running   0          9m
hello-world-b3qx9   1/1       Running   0          7s
hello-world-dnh0q   1/1       Running   0          9m

Pod4個の状態に更新された。

Replicasetを削除

$ kubectl delete replicaset hello-world
replicaset "hello-world" deleted
$ kubectl get replicaset
No resources found.
$ kubectl get pods
No resources found.

Deploymentを使ったデプロイ

Deploymentは変更履歴を記録してくれる。

$ cat deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment          ←種類はDeployment
metadata:
  name: hello-world
spec:
  replicas: 3
  template:
    metadata:
      labels:
        name: hello-world
    spec:
      containers:
        - image: asia.gcr.io/wdpress-XXXXX/wdpress/hello-world
          imagePullPolicy: Always
          name: hello-world

Podをデプロイ

$ kubectl create -f deployment.yaml
deployment "hello-world" created

$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
hello-world-3905944243-t22ld   1/1       Running   0          1m
hello-world-3905944243-tj9j5   1/1       Running   0          1m
hello-world-3905944243-wvrtx   1/1       Running   0          1m

レプリカ数を3→4に変更

$ cat deployment.yaml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: hello-world
spec:
  replicas: 4             ←4に変更

更新後の設定を適用する。
テキストではkubectl updateと記載されているが、今後はreplaceを使うよう促される。
(コマンド自体は完了する)

$ kubectl update -f deployment.yaml
Command "update" is deprecated, use "replace" instead
deployment "hello-world" replaced

$ kubectl get replicaset
NAME                     DESIRED   CURRENT   READY     AGE
hello-world-3905944243   4         4         4         9m

$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
hello-world-3905944243-rmt8j   1/1       Running   0          1m
hello-world-3905944243-t22ld   1/1       Running   0          7m
hello-world-3905944243-tj9j5   1/1       Running   0          7m
hello-world-3905944243-wvrtx   1/1       Running   0          7m

変更が適用されて、4つのPodができた。
Podは削除せず次に進む。

Serviceの作成

Services

  • Serviceを作成
  • これまで作成したPodを集約して1つのサービスとする
  • 外部からアクセス可能とする
$ cat service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello-world
spec:
  type: LoadBalancer    ←2.
  selector:
    name: hello-world   ←1.
  ports:
    - port: 8080        ←3.
  1. selector name:でhello-worldが指定されており、
    ラベル”hello-world”に該当するPodは全てこのServiceに集約される。
  2. typeにLoadBalancerが指定され、外部IPアドレスが付与される
  3. ポート:8080で待ち受け

前提として、Podが起動していること

$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
hello-world-3905944243-rmt8j   1/1       Running   0          18m
hello-world-3905944243-t22ld   1/1       Running   0          24m
hello-world-3905944243-tj9j5   1/1       Running   0          24m
hello-world-3905944243-wvrtx   1/1       Running   0          24m

Serviceをデプロイ

$ kubectl create -f service.yaml
service "hello-world" created

$ kubectl get service -w hello-world
NAME          CLUSTER-IP     EXTERNAL-IP     PORT(S)          AGE
hello-world   10.27.252.33   35.200.xx.xx   8080:32277/TCP   1m

EXTERNAL-IPの”35.200.xx.xx:8080″にアクセス。
Hello-world画面が表示された。
外部ロードバランサがプロキシとして稼働している。

Serviceへのアクセス確認

IngressによるHTTP LoadBalancer

  • Service: Layer4のLB
  • Ingress: Layer7のLB(GCPのMaglevを使う)

以下の構成となる

Internet
↓
Ingress
↓
Service
↓
Pod群

前項で作成したServiceを削除

$ kubectl delete service hello-world
service "hello-world" deleted

yamlを編集し、ServiceからLoadBalancerの設定を削除

【修正前】

$ cat services.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello-world
spec:
  type: LoadBalancer
  selector:
    name: hello-world
  ports:
    - port: 8080

【修正後】
type: LoadBalancerを削除

$ cat services.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello-world
spec:
  selector:
    name: hello-world
  ports:
    - port: 8080

サービスを再デプロイ

$ kubectl create -f service.yaml
service "hello-world" created
$ kubectl get service
NAME          CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
hello-world   10.27.246.31   <none>        8080/TCP   10s

LoadBalancer設定を抜いたのでEXTERNAL-IPは<none>となっている。

Ingressの設定ファイル

$ cat ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress                   ← タイプはIngressを指定
metadata:
  name: hello-world
spec:
  rules:
  - http:
     paths:
      - path: /*
        backend:
          serviceName: hello-world  ← 渡し先のサービス名はhello-world
          servicePort: 8080         ← 渡し先のポート番号は8080

Ingressのデプロイ

$ kubectl create -f ingress.yaml
ingress "hello-world" created

$ kubectl get ingress
NAME          HOSTS     ADDRESS   PORTS     AGE
hello-world   *                   80        13s

(数分後)
$ kubectl get ingress
NAME          HOSTS     ADDRESS       PORTS     AGE
hello-world   *         35.201.x.x   80        5m

デプロイ直後はADDRESS欄は空欄状態だが、
自動でHTTP LoadBalancerが作成されてGlobal IPが割り当たる(数分かかる)。

[トラブル] ingressにHTTPアクセスできない

“http://IngressのIPアドレス/”にアクセス
→エラーが表示された

Serviceへのアクセス確認

$ kubectl get pods
NAME                           READY     STATUS    RESTARTS   AGE
hello-world-3905944243-rmt8j   1/1       Running   0          1h
hello-world-3905944243-t22ld   1/1       Running   0          1h
hello-world-3905944243-tj9j5   1/1       Running   0          1h
hello-world-3905944243-wvrtx   1/1       Running   0          1h

↑ Podは正常に上がっている

$ kubectl get services
NAME          CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
hello-world   10.27.246.31   <none>        8080/TCP   12m

↑ Serviceは正常に上がっている。

$ kubectl get ingress
NAME          HOSTS     ADDRESS       PORTS     AGE
hello-world   *         35.201.x.x   80        7m

↑ ingressは正常に上がっている

一度Ingress,Serviceを消して、作り直し

$ kubectl delete ingress hello-world
ingress "hello-world" deleted
$ kubectl delete service hello-world
service "hello-world" deleted

$ cat service.yaml
apiVersion: v1
kind: Service
metadata:
  name: hello-world
spec:
  selector:
    name: hello-world
  ports:
    - port: 8080


$ cat ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-world
spec:
  rules:
  - http:
     paths:
      - path: /*
        backend:
          serviceName: hello-world
          servicePort: 8080

$ kubectl create -f service.yaml
service "hello-world" created

$ kubectl get service
NAME          CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
hello-world   10.27.243.138   <none>        8080/TCP   10s

$ kubectl create -f ingress.yaml
ingress "hello-world" created

$ kubectl get ingress
NAME          HOSTS     ADDRESS         PORTS     AGE
hello-world   *         35.201.x.x   80        4m

事象変わらず(Error: Server Error画面)。

$ curl http://35.201.xx.xx/

<html><head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<title>502 Server Error</title>
</head>
<body text=#000000 bgcolor=#ffffff>
<h1>Error: Server Error</h1>
<h2>The server encountered a temporary error and could not complete your request.<p>Please try again in 30 seconds.</h2>
<h2></h2>
</body></html>

すぐに原因・切り分け方法が思いつかないため、
いったん投了。

後片付け

  • ingressの削除
$ kubectl get ingress
NAME          HOSTS     ADDRESS         PORTS     AGE
hello-world   *         35.201.xx.xx   80        5d
$ kubectl delete ingress hello-world
ingress "hello-world" deleted
$ kubectl get ingress
No resources found.
  • serviceの削除
$ kubectl get services
NAME          CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
hello-world   10.27.xx.xx   <none>        8080/TCP   5d
kubernetes    10.27.xx.xx     <none>        443/TCP    18d
$ kubectl delete service hello-world
service "hello-world" deleted
$ kubectl get services
NAME         CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.27.xx.xx   <none>        443/TCP   18d
$

[トラブル] Replicasetが削除できない

  • replicasetの削除

Replicasetを削除してもすぐ復活してしまう

$ kubectl get replicaset
NAME                     DESIRED   CURRENT   READY     AGE
hello-world-3905944243   4         4         4         16m
$
$ kubectl delete replicaset hello-world-3905944243
replicaset "hello-world-3905944243" deleted
$
$ kubectl get replicaset
NAME                     DESIRED   CURRENT   READY     AGE
hello-world-3905944243   4         4         3         18s
$
$ kubectl delete replicaset hello-world-3905944243
replicaset "hello-world-3905944243" deleted
$
$ kubectl get replicaset
NAME                     DESIRED   CURRENT   READY     AGE
hello-world-3905944243   4         4         1         18s

Replicasetが消えないので、Podの削除も行えない。

replicasetを直接削除するのでは無く、デプロイに使用した.yamlファイルを指定して削除する必要があった。

$ kubectl delete -f deployment.yaml
deployment "hello-world" deleted

$ kubectl get replicaset
No resources found.

$ kubectl get pods
No resources found.

k8sクラスタの削除

$ gcloud container clusters list
NAME     ZONE               MASTER_VERSION  MASTER_IP       MACHINE_TYPE  NODE_VERSION  NUM_NODES  STATUS
wdpress  asia-northeast1-a  1.6.7           35.190.xx.xx  g1-small      1.6.4 *       3          RUNNING

$ gcloud container clusters delete wdpress
The following clusters will be deleted.
 - [wdpress] in [asia-northeast1-a]

Do you want to continue (Y/n)?  y

Deleting cluster wdpress...done.
Deleted [https://container.googleapis.com/v1/projects/wdpress-xxxxxx/zones/asia-northeast1-a/clusters/wdpress].

$ gcloud container clusters list
$

以上。