2021-06-12 系统化装修 2019-11-27 初稿

惊讶地发现 docker 官网国内访问飞快,可能换CDN了,挺好,当初都打不开。

惯例,依旧是技术卡片:

新技术QA
技术名称 Docker
文档官网 https://www.docker.com/
作者、技术团队 Docker
能做解决什么问题 环境封装隔离、简单轻量部署
特点、优点 名气大,插件丰富,社区氛围好
同类选型 编排docker-swarm/k8s/mesos
缺点、踩坑注意
宣传语
选型关键词 docker、容器

docker基本上是容器技术的事实标准了。

OCI全称为开放容器标准(Open Container Initiative),它是一个轻量级、开放的治理结构。OCI组织在 Linux 基金会的大力支持下,于 2015 年 6 月份正式注册成立。基金会旨在为用户围绕工业化容器的格式和镜像运行时,制定一个开放的容器标准。目前主要有两个标准文档:容器运行时标准 (runtime spec)和容器镜像标准(image spec)。

似乎是现有了标准,倒推 docker 进行标准改革。后来 docker 公司把 docker 中的 containerd 拆分捐献给社区。撇开历史不谈了。

本文并不是 docker 使用说明书,不会完整介绍 docker ,只会在表面上介绍。目标受众是前端同学日常可使用的技巧。

主要结构:
前端可以弄懂的Docker基础 - 图1

0 概念

总体回顾时候掌握几个核心:

  • 镜像 image,可以类比npm包、系统安装盘
  • 容器 container
    • 正在运行的镜像+配置,镜像运行的实体
    • 可以被创建、运行、暂停、停止、删除
  • 仓库 hub
    • 类似npm仓库
  • 网络 network
    • 多容器网络隔离
  • 数据卷 volume
    • 数据统一存储

关于docker的基础概念,可参考知乎的回答,这里做个引用:
https://www.zhihu.com/question/28300645/answer/67707287

docker把开源项目修改成 moby 了,所以github上搜不到。

1 安装和配置

1.1 安装

再次感叹现在下载速度真快,官网找最新版下载,网速拉满。

1.2 配置加速服务

这个是针对国内的,不知道目前还有没有必要,就是配置国内的镜像地址,方便下载。

具体操作:客户端-设置Preferences - Docker Engine,把下边的json放入:

  1. {
  2. //...
  3. "registry-mirrors": [
  4. "https://mirror.ccs.tencentyun.com"
  5. ],
  6. // ...
  7. }

1.3 运行

现在 docker 的控制面板,功能非常全可视化功能很完整,原本我提到 vscode 插件功能很全,现在一对比,官方也不差。
image.png

等到docker图标不动了,打开命令行:

  1. docker -v
  2. docker --help

docker常用api一览:

前端可以弄懂的Docker基础 - 图3
常用命令一览:

  1. docker start 容器名 -d
  2. docker stop 容器名
  3. docker exec -it nginx bash

其他的一般通过 dockerfile 进行配置,后面详细说。

如何构建镜像:

  • docker commit 基于容器提交为镜像
  • docker build 从 dockerfile 构建镜像

2 DockerHub

2.1 安全性

hub类似npm,放了很多镜像。镜像有的是官方维护的,有的是个人的封装的,就和github 是一样。

如果考虑到个人封装带来的安全隐患,常见的是官方的镜像进行组合配置,托管到第三方平台,比如阿里云、腾讯云,能不能自托管我得看看,类似cnpm私有+共有还得再看看。

docker官方提供了一个镜像 https://hub.docker.com/_/registry,用来创建私有仓库。将这个镜像运行起来,就是一个私有化仓库了,可以接受push

具体如何使用,在 05 | 仓库访问:怎样搭建属于你的私有仓库?中提及了,这里略过。专业级的管理工具使用这个 https://goharbor.io/

2.2 Node 的镜像

hub也可以访问了,挺好。
因为是前端,我们ci/cd 都是基于node来完成的,打开 https://hub.docker.com/_/node,观察打开的页面结构。看到密密麻麻的 tags:

image.png

  • node: 测试时候用一下,实际中用不到,node运行在一个系统环境上,这个系统自带了常见软件包
  • node:-alpine。实际主要用这个,基于alpine系统,这个系统体积特别小,自带的工具也特别少。
  • node:-slim

一般来说,我们要使用 alpine+node具体版本号,一般不用latest防止不兼容的变更。
比如,对于2021-06-12 来说,node的lts是14.17。所以我推荐这个 14.17.0-alpine3.13 ,两遍版本都锁定。这个体积是 39.5M(解压之后会大),见下图
image.png

  1. otto@192 ~ % docker pull node:14.17.0-alpine3.13 <-- 这里去下载
  2. 14.17.0-alpine3.13: Pulling from library/node
  3. 540db60ca938: Pull complete
  4. 7cc48f18abe7: Pull complete
  5. d6ae1695c8a1: Pull complete
  6. 3f5fc9c643d6: Pull complete
  7. Digest: sha256:782e891986f16cc661bfe928d0d163d4d0e6cf5cc05453dff2093c015fcc4a64
  8. Status: Downloaded newer image for node:14.17.0-alpine3.13
  9. docker.io/library/node:14.17.0-alpine3.13 <-- 安装完成

3 Network

docker的容器网络,非常复杂且重要。

输入 docker network ls 可以看到网络情况
image.png

  • bridge 默认类型,会为容器分配一个子网ip,通常是 127.17.0.X
  • host 容器共享主机网络,直接占用主机端口,不需要 -p 来映射
    • 好处是不用配置端口
    • 坏处是缺少了网路隔离性
    • 一般不用,但是应为可以访问其他容器,可以做代理
  • none 不会分配ip,其他容器和主机都不能通过网络访问他,和外界隔离
  • 自定义bridge ,截图上是sentry的网络,比如同时存在前端-后端-数据库三个容器,会彼此依赖,如果默认bridge,就不能和其他容器网络隔离
    • 这里自定义一个网络,这几个容器在一个网络中,对外界隔离

如何创建网络 docker network create Name ,创建成功会返回id

  1. docker network create [name]
  2. docker network inspect [name|id] 可以查看网络请求
  3. #手动删除网络
  4. docekr network rm [name|id]
  5. # 清除未使用的网络
  6. docker network prune

稍微了解一些了,以前我用 docker设置nginx容器,如何跨容器管理,其实就可以使用 host了

4 容器存储

三种挂载:

  • 存储卷。
  • 绑定挂载。
  • tmpfs挂载。

首先默认的数据会产生在容器内部,如果容器被删除或者无法访问,数据取不出来,所以一般会外置挂载。

docker本身提供 存储卷,私有格式,只能被docker修改。docker能跨平台读取,容易备份迁移。
比如,如果容器依赖的卷不存在会自动创建,对卷本身可设置属性
docker volume

感觉就是个私有格式,容易操作

绑定挂载。直接挂载主机文件或者目录。可以随意修改。比如配置文件、源码、编译后代码

tmpfs。主机内存中,不写入文件,不想持久化的数据、快速大量读写的数据。

5 Dockerfile

5.1 说明

dockerfile 是配置文件,是实际工作中非常重要的一个概念。里面定义了docker镜像的封装。

属性 解释
from 第一行,基于哪个镜像
run 跟一个具体的命令
add 拷贝本机、远程文件到镜像内部
copy 拷贝本机文件到镜像内部
user 指定容器启动的用户
entrypoint 容器的启动命令
cmd 给entrypoint指令提供默认参数,也可以单独使用cmd指定容器启动参数
env 指定环境变量
arg 外部变量,可被env覆盖
expose 指定容器监听端口
workdir 设置镜像内的basePath
volume 挂载

注:

  • run 命令尽量一行写完,也就是 a命令 && b命令 ,否则构建时候会创建不同的层。
  • add vs copy ,推荐copy,功能更简单。add支持远程,压缩包会自动解压
  • cmd vs entrypoint,前者启动容器,后者容器初始化,语义不同
  • 多个cmd 只有最后一个生效
  • entrypoint 是容器初始化,一般外挂脚本
  • https://yeasy.gitbooks.io/docker_practice/

5.2 最佳实践

  • 务必创建 .dockerignore 文件。
  • 容器只运行一个应用,多容器使用 compose
  • 以为run分层,RUN及时合并
  • 指定images标签,保持一致
  • RUN之后删除无关文件。比如 apt-get update 之后,删除 /var/lib/apt/lists/目录。
  • 镜像尽量使用 alipine
  • 合理调整copy和run顺序

不要每次都安装npm。
COPY package.json /app
RUN npm install
COPY . /app

run时候 RUN npm config set registry xxx && npm i

5.3 .dockerignore

  1. .git
  2. node_module

隔离。

5.4 构建

  1. docker build -t [imageName]:[tag] .
  2. #最后有个点,用来指定构建过程的上下文环境的目录。

6 docker-compose

本质是一套脚本,通过读取 compose 配置文件,生成多个容器,统一编排。

线上单机部署也可以使用 docker-compose,但是 docker-compose 无法做到集群调度,想要把多台服务组成容器集群,推荐使用 kubernetes

前端可以弄懂的Docker基础 - 图7

  1. docker-compose up -d
  2. docker-compose down
  3. docker-compose logs nginx

7 实战

7.1 案例: Mysql+wordpress

  1. version: '3'
  2. services:
  3. mysql:
  4. image: mysql:5.7
  5. volumes:
  6. - mysql_data:/var/lib/mysql
  7. restart: always
  8. environment:
  9. MYSQL_ROOT_PASSWORD: root
  10. MYSQL_DATABASE: mywordpress
  11. MYSQL_USER: mywordpress
  12. MYSQL_PASSWORD: mywordpress
  13. wordpress:
  14. depends_on:
  15. - mysql
  16. image: wordpress:php7.4
  17. ports:
  18. - "8080:80"
  19. restart: always
  20. environment:
  21. WORDPRESS_DB_HOST: mysql:3306
  22. WORDPRESS_DB_USER: mywordpress
  23. WORDPRESS_DB_PASSWORD: mywordpress
  24. WORDPRESS_DB_NAME: mywordpress
  25. volumes:
  26. mysql_data: {}

如何生成docker镜像,要么dockerfile 逐层构建,或者操作之后统一commit,推荐第一种

7.2 案例:Node工程

假设我们有一个 npm 项目。

  1. # 使用官方 Node.js 12 轻量级镜像.
  2. # https://hub.docker.com/_/node
  3. FROM node:14.17-alpine3.11
  4. # 定义工作目录
  5. WORKDIR /usr/src/app
  6. EXPOSE 8080
  7. # 将依赖定义文件拷贝到工作目录下
  8. COPY package*.json ./
  9. copy *.lock ./
  10. # 以 production 形式安装依赖
  11. RUN npm install --only=production
  12. # 将本地代码复制到工作目录内
  13. COPY . ./
  14. # 启动服务
  15. CMD [ "npm","run", "start" ]
  16. // .dockerignore
  17. node_modules

这里为了避免缓存失效,把不常变动的操作放到前面。

  1. docker build . -t name:tagName
  2. docker run -it -d -p 8080:8080 --name xx