Docker Desktopでk8sとcompose V2
Docker Desktop on Macでk8s機能とDocker Compose V2機能を利用してみる。
macOS Big Sur: 11.6.5
Docker Desktop: 4.7.1 (77678)
kubernetes機能検証は基本的には公式ドキュメントに従う。
最新のDocker Desktopでは k8s v1.22.5がサポートされている。
これは、kubernetes release pageやGitHubリポジトリによるとマイナーバージョンはサポートされている(1.22は2022-10-28がEOL)が最新ではなく(1.23.0が2021-12-08にリリース)、パッチバージョンも最新ではない(1.22.9が2022-04-21にリリース済)。
Docker Desktopから機能を有効化するとkubectlコマンドがインストールされて利用できるようになる。
$ which kubectl
/usr/local/bin/kubectl
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.5", GitCommit:"5c99e2ac2ff9a3c549d9ca665e7bc05a3e18f07e", GitTreeState:"clean", BuildDate:"2021-12-16T08:38:33Z", GoVersion:"go1.16.12", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.5", GitCommit:"5c99e2ac2ff9a3c549d9ca665e7bc05a3e18f07e", GitTreeState:"clean", BuildDate:"2021-12-16T08:32:32Z", GoVersion:"go1.16.12", Compiler:"gc", Platform:"linux/amd64"}
また、 contextが docker-desktop
に向いていること、ノードが参照できることが確認できる。
$ kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* docker-desktop docker-desktop docker-desktop
$ kubectl get node
NAME STATUS ROLES AGE VERSION
docker-desktop Ready control-plane,master 19m v1.22.5
k8s上へのデプロイはk8s ドキュメントに従い確認する。
まずはpodのデプロイ。
Podのページに従いデプロイする。
$ cat pod.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: hello
spec:
template:
# これがPodテンプレートです
spec:
containers:
- name: hello
image: busybox
command: ['sh', '-c', 'echo "Hello, Kubernetes!" && sleep 3600']
restartPolicy: OnFailure
# Podテンプレートはここまでです
$ kubectl apply -f pod.yaml
job.batch/hello created
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
hello--1-wchm4 1/1 Running 0 3m2s
$ kubectl logs hello--1-wchm4
Hello, Kubernetes!
ちなみに、k8s上にデプロイしたpod(コンテナ)は docker ps
コマンド等でも確認できる。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6264ff18b2d3 busybox "sh -c 'echo \"Hello,…" 5 minutes ago Up 5 minutes k8s_hello_hello--1-wchm4_default_2132fc5f-45e6-49df-b4de-45bab636f100_0
$ docker logs k8s_hello_hello--1-wchm4_default_2132fc5f-45e6-49df-b4de-45bab636f100_0
Hello, Kubernetes!
続いてDeployment。
$ cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
$ kubectl apply -f deployment.yaml
deployment.apps/nginx-deployment created
こちらも問題なく動作することを確認。
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 2m52s
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-66b6c48dd5 3 3 3 3m14s
thaim@tadegg:~/work/intro-docker-k8s
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
hello--1-wchm4 1/1 Running 0 12m
nginx-deployment-66b6c48dd5-n6xkh 1/1 Running 0 3m29s
nginx-deployment-66b6c48dd5-sf5b5 1/1 Running 0 3m29s
nginx-deployment-66b6c48dd5-tgr54 1/1 Running 0 3m29s
続いてdeploymentの更新。
nginx を 1.16.1にアップデートしてデプロイする。こちらも問題なくアップデートされている。
$ sed -i -e "s/nginx:1.14.2/nginx:1.16.1/" deployment.yaml
$ kubectl apply -f deployment.yaml
deployment.apps/nginx-deployment configured
$ kubectl rollout status deployment.v1.apps/nginx-deployment
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "nginx-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "nginx-deployment" successfully rolled out
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 10m
$ kubectl get rs
NAME DESIRED CURRENT READY AGE
nginx-deployment-559d658b74 3 3 3 66s
nginx-deployment-66b6c48dd5 0 0 0 10m
$ kubectl describe deployments
...
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.16.1
Port: 80/TCP
...
続いてDocker Desktopのk8sでは上手くいかない例として、マルチノードにpodをデプロイするdeploymentを作成する。
スケジューリングの同じNodeに共存させない場合を参考に、同一ホストでは同じpodを稼動させないように設定する。
この設定でapplyすると、podAntiAffinityの制約により複数podが起動しない。これは意図した通り。
$ cat multinode.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-server
spec:
selector:
matchLabels:
app: web-store
replicas: 3
template:
metadata:
labels:
app: web-store
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- web-store
topologyKey: "kubernetes.io/hostname"
containers:
- name: web-app
image: nginx:1.16-alpine
$ kubectl apply -f multinode.yaml
deployment.apps/web-server created
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 3/3 3 3 49m
web-server 1/3 3 1 5m38s
$ kubectl describe -f multinode.yaml
Name: web-server
Namespace: default
CreationTimestamp: Wed, 04 May 2022 19:19:29 +0900
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=web-store
Replicas: 3 desired | 3 updated | 3 total | 1 available | 2 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=web-store
Containers:
web-app:
Image: nginx:1.16-alpine
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available False MinimumReplicasUnavailable
Progressing True ReplicaSetUpdated
OldReplicaSets: <none>
NewReplicaSet: web-server-778bd9d76d (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 5m41s deployment-controller Scaled up replica set web-server-778bd9d76d to 3
最後にserviceをデプロイしてpodにアクセスできるようにする。
以下のmanifest の通りtype: LoadBalaner
を利用してデプロイすることで、http://localhost にアクセスすればいつものnginx画面にアクセスできる。
$ cat service.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
type: LoadBalancer
$ kubectl apply -f service.yaml
service/webapp created
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 125m
webapp LoadBalancer 10.102.75.174 localhost 80:31441/TCP 15s
$ curl localhost
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
続いてDocker Compose V2について。
リリースノートの通り、Docker Desktop 4.7.1では、docker v20.10.14 および Compose v2.4.1 が利用できる。
$ docker version
Client:
Cloud integration: v1.0.23
Version: 20.10.14
API version: 1.41
Go version: go1.16.15
Git commit: a224086
Built: Thu Mar 24 01:49:20 2022
OS/Arch: darwin/amd64
Context: default
Experimental: true
Server: Docker Desktop 4.7.1 (77678)
Engine:
Version: 20.10.14
API version: 1.41 (minimum version 1.12)
Go version: go1.16.15
Git commit: 87a90dc
Built: Thu Mar 24 01:46:14 2022
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.5.11
GitCommit: 3df54a852345ae127d1fa3092b95168e4a88e2f8
runc:
Version: 1.0.3
GitCommit: v1.0.3-0-gf46b6ba
docker-init:
Version: 0.19.0
GitCommit: de40ad0
$ docker compose version
Docker Compose version v2.4.1
Compose V2の仕様変更については Docker Compose V2で変わったdocker-compose.ymlの書き方 がわかりやすい。
この記事に加えて、Compose Specification と Overview of Docker Composeを参照して確認する。
Docker ComposeがCompose Specのリファレンス実装だと述べられているので基本的には両者の仕様は一致するはず?
ただし、dockerのドキュメントを見てもdocker-composeの記載がまだ残っているのでドキュメント含めたCompose V2対応は未完了っぽい。
Composeファイルの名前
ファイル名として docker-compose.yml
などより compose.yaml
が優先されるが、もしデフォルトで対応する4つのファイルのうちいずれか複数が存在する場合は警告を出してくれる。
以下では 4つのファイルが存在する場合の実行結果で、4つのファイルを検出したこと、最終的には compose.yaml
が選択されたことが警告されている。
一方で、 compose.yaml
の替わりに compose.yml
や docker-compose.yml
のみを配置・利用した場合には警告等は表示されない。
$ docker compose ps
WARN[0000] Found multiple config files with supported names: /Users/thaim/work/compose-v2/compose.yaml, /Users/thaim/work/compose-v2/compose.yml, /Users/thaim/work/compose-v2/docker-compose.yml, /Users/thaim/work/compose-v2/docker-compose.yaml
WARN[0000] Using /Users/thaim/work/compose-v2/compose.yaml
NAME COMMAND SERVICE STATUS PORTS
compose-v2-redis-1 "docker-entrypoint.s…" redis running 6379/tcp
compose-v2-web-1 "flask run" web running 0.0.0.0:8000->5000/tcp
version
versionの項目は無視されるので、Compose V1(従来のdocker-compose)ではエラーになる version: "10.0"
や version: "latest"
の指定があっても警告もなく無視される。
$ head -1 compose.yaml
version: "latest"
$ docker compose ps
NAME COMMAND SERVICE STATUS PORTS
compose-v2-redis-1 "docker-entrypoint.s…" redis running 6379/tcp
compose-v2-web-1 "flask run" web running 0.0.0.0:8000->5000/tcp
compose.yaml
でversionが指定できなくなり、替わりにdocker compose CLIは常に最新のCompose Specバージョンとして扱わなければならず(but prefer the most recent schema at the time it has been designed
)、意図的に古いCompose Specバージョンを指定できなくなった。docker compose CLIが扱うCompose Specよりも新しいバージョンのcompose.yamlが渡された場合など、未定義の項目が存在する場合は警告を出すべき(SHOULD)であり、実行モードを指定してエラー扱いにしたり無視したりしてもよい(MAY)となっている (Requirements and optional attributes)。
Dockerでは実行モードを切り替えるオプションは見当たらず、未定義のCompose Specを指定するとエラーになるのでstrictモードで動作しているように見える。これはCompose Specの仕様とは合致しない動作だがよいのだろうか?
$ head -1 compose.yaml
myspecversion: "1.0"
$ docker compose ps
(root) Additional property myspecversion is not allowed
configs
おそらく、AWS ECS FireLensにおける設定ファイルのS3マウントみたいな事がしたいのだと思う。
Docker Desktopでの挙動について、マウントしたファイルモードは書き込み権限は無視される (Writable bit MUST be ignored
)とあるが、実際には以下の動作であることがわかった。これが意図した挙動なのか、不具合なのか、外部のプラットフォームではどうなるかは不明。
- 表示はホスト上の権限(ファイルモード)1と同じ
- composeで指定のmodeやホストのファイルモードによらず、ファイル更新は不可
- ファイルの実行権限はホストのファイルモード依存、composeのmode指定は無視される(ホストで実行権限を付与するとコンテナ上でも実行できる)
$ cat compose.yaml
services:
web:
build: .
configs:
- source: my_config
target: /my_config.txt
mode: 0555
ports:
- "8000:5000"
redis:
image: "redis:alpine"
configs:
my_config:
file: ./my_config.txt
$ ls -al my_config.txt
-rw-r--r-- 1 thaim staff 11 5 5 14:12 my_config.txt
$ docker compose exec web sh
/code # ls -al /my_config
-rw-r--r-- 1 root root 11 May 5 05:12 my_config.txt
$ docker compose exec web sh
/code # echo test >> /my_config.txt
sh: can't create /my_config.txt: Read-only file system
/code # /my_config.txt
sh: /my_config.txt: Permission denied
extends
extendsの元となるサービスは別ファイルを利用することもできる。
その場合はfileで指定する。このような構成の場合、 docker compose up -d
しても起動するのは webとmyredisだけで、myredisが参照するredisは起動しない。これを利用すれば共通設定だけを定義するcommonサービスを別ファイルに定義して利用する(commonは起動しない)こともできる。
services:
web:
build: .
configs:
- source: my_config
target: /my_config.txt
uid: "0"
gid: "0"
mode: 0555
ports:
- "8000:5000"
myredis:
extends:
file: base.yaml
service: redis
environment:
TZ: utc
configs:
my_config:
file: ./my_config.txt
services:
redis:
image: "redis:alpine"
その他、depends_on
のような依存関係を定義したserviceをextends元には利用できない、循環参照は定義できない、fileは相対パスでも絶対パスでも未定義でもよい、extendsするときに上書きするのか追記するのか、といった補足もあり、このあたりは実際に利用するときに検証するなど必要。