Docker初步了解

简介

什么是docker

Docker从狭义上来讲就是一个进程,从广义上来讲是一个虚拟容器,其实更专业的叫法是应用容器( Application Container ),Docker进程和普通的进程没有任何区别,它就是一个普通的应用进程。不过是用来操作镜像文件的。所以Docker进程+构建的应用镜像文件就等于Docker容器。

image

docker images

image

Docker containers

Docker生命周期

image
1、 开发构建镜像并将镜像push到Docker仓库
2、 测试或者运维从Docker仓库拷贝一份镜像到本地
3、 通过镜像文件开启Docker容器并提供服务

docker 的作用

  • 构建容易分发简单
  • 隔离应用解除依赖
  • 快速部署测完就销

docker 与虚拟机的区别

image

docker架构

image

  • namespaces充当隔离的第一级,是对Docker容器进行隔离,让容器拥有独立的hostname,ip,pid,同时确保一个容器中运行一个进程而且不能看到或影响容器外的其它进程;
  • Cgroups是容器对使用的宿主机资源进行核算并限制的关键功能。比如CPU,内存,磁盘等;
  • union FS主要是对镜像也就是image这一块作支持,采用copy-on-write技术,让大家可以共用某一层,对于某些差异层的话就可以在差异的内存存储;
  • Libcontainer是一个库,是对上面这三项技术做一个封装。
  • Docker engine 用来控制容器container的运行,以及镜像文件的拉取。

安装

该安装针对Ubuntu18.06LTS系统

卸载旧版本

旧版本的 Docker 称为docker或者docker-engine,使用以下命令卸载旧版本:

1
2
3
sudo apt-get remove docker \
docker-engine \
docker.io

使用APT安装

由于 apt 源使用 HTTPS以确保软件下载过程中不被篡改。因此,我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书。

1
2
3
4
5
6
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
software-properties-common

鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。为了确认所下载软件包的合法性,需要添加软件源的 GPG 密钥。

1
2
3
curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
#官方源
#curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

然后,我们需要向 source.list 中添加 Docker 软件源

1
2
3
4
5
6
7
8
9
10
11
$ sudo add-apt-repository \
"deb [arch=amd64] https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu \
$(lsb_release -cs) \
stable"


# 官方源
# $ sudo add-apt-repository \
# "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
# $(lsb_release -cs) \
# stable"

以上命令会添加稳定版本的 Docker CE APT镜像源,如果需要测试或每日构建版本的 Docker CE 请将 stable 改为 test 或者 nightly。

安装 Docker CE

更新 apt 软件包缓存,并安装 docker-ce:

1
2
sudo apt update
sudo apt install docker-ce

启动Docker CE

1
2
3
systemctl start docker.service
# 或者
sudo service docker start

建立 docker 用户组

默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。

建立 docker 组:

1
sudo groupadd docker

将当前用户加入 docker 组:

1
sudo usermod -aG docker $USER

退出当前终端并重新登录,进行如下测试。

测试 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
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

镜像加速

国内从 Docker Hub拉取镜像有时会遇到困难,此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务,例如:
1.Docker 官方提供的中国 registry mirror https://registry.docker-cn.com
2.七牛云加速器 https://reg-mirror.qiniu.com/

当配置某一个加速器地址之后,若发现拉取不到镜像,请切换到另一个加速器地址。国内各大云服务商均提供了 Docker 镜像加速服务,建议根据运行 Docker 的云平台选择对应的镜像加速服务。

对于使用 systemd 的系统,请在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件)

1
2
3
4
5
{
"registry-mirrors": [
"https://registry.docker-cn.com"
]
}

检测加速器是否生效

1
2
3
4
5
docker info 

# 如果在结果中看到Registry Mirrors:
# https://registry.docker-cn.com/
# 证明生效了

docker info 的时候会抛出异常,vim/etc/default/grub 找到GRUB_CMDLINE_LINUX="" ,在双引号里面输入cgroup_enable=memory swapaccount=1 ,然后执行: sudo update-grub reboot 搞定。

镜像

获取镜像

1
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]

运行

1
2
3
4
5
6
7
8
docker run -it --rm \
ubuntu:18.04 \
/bin/bash

# -i 代表的是交互操作 -t代表的是终端
# --rm代表的是退出容器后随之将其删除
# /bin/bash 代表的是希望使用的shell 这里使用的是bash shell

列出镜像

1
2
3
docker image ls 
# 或者
docker images

虚悬镜像

镜像既没有仓库名,也没有标签,均为
这类无标签镜像也被称为 ==虚悬镜像(dangling image)== ,可以用下面的命令专门显示这类镜像:

1
docker image ls -f  dangling=true

一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除。

1
docker image prune

列出部分镜像

1
2
3
4
5
6
# 根据仓库名列出镜像
docker image ls ubuntu
# 列出指定的镜像
docker image ls ubuntu:18.04
# -f 参数过滤
docker image ls -f before=ubuntu:18.04 # 在ubuntu:18.04之前构建的镜像

以特定格式显示镜像

1
2
3
4
5
# 只显示镜像id
docker image ls -q
# --filter 显示自己想要显示的格式
docker image ls --format "{{.ID}}: {{.Repository}}"
docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"

删除本地镜像

1
2
3
docker image rm [选项] <镜像1> [<镜像2> ...]
# 或者
docker image rm $(docker image ls -q redis)

Dockerfile定制镜像

1
2
3
4
# 创建Dockerfile
touch Dockerfile
# 构建image
docker build [选项] <上下文路径/URL/->

镜像构建上下文

docker运行原理

Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计。

docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。

实际上 Dockerfile 的文件名并不要求必须为 Dockerfile,而且并不要求必须位于上下文目录中,比如可以用 -f ../Dockerfile.php 参数指定某个文件作为 Dockerfile。

当然,一般大家习惯性的会使用默认的文件名 Dockerfile,以及会将其置于镜像构建上下文目录中。

其他docker bulid构建方式

1
2
3
4
5
6
7
8
# 从URL中构建
docker build https://github.com/....
# 从 tar 包中构建
docker bulid https://server/context.tar.gz
#从标准输入中读取 Dockerfile 进行构建
docker build - < Dockerfile
#或者
cat Dockerfile | docker build -

Dockerfile 指令

1
2
3
4
5
6
7
8
9
10
11
12
FROM(指定基础镜像)
RUN(执行命令行命令的)
COPY (复制文件)
ADD(更高级复制文件,不推荐使用)
CMD(容器启动命令,推荐使用exec模式 CMD["可执行文件", "参数1", "参数2"...])
ENTRYPOINT(入口点)
ENV(设置环境变量)
ARG(构建参数)
VOLUME(定义匿名卷)
EXPOSE(声明端口)
WORKDIR(指定工作目录)
USER(当前用户)

image

容器

新建并启动

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
62
63
64
65
66
67
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

#
# 指定容器运行于前台还是后台,默认为false
-d,--detach=false
# 打开STDIN,用于控制台交互
-i, --interactive=false
# 让docker 分配一个伪终端并绑定到容器的标准输入上
-t, --tty=false
# 指定容器的用户
-u, --user=""
# 登录容器(必须是以docker run -d启动的容器) 指定标准输入输出内容类型,可选 STDIN/STDOUT/STDERR 三项;
-a, --attach=[]
# 指定容器的工作目录
-w, --workdir=""
# 设置容器CPU权重,在CPU共享场景使用
-c, --cpu-shares=0
# 指定环境变量,容器中可以使用该环境变量
-e, --env=[]
# 指定容器的内存上限
-m, --memory=""
# 指定容器暴露的端口
-P, --publish-all=false
# 指定容器暴露的端口
-p, --publish=[]
# 指定容器的主机名
-h, --hostname=""
# 给容器挂载存储卷,挂载到容器的某个目录
-v, --volume=[]
# 给容器挂载其他容器上的卷,挂载到容器的某个目录
--volumes-from=[]
# 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities
--cap-add=[]
# 删除权限
--cap-drop=[]
# 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法
--cidfile=""
# 设置容器可以使用哪些CPU,此参数可以用来容器独占CPU
--cpuset=""
# 添加主机设备给容器,相当于设备直通
--device=[]
# 指定容器的dns服务器
--dns=[]
# 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件
--dns-search=[]
# 覆盖image的入口点
--entrypoint=""
# 指定环境变量文件,文件格式为每行一个环境变量
--env-file=[]
# 指定容器暴露的端口,即修改镜像的暴露端口
--expose=[]
# 指定容器间的关联,使用其他容器的IP、env等信息
--link=[]
# 指定容器的配置文件,只有在指定--exec-driver=lxc时使用
--lxc-conf=[]
# 指定容器名字,后续可以通过名字进行容器管理,links特性需要使用名字
--name=""
# 容器网络设置: bridge 使用docker daemon指定的网桥, host:容器使用主机的网络,container:NAME_or_ID使用其他容器的网路,共享IP和PORT等网络资源, none 容器使用自己的网络(类似--net=bridge),但是不进行配置
--net="bridge"
# 指定容器是否为特权容器,特权容器拥有所有的capabilities
--privileged=false
# 指定容器停止后的重启策略:no:容器退出时不重启,on-failure:容器故障退出(返回值非零)时重启,always:容器退出时总是重启
--restart="no"
# 指定容器停止后自动删除容器(不支持以docker run -d启动的容器)
--rm=false
# 设置由代理接受并处理信号,但是SIGCHLD、SIGSTOP和SIGKILL不能被代理
--sig-proxy=true

当用docker run 创建一个容器的时候,Docker在后台运行的标准操作包括:

  • 检查本地是否存在指定镜像,不存在就从共有仓库下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层面挂载一层可读写层。
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池中分配一个ip地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

启动已经终止的容器

1
2
3
4
# 可以列出所有已经启动和已经停止的容器
docker container ls -a
# 启动一个已经终止的容器
docker container start containerId

容器后台运行

1
2
3
docker run -d 
# 如果想要获取容器输出的信息
docker container logs containerId

终止容器

1
docker container stop containerId

重启容器

1
docker container restart containerId

进入容器

docker进入一个后台运行的容器有两种方式

docker attach 命令

docker attach是Docker 自带的命令。 如果从容器的stdin中退出的话,该容器会停止。

exec (推荐)

用该命令进入stdin中的然后exit,容器不会停止

1
docker exec -it containerId

导入容器快照

1
docker import

导出容器快照

1
docker export

删除容器

1
docker container rm containerId

清理所有处于终止状态的容器

1
docker container prune

Docker Compose 编排

通过单独的docker-compose.yml模板文件(YAML格式)定义一组相关联的应用容器为一个项目。
compose中有两个重要的概念:

  • 服务(service):一个应用的容器,实际上可以包括若干个运行相同镜像的容器实例。
  • 项目(project):由一组关联的应用容器组成的一个完整业务单元,在docker-compser.yml文件中定义。

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 二进制包安装 
# 安装
curl -L https://github.com/docker/compose/releases/download/1.8.0/run.sh > /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
# 卸载 直接删除二进制包就行
rm -rm
/usr/local/bin/docker-compose


# php安装
sudo pip install -U docker-compose
#卸载
pip uninstall docker-compose


# bash 补全命令
curl -L https://raw.githubusercontent.com/docker/compose/1.8.0/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose

docker-compose.yml 属性详解

一份标准的配置文件包括version、services、networks三大部分,其中最关键的是services和networks两个部分。

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
version: '2'
services:
web:
image: dockercloud/hello-world
ports:
- 8080
networks:
- front-tier
- back-tier

redis:
image: redis
links:
- web
networks:
- back-tier

lb:
image: dockercloud/haproxy
ports:
- 80:80
links:
- web
networks:
- front-tier
- back-tier
volumes:
- /var/run/docker.sock:/var/run/docker.sock

networks:
front-tier:
driver: bridge
back-tier:
driver: bridge
version

docker-compose.yml语法版本.单引号和双引号都行。
Compose区分Version 1和Version 2,还有version 3。Version 1将来会被弃用。
版本1指的是忽略version关键字的版本;版本2必须在行首添加version: ‘2’。

Version 1

未声明版本的组合文件被视为“版本1”。

在这些文件中,所有服务都在文档的根目录处声明。

版本1由Compose到1.6.x支持。 它将在未来的Compose版本中被弃用。

版本1文件无法声明命名卷,网络或构建参数。

Version 2

使用版本2语法的撰写文件必须指示文档根目录下的版本号。 所有服务必须在服务键下声明。

Compose 1.6.0+支持版本2文件,并需要版本1.10.0+的Docker引擎。

可以在volumes键下声明命名卷,并且可以在networks关键字下声明网络。

services
  1. image

    1
    2
    3
    services:
    web:
    images: hello-world

    在services标签下的第二级标签是web,这个是自己定义的,指的是服务的名称。
    image是指定的服务镜像的名称或者镜像id,如果本地不存在,compose会尝试pull这个镜像.支持下面写法。

    1
    2
    3
    4
    5
    image: redis
    image: ubuntu:14.04
    image: tutum/influxdb
    image: example-registry.com:4000/postgresql
    image: a4bc65fd
  2. bulid
    服务除了可以基于指定的镜像,还可以基于一份 Dockerfile,在使用 up 启动之时执行构建任务,这个构建标签就是 build,它可以指定 Dockerfile 所在文件夹的路径。Compose 将会利用它自动构建这个镜像,然后使用这个镜像启动服务容器。
    如果你同时指定了 image 和 build 两个标签,那么 Compose 会构建镜像并且把镜像命名为 image 后面的那个名字。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    build: /path/to/build/dir
    #也可以是相对路径
    build: ./dir
    #设定上下文根目录,然后以该目录为准指定 Dockerfile。
    build:
    context: ../
    dockerfile: path/of/Dockerfile
    # 指定环境变量 (该环境变量是构建时候的环境变量)
    build:
    context: .
    args:
    - buildno=1
    - password=secret

    # 允许空值
    args:
    - buildno
    - password

    注意:YAML 的布尔值(true, false, yes, no, on, off)必须要使用引号引起来(单引号、双引号均可),否则会当成字符串解析。

  3. command
    使用 command 可以覆盖容器启动后默认执行的命令。

    1
    2
    3
    command: bundle exec thin -p 3000
    #或者
    command: [bundle, exec, thin, -p, 3000]
  4. container_name
    指定容器名称

    1
    container_name: app
  5. depends_on
    解决了容器的依赖、启动先后的问题。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 该服务先启动db和redis服务 最后才启动web服务
    version: '2'
    services:
    web:
    build: .
    depends_on:
    - db
    - redis
    redis:
    image: redis
    db:
    image: postgres

    # 默认情况下使用 docker-compose up web 这样的方式启动 web 服务时,也会启动 redis 和 db 两个服务,因为在配置文件中定义了依赖关系。
  6. dns
    配置 dns 服务器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    dns: 8.8.8.8
    #或者
    dns:
    - 8.8.8.8
    - 9.9.9.9
    #dns_search 的配置也类似 dns_search 配置 DNS 搜索域
    dns_search: example.com
    dns_search:
    - dc1.example.com
    - dc2.example.com
  7. tmpls
    挂载临时目录到容器内部,与 run 的参数一样效果:

    1
    2
    3
    4
    tmpfs: /run
    tmpfs:
    - /run
    - /tmp
  8. entrypoint
    用于指定接入点

    1
    2
    3
    4
    5
    6
    7
    8
    9
    entrypoint: /code/entrypoint.sh
    #或者
    entrypoint:
    - php
    - -d
    - zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so
    - -d
    - memory_limit=-1
    - vendor/bin/phpunit
  9. env_file
    专门存放变量的文件(这个环境变量是对宿主compose而言,如果在配置文件中有 build 操作,这些变量并不会进入构建过程中,如果要在构建中使用变量还是首选arg标签).

    1
    2
    3
    4
    5
    6
    env_file: .env
    # 或者
    env_file:
    - ./common.env
    - ./apps/web.env
    - /opt/secrets.env
  10. environment
    该标签的作用是设置镜像变量,它可以保存变量到镜像里面,也就是说启动的容器也会包含这些变量设置。
    一般 arg 标签的变量仅用在构建过程中。而 environment 和 Dockerfile 中的 ENV 指令一样会把变量一直保存在镜像、容器中,类似 docker run -e 的效果。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    environment:
    RACK_ENV: development
    SHOW: 'true'
    SESSION_SECRET:

    environment:
    - RACK_ENV=development
    - SHOW=true
    - SESSION_SECRET
  11. expose
    与Dockerfile中的EXPOSE指令一样,用于指定暴露的端口,但是只是作为一种参考,实际上docker-compose.yml的端口映射还得ports这样的标签。

    1
    2
    3
    expose:
    - "3000"
    - "8000"
  12. external_links
    在使用docker时,会有许多单独使用docker run启动的容器,为了使Compose能够连接这些不在docker-compose.yml中定义的容器,需要一个特殊的标签,就是external_links,它可以让Compose项目里面的容器连接到那些项目配置外部的容器(前提是外部容器中必须至少有一个容器是连接到与项目内的服务的同一个网络里面)。

    1
    2
    3
    4
    external_links:
    - redis_1
    - project_db_1:mysql
    - project_db_1:postgresql
  13. extra_hosts
    添加主机名的标签,就是往/etc/hosts文件中添加一些记录,与Docker client的–add-host类似:

    1
    2
    3
    4
    5
    6
    extra_hosts:
    - "somehost:162.242.195.82"
    - "otherhost:50.31.209.229"
    // 启动之后查看容器内部hosts:
    162.242.195.82 somehost
    50.31.209.229 otherhost
  14. labels
    向容器添加元数据,和Dockerfile的LABEL指令一个意思:

    1
    2
    3
    4
    5
    6
    7
    8
    labels:
    com.example.description: "Accounting webapp"
    com.example.department: "Finance"
    com.example.label-with-empty-value: ""
    labels:
    - "com.example.description=Accounting webapp"
    - "com.example.department=Finance"
    - "com.example.label-with-empty-value"
  15. links
    解决的是容器连接问题,与Docker client的–link一样效果,会连接到其它服务中的容器。

1
2
3
4
5
6
7
8
9
10
11
links:
- db
- db:database
- redis


//使用的别名将会自动在服务容器中的/etc/hosts里创建。例如:
172.12.2.186 db
172.12.2.186 database
172.12.2.187 redis
//相应的环境变量也将被创建。
  1. ports
    映射端口的标签。
    1
    2
    3
    4
    5
    ports:
    - "3000"
    - "8000:8000"
    - "49100:22"
    - "127.0.0.1:8001:8001"
  2. volumes
    挂载一个目录或者一个已存在的数据卷容器.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    volumes:
    // 只是指定一个路径,Docker 会自动在创建一个数据卷(这个路径是容器内部的)。
    - /var/lib/mysql

    // 使用绝对路径挂载数据卷
    - /opt/data:/var/lib/mysql

    // 以 Compose 配置文件为中心的相对路径作为数据卷挂载到容器。
    - ./cache:/tmp/cache

    // 使用用户的相对路径(~/ 表示的目录是 /home/<用户目录>/ 或者 /root/)。
    - ~/configs:/etc/configs/:ro

    // 已经存在的命名的数据卷。
    - datavolume:/var/lib/mysql

    //如果你不使用宿主机的路径,你可以指定一个volume_driver。
    volume_driver: mydriver
  3. restart
    默认值为 no ,即在任何情况下都不会重新启动容器;当值为 always 时,容器总是重新启动;当值为 on-failure 时,当出现 on-failure 报错容器退出时,容器重新启动。
    1
    2
    3
    4
    restart: "no" //默认 
    restart: always //总是重新启动
    restart: on-failure
    restart: unless-stopped

docker composer安装包

1
docker run -it --name composer -v /data/www/laravel:/app --privileged=true composer/composer  install

Docker初步了解
https://randzz.cn/2c3dfaa9d7cc/docker初步了解/
作者
Ezreal Rao
发布于
2019年5月29日
许可协议