🐳

Docker でどんなイメージとコンテナかざっくり調べるスクリプトと関連知識

2022/03/17に公開

導入

イメージとかコンテナがどんな内容なのか知りたいことってあるよね?

  • このイメージ M1 Mac で動くのかなー
  • コンテナ起動時に何が起こるのかなー
  • docker execbash 使おうとしたら怒られた、何ならいいんだー
  • Dockerfile 書きたいけど yumapt どっちだー

とかあるよね?

あるある

デバッグしたりチューニングしたり、急に放り込まれた案件に使ってるんだか使ってないんだかわからない Dockerfile が転がってたり

あるある

本題?

そんなわけで、ここに超適当[1]に作った inspect.rb があります

inspect.rb
inspect.rb
require 'json'

def inspect_image(image, arm)
  manifest = JSON.load(`DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose #{image}`)

  platforms = (manifest.instance_of?(Array) ? manifest : [manifest])
                .map { |v| %w[os architecture variant].map { |key| v['Descriptor']['platform'][key] }.compact.join('/') }
                .map { |s| s == 'linux/amd64' ? 'amd64' : s == 'linux/arm64/v8' ? 'arm64' : nil }
                .compact
                .sort

  platform_option = arm && platforms == ['amd64'] ? '--platform=linux/amd64 ' : ''
  pulled = `docker image history #{image} 2>&1`.start_with?('IMAGE')

  unless pulled
    `docker image pull #{platform_option}#{image} > /dev/null 2>&1`
  end

  histories = `docker image history --no-trunc #{image}`.split("\n")

  entrypoint_p = Regexp.new('ENTRYPOINT \[(.*)\]')
  entrypoint = histories
                 .filter { |s| s.include?('ENTRYPOINT') }
                 .map { |s| entrypoint_p.match(s)[1].gsub('"', '').gsub(', ', ' ') }
                 .first || ''

  cmd_p = Regexp.new('CMD \[(.*)\]')
  cmd = histories
          .filter { |s| s.include?('CMD') }
          .map { |s| cmd_p.match(s)[1].gsub('"', '').gsub(', ', ' ') }
          .first || ''

  [platforms, platform_option, pulled, entrypoint, cmd]
end

def inspect_container(image, platform_option)
  container_out = `docker container run #{platform_option}--rm --user root --entrypoint sh #{image} -c 'cat /etc/shells; echo -1-; grep -h -w ID /etc/*-release; echo -2-; ls \`echo $PATH | tr : " "\` 2> /dev/null'`.split("\n")

  shells = container_out.take_while { |s| s != '-1-' }
  shell = shells.filter { |s| s.include?('/bash') }.empty? ? 'sh' : 'bash'

  releases_id = container_out.reverse.take_while { |s| s != '-1-' }.reverse.take_while { |s| s != '-2-' }
  os = releases_id == [] ? 'unknown' : releases_id[0].split('=')[1].gsub(/'/, '').gsub(/"/, '')

  bins = container_out.reverse.take_while { |s| s != '-2-' }
  package_managers = %w[yum dnf microdnf apt apk].filter { |c| bins.include?(c) }

  commands = package_managers.flat_map { |pm|
    case pm
    when 'yum', 'dnf', 'microdnf', 'apt' then
      %w[update install].map { |s| "#{pm} #{s}" }
    when 'apk'
      %w[update add].map { |s| "#{pm} #{s}" }
    end
  }

  [shell, os, commands]
end

for image in ARGV
  platforms, platform_option, pulled, entrypoint, cmd = inspect_image(image, `sysctl machdep.cpu.brand_string`.include?('Apple'))
  shell, os, commands = inspect_container(image, platform_option)

  puts JSON.pretty_generate(
    {
      'image': {
        'name': image,
        'platforms': platforms,
        'entrypoint': entrypoint,
        'cmd': cmd
      },
      'container': {
        'os': os,
        'shell': shell,
        'commands': commands
      },
      'docker': {
        'manifest': "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose #{image}",
        'image': "docker image inspect #{image}",
        'layers': "docker image history --no-trunc #{image}",
        'entry': "docker container run #{platform_option}--rm --user root --interactive --tty --entrypoint=#{shell} #{image}",
        'service': "#{entrypoint} #{cmd}".strip
      },
      'log': {
        'operations': [platform_option == '' ? nil : 'amd64 specified', pulled ? nil : 'image pulled'].compact
      }
    }
  )
end

たとえば node という Docker Hub のイメージについて雑にいろいろ知りたいなーと思ったら、このスクリプトを保存して ruby inspect.rb node ってする

そうするとこんな Json が出てくる

やったね!

node の結果
$ ruby inspect.rb node
{
  "image": {
    "name": "node",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "docker-entrypoint.sh",
    "cmd": "node"
  },
  "container": {
    "os": "debian",
    "shell": "bash",
    "commands": [
      "apt update",
      "apt install"
    ]
  },
  "docker": {
    "inspect": "docker image inspect node",
    "layers": "docker image history --no-trunc node",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash node",
    "service": "docker-entrypoint.sh node"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}

実行例

動作確認でいろいろ叩いたので、雑に貼り付けておくぞ

alpine
alpine の結果
{
  "image": {
    "name": "alpine",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "",
    "cmd": "/bin/sh"
  },
  "container": {
    "os": "alpine",
    "shell": "sh",
    "commands": [
      "apk update",
      "apk add"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose alpine",
    "image": "docker image inspect alpine",
    "layers": "docker image history --no-trunc alpine",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=sh alpine",
    "service": "/bin/sh"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
centos
centos の結果
{
  "image": {
    "name": "centos",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "",
    "cmd": "/bin/bash"
  },
  "container": {
    "os": "centos",
    "shell": "bash",
    "commands": [
      "yum update",
      "yum install",
      "dnf update",
      "dnf install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose centos",
    "image": "docker image inspect centos",
    "layers": "docker image history --no-trunc centos",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash centos",
    "service": "/bin/bash"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
debian
debian の結果
{
  "image": {
    "name": "debian",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "",
    "cmd": "bash"
  },
  "container": {
    "os": "debian",
    "shell": "bash",
    "commands": [
      "apt update",
      "apt install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose debian",
    "image": "docker image inspect debian",
    "layers": "docker image history --no-trunc debian",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash debian",
    "service": "bash"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
fedora
fedora の結果
{
  "image": {
    "name": "fedora",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "",
    "cmd": "/bin/bash"
  },
  "container": {
    "os": "fedora",
    "shell": "bash",
    "commands": [
      "yum update",
      "yum install",
      "dnf update",
      "dnf install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose fedora",
    "image": "docker image inspect fedora",
    "layers": "docker image history --no-trunc fedora",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash fedora",
    "service": "/bin/bash"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
ubuntu
ubuntu の結果
{
  "image": {
    "name": "ubuntu",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "",
    "cmd": "bash"
  },
  "container": {
    "os": "ubuntu",
    "shell": "bash",
    "commands": [
      "apt update",
      "apt install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose ubuntu",
    "image": "docker image inspect ubuntu",
    "layers": "docker image history --no-trunc ubuntu",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash ubuntu",
    "service": "bash"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
oraclelinux:8.5
oraclelinux
{
  "image": {
    "name": "oraclelinux:8.5",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "",
    "cmd": "/bin/bash"
  },
  "container": {
    "os": "ol",
    "shell": "bash",
    "commands": [
      "yum update",
      "yum install",
      "dnf update",
      "dnf install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose oraclelinux:8.5",
    "image": "docker image inspect oraclelinux:8.5",
    "layers": "docker image history --no-trunc oraclelinux:8.5",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash oraclelinux:8.5",
    "service": "/bin/bash"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
bash
bash の結果
{
  "image": {
    "name": "bash",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "docker-entrypoint.sh",
    "cmd": "bash"
  },
  "container": {
    "os": "alpine",
    "shell": "sh",
    "commands": [
      "apk update",
      "apk add"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose bash",
    "image": "docker image inspect bash",
    "layers": "docker image history --no-trunc bash",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=sh bash",
    "service": "docker-entrypoint.sh bash"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
golang
golang の結果
{
  "image": {
    "name": "golang",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "",
    "cmd": "bash"
  },
  "container": {
    "os": "debian",
    "shell": "bash",
    "commands": [
      "apt update",
      "apt install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose golang",
    "image": "docker image inspect golang",
    "layers": "docker image history --no-trunc golang",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash golang",
    "service": "bash"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
rails
rails の結果
{
  "image": {
    "name": "rails",
    "platforms": [
      "amd64"
    ],
    "entrypoint": "",
    "cmd": "irb"
  },
  "container": {
    "os": "debian",
    "shell": "bash",
    "commands": [
      "apt update",
      "apt install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose rails",
    "image": "docker image inspect rails",
    "layers": "docker image history --no-trunc rails",
    "entry": "docker container run --platform=linux/amd64 --rm --user root --interactive --tty --entrypoint=bash rails",
    "service": "irb"
  },
  "log": {
    "operations": [
      "amd64 specified",
      "image pulled"
    ]
  }
}
node
node の結果
{
  "image": {
    "name": "node",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "docker-entrypoint.sh",
    "cmd": "node"
  },
  "container": {
    "os": "debian",
    "shell": "bash",
    "commands": [
      "apt update",
      "apt install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose node",
    "image": "docker image inspect node",
    "layers": "docker image history --no-trunc node",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash node",
    "service": "docker-entrypoint.sh node"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
httpd
httpd の結果
{
  "image": {
    "name": "httpd",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "",
    "cmd": "httpd-foreground"
  },
  "container": {
    "os": "debian",
    "shell": "bash",
    "commands": [
      "apt update",
      "apt install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose httpd",
    "image": "docker image inspect httpd",
    "layers": "docker image history --no-trunc httpd",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash httpd",
    "service": "httpd-foreground"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
nginx
nginx の結果
{
  "image": {
    "name": "nginx",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "/docker-entrypoint.sh",
    "cmd": "nginx -g daemon off;"
  },
  "container": {
    "os": "debian",
    "shell": "bash",
    "commands": [
      "apt update",
      "apt install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose nginx",
    "image": "docker image inspect nginx",
    "layers": "docker image history --no-trunc nginx",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash nginx",
    "service": "/docker-entrypoint.sh nginx -g daemon off;"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
minio/minio
minio/minio の結果
{
  "image": {
    "name": "minio/minio",
    "platforms": [
      "amd64"
    ],
    "entrypoint": "/usr/bin/docker-entrypoint.sh",
    "cmd": "minio"
  },
  "container": {
    "os": "rhel",
    "shell": "bash",
    "commands": [
      "microdnf update",
      "microdnf install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose minio/minio",
    "image": "docker image inspect minio/minio",
    "layers": "docker image history --no-trunc minio/minio",
    "entry": "docker container run --platform=linux/amd64 --rm --user root --interactive --tty --entrypoint=bash minio/minio",
    "service": "/usr/bin/docker-entrypoint.sh minio"
  },
  "log": {
    "operations": [
      "amd64 specified",
      "image pulled"
    ]
  }
}
mailhog/mailhog
mailhog/mailhog の結果
{
  "image": {
    "name": "mailhog/mailhog",
    "platforms": [
      "amd64"
    ],
    "entrypoint": "MailHog",
    "cmd": "/bin/sh"
  },
  "container": {
    "os": "alpine",
    "shell": "sh",
    "commands": [
      "apk update",
      "apk add"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose mailhog/mailhog",
    "image": "docker image inspect mailhog/mailhog",
    "layers": "docker image history --no-trunc mailhog/mailhog",
    "entry": "docker container run --platform=linux/amd64 --rm --user root --interactive --tty --entrypoint=sh mailhog/mailhog",
    "service": "MailHog /bin/sh"
  },
  "log": {
    "operations": [
      "amd64 specified",
      "image pulled"
    ]
  }
}
redis
redis の結果
{
  "image": {
    "name": "redis",
    "platforms": [
      "amd64",
      "arm64"
    ],
    "entrypoint": "docker-entrypoint.sh",
    "cmd": "redis-server"
  },
  "container": {
    "os": "debian",
    "shell": "bash",
    "commands": [
      "apt update",
      "apt install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose redis",
    "image": "docker image inspect redis",
    "layers": "docker image history --no-trunc redis",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash redis",
    "service": "docker-entrypoint.sh redis-server"
  },
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}
jboss/keycloak
jboss/keycloak の結果
{
  "image": {
    "name": "jboss/keycloak",
    "platforms": [
      "amd64"
    ],
    "entrypoint": " /opt/jboss/tools/docker-entrypoint.sh ",
    "cmd": "-b 0.0.0.0"
  },
  "container": {
    "os": "rhel",
    "shell": "bash",
    "commands": [
      "microdnf update",
      "microdnf install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose jboss/keycloak",
    "image": "docker image inspect jboss/keycloak",
    "layers": "docker image history --no-trunc jboss/keycloak",
    "entry": "docker container run --platform=linux/amd64 --rm --user root --interactive --tty --entrypoint=bash jboss/keycloak",
    "service": "/opt/jboss/tools/docker-entrypoint.sh  -b 0.0.0.0"
  },
  "log": {
    "operations": [
      "amd64 specified",
      "image pulled"
    ]
  }
}
registry.access.redhat.com/ubi8/openjdk-11
registry.access.redhat.com/ubi8/openjdk-11 の結果
{
  "image": {
    "name": "registry.access.redhat.com/ubi8/openjdk-11",
    "platforms": [
      "amd64"
    ],
    "entrypoint": "",
    "cmd": ""
  },
  "container": {
    "os": "rhel",
    "shell": "bash",
    "commands": [
      "microdnf update",
      "microdnf install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose registry.access.redhat.com/ubi8/openjdk-11",
    "image": "docker image inspect registry.access.redhat.com/ubi8/openjdk-11",
    "layers": "docker image history --no-trunc registry.access.redhat.com/ubi8/openjdk-11",
    "entry": "docker container run --platform=linux/amd64 --rm --user root --interactive --tty --entrypoint=bash registry.access.redhat.com/ubi8/openjdk-11",
    "service": ""
  },
  "log": {
    "operations": [
      "amd64 specified",
      "image pulled"
    ]
  }
}
redhat/ubi8
redhat/ubi8 の結果
{
  "image": {
    "name": "redhat/ubi8",
    "platforms": [
      "amd64"
    ],
    "entrypoint": "",
    "cmd": ""
  },
  "container": {
    "os": "rhel",
    "shell": "bash",
    "commands": [
      "yum update",
      "yum install",
      "dnf update",
      "dnf install"
    ]
  },
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose redhat/ubi8",
    "image": "docker image inspect redhat/ubi8",
    "layers": "docker image history --no-trunc redhat/ubi8",
    "entry": "docker container run --platform=linux/amd64 --rm --user root --interactive --tty --entrypoint=bash redhat/ubi8",
    "service": ""
  },
  "log": {
    "operations": [
      "amd64 specified",
      "image pulled"
    ]
  }
}

とりあえずなんかすごいわかったきがする!よし!

おまけ?

せっかくなので結果の Json の意味と算出方法などを解説したいと思う

ここから先は退屈だからな?

そして極めてざっくりだからな?

イメージの Platform

ここ ↓ の部分

node の結果 ( 抜粋 )
{
  "image": {
    "platforms": [
      "amd64",
      "arm64"
    ]
  }
}

命令セット ( Instruction Set Architecture )

とても乱暴にいうと CPU に対して指示をする機械語の命令集を命令セット ( ISA ) といい、大抵の Windows や Mac では AMD64 命令セット、M1 の Mac では ARM64 命令セットとなっている

ちなみに x86_64 も AMD64 の仲間で、AArch64 は ARM64 の仲間だ、ざっくりだ

つまりこういうこと

自分のマシン ってことは命令セットは これも仲間
Windows / Intel の Mac AMD64 x86_64
M1 の Mac ARM64 AArch64

Windows や Intel Mac の人は uname -m を使うと確認できる

Intel Mac
$ uname -m
x86_64

M1 Mac でも確認できるけど...

M1 Mac
$ uname -m
arm64

Rosetta2 がいると x86_64 になったりするので、Mac なら CPU ブランドを見る方が確実

Intel Mac
$ sysctl machdep.cpu.brand_string
machdep.cpu.brand_string: Intel(R) Core(TM) i7-7660U CPU @ 2.50GHz
M1 Mac
$ sysctl machdep.cpu.brand_string
machdep.cpu.brand_string: Apple M1

Docker と ISA

Docker のイメージは AMD64 用とか ARM64 用とか、同じ node 指定でも ISA ごとに違うイメージになっていて、勝手にホストマシンの ISA と同じものが選ばれる

んだけど、ARM64 イメージが用意されているかは結構まちまち

たとえば node ってイメージはここら辺を見ると AMD64 イメージも ARM64 イメージもあることがわかる

rails には ARM64 はない

で、この ISA をホストマシンと Docker イメージで揃えないといけない

自分や周囲の人が M1 Mac 使いの場合は、ARM64 イメージが存在するかがとっても気になる

気になるけど Docker Hub で調べるのは面倒だから、docker manifest inspect を使う

Host Machine
$ docker manifest inspect rails

これを jq とかでごにょると Docker Hub と同じ情報がわかる

Dockerfile の ENTRYPOINT と CMD

ここ ↓ の部分

node の結果 ( 抜粋 )
{
  "image": {
    "entrypoint": "docker-entrypoint.sh",
    "cmd": "node"
  }
}

ENTRYPOINT と CMD

Dockerfile は超ざっくりいうとイメージを作るもの

イメージに「お前 docker container run されたらこのコマンドやれや」ってデフォルトの命令を書き込んでおくことで、run させる僕らは複雑な起動コマンドを覚えなくて済むようになっている

で、そのデフォルトの命令ってのが ENTRYPOINTCMD の2パートでできている

命令 ひとこと説明 使い道
ENTRYPOINT docker container run で普通変更しない部分 要するに絶対実行する部分
CMD docker container run で変えられる部分 要するにデフォルト引数

よくあるのは、.sh は必ず実行したい ( ENTRYPOINT ) が、ポートは変えてもいいぞ ( CMD ) みたいな感じ

デフォルトの命令
docker-entrypoint.sh -b 0.0.0.0
--------------------
ENTRYPOINT           ----------
                     CMD

こういうイメージをこう起動 ↓ すると

Host Machine
$ docker container run {image}

コンテナが起動した直後にコンテナ内部で ↓ が実行される

Container
docker-entrypoint.sh -b 0.0.0.0
--------------------
ENTRYPOINT           ----------
                     CMD

けどこう起動 ↓ すると

Host Machine
$ docker container run {image} -b 127.0.0.1

CMD だけ上書きして ↓ が実行される

Container
docker-entrypoint.sh -b 127.0.0.1
--------------------
ENTRYPOINT           ------------
                     run arg

CMDコマンドラインから変えられる って感じだ

ENTRYPOINT の有無

大抵のイメージはこんな感じで bash できる

Host Machine
$ docker container run -it httpd bash

けどたまにできないイメージがあるのは、ENTRYPOINT のせい

Host Machine
$ docker container run -it minio/minio bash
‘bash’ is not a minio sub-command. See ‘minio --help’.

これは内部でこうなっちゃってるからで、そりゃだめよねって感じだ

Container
minio bash
-----
ENTRYPOINT
      ----
      run arg

こういうことがあるので、まぁスクリプト作っちゃうならついでに docker image history の結果をパースしちゃって ENTRYPOINTCMD も調べておこっか的な

Host Machine
$ docker image history rails

今思えば docker image inspect から拾った方が良かったかもしれない...

コンテナの OS

ここ ↓ の部分

node の結果 ( 抜粋 )
{
  "container": {
    "os": "debian"
  }
}

コンテナの OS

まぁ OS が知りたいというよりは多分 aptyum どっち使えばええねんってのが知りたいことの方が多い気がするけど、知って損はしないのでこれも解析しておきたい

/etcxxx-release というファイルがいくつかあって、たとえば redhat-release があれば RedHat とか lsb-release があれば Ubuntu とか絞り込んでいけるんだけど、単純なルールでもないので手で対応表を作るのはやってられない

Host Machine
$ docker container run -it redhat/ubi8 bash
# ls /etc/*-release
/etc/os-release  /etc/redhat-release  /etc/system-release

んでいろいろ見てると ID="ubuntu" みたいな行が /etc/*-release の中にはまず入ってることがわかったので、コンテナを起動してから ↓ んなことをすると十中八九 OS は判断できる

rhel は Red Hat Enterprise Linux だからあってるぽいね

Container
# grep -h -w ID /etc/*-release
ID="rhel"

コンテナで使える Shell

ここ ↓ の部分

node の結果 ( 抜粋 )
{
  "container": {
    "shell": "bash"
  }
}

イメージの軽量化

イメージはデプロイとか docker pull とかを考えると軽い方がいい

軽い方がいいなら bash も入れねぇ

みたいなイメージが割とある

そういうのは ENTRYPOINT に問題がなくても bash すると怒られる、当然だ

Host Machine
$ docker container run -it alpine bash
docker: Error ~~ 略 ~~ exec: "bash": executable file not found in $PATH: unknown.

まぁ怒られたら sh にすりゃいいだけの話なんだけど、スクリプト書くなら調べておいたって (ry

方法は単純に /etc/shells を見れば使える Shell がわかるので、そこに bash が入っているかどうか

Container
# cat /etc/shells
/bin/sh
/bin/ash

コンテナのパッケージマネージャ

ここ ↓ の部分

node の結果 ( 抜粋 )
{
  "container": {
    "commands": [
      "apt update",
      "apt install"
    ]
  }
}

パッケージマネージャは OS でほぼわかるけど

多分結構気になる ( ? ) のがこれ

OS がわかればほぼ絞れるんだけど、スクリプト書くなら (ry

たとえばいくつか例に挙げると、こんな感じで大体決まる

OS ( マジで ) ひとこと OS 解説 パッケージマネージャ
Debian 古参のひとつ apt
Ubuntu Debian から派生したやつ apt
RHEL RedHat 社の有償のやつ yum
CentOS RHEL クローン yum
Alpine 超軽量で Docker でよく見るやつ apk

けどたとえば「yum の後継の dnf を入れてあるぞ」とかで結構ブレるので、OS から類推するよりも実際にパスが通ってるコマンドを全部みた方が確実だよなって感じで力技で調べた

$PATH を適当に空白区切りにして

Container
# echo $PATH | tr : ' '
/usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /sbin /bin

それを ls すりゃパスの通ってるコマンドが全部わかる

Container
# ls `echo $PATH | tr : ' '`
略

そこから yum, dnf, microdnf, apt, apk あたりを探すことにした

まぁ ap<TAB>yum ってコンテナで打てば何が使えるかはわかるんだけどね!

代わりと言っちゃなんだけど、続くサブコマンドが install だか add だかもよく忘れるので適当に表示するようにしておいた
apt update / apk updateapt install / apk add がわかれば大体いいでしょ

pacman とかは Docker じゃああんまりお目にかからんだろうと思って外した
知らんけど

RHEL はなんか仕事ですごい使うので対応した
もっとたくさんの案件でみたときの頻出具合は知らんざます

コマンドいろいろ

ここ ↓ の部分

node の結果 ( 抜粋 )
{
  "docker": {
    "manifest": "DOCKER_CLI_EXPERIMENTAL=enabled; docker manifest inspect --verbose node",
    "image": "docker image inspect node",
    "layers": "docker image history --no-trunc node",
    "entry": "docker container run --rm --user root --interactive --tty --entrypoint=bash node",
    "service": "docker-entrypoint.sh node"
  }
}

ただのガイド

ここら辺は今までの情報の集合

manifest はイメージを調べる Docker のコマンド
「対応 OS/ARCH なにかなー」みたいなときに便利

これは ISA を具体的に指定した1イメージではなく、その名前 ( e.g. node ) について調べている

imagelayers は Dockerfile で何が集積されたか調べる Docker のコマンド
CMD なにかなー」とか「何がインストールされてっかなー」みたいなときに便利

これは docker pull してある ISA が確定したローカルの1イメージを調べている

entry はコンテナをただ Shell で起動する
「 Dockerfile を作る前に手作業したいなー」みたいなときに便利

ENTRYPOINT があることも想定して --entrypoint=bash で Shell を指定
もちろん bash はあることがわかっているから使っている

ARM64 イメージがない場合は適宜 --platform=linux/amd64 とかも付くので、そのまま使える正しいコマンドだ
( ホストマシンとイメージの ISA がずれてたら動くか八卦動かぬも八卦だがな! )

serviceENTRYPOINT + CMD で、つまり entry を叩いてから service を叩くと docker container run と同じ結果になる
docker-entrypoint.sh にデバッグフラグつけて自分で叩きたいなー」みたいなときに便利

そんなことあるかって思うけど、この1年で案外あった
「M1 で動かねー」とか「チューニングしたいなー」とか、なんやかや

ログ

ここ ↓ の部分

node の結果 ( 抜粋 )
{
  "log": {
    "operations": [
      "image pulled"
    ]
  }
}

ただのログ

調べるには docker image pull が必要なので、ローカルになければそれが inspect.rb で実行される
そのときは image pulled って出る

ホストマシンとイメージの ISA がずれてしまう場合は amd64 specified って出る

おわり

大体そんな感じ!

こんだけ知っていればそうそう「???」みたいにはならないぞ!

気は済んだ

脚注
  1. Ruby の開発経験はないし素の Vim で書いた ↩︎

Discussion