1、编写go-demo项目
hello目录有main.go和go.mod文件
main.go文件:
package main
import (
"net/http"
"fmt"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
})
fmt.Println("start server: http://127.0.0.1:8000")
http.ListenAndServe("0.0.0.0:8000", nil)
}
go.mod文件:
module hello
go 1.21.6
编译成可执行文件:
# 修改go环境变量CGO_ENABLED设置为0,默认为1
# 详见:https://zhuanlan.zhihu.com/p/576103234
go env -w CGO_ENABLED='0'
# 编译项目
go build -o app main.go
查看文件:
2、构建镜像并运行
编写Dockerfile文件:
FROM scratch
COPY ./app /app
EXPOSE 8000
CMD ["/app"]
构建镜像:
docker build -t app:v1 .
查看镜像:
运行容器:
docker run --rm -p 8000:8000 app:v1
curl访问:
curl http://127.0.0.1:8000
参考:
https://docs.docker.com/build/building/base-images/
https://docs.docker.com/build/building/multi-stage/
3、分析docker文档的Dockerfile文件
源文件地址:docs/Dockerfile at main · docker/docs · GitHub
3.1、定义参数
# syntax=docker/dockerfile-upstream:master
# check=skip=InvalidBaseImagePlatform
# ALPINE_VERSION sets the Alpine Linux version for all Alpine stages
ARG ALPINE_VERSION=3.20
# GO_VERSION sets the Go version for the base stage
ARG GO_VERSION=1.22
# HTML_TEST_VERSION sets the wjdp/htmltest version for HTML testing
ARG HTMLTEST_VERSION=0.17.0
alpine镜像版本:ALPINE_VERSION=3.20
go版本:GO_VERSION=1.22
htmltest版本:HTMLTEST_VERSION=0.17.0
3.2、base阶段:基于golang:1.22镜像构建
# base is the base stage with build dependencies
FROM golang:${GO_VERSION}-alpine AS base
WORKDIR /src
RUN apk --update add nodejs npm git gcompat
工作目录:/src
apk --update add:命令用于安装一个或多个软件包,并在安装前更新软件包列表
安装或更新 nodejs、npm、git、gcompat软件
3.3、node阶段:基于base阶段构建
# node installs Node.js dependencies
FROM base AS node
COPY package*.json .
ENV NODE_ENV=production
RUN npm install
复制当前Dockerfile所在宿主机的package.json等文件 到 当前镜像的/src目录
设置环境变量NODE_ENV
npm install命令:根据package.json,安装依赖下载到node_modules目录
3.4、hugo阶段:基于base阶段构建
# hugo downloads and extracts the Hugo binary
FROM base AS hugo
ARG HUGO_VERSION=0.132.0
ARG TARGETARCH
WORKDIR /tmp/hugo
RUN wget -O "hugo.tar.gz" "https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-${TARGETARCH}.tar.gz"
RUN tar -xf "hugo.tar.gz" hugo
hugo版本:HUGO_VERSION=0.132.0
工作目录:/tmp/hugo
使用wget下载hugo安装包,并使用tar命令解压
3.5、build-base阶段:基于base阶段构建
# build-base is the base stage for building the site
FROM base AS build-base
COPY --from=hugo /tmp/hugo/hugo /bin/hugo
COPY --from=node /src/node_modules /src/node_modules
COPY . .
复制hugo阶段的/tmp/hugo/hugo文件 到 当前镜像的/bin/hugo
复制node阶段的/src/node_modules文件 到 当前镜像的/src/node_modules
复制当前Dockerfile所在宿主机的当前文件 到 当前镜像的/src目录
3.6、dev阶段:基于build-base阶段构建
# dev is for local development with Docker Compose
FROM build-base AS dev
3.7、build阶段:基于build-base阶段构建
# build creates production builds with Hugo
FROM build-base AS build
# HUGO_ENV sets the hugo.Environment (production, development, preview)
ARG HUGO_ENV
# DOCS_URL sets the base URL for the site
ARG DOCS_URL
RUN hugo --gc --minify -d /out -e $HUGO_ENV -b $DOCS_URL
生成html文件:hugo --gc --minify -d /out -e $HUGO_ENV -b $DOCS_URL
hugo命令部分参数说明:
--gc:enable to run some cleanup tasks (remove unused cache files) after the build
--minify:minify any supported output format (HTML, XML etc.)
-d, --destination string:filesystem path to write files to
-e, --environment string:build environment
-b, --baseURL string:hostname (and path) to the root, e.g. https://spf13.com/
3.8、lint阶段:基于davidanson/markdownlint-cli2镜像构建
# lint lints markdown files
FROM davidanson/markdownlint-cli2:v0.13.0 AS lint
USER root
RUN --mount=type=bind,target=. \
/usr/local/bin/markdownlint-cli2 \
"content/**/*.md" \
"#content/engine/release-notes/*.md" \
"#content/desktop/previous-versions/*.md"
--mount=type=bind,from=构建阶段或镜像名称,source=源路径,target=目标路径
https://blog.51cto.com/gugu/6085739
3.9、test阶段:基于htmltest:0.17.0镜像构建
# test validates HTML output and checks for broken links
FROM wjdp/htmltest:v${HTMLTEST_VERSION} AS test
WORKDIR /test
COPY --from=build /out ./public
ADD .htmltest.yml .htmltest.yml
RUN htmltest
wjdp/htmltest:测试生成的HTML是否存在问题
工作目录:/test
复制build阶段的/out目录 到 当前镜像的/test/public目录
复制当前Dockerfile所在宿主机的.htmltest.yml文件 到 当前镜像的/test/.htmltest.yml
GitHub - wjdp/htmltest: :white_check_mark: Test generated HTML for problems
3.10、update-modules阶段:基于build-base阶段构建
# update-modules downloads and vendors Hugo modules
FROM build-base AS update-modules
# MODULE is the Go module path and version of the module to update
ARG MODULE
RUN <<"EOT"
set -ex
if [ -n "$MODULE" ]; then
hugo mod get ${MODULE}
RESOLVED=$(cat go.mod | grep -m 1 "${MODULE/@*/}" | awk '{print $1 "@" $2}')
go mod edit -replace "${MODULE/@*/}=${RESOLVED}";
else
echo "no module set";
fi
EOT
RUN hugo mod vendor
hugo mod vendor:Vendor all module dependencies into the _vendor directory.
将依赖写入_vendor目录
# syntax=docker/dockerfile:1
FROM debian
RUN <<EOT bash
set -ex
apt-get update
apt-get install -y vim
EOT
set参数说明(Linux系统使用 man set 查看):
-e Exit immediately if a pipeline (which may consist of a single simple command), a subshell command enclosed in parentheses, or one of the commands executed as part of a command list enclosed by braces (see SHELL GRAMMAR above) exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test following the if or elif reserved words, part of any command executed in a && or || list except the command following the final && or ||, any command in a pipeline but the last, or if the command's return value is being inverted with !. A trap on ERR, if set, is executed before the shell exits. This option applies to the shell environment and each subshell environment separately (see COMMAND EXE‐CUTION ENVIRONMENT above), and may cause subshells to exit before executing all the commands in the subshell.
-x After expanding each simple command, for command, case command, select command, or arithmetic for command, display the expanded value of PS4, followed by the command and its expanded arguments or associated word list.
3.11、vendor阶段:基于scratch镜像构建
# vendor is an empty stage with only vendored Hugo modules
FROM scratch AS vendor
COPY --from=update-modules /src/_vendor /_vendor
COPY --from=update-modules /src/go.* /
复制update-modules阶段的/src/_vendor文件 到 当前镜像的/_vendor
复制update-modules阶段的/src/go.*文件 到 当前镜像的/
3.12、build-upstream阶段:基于build-base阶段构建
# build-upstream builds an upstream project with a replacement module
FROM build-base AS build-upstream
# UPSTREAM_MODULE_NAME is the canonical upstream repository name and namespace (e.g. moby/buildkit)
ARG UPSTREAM_MODULE_NAME
# UPSTREAM_REPO is the repository of the project to validate (e.g. dvdksn/buildkit)
ARG UPSTREAM_REPO
# UPSTREAM_COMMIT is the commit hash of the upstream project to validate
ARG UPSTREAM_COMMIT
# HUGO_MODULE_REPLACEMENTS is the replacement module for the upstream project
ENV HUGO_MODULE_REPLACEMENTS="github.com/${UPSTREAM_MODULE_NAME} -> github.com/${UPSTREAM_REPO} ${UPSTREAM_COMMIT}"
RUN hugo --ignoreVendorPaths "github.com/${UPSTREAM_MODULE_NAME}" -d /out
upstream_module_name、upstream_repo、upstream_commit
hugo部分参数说明:
--ignoreVendorPaths string:ignores any _vendor for module paths matching the given Glob pattern
-d, --destination string:filesystem path to write files to
3.13、validate-upstream阶段:基于wjdp/htmltest:0.17.0镜像构建
# validate-upstream validates HTML output for upstream builds
FROM wjdp/htmltest:v${HTMLTEST_VERSION} AS validate-upstream
WORKDIR /test
COPY --from=build-upstream /out ./public
ADD .htmltest.yml .htmltest.yml
RUN htmltest
工作目录:/test
复制build-stream阶段的/out文件 到 当前镜像的/test/public
复制当前Dockerfile所在宿主机的.htmltest.yml文件 到 当前镜像的/test/.htmltest.yml
GitHub - wjdp/htmltest: :white_check_mark: Test generated HTML for problems
3.14、unused-media阶段:基于alpine:3.20镜像构建
# unused-media checks for unused graphics and other media
FROM alpine:${ALPINE_VERSION} AS unused-media
RUN apk add --no-cache fd ripgrep
WORKDIR /test
RUN --mount=type=bind,target=. <<"EOT"
set -ex
./scripts/test_unused_media.sh
EOT
不使用缓存安装fd、ripgrep软件
工作目录:/test
3.15、path-warnings阶段:基于build-base阶段构建
# path-warnings checks for duplicate target paths
FROM build-base AS path-warnings
RUN hugo --printPathWarnings > /path-warnings.txt
RUN <<EOT
DUPLICATE_TARGETS=$(grep "Duplicate target paths" /path-warnings.txt)
if [ ! -z "$DUPLICATE_TARGETS" ]; then
echo "$DUPLICATE_TARGETS"
echo "You probably have a duplicate alias defined. Please check your aliases."
exit 1
fi
EOT
hugo命令部分参数说明:
--printPathWarnings:print warnings on duplicate target paths etc.
3.16、pagefind阶段:基于base阶段构建
# pagefind installs the Pagefind runtime
FROM base AS pagefind
ARG PAGEFIND_VERSION=1.1.0
COPY --from=build /out ./public
RUN --mount=type=bind,src=pagefind.yml,target=pagefind.yml \
npx pagefind@v${PAGEFIND_VERSION} --output-path "/pagefind"
pagefind版本:1.1.0
复制build阶段的/out文件 到 当前镜像/src/public
npx pagefind@v1.1.0 --output-path "/pagefind":生成文件输出到/pagefind
Pagefind CLI configuration options | Pagefind — Static low-bandwidth search at scale
3.17、index阶段:基于scratch镜像构建
# index generates a Pagefind index
FROM scratch AS index
COPY --from=pagefind /pagefind .
复制pagefind阶段的/pagefind文件 到 当前镜像
3.18、test-go-redirects阶段:基于alpine:3.20镜像构建
# test-go-redirects checks that the /go/ redirects are valid
FROM alpine:${ALPINE_VERSION} AS test-go-redirects
WORKDIR /work
RUN apk add yq
COPY --from=build /out ./public
RUN --mount=type=bind,target=. <<"EOT"
set -ex
./scripts/test_go_redirects.sh
EOT
工作目录:/work
安装yq
复制build阶段的/out文件 到 当前镜像的/work/public
3.19、release阶段:基于scratch镜像构建
# release is an empty scratch image with only compiled assets
FROM scratch AS release
COPY --from=build /out /
COPY --from=pagefind /pagefind /pagefind
复制build阶段的/out文件 到 当前镜像的/
复制pagefind阶段的/pagefind文件 到 当前镜像的/pagefind
3.20、Dockerfile部分指令说明
https://docs.docker.com/reference/dockerfile/
1)FROM……AS……
Use a previous stage as a new stage
2)COPY --from
Use an external image as a stage
3)--mount=type=bind
https://blog.51cto.com/gugu/6085739
4)<<EOT