bingoohuang / blog

write blogs with issues
MIT License
177 stars 23 forks source link

玩转一下Docker #80

Open bingoohuang opened 5 years ago

bingoohuang commented 5 years ago

为什么要使用Docker

容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行 10 个不同的应用就要起 10 个虚拟机,而Docker 只需要启动 10 个隔离的应用即可。

具体说来,Docker 在如下几个方面具有较大的优势。

  1. 更快速的交付和部署

对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。

开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。 Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。 Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间。

  1. 更高效的虚拟化

Docker 容器的运行不需要额外的 hypervisor 支持,它是内核级的虚拟化,因此可以实现更高的性能和效率。

  1. 更轻松的迁移和扩展

Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。

  1. 更简单的管理

使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。

Docker vs VM

VM是一个运行在宿主机之上的完整的操作系统,VM运行自身操作系统会占用较多的CPU、内存、硬盘资源。Docker不同于VM,只包含应用程序以及依赖库,基于libcontainer运行在宿主机上,并处于一个隔离的环境中,这使得Docker更加轻量高效,启动容器只需几秒钟之内完成。由于Docker轻量、资源占用少,使得Docker可以轻易的应用到构建标准化的应用中。但Docker目前还不够完善,比如隔离效果不如VM,共享宿主机操作系统的一些基础库等;网络配置功能相对简单,主要以桥接方式为主;查看日志也不够方便灵活。

image

Docker 在容器的基础上,进行了进一步的封装,从文件系统、网络互联到进程隔离等等,极大的简化了容器的创建和维护。使得 Docker 技术比虚拟机技术更为轻便、快捷。

作为一种新兴的虚拟化方式,Docker 跟传统的虚拟化方式相比具有众多的优势。Docker 容器的启动可以在秒级实现,这相比传统的虚拟机方式要快得多;Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。

Docker的组成

Docker是CS架构:

  1. Docker daemon: 运行在宿主机上,Docker守护进程,用户通过Docker client(Docker命令)与Docker daemon交互
  2. Docker client: Docker 命令行工具,是用户使用Docker的主要方式,Docker client与Docker daemon通信并将结果返回给用户,Docker client也可以通过socket或者RESTful api访问远程的Docker daemon

image

Docker包括以下三个部分:

  1. Docker daemon
  2. 一套与 Docker daemon 交互的 REST API
  3. 一个命令行客户端

image

Docker的三个主要概念:

  1. Docker image:镜像是只读的,镜像中包含有需要运行的文件。镜像用来创建container,一个镜像可以运行多个container;镜像可以通过Dockerfile创建,也可以从Docker hub/registry上下载。
  2. Docker container:容器是Docker的运行组件,启动一个镜像就是一个容器,容器是一个隔离环境,多个容器之间不会相互影响,保证容器中的程序运行在一个相对安全的环境中。
  3. Docker hub/registry: 共享和管理Docker镜像,用户可以上传或者下载上面的镜像,官方地址为https://registry.hub.docker.com/,也可以搭建自己私有的Docker registry。

镜像就相当于打包好的版本,镜像启动之后运行在容器中,仓库就是装存储镜像的地方。

Hello World

Docker 中国官方镜像加速可通过registry.docker-cn.com访问。该镜像库只包含流行的公有镜像,私有镜像仍需要从美国镜像库中拉取。

修改系统中docker对应的配置文件即可,如下:

vi  /etc/docker/daemon.json
#添加后
{
    "registry-mirrors": ["https://registry.docker-cn.com"],
    "live-restore": true
}

运行下面的命令,将 image 文件从仓库抓取到本地:

docker pull library/hello-world

运行这个 image 文件:

docker run hello-world

参考

Docker入门教程

bingoohuang commented 5 years ago

常见命令

  1. 拉取镜像 docker pull image_name
  2. 查看镜像 docker images
  3. 删除镜像 docker rmi image_namedocker rmi b39c68b7af30
  4. 查看运行容器 docker ps
  5. 查看所有容器 docker ps -a
  6. 启动、停止、重启容器 docker start/stop/restart container_name/container_id
  7. 删除容器 docker rm container_name/container_id
  8. 删除所有容器 docker rm $(docker ps -a -q)
  9. 查看Docker信息 docker info
  10. 下载某个镜像 docker pull centos:latest
  11. 查找nginx镜像 docker search nginx
  12. 清理镜像 docker system prune
    docker system prune
    WARNING! This will remove:
        - all stopped containers
        - all networks not used by at least one container
        - all dangling images
        - all dangling build cache
bingoohuang commented 5 years ago

Dockerfile 使用介绍

Dockerfile 概念

Docker 镜像、容器和 Dockerfile 三者之间的关系:使用 Dockerfile 定义镜像,运行镜像启动容器。

image

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

镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。

Dockerfile 文件格式

##  Dockerfile文件格式

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..

# 1、第一行必须指定 基础镜像信息
FROM ubuntu

# 2、维护者信息
MAINTAINER docker_user docker_user@email.com

# 3、镜像操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# 4、容器启动执行指令
CMD /usr/sbin/nginx

Dockerfile 分为四部分:基础镜像信息维护者信息镜像操作指令容器启动执行指令。一开始必须要指明所基于的镜像名称,接下来一般会说明维护者信息;后面则是镜像操作指令,例如 RUN 指令。每执行一条RUN 指令,镜像添加新的一层,并提交;最后是 CMD 指令,来指明运行容器时的操作命令。

构建镜像

docker build 命令会根据 Dockerfile 文件及上下文构建新 Docker 镜像。构建上下文是指 Dockerfile 所在的本地路径或一个URL(Git仓库地址)。构建上下文环境会被递归处理,所以构建所指定的路径还包括了子目录,而URL还包括了其中指定的子模块。

将当前目录做为构建上下文时,可以像下面这样使用docker build命令构建镜像:

docker build .

说明:构建会在 Docker 后台守护进程(daemon)中执行,而不是CLI中。构建前,构建进程会将全部内容(递归)发送到守护进程。大多情况下,应该将一个空目录作为构建上下文环境,并将 Dockerfile 文件放在该目录下。

在构建上下文中使用的 Dockerfile 文件,是一个构建指令文件。为了提高构建性能,可以通过.dockerignore文件排除上下文目录下不需要的文件和目录。

在 Docker 构建镜像的第一步,docker CLI 会先在上下文目录中寻找.dockerignore文件,根据.dockerignore 文件排除上下文目录中的部分文件和目录,然后把剩下的文件和目录传递给 Docker 服务。

Dockerfile 一般位于构建上下文的根目录下,也可以通过-f指定该文件的位置:

docker build -f /path/to/a/Dockerfile .

镜像标签

docker build -t nginx/v3 .

如果存在多个仓库下,或使用多个镜像标签,就可以使用多个-t参数:

docker build -t nginx/v3:1.0.2 -t nginx/v3:latest .

Hello world

mkdir myng
cd myng
vi Dockerfile
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
myng> docker build -t nginx:v1 .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM nginx
latest: Pulling from library/nginx
f7e2b70d04ae: Already exists
08dd01e3f3ac: Pull complete
d9ef3a1eb792: Pull complete
Digest: sha256:98efe605f61725fd817ea69521b0eeb32bef007af0e3d0aeb6258c6e6fe7fc1a
Status: Downloaded newer image for nginx:latest
 ---> 881bd08c0b08
Step 2/2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
 ---> Running in ae9928d8c8ed
Removing intermediate container ae9928d8c8ed
 ---> aa17cf1dd2fb
Successfully built aa17cf1dd2fb
Successfully tagged nginx:v1
myng> docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               v1                  aa17cf1dd2fb        5 seconds ago       109MB
influxdb            latest              da30d5f51b22        2 weeks ago         239MB
nginx               latest              881bd08c0b08        2 weeks ago         109MB
mysql               latest              91dadee7afee        2 weeks ago         477MB
myng> docker run  --name docker_nginx_v1   -d -p 8011:80 nginx:v1
d34574891fd6f9a9d2968af64c5a2e4f96869292c6e95efe9be3922ca18c3535

image

修改容器内容

myng>docker exec -it docker_nginx_v1   bash
root@3729b97e8226:/# echo '<h1>Hello, Docker neo!</h1>' > /usr/share/nginx/html/index.html
root@3729b97e8226:/# exit
exit

修改了容器的文件,也就是改动了容器的存储层,可以通过 docker diff 命令看到具体的改动。

docker diff docker_nginx_v1 

参考

  1. Dockerfile 使用介绍
  2. Dockerfile 命令详解
  3. Intro Guide to Dockerfile Best Practices
bingoohuang commented 5 years ago

Docker三剑客

docker-machine是解决docker运行环境问题。

docker技术是基于Linux内核的cgroup技术实现的,那么问题来了,如果在非Linux平台上使用docker技术需要依赖安装Linux系统的虚拟机。docker-machine就是docker公司官方提出的,用于在各种平台上快速创建具有docker服务的虚拟机的技术。你可以把它理解为virtualbox或者vmware,最开始在win7上用得比较多,但是win10开始自带了hyper-v虚拟机,已经不再需要docker-machine了,docker可以直接运行在安装了Linux系统得hyper-v上。

docker-compose主要是解决本地docker容器编排问题。

一般是通过yaml配置文件来使用它,这个yaml文件里能记录多个容器启动的配置信息(镜像、启动命令、端口映射等),最后只需要执行docker-compose对应的命令就会像执行脚本一样地批量创建和销毁容器。

docker-swarm是解决多主机多个容器调度部署得问题。

swarm是基于docker平台实现的集群技术,他可以通过几条简单的指令快速的创建一个docker集群,接着在集群的共享网络上部署应用,最终实现分布式的服务。swarm技术相当不成熟,很多配置功能都无法实现,只能说是个半成品,目前更多的是使用Kubernetes(k8s)来管理集群和调度容器。

总结

如果你是在非Linux环境下考虑使用docker-compose,当然我更推荐使用hyper-v或者virtualbox。如果你需要同时操作多个容器,或者希望使用配置文件记录容器启动命令参数,那么推荐使用docker-compose。如果你需要在多台主机上部署docker容器,并对其进行调度,那么swarm是一种选择,当然更推荐Kubernetes。

参考

  1. Docker 三剑客之 Docker Compose
  2. docker三剑客的区别
  3. Docker — 从入门到实践
bingoohuang commented 5 years ago

多数 Dockerfile 示例可能都不够严谨

改造前:

# DO NOT USE THIS DOCKERFILE AS AN EXAMPLE, IT IS BROKEN
# Python版本的从python:3的不确定性
FROM python:3

# Docker 的层缓存对提高构件速度很有帮助。但是如果把 COPY 操作放在 pip install 前面,所有后续的层就都失效了,也就是说这一镜像会完全重新构建。
COPY yourscript.py /

# 依赖库版本的不确定性
RUN pip install flask

# 缺省情况下,Docker 容器是用 root 身份运行的,这并不安全。
CMD [ "python", "./yourscript.py" ]

改造后:

# Python版本的从python:3的不确定性调整为确定性 
FROM python:3.7
# 依赖库版本的不确定性从pip install flask调整为requirements.txt的确定性
COPY requirements.txt /tmp/

RUN pip install -r /tmp/requirements.txt

# 缺省情况下,Docker 容器是用 root 身份运行的,这并不安全。
# 建议:如果不是有特定需要,例如监听 1024 以下的端口或者完成一些必须 root 身份的操作,建议使用非 root 账号。
RUN useradd --create-home appuser
WORKDIR /home/appuser
USER appuser

# Docker 的层缓存对提高构件速度很有帮助。但是如果把 COPY 操作放在 pip install 前面,所有后续的层就都失效了,也就是说这一镜像会完全重新构建。
# 建议:在合适的时机进行文件复制。
COPY yourscript.py .

CMD [ "python", "./yourscript.py" ]
bingoohuang commented 4 years ago

docker cp 在容器和本地文件系统之间复制文件/文件夹

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
940dd65346de        mysql:5.7           "docker-entrypoint.s…"   15 seconds ago      Up 13 seconds       3306/tcp, 33060/tcp   docker-compose-mysql-master-master_mm2_1
e422e3a22338        mysql:5.7           "docker-entrypoint.s…"   15 seconds ago      Up 13 seconds       3306/tcp, 33060/tcp   docker-compose-mysql-master-master_mm1_1
$ docker cp 940dd65346de:/usr/bin/mysql .
$ docker cp 940dd65346de:/usr/bin/mysqldump .
$ ls -l
total 17776
-rwxr-xr-x  1 bingoobjca  staff  4688624  6 10 22:58 mysql
-rwxr-xr-x  1 bingoobjca  staff  4408760  6 10 22:58 mysqldump
$ docker cp --help

Usage:  docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
    docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

Copy files/folders between a container and the local filesystem

Options:
  -a, --archive       Archive mode (copy all uid/gid information)
  -L, --follow-link   Always follow symbol link in SRC_PATH
bingoohuang commented 4 years ago

容器里有啥,用Inspect container images and their metadata看看

image

bingoohuang commented 4 years ago

十大 Docker 反模式

https://mp.weixin.qq.com/s/K68G-AbNbHceD8MWmBjtvw

https://codefresh.io/containers/docker-anti-patterns/

  1. 试图将 VM 实践用于容器
  2. 创建不透明的 Dockerfile
  3. 创建有副作用的 Dockerfile
  4. 混淆了用于开发的镜像和用于部署的镜像
  5. 为每个环境创建一个不同的镜像
  6. 在生产服务器上拉取 git 代码并在线构建镜像
  7. 基于 git 源码而非 Docker 镜像进行团队协作
  8. 在容器镜像中硬编码密钥和配置
  9. 大而全-把 Docker 用作穷人的 CI/CD
  10. 小而不美-把容器只当成打包工具用
bingoohuang commented 3 years ago

Purging All Unused or Dangling Images, Containers, Volumes, and Networks

https://www.digitalocean.com/community/tutorials/how-to-remove-docker-images-containers-and-volumes

Docker provides a single command that will clean up any resources — images, containers, volumes, and networks — that are dangling (not associated with a container):

docker system prune

To additionally remove any stopped containers and all unused images (not just dangling images), add the -a flag to the command:

docker system prune -a