DockerfileのRUN命令


お題

DockerfileのRUN命令はコンテナ内で実行されるコマンドを書くんだよというだけの話。
※便宜上、Golangアプリのコンテナ化を事例とする。

開発環境

# OS - Linux(Ubuntu)

$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"

# Dockerコンテナ

Docker

$ sudo docker -v
Docker version 19.03.5, build 633a0ea838

RUN

どこに対して実行されるのか?

Dockerfileは自分の作業PC上で書いている。
Dockerfileは「こういうコンテナを作ってね」という指示書のようなもの。
RUNコマンドは、FROMで記載したベースイメージに対して追加で行っていく処理を表す。
※自分の作業PC上に対する処理ではない。

自分の作業PCが↓のようにあって

$ pwd
/home/sky0621/work/dockerfiles/try01/sub01
$
$ tree
.
├── Dockerfile
└── main.go

Dockerfileが以下であるとき

FROM golang:1.13.6-alpine3.11

RUN go build -o try01sub01
CMD ["./try01sub01"]

自分の作業PC上にmain.goがあるからgo buildできるだろうと勘違いして、docker buildすると以下のように失敗する。

$ sudo docker build . -t try01sub01
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM golang:1.13.6-alpine3.11
 ---> caaf68df625f
Step 2/3 : RUN go build -o try01sub01
 ---> Running in baab70c5fc22
can't load package: package .: no Go files in /go

/goというパスにGolangのファイルがない?

確認してみる。以下のようにDockerfileに追記。

FROM golang:1.13.6-alpine3.11

RUN pwd
RUN ls -l
RUN go build -o try01sub01
CMD ["./try01sub01"]

結果は、

$ sudo docker build . -t try01sub01
Sending build context to Docker daemon  3.072kB
Step 1/5 : FROM golang:1.13.6-alpine3.11
 ---> caaf68df625f
Step 2/5 : RUN pwd
 ---> Running in 7e2456a2a237
/go
Removing intermediate container 7e2456a2a237
 ---> adcba019e471
Step 3/5 : RUN ls -l
 ---> Running in d7489f93d1f5
total 8
drwxrwxrwx    2 root     root          4096 Jan 10 00:25 bin
drwxrwxrwx    2 root     root          4096 Jan 10 00:25 src
Removing intermediate container d7489f93d1f5
 ---> a156c11921aa
Step 4/5 : RUN go build -o try01sub01
 ---> Running in 952b36a0a88b
can't load package: package .: no Go files in /go

RUN pwdの結果が/goになってる。作業PCの場所ではない。
RUN ls -lの結果はsrcbinディレクトリが並んでる。やはり作業PCのカレントディレクトリの内容と違う。
これは、FROM命令に記載したベースイメージ(今回指定したベースイメージのDockerfileの内容はここ参照)の中のパスを表してる。

つまり、RUNはコンテナ内で実行されるコマンドを書くので、go buildしたければ作業PC上のファイルをコンテナ内にコピーする必要がある。
以下のように。

FROM golang:1.13.6-alpine3.11

COPY . .
RUN go build -o try01sub01
CMD ["./try01sub01"]

こうすると、以下のようにビルド成功となる。

$ sudo docker build . -t try01sub01
Sending build context to Docker daemon  3.072kB
Step 1/4 : FROM golang:1.13.6-alpine3.11
 ---> caaf68df625f
Step 2/4 : COPY . .
 ---> 1ca460311aed
Step 3/4 : RUN go build -o try01sub01
 ---> Running in 405644fb8481
Removing intermediate container 405644fb8481
 ---> 56cea12c7d73
Step 4/4 : CMD ["./try01sub01"]
 ---> Running in 1aae16780989
Removing intermediate container 1aae16780989
 ---> 87d3a86afc4e
Successfully built 87d3a86afc4e
Successfully tagged try01sub01:latest

実際にイメージが作られているか確認。

$ sudo docker images
REPOSITORY                                TAG                 IMAGE ID            CREATED             SIZE
try01sub01                                latest              87d3a86afc4e        56 seconds ago      361MB
<none>                                    <none>              a156c11921aa        16 minutes ago      359MB
golang                                    1.13.6-alpine3.11   caaf68df625f        27 hours ago        359MB

動かしてみる。

$ sudo docker run try01sub01
Hello, Dockerfile

ちなみに、main.goの中身は下記。

[main.go]
package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, Dockerfile")
}