介绍容器(Docker)的原理和常见的命令。
概念
Docker 是对于 Linux 容器的一种封装,提供了见到那的容器使用接口。Linux 容器是一种虚拟化技术,在正常的进程外面加上了一个保护层,实现了该进程与底层系统的隔离。Docker 是使用Go语言实现的。
(1)虚拟机(virtual machine)和Docker 的联系和区别
虚拟机是在一个系统中运行了另一个系统。比如在mac 电脑中装上了windows(stupid);在windows 中装上了Ubuntu。
相同点:软件开发中环境可能存在两种问题:”程序能在我的机器上跑“ (It worked on my machine);程序中新老版本不兼容,如python2 和python3 中一些库函数。虚拟机和docker 都是为了解决软件开发中的环境配置问题。
不同点:虚拟机是操作系统级别。虚拟机本身需要占用几百兆运行,并且启动慢。Docker 是对于进程的封装,不是模拟完整的操作系统。所以启动快,占用资源少。
联系:Docker 相比于虚拟机有了更大程度的共享,更小级别的隔离。虚拟机虚拟出的是一套硬件,而Docker 虚拟出的是一个操作系统,不同容器之间是可以共享一个操作系统的。
(2)Docker的核心概念
1). 镜像(Image): 是一个只读的模板,类似安装操作系统的iso 文件,通过镜像可以用来完成各种应用的部署。
Image 是一个特殊的文件系统。包含了运行时所需要的程序、库、资源、配置等文件。镜像不包含任何动态的数据。dockerfile 中最开始的 from 表示的就是镜像
2). 容器(Container):镜像和容器的唯一区别是容器的最上一层是可读可写的。所以容器 =镜像+ 可读层。类似一个操作系统,可以create, start, run,stop,kill,pause和rm操作。
最后打成某个程序,就是容器。
3). 仓库:存放镜像的一个场合,仓库分为公开仓库和私有仓库
如果说Docker 是面向对象的,那么容器与镜像的关系类似于面向对象编程中的对象与类。
Docker 的优点
Docker 是一个用于开发,交付和运行应用程序的开放平台。Docker 使您能够将应用程序与基础架构分开,从而可以快速交付软件。借助 Docker,您可以与管理应用程序相同的方式来管理基础架构。通过利用 Docker 的方法来快速交付,测试和部署代码,您可以大大减少编写代码和在生产环境中运行代码之间的延迟。
快速,一致地交付您的应用程序
响应式部署和扩展
在同一硬件上运行更多工作负载
Docker 的缺点:对于开发者是非常友好的,但是对于运维人员,想要在已经封装好的容器上做一些调整,是很难的。
(3)在Ubuntu 18 上的安装和使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 安装常规操作,读取软件列表
$ sudo apt-get update
# 添加 docker 官方 GPG key
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
# 设置稳定仓库
$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
# 安装 Docker Engine
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
|
安装比较简单,这种安装的Docker不是最新版本,不过对于学习够用了,依次执行下面命令进行安装。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
sudo apt install docker.io
sudo systemctl start docker
sudo systemctl enable docker
sudo service docker start #启动守护进程
docker -v
sudo service docker stop #关闭守护进程
#【查】查看本地已有的镜像 Docker images
docker images
# 增加
docker run docker_name
|
测试是否安装成功
1
|
sudo docker run hello-world
|
设置用户组(这样用户就不用使用 sudo
查看docker 的信息)
设置非root账号不用sudo直接执行docker命令
参考这里进行设置
1
2
3
|
$ sudo groupadd docker #添加docker用户组
$ sudo gpasswd -a $USER docker #将当前用户添加至docker用户组
$ newgrp docker #更新docker用户组
|
Docker 常用命令
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
|
#启动docker
sudo service docker start
#停止docker
sudo service docker stop
#重启docker
sudo service docker restart
#列出Docker CLI命令
docker
docker container --help
#显示Docker版本和信息
docker --version
docker version
docker info
Execute Docker image
docker run hello-world
#列出镜像列表
docker image ls
#列出docker容器 (running, all, all in quiet mode)
docker container ls
docker container ls --all
docker container ls -aq
#搜索:
docker search ubuntu
#下载镜像
docker pull ubuntu
#查看所有的镜像
docker images
#删除docker 包
sudo rm -rf /var/lib/docker
|
我们需要确认容器有在运行,可以通过 docker ps 来查看
1
2
3
|
CONTAINER ID:容器ID
NAMES:自动分配的容器名称
|
在容器内使用docker logs命令,查看容器内的标准输出
可以使用 docker logs ID来查看log,也可以通过后面名称来查docker logs silly_poitras
停止容器 docker stop id or docker_name 这样的形式。
查看预装情况
1
2
3
4
5
|
docker -v
docker-compose -v
docker ps # 查看目前的容器名
docker-compose ps #查看当前项目的容器状态
|
https://github.com/ashokc/Serving-Flask-on-Docker/blob/master/quoteserver/docker-compose.yml
从这里看,应该是定义了全局的docker 名称,包括 app_host 等一系列名称,可以在之后的debug 中看看,这些名称是否是全局可达的。
使用docker 的时候默认是sudo 权限,为了避免麻烦,这个时候可以新建一个docker 用户组,将使用的用户加入到docker 用户组中.
1
|
sudo usermod -aG docker jeng
|
1
|
sudo systemctl restart docker
|
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
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
|
FROM nginx # 这个是基础镜像,之后都是基于该镜像
# 镜像的作者信息,包含所有者和联系人
MAINTAINER
# shell格式 (所以是可以根据本地的shell命令构建dockerfile)
RUN <命令行命令>
# <命令行命令> 等同于,在终端操作的 shell 命令。
# exec格式
RUN ["可执行文件", "参数1", "参数2"]
# 例如:
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline
RUN <command> (shell模式)
RUN [ "executable", "param1", "param2" ] (exec模式)
# 多行执行run
RUN yum install wget
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
RUN tar -xvf redis.tar.gz
# 更加建议这种书写,只创建一层镜像
RUN yum install wget \
&& wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
&& tar -xvf redis.tar.gz
# 是从上下文目录复制文件或者目录到指定路径
COPY home* /mydir/
ENV
# 设置环境变量,在后序的指令中,可以使用这个变量
VOLUME
# 定义数据卷,用于挂载数据,避免因重启而丢失重要数据
EXPOST <PORT>
# 声明端口,告诉应用程序容器内的程序会使用的端口,在运行时使用 -p 参数映射
# docker处于安全的目的,不会自动打开端口。
USER
# 指定镜像使用什么用户去运行
USER nginx
WORKDIR <dirpath>
# 在 Dockerfile 文件中,WORKDIR一般是绝对路径,也可是相对路径,不过,是相对于上一个WORKDIR的路径。
# docker 很多情况下是用来打包一个程序,如果docker 打包了这个python 程序(你可以把所有依赖工具打包进docker镜像, 然后用 ENTRYPOINT 指向python脚本本身,当然也可以使用 cmd 指向脚本。但是通常用 ENTRYPOINT 表明docker 镜像只是用来执行这个python 脚本的,不希望用于其他的操作)
ENTRYPOINT 和CMD 的组合使用: ENTRYPOINT 指定默认的运行命令,CMD指定默认的运行参数。比如
# 例子一
FROM ubuntu:trusty
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
# 例子二
ENTRYPOINT [ "python" ] # 这个是命令
# Run app.py when the container launches
CMD [ "app.py","run","--host","0.0.0.0"] # 这个是参数
|
dockerfile常见的一些写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
# install system dependencies
RUN apt-get update \
&& apt-get -y install gcc make \
&& rm -rf /var/lib/apt/lists/*
# 最后的rm 也是可以减少镜像的大小,是一种优化策略
# install dependencies
RUN pip install --no-cache-dir --upgrade pip
RUN pip install --no-cache-dir -r requirements.txt
# --no-cache-dir Disable the cache.
# 如果在国内为了方便测试可以使用国内镜像
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
|
部署
cd 到某个路径中,该路径中有 dockerfile
1
2
3
4
5
6
7
8
9
10
11
|
# build 的命令 -> image 。最后的一个 . 表示上下文路径, 当执行 build 时候,会将该路径下的所有内容打包,所以一定要把没有必要的文件删掉
docker build .
docker build --tag image_name .
# run 的命令 -> docker ps 中可查
docker run --name deployML -p localport:dockerport image_name
# 实现的是端口映射
docker exec -it container_name # docker 进入容器里面
docker stop my_container
|
docker 替换国内镜像
请首先执行以下命令,查看是否在 docker.service
文件中配置过镜像地址。
1
|
$ systemctl cat docker | grep '\-\-registry\-mirror'
|
如果该命令有输出,那么请执行 $ systemctl cat docker
查看 ExecStart=
出现的位置,修改对应的文件内容去掉 --registry-mirror
参数及其值,并按接下来的步骤进行配置。
(1)直接修改 /etc/docker/daemon.json
(docker 版本 >= 1.10 时) 内容为:
阿里云的docker 镜像需要注册,这里使用的是腾讯、百度和网易的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
{
"runtimes": {
"nvidia": {
"path": "nvidia-container-runtime",
"runtimeArgs": []
}
},
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com",
"https://mirror.baidubce.com",
"https://hub-mirror.c.163.com"
]
}
|
修改之后重启服务
1
2
|
systemctl daemon-reload
systemctl restart docker
|
docker-compose
Docker-Compose 是 Docker 的一种编排服务,是一个用于在 Docker 上定义并运行复杂应用的工具,可以让用户在集群中部署分布式应用。
通过 Docker-Compose 用户可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。
Docker Compose 工作原理图
Compose 中有两个重要的概念
- 服务 (service) :一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。
- 项目 (project) :由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。
(1) stop all the runing containers
(2) restart the containders ( after using the stop command)
(3) Starts the containers that were started with docker-compose up
:
(4) Stop all running containers, and remove them and the network:
1
|
docker-compose down [--rmi all]
|
使用以下的命令理解更强
1
|
docker-compose -f docker-compose.yml up -d hello-world
|
-d 表示在后台启动容器
hello-world 是这个名字
docker-comose 中一些术语
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
version: '2.0'
services:
notebook:
build:
context: ./notebook
ports:
- '8888:8888'
volumes:
- ./notebook:/home/jovyan
links:
- milvus
milvus:
image: milvusdb/milvus:0.9.1-cpu-d052920-e04ed5
ports:
- '19530:19530'
- '19121:19121'
volumes:
- ./milvus/db:/var/lib/milvus/db
- ./milvus/conf:/var/lib/milvus/conf
- ./milvus/logs:/var/lib/milvus/logs
- ./milvus/wal:/var/lib/milvus/wal
|
services :多个容器的集合
build: 配置构建时,Compose 会利用它自动构建镜像,该值可以是一个路径,也可以是一个对象,用于指定 Dockerfile 参数
1
2
3
4
5
6
7
|
build: ./dir
---------------
build:
context: ./dir
dockerfile: Dockerfile
args:
buildno: 1
|
ports: 对外暴露的端口定义 和 exose 对应, “宿主机端口:容器暴露端口”
1
2
3
|
ports: # 暴露端口信息 - "宿主机端口:容器暴露端口"
- "8763:8763"
- "8763:8763"
|
links: 服务之间可以使用服务名称相互访问,links 允许定义一个别名,从而使用该别名访问其它服务
参考文献
阿里云的docker 的使用
Docker 学习新手笔记:从入门到放弃
制作 docker 的教程:
迈出使用Docker的第一步,学习第一个Docker容器
有背景图家具替换demo:http://47.92.49.9:9998/
白背景家具替换demo:http://47.92.49.9:9999/