Docker 小记

Docker是开源的容器虚拟化平台,它可以极大的帮助我们保持开发环境与生产环境的统一,也能极大的减轻运维难度,对我们的系统在云上实现资源弹性扩展,动态伸缩有极大推动作用。

1 概念

  • Docker Hub: 用于分享、管理 Docker 容器的 Docker SaaS 平台 – Docker Hub

Docker 使用客户端-服务器 (C/S) 架构模式。Docker 客户端会与 Docker 守护进程进行通信。Docker 守护进程会处理复杂繁重的任务,例如建立、运行、发布你的 Docker 容器。Docker 客户端和守护进程可以运行在同一个系统上,当然你也可以使用 Docker 客户端去连接一个远程的 Docker 守护进程。Docker 客户端和守护进程之间通过 socket 或者 RESTful API 进行通信。

Docker 的三个概念

镜像

类似虚拟机中的镜像,是一个包含文件系统的面向Docker引擎的只读模板。(它就是为我们提供一种容器的生成规则,也就是模板)

容器

类似一个轻量级沙盒,可以看作一个极简的Linux系统(包括root权限、进程空间、用户空间和网络空间等),以及运行在其上的程序。Docker引擎利用容器来运行、隔离各个应用。容器是镜像创建的应用实例,可以创建、启动、停止、删除容器,各个容器之间是是相互隔离的,互不影响。注意:镜像本身是只读的,容器从镜像启动时,Docker在镜像的上层创建一个可写层,镜像本身不变。

(容器就是根据镜像模板生成的具体实例,我们可以对其进行修改,不会影响镜像,也可以指定端口,通过外部程序访问其上服务)

仓库

类似于代码仓库,这里是镜像仓库,是Docker用来集中存放镜像文件的地方。注意与注册服务器(Registry)的区别:注册服务器是存放仓库的地方,一般会有多个仓库;而仓库是存放镜像的地方,一般每个仓库存放一类镜像,每个镜像利用tag进行区分,比如MySQL仓库存放有多个版本(5.7、8.6等)的MySQL镜像。

2 Docker 的基本操作

# 搜索
sudo docker search centos
# 获取 ubuntu 官方镜像
sudo docker pull centos 
# 查看当前镜像列表 
sudo docker images

# docker run - 运行一个容器
# -t - 分配一个(伪)tty (link is external)
# -i - 交互模式 (so we can interact with it)
# -d 后台运行
# ubuntu:14.04 - 使用 ubuntu 基础镜像 14.04
# /bin/bash - 运行命令 bash shell
# 注: ubuntu 会有多个版本,通过指定 tag 来启动特定的版本 [image]:[tag]
sudo docker run -i -t ubuntu:14.04 /bin/bash

# 避免每次 输入sudo
# 输入
sudo groupadd docker
# 回显示groupadd: group 'docker' already exists
# 将docker账户给与权限
sudo gpasswd -a <你的用户名> docker
# 例如:  sudo gpasswd -a ubuntu docker
# 重启docker
sudo service docker restart

# 查看/etc/group,确定是否存在docker组
cat /etc/group | grep docker
# 安装Docker后,docker组已经创建好了,上面的命令输出如下:
docker:x:999:
# 将当前用户添加到 docker 组
sudo gpasswd -a ${USER} docker
# 重新登录或者用以下命令切换到docker组
newgrp - docker
# 重启docker服务
sudo service docker restart
# 不加sudo直接执行docker命令检查效果
docker images
# 安装Docker的时候拉取过hello-world镜像,所以输出如下:
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              4ab4c602aa5e        8 weeks ago         1.84kB
# 删除none的镜像:
#!/bin/bash
docker ps -a | grep "Exited" | awk '{print $1}' | xargs docker stop
docker ps -a | grep "Exited" | awk '{print $1}' | xargs docker rm
docker images | grep none | awk '{print $3}' | xargs docker rmi
# 删除镜像
docker rmi -f image_ID
# 如果要退出就:
Ctrl-D
root@aliyun:/# exit


1、删除容器
1)首先需要停止所有的容器
docker stop $(docker ps -a -q)
2)删除所有的容器(只删除单个时把后面的变量改为image id即可)
docker rm $(docker ps -a -q)
2、删除镜像
1)查看host中的镜像
docker images
2)删除指定id的镜像
docker rmi <image id>
docker rmi $(docker images | grep "^" | awk "{print $3}")
3)删除全部的images
docker rmi $(docker images -q)
[root@xxx ~]# docker run -it centos:latest /bin/bash    # 启动一个容器
[root@aliyun /]#    # 这里命令行形式变了,表示已经进入了一个新环境
[root@aliyun /]# git --version    # 此时的容器中没有git
bash: git: command not found
[root@aliyun /]# yum install git    # 利用yum安装git
......
[root@aliyun /]# git --version   # 此时的容器中已经装有git了
git version 1.8.3.1
[root@xxx ~]# docker ps -a
CONTAINER ID  IMAGE    COMMAND      CREATED   STATUS   PORTS    NAMES
72f1a8a0e395  centos:latest "/bin/bash"  9 minutes ago   Exited (0) 3 minutes ago      angry_hodgkin
# 提交成新的镜像
docker commit -m "centos with git" -a "zhj" 72f1a8a0e395 zhj/centos:git
# 杀docker 进程
docker kill 72f1a8a0e395

3 Dockerfile

Dockerfile 可以用来创建镜像,类似配置文件

# 说明该镜像以哪个镜像为基础
FROM centos:latest
# 构建者的基本信息
MAINTAINER panjinqan
# 在build这个镜像时执行的操作
RUN yum update
RUN yum install -y git
# 拷贝本地文件到镜像中
COPY ./* /usr/share/gitdir/

# -t用来指定新镜像的用户信息、tag等。最后的点表示在当前目录寻找Dockerfile。
[root@xxx ~]# docker build -t="zhj/centos:gitdir" .

# 以上就是构建自己镜像的两种方法。其中也涉及到了容器的一些操作。如果想删除容器或者镜像,可以使用rm命令,注意:删除镜像前必须先删除以此镜像为基础的容器。
[root@xxx ~]# docker rm container_name/container_id
[root@xxx ~]# docker rmi image_name/image_id

1 FROM

用法:

FROM <image>
  • FROM指定构建镜像的基础源镜像,如果本地没有指定的镜像,则会自动从 Docker 的公共库 pull 镜像下来。
  • FROM必须是 Dockerfile 中非注释行的第一个指令,即一个 Dockerfile 从FROM语句开始。
  • FROM可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像。
  • 如果FROM语句没有指定镜像标签,则默认使用latest标签。

2 MAINTAINER

用法:

MAINTAINER <name>

指定创建镜像的用户

RUN 有两种使用方式

  • RUN
  • RUN [“executable”, “param1”, “param2”]

每条RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像,后续的RUN都在之前RUN提交后的镜像为基础,镜像是分层的,可以通过一个镜像的任何一个历史提交点来创建,类似源码的版本控制。

exec 方式会被解析为一个 JSON 数组,所以必须使用双引号而不是单引号。exec 方式不会调用一个命令 shell,所以也就不会继承相应的变量,如:

RUN [ "echo", "$HOME" ]

这种方式是不会达到输出 HOME 变量的,正确的方式应该是这样的

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

RUN产生的缓存在下一次构建的时候是不会失效的,会被重用,可以使用–no-cache选项,即docker build --no-cache,如此便不会缓存。

3 CMD

CMD有三种使用方式:

  • CMD [“executable”,“param1”,“param2”]
  • CMD [“param1”,“param2”]
  • CMD command param1 param2 (shell form)

注意:
CMD指定在 Dockerfile 中只能使用一次,如果有多个,则只有最后一个会生效。

CMD的目的是为了在启动容器时提供一个默认的命令执行选项。如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令。

CMD会在启动容器的时候执行,build 时不执行,而RUN只是在构建镜像的时候执行,后续镜像构建完成之后,启动容器就与RUN无关了,这个初学者容易弄混这个概念,这里简单注解一下。

4 EXPOSE

EXPOSE <port> [<port>...]

对外映射的本地端口,需要在 docker run 的时候使用-p或者-P选项生效。

5 ENV

# 只能设置一个变量
ENV <key> <value>       
# 允许一次设置多个变量
ENV <key>=<value> ...

指定一个环节变量,会被后续RUN指令使用,并在容器运行时保留。

例子:

ENV myName="John Doe" myDog=Rex\ The\ Dog \
    myCat=fluffy

等同于

ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat fluffy

6 ADD

ADD <src>... <dest>

ADD复制本地主机文件、目录或者远程文件 URL S从 并且添加到容器指定路径中 。

支持通过 GO 的正则模糊匹配,具体规则可参见 Go filepath.Match

# adds all files starting with "hom"
ADD hom* /mydir/        
# ? is replaced with any single character
ADD hom?.txt /mydir/

注意:

  • 路径必须是绝对路径,如果 不存在,会自动创建对应目录
  • 路径必须是 Dockerfile 所在路径的相对路径
  • 如果是一个目录,只会复制目录下的内容,而目录本身则不会被复制

7 COPY

COPY <src>... <dest>

COPY复制新文件或者目录从 并且添加到容器指定路径中 。用法同ADD,唯一的不同是不能指定远程文件 URLS。

8 ENTRYPOINT

  • ENTRYPOINT [“executable”, “param1”, “param2”](http://opskumu.github.io/the preferred exec form,优先选择)
  • ENTRYPOINT command param1 param2 (shell form)

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖,而CMD是可以被覆盖的。如果需要覆盖,则可以使用docker run --entrypoint选项。

每个 Dockerfile 中只能有一个ENTRYPOINT,当指定多个时,只有最后一个生效。

Exec form ENTRYPOINT 例子

通过ENTRYPOINT使用 exec form 方式设置稳定的默认命令和选项,而使用CMD添加默认之外经常被改动的选项。

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]

通过 Dockerfile 使用ENTRYPOINT展示前台运行 Apache 服务

FROM debian:stable
RUN apt-get update && apt-get install -y --force-yes apache2
EXPOSE 80 443
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

Shell form ENTRYPOINT 例子

这种方式会在/bin/sh -c中执行,会忽略任何CMD或者docker run命令行选项,为了确保docker stop能够停止长时间运行ENTRYPOINT的容器,确保执行的时候使用exec选项。

FROM ubuntu
ENTRYPOINT exec top -b

如果在ENTRYPOINT忘记使用exec选项,则可以使用CMD补上:

FROM ubuntu
ENTRYPOINT top -b
CMD --ignored-param1 # --ignored-param2 ... --ignored-param3 ... 依此类推

9 VOLUME

VOLUME ["/data"]

创建一个可以从本地主机或其他容器挂载的挂载点,后续具体介绍。

10 USER

USER daemon

指定运行容器时的用户名或 UID,后续的RUN、CMD、ENTRYPOINT也会使用指定用户。

11 WORKDIR

WORKDIR /path/to/workdir

为后续的RUN、CMD、ENTRYPOINT指令配置工作目录。可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。

WORKDIR /a
RUN pwd

最终路径是/a/b/c。

WORKDIR指令可以在ENV设置变量之后调用环境变量:

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME

最终路径则为 /path/$DIRNAME。

12 ONBUILD

ONBUILD [INSTRUCTION]

配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。

例如,Dockerfile 使用如下的内容创建了镜像 image-A:

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

如果基于 image-A 创建新的镜像时,新的 Dockerfile 中使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。

# Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src

使用ONBUILD指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild。

docker run的其他参数命令如下:

-d, --detach=false, 指定容器运行于前台还是后台,默认为false
-i, --interactive=false, 打开STDIN,用于控制台交互
-t, --tty=false, 分配tty设备,该可以支持终端登录,默认为false
-u, --user="", 指定容器的用户
-a, --attach=[], 登录容器(必须是以docker run -d启动的容器)
-w, --workdir="", 指定容器的工作目录
-c, --cpu-shares=0, 设置容器CPU权重,在CPU共享场景使用
-e, --env=[], 指定环境变量,容器中可以使用该环境变量
-m, --memory="", 指定容器的内存上限
-P, --publish-all=false, 指定容器暴露的端口
-p, --publish=[], 指定容器暴露的端口
-h, --hostname="", 指定容器的主机名
-v, --volume=[], 给容器挂载存储卷,挂载到容器的某个目录
--volumes-from=[], 给容器挂载其他容器上的卷,挂载到容器的某个目录
--cap-add=[], 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities
--cap-drop=[], 删除权限,权限清单详见:http://linux.die.net/man/7/capabilities
--cidfile="", 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法
--cpuset="", 设置容器可以使用哪些CPU,此参数可以用来容器独占CPU
--device=[], 添加主机设备给容器,相当于设备直通
--dns=[], 指定容器的dns服务器
--dns-search=[], 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件
--entrypoint="", 覆盖image的入口点
--env-file=[], 指定环境变量文件,文件格式为每行一个环境变量
--expose=[], 指定容器暴露的端口,即修改镜像的暴露端口
--link=[], 指定容器间的关联,使用其他容器的IP、env等信息
--lxc-conf=[], 指定容器的配置文件,只有在指定--exec-driver=lxc时使用
--name="", 指定容器名字,后续可以通过名字进行容器管理,links特性需要使用名字
--net="bridge", 容器网络设置:bridge 使用docker daemon指定的网桥
host //容器使用主机的网络
container:NAME_or_ID >//使用其他容器的网路,共享IP和PORT等网络资源
none 容器使用自己的网络(类似--net=bridge),但是不进行配置
--privileged=false, 指定容器是否为特权容器,特权容器拥有所有的capabilities
--restart="no", 指定容器停止后的重启策略:no:容器退出时不重启
on-failure:容器故障退出(返回值非零)时重启
always:容器退出时总是重启
--rm=false, 指定容器停止后自动删除容器(不支持以docker run -d启动的容器)
--sig-proxy=true, 设置由代理接受并处理信号,但是SIGCHLD、SIGSTOP和SIGKILL不能被代理

启动、停止、重启容器命令:

[root@xxx ~]# docker start container_name/container_id
[root@xxx ~]# docker stop container_name/container_id
[root@xxx ~]# docker restart container_name/container_id
# 进入后台启动的容器
[root@xxx ~]# docker attach container_name/container_id

挂载路径的方法:

docker run -it -v /path/to/project:/path/to/project -v /path/to/dataset:/path/to/dataset  docker-image:tag
# 或者使用`pwd`表示当前路径
docker run --rm -it -v `pwd`:/workspace data:cv2 /bin/bash
# 对容器处理完的数据更改一下权限:
chmod -R 1000:1000 <要修改权限的文件或文件夹>

推送远程仓库:

[root@xxx ~]# docker push panjinqan/centos:git    # 成功推送
[root@xxx ~]# docker push xxx/centos:git    # 失败
The push refers to a repository [docker.io/xxx/centos]
unauthorized: authentication required