Docker 快速上手指南

以下记录 Docker 中的常用操作、指令,使得大家能够快速地使用 Docker。

为什使用 Docker?

为什么要使用 Docker?就个人而言,Docker 能提供的最大便利在于这种技术面对程序环境迁移时所表现的卓越性。

这种性能使得我们不必在分享代码或项目合作、交接时附带一长串的环境配置指南进行版本限制和扩展描述;也不必再特意记录自己对现有环境的配置改变,以免下次系统迁移时手足无措;更为重要的是,Docker 在提供以上解决方案的同时没有把解法变得复杂,相反的,它提供了一种更快、更容易的方式。

Docker 独到的对增量和文件层的应用,使得镜像下载、共享 & 隔离、版本控制等问题的解决方法变得格外优雅。有兴趣的读者可以参考「循序渐进学 Docker」这本书。

下载 & 安装

下载

访问下面的网址,从菜单的 Get Docker 中选择适合的版本进行下载。

https://www.docker.com/

安装

Mac 和 Windows 中都可以使用桌面版傻瓜式安装。

在 Centos 中,安装步骤分为 安装 Docker安装 Docker Compose。注意,Docker 的安装受限于系统内核版本,可查看 参考链接

安装完成后,可以通过以下方式查看 Docker 版本:

docker --version

更改镜像源

国内访问官方镜像的速度较慢,可以选用国内的镜像源,这里给出 DaoCloud 和阿里云的镜像源,镜像源更换方法在以下网址中都有说明:

DaoCloud: https://www.daocloud.io/mirror
AliCloud: https://cr.console.aliyun.com/#/accelerator

镜像市场

可以从以下网址中搜索需要的镜像:

// 官方:
https://store.docker.com/

// DaoCloud: 
http://hub.daocloud.io/

// AliCloud
https://dev.aliyun.com/

名词解释

  1. 镜像:从镜像市场中下载的即为镜像,可以理解为容器的模板。

  2. 容器:应用程序运行的环境,容器的创建依赖于某一镜像。注意,容器不是镜像的拷贝,容器只是在镜像之上建立了一层读写层,用以覆盖容器内对镜像配置、文件的修改。采用这一方式可以避免因频繁的镜像复制而导致的资源浪费。具体可以参考相关书籍或博客。

镜像与容器

镜像的搜索与获取

搜索镜像

可以在安装了 Docker 的机器上使用以下指令搜索镜像,不过还是建议通过访问镜像商店的方式搜索。注意,$mirror-name 需要替换为想要搜索的镜像名。

docker search $mirror-name

获取镜像

可以使用以下指令拉取镜像到本地,其中冒号后的 $tag 为镜像的版本标签,如果省略冒号及之后的内容,则为下载最新版本即 :latest。版本标签信息可以在镜像市场中查找到。

docker pull $mirror-name:$tag

注意,若下载的镜像携带有版本标签,则之后对这一镜像的使用都需要携带版本标签,否则会因为版本不同而再次下载。

镜像的查看与删除

查看镜像

查看所有镜像可以使用:

docker images

也可通过以下方式查看单个镜像:

docker images $mirror-name

删除镜像

我们可以通过以下方式删除镜像,但此时需要保证没有容器使用这一镜像:

docker rmi $mirror-name

查看容器

查看已启动的容器

docker ps

查看全部容器

docker ps -a

查看容器日志

采用以下方法可以查看容器的操作历史和输出:

docker logs $container-name

启动容器

生成容器

可以通过以下方式生成一个基于某一镜像的容器,注意,如果宿主机中没有该镜像则会先进行下载。务必注意镜像标签是否正确。

docker run $mirror-name:$tag

使用这一命令会使得容器在创建后自动启动。

给容器添加自定义名字

通过 docker ps -a 可以看到容器的 ID 和 name,这两者都可以作为后续对容器删除、启动、关闭及设置等操作的标识。使用 ID 时,只需输入 ID 的前几位即可(能与其他容器区分)。

Docker 会为其随机生成 64 位长度的字符串作为 ID,当然,我们也可以通过如下方式手动指定容器的名字,其中 $container-name 即为指定的容器名。

docker run --name $container-name $mirror-name

启动容器

docker start $container-name

关闭容器

docker stop $container-name

以交互方式创建容器

可以通过以下方式,以交互的方式创建容器,当然也可以在 $mirror-name 的前面加上 --name xxx 来指定容器的名字,在交互模式中,可以输入 exit 退出:

docker run -it $mirror-name

以后台运行方式创建容器

我们可以使用 -d 操作使容器在后台运行:

docker run -d $mirror-name

容器状态问题

容器在启动后,如果没有活动的前台进程,容器会自动关闭。若要保持容器启动状态,可以强制其执行一个前台进程。具体可查看 参考链接 I参考链接 II

可以用以下方式创建一个不自动关闭的 centos 镜像:

docker run -it --name mycentos centos
docker start mycentos

// 此时可以看到该容器没有自动关闭
docker ps

容器执行操作

我们可以通过以下方式对已经启动的容器执行一些操作,其中 $container-name 可以是容器的名字,也可以是容器的 ID:

docker exec $container-name echo "hello" && echo "world"

也可以通过以下方式进入交互模式:

docekr exec -it $container-name bash

其中,&& 是起到操作间连接的作用,详细可以查看 参考链接。此外,我们也可以在创建的容器的时候就使其执行一些操作:

docker run $mirror-name echo "hello world"

查看容器详情

通过以下方式可以查看容器的详细信息,这些信息是采用 JSON 的格式展现的:

docker inspect $container-name

删除容器

可以在 rm 之后加入一个或多个容器名或容器 ID 进行批量删除。

docker rm $container-name-1 $container-name-2 ...

可以使用以下方法删除全部容器:

docker rm $(docker ps -aq)

Docker 网络

网络类别

查看网络类别

网络的类别为 nonehostbridge 三种,可以通过以下方式查看:

docker network ls

none 型网络

顾名思义,此类网络表示容器为独立个体,不与外部通信。

host 型网络

此类网络表示该容器与宿主机(安装 Docker 的机器)共享网络。

bridge 型网络

这是容器的默认网络类型,网桥模式意味着容器间可以互相通信,而对外的通信需要借助宿主机,这一形式通常表现为端口号的映射。

查看网络类别详情

docker network inspect $network-name 

通过这种方式可以查看 JSON 格式的网络类别,在 Containers 条目中可以看到使用当前网络类型的容器列表,注意 Containers 中只会显示那些已经启动的容器。

创建网络

可以通过以下方式创建一个网络,其中,$network-driver 表示网络类别,即 nonebridgehost,而 $network-name 为自定义的网络名:

docker network create --driver $network-driver $network-name

如果省略 --driver $network-driver 则默认创建 bridge 类型的网络。

为容器指定网络

我们可以创建自定义的网络环境,并将一些容器放入这一网络内,以此管理容器间的网络连通情况。这种局域网网段的模拟实际是由内部 DNS 实现的。以下罗列将容器添加或移除到某一网络中的方法。

将容器添加进某一网络

可以通过以下方式将容器 $container-name 加入 $network-name 网络中。注意,一个容器可以属于多个网络。

docker network connect $network-name $container-name

之后,当容器启动时,我们就可以在 docker network inspect $network-nameContainers 中看到这一容器了。

将容器从某一网络中移除

docker network disconnect $network-name $container-name

在容器生成时指定网络

我们也可以在容器生成时指定网络,使用如下方法:

docker run --network $network-name $mirror-name

测试网络连通情况

我们可以通过以下方式查询到容器的 IP 地址:

  1. 在容器交互模式中使用 ip addr

  2. 使用 docker inspect $container-name

  3. 使用 docker inspect $container-name | grep IPAddress

之后可以使用 ping 指令测试容期间的网络连通情况。没有 ping 命令的容器需要安装 iputils

删除网络

docker network rm $network-name

Docker 存储

有时,我们需要将容器的部分存储映射到宿主机器件中,以便对配置文件、日志文件、数据文件等进行备份或统一管理。

指定数据卷

我们可以在创建时,将系统的某一目录指定为容器某一目录的数据卷,其中 --volume 可以使用 -v 缩写:

docker run --volume /my/mac/dir:/container/dir $mirror-name

此时,容器内部的 /container/dir 将与宿主机的 /my/mac/dir 形成映射。

当然,我们也可以将文件与文件映射起来。

docker run --volume /my/mac/file:/container/file $mirror-name

在指定数据卷时,可以省略宿主主机目录,此时 Docker 会自动指定一个主机空间用以映射:

docker run --volume /container/dir $mirror-name

此外,还可以选择只读方式,这样文件或目录的修改就只能在宿主机中进行了。只需添加 :ro 即可:

docker run --volume /my/mac/dir:/container/dir:ro $mirror-name

我们可以通过 docker inspect $container-name,并在 Mounts 中看到数据卷的详细情况。

查看数据卷

我们可以通过以下指令查看数据卷的情况:

docker volume ls

当容器被删除时,主机上的数据卷并不会被删除,此时可以通过以下指令查看那些没有容器使用的数据卷,注意,这里只会显示那些由 Docker 自动指定的数据卷,即没有手动指定主机映射目录的数据卷:

docker volume ls -f dangling=true

顺便,如果需要在删除容器时一并删除数据卷,可以使用以下指令:

docker rm -v $container-name

数据卷的继承

有时我们可能需要在创建容器时,选择该容器的数据卷与之前的某容器相同,比如在面对多容器共享项目目录空间这一需求时。此时我们可以通过以下方式实现:

docker run --volumes-from $container-name $mirror-name

数据卷的删除

可以使用如下方式删除数据卷,其中 $volume-id 可以通过 docker volume ls 查看:

docker volume rm $volume-id

给已创建时的容器添加数据卷

容器一旦创建后,再添加数据卷映射会比较麻烦,可以查看 参考链接

而且,这里并不建议这么做,建议的做法是将容器提交为镜像后,以此镜像再次创建容器。

Docker 端口

绑定端口

我们可以将容器的端口绑定到主机的某一端口上,已完成某些应用的需求,如将主机的 12345 端口绑定到容器的 80 端口上,这样我们对 localhost:12345 的访问就相当于对容器 80 端口的访问。

通过以下方式可以实现端口绑定,其中 $host-port 为宿主主机的端口,而 $container-port 为容器的端口,如 12345:80

docker run -p $host-port:$container-port nginx

我们也可以只指定容器的端口号,此时 Docker 会自动分配一个主机上的端口号。

docker run -p $container-port nginx

对于 Nginx,官方镜像在制作时指定暴露 80443 端口用于 httphttps 请求,对于这种在镜像中暴露的端口,可以在创建时使用以下方式全部指定:

docker run -P nginx

此时,Docker 会自动分配主机上的两个端口分别映射容器的 80443 端口。自动分配的数量与镜像中暴露的端口数量对应。

查看端口

我们可以在 docker ps -a 中的 PORTS 栏看到端口映射情况。注意只有处于运行中的容器才会有实际的端口映射。

此外,我们还可以使用以下指令查看某一容器的端口映射:

docker port $container-name

docker-compose

创建一个容器时,可能需要对多项参数进行限制,比如指定网络、指定数据卷、指定端口等等。而且,有时我们可能需要同时使用多个容器共同支撑应用,比如 Nginx 容器、php & php-fpm 容器、MySQL 容器、Redis 容器等。

如果每次都使用各种参数,按某种顺序依次启动容器(容器之间可能存在先后顺序,比如要先启动 PHP 然后再启动 Nginx 容器)的话,会造成很多繁琐的操作。为了解决这个问题,我们可以使用 docker-compose。

执行以下指令查看 docker-compose 可以执行的指令:

docker-compose --help

YAML

docker-compose 依赖一个 docker-compose.yml 文件,用以指定容器数据卷、网络等。

.yml 文件遵循 YAML 语法,这是一种使用缩进的语法。详细可见 YAML 语言教程

样例

下面给出一个简单的样例,用以说明 docker-compose 的用法

version: '2.0'
services: 
  # 启用一个镜像为 nginx 的容器并命名为 web1
  web1:
    image: nginx
    # 开启 80 和 443 端口,实际映射端口由 Docker 指定
    ports:
      - "80"
      - "443"
    # 将该容器加入 mynetwork 中
    networks:
      - "mynetwork"
    # 指定该容器要在 web2 容器启动之后启动,且在其停止前停止
    depends_on:
      - web2
  web2:
    image: nginx
    ports:
      - "33333:80"
    networks:
      - "mynetwork"
      - "bridge"
    volumes:
      - "/mnt"
networks:
  # 创建一个驱动为 bridge 的网络,命名为 mynetwork
  mynetwork:
    driver: bridge

其中,容器的实际名字为 .yml 文件所在目录名_.yml 文件中指定的名字_序号,如 mydir_web1_1。但是,在容器内部,可以使用 mydir_web1_1 也可以使用 web1 作为域名访问另一主机,这对于之后多容器共同支撑 web 服务是至关重要的。

更多的 .yml 文件的写法可以参考 [YAML 模板文件][9]

docker-compose 的使用

生成容器

首先,我们需要有一个名为 docker-compose.ymldocker-compose.yaml 的文件,并进入该文件所在目录下,通过以下命令生成并启动:

docker-compose up

Docker 会根据 docker-compose.yml 中的内容创建网络、数据卷和容器。

当然也可以加入参数 -d 使其生成后在后台运行:

docker-compose up -d

停止

停止容器可以使用:

docker-compose stop

运行

再次运行容器可以使用

docker-compose start

查看日志

docker-compose logs

删除

通过以下命令可以删除容器,但不会删除之前创建的网络

docker-compose rm

若想既删除容器,又删除网络,可以使用:

docker-compose down

注意,数据卷的删除仍要使用之前删除数据卷的方法。

生成 & 提交镜像

生成镜像

我们对使用某一镜像的容器做了修改,比如在使用 centos 这一镜像创建的容器中安装了 nginx,此时我们可以将这一容器生成为一个新的镜像,这之后就可以通过这个新镜像创建其他容器,而这些容器也同样已经安装了 nginx。

生成镜像可以使用如下方式:

docker commit -m $commit-msg -a $author $container-id $namespace/$mirror-name:$tag

如:

docker commit -m 'install nginx' -a 'dailybird' abcd1234 dailybird/nginx:test

之后便可以通过 docker images 查看已经创建的镜像了。

提交镜像

我们可以将镜像提交到官方仓库中,这样就可以像最初获取镜像的方式一样获得自己制作的镜像了。

首先,我们需要在 Docker Hub 上注册账户:

https://hub.docker.com/

然后使用以下命令登录:

docker login

之后会提示输入用户名和密码,显示登录成功后,用以下方式推送:

docker push $namespace/$mirror-name:$tag

由于国内访问 Docker Hub 的速度较慢,我们也可以使用 DaoCloud 或阿里云的 Docker 服务,具体的推送方法可以访问之前给出的网址,参照其中的方法即可。

Dockerfile

使用已有容器创建镜像的方式固然可行,可当我们需要再次对镜像修改时,就需要再次生成容器、进行配置修改或软件安装、提交镜像。此外,不断在原有提交镜像的基础上修改提交会存在两个问题:

  1. 累积的修改条目不够直观,可能之后自己也不知道对原有镜像做了什么修改;

  2. 不断修改的过程在 Docker 看来是增量更新的过程。这一次的修改相当于在上一次的基础上增加了一个只读层用于记录本次的修改情况。而只读层的数量是有限的,也就意味着这种修改和提交的方式的操作次数是受限的。

那么,有没有一种更好的方式可以实现镜像的生成呢?类比 docker-compose.yml,我们可以使用另一种类似配置文件的方式来指导镜像的生成,这就是 Dockerfile

Dockerfile 可以指定新镜像的原镜像来源、对原镜像的操作、环境变量,以及以此创建容器时执行的指令等。

样例

# 新镜像基于的原镜像
FROM centos:centos6.8

# 指明维护者
MAINTAINER dailybird <dailybird@mail.com>

# 设置一些环境变量,使用 \ 表示连接多个设置
ENV NGINX_VERSION 1.11.11 \
    TEST_ENV hello

# 指定暴露的端口号,
EXPOSE 80 443

# 在原镜像基础上进行的修改
RUN yum install -y wget iputils \
    && wget http://nginx.org/download/nginx-1.11.11.tar.gz

# 以此镜像创建并启动时,容器执行的指令,通常用于启动服务
CMD ["echo", "hello world"]

比如使用以下配置可以在 centos 中安装 nginx

FROM centos:centos6.8

MAINTAINER dailybird <dailybird@mail.com>

EXPOSE 80 443

RUN cd / \
    && mkdir data \
    && cd data \
    && mkdir nmp \
    && cd nmp \
    && yum install -y wget pcre-devel gcc gcc-c++ \
       ncurses-devel perl make zlib zlib-devel \
       openssl openssl--devel iputils \
    && wget http://nginx.org/download/nginx-1.11.11.tar.gz \
    && tar zxf nginx-1.11.11.tar.gz \
    && cd nginx-1.11.11 \
    && ./configure --prefix=/usr/local/nginx \
    && make && make install && make clean \

更多的 Dockerfile 指令可以参考 Dockerfile 指令

Dockerfile 的使用

使用 Dockerfile 创建镜像

首先,我们需要新建一个名为 Dockerfile 的文件(没有后缀),并写入一些配置内容。然后在该文件的目录中,通过以下指令创建镜像:

docker build --tag $namespace/$mirror-name:$tag $dockerfile-dir

其中,$dockerfile-dirDockerfile 所在目录,比如执行:

docker build --tag dailybird/nginx-demo:demo ./

等待一段时间之后,便可通过 docker images 看到新创建的镜像了。

docker-compose 中使用 Dockerfile

当我们需要启动一个新镜像时,可以先将此镜像创建出来,然后在 docker-compose.yml 文件中通过 image 指定新镜像;也可以直接通过以下方式将这两个步骤合并:

version: '2.0'
services: 
  web1:
    # build 后的参数为 Dockerfile 文件所在的目录位置,替换原先的 image
    build: ./
    ports:
      - "80"
    networks:
      - "mynetwork"
      
# ...
# 其他配置

此后,可以通过以下指令创建容器:

docker-compose build
docker-compose up

或者,直接执行:

docker-compose up --build

这时,Docker 会自动创建一个镜像,并以此创建容器。

后记

关于 Docker 还有很多内容,比如备份、集群、插件等。不过这些就留到进一步研究之后再撰文吧。

问题备忘

权限问题

有时可能遇到如下 Docker 报错:

WARNING: Error loading config file:/home/user/.docker/config.json - stat /home/user/.docker/config.json: permission denied

此时,可以通过以下方法解决:

sudo chmod -R g+rwx /your/path/to/.docker/

参考

  1. Docker 中文社区站

  2. 阿里云ECS部署Docker服务 – CSDN

  3. 为什么docker容器启动不了? – Segmentfault

  4. Docker为什么刚运行就退出了? – CSDN

  5. &&,&,:的区别

  6. 给一个正在运行的Docker容器动态添加Volume

  7. YAML 语言教程 – 阮一峰

  8. YAML 模版文件 – 极客学院

  9. Docker:寻找一些docker-compose.yml文件的示例 – segmentfault

  10. Docker Compose—简化复杂容器应用的利器 – 推酷

  11. 一些 Docker 的技巧与秘诀 – segmentfault

  12. Dockerfile 指令 – 极客学院

  13. Docker permission denied – askubuntu

  14. DOCKER 教程 – 宁皓网

  15. CenOS Linux安装nginx – 博客园

  16. docker-compose.yml 语法说明

  17. Docker学习笔记(2)–Docker常用命令 – CSDN

  18. Dockerfile文件格式的简单介绍 – 博客园

    原文作者:dailybird
    原文地址: https://segmentfault.com/a/1190000008822648
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞