概要

PVCの使い方を学習するために、Harborの説明に使用した harbor-nginx を使っていきます。

2つのPodから成るnginxクラスターを構成した時に、コンテンツを管理するためには、2台のPodの/usr/share/nginx/html/以下を管理する必要があります。(具体的には、両方のPodの/usr/share/nginx/html/以下を同一にする必要がある)

現状の確認と準備作業

作業を始める前に、SCCPのトップページに戻り、kubectlコマンドを使うための準備作業 を完了してください。

次に、前提となる設定が行なわれているか確認します。

pod/harbor-nginxの確認

実際はDeployment定義からReplicaSet定義が生成され、これによりPodが作成されていますが、ここでは実体であるPodの状況を確認します。

$ kubectl -n $(id -un) get pod -l app=harbor-nginx -o wide

次のように2つのPodの状況が出力されれば成功です。

NAME                           READY   STATUS    RESTARTS   AGE     IP               NODE       NOMINATED NODE
   READINESS GATES
harbor-nginx-6cc7576bc-kkc8q   1/1     Running   0          5h34m   10.233.105.70    u109ls04   <none>    <none>
harbor-nginx-6cc7576bc-vbzgt   1/1     Running   0          5h34m   10.233.112.164   u109ls03   <none>    <none>

表示されない場合には、Harbor - my-nginxイメージの登録 から最後までの作業を完了してから進めてください。

pod/poroxy の確認

まず `app: my-proxy が稼動しているか確認します。 なお詳細は、リバース・プロキシー を確認してください。

$ kubectl -n $(id -un) get pod -l app=my-proxy

次のようにPod(proxy-XXXXXX-XXX)が表示されれば、次を飛して作業を進めてください。

NAME                     READY   STATUS    RESTARTS   AGE
proxy-755bf9945f-jwqtn   1/1     Running   0          76m

画面にPodが表示されなかった場合は、次のコマンドを実行して、proxyを稼動させてから進めてください。

$ kubectl -n $(id -un) apply -f https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/setupk8s-centos/deploy-proxy.yaml

app: my-proxy が稼動していても、svc/<namespace>-svc の内容が proxy を向いていない可能性があるので、次のコマンドで svc/<namespace-svc 定義を更新します。

$ curl "https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/volumes/svc-proxy.yaml" | sed -e "s/s12xxxxx/$(id -un)/" | kubectl -n $(id -un) apply -f -
これら deploy-proxy.yaml, svc-proxy.yaml のYAMLファイルは繰り返し、kubectlコマンドで適用しても問題ありませんので、なんとなく実行してしまっても問題ありません。

ここまでの確認

ここに到達している人は次の作業が完了しているはずです。

  1. Harbor(https://inovtst9.u-aizu.ac.jp/)にmy-nginx:1.0が登録されている

  2. Kubernetes上で、app: harbor-nginxapp: my-proxy が稼動している

  3. <namespace>-svc が selectorで、 app: my-proxy を向いている

ここからの目標

準備作業が終ったところで、PVC(Filesystem/FileStorage)を利用し、2つ以上の pod/harbor-nginx から同時に参照できるコンテンツ領域を準備します。

そして、可能であれば外部からコンテンツを配置できるような仕組みを準備しましょう。

全体の構成は以下のようになります。

ingress proxy.20210531 SCCP IngressNetwork Advanced

PVCについては記述していませんが、Pod#1, Pod#2の(nginx)から、1つのPVCを参照するようにします。

PVCの準備

ここでは、PVとPVC で利用したPVC、myfs-pvc を利用します。

次のコマンドで、myfs-pvc があることを確認します。

$ kubectl -n $(id -un) get pvc

次のように表示されれば問題ありません。次の myfs-pvc を作成する手順はスキップして、次に進みます。

NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
myfs-pvc    Bound    pvc-8bb6ede5-fa54-4d77-b576-408072630231   10Gi       RWX            rook-cephfs    5m23s

もし、表示されない場合は次の要領で、myfs-pvc を作成して次に進みます。

$ kubectl -n $(id -un) apply -f https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/volumes/pvc-cephfs.yaml

harbor-nginx からの myfs-pvc の利用

まず、pod/harbor-nginx を定義した時のYAMLファイルを確認します。

Harbor - KubernetesからのHarborの利用 に記載されている、deploy-nginx.yaml ファイルの内容を確認します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: harbor-nginx
spec:
  selector:
    matchLabels:
      app: harbor-nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: harbor-nginx
    spec:
      containers:
      - name: harbor-nginx
        image: inovtst9.u-aizu.ac.jp/s12xxxxx/my-nginx:1.0
        ports:
        - containerPort: 80

ここに、PVとPVC で利用した deploy-nginx-cephfs.yaml ファイルに書かれていた、volumeMountsvolumes の定義を書き写します。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-cephfs
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx-cephfs
  template:
    metadata:
      labels:
       app: nginx-cephfs
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: "Always"
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-data-storage
          mountPath: /usr/share/nginx/html
      volumes:
      - name: nginx-data-storage
        persistentVolumeClaim:
          claimName: myfs-pvc

まずこの2つのファイルをダウンロードします。 そして、emacsなどのエディタで開き、よく見比べてください。

最初の ./harbor/deploy-nginx.yaml の下に、2番目の ../volumes/deploy-nginx-cephfs.yaml にある volumeMounts から下の行全てをコピーします。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: harbor-nginx
spec:
  selector:
    matchLabels:
      app: harbor-nginx
  replicas: 2
  template:
    metadata:
      labels:
        app: harbor-nginx
    spec:
      containers:
      - name: harbor-nginx
        image: inovtst9.u-aizu.ac.jp/s12xxxxx/my-nginx:1.0
        ports:
        - containerPort: 80
        volumeMounts:
        - name: nginx-data-storage
          mountPath: /usr/share/nginx/html
      volumes:
      - name: nginx-data-storage
        persistentVolumeClaim:
          claimName: myfs-pvc

このような内容のファイルを適当な名前をつけてローカルファイルシステムに保存します。

そして、そのファイルを末尾に指定するような $ kubectl -n $(id -un) apply -f コマンドを実行します。

実行例は以下のとおりです。

$ sed -e "s/s12xxxxx/$(id -un)/" deploy-harbor-nginx-with-myfs-pvc.yaml | kubectl -n $(id -un) apply -f -
deployment.apps/harbor-nginx configured

ここまでで、harbor-nginx は myfs-pvc を利用するように構成されているはずです。

自信のない人は、次のファイルを利用しても構いません。

$ curl "https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/tutorial-nginx-pvc/deploy-harbor-nginx-with-myfs-pvc.yaml" |  sed -e "s/s12xxxxx/$(id -un)/" | kubectl -n $(id -un) apply -f -

ProxyのConfigMapの更新

Proxyから app: harbor-nginx に処理を向ける必要があります。

apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-conf
data:
  proxy.conf: |
    server {
      listen 80;
      location /s12xxxxx/ {
        proxy_pass    http://harbor-nginx/;
      }
    }

このファイルを次の要領で適用します。

$ curl "https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/tutorial-nginx-pvc/configmap-proxy-to-harbor-nginx.yaml" |  sed -e "s/s12xxxxx/$(id -un)/" | kubectl -n $(id -un) apply -f -

このファイルが無事に適用できたら、app: my-proxy を再起動します。

$ kubectl -n $(id -un) delete pod -l app=my-proxy

最後に、無事に稼動しているか、以下の自分のIDのリンクを辿ってみてください。

コンテンツが配置されていない場合は、動いているか分かりませんが、つぎの要領で、app: harbor-nginx のログを確認して、Webブラウザからのアクセスがあることを確認してください。

$ kubectl -n $(id -un) logs -l app=harbor-nginx

deploymentを経由することで、2つのpod両方のログを表示させています。

10.233.112.220 - - [12/Jul/2021:08:19:58 +0000] "GET / HTTP/1.0" 304 0 "https://web-int.u-aizu.ac.jp/" "Mozill
a/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0" "192.168.100.53"

ユーザーリスト

コンテンツ(htmlファイル等)の確認と配置について

ここから先の作業は、ゼミ室10のネットワーク(192.168.100.0/24)の内部でないと、最終的に構築したSSHサーバーにアクセスできません。(rsyncコマンド)の実行に失敗します。

ここまででは、自由にコンテンツを配布できるWebサーバーが入手できた感じはあまりません。 特に pvc/myfs-pvc にファイルを配置していない場合には、やっぱり何をしたか実感がわかないと思います。

使用した pvc/myfs-pvc は、他のPodからも利用(mount)することができます。

ここから先では、ファイル更新用のSSHサーバーを準備して、rsyncなどを利用して、ローカルで準備したファイルを更新してみます。

このために利用するSSHサーバーのイメージをHarborに登録しました。

この使い方について、説明していきます。

設定ファイル

このSSHサーバーは次のようなDockerfileから構成されています。

FROM alpine:3.13.5

MAINTAINER YasuhiroABE <yasu-abe@u-aizu.ac.jp>

## openrc
RUN apk --no-cache add tzdata bash ca-certificates make openssh rsync openssl

ENV SSHD_CONFIG_FILEPATH /etc/ssh/sshd_config
ENV ROOK_SSH_AUTHKEYS_FILEPATH /root/.ssh/authorized_keys
ENV ROOK_SSH_PUBKEY_FILEPATH /conf/id_key.pub
ENV ROOK_SSH_SERVERKEYS_FILEPATH_LIST "/etc/ssh/ssh_host_ecdsa_key /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_ed25519_key"

COPY run.sh /run.sh
RUN chmod +x /run.sh

EXPOSE 22

ENTRYPOINT ["/run.sh"]

ここで実行している run.sh ファイルの内容は次のようになっています。

#!/bin/bash -x

## update /etc/ssh/sshd_config
SSHD_CONFIG_FILEPATH="${SSHD_CONFIG_FILEPATH:-/etc/ssh/sshd_config}"
ROOK_SSH_SERVERKEYS_FILEPATH_LIST="${ROOK_SSH_SERVERKEYS_FILEPATH_LIST:-/etc/ssh/ssh_host_rsa_key}"
for item in ${ROOK_SSH_SERVERKEYS_FILEPATH_LIST}
do
    echo "HostKey ${item}" >> "${SSHD_CONFIG_FILEPATH}"
done
echo "PubkeyAuthentication yes" >> "${SSHD_CONFIG_FILEPATH}"
echo "PasswordAuthentication no" >> "${SSHD_CONFIG_FILEPATH}"
echo "PermitRootLogin yes" >> "${SSHD_CONFIG_FILEPATH}"
echo "StrictModes yes" >> "${SSHD_CONFIG_FILEPATH}"

## prepare the ~/.ssh/authorized_keys
ROOK_SSH_PUBKEY_FILEPATH="${ROOK_SSH_PUBKEY_FILEPATH:-/conf/id_key.pub}"
ROOK_SSH_AUTHKEYS_FILEPATH="${ROOK_SSH_AUTHKEYS_FILEPATH:-/root/.ssh/authorized_keys}"
ROOK_SSH_AUTHKEYS_BASEDIR="$(dirname ${ROOK_SSH_AUTHKEYS_FILEPATH})"
mkdir -p "${ROOK_SSH_AUTHKEYS_BASEDIR}"
chmod 700 "${ROOK_SSH_AUTHKEYS_BASEDIR}"
cp "${ROOK_SSH_PUBKEY_FILEPATH}" "${ROOK_SSH_AUTHKEYS_FILEPATH}"
chmod 400 "${ROOK_SSH_AUTHKEYS_FILEPATH}"

## randamize root password
echo "root:$(openssl rand -hex 12)" | chpasswd

exec /usr/sbin/sshd -D

この run.sh スクリプトは、あらかじめ外部で準備したSSHサーバー用の鍵ファイルを利用するように構成されていて、これだけでは起動しません。Ubuntu/CentOSなどに標準添付されている openssh-server の標準的な動作では、ランダムな鍵ファイルを生成しますが、ここでは生成されたファイルを配置することだけ行なっています。

Kuberntesでの設定

この openssh-ssh-server をKubernetes上で動作させ、myfs-pvc をマウントし、必要なファイルを転送します。

ここでの作業は適当な作業用ディレクトリを準備してから行なってください。

まず作業用ディレクトリ conf を準備し、必要な設定ファイルを生成します。

$ mkdir conf
$ for key in rsa ecdsa ed25519 ; do test ! -f "ssh_host_${key}_key" && ssh-keygen -t "${key}" -f "conf/ssh_host_${key}_key"; done
$ ssh-keygen -t ed25519 -f conf/id_ed25519
ssh-keygenコマンドを実行すると、パスフレーズ(pass-phrase)を入力するよう促されますが、エンターキーを押してパスフレーズは設定しないでください。(no pass-phrase設定)

無事に生成すると、次のようなファイルが作成されているはずです。

$ ls conf/
id_ed25519          ssh_host_ecdsa_key.pub    ssh_host_rsa_key
id_ed25519.pub      ssh_host_ed25519_key      ssh_host_rsa_key.pub
ssh_host_ecdsa_key  ssh_host_ed25519_key.pub

次にkubectlコマンドを利用して、secretsオブジェクトを生成します。

$ kubectl -n $(id -un) create secret generic ssh-host-keys --from-file=conf/ssh_host_ecdsa_key --from-file=conf/ssh_host_ed25519_key --from-file=conf/ssh_host_rsa_key
$ kubectl -n $(id -un) create secret generic ssh-auhorized-keys --from-file=conf/id_ed25519.pub
鍵ファイルを再度登録したい場合には、一度Secretオブジェクトを削除してから再度作成してください。

$ kubectl -n $(id -un) delete secret ssh-host-keys
$ kubectl -n $(id -un) delete secret ssh-auhorized-keys

次はこの登録された情報を利用して、sccp-ssh-server を起動します。

$ kubectl -n $(id -un) apply -f "https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/tutorial-nginx-pvc/deploy-sccp-ssh-server.yaml"

続いてサービスを登録しますが、これは type: loadBalancer を利用します。

$ kubectl -n $(id -un) apply -f "https://web-int.u-aizu.ac.jp/~yasu-abe/ja/sccp/manual/tutorial-nginx-pvc/svc-ssh-server.yaml"

ここまで無事に完了すると、SSHでアクセスするためのIPアドレスが割り当てられているはずです。

$ kubectl -n $(id -un) get svc openssh-server
NAME             TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)        AGE
openssh-server   LoadBalancer   10.233.2.117   192.168.100.160   22:31068/TCP   4m22s

EXTERNAL-IP / 192.168.100.160 の部分は、各自で異なるので、自分のEXTERNAL-IPを確認してください。 ここで表示されるSSHサーバーを利用し、手元でindex.htmlファイルを作成し、転送してみます。

まず適当なindex.htmlファイルを準備します。

$ emacs index.html

内容は任意ですが、例えば、次のような内容にします。

<html>
<h1>Hello, guys!!</h1>
<p>abcdefg</p>
</html>

次にこのファイルを転送します。

$ scp -i conf/id_ed25519 index.html root@192.168.100.160:/data/index.html

転送したファイルが表示されるか確認してください。

Hugoを利用した静的コンテンツの生成

Webページを作成する際には、WordPressのようなCMSツールを利用するケースも一般的ですが、動的なアプリケーションである必要がないのであれば、静的コンテンツとして生成する方法がより安全といえます。

ここでは、Hugoを利用して静的コンテンツについて学びつつ、生成物をrsyncコマンドによって、PVCに転送し、nginxからそれなりの見栄えのWebページを作成してみます。

Hugoの利用について

作業用に適当なディレクトリを作成してから始めることをお勧めします。

まず始めることは、公式サイトのQuick StartのページのStep2から始めていきます。

公式ページの説明では、quickstart のように名前をつけていますが、例えば、my-site のように別名を指定することもできます。

$ hugo new site my-site

この時指定した、my-sitequickstart という名前のディレクトリが作成されます。 画面には次のようなメッセージが表示されます。

Congratulations! Your new Hugo site is created in ......../my-site.

Just a few more steps and you're ready to go:

1. Download a theme into the same-named folder.
   Choose a theme from https://themes.gohugo.io/ or
   create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
   with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".

このディレクトリに移動してから、操作を行なっていきます。 公式ガイドでは、`git init などのコマンドを実行していますが、必須ではありません。

この操作では、テーマを配置しています。 公式サイトでは様々なHugo用のテーマが紹介されていますので、この他のテーマを利用することも可能です。

$ cd my-site
$ git clone https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke

具体的なコンテンツを作成します。 このため、hugoコマンドにコンテンツファイルを作成させます。

$ hugo new posts/my-first-post.md

次のようなメッセージが表示され、実際のファイルは、content/posts/my-first-post.md に作成されていることが分かります。

/home/..../my-site/content/posts/my-first-post.md created

ここでは公式ガイドに手順はありませんが、作成した content/posts/my-first-post.md ファイルを編集します。 emacs などで開き、次のように draft: false を設定します。title/dateは変更する必要はありません。(変更しても構いません。)

---
title: "My First Post"
date: 2021-07-13T06:38:38Z
draft: false
---

emacsを閉じて、次に進みます。

公式ガイドでは、Step5でサーバーを立てて内容を確認しています。 実行前に、全てのhugoコマンドを強制的に停止させます。

$ sudo killall hugo
$ hugo server -D &

次のように画面に表示されるので、ポート番号を確認して、Webブラウザから同じ番号に接続してください。

Start building sites …
hugo v0.85.0+extended linux/amd64 BuildDate=unknown
WARN 2021/07/13 06:42:22 found no layout file for "HTML" for kind "page": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.

                  | EN
-------------------+-----
  Pages            |  4
  Paginator pages  |  0
  Non-page files   |  0
  Static files     |  0
  Processed images |  0
  Aliases          |  0
  Sitemaps         |  1
  Cleaned          |  0

Built in 31 ms
Watching for changes in /home/professor/yasu-abe/sccp/sccp2021_1H/20210713.hugo/my-site/{archetypes,content,data,layouts,static}
Watching for config changes in /home/professor/yasu-abe/sccp/sccp2021_1H/20210713.hugo/my-site/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop

$

Webブラウザから、http://localhost:1313/ に接続しますが、まだ画面はまっしろなままのはずです。

次に公式ガイドのStep6に進み、 config.toml ファイルを emacs などのエディタで開き、次のように編集します。 具体的には、先頭のURLを自分が使用するものに変更し、一番最後のテーマを指定する1行を追加します。

baseURL = "https://inovtst9.u-aizu.ac.jp/s12xxxxx/"
languageCode = "en-us"
title = "My New Hugo Site"
theme = "ananke"

s12xxxxx の部分が自分のnamespace(AINS-ID)と同じになっていることを確認してください。

この編集が終ると、Webブラウザが再描画され、先ほど作成した My First Post が画面に表示されるはずです。

ここまで確認したら、一度 hugo を停止します。

$ killall hugo

PVCへの転送

先ほど画面に表示したコンテンツを public ディレクトリに生成します。

$ rm -rf public
$ hugo

hugoコマンドをオプションなしに実行すると、public/ ディレクトリに、コンテンツを出力します。 画面には次のように表示されるはずです。

Start building sites …
hugo v0.85.0+extended linux/amd64 BuildDate=unknown

                   | EN
-------------------+-----
  Pages            |  7
  Paginator pages  |  0
  Non-page files   |  0
  Static files     |  1
  Processed images |  0
  Aliases          |  0
  Sitemaps         |  1
  Cleaned          |  0

Total in 84 ms

このファイルの内容を、rsync を利用して、PVCに転送します。

まず自分の app/my-sshserver のExternal-IPを確認しましょう。

kubectl -n $(id -un) get svc openssh-server -o wide

次のように表示されますので、自分の External-IP 192.168.100.xxx を確認します。

NAME             TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)        AGE   SELECTOR
openssh-server   LoadBalancer   10.233.2.117   192.168.100.160   22:31068/TCP   22h   app=my-sshserver

この External-IP を利用して、次のようにrsyncコマンドを実行します。 ここで conf/id_ed25519 の部分は、自分が作成したファイルのパス(path)に置き換えてください。

$ rsync -av -e "ssh -i conf/id_ed25519" public/. root@192.168.100.160:/data/.

ファイルが転送されると、無事にコンテンツが表示されるはずです。

実際にHugoを利用したプロジェクトについて

このSCCPのWebページのディレクトリを以下のURLで公開しています。

以上