Docker 使用镜像

学习内容

  1. 从仓库获取镜像。
  2. 管理本地主机上的镜像。
  3. 介绍镜像实现的基本原理。

命令

拉取镜像

docker pull  [选项]   [DockerRigistry地址[ :端口号] / ] 仓库名 [ :标签]
  • Docker镜像仓库地址: <域名 / IP> [ :端口号 ],默认是 Docker Hub
  • 仓库名:<用户名> / <软件名> 。对于 Docker Hub ,如果不给用户名,默认为 library, 即官方镜像。
docker pull ubuntu:16.04

运行容器

docker  run  -it   --rm  ubuntu:16.04 bash
  • -it-i 是交互式操作,-t 是终端,一般一起使用。
  • --rm:容器退出后随之删除容器,一般不用。
  • ubuntu:16.04:使用镜像。
  • bash: 放在镜像名后面的是命令,指定交互式 shell

列出镜像

docker  image ls 

查看镜像、容器、数据卷占用空间

docker  system df

虚悬镜像

1.概念:没有仓库名,也没有标签的的镜像,基本是废弃不用的。
2.查看。

docker image ls -f dangling=true

3.删除所有虚悬镜像。

docker image prune

显示中间层镜像

1.不加 -a 只显示顶层镜像

docker image ls -a    

根据条件列出镜像

1.根据仓库名。

docker  image ls ubuntu

2.根据仓库名和标签

docker  image ls  ubuntu:16.04

3.--filter 过滤参数, 简写 -f

docker image ls -f since=mongo:3.2
docker image ls -f  label=com.example.version=0.1

只显示镜像id

1.显示id

docker image ls -q

删除本地镜像

1.删除

docker image  rm [选项]  <镜像1>  [<镜像2> ....]

镜像可以是镜像id、镜像名、摘要

2.配合 ls 删除

docker image rm $(docker image ls -q -f before-mongo :3.2)

查看镜像历史

docker  history  nginx : v2

定制镜像

1.默认的镜像生成文件的名字为 Dockerfile , 可以通过 -f 指定生成文件。
2. FROM scratch 不以任何镜像为基础。

定制 nginx 的例子:

1.新建目录 mynginx,切入到目录。
2.新建 Dcokerfile
3.在 Dockerfile 文件所在的目录执行:

docker build -t nginx:v3  .

4.docker build 命令的格式:

docker build [选项] <上下文路径/URL/->

docker build的用法

1.直接用 Git repo 构建。

$ docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14

这行命令指定了构建所需的 Git repo,并且指定默认的 master 分支,构建目录为 /8.14/ , 然后 Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始 构建。

2.给定 tar 包构建。

$ docker build http://server/context.tar.gz

3.标准输入读取 Dockerfile 构建,输入为文件的时候。

docker build - < Dockerfile 
// 或
cat Dockerfile | docker build - 

这种格式没有上下文,不能使用 COPY 指令

4.标准输入压缩包(gzip, bzip2,xz)。

docker build - < context.tar.gz

镜像上下文

Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 APIDocker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实 际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计, 所以 上下文路径 指定打包给远程服务器的一切文件。

Dockerfile 的指令都是在 上下文路径 指定的范围内运行的。如果 上下文路径 写成了当前主机的根目录,那么就会把大当前系统的所有文件传给 Docker 引擎,这是错误的。

Dockerfile 命令

1.Dockerfile 中每一个指令都会建立一层。
2.CMDENTRYPOINTHEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。

RUN

1.两种使用格式:

  • shell 格式:RUN <命令>
  • exec 格式:RUN [ "可执行文件", "参数1", "参数2" ]
FROM nginx 
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

2.制作redi容器的一个Dockerfile 不合格的写法。

FROM debian:jessie 
RUN apt-get update 
RUN apt-get install -y gcc libc6-dev make 
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" 
RUN mkdir -p /usr/src/redis 
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 
RUN make -C /usr/src/redis 
RUN make -C /usr/src/redis install

这里每个 RUN 指令都会新建一层

合格的写法

FROM debian:jessie 
RUN buildDeps='gcc libc6-dev make' \ 
	&& apt-get update \ 
	&& apt-get install -y $buildDeps \ 
	&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \ 
	&& mkdir -p /usr/src/redis \
	&& tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \ 
	&& make -C /usr/src/redis \ 
	&& make -C /usr/src/redis install \ 
	&& rm -rf /var/lib/apt/lists/* \ 
	&& rm redis.tar.gz \ 
	&& rm -r /usr/src/redis \ 
	&& apt-get purge -y --auto-remove $buildDeps

COPY 复制文件

1.两种格式:

  • COPY <源路径> ...<目标路径>
  • COPY [<源路径> ...<目标路径>]

目标路径可以是容器内的绝对路径,也可以是相对于工作目录的相对路径,工作目录可以用 WORKDIR 指令指定。

2.使用通配符格式:

COPY  hom*  /mydir/
COPY  hom?.txt  /mydir/

3.使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间。

ADD 高级复制文件

1.在复制的文件需要自动解压缩的场景下使用:

FROM scratch 
ADD ubuntu-xenial-core-cloudimg-amd64-root.tar.gz / 

2.如果复制的某个压缩文件,不需要解压缩,不要用 ADD 指令。

CMD 容器启动命令

1.命令格式:

  • shell 格式:CMD <命令>
  • exec 格式:CMD ["可执行文件", "参数1", "参数2" ....]。 (使用双引号)
  • 参数列表格式:CMD ["参数1", "参数2" ....]

2.使用 shell 格式,下列命令会被包装成:

CMD echo $HOME

为:

CMD ["sh", "-c", "echo $HOME"]

3.容器中内没有后台服务的概念。对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退 出,容器就失去了存在的意义,从而退出。

命令 CMD service nginx start 被解释成 CMD [ "sh", "-c", "service nginx start"],因此主进程是 sh。那么当 service nginx start 结束了, sh 也就结 束了, sh 作为主进程退出了,自然就会令容器退出。正确的做法是: CMD ["nginx", "-g", "daemon off;"]nginx 为主进程。

ENTRYPOINT 入口点

1.ENTRYPOINT 的目的和 CMD 一样,都是在指定容器启动程序及参数。当指定了 ENTRYPOINT 后, CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:

<ENTRYPOINT> "<CMD>"
使用场景一

下面查询公网 ip 容器构建脚本:

FROM ubuntu:16.04 
RUN apt-get update \ 
	&& apt-get install -y curl \ 
	&& rm -rf /var/lib/apt/lists/* 
CMD [ "curl", "-s", "http://ip.cn" ]

生成的容器 myip 是没办法动态指定 curl 的参数 -i 的:

docker run myip -i   

// 会报错
docker: Error response from daemon: invalid header field value "oci runtime error: con tainer_linux.go:247: starting container process caused \"exec: \\\"-i\\\": executable file not found in $PATH\"\n".

修改脚本为:

FROM ubuntu:16.04 
RUN apt-get update \ 
	&& apt-get install -y curl \ 
	&& rm -rf /var/lib/apt/lists/*
ENTRYPOINT [ "curl", "-s", "http://ip.cn" ]

这样构建的容器运行 docker run myip -i 就不会报错。因为 当脚本存在 ENTRYPOINT 指令后, CMD 的内容将会作为参数传给 ENTRYPOINT ,而这里 -i 就是新的 CMD ,因此会作为参数传给 curl ,从而达到了我们预 期的效果。

使用场景二

官方镜像 redis 中:

FROM alpine:3.4 
... 
RUN addgroup -S redis && adduser -S -G redis redis 
... 
ENTRYPOINT ["docker-entrypoint.sh"] 
EXPOSE 6379 
CMD [ "redis-server" ]

entrypoint.sh 脚本的部分内容:

#!/bin/sh 
... 
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then 
	chown -R redis . 
	exec su-exec redis "$0" "$@" 
fi
exec "$@"

构建命令 docker run -it redis id

ENV 设置环境变量

1.格式:

  • ENV <key> <value>
  • ENV <key1>=<value1> <key2>=<value2> ...
ENV VERSION=1.0 DEBUG=on \ NAME="Happy Feet"

对含有空格的值用双引号括起来
容器运行时,变量也会存在

2.在官方 node 镜像 Dockerfile 中:

ENV NODE_VERSION 7.2.0 
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.ta r.xz" \ 
	&& curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \ 
	&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \ 
	&& grep " node-v$NODE_VERSION-linux-x64.tar.xz\$" SHASUMS256.txt | sha256sum -c - \ 
	&& tar -xJf "node-v$NODE_VERSION-linux-x64.tar.xz" -C /usr/local --strip-components= 1 \
	&& rm "node-v$NODE_VERSION-linux-x64.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
	&& ln -s /usr/local/bin/node /usr/local/bin/nodejs

3.支持的指令:

ADD 、 COPY 、 ENV 、 EXPOSE 、 LABEL 、 USER 、 WORKDIR 、 VOLUME 、 STOPSIGNAL 、 ONBU ILD

ARG 参数

1.格式: ARG <参数名>[=<默认值>]
2.ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。
3.Dockerfile 中的 ARG 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build 中用 --build-arg <参数名>=<值> 来覆盖。

VOLUME 定义匿名卷

1.格式:

  • VOLUME ["<路径1>", "<路径2>" ...]
  • VOLUME <路径>

2.容器运行时应该尽量保持容器存储层不发生写操作。为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂 载,其应用也可以正常运行,不会向容器存储层写入大量数据。

VOLUME /data

运行时可以覆盖这个挂载设 置。比如:

docker run -d -v mydata:/data xxxx

使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。

EXPOSE 声明端口

1.格式:EXPOSE <端口1> <端口2>...

WORKDIR 指定工作目录

1.格式:WORKDIR <工作目录路径>
2.使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改 为指定的目录,如该目录不存在, WORKDIR 会帮你建立目录。
3. WORKDIR 会改变环境状态并影响以后的层。

错误脚本书写

Docker 是有分层概念的,一个 RUN 指令对于一个分层,那么在 cdecho 之间就不一定是同一个进程执行环境了。

RUN cd /app
RUN echo "hello" > world.txt

USER 指定当前用户

1.格式:USER <用户名>
2.示例:

RUN groupadd -r redis && useradd -r -g redis redis
USER redis 
RUN [ "redis-server" ]

3.如果以 root 执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来 运行某个服务进程,不要使用 su 或者 sudo ,这些都需要比较麻烦的配置,而且在 TTY 缺 失的环境下经常出错。建议使用 gosu

# 建立 redis 用户,并使用 gosu 换另一个用户执行命令 
RUN groupadd -r redis && useradd -r -g redis redis 
# 下载 gosu RUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/ gosu-amd64" \ 
	&& chmod +x /usr/local/bin/gosu \ 
	&& gosu nobody true 
# 设置 CMD,并以另外的用户执行
CMD [ "exec", "gosu", "redis", "redis-server" ]

HEALTHCHECK 健康检查

1.格式:

  • HEALTHCHECK [选项] CMD <命令> : 设置检查容器健康状况的命令。
  • HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令。

2.支持选项:

  • --interval=<间隔>:两次健康检查的间隔,默认为 30 秒;
  • --timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被 视为失败,默认 30 秒;
  • --retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3 次。

3.HEALTHCHECK [选项] CMD 命令的返回值决定了该次健康检查的成功与否: 0 :成功; 1 :失败; 2 : 保留,不要使用这个值。

4.示例:

FROM nginx 
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* HEALTHCHECK --interval=5s --timeout=3s \ CMD curl -fs http://localhost/ || exit 1

5.查看容器状态:

docker container ls

ONBUILD 构建时运行

假设有一个基础镜像 mynode

FROM node:slim 
RUN mkdir /app WORKDIR /app
COPY ./package.json /app 
RUN [ "npm", "install" ] 
COPY . /app/ 
CMD [ "npm", "start" ]

在构建 mynode 基础镜像时,上面的命令时立即执行比如 copy。如果改成下面的:

FROM node:slim 
RUN mkdir /app WORKDIR /app 
ONBUILD COPY ./package.json /app 
ONBUILD RUN [ "npm", "install" ] 
ONBUILD COPY . /app/ 
CMD [ "npm", "start" ]

那么,在引用 mynode 镜像的其他镜像里面,比如下面的:

FROM  mynode

只有在构建上面只有一行的镜像时,mynode 里面带 ONBUILD 的指令才真正的执行。

多阶段构建

FROM golang:1.9-alpine as builder  // as 第一阶段
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 as prod    // as 第二阶段
RUN apk --no-cache add ca-certificates 
WORKDIR /root/ 
COPY --from=0 /go/src/github.com/go/helloworld/app . 
CMD ["./app"]

镜像的保存

$ docker image ls alpine 
REPOSITORY TAG IMAGE ID CREATED SIZE alpine latest baa5d63471ea 5 weeks ago 4.803 MB

$ docker save alpine | gzip > alpine-latest.tar.gz

$ docker load -i alpine-latest.tar.gz 
Loaded image: alpine:latest

[1] Docker技术入门与实战
[2] Docker 官方镜像 Dockerfile
[3] Dockerfile 最佳实践文档

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值