Docker 学习笔记

Build, Ship, and Run Any App, Anywhere.

镜像构建

Dockerfile
# 设置基础镜像为Debian
FROM debian
# 将软件包emacs.tgz解压到/usr/local/目录下
ADD emacs.tgz /usr/local
# 将软件包apache.tgz解压到/usr/local/目录下
ADD apache.tgz /usr/local
# 设置匿名卷目录/data
VOLUME /data
# 设置容器的启动命令,该配置可用运行时参数覆盖
CMD ["/usr/local/start.sh"]

构建镜像

  • Dockerfile中的每一条指令都会生成一层新的镜像层
  • 从一个父镜像开始构建,docker build 的时候会去检查下一条命令的hash值是否于现有镜像层相等,如果相等,则不执行这条命令,而直接基于现有镜像层来执行接下来的语句。
  • 对于ADD或者COPY指令来说,docker 会检查每个文件的校验和(元数据和数据),最后修改时间和最后访问时间不作考虑。
  • 对于RUN yum install 来说,docker 不会去检查文件的校验和,只检查指令是否变化。如果需要强制更新镜像,那么需要docker build –no-cache
  • 一旦从某一层开始不使用cache,接下来的每一层都不会再检查是否有cache
最佳实践
1. FROM

尽可能使用当前官方仓库作为你构建镜像的基础。如果公司内部使用,可下载官方仓库镜像,再推送至私有 registry。推荐使用 Alpine 镜像(Alpine Linux 是一个完整的操作系统),因为其被严格控制并保持在最小尺寸(目前大小 5.52M)。基于此基础镜像,再去构建自己的基础镜像,可以有效控制镜像的大小。

2. LABEL

通过给镜像添加标签可以帮助组织镜像、记录许可信息、辅助自动化构建等。

3. RUN

为了保持 Dockerfile 文件的可读性,可理解性,以及可维护性,过长的或复杂的 RUN 指令使用反斜杠 \ 分行。

RUN yum install -y pip \
git-1.9.3.1 \
wget-1.14 && \
yum clean all

如果将 RUN apt-get update 和 apt-get install 拆解为两条命令,会导致缓存问题记忆后续的 install 失败。下图从左边修改最后一行再次构建镜像是时,Docker 发现 RUN apt-get update 指令一样。这样会导致 apt-get update 不再执行,使用缓存镜像。后面使用 apt-get install 安装的是过时的 curl 和 nginx 版本。


4. EXPOSE

EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是 docker run -P时,会自动随机映射 EXPOSE 的端口。

5. ENV

为容器化服务提供必要的环境变量。

6. ADD 和 COPY

优先使用 COPY,COPY 语义更明确。ADD 能够将本地 tar 文件自动提取到镜像中,这种场景用 ADD 更合适。如果 Dockerfile 中需要 COPY 多个上下文中的文件,不要一次性 COPY 所有文件,这将保证每个步骤的构建缓存只在特定的文件变化时失效。最好的做法是按文件组织结构以及功能去 COPY 文件。

7. VOLUME

建议使用 VOLUME 来管理镜像中的可变部分和用户可以改变的部分,如数据库存储文件、配置文件、容器创建的文件和目录等。

8. WORKDIR

用于指定容器的一个目录, 容器启动时执行的命令会在该目录下执行。为了清晰性和可靠性,应该总是在 WORKDIR 中使用绝对路径。

其他
# 使用 .dockerignore
# 使用多阶段构建
# 避免安装不必要的包
# 一个容器只运行一个进程
# 镜像层数尽可能少
# 清除缓存的包 (apt-get clean)
# 充分利用构建缓存
docker commit

docker commit

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

多阶段构建(Docker 17.05 or higher)

# 第一阶段构建生成可执行文件
# builder
FROM golang:1.16 AS builder
COPY go.mod /src/
COPY go.sum /src/
RUN cd /src && go mod download
COPY . /src/
RUN cd /src && go build -ldflags '-linkmode "external" --extldflags "-static"' cmd/ipasd/ipasd.go

# 第二阶段构建,使用第一阶段的构建产物
# runtime
FROM ineva/alpine:3.10.3
LABEL maintainer="Steven <s@ineva.cn>"
WORKDIR /app
COPY --from=builder /src/ipasd /app
COPY docker-entrypoint.sh /docker-entrypoint.sh
RUN chmod +x /docker-entrypoint.sh
ENTRYPOINT /docker-entrypoint.sh

镜像的存储和传输

docker pull docker push

# docker镜像完整路径
<registry>/<repository>/<image>:<tag>
10.0.0.1:5000/blazehu/myapache:v1

# docker默认registry
docker.io("dockerhub")

# Insecure-registry配置
/etc/docker/daemon.json
"insecure-registries": ["10.0.0.0/8"],

#无法访问 registry?
docker save –o xxx.tgz ${imageId} 将镜像打包成文件
docker load -i xxx.tgz 将镜像从文件中加到本地的docker存储

docker run 做了什么?

docker               客户端命令行工具
dockerd 守护进程,容器的元数据管理、镜像管理、容器运行时及网络、存储等插件
containerd 负责容器的生命周期管理,向上为Docker守护进程提供gRPC接口,屏蔽底层细节,向下通过containerd-shim操控RunC,使得上层Docker守护进程和底层容器运行时可独立升级发展
containerd-shim 处理 exit code,wait4() 等问题,实现daemonless容器
runc 专注于容器实现,包括环境隔离、资源限制、容器安全等

Docker Client基础命令总览

Docker仓库相关
docker search
docker pull
docker push
docker login
查看镜像和容器信息
docker ps
docker images
docker logs
docker port
docker diff
docker history
操作容器和镜像
docker run
docker attach
docker start
docker stop
docker rm
docker rmi
docker tag
docker commit
docker save
docker build
docker load
docker exec