はじめに
意気揚々とM1 MacbookAirを購入して「もう1年半くらい経つしほぼIntel製と使い勝手変わらんやろ」と思い、手始めにOpenShiftにコンテナデプロイしようとしたところ、いろんな壁にブチあたりましたのでそれらを回避すべくとった対処法を備忘録としてまとめます。
前提条件
- OpenShift:v4.9 on AWS
- 2020 M1 MacBook Air
- 以下インストール済
- homebrew
- oc
- OpenShiftにアクセスできること
- 以下インストール済
やりたかったこと
- ローカルでコンテナイメージをビルド→デプロイ
- ローカルビルドしたイメージをOpenShift Image RegistryにPush
- OpenShift上にPod/Service/Routeをデプロイ→アクセス確認
IntelCPU(x86-64)/Kubernetes/Dockerなら瞬殺できそうなものですが、今回はM1(arm64)/OpenShift/podmanという環境なので色々工夫が必要そう。
Podmanのインストール
マニュアルによるとOpenShiftデフォルトのImage Registryには、Podmanを使用するような手順になっている。そのためM1 MacにPodmanをインストールします。
以下のサイトを参考にインストール
https://rheb.hatenablog.com/entry/podman-machine#podman-machine%E3%81%AE%E5%AE%9F%E8%A1%8C
$ brew install podman
コンソールを立ち上げ直して以下を実施。podmanの初期セットアップです。
$ podman machine init
Downloading VM image: fedora-coreos-35.20211029.2.0-qemu.aarch64.qcow2.xz: done
Extracting compressed file
PodmanのVMを立ち上げます。
$ podman machine start
INFO[0000] waiting for clients...
INFO[0000] listening tcp://127.0.0.1:7777
INFO[0000] new connection from to /var/folders/pn/gs04f4fd7yj7xtkvw1m_csbm0000gn/T/podman/qemu_podman-machine-default.sock
Waiting for VM ...
Machine "podman-machine-default" started successfully
以下のコマンドで実際に立ち上がっているかわかります。
$ podman machine list
NAME VM TYPE CREATED LAST UP CPUS MEMORY DISK SIZE
podman-machine-default* qemu 31 seconds ago Currently running 1 2.147GB 10.74GB
これでPodmanが使える状態になったつもりです。
コンテナイメージをビルドしてみる
今回はPythonでHello worldを表示する簡単なイメージを作成しました。
まずはapp.pyを作成
$ cat << 'EOF' > app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Hello world'
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0', port=80)
EOF
続いてDockerfileの作成
$ cat << 'EOF' > Dockerfile
FROM python:3.9.6-slim
RUN pip install flask \
&& mkdir /app
COPY app.py /app/
WORKDIR /app
CMD ["python", "app.py"]
EOF
これでイメージをビルドする材料は揃いました。
$ ls
Dockerfile app.py
実際にビルドします。
$ podman build -t my-app:latest .
STEP 1/5: FROM python:3.9.6-slim
Resolved "python" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
...omit...
Successfully tagged localhost/my-app:latest
6dc1643647facb115369f9988fb11fb4a3c1c6e49d0c5b89b867f85d4ffc53f0
無事にビルドできました。余裕ですね。
OpenShift Image RegistryにPushする。。。準備をする
次にOpenShiftのImage Registryにこのイメージを持っていきたいのですが、OpenShiftはBuildConfigによるクラスタ内でのビルドが一般的ですので、IPIインストールの状態のままでは外部からのイメージPushに対応できません。ここらへんがDokcerHubやECR/GCRとちょっと色が違うところですね。
ですので、このImage Registryに外部からログインできるよう設定します。
手順ですが、OpenShiftのマニュアルに記載があります。
https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.9/html/registry/registry-exposing-secure-registry-manually_securing-exposing-registry
手順に則り作業を進めます。
まずはImage Regisry用のRouteとしてDefaultRouteを使用するよう設定します。
$ oc patch configs.imageregistry.operator.openshift.io/cluster --patch '{"spec":{"defaultRoute":true}}' --type=merge
するとProject:openshift-image-registryにdefault-routeがデプロイされます。
$ oc get route -n openshift-image-registry
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
default-route default-route-openshift-image-registry.apps.democluster.XXX.XXX.XXX.com image-registry <all> reencrypt None
次にPodmanでImage Registryにログインします。
$ HOST=$(oc get route default-route -n openshift-image-registry --template='{{ .spec.host }}')
$ podman login -u kubeadmin -p $(oc whoami -t) --tls-verify=false $HOST
Login Succeeded!
これでM1 Mac上からOpenShift Image RegistryにイメージをPushする準備が整いました。
OpenShift Image RegistryにPushする!
一通り準備できたので実際にPushします。
まずは先ほどのイメージのtagを付け替えます。
対象のイメージ名を確認
$ podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/my-app latest 6dc1643647fa 16 minutes ago 132 MB
docker.io/library/python 3.9.6-slim 65db2d7dbed0 4 months ago 121 MB
tagの命名規則ですが、<default-routeのHOST>/<Openshift上のProject名>/<iamge名>:latest
のように付与します。以下のコマンドではocp-demo
Project内のイメージとしてpushしています。
$ podman tag localhost/my-app:latest default-route-openshift-image-registry.apps.democluster.XXX.XXX.XXX.com/ocp-demo/my-app:latest
そしてPush!
※今回は証明書を設定してないので、オプションに--tls-verify=false
を設定
$ podman push default-route-openshift-image-registry.apps.democluster.XXX.XXX.XXX.com/ocp-demo/my-app:latest --tls-verify=false
なんのコンソールログも出ませんが、これでPushは成功です。
ためしにOpenShift上でイメージを確認しましょう。
$ oc get imagestream | grep my-app
my-app default-route-openshift-image-registry.apps.democluster.XXXX.XXXX.XXXX.com/ocp-demo/my-app latest About an hour ago
OpenShift上にデプロイする
これでイメージのBuild→Pushができたので、あとはDeployです。
今回は簡単にPodとService、そしてMacのブラウザからアクセスできるようRouteを作成します。
まずはPodのyamlを作成
app-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: flask
labels:
app: flask
spec:
containers:
- name: flask
image: image-registry.openshift-image-registry.svc:5000/ocp-demo/my-app:latest
ports:
- containerPort: 80
imageですが、OpenShift上からImage Registryにアクセスする場合は内部アクセスになるため、image-registry.openshift-image-registry.svc:5000/(パス/)イメージ名:バージョン
でイメージpullできます。
次にserviceのyamlを作成
apiVersion: v1
kind: Service
metadata:
name: flask
spec:
type: ClusterIP
selector:
app: flask
ports:
- protocol: TCP
port: 80
targetPort: 80
name: http
デプロイしてみます。
$ oc new-project ocp-demo
$ oc apply -f .
pod/flask created
service/flask created
Podの状態を見てみましょう
$ oc get pods
NAME READY STATUS RESTARTS AGE
flask 0/1 CrashLoopBackOff 2 (17s ago) 36s
うまくいってない。。。
とりあえず原因を探ります。
$ oc describe pod flask
...omit...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 83s default-scheduler Successfully assigned ocp-demo/flask to ip-10-0-136-247.ap-northeast-1.compute.internal
Normal AddedInterface 81s multus Add eth0 [10.129.0.27/23] from openshift-sdn
Normal Pulled 80s kubelet Successfully pulled image "image-registry.openshift-image-registry.svc:5000/ocp-demo/my-app-test:latest" in 532.909698ms
Normal Pulled 80s kubelet Successfully pulled image "image-registry.openshift-image-registry.svc:5000/ocp-demo/my-app-test:latest" in 71.807742ms
Normal Pulled 65s kubelet Successfully pulled image "image-registry.openshift-image-registry.svc:5000/ocp-demo/my-app-test:latest" in 142.873945ms
Normal Pulling 34s (x4 over 81s) kubelet Pulling image "image-registry.openshift-image-registry.svc:5000/ocp-demo/my-app-test:latest"
Normal Pulled 34s kubelet Successfully pulled image "image-registry.openshift-image-registry.svc:5000/ocp-demo/my-app-test:latest" in 130.168857ms
Normal Created 33s (x4 over 80s) kubelet Created container flask
Normal Started 33s (x4 over 80s) kubelet Started container flask
Warning BackOff 6s (x7 over 79s) kubelet Back-off restarting failed container
$ oc logs flask
standard_init_linux.go:228: exec user process caused: exec format error
コンテナ内でエラーを吐いているようです。
「なんだこのエラー?」と思ってググるとこんな記事がありました。
https://qiita.com/keita_ogawa/items/e115c46f1c8caf6fd34d
どうやらイメージビルドした環境とコンテナ実行環境のCPUアーキテクチャが異なるために発生しているみたいです。
解決策はDockerの際のものだったので、Podmanのコマンドを調べます。
こちらにpodman build
のオプションが記載されています。
https://docs.podman.io/en/latest/markdown/podman-build.1.html
Dockerと同様--platform
をつければいけそうですが、<OS>/<Arch>
のような書き方が必要で、その組み合わせはgoの環境変数を使うとのこと。以下のURLで調べます。
https://golang.org/doc/install/source#environment
今回はlinux/amd64
が該当します。
では改めて、--platform
オプションをつけてビルドしてみます。
$ podman build --platform linux/amd64 -t my-app:v1.1 .
STEP 1/5: FROM python:3.9.6-slim
Resolved "python" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull docker.io/library/python:3.9.6-slim...
Getting image source signatures
Copying blob sha256:1374279231a56d30ab37ec576d0d9f339d627fdaa5e5b99a1a1c6216234e6731
Copying blob sha256:99046ad9247f8a1cbd1048d9099d026191ad9cda63c08aadeb704b7000a51717
Copying blob sha256:dae61f727682a7d9dbb8974480f2674f80187673a550423d383930a4e80cf237
Copying blob sha256:466485ee627754b8f2ee4a302965fdea56ac3b2ed509b4f54e9796ab4162e837
Copying blob sha256:de739b0566731810358ff60001048daf2672b9c3a9125a3bb2fa65165f26a8eb
Copying blob sha256:466485ee627754b8f2ee4a302965fdea56ac3b2ed509b4f54e9796ab4162e837
Copying blob sha256:99046ad9247f8a1cbd1048d9099d026191ad9cda63c08aadeb704b7000a51717
Copying blob sha256:1374279231a56d30ab37ec576d0d9f339d627fdaa5e5b99a1a1c6216234e6731
Copying blob sha256:dae61f727682a7d9dbb8974480f2674f80187673a550423d383930a4e80cf237
Copying blob sha256:de739b0566731810358ff60001048daf2672b9c3a9125a3bb2fa65165f26a8eb
Copying config sha256:c6e50ab8d4256a0d25bb6246d77a61d5f7407b898278f97c89cf98ef91fd2292
Writing manifest to image destination
Storing signatures
STEP 2/5: RUN pip install flask && mkdir /app
exec container process `/bin/sh`: Exec format error
Error: error building at STEP "RUN pip install flask && mkdir /app": error while running runtime: exit status 1
うまくいかないじゃん。
エラーを見るとなんだかさっきと似たような事象っぽいです。
内容を頼りに色々調べると、こんなissueが。
https://github.com/containers/podman/issues/12144
PodmanのVMにqemu-user-staticをインストールして複数のアーキテクチャに対応できる環境を作ろうってことですね。
$ podman machine ssh sudo rpm-ostree install qemu-user-static
Warning: Permanently added '[localhost]:53022' (ED25519) to the list of known hosts.
Checking out tree 68a745f...done
...omit...
Changes queued for next boot. Run "systemctl reboot" to start a reboot
そして再起動
$ podman machine ssh sudo systemctl reboot
この状態で先ほどのビルドコマンドを実行してみます。
$ podman build --platform linux/amd64 -t my-app:v1.1 .
STEP 1/5: FROM python:3.9.6-slim
Resolved "python" as an alias
...omit...
Successfully tagged localhost/my-app:v1.1
f7a36870966d7a0a1dba7348b6ca03c0855eaf32e7376259221ca4619481f028
ビルド通った!
早速OpneShift上で動かしてみましょう。
タグ付け→Push
$ podman tag localhost/my-app:v1.1 default-route-openshift-image-registry.apps.democluster.XXXX.XXXX.XXXX.com/ocp-demo/my-app:v1.1
$ podman push default-route-openshift-image-registry.apps.democluster.XXXX.XXXX.XXXX.com/ocp-demo/my-app:v1.1 --tls-verify=false
v1.1をつけたので、app-pod.yamlのimageを書き換えます。
apiVersion: v1
kind: Pod
metadata:
name: flask
labels:
app: flask
spec:
containers:
- name: flask
image: image-registry.openshift-image-registry.svc:5000/ocp-demo/my-app:v1.1
ports:
- containerPort: 80
エラーを繰り返しているPodを削除して、再作成!
$ oc delete pod flask
pod "flask" deleted
$ oc apply -f app-pod.yaml
pod/flask created
確認してみます
$ oc get pods
NAME READY STATUS RESTARTS AGE
flask 1/1 Running 0 24s
動いたーーー!!
ですがまだコンテナが動いただけなので、正常にアクセスできるかわかりません。
なので、routeを作成してMacのブラウザからアクセスできるか確認します。
以下のコマンドでrouteを作成
$ oc expose service flask
しばらくするとrouteが作成され、HOSTが表示されます。
$ oc get route
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
flask flask-ocp-demo.apps.democluster.XXXX.XXXX.XXXX.com flask http None
このHOSTにhttp://
をつけてブラウザでアクセスすると
ということで、やっとデプロイすることができました。。。!
おわりに
これまでCPUのアーキテクチャを気にしてビルド・デプロイを行なってこなかったので、いい勉強になりました。
これからもまだまだ苦労はありそうですが、なんとかM1 Mac上でOpenShiftの開発環境を整備できるよう対応していきます。
最後までお読みいただきありがとうございました。