Skip to content

Commit

Permalink
Merge pull request #247 from yeasy/dev
Browse files Browse the repository at this point in the history
Add multistage builds
  • Loading branch information
khs1994 authored Nov 25, 2017
2 parents addf6b9 + 766f301 commit 7022c5a
Show file tree
Hide file tree
Showing 12 changed files with 317 additions and 24 deletions.
1 change: 1 addition & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
* [HEALTHCHECK 健康检查](image/dockerfile/healthcheck.md)
* [ONBUILD 为他人作嫁衣裳](image/dockerfile/onbuild.md)
* [参考文档](image/dockerfile/references.md)
* [Dockerfile 多阶段构建](image/multistage-builds.md)
* [其它制作镜像的方式](image/other.md)
* [删除本地镜像](image/rmi.md)
* [实现原理](image/internal.md)
Expand Down
8 changes: 4 additions & 4 deletions container/daemon.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## 后台(background)运行
## 后台运行

更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 `-d` 参数来实现。

Expand All @@ -7,7 +7,7 @@
如果不使用 `-d` 参数运行容器。

```bash
$ docker run ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
$ docker run ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
hello world
hello world
hello world
Expand All @@ -19,7 +19,7 @@ hello world
如果使用了 `-d` 参数运行容器。

```bash
$ docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
$ docker run -d ubuntu:17.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a
```

Expand All @@ -32,7 +32,7 @@ $ docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep
```bash
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
77b2dc01fe0f ubuntu:14.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright
77b2dc01fe0f ubuntu:17.10 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright
```
要获取容器的输出信息,可以通过 `docker logs` 命令。
Expand Down
1 change: 1 addition & 0 deletions image/demo/multistage-builds/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
app
21 changes: 21 additions & 0 deletions image/demo/multistage-builds/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM golang:1.9-alpine

RUN apk --no-cache add git

WORKDIR /go/src/github.com/go/helloworld/

RUN go get -d -v github.com/go-sql-driver/mysql

COPY app.go .

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest

RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY --from=0 /go/src/github.com/go/helloworld/app .

CMD ["./app"]
10 changes: 10 additions & 0 deletions image/demo/multistage-builds/Dockerfile.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM golang:1.9-alpine

RUN apk --no-cache add git

WORKDIR /go/src/github.com/go/helloworld

COPY app.go .

RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
9 changes: 9 additions & 0 deletions image/demo/multistage-builds/Dockerfile.copy
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM alpine:latest

RUN apk --no-cache add ca-certificates

WORKDIR /root/

COPY app .

CMD ["./app"]
15 changes: 15 additions & 0 deletions image/demo/multistage-builds/Dockerfile.one
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM golang:1.9-alpine

RUN apk --no-cache add git ca-certificates

WORKDIR /go/src/github.com/go/helloworld/

COPY app.go .

RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
&& cp /go/src/github.com/go/helloworld/app /root

WORKDIR /root/

CMD ["./app"]
7 changes: 7 additions & 0 deletions image/demo/multistage-builds/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package main

import "fmt"

func main(){
fmt.Printf("Hello World!");
}
14 changes: 14 additions & 0 deletions image/demo/multistage-builds/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/sh

echo Building go/helloworld:build

docker build -t go/helloworld:build . -f Dockerfile.build

docker create --name extract go/helloworld:build
docker cp extract:/go/src/github.com/go/helloworld/app ./app
docker rm -f extract

echo Building go/helloworld:2

docker build --no-cache -t go/helloworld:2 . -f Dockerfile.copy
rm ./app
176 changes: 176 additions & 0 deletions image/multistage-builds.md
Original file line number Diff line number Diff line change
@@ -1 +1,177 @@
## 多阶段构建

### 之前的做法

在 Docker 17.05 版本之前,我们构建 Docker 镜像时,通常会采用两种方式:

#### 全部放入一个 Dockerfile

一种方式是将所有的构建过程编包含在一个 `Dockerfile` 中,包括项目及其依赖库的编译、测试、打包等流程,这里可能会带来的一些问题:

* `Dockerfile` 特别长,可维护性降低

* 镜像层次多,镜像体积较大,部署时间变长

* 源代码存在泄露的风险

例如

编写 `app.go` 文件,该程序输出 `Hello World!`

```go
package main

import "fmt"

func main(){
fmt.Printf("Hello World!");
}
```

编写 `Dockerfile.one` 文件

```docker
FROM golang:1.9-alpine
RUN apk --no-cache add git ca-certificates
WORKDIR /go/src/github.com/go/helloworld/
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
&& cp /go/src/github.com/go/helloworld/app /root
WORKDIR /root/
CMD ["./app"]
```

构建镜像

```bash
$ docker build -t go/helloworld:1 -f Dockerfile.one .
```

#### 分散到多个 Dockerfile

另一种方式,就是我们事先在一个 `Dockerfile` 将项目及其依赖库编译测试打包好后,再将其拷贝到运行环境中,这种方式需要我们编写两个 `Dockerfile` 和一些编译脚本才能将其两个阶段自动整合起来,这种方式虽然可以很好地规避第一种方式存在的风险,但明显部署过程较复杂。

例如

编写 `Dockerfile.build` 文件

```docker
FROM golang:1.9-alpine
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
```

编写 `Dockerfile.copy` 文件

```docker
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
```

新建 `build.sh`

```bash
#!/bin/sh
echo Building go/helloworld:build

docker build -t go/helloworld:build . -f Dockerfile.build

docker create --name extract go/helloworld:build
docker cp extract:/go/src/github.com/go/helloworld/app ./app
docker rm -f extract

echo Building go/helloworld:2

docker build --no-cache -t go/helloworld:2 . -f Dockerfile.copy
rm ./app
```

现在运行脚本即可构建镜像

```bash
$ chmod +x build.sh

$ ./build.sh
```

对比两种方式生成的镜像大小

```bash
$ docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
```

### 使用多阶段构建

为解决以上问题,Docker v17.05 开始支持多阶段构建 (`multistage builds`)。使用多阶段构建我们就可以很容易解决前面提到的问题,并且只需要编写一个 `Dockerfile`

例如

编写 `Dockerfile` 文件

```docker
FROM golang:1.9-alpine
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]
```

构建镜像

```bash
$ docker build -t go/helloworld:3 .
```

对比三个镜像大小

```bash
$ docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 3 d6911ed9c846 7 seconds ago 6.47MB
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
```

很明显使用多阶段构建的镜像体积小,同时也完美解决了上边提到的问题。
Loading

0 comments on commit 7022c5a

Please sign in to comment.