Docker 大时代

常见操作命令集

目录

介绍

Docker 是怎么工作的

Docker是一个 Client-Server结构的系统,Docker的守护进程运行在主机上,
通过Socket从客户端访问,Server端接收到Client端的指令,就会执行这个命令。

Docker 镜像加载原理

UnionF5(联合文件系统)是一种 分层、轻量、高性能 的文件系统,
他支持对文件系统的修改作为一次提交来层层的叠加,同时可将不同目录挂在到同一个虚拟文件系统下。
Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统就是 UnionF5。

举例说明:
redis 的镜像 为什么比 redis的软件 大这么多?
因为 镜像 是一个分层组合的 软件包:大致说一下分层成分
底层是 操作系统基础依赖
中间层是 redis软件依赖库
上层是 redis软件
在形成容器的过程中,如果redis的任何一层 可以和 别的容器的 层 共用时,就会抛弃本身的,实际上 越是底层 越容易和 别的 共用。
注意,虽然容器之间有公用的层,但是容器之间是完全隔离的。

Docker vs 虚拟机

  • 虚拟机

硬件 /+/ 操作系统 /+/ 虚拟机软件 /+/ 虚拟机中的操作系统 /+/ 软件

  • Docker

硬件 /+/ 操作系统 /+/ Docker /+/ 软件

说明: 从左向右 依次为 计算机从底层到高层, 对比可见Docker优势。

docker的中央仓库

  • 官方的中央仓库: 镜像最全,但是下载速度较慢。
    官网
  • 国内的镜像网站: 推荐使用。
    网易蜂巢



安装

Dokcer 分为 EECE 两个版本;
CE 社区版【免费】,由社区维护提供技术支持,应用场景适合个人开发和小团队使用。
EE 企业版【收费】,有技术团队和售后团队提供技术支持,应用场景适合企业开发.
EE 版的功能要比 CE 多,且更加安全,这里只做 CE 版本的安装。

ubuntu

Docker CE 可以安装在 64 位的 x86 平台或 ARM 平台上。Ubuntu 发行版中,LTS(Long- Term-Support)长期支持版本,会获得 5 年的升级维护支持,这样的版本会更稳定,因此在 生产环境中推荐使用 LTS 版本,当前最新的 LTS 版本为 Ubuntu 20.04。

1. 准备工作
  • 卸载旧版本
sudo apt-get autoremove docker docker-ce docker-engine  docker.io  containerd runc
  • 删除 Docker 容器数据
$ sudo rm -rf /var/lib/docker
$ sudo rm -rf /var/lib/containerd
2. 更新 apt 软件包索引并安装软件包以允许 apt 通过HTTPS使用存储库:(执行以下命令)
sudo apt-get update 

sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
3. 添加Docker的官方GPG密钥,确认所下载软件包的合法性,(执行以下命令)
curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

// 系统提示 OK 表示设置完成
4. 添加 Docker 源,(执行以下命令)
echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
5. 开始安装 Dokcer CE
sudo apt-get update
sudo apt-get install docker-ce

说明:
这里可能会如果出现一个 WARNING 提示 无法验证的安装包(WARNING: The following packages cannot be authenticated!
containerd.io docker-ce-cli docker-ce docker-ce-rootless-extras docker-scan-plugin)
提示你 是否要执行安装(Install these packages without verification? [y/N])这里选择 y 进行安装

6. 执行命令检验是否安装成功
sudo docker run hello-world

出现 Hello from Docker 则 安装成功!

centos

  1. 下载Docker的依赖环境
yum -y install yum-utils device-mapper-persistent-data lvm2
  1. 设置一下下载Docker的镜像源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  1. 安装Docker
yum update
yum makacache fast
yum -y install docker-ce
  1. 启动、开机自启、测试
systemctl start docker
systemctl enable docker
docker run hello-world

免去sudo

sudo groupadd docker
# 将用户加入 docker 组
sudo usermod -aG docker $USER
# 刷新用户组权限
newgrp docker

镜像命令

  • 搜索镜像
docker search 镜像名称

# 设置筛选条件:搜索出来的镜像 stars 数大于3000
docker search 镜像名称 --filter=STARS=3000
  • 拉取镜像
docker pull 镜像名称[版本]

默认到Docker官方仓库拉取

  • 查看本地全部镜像
docker images
  • 删除本地镜像
docker rmi imageID

# 删除本地 所有镜像
docker rmi $(docker images -q)

正在运行的镜像是无法删除的,也就是说若想删除该镜像就 必须 先删除该镜像生成的容器。

  • 将 本地镜像 打包成 镜像文件 存储到指定路径
# 将指镜像 在当前目录 生成 wtt.tar文件
docker save -o wtt.tar imageID
  • 将 镜像文件 加载回 本地镜像
docker load -i 镜像文件
  • 为本地镜像设置 镜像名称 和 tag信息
docker tag imageID 镜像名称:tag信息
  • 查看镜像历史

一个镜像是由多个层(layer)组成的,通过 docker history 命令,可以列出各个层(layer)的创建信息

docker history kiratalent/mysql:5.7

# 想要看具体信息,可以通过添加 --no-trunc 选项
docker history --no-trunc kiratalent/mysql:5.7

容器命令

运行容器

docker run [可选参数] imageID
1. 常用命令参数介绍
################## 类型一:前台启动
# -it:进行交互操作;分配tty设备
# imageID 之后的位置 :启动容器后 执行的一条指令
# -e:配置环境变量
# --name:指定容器的名字
docker run -it -e AAA=aaa -e BBB=ccc --name aaaaaa imageID /bin/bash

# 此时 容器运行且打开一个终端,输入以下命令 退出容器:
exit


################### 类型二:后台启动
# -d: 后台运行
# -p: 端口映射
docker run -d -p 真机端口:docker端口 --name 容器名称 imageID

imageID 之后的位置说明:

我们可以 放置一条指令,例如可以是ls 或者pwd,
当容器开启后 就会执行 该指令,所以可以用于 服务启动
但是注意 不要妄想 用 命令连接符 连接多条指令,
如果那样的话也是仅仅执行第一条指令

docker exec 进入容器 指定 的 containerID之后的位置 和 imageID 之后的位置 作用类似,
但是 docker exec 往往都是放 /bin/bash 指令 也可以简化为 bash
/bin/bash 的意思是 打开一个终端,相当于Ubuntu下的 Ctrl + Alt + T 的快捷键。
打开终端是为了 后面更多命令的交互,所以 /bin/bash 是输入更多指令的 前提指令。

2. 给容器分配 内存 和 cpu

默认情况下,容器使用的资源是 不受限制 的,也就是说可以使用主机内核调度器所允许的最大资源。
这里简单一提,在物力资源限制 章节 有具体说明

# CPU

docker run --name "container_A" -c 1024 ubuntu
docker run --name "container_B" -c 512 ubuntu

可以通过-c或–cpu-shares设置容器使用 CPU 的权重, 如果不指定,默认值为 1024
与内存限额不同,通过-c设置的 cpu share 并不是 CPU 资源的绝对数量,而是一个相对的权重值

某个容器最终能分配到的 CPU 资源取决于它的 cpu share 占所有容器 cpu share 总和的比例。
container_A 的 cpu share 1024,是 container_B 的两倍。当两个容器都需要 CPU 资源时,
container_A 可以得到的 CPU 是 container_B 的两倍。
需要注意的是,这种按权重分配 CPU只会发生在 CPU资源紧张的情况下。
如果内存够用 或者 container_A 处于空闲状态,为了充分利用 CPU资源,container_B 也可以分配到全部空闲可用的 CPU。

# 内存

docker run -m 200M --memory-swap=300M ubuntu

与操作系统类似,容器可使用的内存包括两部分:物理内存和 swap(虚拟内存,交换区)。
-m 或 –memory:设置内存的使用限额,例如 100M, 2G。
–memory-swap:设置 内存+swap 的使用限额。
默认情况下,上面两组参数为 -1,即对容器内存和 swap 的使用没有限制。

3. 校准容器时间
docker run -v /etc/localtime:/etc/localtime ubuntu
4. 容器 自动删除

有时候,我们仅仅是想 测试某些东西 而 启动 进入容器,
等 退出容器 后,容器也就没有用了, 我们希望 容器可以自动删除,
此种情形下 就可以用 --rm 参数

docker run -it --rm --name tantan_test 5d0da3d /bin/bash
5. 容器获取 除宿主机root权限

大约在0.6版,privileged被引入docker。使用该参数,container内的root拥有真正的root权限
否则,container内的root只是外部的一个普通用户权限。

privileged启动的容器,可以看到很多host上的设备,并且可以执行mount
甚至允许你在docker容器中启动docker容器。

docker run -it --name tantan_test --privileged=true 5d0da3d /bin/bash
6. 容器 自动重启

Docker 提供了 容器 自动重启机制,
可以在 容器意外退出 或者 Docker软件服务重启 时 使得 容器能够自启动。

命令参数: --restart
参数值:

  • no ==》容器退出时,不重启容器, 默认值
  • on-failure ==》只有在 非0状态(非正常) 退出时才从新启动容器;
    on-failure:10 可以自定 自动重启的 具体次数,此处为10次
  • always ==》无论退出状态是如何,都重启容器,该值赋予了 容器 开启自起 的能力

.
需要注意的是:无论那种 自启方式,通过 docker stop 的方式 停止容器之后 都是不会重启的。

例子:

docker run --restart=on-failure:10 redis 

注意,如果容器是使用== “-rm” 标志== 启动的,则不能 使用 重新启动策略。
对于容器来说是相互排斥的。

查看容器

docker ps

docker ps -a

docker ps -q  # 常用于停止所有运行容器  docker stop $(docker ps -q)

docker ps -n 3

参数说明:
-a: 查看全部的容器,包括 没有运行的
-q : 只查看正在运行容器的 标识信息
-n=3 : 查看最近3个创建的容器

  • 查看容器的端口使用情况
docker port containerID
  • 查看容器日志
docker logs -tf --tail 10 containerID

参数说明:
-tf: 修饰日志的显示内容;
–tail 10: 指定日志的显示 条数。

  • 查看容器详情
docker inspect  containerID

进入容器(运行的)

容器通常是后台方式运行的,可以进入容器内进行一个配置的修改,
每个容器内部 本身也是一个阉割版的Linux系统。

# 方式一(核心)
docker exec -it containerID /bin/bash  # /bin/bash ===可简写为===> bash

# 方式二
docker attach containerID

区别:
exec: 进入容器后开启一个新的终端(常用方式);
attach:进入容器正在执行的终端,不会启动新的终端进程。

  • 以 容器里面root 用户的身份 进入 容器

有些容器默认进入的身份不是 root用户,则可以使用如下命令

docker exec -it -u root ContainerID bash
  • 退出容器
exit          # 退出容器,容器停止
Ctrl + P + Q  # 退出容器,容器不停止

启、停、删 杀 容器

docker start containerID/容器名称

# 停止容器后,容器相关的数据不会丢失
docker stop containerID/容器名称 

docker rm containerID/容器名称
docker rm $(docker ps -qa)      # 删除所有的容器

# 强制停止当前容器
# 使用kill是直接关闭这个容器的进程,如果有正在运行的文件,那么可能造成数据丢失。
# 使用stop是给容器发出一个退出的信号,相当于给容器一些缓冲的余地。保存好自己的文件,然后再关闭。
docker kill containerID         

注意:
有这么一个现象,docekr run -d centos 后 docker ps 发现 容器停止了,
这是因为 容器使用后台运行 必须要有一个前台进程,就是说要有一个对外提供服务的进程,
如果docker 服务端 发现 容器没有 则会自动停掉 该容器,
换句话说 docker 的这种机制 就是 不养闲人

更新 容器 的启动参数

docker update命令 动态更新 容器(不论是否正在运行) 的 参数配置。

例子:
docker run -it --name reids111 -d s4df64s redis-server /etc/redis/redis.conf

# 更新 容器重启的配置
docker update --restart=on-failure:3 redis111
可以更新的 命令参数如下:

名字,简写

默认

描述

-blkio-weight

0

阻止IO(相对权重),介于10和1000之间,或0禁用(默认值为0)

--cpu-period

0

限制CPU CFS(完全公平调度程序)期间

--cpu-quota

0

限制CPU CFS(完全公平调度程序)配额

--cpu-rt-period

0

以微秒为单位限制CPU实时周期

--cpu-rt-runtime

0

以微秒为单位限制CPU实时运行时间

--cpu-shares, -c

0

CPU份额(相对重量)

--cpus


CPU数量

--cpuset-cpus


允许执行的CPU(0-3,0,1)

--cpuset-mems


允许执行的MEM(0-3,0,1)

--kernel-memory

0

内核内存限制

--memory, -m

0

内存限制

--memory-reservation

0

内存软限制

--memory-swap

0

交换限制等于内存加交换:'-1'以启用无限交换

--restart


重新启动策略以在容器退出时应用

栗子:

  • Mysql
    启动容器的常规4步骤

step1: 确定 服务 的端口 3306
step2: 确定 服务 的 配置文件 的存储位置

每个人部署的 MySQL 内,可能文件路径不一致。我们可以先创建个测试的 MySQL 容器,然后再根据查找出的文件具体路径位置。

# 进入容器后
mysql --help | grep my.cnf
# 输出以下内容:
         order of preference, my.cnf, $MYSQL_TCP_PORT,
         /etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf
# 意思是路径按优先排序,会是在以下路径里:/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf

step3: 确定 服务 的 应用数据 的存储位置

运行 查看容器详情 de 命令,查看 “Mounts” 内的相关信息

docker inspect mysqltest
# 主要查看以下的输出信息
  "Mounts": [
           {
             "Type": "volume",
               "Name": "4f2d463cfc4bdd4baebcb098c97d7da3337195ed2c6572bc0b89f7e845d27652",
               "Source": "/var/lib/docker/volumes/4f2d463cfc4bdd4baebcb098c97d7da3337195ed2c6572bc0b89f7e845d27652/_data",
               "Destination": "/var/lib/mysql", # 这里就是 软件所在目录,其中包括 软件数据的存储位置
               "Driver": "local",
               "Mode": "",
               "RW": true,
               "Propagation": ""
           }
       ],

step4: 确定 相关的 环境变量

mysql 的相关变量都配置差不多,这里只需要配置一个 数据库密码的环境变量: MYSQL_ROOT_PASSWORD

# 启动Mysql容器
docker run --name mysql-wtt \
-p 3333:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
--mount type=bind,src=/home/docker/mysql/conf/my.cnf,dst=/etc/mysql/my.cnf \ # 配置文件
--mount type=bind,src=/home/docker/mysql/datadir,dst=/var/lib/mysql \ # 服务数据
--restart=on-failure:3 \ # 是指容器在未来出现异常退出(退出码非0)的情况下循环重启3次; 
-d imageID



# 连接测试:
# 进入容器后输入: mysql -uroot -p123456 --default-character-set=utf6
# 在真机中登录:  mysql -uroot -p123456  --P 3333  --default-character-set=utf6
  • Redis容器
# Redis
docker pull redis
docker run -d -p 6666:6379 --name wtt-redis --restart=always redis
docker exec -it test-redis bash
进入容器系统中登录: redis-cli
在真机中登录: redis-cli -p 6666
  • docker使用python3.7运行python脚本
docker pull rackspacedot/python37

# 在本地创建一个 待执行的 py文件
mkdir -p /home/hero/docker-worker-space
touch /home/hero/docker-worker-space/wtt.py #写入一些代码

# 运行镜像,让生成容器 内 的 python 环境执行一遍 wtt.py中的代码,然后停止容器
docker run  -v /home/hero/docker-worker-space:/usr/src/file  -w /usr/src/file rackspacedot/python37 python wtt.py

Docker日志

Docker 日志分为两类:

  1. Docker 引擎日志(也就是 dockerd 运行时的日志)
  2. 容器的日志,容器内的服务产生的日志

.
这里重点说 容器日志。

前序栗子

# step1:启动一个容器,redis-server /etc/redis/redis.conf 是redis服务 启动命令
docker run -it --name reids111 -d s4df64s redis-server /etc/redis/redis.conf

# step2:查看 运行容器list,发现 redis111 容器没有 正常运行
ps

# step3:查看容器日志,获取 没有正常运行的 原因
docker logs -tf --tail 10 redis111

Docker 日志驱动

Docker的默认 logging driver是 json-file,json-file 会将容器的日志保存在 .json文件 中,日志路径为 /var/lib/dockercontainer/ID/ID.json.log
除了 json-file 还支持很多 logging driver:

  • none 是 disable 容器日志功能
  • syslog 和 journald 是linux上的两种日志管理服务
  • awslogs,splunk 和 gcplogs 是第三方日志托管服务
  • gelf 和 fluentd 是两种开源的日志管理方案

.

驱动程序描述
none容器没有日志可用,docker logs 什么都不返回
local日志以自定义格式存储,设计这种格式的目的是将开销降到最低。
syslog将日志消息写入 syslog 工具,syslog 守护程序必须在主机上运行。
journald将日志消息写入 journald,journald 守护程序必须在主机上运行。
gelf将日志消息写入 Graylog Extended Log Format (GELF) 终端,例如 Graylog 或 Logstash。
fluentd将日志消息写入 fluentd(forward input),fluentd 守护程序必须在主机上运行。
awslogs将日志消息写入 Amazon CloudWatch Logs。
splunk使用HTTP事件收集器将日志消息写入splunk。
etwlogs将日志消息写为 Windows 的 Event Tracing 事件,仅在Windows平台上可用。
gcplogs将日志消息写入 Google Cloud Platform (GCP) Logging。
logentries将日志消息写入 Rapid7 Logentries。
json-file日志格式化为 JSON,这是 Docker 默认的日志驱动程序。
.

容器启动时可以通过 - -log-driver 指定使用的 logging friver

docker run  --log-driver none  -dit  --name redis123 df4s5d644f

物理资源限制

对于 容器 而言 默认是可以 使用 系统的 全部资源的,
在某些情况下 我们可能 希望 它只使用 一部分,此时 就要 对 容器的 资源 进行限制。

内存容量

  • -m
    对容器 物理内存 的 使用限制。
  • –memory-swap
    对 内存 和 交换区 总和 的限制。
# 举例:限制 物理内存为 100M, 交换内存为 66M
docker run -m 100M --memory-swap=166M 容器ID

CPU使用权重

默认情况下,所有的 容器 都是可以 平等的 使用 CPU资源的,
我们可以通过调整 不同 容器的CPU使用权重(默认是1024) 来 按需分类CPU资源。

docker run -c 1024 --name mysql1 容器ID
docker run -c 512 --name mysql2 容器ID

# 说明:
这里将 两个 容器的 CPU权重 设置为 2:1,假设 cpu的总的资源 为 9,  
两个容器 同时工作(假设没有其他进程需要消耗CPU资源),mysql1 能用到 6, mysql2 能用到3。  
但是注意,假设 只有 mysql2 工作, 那么 mysql2 也是可以 用到 9 的, 别的cpu资源闲着也是闲着,拿来用得。
cpu压力测试工具
# 先安装
apt install stress

# 产生4个进程,不断计算 随机数的 平方根,将CPU资源 直接拉满。
stress -c 4

具体使用的CPU

–cpuset-cpus 参数 可以 直接限制 在哪个 CPU上运行,
比如 宿主机是4核的, 那么就有: 0、1、2、3 这几个参数值可以选择:

# 让该容器 只使用 第一个和最后一个 CPU
docker run --cpuset-cpus=0,3 容器ID

使用CPU的个数

–cpus 可以指定使用 几个CPU,
比如 宿主机是4核的, 那么就有: 0、1、2、3 这几个参数值可以选择:

# 让该容器 随机使用 任意组合的 2个
docker run --cpus=2 容器ID

磁盘IO读写性能 的 限制

可以通过 –device-read/write-bps–device-read/write-iops 对容器进行 IO限制。

  • bps
    每秒读写的数据量。
  • iops
    每秒的IO次数。
# 因为 容器文件系统实在 、dev/sda上的,所以这里我们就 /dev/sda:10MB   
# 来限制 对 /dev/sda 的 写入 速度 只有 10MB/s
docker run --device-wirite-bps=/dev/sda:10MB 容器ID

进阶命令

查看元数据

  • 查看Docker软件信息
docker info 
  • 查看镜像元数据
docker inspect imageID
  • 查看容器元数据
docker inspect containerID

在宿主机 直接使用 容器内部命令

docker exec mysql-wtt mysqldump --flush-logs --lock-all-tables -uroot -p123456 wtt > ./${cur_date}_wtt.sql

从容器内 拷贝文件 到宿主机

docker cp containerID:容器内文件路径 宿主机路径

docker cp asdg45df:/home/tom/aaa.txt /home/hero

容器监控

对容器的 运行状态、资源占用 进行实时监控,类似于top命令

docker stats

# 输出:
CONTAINER ID        NAME                CPU %               MEM USAGE / LIMIT     MEM %               NET I/O             BLOCK I/O           PIDS
f61ee0e4947c        test_cp_my          0.04%               187.5MiB / 11.59GiB   1.58%               1.12MB / 40.9kB     18.3MB / 12.7MB     29
  • 容器内部的进程监控
docker top 容器ID/名称

DockerFile

重要性:
DockerFile 是面向开发的,我们以后发布项目要做成镜像,就需要编写 DockerFile文件,
Docker镜像逐渐成为企业交付的标准,是必须熟练掌握的。


自定义镜像:把原有镜像的配置,通过自己的需要进行修改,使镜像个人化更强。
这部分主要围绕4个步骤:

  1. 编写一个Dockerfile文件
  2. docker build 构建出一个镜像
  3. docker run 运行镜像
  4. docker push 发布镜像

1.编写一个Dockerfile文件

前序

  • 每个保留关键字(指令)都必须是大写字母
  • 执行顺序从上到下
  • # 表示注释
  • 每一个指令都会创建提交一个新的镜像层,并提交

常用基本指令

1. FROM

所有Dockerfile的第一个指令都必须是 FROM,用于指定一个构建镜像的基础源镜像
如果本地没有就会从公共库中拉取,没有指定镜像的标签会使用默认的latest标签
如果需要在一个Dockerfile中构建多个镜像,可以使用多次
scratch 是百分之九十九镜像的始祖镜像。

2. MAINTAINER

描述镜像的创建者,通常是 名称+邮箱,例如:MAINTAINER tom123456@qq.com
现在更推荐的写法是: LABEL maintainer="tom<123123@qq.com>"

3. WORKDIR

镜像的工作目录,
为RUN、CMD、ENTRYPOINT指令配置 工作目录,
也是进入容器的默认的shell目录,
可以使用多个WORKDIR指令,后续参数如果是相对路径,则会基于之前WORKDIR命令指定的路径。

4. RUN

镜像 构建过程 中需要运行的命令
RUN 每条指令将在当前镜像基础上执行,并提交为新的镜像。
在docker build时运行。

 RUN /bin/bash -c 'source $HOME/.bashrc; \
     echo $HOME'

说明:

  • RUN有两种形式:

1.RUN <命令>:shell形式

类似于直接在终端输入命令
命令在shell中运行,
默认在Linux上使用/bin/sh -c
在Windows上使用cmd /S /C

2.RUN [“可执行文件”,“参数1”,“参数2”]:exec形式

不会触发shell,所以$HOME这样的环境变量无法使用,
但它可以在没有bash的镜像中执行。

  • RUN在下一次构建期间,指令缓存不会自动失效。可以使用–no-cache选项使指令缓存失效。如RUN apt-get update之类的构建缓存将在下一次构建期间被重用,此时构建中可能安装过时版本的工具,但我们可以使用–no-cache标志来使RUN命令的缓存失效,如docker build --no-cache
  • RUN一次就代表Dockerfile中的一层。而docker镜像的构建就是不断去完善每一层需要做的事情。而dockerfi对一个file中层数是有限制的,最大不超过127层。因此,RUN提供命令的串联功能,也就是允许每一层可包含多种操作,他们会按照书写顺序来依次执行。
FROM ubuntu:14.04

RUN apt-get update
RUN apt-get install -y gcc libc6-dev make
RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install


# 可以优化为:
FROM ubuntu:14.04
RUN buildDeps='gcc libc6-dev make' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-3.2.5.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

说明:

换行用 \ ,注释用 # ,
平时书写注意 缩进 来保证文件的可读性。
上述例子中的最后一句还进行了无关文件的清理,进一步保证每一层的最优和最小。

5. CMD、ENTRYPOINY

指定这个容器启动的时候要运行的命令。
.
Dockerfile使用RUN指令完成 **docker build**所有的环境安装与配置,
通过CMD、ENTRYPOINY 指令来指示 docker run 命令运行镜像时要执行的命令。

区别:

Dockerfile只允许使用一次CMD命令。
使用多个CMD会抵消之前所有的命令,只有最后一个命令生效。
一般用于执行容器时提供默认值。

ENTRYPOINY 可以有多个,依次执行

例子:

CMD 指定了 ls -a 的命令,在运行容器时 docker run -h, 那么-h 会替换掉 ls -a, -h 无法运行

ENTRYPOINY 指定了 ls -a 的命令,在运行容器时 docker run -h, 那么-h 会追加到 ls -a,最终执行 ls -a -h 命令

 FROM ubuntu
 CMD ["/usr/bin/ls","--help"] # 查看 ls 命令的 帮助信息

CMD有三种形式:

  • CMD [“exec”,“param1”,“param2”]:使用exec执行,推荐方式。
  • CMD command param1 param2:在/bin/sh中执行,可以提供交互操作。
  • CMD [“param1”,“param2”]:提供给ENTRYPOINT的默认参数(极少这样使用)。
6. COPY

复制本机文件或目录,添加到指定的容器目录
COPY 指令和 ADD 指令的唯一区别在于:是否支持从远程URL获取资源。
COPY 指令只能从本地读取资源并复制到镜像中。
ADD 指令支持通过 URL 从远程服务器读取资源并复制到镜像中。

说明:

相同复制命令下,使用ADD构建的镜像比COPY命令构建的体积大,
所以如果只是使用本地文件建议使用COPY命令。
ADD 指令更擅长读取本地tar文件并解压缩。

7. ADD

ADD 命令的格式和 COPY 命令相同,也是:
ADD
每个都 可能包含通配符
是一个绝对路径,或相对 WORKDIR 的相对路径。
ADD 命令可以完成 COPY 命令的所有功能,并且还可以完成两类超酷的功能:

  1. 解压压缩文件并把它们添加到镜像中,
  2. 从 url 拷贝远程文件到镜像中
ADD http://example.com/big.tar.xz /usr/src/things/


WORKDIR /aaa
ADD ./wtt* ./bbb/ # 将本地wtt开头的文件, 复制到容器的 /aaa/bbb目录下

WORKDIR /app
ADD nickdir.tar.gz ./ # 复制压缩包时,它的行为中包含tar -x 行为
8. VOLUME

VOLUME 在容器中创建 通向宿主机 的目录 挂载点
就相当与 把宿主机的一个目录 以U盘的方式 插入到 容器的指定目录下。
插入的目录就是挂载点。

主要作用:

  • 避免重要的数据因容器重启而丢失。
  • 避免容器不断变大。
  • 保留配置文件。
VOLUME ["/root/aaa", "/root/bbb", "/root/ccc"]

说明:

通过 VOLUME 在镜像中预置了 挂载点后,在docker run 启动容器的时候,
需要用 -v 指定 宿主机目录 : 容器目录 的对应关系。
而这个 -v 使用时主要做 三件事:

一:创建目录
如果 -v 指定 宿主机目录 中,部分目录不存在,则会在 宿主机中 创建目录;
同理 容器目录 也是如此,不存在 就 创建。

二:创建挂载点
根据 容器目录 在容器中 自动创建 挂载点,

三:创建卷
根据 宿主机目录 在宿主机中 自动创建 卷,

通过 -v 指定,无需提前创建好 数据卷 和 挂载点

从这里可以看出, VOLUME 命令很鸡肋, 因为使用了VOLUME 命令后还是需要使用 -v参数指定,
而 VOLUME 命令 做的工作 -v参数 还会再做一遍,而且 -v参数 强大,
所以 VOLUME 命令 更多的时候是 起到标识的作用。

9. EXPOSE

指定 容器 在运行时监听某个 容器端口
也就是 暴露出容器将要提供服务所开放的端口
可以指定TCP或UDP,如果不指定协议,默认为TCP端口。

EXPOSE 80
EXPOSE 80/tcp
EXPOSE 80/udp

docker run 的时候通过参数 -P(大写) 或者 -p(小写) <宿主端口>:<容器端口> 将容器的端口映射到宿主机上。
这样外界访问宿主机就可以获取到容器提供的服务了。
-P(大写) 是随机宿主机的端口
-p(小写) 映射到宿主机的指定端口

10. LABEL

为镜像添加标签

11. ENV

设置持久性 环境变量
说是环境变量还不如说是一个全局变量
前面ENV定义,后面可以通过 $ +变量名 取值进行使用。

12. ARG

构建参数
ARG和ENV效果类似,都是用来设置全局变量
唯一 不同的是dockerfile中的ARG 编译好后 是不会出现在打开的 容器内 的。
也就是说 ARG指令的有效范围在其定义的构建阶段内
ENV设置参数的有效期为整个构建期内。

说明:

可以使用ARG或ENV指令来指定RUN指令可用的变量,
如果ARG和ENV同时指定了一个相同名称的变量、
则ENV设置的变量会覆盖ARG设置的变量。如下:

FROM ubuntu
ARG CONT_IMG_VER
ENV CONT_IMG_VER v1.0.0
RUN echo $CONT_IMG_VER

使用 docker build --build-arg CONT_IMG_VER=v2.0.1 . 最终输出v1.0.0

13. USER

在书写dockerfile时,某些层的操作若想切换用户名
可以使用该参数指定某些层的用户,并且是存在的用户名。

2. docker build 构建出一个镜像

docker build . -f DockerFile文件路径 -t 生成镜像名称[:tag]

-f: 指定build时使用的 DockerFile文件, 如果 DockerFile文件 是官方默认的 Dockerfile
那么build时就会自动寻找这个文件,就不需要 -f 了。
-t: target, 指定目标镜像的信息.

  • 查看镜像构建的大概步骤
docker history 镜像ID

3.docker push 发布镜像

  • step1: 注册账号

https://hub.docker.com注册自己的账号

  • step2: 登录账号
docker login -u tom
# 回车 输入密码
  • step3: 发布镜像
docker push tom/镜像名:1.0
# 回车 输入密码

栗子

1、部署前端项目 到 docker中的nginx中

自定义一个文件夹放入三部分内容:

  1. dist文件夹
  2. Dockerfile 文件
  3. nginx.conf 配置文件

Dockerfile 文件的内容:

FROM nginx
COPY dist/ /usr/share/nginx/html/
COPY nginx.conf /etc/nginx/nginx.conf

nginx.conf 文件的内容:

user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
  worker_connections  1024;
}
http {
  include       /etc/nginx/mime.types;
  default_type  application/octet-stream;
  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
  access_log  /var/log/nginx/access.log  main;
  sendfile        on;
  keepalive_timeout  65;
  # #########################################################
  server {
    listen       80;
    server_name  localhost;
    location / {
      root   /root/wtt/music_level/music_webpage_admin;
      index  index.html index.htm;
      try_files $uri $uri/ /index.html;
    }
    # #########################################################
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
      root   /usr/share/nginx/html;
    }
  }
}

在自定义文件下执行命令:

docker build . -t vueImage:v1.0		# 生成 自定义 镜像 到本地
docker run -d -p 8080:80 --name vue imageID   # 将自定义镜像 运行成容器
2、创建自己的centos

Dockerfile 文件的内容:

FROM centos
MAINTAINER tom<123123@qq.com>

WORKDIR /root
COPY ./testmusiclevel .

RUN chmod 777 testmusiclevel

VOLUME ["/home/aaa/, "/root"]

EXPOSE 8080

CMD ./testmusiclevel
docker build . -t wtt:1.0		# 生成 自定义 镜像 到本地  

# 通过 -v 指定,无需提前创建号 数据卷 和 挂载点
docker run -v /home/hero/Desktop/test/zqq/:/root/ -v /home/hero/Desktop/test/wang/:/home/tom/ -d -p 8080:8080 --name ddd wtt:1.0    # 将自定义镜像 运行成容器

Docker数据卷

简介

想要了解Docker Volume,首先我们需要知道Docker的 文件系统 是如何工作的。
Docker镜像 是由 多个文件系统(只读层)叠加而成
当我们启动一个 容器 的时候,Docker会 加载镜像层 并在其上 添加一个读写层
如果运行中的容器 修改了 现有的一个已存在的文件,那该文件将会从 读写层 下的 只读层 复制读写层
该文件的 只读版本 仍然存在,只是已经被 读写层 中该文件的 副本所隐藏
当删除Docker容器,并通过该镜像重新启动时,之前的更改将会丢失,即 读写层 的内容 包括读写层 一起丢失。
在Docker中,只读层 以及在 顶部的 读写层 的组合被称为 Union FIle System(联合文件系统) 即:

联合文件系统 = 只读层 + 读写层

为了能够 数据持久化 以及 共享容器间 的 数据,Docker提出了Volume的概念。
简单来说,Volume就是 目录 或 文件,它可以绕过默认的 联合文件系统
而以正常的文件或者目录的形式存在于 宿主机 上。

必要性

容器中 应用产生的数据 是可以存储在 容器中的,但是有以下缺点

  1. 当容器销毁时,或者出现意外无法再次启动时,会造成数据丢失
  2. 容器内数据 需Storage driver来管理filesystem,数据量过大的话,容器会大大降低性能
数据卷的特性

• 数据卷 可以在容器之间共享和重用
• 对数据卷的修改会立马生效
• 对数据卷的更新,不会影响镜像
• 数据卷 默认会一直存在,即使容器被删除

相关操作

docker volume create 数据卷名称		# 创建的数据卷默认路径: /var/lib/docker/volume/数据卷名称/_data
docker volume ls			# 查看全部数据卷
docker volume inspect 数据卷名称			# 查看指定数据卷的详细信息
docker volume rm 数据卷名称				# 删除

用法 ===> (-v --mount)

数据卷的使用,可类似于 Linux 下对目录或文件进行 mount。
用户可以通过docker run的 -v 或 --mount 选项来创建带有数据卷的容器,但两个参数不能同时使用
总的来说,–mount更加明确和冗长。
最大的区别是-v语法将所有选项组合在一个字段中,而–mount语法将它们分离。

-v 这种方式是最早提供的,其使用特性在 Dockerfile中 volume命令 和 数据卷中有详细解说,这里重点说Bind mounts,
当 Swarm 出来后就提倡使用 mount,k8s 也是使用的这种方式。研究表明它更易于使用。
这里主要说一下使用上 两者的差别:

  • 使用-v 时,如果 宿主机 上没有这个文件,也会自动创建,
  • 但是如果使用–mount时,宿主机 中没有这个文件会报错找不到这个文件,并创建失败
  • –mount可以支持创建集群服务(services)的数据卷,而-v不行。

Swarm 是Docker官方提供的一款集群管理工具,其主要作用是把若干台Docker主机抽象为一个整体,
并且通过一个入口统一管理这些Docker主机上的各种Docker资源。
Swarm和k8s比较类似,但是更加轻,具有的功能也较kubernetes更少一些。

–mount

可以存储在宿主机系统的任意位置;(比较常用的方式)
–mount: 由多个用逗号分隔的=键值对组成,键的顺序随意。
对于–moun选项,目前Docker提供了三种不同类型的 数据卷 从 宿主机 挂载到 容器中:

  • volume
  • bind
  • tmpfs
  • volume

普通数据卷(默认即这种类型),Docker管理宿主机文件系统的一部分,默认位于 /var/lib/docker/volumes 目录中;

docker service create --mount 'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>' --name myservice <IMAGE> 
  • bind

绑定 数据卷,文件或目录挂载,意为着可以存储在 宿主机 系统的 任意位置

# mount的使用方法: --mount
#   type 指定mount的类型
#   src 指定宿主机的目录
#   dst 指定容器目录

# 在宿主机 创建一个待挂载的目录:
mkdir /home/tom/file

# 启动容器
dk run -d -p 8080:8080 --mount type=bind,src=/home/tom/file,dst=/home/tom --name test 4ed69dc

说明:

  1. dst 指定容器目录 如果在容器中不存在 自会自动创建
  2. 宿主机/home/tom/file目录下如果有文件,在初始化时 容器目录/home/tom下会复制过来, 反之亦然。
  3. 如果 宿主机 和 容器 在内容互通复制的过程中,有同名文件,则以 宿主机的文件 为主。
  4. 宿主机 和 容器目录 产生挂载关系后 数据都是 双向实时同步的

注意:
bind mount在不同的宿主机系统时不可移植的,比如Windows和Linux的目录结构是不一样的,
bind mount所指向的host目录也不能一样。
这也是为什么bind mount不能出现在Dockerfile中的原因,因为这样Dockerfile就不可移植了。

  • tmpfs

临时数据卷,挂载存储在宿主机系统的 内存中,而不会写入宿主机的 文件系统;

docker run -d -it --name tmptest --mount type=tmpfs,destination=/app,tmpfs-size=1024,tmpfs-mode=1770 nginx:latest

注:这个功能只有在Linux上运行Docker时才可用

-v

  • 根据 数据卷名称 进行挂载
# 将数据卷 my_vol 挂载到容器的 /data/docker/volume/ 目录
docker run -itd --name=vol1_ubuntu -v my_vol:/data/docker/volume/ ubuntu /bin/bash

:如果数据卷 my_vol 存在将直接进行挂载,如果不存在 docker 将先 自动创建 数据卷再进行挂载。

  • 根据 宿主机 目录 的 绝对路径 进行挂载
docker run -d -p 8080:80 --name vue -v /home/hero/aaa/:/usr/share/nginx/html/ vueImage:v1.0

说明

  • 绝对路径

目录映射时 使用的是 绝对路径 而不是 相对路径,可以使用shell变量省略对 绝对路径的写法,例如:$(pwd) /a.txt

  • 目录 映射

一定不要 省略 目录后的 /, 否则 目录视为 文件。
例如:宿主机aaa目录 和 容器 abc目录 映射 应该写为 -v aaa/:abc/

  • 根据宿主机 文件 的 绝对路径进行挂载
# 校准 容器时间
-v /etc/localtime:/etc/localtime 
  • 文件 映射

无论是 --mount 还是 -v,对于文件映射:
例如: 宿主机 $(pwd)/aaa.txt <===> 容器:/root/aaa123.txt
两边的 文件名 可以不一致,但是规范上 建议 最好是保持一致。

文件 映射 有时候 会出现这么个情况:
在宿主机修改文件之后,容器内部 没有发生对应的修改
需要 重启容器 才可以 正常同步 。
目录映射 就不会出现这个 情况。

  • 挂载匿名卷

-v 参数如果不加任何宿主机相关卷信息docker将创建一个匿名卷进行挂载

# 挂载匿名卷到容器的 /data/docker/volume/my_vol 目录
docker run -itd --name=vol4_ubuntu -v /data/docker/volume/my_vol ubuntu /bin/bash

Docker 网络

在开启Docker服务时,通过 ifconfig 我们可以看到多了一个 docker0 的ip地址。
这是以为在安装Docker时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 、host
可以使用以下docker network ls命令列出这些网络:

sudo docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
3235d7f3a836        bridge              bridge              local
46b4b90aa98a        host                host                local
46b6d12108f9        none                null                local

该bridge网络代表docker0, 是所有Docker安装中默认使用的网络。
除非你使用该docker run --network=选项指定,否则Docker守护程序默认将容器连接到此网络。

网络模式

网络模式简介
Host容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
Bridge此模式会为每一个容器分配、设置IP等,并将容器连接到一个docker0虚拟网桥,通过docker0网桥以及Iptables nat表配置与宿主机通信。
None该模式关闭了容器的网络功能。
Container创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围。
自定义网络

网络模式—Bridge (默认模式)

evth-pair技术

每次启动一个容器,docker就会给 容器分配一对ip,当删除一个容器,就会减少一对ip,
evth-pair 就是一对的虚拟设备接口,他们都是成对出现的,一端连着协议,一端连着彼此,
正是因为这个特性,evth-pair充当一个桥梁,连接着各种虚拟网络设备。
Docker 容器之间的连接 使用的就是 evth-pair技术。
也就是说 docker0 桥接模式 使用的技术就是 evth-pair技术,
所以 容器之间、容器和宿主机之间 的 ip 都是可以 ping 通的。

容器间通信de过程

Docker服务端的 docker0 在容器通信中扮演的角色就是 交换机
同一网络下有 容器A和容器B,现在容器A要和容器B进行通信,
容器A 先将 数据包 发送到 docker0, 然后 docker0 通过查表的,
把容器A的 数据包 要么通过广播的方式, 要么通过 ip直达的方式 送给 容器B.

容器访问宿主机的ip地址

容器默认的使用的网络是 docker0局域网,宿主机 和 容器 不在同一个网段,
所以 容器 想访问 局域网 之外的网络地址,要通过 网关 进行访问,
局域网的 网关地址 即 路由地址 就是 局域网内的 头号地址(\*.\*.\*.1),
假设 宿主机在8080端口开发一个服务, 容器A想访问:

# step1: 在宿主机中 获取网关地址
ip add # 找到 docker0 的地址

# step2: 在容器中 进行访问
curl *.*.*.1:8080

说明: 把 宿主机 防火墙 对 8080端口的 拦截 打开,否则 容器访问不到

网络模式—host (原生模式)

容器 默认 使用了 bridge 网络模式, 提供服务的端口 要提前 就都设定好,因此端口的使用不够灵活。
例如 nginx容器,一开始启动容器时 我们只给 目前的 服务需要的端口 就行 映射开启,
如果后期 增加了 别的服务, 需要用到 更多 端口,显然 bridge 网络模式 不再适用。
所以 我们 希望 容器 以一种 原生软件 的方式 运行,就是说 容器 直接使用 宿主机的 网络资源(ip、port等),
即 nginx容器 就像直接在宿主机上 安装的 nginx软件 一样。
这就是 host网络模式 诞生的 背景。

docker run -itd --net=host --name nginx-test nginx:latest

说明:
容器使用’host’网络驱动,意味着容器共享宿主机网络栈,双方在 网络名称空间 并没有隔离。
例如,如果容器绑定到‘host’网络的80端口,则可通过宿主机IP加上80端口访问容器。
'host’类型网络驱动 只在Linux操作系统主机 上可用,不支持 Mac、Windows

容器联通 --link

在实际运用中,我们更想实现的效果是: 同一个网络下的两个容器 不必通过ip,而是通过容器名字就可以 ping通彼此。
在创建容器的使用 使用 --link 指定 一个容器名字 就可以实现 通过容器名字就可以 ping通。

# 现在已经创建容器 wtt
# 再创建一个容器hero 联通 wtt
docker run -d --name hero --link wtt imageID

现在 hero 可以 通过 容器名字 ping 通 wtt 了,但是 wtt 通过 容器名字 无法ping通 hero.
这个现象的原因 和 --link 的工作原理有关:
–link wtt 的工作原理 实际上就是 在 hero的 /etc/hosts 文件中 做了 wtt 和 容器wtt的ip 映射,
所以ping通 是单向的。

自定义网络

我们知道 docker 默认创建的 docker0 网络 下的容器之间 是无法通过 容器名 彼此 ping通的,
这也是 docker0 不足的地方,我们通过 自定义一个网络 就可以 弥补 docker0 此处的不足。

  • 自定义网络

# 简单 创建 类型为 bridge 的网络
docker network create wtt
  • 查看docker已有的网络
docker network ls
# 可以看到 其中就有 新创建的 网络 mynet
  • 创建容器是 加入 自定义网络
docker run -d --name aaaaaa --net mynet imageID
docker run -d --name bbbbbb --net mynet imageID
  • 查看 docker 网络详情
docker network inspect wtt
# 可以看到 新创建网络 mynet的详情信息,以及 加入 该网络的容器ip分配情况。

此时 只要加入 mynet 网络的容器 都是可以 通过 容器的名字 彼此ping通的。

  • 删除网络
docker network rm wtt

网络连通

思考这么一种情况, 现在创建了两个 自定义网络 mynet1 和 mynet2,
想让 mynet1 网络下 的 容器 abc 能 通过名字 ping通 mynet2 网络下的所有容器,
明显 这个需求是 两个局域网 之间的通信,

docker network connect mynet2 abc
# 此时 mynet1网络下的容器 abc 就能 通过名字 ping通 mynet2 网络下的所有容器

实现原理是: 上述命令 将 容器abc 加入到了 mynet2 网络中,
也就是 通过 1个容器 2个IP的方式 实现了 网络连通(跨局域网通信)

Docker-Compose

Docker-Compose 可以帮助我们批量管理容器
只需要通过一个 docker-compose.yml 文件去维护即可。

注意:docker-compose 启动的 容器 可以正常使用 docker 命令来管理。

1. 下载安装

  1. github官网下载 相应版本

这是使用1.24.1版本:
https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64
推荐使用 2.20.0 版本

  1. 处理下载好的文件
mv docker-compose-Linux-x86_64 /usr/local/docker-compose
chmod 777 /usr/local/docker-compose
配置path环境变量
docker-compose -v

2. 常用命令

在使用docker-compose的命令时, 默认会 在当前目录下找 docker-compose.yml文件

  • 启动服务
docker-compose up -d
  • 关闭&删除 服务
# 针对所有的 服务
docker-compose down

# 针对所有的 个别服务
docker-compose down 服务名称
  • 开启|关闭|重启 管理的容器
# 后面加上 服务名称, 针对的就是 个别服务,而不是所有的服务
docker-compose start|stop|restart [服务名称]
  • 查看 管理的容器
docker-compose ps
  • 查看日志
docker-compose logs -f

3. 基础用法

使用Docker-Compose管理Mysql 和 Tomcat 容器

docker-compose.yml 文件
  • yml 文件以 key: value的方式指定配置信息, 注意 冒号 和value之间一定要有 空格
  • 多个配置信息 以 换行 + 缩进 的方式进行区分
  • 不用使用 制表符
version: '1.0'
services:
    mysql:   # 服务名称
        restart: always   # 只要docker启动,该容器就总是跟着一起启动, 开机自启
        image: daocloud.io/library/mysql:5.7.4   # 制定镜像路径
        container_name: mysql   # 指定容器名称
        ports:
            - 3308:3306  # 端口号映射
        environment:
            MYSQL_ROOT_PASSWORD: root # 指定mysql的 root 的登录密码
            TZ: Asia/Shanghai   # 制定时区
        volumes:
            - /home/hero/data:/var/lib/mysql   # 映射数据卷
    tomcat:
        restart: always
        ....

4. 配合使用 Dockerfile

通过 build 关键字, 在 docker-compose.yml 文件中 使用 指定名称的 Dockerfile 文件。

version: '1.0'
services:
    wtt:   # 服务名称
        restart: always   # 只要docker启动,该容器就总是跟着一起启动
        build:    # 构建自定义镜像 
            context: ./dockerfilePath    # 制定dockerfile文件的所在路径
            dockerfile: Dockerfile   # 指定Dockerfile的文件名称, 默认为Dockerfile 
        container_name: wtt   # 指定容器名称
        ports:
            - 3308:3306  # 端口号映射
        environment:
            MYSQL_ROOT_PASSWORD: root # 指定mysql的 root 的登录密码
            TZ: Asia/Shanghai   # 制定时区
        volumes:
            - /home/hero/data:/var/lib/mysql   # 映射数据卷
  • 可以直接启动基于 docke-compose.yml 以及 Dockfile文件 构建的自定义镜像
  • 如果自定义镜像不存在,会帮助我们构建出自定义镜像,
  • 如果自定义镜像已经存在, 会直接运行这个自定义镜像
docker-compose up -d
  • 重新构建自定义镜像
docker-compose build
  • 运行前,重新构建
docker-compose -d --build

5. 使用 .env 配置文件

docker-compose.yml 文件启动时是可以使用 配置文件 的,它的配置文件默认是 同目录下.env 文件。
这样的好处是 抽离配置信息, 每次修改 无需修改 结构繁杂的 yml文件中。

  • .env文件
THE_NAME_IS=wtt
  • docker-compose.yml文件
version: '1.0'
services:
    goserver:
        restart: always
        image: wtt:1.1.1
        container_name: ${THE_NAME_IS}	# 使用配置文件中的变量要用 ${} 包裹。
        ports:
            - 8080:8080

6. docker-compose.yml 常用命令说明

默认的 模板文件 名称为docker-compose.yml,格式为YAML格式。
模板文件 是使用Compose的核心,涉及到的指令关键字也比较多。大部分指令跟docker run相关参数的含义都是类似的。

version

version: '3.5'

使用版本声明:

  1. v3 版本不支持 volume_from 和 extends 属性
  2. cpu 和 内存属性的设置移到了 deploy 中
  3. 2和3最大的区别在于 v3 版本直接支持 docker swarm,而 v2 版本不支持
  4. docker-compose 2.xx 使用 YAML v3 语法进行编写配置文件, docker-compose 1.xx 使用 YAML v1

networks

version: '3.5'

# 创建 自定义的网络
networks:
  backend:
    driver: bridge
  wtt:
    driver: bridge

网络配置

services

version: '3.5'

networks:
  backend:
    driver: bridge
  wtt:
    driver: bridge

services:
  # 自定义服务名称
  golang:
    # 指定容器名称 
    container_name: golang
    
    # 指定服务镜像, ====> 和 build 二选一 <=====
    image: ubuntu 

    # 指定构建使用的 Dockerfile 文件, ====> 和 image二选一  <=====
    build:
      context: ./dockerfilePath    # 指定 dockerfile文件 的 所在路径                 
      dockerfile: Dockerfile       # 指定 Dockerfile的文件名称, 默认就是 Dockerfile

    # 设置环境变量
    environment:                         
      - TZ=Asia/Shanghai 

    # 容器可获得root权限,当将privileged设置为true时,这意味着容器内的进程可以具有对主机系统的直接访问权限,包括访问设备、文件系统等特权操作。  
    # 通常情况下,Docker在一个安全的沙箱环境中运行容器,容器的进程不会对主机系统造成任何损害。然而,通过设置privileged为true,容器将绕过该沙箱环境,获取与主机系统相同的权限。
    # 使用privileged特权模式可能会存在一些潜在的风险和安全隐患。
    privileged: true

    # 设置挂载目录
    volumes:                             
      - /usr/src/code:/usr/src/code  
      - /home/bbb:/home/aaa  
      
    # 暴露端口,指定两者的端口(主机:容器),或者只是容器的端口(主机会被随机分配一个端口) 
    ports:                               
      - "8000:8000"
      - "8001:8001"
    
    # 暴露端口 而不必向主机发布它们,而只是会向链接的服务(linked service)提供,只有内部端口可以被指定 
    expose:  
     - "3000"  
     - "8000"  

    # 打开标准输入,可以接受外部输入
    stdin_open: true
    tty: true

    # 网络配置:这里使用 两个 自定义网络
    networks:
      - backend
      - wtt
    
    # always每次docker启动时候重启
    restart: always   

    # 根据 依赖的容器 制定 服务启动顺序, 
    depends_on:                                           
      - mysql

    # 连接到其他服务中的容器,可以指定服务名称和这个链接的别名,或者只指定服务名称 
    links:  
      - db:database  
      - redis  

7. 网关冲突

现象

在使用docker-compose up启动时,直接服务器断开连接,同时会出现一行字
Creating network “xxxxxxx” with the default driver

原因

这是因为启动docker服务后,默认会有个 docker0网卡,通过命令 ifconfig 可以看到该网卡
而 docker-compose 启动容器时,也会创建一个网卡,即上边出现的那行字就是在创建网卡,通过命令 docker network ls 可以看到这个网卡。

一般情况下,这么做不会出现问题,但是特殊情况下,docker-compose创建的网卡 可能会和 宿主机的 冲突,直接导致网络断开。
例如 服务器是 网关172.xx 的, docker 默认 docker0网桥 的 网关 也是172.xx,这就会导致 启动docker后就直接断网,远程连接不上服务器了。

解决

  • docker 网管冲突
    修改 docker 默认的网关,避开 宿主机。
    vim /etc/docker/daemon.json //如果没有该文件,请新建。
{
  "bip": "192.168.1.1/24"   #自定义docker0 网桥,如果 192 也冲突了  还是可以使用 10.xx 
}
  • docker-compose 网管冲突
    在 docker-compose.yml 中的 网络模型中,选择 bridge
network_mode: "bridge" 

这个方法生效的前提是:docker0 已经设置为与宿主机网桥不同网关段。

8. 实战运用

  • 目录结构
.
├── data  # 数据卷 映射目录
├── docker-compose.yml 
├── .env  # docker-compose.yml 文件的配置文件
├── dtm
│   └── config.yml  # dtm容器的配置文件
└── prometheus 
    └── prometheus.yml # prometheus容器的配置文件

3 directories, 4 files
  • docker-compose.yml
version: '3.5'
# docker-compose 2.xx 使用 YAML v3 语法进行编写配置文件, docker-compose 1.xx 使用 YAML v1
# 这里使用的 Docker Compose version v2.20.0

# 网络配置
networks:
  backend:
    driver: ${NETWORKS_DRIVER}

# 服务容器配置(etcd、mysql、redis、prometheus、grafana、jaeger、dtm)
services:
  etcd:                                  # 自定义容器名称
    image: bitnami/etcd
    environment:
      - TZ=${TZ}
      - ALLOW_NONE_AUTHENTICATION=yes
      - ETCD_ADVERTISE_CLIENT_URLS=http://etcd:2379
    ports:                               # 设置端口映射
      - "${ETCD_PORT}:2379"
    networks:
      - backend
    restart: always

  mysql:
    image: mysql:5.7
    environment:
      - TZ=${TZ}
      - MYSQL_USER=${MYSQL_USERNAME}                  # 设置 Mysql 用户名称
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}              # 设置 Mysql 用户密码
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}    # 设置 Mysql root 用户密码
    privileged: true
    volumes:
      - ${DATA_PATH_HOST}/mysql:/var/lib/mysql        # 引用 .env 配置中 DATA_PATH_HOST 变量,将宿主机上存放 Mysql 数据的目录挂载到容器中 /var/lib/mysql 目录
    ports:
      - "${MYSQL_PORT}:3306"                          # 设置容器3306端口映射指定宿主机端口
    networks:
      - backend
    restart: always

  redis:
    image: redis:5.0
    environment:
      - TZ=${TZ}
    privileged: true
    volumes:
      - ${DATA_PATH_HOST}/redis:/data                 # 引用 .env 配置中 DATA_PATH_HOST 变量,将宿主机上存放 Redis 数据的目录挂载到容器中 /data 目录
    ports:
      - "${REDIS_PORT}:6379"                          # 设置容器6379端口映射指定宿主机端口
    networks:
      - backend
    restart: always

  prometheus:
    image: bitnami/prometheus
    environment:
      - TZ=${TZ}
    privileged: true
    volumes:
      - ./prometheus/prometheus.yml:/opt/bitnami/prometheus/conf/prometheus.yml  # 将 prometheus 配置文件挂载到容器里
    ports:
      - "${PROMETHEUS_PORT}:9090"                     # 设置容器9090端口映射指定宿主机端口,用于宿主机访问可视化web
    networks:
      - backend
    restart: always

  grafana:
    image: grafana/grafana
    environment:
      - TZ=${TZ}
    ports:
      - "${GRAFANA_PORT}:3000"                        # 设置容器3000端口映射指定宿主机端口,用于宿主机访问可视化web
    networks:
      - backend
    restart: always

  jaeger:
    image: jaegertracing/all-in-one:1.28
    environment:
      - TZ=${TZ}
    ports:
      - "${JAEGER_PORT}:16686"                        # 设置容器16686端口映射指定宿主机端口,用于宿主机访问可视化web
    networks:
      - backend
    restart: always

  dtm:
    image: yedf/dtm
    environment:
      - TZ=${TZ}
    entrypoint:
      - "/app/dtm/dtm"
      - "-c=/app/dtm/configs/config.yaml"
    privileged: true
    volumes:
      - ./dtm/config.yml:/app/dtm/configs/config.yaml # 将 dtm 配置文件挂载到容器里
    ports:
      - "${DTM_HTTP_PORT}:36789"
      - "${DTM_GRPC_PORT}:36790"
    networks:
      - backend
    restart: always
  • .env
# 设置时区
TZ=Asia/Shanghai
# 设置网络模式
NETWORKS_DRIVER=bridge


# PATHS ##########################################
# 宿主机上Mysql Reids数据存放的目录路径
DATA_PATH_HOST=./data


# MYSQL ##########################################
# Mysql 服务映射宿主机端口号,可在宿主机127.0.0.1:3306访问
MYSQL_PORT=3306
MYSQL_USERNAME=admin
MYSQL_PASSWORD=123456
MYSQL_ROOT_PASSWORD=123456

# Mysql 可视化管理用户名称,同 MYSQL_USERNAME
MYSQL_MANAGE_USERNAME=admin
# Mysql 可视化管理用户密码,同 MYSQL_PASSWORD
MYSQL_MANAGE_PASSWORD=123456
# Mysql 可视化管理ROOT用户密码,同 MYSQL_ROOT_PASSWORD
MYSQL_MANAGE_ROOT_PASSWORD=123456
# Mysql 服务地址
MYSQL_MANAGE_CONNECT_HOST=mysql
# Mysql 服务端口号
MYSQL_MANAGE_CONNECT_PORT=3306
# Mysql 可视化管理映射宿主机端口号,可在宿主机127.0.0.1:1000访问
MYSQL_MANAGE_PORT=1000


# REDIS ##########################################
# Redis 服务映射宿主机端口号,可在宿主机127.0.0.1:6379访问
REDIS_PORT=6379

# Redis 可视化管理用户名称
REDIS_MANAGE_USERNAME=admin
# Redis 可视化管理用户密码
REDIS_MANAGE_PASSWORD=123456
# Redis 服务地址
REDIS_MANAGE_CONNECT_HOST=redis
# Redis 服务端口号
REDIS_MANAGE_CONNECT_PORT=6379
# Redis 可视化管理映射宿主机端口号,可在宿主机127.0.0.1:2000访问
REDIS_MANAGE_PORT=2000


# ETCD ###########################################
# Etcd 服务映射宿主机端口号,可在宿主机127.0.0.1:2379访问
ETCD_PORT=2379

ETCD_MANAGE_PORT=7000

# PROMETHEUS #####################################
# Prometheus 服务映射宿主机端口号,可在宿主机127.0.0.1:3000访问
PROMETHEUS_PORT=3000


# GRAFANA ########################################
# Grafana 服务映射宿主机端口号,可在宿主机127.0.0.1:4000访问
GRAFANA_PORT=4000


# JAEGER #########################################
# Jaeger 服务映射宿主机端口号,可在宿主机127.0.0.1:5000访问
JAEGER_PORT=5000


# DTM #########################################
# DTM HTTP 协议端口号
DTM_HTTP_PORT=36789
# DTM gRPC 协议端口号
DTM_GRPC_PORT=36790
  • dtm/config.yml
# 指定要存储trans状态的存储驱动
# Store: 

### 默认存储驱动
#   Driver: 'boltdb'

### redis 存储驱动
#   Driver: 'redis'
#   Host: 'localhost'
#   User: ''
#   Password: ''
#   Port: 6379

### mysql 存储驱动
#   Driver: 'mysql'
#   Host: 'mysql'
#   User: 'root'
#   Password: '123456'
#   Port: 3306

### postgres 存储驱动
#   Driver: 'postgres'
#   Host: 'localhost'
#   User: 'postgres'
#   Password: 'mysecretpassword'
#   Port: '5432'

### 以下配置仅适用于 postgres/mysql 驱动
#   MaxOpenConns: 500
#   MaxIdleConns: 500
#   ConnMaxLifeTime: 5
#   TransGlobalTable: 'dtm.trans_global'
#   TransBranchOpTable: 'dtm.trans_branch_op'

### 以下配置仅适用于 redis/boltdb 驱动
#   DataExpire: 604800 # Trans 过期时间
#   RedisPrefix: '{}'  # Redis 存储前缀

# 微服务
MicroService:
  Driver: 'dtm-driver-gozero'           # 要处理注册/发现的驱动程序的名称
  Target: 'etcd://etcd:2379/dtmservice' # 注册 dtm 服务的 etcd 地址
  EndPoint: 'dtm:36790'

# 以下配置的单位为'秒'
# TransCronInterval: 3
# TimeoutToFail: 35
# RetryInterval: 10

# 日志等级
# LogLevel: 'info' 
  • prometheus/prometheus.yml
# my global config
global:
  scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute. 
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.     
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.  
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    static_configs:
      - targets: ["localhost:9090"]

  # 我们自己的商城项目配置
  - job_name: 'mall'
    static_configs:
      # 目标的采集地址
      - targets: ['golang:9080']
        labels:
          # 自定义标签
          app: 'user-api'
          env: 'test'

      - targets: ['golang:9090']
        labels:
          app: 'user-rpc'
          env: 'test'

      - targets: ['golang:9081']
        labels:
          app: 'product-api'
          env: 'test'

      - targets: ['golang:9091']
        labels:
          app: 'product-rpc'
          env: 'test'

      - targets: ['golang:9082']
        labels:
          app: 'order-api'
          env: 'test'

      - targets: ['golang:9092']
        labels:
          app: 'order-rpc'
          env: 'test'

      - targets: ['golang:9083']
        labels:
          app: 'pay-api'
          env: 'test'

      - targets: ['golang:9093']
        labels:
          app: 'pay-rpc'
          env: 'test'

Docker仓库管理

Docker仓库, 类似于apt仓库, 是用来保存镜像的仓库.
为了方便管理和使用Docker镜像, 可以将镜像集中保存至Docker仓库中,
将制作好的镜像推送(push)到仓库之种保存, 在需要镜像时, 从仓局中拉取(pull)镜像即可.
目前 Docker 官方维护了一个公共仓库 Docker Hub。

Docker仓库分为 公有云仓库私有云仓库:

  • 公有云仓库
    由互联网公司对外公开的仓库, 官方Docker hub、阿里云等第三方仓库
  • 私有云仓库
    在组织内部搭建的仓库, 一般只为组织内部使用, 常使用如下软件搭建仓库,
    docker registry、docker harbor

登录操作

# -u : 登陆的用户名
# -p : 登陆的密码
ocker login -u wtt -p 123456
# 登录到指定仓库 : hub.atguigu.com
docker login -u wtt -p 123456 hub.atguigu.com

# 查看登录用户
docker info | grep Username

# 退出登录
docker logout
  • 登陆 到一个Docker镜像仓库,如果未指定镜像仓库地址,默认登录到官方仓库 Docker Hub, 退出登录 亦是如此。
  • 执行 docker login 命令登录后, 会 生成 ~/.docker/config.json文> 件保存验证信息, 也可以通过这个文件 判断当前登录的用户是否已经退出
# config.json 文件
{
        "auths": {
            # 这里有内容 则说明 用户登录 未退出。
        },
        "HttpHeaders": {
                "User-Agent": "Docker-Client/19.03.8 (linux)"
        }
}

上传镜像到仓库

  1. 在用户名为 tom 的docker hub 账号下创建一个wtt仓库
  2. 给要上传的镜像按照规则打上标签名
# 标签名规则: -----======>   仓库服务域名地址/用户名/仓库名:镜像说明   <======-----
# 仓库服务域名地址 默认 docker.io, 可以省略, 因为上传镜像默认就是向自己的Docker Hub上传
docker tag hello-world:latest tom/wtt:test

# 上传镜像
docker push tom/wtt:test

# 下载镜像
docker pull tom/wtt:test

Harbor

Harbor是构建企业级私有docker镜像的仓库的开源解决方案,
它是Docker Registry的更高级封装,它除了提供友好的Web UI界面,
角色和用户权限管理,用户操作审计等功能。
另外它还整合了两个开源的安全组件,一个是Notary,另一个是Clair。
简单来说harbor就是VMWare公司提供的一个docker私有仓库构建程序,功能非常强大。

磁盘空间清理

Docker 很占用空间,每当我们运行容器、拉取镜像、部署应用、构建自己的镜像时,我们的磁盘空间会被大量占用。

  • 查看 空间占用项
docker system df

# 输出:
TYPE               TOTAL      ACTIVE       SIZE           RECLAIMABLE
Images             17         9            5.799GB        2.271GB (39%)
Containers         10         0            247.6MB        247.6MB (100%)
Local Volumes      30         2            300MB          93.04MB (31%)
Build Cache        0          0            0B             0B

# 名词解释:
TYPE:资源类型
    Images:所有镜像占用的空间,包括拉取下来的镜像,和本地构建的。
    Containers:运行的容器占用的空间,表示每个容器的读写层的空间。
    Local Volumes:容器挂载本地数据卷的空间。
    Build Cache:镜像构建过程中产生的缓存空间

SIZE:占用空间大小

RECLAIMABLE:可回收空间大小。当停止容器后,容器占用的空间就会变为可回收的
  • 清理 容器 空间

每次创建一个容器时,都会有一些文件和目录被创建,比如说:

  • 容器日志占用的空间
    /var/lib/docker/containers/ID目录,如果容器使用了默认的日志模式,他的所有日志都会以JSON形式保存到此目录下。
  • 容器应用数据
    /var/lib/docker/overlay2 目录下含有容器的读写层,如果容器使用自己的文件系统保存了数据,那么就会写到此目录下。

删除容器 时 会自动删除其关联的 读写层 占用的空间

docker rm ContainID

# 一键删除所有已经停止的容器
docker container prune
  • 清理 镜像 空间

有一些镜像是隐形的:

  • 子镜像,就是被其他镜像引用的中间镜像,不能被删除。
  • 悬挂状态的镜像,就是不会再被使用的镜像,可以被删除。
# 列出所有 悬挂状态的镜像 
docker image ls -f dangling=true

# 清楚掉
docker image rm $(docker image ls -f dangling=true -q)

# or
docker image prune
  • 清理 数据卷 空间

数据卷是容器自身文件体统之外的数据存储。

# 删除不再使用的数据卷
docker volume prune
  • 清理 构建缓存 卷空间

Docker 18.09 引入了 BuildKit,提升了 构建过程 的性能、安全、存储管理等能力

# 删除不再使用的数据卷
docker builder prune
  • 一键清理

docker 系统层面也有 prune 这个子命令,可以一键清理没用的空间

# 定期执行这个命令是个好习惯。
docker system prune

prune: 修剪枝叶

特别说明

防火墙对docker的影响

docker服务 启动时定义的自定义 链docker
由于centos7 firewall的底层是使用iptables进行数据过滤,建立在iptables之上。
当 firewalld 启动 或者 重启 的时候,将会从 iptables 中 移除 docker的规则,从而影响了 Docker 的正常工作。

解决办法:
重启 firewalld 后,再重启 Docker 进程,重启docker服务及可重新生成自定义链docker。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值