标签归档:容器

Docker 简明指南

一站式文档

docs.docker.com

架构

docker 是 C/S 架构, 一个 docker daemon 运行在后台, 我们直接使用的是 docker 的前端程序. 运行前端程序时, 前端程序会将相关的指令发给 daemon, daemon 解析执行再返回给前端.

镜像与容器

An image is a filesystem and parameters to use at runtime. It doesn’t have state and never changes. A container is a running instance of an image. When you ran the command, Docker Engine:

- checked to see if you had the hello-world software image
- downloaded the image from the Docker Hub (more about the hub later)
- loaded the image into the container and “ran” it

镜像来源

任何人都可以创建自己的镜像, 并将其放到 Docker Hub 上. docker daemon 默认会知道从 Docker Hub 上找镜像.

镜像的创建

自己创建镜像需要一个 Dockerfile 描述文件, 写好 Dockerfile 后执行 docker 命令, docker daemon 就会创建相应的镜像. 再执行 docker images 命令就能看到创建的这个镜像.

可以看到创建镜像我们只提供了一个描述文件, 没有提供创建镜像所需要软件和文件等, 也没指定创建好的镜像放在那里, 实际上我们自己创建的镜像和下载的镜像是放在一个专门的目录里的, 我们不需要操心镜像在本地的位置, docker 自己能找到它.

提示

docker 镜像一般是尽可能做的小, 以节省带宽和存储. 比如 Docker Hub 中的 ubuntu 镜像, 默认是连 ifconfig, ping 这样的工具都不包含的, 初次之外默认还砍掉了很多其他组件, 所以 ubuntu 的镜像总共才 100MB. 安装以后如果需要的话可以自己 apt-get install 添加.

Docker for Mac 的问题

Mac 版的 docker 没有 docker0 接口, 宿主机无法与容器直接通信.

https://docs.docker.com/docker-for-mac/networking/#use-cases-and-workarounds

Docker 的存储理解

理解镜像, 容器, 层以及存储驱动的概念:

https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/

要持久化存储容器里的改动, 一个是 commit 自己的改动, 构建新自己的新的镜像. 一个是挂载 data volume, 将数据存 data volume 上.

data volume 就是宿主机上的文件或目录, 容器启动时可以挂载这个文件或目录. 多个容器可以共享相同的 data volume.

https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/#data-volumes-and-the-storage-driver

Docker 的网络绑定

https://docs.docker.com/engine/userguide/networking/default_network/binding/

数据存储

https://docs.docker.com/engine/admin/volumes/

三种方式:

  • volumes
  • bind mounts
  • tmpfs volumes

这三种方式对容器内部而言表现一样, 展现给容器内部要么是一个目录, 要么是个文件.

osx 下的 bind mounts

在 osx 系统下, 因为种种原因, bind mounts 和在 Linux 系统下不太一样. osx 系统下需要先配置要 bind mounts 的目录, 这可以在 Preferences -> File Sharing 下完成, 默认有配置四个目录:

  • /Users
  • /Volumes
  • /tmp
  • /private

这四个目录包括子目录都能直接 mount 到容器里.

你可能也发现过有些目录不在这四个之中但是也 mount 成功了, 比如:

  • docker run -it -v /etc:/dockeroot ubuntu:16.04
  • docker run -it -v /bin:/dockeroot ubuntu:16.04

也都是可以成功的. 这是因为 docker for mac 里还存在一个叫做 Moby Linux VM 的东西, 当你 mount 了一个没在 File Sharing 中配置过的目录时, 就会从 Moby Linux VM 中找这个目录. 上面两个例子
, 如果仔细看一下, 其实挂载到 /dockeroot 下的内容并不是你 osx 机器的 /etc 或者 /bin 的内容.

关于这一点可以看官方的说明, 以及后两个链接:

https://docs.docker.com/docker-for-mac/osxfs/#namespaces
https://stackoverflow.com/questions/45122459/docker-mounts-denied-the-paths-are-not-shared-from-os-x-and-are-not-known
https://github.com/docker/for-mac/issues/1970

打造自己的开发环境

使用 macbook 有半个月了, 很好, 但是在服务器开发环境方面和 linux 确实还是有点距离的, 最简单的 php + fpm + nginx 的环境都不好搞. 所以今天想借助 docker 搞一个这样的环境试一下.

方法一:

所做的修改及时 commit

方法二:

自己写 Dockerfile 或者 compose.yaml

方法三:

使用这个项目: https://github.com/phusion/baseimage-docker

企业化

docker 实际使用时, 一个容器内可能启动多个服务, 如何管理这多个服务呢?

docker 容器里一般不包含 init 程序的, CMD 命令所指定的进程就是 dokcer 里的第一个程序, 一般我们使用 python 的 supervisord 框架来作为 docker 容器内的第一个进程, 并用它来管理其它的进程

Q&A

镜像 ID 是什么

关于镜像 ID, 官方文档说的很不清楚. 下面这个链接说的比较清楚, 认真看完这个链接, 就都懂了

http://windsock.io/explaining-docker-image-ids/

如何查看某镜像的创建历史及其父镜像

http://windsock.io/explaining-docker-image-ids/

docker inspect 命令结果的含义

http://windsock.io/explaining-docker-image-ids/

docker 镜像在 osx 系统上的位置

~/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/Docker.qcow2, 这是一个 qmenu 镜像文件

Dockerfile 中的 FROM 指令对应 docker history 中的 ADD file, Add file 中的 “file” 是哪来的

https://stackoverflow.com/questions/41972328/docker-history-base-image-addsha256hash

如何得到某个镜像的 base image

https://stackoverflow.com/questions/31149775/docker-how-to-get-the-name-user-repotag-of-the-base-image-used-to-build-ano

docker history 命令的输出不是完全按照 Dockerfile 中的指令逆序输出的

Dockerfile 中这么写

FROM xxx
MAINTAINER cifer

生成的镜像, docker history 的输出可能是

ADD file:xxxxxxx
MAINTAINER cifer

镜像越来越大了, 删除文件然后 commit, 镜像不会减小的?

是的, 了解了镜像的结构就会明白了, 镜像本身包含的 layer 都是只读的. 容器运行时, 我们处于一个新的可写的 layer 中, 删除文件, 然后 commit, commit 会把这个新 layer 写入镜像, 我们对文件的
删除也只是在新的 layer 中删除了, 老的 layer 中是依然包含这个文件的, 所以镜像不会变小的. 但是启动镜像之后, 你所删的文件确实是没有了.

那该怎么办呢? 使用 docker save 或者 docker export 命令

https://stackoverflow.com/questions/22655867/what-is-the-difference-between-save-and-export-in-docker

https://tuhrig.de/difference-between-save-and-export-in-docker/ 这篇文章的作者就是上面 so 链接里的提问者, 作者收到的回答之后回去写了一篇博客作为总结, 赞!

docker tag 的作用

在我们本地构建的镜像会包含多个层, 执行 docker history 会发现每个层有一个对应的 IAMGE ID, 这是因为 Dockerfile 里的每条指令都会创建一个新层并 commit.

docker tag 可以让这个名字的镜像回到某个层. 但是似乎必须是在本地构建的层才能回去.