Docker的一些有趣的东西

Docker命令相关

创建一个网络

1
docker network create --subnet=172.18.0.0/16 mynetwork

docker默认使用的网络是172.17.0.0/16,所以创建网络应该从内网的其他部分选取。

列出网络

1
docker network ls

查看镜像信息

1
docker inspect imageid

查看使用记录

1
docker history

docker内网访问的问题

我需要在一个容器中使用反向代理,代理其它容器的服务,此容器使用公网,其他容器只能在内网访问。

即有如下三个容器A, B, C

然后A容器暴露80端口到公网

BC暴露各自的服务端口如8080或3000等到内网

然后在A容器中使用反向代理,代理容器BC中的服务,使得公网可以通过访问A来访问BC的服务


直接使用-p绑定端口到默认网络

容器A: 0.0.0.0:80:80
容器B: 172.17.0.1:8080:8080
容器C: 172.17.0.1:8081:3000

这个方案可以使容器A可以从公网的80端口访问,而容器A可以使用127.17.0.1:8080127.17.0.1:8081分别访问容器B和容器C。

通过这种方式我们可以使用容器A来代理所有的内网服务。

分配一个固定的ip,直接访问

使用172.18.0.0/16网络,分配固定ip,并直接访问

比如容器A是 0.0.0.0:80:80
容器B使用ip和端口: 172.18.0.10:8080
容器C使用ip和端口: 172.18.0.11:3000

这种方式需要改变容器内应用监听的ip和port号,比较麻烦。
此外没有试过在默认网络是不是也可以用。讲道理我觉得可以。

这个命令似乎有神奇的作用,有机会看一看

-p的原理

使用iptables可以查看实际进行的操作。

Docker的笔记

Docker使用Go语言开发,对进程进行封装隔离,属于操作系统层面的虚拟化技术。比虚拟机技术更加轻便,快捷。

和虚拟机的区别

  • 虚拟机技术在所有在虚拟机之上使用Hypervisor,然后每一个虚拟机包含了应用,库文件以及整个操作系统
  • Docker技术则使用Docker Engine对所有的容器进行管理,每一个容器中只有应用的库文件

Docker的好处

  • 更高效地利用系统资源(不需要完整的操作系统)
  • 更快的启动时间(比起虚拟机,不需要一个操作系统,所以很快)
  • 一致的运行环境(同样的Dockerfile,不用担心换环境导致错误)
  • 持续交付和部署(使用Dockerfile,CI/CD)
  • 轻松迁移(保证执行环境的一致性)
  • 更轻松的维护和扩展(什么是分层存储和镜像技术?)(有很多官方镜像可以使用)

镜像,容器和仓库

镜像

Docker镜像相当于一个root文件系统。Linux内核启动后会挂载root文件系统为其提供用户空间支持。

不是很懂这个

分层存储

分层存储利用了Union FS技术,docker的镜像并不是一个单一的文件,而是由一组文件组成。

镜像构建时会一层一层地构建。而在后面的层将无法改动前面的层,删除上一层的文件的操作实际上是将其标志为删除。

好处是在镜像复用或者定制时更加容易。

容器

一个镜像可以有多个容器实例,就像面向对象的类和实例的关系一样。

容器可以进行的操作有:

  • 创建
  • 启动
  • 停止
  • 删除
  • 暂停

跟虚拟机可以进行的操作很类似。

特性:

  • 容器的实质是进程,这个进程运行于独立的命名空间
  • 每一个容器运行时,会在镜像上面创建一层容器存储层,最佳实践推荐不要向存储层写入任何数据。

文件操作:

应该使用数据卷或者绑定宿主目录的方式对宿主进行读写,在这种操作方式下,容器消亡时数据并不会消亡。

仓库

Docker Registry是一个集中存储、分发镜像的服务

一个Registry里面可以包含多个仓库(Repository),每个仓库可以包含多喝标签(tag); 每个标签对应一个镜像。

比如:

  • ubuntu:16.04
  • ubuntu:latest

如果在docker pull时不指定标签,使用默认标签latest

常用的公开registry服务:

  • Docker Hub
  • Quay.io
  • Google Container Registry

常见的加速器(Registry Mirror):

  • 阿里云加速器
  • DaoCloud加速器

(配置方式呢)

Dockerfile的操作

每一个指令,都会建立一层存储

FROM

选择基础镜像,如果从空白开始则是FROM scratch

使用Go开发的应用很多会使用这种方式来制作镜像,体积更加小巧

RUN

用于执行命令行命令,注意进行一个完整的操作时应该将命令行命令写在同一个RUN中,用\换行

举例如下: (摘自 https://yeasy.gitbooks.io/docker_practice/image/build.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
ADD
CMD

CMD是容器启动时执行的命令,和RUN一样有两种格式:

  • shell格式
  • exec格式
1
2
CMD 命令
CMD ["xxx", "arg1", "arg2"]

通常推荐使用exec格式

也可以先使用ENTRYPOINT再在CMD里面放参数列表

ENTRYPOINT

ENTRYPOINT主要有两个作用:

  1. 是使镜像可以像命令一样使用
  2. 应用运行前的准备工作

第一种是将原本的CMD命令用ENTRYPOINT替换

如我们构建了一个查看ip地址的镜像,其中的CMD如下:

1
CMD [ "curl", "-s", "https://ip.cn" ]

当我们使用docker命令运行时,跟在镜像后面的部分会替换CMD,也就是我们不能通过在后面加上一些部分如-i来运行curl -s https://ip.cn -i,这个时候我们可以使用ENTRYPOINT

1
ENTRYPOINT [ "curl", "-s", "https://ip.cn" ]

这时候我们就可以docker run ip_check -i 这样来运行了(假设镜像叫ip_check)

第二种情况则是我们需要进行的一些初始化操作是需要根据命令行参数来决定的情况,可以在CMD里面放默认的启动参数,如果我们需要执行其他的操作,可以在命令行运行时指定参数,替换CMD中的参数。

ENV

ENV是用来设置环境变量的。

格式有两种:

  • ENV <key> <value>
  • ENV <key1>=<value1> <key2>=<value2>

换行使用\

ARG
VOLUME
EXPOSE

EXPOSE 指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务

所以它实际上并不能暴露端口。

如果EXPOSE和-P参数一起使用,则会使用随机映射的策略进行映射,这估计是EXPOSE的唯一作用。

-p 参数则用于手动指定端口映射。 格式大致如ip:宿主机port:容器port

这里的ip可以用的有0.0.0.0localhost以及docker中的其他网络。

WORKDIR

用于改变各层工作目录的位置。

也就是指明后面执行的命令的位置…

具体的命令有:RUN,CMD,ENTRYPOINT

USER

USER则是指定后面的命令执行时的用户身份。

(这个用户必须是之前建立好的)

HEALTHCHECK
ONBUILD

收藏

一个关于docker的gitbook: https://yeasy.gitbooks.io/docker_practice/

操作数据