docker笔记

背景

近年来docker相比虚拟机,呼声越来越高,因此决定看一下

简介

自古以来有个问题,软件能不能带环境安装,不要总是在我的电脑上能跑.
对此有两个解决方案

  • 虚拟机
    • 在虚拟机上虚拟出硬件,然后在虚拟硬件上运行虚拟系统
    • 特点是资源占用多,启动慢,冗余步骤多(有可能需要用户先登陆然后再做一些操作)等等
  • Linux容器(简称LXC)
    • 最开始是利用linux内核的namespace和control group技术来隔离进程和资源,
      以创建相对独立的环境(文件系统,网络,CPU),但不独立于宿主,而是共用.脱胎与linux的jail技术.
    • 大部分的数据沟通思想是映射.比如使用-v(volume)指定数据文件的映射,-p(port)指定信息指令的映射

优点:

  • 轻量,效率高,相当于绿色软件,标准化(包含了依赖)

缺点:

  • 资源隔离不彻底(容器之间会争抢资源,容器内部的病毒会传给宿主系统)
  • 跨平台不彻底(用的是linux内核,但随着发展,也出现了windows原生docker)

Docker是LXC的一种封装,使用go语言构建.
将程序与程序的依赖打包在一个文件中,
并且提供了方便的接口供使用.
属于C/S架构,使用起来感觉像是ssh.

Docker原先由dotcloud公司开发,2013年因为经营不好索性开源,
后来火了,公司也改名Docker,Inc.
成功点在于Docker有个资源网站Docker Hub,犹如苹果有app store.

Docker的侧重在于软件的快速部署,
用户来看就像安装了一个app,然后就能用了.
比如OpenWrt用户,安装了一个叫oldiy/music-player-docker的docker,
启动后就能使用 192.158.1.1:264访问了,
而这个用户不必往自身中安装任何web应用所需的环境,语言,数据库,中间件等等.

因此docker的使用场景有

  • 提供完整环境(就比如在openwrt上安装web应用)
  • 提供弹性云服务(可以随意开关,适合扩容和缩容)
  • 组件微服务架构(多个容器一起)

安装

docker有两个版本,ce(Community Edition)和ee(Enterprise Edition),个人使用安装ce.

若在ubuntu上手动安装,需要添加docker的源,注册密钥等等.最终安装的是
docker-ce, docker-ce-cli, container.io

好在一般可以用脚本安装

1
2
3
curl -fsSL get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# sudo sh get-docker.sh --mirror Aliyun

docker运行靠的是docker组的权限,如果不是docker组,需要每次使用sudo,
把当前用户加入 docker 组,重新登陆生效

1
2
sudo groupadd docker
sudo usermod -aG docker $USER

安装确认:

1
2
docker version  # 显示一些版本信息
docker info # 显示比version更详细的信息,甚至显示当前计算机用的什么CPU

hello-world

1
docker run hello-world

下载下来的hello-world包会运行并输出一些文字,然后退出.
有些容器运行的是服务,不会在run之后自动停止,除非手动杀掉.

名词解释

镜像

镜像文件.就是把程序和程序的依赖打包成的文件.是二进制文件.
通常为了避免重复造轮子,都是继承别人的文件(最初始的应该叫scratch),加上自己的定制,然后做出自己的镜像.

docker的基础是UnionFS联合文件系统,
特点是分层(对文件系统的修改作为一次提交),轻量,高性能.
使用docker pull时可以看到下载了多个分层,越往下越靠外,最终ls时向外暴露的是最外层的名称.
好处就是容易共享.

通常一个完整的image名为
library/hello-world:latest
library为组名,表明谁发布的这个镜像,默认是官方的library组
hello-world为镜像名.
latest为标签名,通常用来表示版本,1.0,2.0等等.默认为latest.
另外docker中指定一个镜像,还可以使用image~id~.

容器

当运行起文件时,生成的就叫容器,同一个镜像可以同时生成多个容器.
对容器的操作有,run,start,stop,kill,restart等
容器的状态有7种

  • created(已创建)
  • restarting(重启中)
  • running(运行中)
  • removing(迁移中)
  • paused(暂停)
  • exited(停止)
  • dead(死亡)

仓库

就像linux系统中的各个软件仓库一样,docker也有仓库. Docker Hub.

容器文件

当容器运行起来后,会有生成一个容器文件,关闭容器后这些容器文件仍任存在(只是容器停止运行了)

docker中指定一个容器,可以用<container~id~>,也可以用<container~name~>
如果不手动使用 --name 指定,那么docker会随机生成一个不是乱码的名字.

持久化

尤其是redis这一类的docker,本身是可以将内存中的数据存到硬盘中的,
为了更加保险,docker允许以映射的方式将本地路径和docker中路径联系起来,保存文件.
-v 本地路径:docker中路径:权限 权限可以省略.
权限举例: ro表示readonly,效果是container不能编辑host上的文件,
redis可以配置 -v /host/data:/data.
另外两端不存在的文件夹会被建立.

常用命令

镜像相关

1
2
3
4
5
6
7
8
# 从仓库拉取镜像
docker pull library/hello-world # 简写为 docker pull hello-world,默认是library官方用户
# 列出本机镜像
docker image ls # 有个别名叫 docker images
# 查看镜像详细信息
docker inspect hello-world
# 删除镜像
docker image rm <image_id> # 别名 docker rmi

容器运行相关

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
# 运行
docker run hello-world # 通常要带上tag,除非是latest

# 运行
docker run \
-p 443:443 -p 80:80 -p 22:22 \ # -p指定端口映射 host端口:docker端口
--name gitlab \ # name用于手动指定container的名字
-v ~/gitlab/config:/etc/gitlab \ # -v指定卷映射 便于将docker内部文件共享到外界
-v ~/gitlab/logs:/var/log/gitlab \
-v ~/gitlab/data:/var/opt/gitlab \
-d \ # -d表示detahc,docker会后台运行,不输出log
gitlab:13.3.5-ce # 指定image名

# -i表示interactive交互模式,但hello-world这样简单的则不会
# 默认可能是-d模式
# 在交互模式下可以按C-c退出交互也停止container
# 在交互模式下可以按C-p C-q退出交互,但不退出container
# 有些情况下按C-c也无法退出,只好重启container,利用默认的-d模式,让新的container不再交互

# -e(environment)用于设置环境变量
# 比如 PUID=1000,PGID=1000,TZ(Time Zone),UMASK_SET=022(新下载的文件都是755权限),

# 列出本机容器文件
docker container ls # 用--all显示停止运行的,用-q则只显示id

# 重新连接container
docker attach <container_name/container_id>

# 让container执行一个命令,类似 ssh padvan echo 0,执行完退回宿主界面.
docker exec <container_name/container_id> <command>

# 温和停止,先发送SIGTERM信号再发送SIGKILL信号
docker stop <container_name/container_id>

# 强行停止,直接发送SIGKILL信号
docker kill <container_name/container_id>

# 启动
docker start <container_name/container_id>

# 重启
docker restart <container_name/container_id>

# 移除容器文件
docker container rm <container_name/container_id>

具体其实启动参数还是要使用 docker run --help 等命令来查看,
docker自身更新更新也算是很快了.有可能一些参数会消失.

管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 查看正在运行的docker
docker ps # 加上--all则会包括停止运行的

# 查看docker的系统占用情况
docker stats

# 查看指定container的使用情况,会返回一页类似top命令输出的结果
docker top <name>

# 查看指定container的端口使用情况
docker port <name>

# 查看指定container的log
docker logs <name>

# 拷贝文件到本地
docker cp <name>:/etc/xxx .

# 拷贝文件到容器
docker cp test.txt <name>:~/path/to/test.txt

自定义docker ps输出内容

docker提供这一功能

1
2
3
4
docker ps --format='table {{.Names}}\t{{.Status}}'
# 默认内容为
# 注意Created写为RunningFor
docker ps --format='table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.RunningFor}}\t{{.Status}}\t{{.Ports}}'

制作和发布

Docker Hub 注册一个账户后就可以往仓库中发布自己的容器了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
###############
# 制作镜像
###############
docker image build -f ./Dockerfile -t <name>:<tag> .
# -f用来指定build时所用配置文件的路径,如果是默认的./Dockerfile则整个可省
# -t用于指定镜像名和标签

###############
# 发布镜像
###############
# 需要先login
docker login
# 为指定的镜像文件koa-demos指定上自己的用户名和版本号
docker image tag koa-demos <username>/koa-demos:0.0.1
# 推送
docker image push <username>/koa-demos:0.0.1

其他命令

export和import

可能会连内存中的东西一起保存

1
2
docker export 1e560fca3906 > ubuntu.tar
docker import ubuntu.tar test/ubuntu:v1

commit

提交容器副本使之成为一个镜像.

1
2
3
4
5
6
# 对一个container做修改
docker exec -it <container_id> /bin/bash
rm /webapp/docs/

# 保存次container副本为镜像
docker commit -m='first commit, comments' -a='auther' <container_id> <image_name>:<tag>

如果不用commit来保存数据,也可以使用卷映射来设法持久化数据.

Dockerfile相关

Dockerfile 是docker在build镜像时的默认配置文件.
另外docker也支持使用 .dockerignore 文件来定义不要打包到镜像中的文件列表,比如

1
2
3
4
.git
.gitignore
node_modules
*.log

一个基础的Dockerfile如下,
另外可以在docker-library/redis等项目里找到Dockerfile的样板文件.

1
2
3
4
5
6
7
8
9
FROM node:8.4    # 继承名为node,标签为8.4的镜像
MAINTAINER your_name <your_email> # 维护者
COPY . /app # 将.路径的文件拷贝入镜像中的/app路径(除了.dockerignore指定的)
WORKDIR /app # 接下来的工作路径为/app(相当于全局cd /app,注意是镜像中的/app)
# 运行时终端默认进入的路径也是这个
RUN npm install # 为镜像中环境安装npm的依赖
EXPOSE 3000 # 允许外部连接容器的3000端口
CMD node demos/01.js # 当容器启动后自动用node命令运行容器里的node.js项目,
# 很显然这个nodejs项目入口是demos/01.js

一些基础:

  • 第一列必须大写, # 表示注释,
  • 从上到下执行,
  • docker通常会先运行一个基础的容器,然后逐条指令执行,
  • 每条操作后都会做一个类似commit的操作.直到做完.

其他的关键字

  • ENV: 配置环境变量,比如PATH等
  • VOLUME: docker会以数组形式接收需要共享的文件夹,
    出于可以移植考虑,对应host上的文件夹由docker自动生成,通常是/var/lib/docker/volumes/<container~id~>/~data~/
  • ADD: 功能类似COPY,但会自动处理URL和解压缩tar包
  • RUN: 构建docker时使用的命令
  • CMD: 容器运行起来后执行的命令,不过有多个CMD时只有最后一个生效(但build时仍然显示被commit了),
    且CMD会被命令行覆盖.
    (比如Dockerfile让使用python,而命令行让使用python exam.py则按照命令行说的算)
    (再比如Dockerfile让第一句让使用xxxdaemon --run,第二句让ls,则只会执行第二句就退出,不再启动守护程序了)
  • ENTRYPOINT: 类似CMD不会被覆盖而是增加,
    但目前观察到的都是在同一个命令里增加参数,比如Dockerfile里使用entrypoint ls,
    然后在run时,在命令的最后添加-l参数则添加称为ls -l效果.
  • ONBUILD 父镜像被继承时,父镜像的ONBUILD会被触发,通常ONBUILD后会跟其他指令,比如 ONBUILD RUN echo “FBI Warning”
  • USER 指定可以运行的用户?

愿望清单

TODO 开发环境中是如何使用docker的

TODO 改动了代码还需要更新docker吗

参考

  1. 阮一峰的介绍
  2. 菜鸟
  3. 一个比较全的参考
  4. docker0.6.5起不再支持t表示terminal
  5. 自定义ps输出内容