CentOS5,6系とUbuntu22.04間のssh接続について

概要

古いサーバ(CentOS5,6系)では近年のサーバ(特にUbuntu22.04以降)とのSSH接続に関して、多くの制約がある。

本記事ではそれについて解説していく

古いサーバ⇒Ubuntu22.04への接続

結論から言って、この接続を行うことは不可。

OpenSSH7.7以上にアップデートする必要があるが、基本的にCentOS5,6系でサポートされているOpenSSHは4.3までである

参考
https://www.faq.idcf.jp/app/answers/detail/a_id/1332/~/作成した-rocky-linux-9、-ubuntu-22.04-の仮想マシンにsshログインできません。

Ubuntu22.04⇒古いサーバへの接続

Ubuntu22.04側のサーバで、[<ユーザのホームディレクトリ>/.ssh]配下に、configファイルを作成し、下記を記載する

vi ~/.ssh/config

Host <IP address>
        KexAlgorithms +diffie-hellman-group1-sha1
        Ciphers aes128-cbc
        HostkeyAlgorithms +ssh-rsa
        PubkeyAcceptedAlgorithms +ssh-rsa

以上

Passwordログイン無効化で躓いた話

概要

/etc/ssh/sshd_configを修正してもパスワード変更ができてしまった事象があり、そのときの対処法について記載

作業内容

始め下記のような変更を行っていましたがなぜがパスワードログインができてしまっていました。

/etc/ssh/sshd_config

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
PasswordAuthentication no

調べていたら、/etc/ssh/sshd_config.d/50-cloud-init.confも変更しないといけないらしいです。

/etc/ssh/sshd_config.d/50-cloud-init.conf

#PasswordAuthentication yes
PasswordAuthentication no

参考文献

PasswordAuthenticationで躓いたお話

以上

WSL2でapt updateができない件

WSL2でapt updateができない件

概要

社内のPCにwsl2をインストールした場合、ネットワークにはつながるが、symantec protectionのせいでaptコマンドが実行できない

回答としては、aptコマンドを実行するときだけsymantecを切ればよいのだが、そうもいかないので、暫定的なワークアラウンドを検討する

対応方法

以下のページを参考にしている
WSL環境から(Symantec Endpoint Protectionが邪魔して) apt update できない場合の対処法

ホスト端末自体からは通信可能なため、ホストの Windowsフォワード Proxy を立てて、この Proxy を経由すれば対処可能。

windowsApacheの導入

Apache Loungeからhttpd-2.4.54-win64-VS16.zipをダウンロードする

https://www.apachelounge.com/download/

適当なディレクトリに解凍し、powershellで以下を実行する

> bin\httpd -v
Server version: Apache/2.4.54 (Win64)
Apache Lounge VS16 Server built:   Jun 22 2022 09:58:15

conf\httpd.confを編集

SRVROOTに解凍したディレクトリを指定し、Listenに適当なポートを指定

Define SRVROOT "c:/<path>/apache24"
ServerRoot "${SRVROOT}"
    
Listen 8080

Proxyモジュール読み込み設定を追記(末尾に追記でよい)

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
    
<IfModule proxy_module>
  ProxyRequests On
  ProxyVia On
  AllowCONNECT 443
  <Proxy *>
    Require all granted
  </Proxy>
</IfModule>

変更したら、powershellで以下を実行

> bin\httpd -t

問題なければ、Apacheを起動する
停止するときはCtrl+C

> bin\httpd

WSL2側の設定

httpプロキシの環境変数を指定する

# export http_proxy=http://$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):8080
# export https_proxy=${http_proxy}

これによりaptコマンドを実行することができるようになる

# apt update
# apt upgrade
# apt install net-tools (など)

bashrcへの記載

WSL2を起動するたびにexportするのは骨が折れるのでbashrcに環境変数を記載する
基本的なLinuxでの環境変数の記載は、/etc/environmentに定義することが多い。
しかし、Win10 20H2 時点の WSL ではこのファイルに定義した内容が bash から見えない模様(Win11 でも同様)  

そのため、.bashrcなどに記載するが、今回は/etc/bash.bashrcに記載することにする

もちろん、この環境変数を使うのはaptコマンドを実行するときのみなので、bashrcに記載せずに都度指定し、PowerShellからApacheを立ち上げるという手順を手動で実行するのでも可

一番下に下記を追記する形でよい

if [ -z "$http_proxy" ]; then
    /mnt/c/<windows側にあるApacheのPath>/bin/httpd.exe &
    export http_proxy=http://$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):8080
    export https_proxy=${http_proxy}
    export HTTP_PROXY=${http_proxy}
    export HTTPS_PROXY=${https_proxy}
fi

上記ではWSL2を起動した際にwindows側のApacheも起動するようになっているためaptを実行したいときに都度PowerShellからApacheを立ち上げる必要はない

Kubernetes ハンズオン

この記事は前回の記事と紐づいています。適宜参照してください。

Kubernetes基礎とアーキテクチャ

kubectlコマンド

まずはPodを作成しましょう。

# basic-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: web
spec:
  containers:
    - name: nginx
      image: nginx:stable-alpine
---
apiVersion: v1
kind: Pod
metadata:
  name: nginx-no-label
spec:
  containers:
    - name: nginx
      image: nginx:stable-alpine


マニフェストファイルに記載されたリソースを作成する時は、kubectl applyコマンドを使用します。

$ kubectl apply -f basic-pod.yaml
pod/nginx created
pod/nginx-no-label created


起動中のリソースを一覧表示するときはkubectl getコマンドを使います。Podを一覧するときはkubectl get podsです。

$ kubectl get pods 
NAME             READY   STATUS    RESTARTS   AGE
nginx            1/1     Running   0          2m44s
nginx-no-label   1/1     Running   0          2m17s


より詳しい情報を見るときは-o wideを付けます。

$ kubectl get pods -o wide
NAME             READY   STATUS              RESTARTS   AGE   IP       NODE                                          NOMINATED NODE   READINESS GATES
nginx            0/1     ContainerCreating   0          3s    <none>   gke-kubernetes-1-default-pool-2c12379a-jkjk   <none>           <none>
nginx-no-label   0/1     ContainerCreating   0          3s    <none>   gke-kubernetes-1-default-pool-2c12379a-jkjk   <none>           <none>


Podにはラベルを付けることができます。特定のラベルがついているPodの一覧は-lオプションを使用することでラベルを指定することができます。

$ kubectl get pods -l app=web
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          22m


起動中のPodの中にあるコンテナにログインすることは非常に多いので覚えましょう。kubectl execコマンドでログインができます。

$ kubectl exec -it nginx ash
/ #
/ # cat /etc/hosts
# Kubernetes-managed hosts file.
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.16.2.2       nginx
/ #


Nginxサーバのコンテナを起動するPodに直接トラフィックを送ってみましょう。kubectl port-fowardでPodへのポートフォワードを行うことができ、kubectl port-foward <Pod名> ローカルポート:Podのポート番号のように使います。

$ kubectl port-forward nginx 8081:80
Forwarding from 127.0.0.1:8081 -> 80


起動中のPodの定義を少しだけ変更したい場合、わざわざマニフェストファイルを書き出してkubectl applyをするのは面倒です。そんな時はkubectl edit <Pod名>で定義を直接変更することができます。

$ kubectl edit pods nginx
※vimが起動して編集することができます


k8sのリソースを一覧表示するコマンドは以下です。やってみてください。

$ kubectl api-resources
(省略)


それぞれのリソースにどんな定義があるかはkubectl explainコマンドで調べられます。

$ kubectl explain Pod.spec
KIND:     Pod
VERSION:  v1

RESOURCE: spec <Object>

DESCRIPTION:
     Specification of the desired behavior of the pod. More info:
     https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status

     PodSpec is a description of a pod.

FIELDS:
...

Podをネットワークに公開するService


ClusterIP
以下のマニフェストファイルには先ほど作ったapp=webのラベルを持つnginxのPodに対応したselectorを持つClusterIP Serviceを起動する定義が書いてあります。kubectlコマンドを利用してデプロイしてください。

# nginx-clusterip.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-clusterip
spec:
  selector:
    app: web
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 80


ClusterIPはクラスタの内部に公開するServiceなので、このままローカル環境にいてはトラフィックを送れません。そのため、alpineコンテナを起動するPodをデバック用のPodとして用意します。このようなケースでわざわざマニフェストファイルを作るのは面倒なので、kubectl runコマンドで直接Podを作成しましょう。

$ kubectl run -it --generator=run-pod/v1 alpine --image=alpine ash
If you don't see a command prompt, try pressing enter.
/ #
/ #


プロンプトが表示されたら、apkパッケージのアップデート、curlコマンドのインストールを行いましょう。インストールが終わったら、nginx-clusteripに対してリクエストを送ってみましょう。

/ # apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.11/community/x86_64/APKINDEX.tar.gz
v3.11.5-40-ge30587e6d0 [http://dl-cdn.alpinelinux.org/alpine/v3.11/main]
v3.11.5-39-g0b5b5381b3 [http://dl-cdn.alpinelinux.org/alpine/v3.11/community]
OK: 11268 distinct packages available

/ # apk add curl
(1/4) Installing ca-certificates (20191127-r1)
(2/4) Installing nghttp2-libs (1.40.0-r0)
(3/4) Installing libcurl (7.67.0-r0)
(4/4) Installing curl (7.67.0-r0)
Executing busybox-1.31.1-r9.trigger
Executing ca-certificates-20191127-r1.trigger
OK: 7 MiB in 18 packages

/ # curl -i nginx-clusterip
HTTP/1.1 200 OK
Server: nginx/1.16.1
...
<h1>Welcome to nginx!</h1>
...



NodePort
まずはtype: NodePortのServiceを以下のマニュフェストファイルから作成しましょう。

# nginx-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
spec:
  selector:
    app: web
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30001


NodePortにトラフィックを送信するためにはもちろんNodeのIPアドレスが必要です。k8sクラスタを構成するNodeのIPアドレスkubectl get nodesで調べられます。

$ kubectl get nodes -o wide
NAME                                          STATUS   ROLES    AGE    VERSION           INTERNAL-IP   EXTERNAL-IP      OS-IMAGE                             KERNEL-VERSION   CONTAINER-RUNTIME
gke-kubernetes-1-default-pool-0c238d8b-p9z9   Ready    <none>   28h    v1.14.10-gke.27   10.146.0.52   34.85.11.21      Container-Optimized OS from Google   4.14.138+        docker://18.9.7
gke-kubernetes-1-default-pool-2c12379a-jkjk   Ready    <none>   73m    v1.14.10-gke.27   10.146.0.54   35.221.123.134   Container-Optimized OS from Google   4.14.138+        docker://18.9.7
gke-kubernetes-1-default-pool-c724d8c0-l09b   Ready    <none>   7d8h   v1.14.10-gke.27   10.146.0.53   104.198.92.230   Container-Optimized OS from Google   4.14.138+        docker://18.9.7


この出力の例では、34.85.11.2135.221.123.134または104.198.92.230です。早速、34.85.11.21の30001番にトラフィックを送ってみたいところですが、GCPではファイアウォールルールが許可されていないため、トラフィックを送信することができません。まずはこのGKEクラスタが作成されているdefaultネットワークに対して、NodePortの範囲である30000~32767番のポートへのTCPを許可するルールを作ります。

$ gcloud compute firewall-rules create allow-nodeport \
    --allow=tcp:30000-32767    \
    --source-ranges=0.0.0.0/0  \
    --network=default
Creating firewall...⠧Created [https://www.googleapis.com/compute/v1/projects/ca-container-book/global/firewalls/allow-nodeport].
Creating firewall...done.
NAME            NETWORK  DIRECTION  PRIORITY  ALLOW            DENY  DISABLED
allow-nodeport  default  INGRESS    1000      tcp:30000-32767        False


こうすることでNodePortへのトラフィックが許可されます。

$ curl -i 34.85.11.21:30001
HTTP/1.1 200 OK
Server: nginx/1.16.1
...



LoadBalancer
実際にコンテナを外部に公開する際には、LoadBalancerを使います。以下のマニフェストファイルをkubectlでデプロイして、LoadBalancerを作成してください。

#nginx-loadbalancer.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-lb
spec:
  selector:
    app: web
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 80



ここで、EXTERNAL-IPが”pending”となっているはずです。これは、GCPのロードバランサが外部IPアドレスを確保している最中であることを意味しています。ロードバランサが外部IPアドレスの確保に成功すると、"pending"が次のように置き換わります。

$ kubectl get svc nginx-lb -w
NAME       TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
nginx-lb   LoadBalancer   10.0.12.136   <pending>     80:30097/TCP   5s
nginx-lb   LoadBalancer   10.0.12.136   35.243.113.150   80:30097/TCP   73s


kubectl getコマンドの最後につけている-wは、一覧表示中のリソースに変更があった場合に自動的に新しい情報を出力してくれるオプションです。この場合は、curlを使って次のようにロードバランサにトラフィックを送信できます。

$ curl -i 35.243.113.150
HTTP/1.1 200 OK
Server: nginx/1.16.1

試しに上記のIPに別のWindowからアクセスしてみましょう。
"http://35.243.113.150"
どうですか?Welcome to nginxと出てきましたか?

コンテナの監視

本番環境でコンテナを起動し続けるためには、readinessProbeとlivenessProbeの設定が不可欠です。ここでは2つのProbeの動作を確認するためのコンテナを作ってみます。 まずはそのコンテナをビルドします。以下のDockerfile, main.goを使ってください。 そのあと、GCR(Google Containar Registry)にpushします。(DockerhubではなくGoogleの自分のローカルGCP上に安全に保存することができます)
YOUR_PROJECT_IDの部分は、自分のGCPプロジェクトIDに変更して実行してください。(私はここをプロジェクト名にしててpushできなくて1週間悩みました。無知)

# Dockerfile

FROM golang:latest as builder
WORKDIR /app
COPY main.go ./
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
FROM alpine:latest
RUN apk --no-cache add ca-certificates curl
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080 80
CMD ["./main"]
// main.go

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
)

var isHealth bool
var hostname = os.Getenv("HOSTNAME")

func main() {
    if os.Getenv("HEALTHY") == "FALSE" || os.Getenv("HEALTHY") == "false" {
        isHealth = false
    } else {
        isHealth = true
    }

    http.HandleFunc("/health", healthyHandler)
    http.HandleFunc("/unhealth", unhealthyHandler)
    http.HandleFunc("/ping", pingHandler)
    http.HandleFunc("/", indexHandler)
    port := os.Getenv("PORT")
    if port == "" {
        port = "80"
        log.Printf("HostName is %s", hostname)
    }
    log.Printf("Listening on port %s", port)
    log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
}

func healthyHandler(w http.ResponseWriter, r *http.Request) {
    isHealth = true
    _, err := fmt.Fprintf(w, "%s: change response code to 200", hostname)
    if err != nil {
        w.WriteHeader(http.StatusOK)
    }
}

func unhealthyHandler(w http.ResponseWriter, r *http.Request) {
    isHealth = false
    _, err := fmt.Fprintf(w, "%s: change response code to 503", hostname)
    if err != nil {
        w.WriteHeader(http.StatusOK)
    }
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
    if isHealth == true {
        _, err := fmt.Fprintf(w, "HostName is %s", hostname)
        if err != nil {
            w.WriteHeader(http.StatusOK)
        }
        return
    }

    w.WriteHeader(http.StatusServiceUnavailable)
    return
}

func pingHandler(w http.ResponseWriter, r *http.Request) {
    _, err := fmt.Fprintf(w, "%s: pong", hostname)
    if err != nil {
        w.WriteHeader(http.StatusOK)
    }
}
$ docker build -t gcr.io/YOUR_PROJECT_ID/probe .


GCRにコンテナをpushするために、dockerコマンドに対してGCPに認証させる必要があります。

$ gcloud auth configure-docker


ビルドしたイメージをpushします。

$ docker push gcr.io/YOUR_PRIOJECT_ID/probe



GCP GUIにContainar Registryにコンテナがpushされているのを確認できると思います。
コンテナイメージがpushできたら、さっそくk8sにデプロイします。以下のマニフェストファイルをkubectlコマンドでデプロイしてください。

apiVersion: v1
kind: Pod
metadata:
  name: probe
  labels:
    app: probe
spec:
  containers:
    - name: probe
      image: gcr.io/k8s-seminar-272301/probe
      readinessProbe:
        initialDelaySeconds: 10
        periodSeconds: 30
        failureThreshold: 2
        successThreshold: 1
        timeoutSeconds: 5
        httpGet:
          port: 80
          path: /
      livenessProbe:
        httpGet:
          port: 80
          path: /
---
apiVersion: v1
kind: Service
metadata:
  name: probe
spec:
  selector:
    app: probe
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30003


readinessProbeに成功している場合、kubectl get podsのREADYステータスが1/1となります。(この数字は正常に動いているコンテナ数/内部コンテナ数) このコンテナはルートパス/へのリクエストの結果が/healthへのリクエストで成功するようになり、/unhealthへのリクエストで失敗するようになります。つまり、この状態で/unhealthへリクエストを送ると、readinessProbeに失敗するようになります。

# 最初はリクエストに成功する
$ curl -i 34.84.230.195:30003
HTTP/1.1 200 OK

#/unhealthにリクエストを送る
$ curl -i 34.84.230.195:30003/unhealth
HTTP/1.1 200 OK

#リクエストに失敗するようになる
$ curl -i 34.84.230.195:30003
HTTP/1.1 503 Service Unavailable


この状態でREADYステータスを確認します。

$ kubectl get po probe -w
NAME    READY   STATUS    RESTARTS   AGE
probe   0/1     Running   3          42m
probe   1/1     Running   3          42m


readinessProbeはREADYではないPodにトラフィックを送信しないように振る舞います。
livenessProbeはREADYではないPodを再起動します。
いまはlivenessProbeがいるのですぐに再起動してしまいますが、livenessProbeをコメントアウトしてPodを作り直すと、503のあとにしばらくしてリクエストを送ると何も帰ってこないというreadinessProbeの振る舞いを確認することができます。

ConfigMap,Secret

ConfigMap
以下のマニフェストファイルはspec.envFromでConfigMapを指定し、指定しているConfigMapを作成するマニフェストファイルです。 では、実際にkubectlコマンドでデプロイしてみましょう。

# pod-with-configmap.yaml

apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-config
  labels:
    app: web
spec:
  containers:
    - name: nginx
      image: nginx:stable-alpine
      envFrom:
        - configMapRef:
            name: env-value
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: env-value
data:
  ENV: "Hello World"


作成したPodにログインして環境変数を確認してみましょう。

$ kubectl exec -it nginx-with-config ash
/ #
/ # echo $ENV
Hello World
/ #


実際に環境変数が入っていることが確認できました。

Secret
Secretはまず自分で入れたい環境変数base64エンコードします。

$ echo -n 'Hello World!' | base64    
SGVsbG8gV29ybGQh


それをSecretに入れます。

# secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: secret
data:
  key: SGVsbG8gV29ybGQh


これで上のConfigMapと同じ要領でPodをデプロイすれば環境変数keyHello World!が設定されます。

Kubernetesにおけるコンテナのスケール

すいません。この項目については私自身きちんと理解しきれていないので今回は控えさせていただきます。



今回はここまでです。
一応直近3つの記事で自分が理解したことは書けたかなと思います。
それでは

Kubernetes基礎

この記事は個人的な理解を多分に含んでいるため、間違っている情報も含まれているかもしれません。ご了承ください。また有識者の方が見ていれば修正コメントをいただけると幸いです。

コンテナとDocker

k8sを知るうえでまずDockerについて知らなければいけません。Dockerは主題ではないので軽く必要な部分だけを説明していこうと思います。

VM仮想化とコンテナ仮想化

仮想サーバ」「仮想マシン」というのは、
ハードウェアに搭載されているプロセッサやメモリの使用時間を細かく分割しそれぞれをひとまとめにし複数の独立したサーバのように機能させ作られたサーバのことです。
仮想サーバの利点はあるOS上に別のOSのVMを立てられることにあります。WindowsOS上にLinuxOSを立てるといったことが可能です。

それに対し、
1つのOS上に「コンテナ」と呼ばれる「他のユーザから隔離されたアプリケーション実行環境」を作り、あたかも独立したサーバのように使おうというのが「コンテナ仮想化」です。
仮想サーバに比べリソース消費が少なく同じHWであれば仮想化―バより多くの実行環境を用意できます。 (Dockerが注目される理由はここではなく、DevOpsという開発手法との関わりから来ています)

OS仮想化による依存性の分離

なぜコンテナが必要なのか

コンテナ、というかDockerの利点は、

  • ポータブルである
  • どんな環境でも(開発、テスト、本番環境)全く同じ環境で実行可能である
  • アプリケーションがパッケージ化されることによる開発時間の短縮
  • マイクロサービス化の促進

などがあげられます。

Docker基礎

よく使うコマンドはこの4つ程度です。

  • docker run
    • コンテナを実行する
  • docker images (image lsでも代用可)
    • コンテナイメージの一覧を見る
  • docker ps
    • 実行中のコンテナ一覧を見る
    • -aオプションで停止中のコンテナも見える
  • docker build
    • Dockerfileの定義に基づいてコンテナイメージをビルドする

Dockerの詳しい使い方?というか基礎コマンドの使い方は以前このブログでも紹介したのでそれを参照してくてみてください。
ken-memo.hatenablog.com

ここで説明していないものとしてDockerfileがあります。 dockerはコマンド1行でコンテナイメージを作成できますが、長くなる時や何度も作るときはDockerfileという定義ファイルを用意してdocker buildコマンドで実行しコンテナイメージを作成する方法があります。 Dockerfileを使うことで公開されているimageに自分独自の環境設定を加えることができます。 以下Dockerfileの例です。

FROM golang:latest as builder
WORKDIR /app
COPY go.mod main.go ./
RUN CGO_ENABLE=0 GOOS=linux go build -a -installsuffix cgo -o main .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/mail .
EXPOSE 8080:80
CMD ["./main"]

ここにDockerfileの各定義の詳細が書いてあります。
Dockerfileによるビルド

以下はFROMを二つ使ったマルチステージビルドの方法が書いてあります。
Dockerのマルチステージビルドを使う

Kubernetes基礎とアーキテクチャ

それでは本題に入っていきます。

クラスタ, Node, Pod

k8sクラスタと呼ばれる構成単位からなります。クラスタは一つ以上のマスターノードと一つ以上のワーカーノードからなります。


我々はまずマスターノードのapi-serverへアクセスし、ワーカーノードの監視や管理をマスターノードが行います。 マスターノードは大きく分けて以下の4つのコンポーネントから構成されます。

  • kube-api-server
    • すべての操作はkubectlコマンドによってapi-serverを介して行われる
  • etcd
    • k8sのデータストア
  • kube-control-manager
    • 各コントローラー(Deployment, Service, Cronjob...)を管理実行
  • kube-scheduler
    • どのサーバにどのコンテナを入れるかなどを管理します。 (Cassandraなどの重いアプリは高負荷にも耐えられるサーバへ配置するなどの処理を行ってくれます)

GKEではマスターノードを作業者が操作することは基本的にありませんので特に意識する必要はないと思います。(私自身よくわかっていません。)

各ワーカーノードにはkubeletと呼ばれる管理コンポーネントが存在します。 kubeletはPod(≒コンテナ)とapi-serverを繋いで、リソースの管理を行います。

Pod
k8sはPodと呼ばれる最小単位で構成されています。 Podは常にワーカーノード上で動作します。ノードは複数のPodを持つことができ各Podはkubeletによって管理されています。 Podは先ほど"≒コンテナ"と表しましたが、具体的には、複数のコンテナを含むことができ以下のものを含みます。

  • 共有ストレージ
  • ネットワーキング(クラスターに固有のIPアドレス)
  • 各コンテナをどう動かすかに関する情報
    • コンテナイメージバージョン
    • 使用するポートなど

また同じような機能を持ったPodはServiceという論理セットにまとめられ管理されます。 Podは停止を想定して設計されますが、DeploymentというAPIリソースによって同じサービスのPodが最低何台立ち上がっているかを監視しています。 Serviceの中でPodが1台停止した場合、Deploymentはすぐに1台を複製してくれます。 (ServiceとDeploymentについては後述します)

k8sにおけるスケールは基本的にPod単位で行われます。 仮にWordPressのサーバを立てるとして、
①Pod1つに、Webサーバ・DBサーバを入れる
②PodAにWebサーバ、PodBにDBサーバ
という2つのデザインがあると思います。 この場合どちらで構成するかは時と場合によります。 先ほどスケールはPod単位といいましたが、①をスケールする場合DBもスケールされてしまいます。各Webサーバが別々のDBを参照してしまいます。これでは良くないですよね?なのでスケールが想定されている場合は、②のような構成にして、スケールする際はPodA(webサーバ)のみをスケールするようにします。

kubectl コマンド

先ほど少し取り上げましたがk8sは基本的にkubectl(きゅーぶこんとろーる)コマンドですべてを行います。よく使われるコマンドについて説明していきます。

まず、Podの作成について、k8sではマニフェストファイルというファイルを用いてPodの作成を行います。Serviceなどの作成でも用います。(Dockerfileみたいな感じです) このマニフェストファイルはYAMLで記述します。 テンプレートは以下のような感じです

apiVersion: v1
kind: Pod
metadata: 
   name: pod-name
   labels:
      key: value
spec:
   containers:
      - name: container-name
        image: container-image

上から説明していきます。
"apiVersion": 下記を参照してください。
Kubernetesの apiVersion に何を書けばいいか

kind: Pod, Service, Deploymentなどのリソースを記載します。
metadata.name: そのkindの名前を定義します。
metadata.labels: Podの場合ラベルを記載します。Serviceを立てる際はこのラベルとServiceのマニフェストファイルに記述するセレクタと呼ばれる属性とを対応させます。
spec.container: 名前と使用するコンテナイメージを記載します。imageにはalpine:latestなどと記載します。

ちなみにalpineOSとはコンテナ用に開発されたOSでとても軽量に出来ています。コンテナでは軽量=高速と考えて頂いてOKです。 (イメージサイズはCentOSが240MB程度に対してalpineOSは5MB程度)

マニフェストファイルを用いてPodを作成する

$ kubectl apply -f basic-pod.yaml

起動中のPodを一覧表示

$ kubectl get pods
または、
$ kubectl get po 

Podのラベルで検索することもできます。

$ kubectl get pods -l app=web

kubectl getコマンドでは-o wideを付けることでより詳細な情報を得ることができます。

起動中のPodのログイン

$ kubectl exec -it <Pod名> <実行コマンド>

Dockerでも出てきたこのexecコマンドは実行名のところをbashとしてよく用いられますが、書いてある通り実行コマンドなのでlsなどのコマンドで表示だけさせることも可能です。 (bashを実行することでログインと等価になっている)

起動中のPodの定義を変更
マニフェストファイルで作ったPodの定義を少しいじりたいときは、停止→変更→起動ではなく直接いじることも可能です。

$ kubectl edit pods <Pod名>



API リソースを一覧表示
ここまでPodに関する操作のみを扱ってきましたが、k8sにはもっとたくさんのリソースがあります。どんなリソースがあるのか見たいときは以下のコマンドを打ってみましょう。

$ kubectl api-resources

ServiceやDeploymentなどと言ったapi-resourceが一覧表示されます。 kubectl get podskubectl get poと表記しましたが、この略称も出てきます。

マニフェストファイルの定義を調べる

$ kubectl explain Pod  
$ kubectl explain Pod.spec

APIリソースの定義に何があるか忘れてしまったときは上記のコマンドで調べることができます。

Podをネットワークに公開するService

Serviceがどういうものか少し上でも説明しましたが、PodのIPアドレスを作業者が意識せずに単一のエンドポイントで通信する方法です。
通信というのは、 Pod⇔Pod、作業者⇔Pod の2種類がありますが、それぞれにServiceのタイプが違います。

ClusterIP(Pod⇔Pod)
クラスタ内のIPにServiceを公開します。ClusterIPではPodからのみ別のPodにアクセスすることができます。

NodePort (Pod⇔作業者またはインターネット)
各NodeのIPで、NodePort上でServiceを公開します。また、そのServiceが転送する先のClusterIPが自動的に作成されます。

LoadBalancer
Serviceを外部に公開したい(外部IPを割り当てる)場合はLoadBalancerをServiceのtypeをLoadBalancerにします。 NodePortではClusterIPを自動的に作成してくれましたが、LoadBalancerはNodePortとClusterIP両方を作成してくれます。
もちろん本来のロードバランサの機能(ネットワーク経由でのタスクの割り当てを、複数のバックエンドサーバーに分散させるロードバランシング)も持っています。
※ここで作成されるロードバランサは基本的にL4ロードバランサです。

ラベルとセレクタ

PodとServiceを対応させるために、Podにラベル、Serviceにセレクタを記述します。 以下はマニフェストファイルの例です。

# Podのマニフェストファイル

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app: web   #これがラベル
spec:
  containers:
    - name: nginx
      image: nginx:stable-alpine
#Serviceのマニフェストファイル

apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
spec:
  selector:
    app: web   #これがセレクタ
  type: NodePort  # このマニフェストファイルはNodePortのもの
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30001

コンテナの監視

k8sが行うコンテナの監視は2種類あります。 Podを作成する際にマニフェストファイルに記述します。
spec.containers.readinessProbe spec.containers.livenessProbe

  • readinessProbe

  • livenessProbe

    • そのコンテナを再起動すべきかどうか判断する

コンテナ(というかプロセス)は落ちるもの(らしい) その落ちたプロセスを監視して自動復旧してくれるからk8sはすごいんです。

ただこれらのProbeには何も設定されていません。作業者が自分で設定します。 以下のような設定項目があります。

  • initialDelaySeconds
    • コンテナ起動後からProbeを最初に実行するまでの時間(秒)
  • periodSeconds
    • Probeを実行する間隔
  • failureThreshold
    • 失敗と見做すProbeの結果回数
  • successThreshold
    • 成功と見做すProbeの結果回数
  • timeoutSeconds
    • Probeの実行時間猶予

ヘルスチェック項目としては以下の3種類があります。

  • httpGet
  • exec
    • コマンドの終了コードが0なら成功
  • tcpSocket
    • 特定のPortへのTCP接続、Portが開いていれば成功

ConfigMap, Secret

k8sにおける依存性として以下のようなものがあり、コンテナの実行環境によって変更したいものがあります。

それをかく環境に向けてコンテナをビルドするのは非効率です。 そのためにk8sにはConfigMapというものが存在します。

基本的にPod作成時に環境変数を設定するときは下の図のようにします。

ConfigMapを使用したときのマニフェストファイルは下の図のようになります。

これらの図は同義でENVという環境変数に"HELLO WORLD"という値が設定されます。

Secret
Secretとは暗号化されたConfigMapです。 ConfigMapと同じように作成しますが作成した後は環境変数などのkeyが見えなくなります。
暗号化といってもBase64エンコードされているだけですが。その丸見えの暗号化で重要なのは、誰にでも見えていい情報と見る人が限定されている情報を区別することにあります。

今回は深く紹介しませんがk8sにはRBAC (RoleBasedAccessControl)という機能があり、だれがどのリソースを閲覧/作成/削除/更新ができるのかを制御することができます。 この機能を用いてSecretを閲覧できる人を制限します。

Kubernetesにおけるコンテナのスケール

k8sに備わっているスケール機能には以下のようなものがあります。

Deployment

DeploymentについてはServiceでも少し触れましたが、Podを複数起動し決められたPodが起動していることを担保してくれます。

マニフェストファイルはこんな感じです。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:stable-alpine

このDeploymentによって同等のnginxを3台起動し、別でnginxというセレクタのサービスを起動しDeploymentによって監視します。

HPA (Horizontal Pod Autoscaler)

k8sではPodにリソース(CPU/メモリ)を指定し、必要以上のリソースを使ったり、最低限必要なリソースを確保したりできます。(pod.spec.resources.requests/.limit以下に書き込む)

    spec:
      containers:
        - name: nginx
          image: nginx:stable-alpine
          resources:
            requests:
              cpu: 100m
              memory: 100Mi
            limits:
              cpu: 500m
              memory: 500Mi

※1 vCPU = 940 mCPU

requestsを満たせないNodeにはPodが配置されません。また、limit.memoryに指定した量以上のmemoryを消費するとPodが強制的にkillされます。

HPAはPodのCPU使用量が、resources.requestsに指定した量に対して、望む割合を満たすようにPodを水平スケールさせます。そのPodに流れ込むトラフィックが限界値を超えれば勝手にスケールし、収まればkillされていくわけですね。

この記事で触れていないKubernetesのキーワード

Ingress(アプリケーションロードバランサ)
・StatefulSet
・RBAC
・PodDisruptionBudget
・Job / CronJob
・nodeAffinity / Taint, Torelation
・Daemonset
・LimitRange
・ServiceAccount

また、 下記コマンドにて出力される内容を一から網羅すればk8sマスターになれます。

$ kubectl api-service

資格について

Kubernetes Application Developer

Kubernetesクラスタ上で各リソースの操作とトラブルシューティングをする技術
試験官が問題を出し解決する過程を監視する
Certified Kubernetes Application Developer (CKAD) Program

Kubernetes Administrator

Kubernetesクラスタを管理する技術
Certified Kubernetes Administrator (CKA) Program





少々長くなってしまいましたが、この辺で終わろうかと思います。 次の記事ではハンズオン、やっていこうと思います!

それでは

GKE (Google Kubernetes Engin) の使い方入門

こんにちは。先日kubernetesの技術研修に行ってきました。その時のまとめをいくつかの記事に分けて書いていこうかなと思います。

初回はGKE(Google Kubernetes Engine)の使い方とプロジェクトを作成して、環境を整えkubernetesを使えるようにするところまで紹介しようと思います。

Kubernetesとは

Kubernetes(以下、k8s)とは何でしょうか。この辺の話題は次の記事で詳しく書こうと思うので、簡単にだけ説明します。

コンテナを使った宣言的な構成管理や自動化を促進するためのツールとしてk8sがあります。dockerの運用管理、自動化やスケールなどコンテナ運用に便利な機能がたくさん詰まっているソフトウェア基盤がk8sです。

GKEとは

GKEとはGoogle Cloud Platformのひとつです。

公式には以下のように書いてあります。

Google Kubernetes Engine は、Google のインフラストラクチャを使用して、コンテナ化されたアプリケーションのデプロイ、管理、スケーリングを行うマネージド環境を提供します。GKE 環境は複数のマシン(具体的には、Google Compute Engine インスタンス)で構成され、これらのマシンがグループ化されてクラスタを形成します。[1]

 

うーん、わかりそうでよくわからん?なんにせよ、お手軽に数クリックでk8s環境を構築することができブラウザがあれば簡単に参照することができます。

GKEデモ

実際に作ってみましょう。Googleアカウントがあれば作れます。

 

まずGKEにログインしてください。支払い等の設定があるかもしれませんがいくつもプロジェクトを作成しなければ支払いが発生することはないと思います。どの程度しようすれば支払いが発生するのかよくわかっていません(ザコ)が、

少なくとも、GKEの無料トライアルで1年間の使用(または3万円分のリソースの使用)が無料なので、しばらくは発生しません。

 

アカウントが作成出来たらプロジェクトを作成しましょう。(最初は"MyFirstProject"というプロジェクトが作成されると思いますがそれではなく新規に作りましょう)

以下のようにしていってください

 

f:id:ken_memo:20200329192934p:plain

 

f:id:ken_memo:20200329192944p:plain

 

プロジェクトに名前を付けます。なんでもいいです

f:id:ken_memo:20200329192951p:plain

 

プロジェクトが作成されました。

f:id:ken_memo:20200329192958p:plain

 

左上のメニューバーから[KubernetesEngine]>[クラスタ]と選択してください

f:id:ken_memo:20200329193005p:plain

 

クラスタを作成を押します

f:id:ken_memo:20200329193009p:plain

 

以下、クラスタの設定を行っていきます。

リージョンを設定してください。(asia-northeast1が東京です)

f:id:ken_memo:20200329193015p:plain

 

ノード数を選択します。デフォルトは3ですが、ノード数1で3台作成されるのでそんなにいらないので1にします。

自動スケーリングの設定を有効にし最大10台ほどにしておきます。

f:id:ken_memo:20200329193024p:plain

 

自動アップグレードは有効でいいと思います。

f:id:ken_memo:20200329193030p:plain

 

 

ブートディスクサイズもデフォルトは100GBですがそんなにいらないので20GB程度にします。

プリエンプティブノードは24時間立つと停止しますが、70%ほど安価に契約できるらしいです。

f:id:ken_memo:20200329193036p:plain

 

以上で作成しましょう。

cluster1というクラスタが作成できたと思います。

f:id:ken_memo:20200329193047p:plain

 

右上のstart shellボタンでシェルを有効にしshellにアクセスします。

f:id:ken_memo:20200329193149p:plain

 

上図のクラスタ右側の接続ボタンを押すと以下のようなポップアップが表示されるのでこれをコピーするかシェルで実行を押しシェルで実行します。

これでクラスタに接続することができます。

f:id:ken_memo:20200329193156p:plain

 

f:id:ken_memo:20200329193208p:plain

 

試しに以下のコマンドを打ってみましょう

# kubectl get nodes

f:id:ken_memo:20200329192924p:plain

実際に先ほど作成したノード3台が表示されていると思います。

これでセットアップは完了したので、k8sの環境を楽しむことができます。

 

今回はこの辺にしたいと思います。

参考文献

[1] GKEの概要 | Kubernetes Enginのドキュメント

https://cloud.google.com/kubernetes-engine/docs/concepts/kubernetes-engine-overview?hl=ja 

 

Docker installからGoでHelloWorldまで(2)

おはようございます。

間違えて記事を消してしまい時系列がおかしいことになっているかもしれませんが気にしないでください。

続きやります。前回の記事はこちら

ken-memo.hatenablog.com

Docker基本操作

 Dockerを運用する流れとしては

  1. Dockerイメージを入手
  2. イメージからコンテナを起動
  3. 運用・開発・変更など
  4. 変更を加えたコンテナをCommitして新たなイメージを作成

こんな感じです。

この手順がどう生きるかは、インミュータブル・インフラストラクチャでググってみてください。

試しに、CentOSイメージの入手からCommitまでやってみます。

 

CentOSイメージの入手

イメージの入手は下記コマンドを使用します。

latestで最新版を入手できます。versionを指定したいときは2つ目のコマンドのように指定します。

# docker image pull centos:latest
# docker image pull centos:7.5.1804

 下記コマンドで今あるイメージを一覧表示します。-aはすべて表示です。つけてもつけなくてもあんま変わりません。

# docker image ls -a

f:id:ken_memo:20200116215159p:plain

 コンテナの生成と起動

docker container run コマンドでdockerを生成・起動します。

昔はdocker runコマンドだったらしいですが最近docker containerコマンドにいろいろ集約されたみたいです。(docker runでも動きます)

runのオプションは無限にあるのでここでは書いたものを説明します。(基本 -it は付ければいいと思います。) 

  • -i : dockerコンテナ起動時に標準入力を受け付ける
  • -t : 仮想端末(pseudo-TTY)をコンテナに割り当てる
  • -h : hostnameを指定します。デフォルトはコンテナIDです。(コンテナなのでブートがないため内部で/etc/hostnameを変更しても反映されません)
  • --name : コンテナの名前を指定します。
  • "centos:latest" : 起動したいイメージを指定します。
  • "/bin/bash" : PID=1のプロセスを指定します。(例えば "/sbin/init"のようにすれば通常のVMのように扱えます)
# docker container run -i -t -h test01 --name test01 centos:latest /bin/bash

 起動したらプロンプトが切り替わりコンテナに入ります。

(test02になってるのは気にしないでください)

f:id:ken_memo:20200116224142p:plain

何か適当に変更を加えます。

# mkdir /home/$HOSTNAME

 そしたら、exitでコンテナから抜けましょう

# exit

 exitしたコンテナは停止(stop)状態になっているので再度接続するためには起動(start)させる必要があります。

以下のような流れになります。

(container)# exit
(host)# docker container start <コンテナ名>
(host)# docker container attach <コンテナ名>
(container)#

 コンテナを停止せず抜けるにはexitではなく「ctrl+p → ctrl+q」という手順を踏むか、attachではなくexecを使います。runしたときは前者じゃないといけない気がしますが。

qiita.com

変更したコンテナをCommitして新たなイメージにする

一応生成したコンテナがあるか確認しましょう。

こっちの -a に関しては意味があって、停止しているコンテナも表示してくれます。

# docker container ls -a

 ではCommitしましょう。コミット...コミット...

 

以下のように指定します。コンテナIDは上のコマンドで見れます。<リポジトリ名>:<タグ>は任意でわかりやすいものをつけましょう

# docker container commit <コンテナID> <リポジトリ名>:<タグ>
# docker container commit 227500f9fdd1 centos:docker001

# docker image ls -a

 

以上で完了です。DockerイメージはDocker hubと言われるリポジトリに先人が構築してイメージにしてくれたものが上がっています。そこから欲しいものをとってきてコンテナにしましょう。

https://hub.docker.com/

 

Goコンテナイメージの入手とHelloWorld

なんでGoなの?っていうとDockerやKubernetesではGoがアツいからです。

て言っても筆者はゴーのごの字も知らないのでこれから勉強します。すいません。

Goのイメージは以下でとってこれます。Ubuntuベースです。

# docker image pull golang:latest

 runします

# docker container run -it -h gotest --name gotest01 golang:latest /bin/bash

 めんどくさくなってきたのでHello.goの作成まで一気に書きます。

# apt update
# apt-get install vim
# mkdir /home/$HOSTNAME
# cd $HOSTNAME
# vim Hello.go
package main

import (
    "fmt"
)

func main() {
    fmt.Println("hello world")
} 
# go fmt Hello.go
# go run Hello.go

f:id:ken_memo:20200116234838p:plain

 オッケー終わり!

各自Commitしてイメージを保存してくださいね。

 

 

ではでは、おやすみなさいませ