Docker
Docker简介 -- Docker Engine
Docker Engine 也就是我们常说的 "Docker",是一种 C/S 模型,Docker服务端是一个服务进程,管理着所有的容器,Docker 客户端则扮演着 Docker 服务端的远程控制器,可以用来控制 Docker 的服务端进程。
用户通过 Docker 客户端向 Docker Daemon 发送 REST 请求。
Docker 包括这么几个部分:
- Docker Daemon — Docker 的守护进程,属于 C/S 中的服务端
- Docker REST API — Docker Daemon 向外暴露的 REST 接口
- Docker CLI — Docker 向外暴露的命令行接口(Command Line API)
一般用命令行在客户端使用 Docker,比如
docker run ... # 运行容器
docker ps # 查看正在运行的容器
Docker优势
- 容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,Docker 对系统资源的利用率更高,空闲的内存可以被宿主机和其他容器使用,相同的资源相较于虚拟机可以部署更多容器。
- Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性。无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。
- Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。
- Docker 团队同各个开源项目团队一起维护了一大批高质量的官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。
Docker 三大组件
- Docker Client:Docker 客户端,提供给用户一个终端,用户输入 Docker 提供的命令来管理本地或远程的服务器。
- Docker Daemon:服务端守护进程,接收 Client 发送的命令并执行相应的操作(DOCKER_HOST 环境变量默认是本机的 localhost:2375)。
- Docker Container:Docker 容器,提供了运行环境,基于 Docker Images 启动,在 Docker Images 基础上运行应用。
- Docker Images:Docker 镜像,一个特殊的文件系统,封装了运行时需要的库、资源、应用等。
- Docker Registry:Docker 仓库,用来管理镜像,实现镜像的上传、下载、浏览。
镜像
Docker 镜像是一个特殊的文件系统,这个文件系统封装了运行时需要的库、资源、应用等。
docker pull ubuntu:18.04 # 获取镜像
docker images # 列出已经下载下来的镜像
docker image rm # 删除镜像
docker run -d -p xxxx:xxxx --name xxxx ubuntu:18.04 # 以ubuntu 18.04为基础镜像运行容器
容器
Docker 容器本质上是一个进程,运行于自己独立的命名空间中,所有的数据都存储在容器中。容器销毁后,这些数据也随之消失。多个容器互相之间是隔离的,一个容器所做的任何变更都只影响容器本身。
docker ls # 列出所有在运行的容器
docker ls -a # 列出所有容器
docker logs [container ID or NAMES] # 获取容器的输出信息
docker stop # 终止一个运行中的容器
docker rm [container ID or NAMES] # 删除一个运行中的容器
docker rm -f [container ID or NAMES] # 删除一个容器
docker run -d -p 8000:5000 --name demo ubuntu:18.04 # 在ubuntu 18.04 上运行名字是demo的容器,外部端口8000,容器端口5000
仓库
通常情况下我们可以使用 https://hub.docker.com/ 作为 Docker image 的仓库,但是有些场景下,我们希望能够有本地的仓库。使用 Docker 容器运行 registry 镜像的方式,来创建本地仓库。
使用 Docker
1. 数据卷
默认容器的数据是保存在容器的可读写层,当容器被删除时其上的数据将会丢失,所以为了实现数据的持久性则需要选择一种数据持久技术来保存数据。
数据卷是存在于一个或多个容器中的特定文件或文件夹,这个文件或文件夹以独立于 Docker 文件系统的形式存在于宿主机中,其生存周期独立于容器的生存周期。
对于容器的应用来说,数据卷是透明的,无法感知数据卷的存在,因此删除容器的时候,不会影响到数据卷。
数据卷优势:
- 多容器可以通过挂载同一个数据卷,实现数据共享。
- 数据卷可以方便的备份、存储数据
# 创建一个名为test的数据卷
docker volume create test
# 列出所有的数据卷
docker volume ls
# 删除数据卷
docker volume rm test
我们使用 mount 选项来挂载数据卷。--mount 由多个键-值对组成,由逗号分隔,每一对由 key=value 组成。下面是 mount 参数的 key :
- 挂载的类型(type),可以是绑定(bind mounts)、卷(volumes)或tmpfs mounts。
- 挂载源(source),对于命名卷,是卷的名称。
- 挂载目标(target)的值是将文件或目录安装在容器中的路径。
数据卷挂载方式
1.volumes
在多个容器间共享数据,希望将数据存储在远程主机或云提供商上,实现数据持久化,Docker 默认在主机上会有一个特定的区域(Linux系统上是 /var/lib/docker/volumes/),该区域用来存放 volume。
非 Docker 进程不可以去修改该区域。volume 是由Docker 创建的,可以通过 docker volume 进行管理,如创建、删除等操作。
生产环境---volume方式实现代码数据持久化
# 创建一个名为demovolume1数据卷
docker volume create demovolume1
# 在运行容器demo时,使用--mount参数
docker run -d --name demo --mount type=volume, source=demovolume2,targe=/volume ubuntu:18.04
# 其中--mount参数的含义是,把名为 demovolume2 的数据卷挂载到名为 demo 容器的 /volume 目录上
# 在启动容器时,自动创建数据卷 demovolume2
--mount source=demovolume,target=/volume
适用场景:
- 在不同容器中共享数据。
- 生产环境下,在远程服务器上部署代码。
- 需要备份数据或迁移数据,在对应的文件找到数据卷(/var/lib/docker/volumes/)
2.bind mounts
可以被挂载在主机的任何地方,非Docker应用程序可以改变这些数据。在宿主机和容器间共享代码。
例如将宿主机某个项目的target目录挂载到容器中,这样在宿主机上修改代码,可以直接在容器中运行,而不用生成一个新的镜像。
适用场景:
- 宿主机和容器之间共享数据,比如配置文件。
- 开发环境下,宿主机和容器之间共享代码。
开发环境---共享目录实现代码实时更新
在开发环境里面,我们不可能每更新一次代码,就重新建立一个带有更新后的代码数据卷的镜像。我们可以把代码目录挂载到共享目录,开发时在本地主机修改代码时,Docker 实时生效。使用--mount参数type=bind表明要挂载在共享目录。
# 把本地主机当前目录 "${PWD}" 挂载到容器的/share目录下
docker run -d --name demo --mount type=bind,source="${PWD}",target=/share ubuntu:18.04
# 把本地主机当前目录下的/test.py文件挂载到容器的/share目录下
docker run -d --name demo --mount type=bind,source="${PWD}"/test.html,target=/share ubuntu:18.04
2. 利用Dockerfile构建自定义镜像
如果在一个基础镜像启动容器,我们采用命令行的方式,需要配置镜像,安装程序,运行容器,如果要重新启动一个容器,我们需要再重新配置一次。
Docker 可以让我们自己写配置镜像的文件(Dockerfile,注意大小写)来构建镜像。在实际应用中,我们可以把整个运行程序打包成一个新的 Docker 镜像,从镜像直接运行容器。
# 在 Dockerfile 文件中 # 是注释
# FROM 用于指定构建镜像使用的基础镜像
FROM ubuntu:18.04
# RUN 用于在构建镜像的时候在镜像中执行命令
# 安装 python3 和 相关的库
RUN apt update
RUN apt -y install python3 python3-pip
RUN pip3 install flask
RUN pip3 install gevent
RUN pip3 install flask_redis
RUN pip3 install requests
# 把本机当前目录下的 mpa-admin 文件拷贝到镜像的 /mpa-admin
# COPY 会自动创建镜像中不存在的目录
COPY mpa-admin /mpa-admin
# WORKDIR 用于指定从镜像启动的容器内的工作目录
WORKDIR /mpa-admin
# CMD 用于指定容器运行后要执行的命令和参数列表
# 这样从本镜像启动容器后会自动执行 python3 server.py 这个命令
# 在 /mpa-admin 下执行的
CMD ["python3", "server.py"]
在完成Dockerfile配置后,我们可以从镜像运行容器
docker build -t mpa_admin_image . # 在当前目录构建镜像
docker run -p 8000:7050 --name mpa_admin_1 mpa_admin_image # 运行容器
3.利用docker-compose管理多容器
Docker 的容器,是一个容器运行一个程序,但是在实际项目中,我们很多时候需要多个程序配合一起运行。
比如,web 程序里就需要包含server.py, redis, mysql等等。
Docker 官方推出 Compose 程序用于配置、管理多容器运行。Compose 通过单独的 docker-compose.yml 文件来管理一组容器。
Compose 把一组容器作为一个项目运行,并会设置好容器之间的互联的内部网络。
# 表示这是 compose 配置文件第三版
version: '3'
# 每个服务都是一个 Docker 容器
# 所以必须用 image 指定服务的镜像名或者从 Dockerfile 中 build 镜像
services:
mpa_admin:
# build 指定了 Dockerfile 所在的路径
build: .
# ports 指定暴露的端口,8000 是宿主机,7050 是容器使用端口
# docker 容器的网络, 是相对于实体机的私有网络
# 端口映射, 把容器的端口和实体机端口成对连通
ports:
- "8000:7050"
# depends_on 设定了依赖,这里 redisdemo 会先于 mpa_web 启动
# 但是如果 redisdemo 启动时间长于 mpa_admin
# 那么 mpa_web 运行的时候 redisdemo 未必可用
depends_on:
- redisdemo
# volumes 参数把当前目录挂载到容器的 /mpa_admin
# docker-compose 的配置中支持相对路径的挂载
volumes:
- .:/mpa_admin
# redis:alpine 是 redis 项目的官方 Docker 镜像
redisdemo:
image: "redis:alpine"
其中 mpa-admin 容器是通过当前目录的 Dockerfile 进行构建,同时将当前目录挂在到 /mpa_admin 目录,而 redis 则直接使用官方进行。
项目mpa-admin实践
- 安装 docker
- 安装 docker-compose
- 配置 Dockerfile 文件
# FROM 指定基础镜像
FROM ubuntu:18.0 安装 python3 和 flask web 框架 mpa-admin
RUN apt update
RUN apt -y install python3 python3-pip
RUN pip3 install flask
RUN pip3 install gevent
RUN pip3 install flask_redis
RUN pip3 install requests
#COPY mpa-admin /mpa-admin
# WORKDIR 用于指定从镜像启动的容器内的工作目录
WORKDIR /mpa-admin
# 在 /mpa-admin 下执行的
CMD ["sh", "devup.sh"]
- 配置 docker-compose.yml 文件
..:/mpa-admin:ro # 只读
version: '3'
services:
pyweb:
build: .
ports:
- "8075:7075"
# volumes 关键字,将当前主机上的工程目录以 bind mounts 方式
# 挂载到容器中的/mpa-admin 目录,这样就允许在不重复构建镜像实时修改代码
volumes:
- ..:/mpa-admin
- 启动项目:
Docker 会根据当前的目录下得Dockerfile构建基础镜像,并且运行 devup.sh 脚本。
同时由于使用了 volumes 挂载了本地目录到/mpa-admin,我们就可以直接在本地使用编辑器去编写代码,并且更新的代码能够实时被重新加载。
docker-compose up # 启动compose里的项目
docker-compose down # 关闭并删除compose项目的容器