Harborからイメージをpullしてkubernetesクラスタにデプロイしてみる

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。
前回DjangoのコンテナイメージをHarborにpushしました。

https://qiita.com/ohtsuka-shota/items/e7202fcb318b277789af

今回は上記で作成したDjangoイメージを使ってkubernetesにpod(正確にはDaemonSet)をデプロイしていき、NodePortを使いアクセスをしていきたいと思います。

# 環境イメージ

今回の環境構築イメージはざっくり以下となります。
kubernetes上に構築したRook-Ceph + Harbor環境からDjangoイメージをpullし、そのイメージを使用してDaemonSetとして全ノードにpodをデプロイしていきたいと思います。HarborからイメージをpullするためにcontainerdにHarborの証明書を認識させる必要があるため、併せて実施します。また、これらのpodに対してはNodePortを使いアクセスしていきたいと思います。

![k8s_3-ページ16.drawio (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/39f87a44-3e5a-0940-9217-9a1b8d543006.png)

Rook-Ceph + Harborの構築は以下をご覧ください。

https://qiita.com/ohtsuka-shota/items/55a85409558ee086b098

https://qiita.com/ohtsuka-shota/items/aaaae737594cfc572435

# 参考サイト

以下サイトを参考にしました。

https://zenn.dev/t_ume/articles/a8ff4b41286f05#%E8%A8%BC%E6%98%8E%E6%9B%B8%E6%BA%96%E5%82%99%EF%BC%88%E3%83%8E%E3%83%BC%E3%83%89%EF%BC%89

# 用語
## コンテナランタイム

難しく聞こえてしまいそうですが、下記にも書いている通りkubernetes上でコンテナを走らせたり管理するためのソフトウェアです。dockerもコンテナランタイムの1つです。dockerのほかにもcontainerdやcri-o、podmanなどもこれに該当します。

>コンテナ ランタイムは、Kubernetes ノード上のコンテナとコンテナ イメージを管理するソフトウェアです。

https://cloud.google.com/anthos/clusters/docs/on-prem/latest/concepts/using-containerd?hl=ja#:~:text=%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%20%E3%83%A9%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0%E3%81%AF%E3%80%81Kubernetes%20%E3%83%8E%E3%83%BC%E3%83%89,%E7%A7%BB%E8%A1%8C%E3%81%97%E3%81%9F%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%20%E3%83%A9%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0%E3%81%A7%E3%81%99%E3%80%82

# 構築
## 各ノードで稼働しているcontainerdにHarborの証明書を認識させる

まず、今のkubernetesクラスタのコンテナランタイムを確認します。kubectl get node -o wideで確認することができます。kubeadmでkubernetesクラスタをデプロイしている場合、基本的にはcontainerdかと思います。

```
root@k8s-master:~# kubectl get node -o wide
NAME           STATUS   ROLES           AGE   VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE             KERNEL-VERSION      CONTAINER-RUNTIME
k8s-ceph01     Ready    <none>          17d   v1.27.3   192.168.2.37   <none>        Ubuntu 22.04.2 LTS   5.15.0-75-generic   containerd://1.6.21
k8s-ceph02     Ready    <none>          17d   v1.27.3   192.168.2.38   <none>        Ubuntu 22.04.2 LTS   5.15.0-75-generic   containerd://1.6.21
k8s-ceph03     Ready    <none>          17d   v1.27.3   192.168.2.39   <none>        Ubuntu 22.04.2 LTS   5.15.0-75-generic   containerd://1.6.21
k8s-master     Ready    control-plane   31d   v1.27.2   192.168.2.30   <none>        Ubuntu 22.04.2 LTS   5.15.0-76-generic   containerd://1.6.21
k8s-worker01   Ready    <none>          31d   v1.27.2   192.168.2.31   <none>        Ubuntu 22.04.2 LTS   5.15.0-73-generic   containerd://1.6.21
k8s-worker02   Ready    <none>          31d   v1.27.2   192.168.2.32   <none>        Ubuntu 22.04.2 LTS   5.15.0-73-generic   containerd://1.6.21
k8s-worker03   Ready    <none>          31d   v1.27.2   192.168.2.33   <none>        Ubuntu 22.04.2 LTS   5.15.0-73-generic   containerd://1.6.21
```

次にkubernetesのmaster nodeで以下のコマンドを実行してharbor-nginxというSecretから証明書を取得します。
```
root@k8s-master:~/yaml/harbor# mkdir -p /usr/share/ca-certificates/harbor 
root@k8s-master:~/yaml/harbor# kubectl get secrets -n harbor harbor-nginx -o jsonpath='{.data.ca\.crt}' | base64 -d > /usr/share/ca-certificates/harbor/harbor.crt 
root@k8s-master:~/yaml/harbor# cat /usr/share/ca-certificates/harbor/harbor.crt 
-----BEGIN CERTIFICATE----- 
MIIDEzCCAfugAwIBAgIQXYj6XTtDZV6pP1KVd/6nvDANBgkqhkiG9w0BAQsFADAU 
MRIwEAYDVQQDEwloYXJib3ItY2EwHhcNMjMwNzA5MDQ1MjA5WhcNMjQwNzA4MDQ1 
MjA5WjAUMRIwEAYDVQQDEwloYXJib3ItY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IB 
DwAwggEKAoIBAQCmFplfRJUbXkHUgKcRCOAModVzpLHnQpvxi54mGgxgt2h+dFeh 
VwFYLWwe53Lyu3IRqjGvvTU07QVK627W/cP1rTXgeN6W8dkN3vVWyMwR75CXwsn8 
kQMVZxMwEh3Knh5Hc81snCGatzKOmPoFXMLXoq+bo5j2MM5BZXJQTZxGFRceXhSq 
r1J05bLwfIjgNJURP+zEMMB/X/xb0ROJM9oIQhSQmRMBePM33b2lWI9mVN1vH+rw 
fVrLUpp1sIkbp/S5ynRGjpjF7ScRBEXHjTL9ZOk1OBFI5QyB+jN26IDcgGVEHGny 
+S0lvxVigYGCzGk3DGofPm3jx2SzyvKzjTxLAgMBAAGjYTBfMA4GA1UdDwEB/wQE 
AwICpDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUw 
AwEB/zAdBgNVHQ4EFgQUSiAwyZYykvbDD5ToAhsfoFS+BYwwDQYJKoZIhvcNAQEL 
BQADggEBAHgaDDryqU4RRrpeZhOv8Vi6+A/MGqaNsSLaWSn8zFe31lCRI1a7vhxC 
+jXRNRs6fBPyGNfrGKDqoVGlr7sTL3iRDrhWHBzb2rcIWt1r++AkluTwVa7rp4RO 
848A1rh8lDZ10CN25gPZmGCk7AYYZGFo0ITZYf13FdyH+GqWlWovbWFVL3ZHfTjR 
4yliGWKMN7GeQVySJ4i0rpT0oGnvZphZ/hNlvTuByIYm0XB3tpIhJEH96yku2K69 
d+KI5llKjPk7eLUnf3KaK6iJnp0x1h2L8ZjSqHojzYs/SoKgexPUUswEKSajEalV 
FCN+HZUp61bgZxWpECiFYOUEfXJnIi0= 
-----END CERTIFICATE----- 
```

或いはHarborのWebUIからも証明書をインストールできます。
Project内のREGISTRY CERTIFICATEを押下することでインストールできます。
![2023071205.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/a55832f1-6b11-eb83-268b-f4cef0b80e2e.png)

この証明書をHarborからイメージをpullしたいnodeに配置します。配置する方法はscpコマンドでも良いですし、WinSCPでもいいですし、なんでもOKです。
今回はscpコマンドを使ってみました。
```
root@k8s-master:~/yaml/harbor# scp /usr/share/ca-certificates/harbor/harbor.crt
 k8s-worker03:/tmp 
The authenticity of host 'k8s-worker03 (192.168.2.33)' can't be established.
ED25519 key fingerprint is SHA256:ASvtbwzNVzCXidwxa8h3uqe5SnINTkeZB44Ty97mgac.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'k8s-worker03' (ED25519) to the list of known hosts.
root@k8s-worker03's password:
harbor.crt                                                  100% 1127   614.7KB/s   00:00
```

各nodeのコンテナランタイムのcontainerdに証明書を認識させます。
以下のコマンドを実行します。update-ca-certificatesコマンドでharbor.crtを認識させた後、containerdを再起動しています。これでcontainerdはHarborからコンテナイメージをpull出来るようになるはずです。
```
root@k8s-worker01:/usr/share/ca-certificates/harbor# ls -ltr 
total 4 
-rw-r--r-- 1 root root 1127 Jul  9 08:16 harbor.crt 
root@k8s-worker01:/usr/share/ca-certificates/harbor# cat harbor.crt 
-----BEGIN CERTIFICATE----- 
MIIDEzCCAfugAwIBAgIQXYj6XTtDZV6pP1KVd/6nvDANBgkqhkiG9w0BAQsFADAU 
MRIwEAYDVQQDEwloYXJib3ItY2EwHhcNMjMwNzA5MDQ1MjA5WhcNMjQwNzA4MDQ1 
MjA5WjAUMRIwEAYDVQQDEwloYXJib3ItY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IB 
DwAwggEKAoIBAQCmFplfRJUbXkHUgKcRCOAModVzpLHnQpvxi54mGgxgt2h+dFeh 
VwFYLWwe53Lyu3IRqjGvvTU07QVK627W/cP1rTXgeN6W8dkN3vVWyMwR75CXwsn8 
kQMVZxMwEh3Knh5Hc81snCGatzKOmPoFXMLXoq+bo5j2MM5BZXJQTZxGFRceXhSq 
r1J05bLwfIjgNJURP+zEMMB/X/xb0ROJM9oIQhSQmRMBePM33b2lWI9mVN1vH+rw 
fVrLUpp1sIkbp/S5ynRGjpjF7ScRBEXHjTL9ZOk1OBFI5QyB+jN26IDcgGVEHGny 
+S0lvxVigYGCzGk3DGofPm3jx2SzyvKzjTxLAgMBAAGjYTBfMA4GA1UdDwEB/wQE 
AwICpDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDwYDVR0TAQH/BAUw 
AwEB/zAdBgNVHQ4EFgQUSiAwyZYykvbDD5ToAhsfoFS+BYwwDQYJKoZIhvcNAQEL 
BQADggEBAHgaDDryqU4RRrpeZhOv8Vi6+A/MGqaNsSLaWSn8zFe31lCRI1a7vhxC 
+jXRNRs6fBPyGNfrGKDqoVGlr7sTL3iRDrhWHBzb2rcIWt1r++AkluTwVa7rp4RO 
848A1rh8lDZ10CN25gPZmGCk7AYYZGFo0ITZYf13FdyH+GqWlWovbWFVL3ZHfTjR 
4yliGWKMN7GeQVySJ4i0rpT0oGnvZphZ/hNlvTuByIYm0XB3tpIhJEH96yku2K69 
d+KI5llKjPk7eLUnf3KaK6iJnp0x1h2L8ZjSqHojzYs/SoKgexPUUswEKSajEalV 
FCN+HZUp61bgZxWpECiFYOUEfXJnIi0= 
-----END CERTIFICATE-----
root@k8s-worker01:/usr/share/ca-certificates/harbor# update-ca-certificates 
Updating certificates in /etc/ssl/certs... 
0 added, 0 removed; done. 
Running hooks in /etc/ca-certificates/update.d... 
done.
root@k8s-worker01:/usr/share/ca-certificates/harbor# systemctl restart containerd
```

## DjangoイメージをHarborからpullしてpodを走らせる

yamlをベースにDjango環境をデプロイしていきたいと思います。今回用意したyamlファイルは以下となります。1つのyamlでDjango pod（正確にはDaemonSet）をデプロイするins-env namespaceとDjangoのDaemonSet、DaemonSetにWebブラウザで接続する為のNodePortを定義し、一斉にデプロイしていきます。
```ds-django.yaml 
kind: Namespace 
apiVersion: v1 
metadata: 
  name: ins-env 
  labels: 
    name: ins-env 

--- 
apiVersion: apps/v1 
kind: DaemonSet 
metadata: 
  name: django-rs 
  namespace: ins-env 
  labels: 
    app: django-web 
spec: 
  selector: 
    matchLabels: 
      name: django 
  template: 
    metadata: 
      labels: 
        name: django 
    spec: 
      containers: 
        - name: django-pod 
          image: 192.168.2.37:30003/ins-env/django:1.1 
          ports: 
          - containerPort: 8000 
          command: ["python3", "/home/testProject/manage.py", "runserver", "0.0.0.0:8000"] 

--- 
apiVersion: v1 
kind: Service 
metadata: 
  name: django-nodeport 
  namespace: ins-env 
spec: 
  ports: 
  - name: django-nodeport 
    port: 8000 
    protocol: TCP 
    targetPort: 8000 
    nodePort: 30080 
  selector: 
    name: django 
  type: NodePort
```

デプロイしていきます。
```
root@k8s-master:~/yaml/ins-env# kubectl apply -f ds-django.yaml 
```

結果を確認します。podがrunningステートになっていれば、DjangoコンテナイメージをHarborからpull出来ている。つまり証明書をcontainerdが認識しているということを示しています。
```
root@k8s-master:~/yaml/ins-env# kubectl get all -n ins-env -o wide 
NAME                  READY   STATUS    RESTARTS   AGE   IP               NODE           NOMINATED NODE   READINESS GATES 
pod/django-rs-2b59h   1/1     Running   0          50s   172.16.39.248    k8s-worker03   <none>           <none> 
pod/django-rs-nmbbg   1/1     Running   0          50s   172.16.93.180    k8s-ceph02     <none>           <none> 
pod/django-rs-s85vw   1/1     Running   0          50s   172.16.69.246    k8s-worker02   <none>           <none> 
pod/django-rs-s87bk   1/1     Running   0          50s   172.16.79.100    k8s-worker01   <none>           <none> 
pod/django-rs-wg6dm   1/1     Running   0          50s   172.16.148.36    k8s-ceph03     <none>           <none> 
pod/django-rs-zfd46   1/1     Running   0          50s   172.16.114.142   k8s-ceph01     <none>           <none> 
NAME                      TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE   SELECTOR 
service/django-nodeport   NodePort   10.96.87.188   <none>        8000:30080/TCP   50s   name=django 
NAME                       DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE   CONTAINERS   IMAGES                                  SELECTOR 
daemonset.apps/django-rs   6         6         6       6            6           <none>          50s   django-pod   192.168.2.37:30003/ins-env/django:1.1   name=django
```

Webブラウザを使ってアクセス出来ることを確認します。Harborを使いkubernetesにpodをデプロイ出来ました。
![2023071206.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/f98ae3d3-10d4-7a85-7358-068e294d8ac8.png)



# ちなみに
「Harborからコンテナイメージをpush/pullするサーバにdockerをインストールしないといけないんだなぁ」と思い、Harbor環境構築時に脳死でdockerをインストールしてしまったのですが、よくよく考えるとkubernetesのコンテナランタイムはcontainerdだから必要ないよな～と思いました。

私の環境だとコンテナを走らせたりHarborにpush/pullするためにk8s-master上のdockerを使い、kubernetes上でpod(コンテナ)を走らせるためにcontainerdを使っているような若干歪な形になってしまっていると思います。
![k8s_3-ページ17.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/18129f00-209d-2efd-63bc-ea1ac423b9e5.png)


また、master nodeでHarbor構築時にインストールしてしまったdockerとcontainerdが走っていますが、それぞれに対して証明書を認識させないとHarborにアクセス出来ない点が注意な気がします。dockerに証明書を認識させたとしてもcontainerdは認識していない点には注意かと思いました。私はそこに気付けず暫くkubernetesのイメージpullでエラーを吐かれ、悩みました。