Docker学习笔记第二篇--镜像和容器

Docker学习第二篇🌶,这篇我们将学习Docker的镜像和容器以及原理。

先阅读这篇参考文章再阅读这篇笔记:https://mp.weixin.qq.com/s/Ndu17Pa_8Y-RfgqfwqjTSg

Docker的架构和底层技术简介

image-20181022112421891

Docker Platform
  1. 首先我们明确一下Docker提供了一个开发、打包、运行app的平台

  2. app和底层infrastructure隔离开来

image-20181021210142195

我们从上图看到Docker Engine将底层的物理设备和虚拟设备和上层Application隔离开来,这样我们就可以在Docker之上做一些事情。

Docker Engine

Docker Engine是什么呢?

image-20181021210643471

我们看一下上面的架构图,Docker Engine由下面几个部分组成:

后台进程(dockerd)

REST API Server

CLI接口(docker)

后台进程就是对Docker的镜像,容器,网络,存储进行管理。

Docker 客户端是 Docker 用户与 Docker 交互的主要方式。当您使用 docker 命令行运行命令时, Docker 客户端将这些命令发送给服务器端,服务端将执行这些命令。 docker 命令使用 docker APIDocker 客户端可以与多个服务端进行通信。

我们可以在命令行下查看一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@hongshaorou ~]#  docker version
Client:
Version: 18.06.1-ce
API version: 1.38
Go version: go1.10.3
Git commit: e68fc7a
Built: Tue Aug 21 17:23:03 2018
OS/Arch: linux/amd64
Experimental: false

Server:
Engine:
Version: 18.06.1-ce
API version: 1.38 (minimum version 1.12)
Go version: go1.10.3
Git commit: e68fc7a
Built: Tue Aug 21 17:25:29 2018
OS/Arch: linux/amd64
Experimental: false

上面是显示了客户端和服务端的一些信息。

1
2
3
[root@hongshaorou ~]# ps -ef | grep docker
root 17519 1 0 Oct20 ? 00:01:37 /usr/bin/dockerd
root 17525 17519 0 Oct20 ? 00:01:15 docker-containerd --config /var/run/docker/containerd/containerd.toml

上面就是守护进程。

Docker Architecture

我们看下Docker的总体架构图

image-20181021211916501

Docker 的核心组件包括:

  1. Docker Client
  2. Docker daemon
  3. Docker Image
  4. Docker Registry
  5. Docker Container

Docker 采用的是 Client/Server 架构。客户端向服务器发送请求,服务器负责构建、运行和分发容器。客户端和服务器可以运行在同一个 Host 上,客户端也可以通过 socketREST API 与远程的服务器通信。

Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。

Linux 容器是 Linux 发展出了另一种虚拟化技术,简单来讲, Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离,相当于是在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。

Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker ,就不用担心环境问题。

Linux对Docker底层技术支持

Namespaces:做隔离pid,net,ipc,mnt,uts

Control groups:做资源限制(对不同容器进行资源管理)

Union file systemsContainerimage的分层

底层技术还是依赖于Unix的一些技术。

Docker Image概述

下面是关于Image的一些定义:

image-20181021214626935

不包含内核空间信息 只是用户空间信息

所谓的共享layer就是image4image2都是使用了底层的Base image

Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。我们可将 Docker 镜像看成只读模板,通过它可以创建 Docker 容器。

镜像有多种生成方法:

  1. 从无到有开始创建镜像
  2. 下载并使用别人创建好的现成的镜像
  3. 在现有镜像上创建新的镜像

我们可以将镜像的内容和创建步骤描述在一个文本文件中,这个文件被称作 Dockerfile ,通过执行 docker build <docker-file> 命令可以构建出 Docker 镜像

1
2
3
[root@hongshaorou ~]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest 4ab4c602aa5e 6 weeks ago 1.84kB

我们可以使用docker image ls查看当前主机有哪些镜像。

从远程仓库拉取被人创建好的镜像文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@hongshaorou ~]#  docker pull ubuntu:14.04
14.04: Pulling from library/ubuntu
027274c8e111: Pull complete
d3f9339a1359: Pull complete
872f75707cf4: Pull complete
dd5eed9f50d5: Pull complete
Digest: sha256:e6e808ab8c62f1d9181817aea804ae4ba0897b8bd3661d36dbc329b5851b5637
Status: Downloaded newer image for ubuntu:14.04

#
[root@hongshaorou ~]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 14.04 f216cfb59484 2 days ago 188MB
hello-world latest 4ab4c602aa5e 6 weeks ago 1.84kB

我们可以在Docker Hub进行搜索镜像,然后拉取下来:https://hub.docker.com/explore/

上面我们知道Base Image是基于Linux的内核之上为每个Linux发行版做的镜像。

一个小知识,如何在普通用户执行Docker命令的时候不加sudo

1
2
3
4
5
6
7
8
# 新建用户组
sudo groupadd docker

# 将普通用户加入到用户组
sudo gpasswd -a user docker

# 重启docker服务
sudo systemctl restart docker
什么是Container?

下面是关于Container的一些定义:

通过Image创建

Image Layer之上建立一个container layer(可读写层)

类比面向对象:类(镜像)和实例(镜像)

Image负责app的存储和分发,Container负责运行app

image-20181022140909601

Docker 容器就是 Docker 镜像的运行实例,是真正运行项目程序、消耗系统资源、提供服务的地方。 Docker Container 提供了系统硬件环境,我们可以使用 Docker Images 这些制作好的系统盘,再加上我们所编写好的项目代码, run 一下就可以提供服务啦。

我们看下现在系统中有哪些镜像:

1
2
3
4
[root@hongshaorou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 75835a67d134 12 days ago 200MB
hello-world latest 4ab4c602aa5e 6 weeks ago 1.84kB

我们运行通过镜像文件来运行一个容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@hongshaorou ~]# docker run hello-world

Hello from Docker!


# 查看当前正在运行的容器
[root@hongshaorou ~]# docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

# 查看所有容器 包括已经退出的容器
[root@hongshaorou ~]# docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5dc27362322f hello-world "/hello" 28 seconds ago Exited (0) 22 seconds ago peaceful_haibt
02b2e9b24c53 hello-world "/hello" 46 hours ago Exited (0) 46 hours ago tender_aryabhata

之所以运行完hello-world之后容器立即停止,是因为它不是一个常驻内存的容器。有些容器不会自动终止,因为提供的是服务,比如Mysql镜像等。

如果我们不想立即退出容器,就需要以交互的形式进入容器:

1
2
[root@hongshaorou ~]# docker run -it centos
[root@d046d1529fc9 /]#

因为Container是一个读写层,我们可以在这个层进行一个读写操作

1
2
3
4
[root@d046d1529fc9 /]#  yum install vim
[root@d046d1529fc9 /]# touch text
[root@d046d1529fc9 /]# exit
exit

对于已经退出的容器我们可以使用rm删除掉

1
2
3
4
5
6
7
8
[root@hongshaorou ~]# docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d046d1529fc9 centos "/bin/bash" 7 minutes ago Exited (0) 18 seconds ago clever_nightingale
127b37b722e1 centos "/bin/bash" 8 minutes ago Exited (0) 8 minutes ago serene_bardeen
5dc27362322f hello-world "/hello" 15 minutes ago Exited (0) 15 minutes ago peaceful_haibt
02b2e9b24c53 hello-world "/hello" 47 hours ago Exited (0) 47 hours ago tender_aryabhata
[root@hongshaorou ~]# docker container rm d046d1529fc9
d046d1529fc9

但是当退出的数量过多的时候,我们一个一个删有些麻烦。。。。

下面是一些命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# 查看所有正在运行的docker
[root@hongshaorou ~]# docker container ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

# 查看包括退出的docker
[root@hongshaorou ~]# docker container ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
127b37b722e1 centos "/bin/bash" 11 minutes ago Exited (0) 11 minutes ago serene_bardeen
5dc27362322f hello-world "/hello" 17 minutes ago Exited (0) 17 minutes ago peaceful_haibt
02b2e9b24c53 hello-world "/hello" 47 hours ago Exited (0) 47 hours ago tender_aryabhata

# 查看所有的镜像
[root@hongshaorou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 75835a67d134 12 days ago 200MB
hello-world latest 4ab4c602aa5e 6 weeks ago 1.84kB

# 删除指定的镜像 这里失败的原因是因为存在引用镜像的容器还没有删除 导致镜像无法删除
[root@hongshaorou ~]# docker rmi 4ab4c602aa5e
Error response from daemon: conflict: unable to delete 4ab4c602aa5e (must be forced) -
image is being used by stopped container 02b2e9b24c53

# 获得所有容器的ID
[root@hongshaorou ~]# docker container ls -aq
127b37b722e1
5dc27362322f
02b2e9b24c53

# 删除全部的容器
[root@hongshaorou ~]# docker rm $(docker container ls -aq)
127b37b722e1
5dc27362322f
02b2e9b24c53

[root@hongshaorou ~]# docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

[root@hongshaorou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 75835a67d134 12 days ago 200MB
hello-world latest 4ab4c602aa5e 6 weeks ago 1.84kB

# 再次删除刚才的镜像文件
[root@hongshaorou ~]# docker rmi 4ab4c602aa5e
Untagged: hello-world:latest
Untagged: hello-world@sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Deleted: sha256:4ab4c602aa5eed5528a6620ff18a1dc4faef0e1ab3a5eddeddb410714478c67f
Deleted: sha256:428c97da766c4c13b19088a471de6b622b038f3ae8efa10ec5a37d6d31a2df0b

[root@hongshaorou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 75835a67d134 12 days ago 200MB

当既存在运行中又存在已退出的容器时候,如果删除全部退出容器呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@hongshaorou ~]# docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fec80e18fa17 centos "/bin/bash" About a minute ago Up About a minute vibrant_bohr
5108bfc07a86 centos "/bin/bash" 2 minutes ago Exited (0) 2 minutes ago stupefied_perlman

# 使用filter 过滤出已经退出的容器
[root@hongshaorou ~]# docker container ls -f "status=exited"
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5108bfc07a86 centos "/bin/bash" 2 minutes ago Exited (0) 2 minutes ago stupefied_perlman

# 获得退出容器的ID
[root@hongshaorou ~]# docker container ls -f "status=exited" -q
5108bfc07a86
[root@hongshaorou ~]# docker rm $(docker container ls -f "status=exited" -q)
5108bfc07a86
[root@hongshaorou ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fec80e18fa17 centos "/bin/bash" 2 minutes ago Up 2 minutes vibrant_bohr
构建自己的Docker镜像

这个小节我们主要学习两个命令:

1
2
3
4
5
6
7
[root@hongshaorou ~]# docker container commit
"docker container commit" requires at least 1 and at most 2 arguments.
See 'docker container commit --help'.

Usage: docker container commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

Create a new image from a container's changes

这个命令可以从一个容器中创建一个镜像,简写docker commit

1
2
3
4
5
6
7
[root@hongshaorou ~]# docker image build
"docker image build" requires exactly 1 argument.
See 'docker image build --help'.

Usage: docker image build [OPTIONS] PATH | URL | -

Build an image from a Dockerfile

这个命令可以从一个Dockerfile中创建一个镜像,简写docker build

我们先学习第一个命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 创建一个容器并进入到交互页面
[root@hongshaorou ~]# docker run -it centos

# 安装 vim
[root@5d79bbcf0f35 /]# yum -y install vim

# 退出
[root@5d79bbcf0f35 /]# exit
exit

# 查看退出容器
[root@hongshaorou ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5d79bbcf0f35 centos "/bin/bash" About a minute ago Exited (0) 7 seconds ago mystifying_edison

# 查看commit的命令
[root@hongshaorou ~]# docker commit
"docker commit" requires at least 1 and at most 2 arguments.
See 'docker commit --help'.

Usage: docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

Create a new image from a container's changes

# 创建镜像 指定容器名字 镜像名字
[root@hongshaorou ~]# docker commit mystifying_edison hongshaorou/centos-vim
sha256:ef527e01da739f55b79606eb8490e6a2ec711d9943f0a84d92da608b036b648d

# 查看创建好的镜像
[root@hongshaorou ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hongshaorou/centos-vim latest ef527e01da73 35 seconds ago 355MB
centos latest 75835a67d134 12 days ago 200MB

# 查看 baseimage 的分层结构
[root@hongshaorou ~]# docker history 75835a67d134
IMAGE CREATED CREATED BY SIZE COMMENT
75835a67d134 12 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 12 days ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 12 days ago /bin/sh -c #(nop) ADD file:fbe9badfd2790f074… 200MB

# 查看新建的 image 的分层结构
[root@hongshaorou ~]# docker history ef527e01da73
IMAGE CREATED CREATED BY SIZE COMMENT
ef527e01da73 2 minutes ago /bin/bash 155MB
75835a67d134 12 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 12 days ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 12 days ago /bin/sh -c #(nop) ADD file:fbe9badfd2790f074… 200MB

我们看到新的镜像和之前的镜像有重复利用的层,只是在原来的基础镜像上新增了一层。

下面我们看从Dockerfile创建镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[root@hongshaorou ~]# mkdir docker-mkdir-vim
[root@hongshaorou ~]# cd docker-mkdir-vim/

# 创建Dockerfile
[root@hongshaorou docker-mkdir-vim]# vim Dockerfile
FROM centos
RUN yum install vim -y

# 本地的Dockerfile创建镜像 -t 指定标签
[root@hongshaorou docker-mkdir-vim]# docker build -t hongshaorou/centos-vim-new .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM centos
---> 75835a67d134
Step 2/2 : RUN yum install vim -y
---> Running in 1b94e692dc29
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
* base: mirrors.cn99.com
* extras: mirrors.aliyun.com
* updates: mirrors.aliyun.com
Resolving Dependencies

Complete!
Removing intermediate container 1b94e692dc29
---> 9acfd79f47c6
Successfully built 9acfd79f47c6
Successfully tagged hongshaorou/centos-vim-new:latest

我们看到构建镜像的第一步是基于初始的镜像层,第二步是新建了一个临时容器。然后在临时容器中安装vim安装成功后删容器,最后基于临时的容器commit了一个镜像文件。

查看创建的镜像

1
2
3
4
[root@hongshaorou docker-mkdir-vim]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hongshaorou/centos-vim-new latest 9acfd79f47c6 5 minutes ago 355MB
centos latest 75835a67d134 12 days ago 200MB
Dockerfile语法梳理及最佳实践

Dockerfile 中所有的命令都是以下格式:INSTRUCTION argument ,指令 (INSTRUCTION) 不分大小写,但是推荐大写,和sql语句是不是很相似呢?下面我们正式来学习一下这些指令集吧。

FROM

FROM 是用于指定基础的 images ,一般格式为 FROM <image> or FORM <image>:<tag> ,所有的 Dockerfile 都用该以 FROM 开头,FROM 命令指明 Dockerfile 所创建的镜像文件以什么镜像为基础,FROM 以后的所有指令都会在 FROM 的基础上进行创建镜像。可以在同一个 Dockerfile 中多次使用 FROM 命令用于创建多个镜像

1
2
3
FROM scratch  # 制作base image
FROM centos # 使用base image
FROM ubuntu:14.04 # 带标签

建议尽量都使用官方的 base image

LABEL

这个指令主要描述了镜像文件的信息如:作者,版本,描述等

1
2
3
LABEL maintainer="hongshaorou@gmail.com"
LABEL version="1.0"
LABEL description="This is description"

MAINTAINER 一般必不可少

WORKDIR

WORKDIR 用于配合 RUNCMDENTRYPOINT 命令设置当前工作路径。可以设置多次,如果是相对路径,则相对前一个 WORKDIR 命令。默认路径为/。一般格式为 WORKDIR /path/to/work/dir

1
2
3
WORKDIR /test	# 如果没有会自动创建test目录
WORKDIR demo
RUN pwd # 输出结果应该是 /test/demo

用WORKDIR,不要用RUN cd !尽量使用绝对目录!

ADD and COPY

COPY 是用于复制本地主机的 <src> (为 Dockerfile 所在目录的相对路径)到容器中的 <dest>

当使用本地目录为源目录时,推荐使用 COPY 。一般格式为 COPY <src><dest>

ADD也是将本地文件复制到容器中。

ADDCPOY有一个不同点是不仅可以拷贝还可以解压。

1
2
3
4
5
6
7
8
9
10
ADD hellp /	# 将hello文件复制到容器

ADD test.tar.gz / # 添加到根目录并解压

# 和 WORKDIR联合使用的时候
WORKDIR /root
ADD hello test/ # /root/test/hello 将文件拷贝到新建的目录下

WORKDIR /root
COPY hello test/ # /root/test/hello 将文件拷贝到新建的目录下

大部分情况,COPY优先 ADD使用!

ADD除了COPY还有额外功能(解压)!

添加远程文件/目录请使用RUN curl或者wget!

ENV

使用ENV设置常量或环境变量

1
2
3
ENV MYSQL_VERSION 5.6  #设置常量
RUN apt-get install -y mysql-server="${MYSQL_VERSION}" \
&& rm -rf /var/lib/apt/lists/* # 引用上面使用的常量

尽量使用ENV 增加可维护性!

EXPOSE

EXPOSE 命令用来指定对外开放的端口。一般格式为 EXPOSE <port> [<port>...]

1
EXPOSE 5000
VOLUME

后续补充

Shell和Exce格式

我们看下命令的两种格式

Shell格式

1
2
3
RUN apt-get install -y vim
CMD echo "hello docker"
ENTRYPOINT echo "hello docker"

Exce格式

1
2
3
4
5
RUN ["apt-get",  "install",  "-y",  "vim"]

CMD ["/bin/echo", "hello docker"] # 指定命令具体位置

ENTRYPOINT ["/bin/echo", "hello docker"]

Exce情况下是不能直接引用常量的,需要指定Shell环境。而使用Shell格式则会使用默认的Shell环境执行命令。

1
2
3
FROM centos
ENV name Docker
ENTRYPOINT ["/bin/bash", "-c", "echo hello $name"]
RUN

RUN 用于容器内部执行命令创建新的Image Layer。每个 RUN 命令相当于在原有的镜像基础上添加了一个改动层,原有的镜像不会有变化。一般格式为 RUN <command>

1
2
RUN yum update && yum install -y vim \
python-dev # 反斜线换行

为了美观,复杂的RUN请用反斜线换行! 避免无用分层,合并多条命令成一行!(使用 &&)

ENTRYPOINT

ENTRYPOINT 设置容器启动时运行的命令,可以让容器以应用程序或者服务的形式运行。

一个 Dockerfile 中只能有一个 ENTRYPOINT,如果有多个,则最后一个生效。

不会被忽略,一定会执行(CMD则可能会被忽略)。

ENTRYPOINT 命令也有两种格式:

  • ENTRYPOINT ["executable", "param1", "param2"] :推荐使用的 exec形式
  • ENTRYPOINT command param1 param2shell 形式

最佳实践:写一个shell脚本作为entrypoint

1
2
3
4
5
COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 27017
CMD ["mongod"]
CMD

CMD 设置容器启动后默认指定的命令和参数,CMD 命令可以包含可执行文件,也可以不包含可执行文件。不包含可执行文件的情况下就要用 ENTRYPOINT 指定一个,然后 CMD 命令的参数就会作为ENTRYPOINT的参数。

CMD 命令有三种格式:

  • CMD ["executable","param1","param2"]:推荐使用的 exec 形式。
  • CMD ["param1","param2"]:无可执行程序形式
  • CMD command param1 param2:shell 形式。

一个 Dockerfile 中只能有一个CMD,如果有多个,则最后一个生效。而 CMDshell 形式默认调用 /bin/sh -c 执行命令。

如果docker run指定了其他命令,CMD 命令会被 Docker 命令行传入的参数覆盖:docker run busybox /bin/echo Hello Docker 会把 CMD 里的命令覆盖。

设置容器启动后默认指定的命令和参数

Dockerfile官方文档:https://docs.docker.com/engine/reference/builder/#from

官方镜像的Dockerfile参考:https://github.com/docker-library

镜像的发布
Dockerfile 实战

这个小节我们将学习将一个python文件放在容器中运行,并能访问到文件对应的服务。

1
2
3
4
5
6
7
8
9
10
from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
return "Hello World!"


if __name__ == '__main__':
app.run()

我们运行上述代码需要python环境和安装flask模块。

下面编写Dockerfile

1
2
3
4
5
6
7
FROM python:3.7	# 指定Python3.7为基础镜像
LABEL maintainer="Hong Shaorou"
RUN pip install flask # 安装必要包
COPY app.py /app/app.py # 拷贝代码到容器中
WORKDIR /app
EXPOSE 5000
CMD ["python", "app.py"] # 运行程序

通过Dockerfile创建镜像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@hongshaorou flask-hello]# docker build -t flask-hell .
Sending build context to Docker daemon 3.072kB
Step 1/7 : FROM python:3.7
---> a187104266fb
Step 2/7 : LABEL maintainer="Hong Shaorou"
---> Running in 55914be36a54
Removing intermediate container 55914be36a54
---> 7beb75bddab4
Step 3/7 : RUN pip install flask
---> Running in 37a8948142e8
Removing intermediate container 37a8948142e8
---> 1e07504373aa
Step 4/7 : COPY app.py /app/app.py
---> ba579aefd9fe
Step 5/7 : WORKDIR /app
---> Running in 359dfd275690
Removing intermediate container 359dfd275690
---> afca2f5cf283
Step 6/7 : EXPOSE 5000
---> Running in a3d926e2abbd
Removing intermediate container a3d926e2abbd
---> 57a42013406d
Step 7/7 : CMD ["python", "app.py"]
---> Running in b431bc07d332
Removing intermediate container b431bc07d332
---> 474f01263bba
Successfully built 474f01263bba
Successfully tagged flask-hell:latest

构建镜像的临时镜像 我们是可以 运行的

运行容器:

1
2
3
4
5
6
7
8
9
[root@hongshaorou flask-hello]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
flask-hell latest 474f01263bba 2 minutes ago 933MB

centos latest 75835a67d134 2 weeks ago 200MB

# 后台运行容器
[root@hongshaorou flask-hello]# docker run -d 474f01263bba
2172f9a1b4b345ea5bfc277341bfb160f3752fbe3fcfd7f6933bf0c7ad6f57b1
容器的操作

我们上面创建了一个容器,现在我们学习一些关于容器的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 查看运行中的容器
[root@hongshaorou flask-hello]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2172f9a1b4b3 474f01263bba "python app.py" 3 minutes ago Up 3 minutes 5000/tcp vibrant_darwin

# 使用 exec 进入到运行的容器 并且指定运行命令
[root@hongshaorou flask-hello]# docker exec -it 2172f9a1b4b3 /bin/bash
root@2172f9a1b4b3:/app# pwd
/app
root@2172f9a1b4b3:/app# ls
app.py
root@2172f9a1b4b3:/app# ps -ef | grep python
root 1 0 0 09:06 ? 00:00:00 python app.py
root 12 6 0 09:10 pts/0 00:00:00 grep python
root@2172f9a1b4b3:/app# exit
exit

[root@hongshaorou flask-hello]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2172f9a1b4b3 474f01263bba "python app.py" 35 minutes ago Up 35 minutes 5000/tcp vibrant_darwin

# 暂停容器
[root@hongshaorou flask-hello]# docker stop 2172f9a1b4b3
2172f9a1b4b3

# 查看所有的容器 包括退出的
[root@hongshaorou flask-hello]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2172f9a1b4b3 474f01263bba "python app.py" 36 minutes ago Exited (137) 27 seconds ago vibrant_darwin

# 删除所有的容器
[root@hongshaorou flask-hello]# docker rm $(docker ps -aq)
2172f9a1b4b3

# 运行容器的时候 给容器起个唯一的名字
[root@hongshaorou flask-hello]# docker run -d --name=demo flask-hell
364eac27b3e052cd84ab48ef3a1a12fb7bf9ca20dd461e3b7cff514f5da671e8
[root@hongshaorou flask-hello]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
364eac27b3e0 flask-hell "python app.py" 6 seconds ago Up 5 seconds 5000/tcp demo

# 使用给容器起的名字进行一些操作
[root@hongshaorou flask-hello]# docker stop demo
demo
[root@hongshaorou flask-hello]# docker start demo
demo

# 查看容器的具体详细信息
[root@hongshaorou flask-hello]# docker inspect demo
[
{
"Id": "364eac27b3e052cd84ab48ef3a1a12fb7bf9ca20dd461e3b7cff514f5da671e8",
"Created": "2018-10-24T09:44:05.868220178Z",
"Path": "python",
"Args": [
"app.py"
],

# 查看容器的运行时日志
[root@hongshaorou flask-hello]# docker logs 364eac27b3e0
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: Do not use the development server in a production environment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

命令的官方文档:https://docs.docker.com/engine/reference/commandline/container/

Dockerfile的另一种实践

上面我们使用Dockerfile生成了常驻内存的Docker容器。还可以生成一直具有交互式设计的容器。

1
2
3
4
FROM ubuntu
RUN apt-get update && apt-get install -y stress
ENTRYPOINT ["/usr/bin/stress"] # ENTRYPOINT 和 空的CMD一起使用
CMD [] # 空的CMD接收docker run 传入的参数
1
2
# 通过在docker run的时候 可以传递参数给 Docker file中空的CMD
docker run -it ubuntu-stress --vm 1 --verbose
容器的资源限制

我们在启动容器的时候,可以对使用资源进行一些限制。

当我们新建一个容器的时候,如果没有限制资源的话将会无限制使用主机资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-m, --memory bytes                   Memory limit
--memory-reservation bytes Memory soft limit
--memory-swap bytes Swap limit equal to memory plus swap: '-1' to enable unlimited swap
--memory-swappiness int Tune container memory swappiness (0 to 100) (default -1)
--mount mount Attach a filesystem mount to the container
--name string Assign a name to the container
--network string Connect a container to a network (default "default")
--network-alias list Add network-scoped alias for the container
--no-healthcheck Disable any container-specified HEALTHCHECK
--oom-kill-disable Disable OOM Killer
--oom-score-adj int Tune host's OOM preferences (-1000 to 1000)
--pid string PID namespace to use
--pids-limit int Tune container pids limit (set -1 for unlimited)
--privileged Give extended privileges to this container

我们通过--memory指定内存大小,--memory-swap指定swa大小。如果没有指定swap就是和memory一样大小。

1
2
# 限制内存使用
docker run --memory=200M # 因为没有限制swap 所以总共内存是 400M
1
2
3
4
-c, --cpu-shares int                 CPU shares (relative weight)
--cpus decimal Number of CPUs
--cpuset-cpus string CPUs in which to allow execution (0-3, 0,1)
--cpuset-mems string MEMs in which to allow execution (0-3, 0,1)

CPU设置限制不是个数,是相对权重。

1
docker run --cpu-shares=10

当有多个容器的时候 是相对权重可以类别NGINX的负载均相对权重

容器的资源限制,底层技术就是Control groups

容器的文件系统,底层技术是Namespaces

容器的分层,底层技术是Union file systems

知识就是财富
如果您觉得文章对您有帮助, 欢迎请我喝杯水!