Docker 快速入门


Docker 概述

Docker 是什么

  1. 使用最广泛的开源容器引擎
  2. 一种操作系统级的虚拟化技术
  3. 依赖于Linux内核特性:Namespace(资源隔离)和Cgroups(资源限制)
  4. 一个简单的应用程序打包工具

Docker 设计目标

  1. 提供简单的应用程序打包工具
  2. 开发人员和运维人员职责逻辑分离
    • 开发人员 如何使用docker部署
    • 运维人员 如何管理容器
  3. 多环境保持一致性
    • 构建:镜像产出 线上线下环境一致
    • 运输:处处运行 不需要配置和修改 减少环境不一致的问题
    • 构建,运输,随处运行 (Build,Ship and Run any App,Angwhere)

Docker 基本组成

  1. Docker Client:客户端
  2. Ddocker Daemon:守护进程
  3. Docker Images:镜像
  4. Docker Container:容器
  5. Docker Registry:镜像仓库

容器 vs 虚拟机

Docker 应用场景

  1. 应用程序打包和发布
  2. 应用程序隔离
  3. 持续集成
  4. 部署微服务
  5. 快速搭建测试环境
  6. 提供PaaS产品(平台即服务)

Docker 安装

Docker版本

  1. 社区版(Community Edition,CE)
  2. 企业版(Enterprise Edition,EE)

支持平台

CentOS7.x 安装 Docker

1
2
3
# 官方文档
https://docs.docker.com/
https://docs.docker.com/v18.03/
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
# 关闭方后墙和selinux
[root@docker ~]# systemctl status firewalld
[root@docker ~]# systemctl disable firewalld

[root@docker ~]# getenforce
Disabled

# 安装依赖包
yum install -y yum-utils device-mapper-persistent-data lvm2

# 添加Docker官方源
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 查看仓库中所有docker安装版本 docker-ce-stable
yum list docker-ce --showduplicates | sort -r

# 安装指定版本 Docker CE
# yum install docker-ce-<VERSION STRING>
yum install docker-ce-18.06.3.ce

# 默认安装是最新版本
yum install docker-ce -y

# 启动docker
systemctl start docker
systemctl enable docker.service

# 通过运行hello world 镜像验证Docker是否正确安装。
docker run hello-world

# 查看docker 版本信息
docker info

# 卸载 Docker CE
yum remove docker-ce
rm -rf /var/lib/docker

Docker 镜像管理

镜像是什么

1
2
3
4
5
1. 一个分层存储的文件
2. 一个软件的环境
3. 一个镜像可以创建N个容器
4. 一种标准化的交付1.
5. 一个不包含Linux内核而又精简的Linux操作系统
  1. 镜像不是一个单一的文件,而是有多层构成。
  2. 我们可以通过docker history <ID/NAME> 查看镜像中各层内容及大小,
  3. 每层对应着Dockerfile中的一条指令。
  4. Docker镜像默认存储在/var/lib/docker/\<storage-driver>中。

镜像仓库

  1. Docker Hub是由Docker公司负责维护的公共注册中心,包含大量的容器镜像,Docker工具默认从这个公共镜像库下载镜像。
  2. 地址:https://hub.docker.com/explore

镜像加速器 时速云

1
2
3
4
https://www.daocloud.io/mirror

curl -sSL https://get.daocloud.io/daotools/set_mirror.sh | sh -s http://f1361db2.m.daocloud.io
systemctl restart docker

镜像与容器联系

  1. 容器其实是在镜像的最上面加了一层读写层,在运行容器里文件改动时,
  2. 会先从镜像里要写的文件复制到容器自己的文件系统中(读写层)。
  3. 如果容器删除了,最上面的读写层也就删除了,改动也就丢失了。所以无论多
  4. 少个容器共享一个镜像,所做的写操作都是从镜像的文件系统中复制过来操作的,并不会修改镜像的源文件,
  5. 这种方式提高磁盘利用率。
  6. 若想持久化这些改动,可以通过docker commit 将容器保存成一个新镜像。

查看读写层

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@docker ~]# docker run -it -d --name web nginx:1.16
[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d09ffde2ee5e nginx:1.16 "nginx -g 'daemon of…" 29 seconds ago Up 27 seconds 80/tcp web
[root@docker ~]# docker inspect d09ffde2ee5e

[root@docker 0b6de35669d9b5a5ae37514e3236083b3fed6d8f5b1f4ad9fd1c2ba50fdc96ff]# ls
diff link lower merged work

# 开启个端口 添加个文件
[root@docker ~]# docker exec -it d09ffde2ee5e bash
root@d09ffde2ee5e:/# touch test.log

[root@docker merged]# cd /var/lib/docker/overlay2/0b6de35669d9b5a5ae37514e3236083b3fed6d8f5b1f4ad9fd1c2ba50fdc96ff/merged/
[root@docker merged]# ls -l

镜像常用命令

镜像离线导出和导入

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
# 离线传镜像 导出和导入

[root@docker ~]# docker pull busybox
Using default tag: latest
latest: Pulling from library/busybox
7c9d20b9b6cd: Pull complete
Digest: sha256:be38efcd3a8289ff66e60c5652beed36968eeb856ae5d90f3cf02ea81e4a57da
Status: Downloaded newer image for busybox:latest

[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
busybox latest 19485c79a9bb 7 weeks ago 1.22MB

[root@docker ~]# docker image save busybox > busybox.tar
[root@docker ~]# docker image rm busybox
Untagged: busybox:latest
Untagged: busybox@sha256:be38efcd3a8289ff66e60c5652beed36968eeb856ae5d90f3cf02ea81e4a57da
Deleted: sha256:19485c79a9bbdca205fce4f791efeaa2a103e23431434696cc54fdd939e9198d
Deleted: sha256:6c0ea40aef9d2795f922f4e8642f0cd9ffb9404e6f3214693a1fd45489f38b44

[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 540a289bab6c 6 days ago 126MB
hello-world latest fce289e99eb9 10 months ago 1.84kB

[root@docker ~]# docker image load < busybox.tar
6c0ea40aef9d: Loading layer [==================================================>] 1.437MB/1.437MB
Loaded image: busybox:latest

查看镜像版本

1
2
# 官方镜像版本查找 筛选想要找到的镜像名称,选择tags查看
https://hub.docker.com/_/nginx

Docker 容器管理

创建容器常用选项

1
2
3
# 容器退出时重启策略
# 镜像名称在最后,最后可以加cmd
docker run -d --name web3 --restart always -P nginx

容器资源限制

1
2
3
4
# OOM Killer 操作系统在cpu不够用时 会删除使用率最高的进程
# 当容器没有限制的时候 会使用所有的cpu和内存
# 如果 --memory-swap = --memory 那么容器使用swap 为0
# 不设置 --memory-swap = 默认是内存的2倍

1
2
3
4
5
6
7
[root@docker merged]#  docker run -it -d --name web05 --cpus=".5" --memory="500m" --memory-swap="600m" busybox
03d354261b2a3d0c2f51c3f5557f6c3ce7be2fbd2ac1b68e9a51f32c372c4f52

[root@docker merged]# docker stats --no-stream web05
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
03d354261b2a web05 0.00% 1.074MiB / 500MiB 0.21% 0B / 0B 0B / 0B 1
# 多个容器的资源限制要做 担心黑客攻击、搞瘫宿主机

管理容器常用命令

1
2
# 列出最新创建的容器
[root@docker ~]# docker ps -l
1
2
# 显示一个容器运行的进程
[root@docker merged]# docker top 03d354261b2a

Docker 数据管理

  1. 容器是即行即用的
  2. 设计目标其中一条就是,不要轻易的进入容器,修改容器
  3. 容器删除后里面的数据就不会存在,不能每次都commit构建一个新的镜像吧…
  4. 那我们程序中经常需要变动数据 如web的根目录,或者是mysql的data目录,怎么办呢?
  5. 需要专门的存储方式,不能存在镜像里
  6. 将这些经常变动的数据目录,放在宿主机存储,删除容器数据也不会丢失,下次使用的时候挂载宿主机里面的数据即可

将数据从宿主机挂载到容器中的三种方式

  1. Docker提供三种方式将数据从宿主机挂载到容器中:
  2. volumes:
    • Docker管理宿主机文件系统的一部分(/var/lib/docker/volumes)。保存数据的最佳方式。
    • 把数据存储在宿主机上
  3. bind mounts:
    • 将宿主机上的任意位置的文件或者目录挂载到容器中。
    • 常用于 容器想使用宿主机上的目录文件
  4. tmpfs:
    • 挂载存储在主机系统的内存中,而不会写入主机的文件系统。如果不希望将数据持久存储在任何位置,可以使用
    • tmpfs,同时避免写入容器可写层提高性能。

Volume

1
2
3
4
5
6
注意:
1. 如果没有指定卷,自动创建。
2. 建议使用--mount,更通用。

文档:
https://docs.docker.com/engine/admin/volumes/volumes/#start-a-container-with-a-volume
  1. 管理数据卷
1
2
3
4
5
6
# 创建
docker volume create nginx-vol
# 列出当前
docker volume ls
# 查看卷
docker volume inspect nginx-vol
  1. 用卷创建一个容器:
1
2
docker run -d --name=nginx-test --mount src=nginx-vol,dst=/usr/share/nginx/html nginx
docker run -d --name=nginx-test -v nginx-vol:/usr/share/nginx/html nginx
  1. 清理:
1
2
3
# docker stop nginx-test
# docker rm nginx-test
# docker volume rm nginx-vol
  1. 使用:
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
[root@docker merged]# docker volume create nginx_vol
nginx_vol
# 这个数据卷就可以让多个容器 共享数据
[root@docker merged]# docker volume ls
DRIVER VOLUME NAME
local nginx_vol

[root@docker merged]# docker volume inspect nginx_vol
[
{
"CreatedAt": "2019-10-29T12:12:35+08:00",
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/nginx_vol/_data", # 宿主机的挂载点
"Name": "nginx_vol",
"Options": {},
"Scope": "local"
}
]

[root@docker _data]# docker run -d --name web -p 80:80 --mount src=nginx_vol,dst=/usr/share/nginx/html nginx
[root@docker merged]# docker inspect web

[root@docker merged]# cd /var/lib/docker/volumes/nginx_vol/_data
[root@docker _data]# echo "hello nginx" > index.html

# 再启动一个容器 挂载同一个数据卷 查看数据是否共享
[root@docker _data]# docker run -d --name web2 -p 88:80 --mount src=nginx_vol,dst=/usr/share/nginx/html nginx

  1. 如果我们的某一个容器坏了,比如web2,如果不使用数据卷,里面的数据就看不到了
  2. 如果使用数据卷,直接挂载到nginx_vol就能看到了
  3. 删除容器,数据卷没有被删除掉,数据还在,除非也要删除数据卷
1
2
3
# 停掉web02 再启动一个容器 把名字改了 端口不变 继续使用
# 容器挂了没事,数据还在
[root@docker _data]# docker run -d --name web3 -p 88:80 --mount src=nginx_vol,dst=/usr/share/nginx/html nginx

Bind Mounts

1
2
3
注意:
1. 如果源文件/目录没有存在,不会自动创建,会抛出一个错误。
2. 如果挂载目标在容器中非空目录,则该目录现有内容将被隐藏。
1
2
3
4
5
6
7
8
9
10
用卷创建一个容器:
# docker run -d -it --name=nginx-test --mount type=bind,src=/app/wwwroot,dst=/usr/share/nginx/html nginx
# docker run -d -it --name=nginx-test -v /app/wwwroot:/usr/share/nginx/html nginx

验证绑定:
# docker inspect nginx-test

清理:
# docker stop nginx-test
# docker rm nginx-test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 必须先要有这个目录
[root@docker _data]# mkdir -p /app/www/root
[root@docker _data]# docker run -d -it --name=web -p 80:80 --mount type=bind,src=/app/www/root,dst=/usr/share/nginx/html nginx
9c16811aa4553e47bf3416bd3617a99ddff4f0f940e42804c76fae278a5bcb73


# 挂载的目录 没有数据 和数据卷有区别,数据卷会把容器的数据拿出来
[root@docker _data]# cd /app/www/root/
[root@docker root]# ls -l
total 0

# 查看容器里 没有数据 挂载了宿主机的目录
[root@docker ~]# docker exec -it d09ffde2ee5e bash
root@9c16811aa455:/# cd /usr/share/nginx/html/
root@9c16811aa455:/usr/share/nginx/html# ls
root@9c16811aa455:/usr/share/nginx/html#

# 再有新数据就会被同步过来
root@9c16811aa455:/usr/share/nginx/html# echo "hello nginx" > index.html
[root@docker root]# ls
index.html

数据管理总结

1
2
3
4
5
6
7
Volume特点: 
0 多个运行容器之间共享数据。
1. 当容器停止或被移除时,该卷依然存在。
2. 多个容器可以同时挂载相同的卷。
3. 当明确删除卷时,卷才会被删除。
4. 将容器的数据存储在远程主机或其他存储上
5. 将数据从一台Docker主机迁移到另一台时,先停止容器,然后备份卷的目录(/var/lib/docker/volumes/)
1
2
3
4
5
Bind Mounts特点: 
1. 从主机共享配置文件到容器。默认情况下,挂载主机/etc/resolv.conf到每个容器,提供DNS解析。
2. 在Docker主机上的开发环境和容器之间共享源代码。例如,可以将Maven target目录挂载到容器中,每次在Docker主机
上构建Maven项目时,容器都可以访问构建的项目包。
3. 当Docker主机的文件或目录结构保证与容器所需的绑定挂载一致时

Docker 网络模式

  1. bridge
    • –net=bridge
    • 默认网络,Docker启动后创建一个docker0网桥,默认创建的容器也是添加到这个网桥中。
  2. host
    • –net=host
    • 容器不会获得一个独立的network namespace,而是与宿主机共用一个。这就意味着容器不会有自己的网卡信息,
    • 而是使用宿主机的。
    • 容器除了网络,其他都是隔离的。
  3. none
    • –net=none
    • 获取独立的network namespace,但不为容器进行任何网络配置,需要我们手动配置。
  4. container
    • –net=container:Name/ID
    • 与指定的容器使用同一个network namespace,具有同样的网络配置信息,两个容器除了网络,其他都还是隔离的。
  5. 自定义网络
    • 与默认的bridge原理一样,但自定义网络具备内部DNS发现,可以通过容器名或者主机名容器之间网络通信。

bridge

1
[root@docker root]# docker run --rm -it --name bridge_test busybox bash

host

1
2
3
4
5
# 与宿主机网络配置一致
# 在这个容器起的任何服务,都是使用宿主机的网络命名空间
# 启动一个nginx 80端口 就真的是再使用宿主机的80端口
# 容器网络没有隔离,其他隔离
[root@docker root]# docker run --rm -it --net=host --name bridge_test busybox

none

1
2
3
# 独立的网络命名空间,什么都不给你配置,需要自己配置
# 自己手动配置 ip ns 这个用的很少
[root@docker root]# docker run --rm -it --net=none --name bridge_test busybox

container

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# container: 让web2 与 web1 使用同一个网络命名空间

[root@docker ~]# docker run -it -d --name web1 -p 88:80 busybox
0f94a0d8e173f0d724901c569f6c1df45e3e550503bc8b08ffcea231809f5c63
[root@docker ~]# docker run -it -d --name web2 --net=container:web1 nginx:1.16
771d7686b84e611c5058a59ad4fb238eeda5cac3ac0d1f907333a19b500a2235
# 访问88
http://47.94.132.190:88/

[root@docker ~]# docker exec -it web1 sh
/ # netstat -tnlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN -

自定义网络

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
# 默认创建的容器之间网络是隔离的,无法互相访问

[root@docker ~]# docker network create bs-test

[root@docker ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c21a41553f06 bridge bridge local
4ea4e3d78a78 bs-test bridge local
c86b096a9bad host host local
8ac4d9069089 none null local

# 创建两台容器 指定自定义网络 --net=bs-test
[root@docker ~]# docker run -it -d --name web1 --net=bs-test busybox
a4de56fe66deb842e15df0fc5e3729337a73da76171eee462987b187163a6a39
[root@docker ~]# docker run -it -d --name web2 --net=bs-test busybox
9b2d3aac32d33c34c579eccf063639cf81cc12618885da0b7da4f408b2b86aff

# 测试互相访问

/ # ping 172.20.0.3
PING 172.20.0.3 (172.20.0.3): 56 data bytes
64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.093 ms

/ # ping web2
PING web2 (172.20.0.3): 56 data bytes
64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.061 ms

自定义网段网络

  1. 创建网络的时候 指定网段
  2. 启动容器 指定IP
  3. 自动dns 这个解析 只对用户自定义网络有效
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
[root@docker ~]# docker network create --subnet 172.22.16.0/24 --gateway 172.22.16.1 my-net 
e9a2a92c293b2b08d5a3bd1344867cddf53c455d235b2fd960ddc10b5f09c871
[root@docker ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
c21a41553f06 bridge bridge local
a736c14402e0 bs-test bridge local
c86b096a9bad host host local
e9a2a92c293b my-net bridge local
8ac4d9069089 none null local

# 启动容器的时候 指定创建的网络 和 IP地址
[root@docker ~]# docker run -it -d --network=my-net --ip 172.22.16.100 --name web3 busybox
29747c8bb924b2aa0fc6e838614c21eef243df0015fb27d903f6a00abc6e019a
[root@docker ~]# docker run -it -d --network=my-net --ip 172.22.16.200 --name web4 busybox
9ce7e2a67a6c8d0dc900fa4985fe81e66a62f5c13ce5112164550fc12814be4a


[root@docker ~]# docker exec -it web3 sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
56: eth0@if57: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:16:10:64 brd ff:ff:ff:ff:ff:ff
inet 172.22.16.100/24 brd 172.22.16.255 scope global eth0
valid_lft forever preferred_lft forever

/ # ping 172.22.16.200
PING 172.22.16.200 (172.22.16.200): 56 data bytes
64 bytes from 172.22.16.200: seq=0 ttl=64 time=0.114 ms
64 bytes from 172.22.16.200: seq=1 ttl=64 time=0.066 ms

把容器加入到自定义网络

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
# 创建一个基础容器
[root@docker ~]# docker run -it -d --name web05 busybox
55920dacacf33aa03b7000c098a88750e0efb771c8e2eac53864d5d22a573b24

[root@docker ~]# docker exec -it web05 sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
60: eth0@if61: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever

# 加入到自定义网络 相当于添加了一块网卡
[root@docker ~]# docker network connect my-net web05

# 新增了网络配置
[root@docker ~]# docker exec -it web05 sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
60: eth0@if61: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
62: eth1@if63: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:16:10:02 brd ff:ff:ff:ff:ff:ff
inet 172.22.16.2/24 brd 172.22.16.255 scope global eth1
valid_lft forever preferred_lft forever

# 测试互通
/ # ping 172.22.16.100
PING 172.22.16.100 (172.22.16.100): 56 data bytes
64 bytes from 172.22.16.100: seq=0 ttl=64 time=0.129 ms

容器网络访问原理

veth pair(对)

  1. Docker安装完成时会创建一个 命名为 docker0 的 linux bridge

  1. 安装网桥工具
1
2
3
4
5
6
7
8
9
10
11
yum install bridge-utils

# 新建一台容器 默认使用bridge连接方式
[root@docker ~]# docker run -it -d --name web busybox
f60c49b81814a74d0ae7c06f9cd3f14e5ae2f57b7876afc2a1478f9f9bf73d22

# 查看网桥信息
# 一个新的网络接口 veth808f338 被挂到了 docker0 上,veth808f338 就是新创建容器的虚拟网卡。
[root@docker ~]# brctl show
bridge name bridge id STP enabled interfaces
docker0 8000.0242c21ccdde no veth808f338
  1. 容器的网络配置
1
2
容器有一个网卡 eth0@if65。大家可能会问了,为什么不是 veth808f338 呢?
实际上 eth0@if65 和 veth808f338 是一对 veth pair(对)。

  1. veth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,
  2. 网卡的一头(eth0@if65)在容器中,另一头(veth808f338)挂在网桥 docker0 上,
  3. 其效果就是将 eth0@if65 也挂在了 docker0 上。
  4. eth0@if65 已经配置了 IP 172.18.0.2,为什么是这个网段呢?
1
2
3
4
5
[root@docker ~]# docker network inspect bridge

bridge 网络配置的 subnet 就是 172.18.0.0/16,并且网关是 172.18.0.1
这个网关在哪儿呢?大概你已经猜出来了,就是 docker0
容器创建时,docker 会自动从 172.18.0.0/16 中分配一个 IP,这里 16 位的掩码保证有足够多的 IP 可以供容器使用。

  1. Docker 提供三种 用户自定义 网络驱动:bridge, overlay 和 macvlan。
  2. overlay 和 macvlan 用于创建跨主机的网络,我们可通过 bridge 驱动创建类似前面默认的 bridge 网络。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
docker network create --subnet 172.22.16.0/24 --gateway 172.22.16.1 my-net 

# 这里我们创建了新的 bridge 网络 my-net,网段为 172.22.16.0/24,网关为 172.22.16.1。
# 与前面一样,网关在 my-net 对应的网桥 br-5d863e9f78b6 上:

[root@docker ~]# brctl show
bridge name bridge id STP enabled interfaces
br-78039658d427 8000.0242f6ae7c81 no
docker0 8000.0242c21ccdde no veth808f338

[root@docker ~]# ifconfig

[root@docker ~]# docker network inspect my-net

# 只有使用 --subnet 创建的网络才能指定静态 IP。
docker run -it -d --network=my-net --ip 172.22.16.100 --name web3 busybox
[root@docker ~]# docker exec -it web3 sh

# veth pair(对) vethd795390 eth0@if65

[root@docker ~]# brctl show
bridge name bridge id STP enabled interfaces
br-78039658d427 8000.0242f6ae7c81 no vethd795390
docker0 8000.0242c21ccdde no veth808f338

数据包是怎样到达外网的

1
2
3
4
5
# 查看iptables 
[root@docker ~]# iptables -t nat -S

-A POSTROUTING -s 172.22.16.0/24 ! -o br-78039658d427 -j MASQUERADE
-A POSTROUTING -s 172.18.0.0/16 ! -o docker0 -j MASQUERADE

如果网桥 docker0 收到来自 172.18.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。
而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)。

  1. tcpdump 查看地址是如何转换的
1
2
3
4
5
6
7
[root@docker ~]# ip r
default via 172.17.79.253 dev eth0
169.254.0.0/16 dev eth0 scope link metric 1002

172.17.64.0/20 dev eth0 proto kernel scope link src 172.17.70.238
172.18.0.0/16 dev docker0 proto kernel scope link src 172.18.0.1
172.22.16.0/24 dev br-78039658d427 proto kernel scope link src 172.22.16.1
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
# 监控 docker0 和 eth0 上的 icmp(ping)数据包
当 busybox ping www.baidu.com 时,tcpdump 输出如下:

/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
64: eth0@if65: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue
link/ether 02:42:ac:12:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.18.0.2/16 brd 172.18.255.255 scope global eth0
valid_lft forever preferred_lft forever
/ # ping www.baidu.com
PING www.baidu.com (220.181.38.149): 56 data bytes
64 bytes from 220.181.38.149: seq=0 ttl=51 time=4.998 ms
64 bytes from 220.181.38.149: seq=1 ttl=51 time=5.050 ms


[root@docker ~]# tcpdump -i docker0 -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on docker0, link-type EN10MB (Ethernet), capture size 262144 bytes
21:23:21.775420 IP 172.18.0.2 > 220.181.38.149: ICMP echo request, id 6400, seq 0, length 64
21:23:21.780433 IP 220.181.38.149 > 172.18.0.2: ICMP echo reply, id 6400, seq 0, length 64


[root@docker ~]# tcpdump -i eth0 -n icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
21:23:21.775433 IP 172.17.70.238 > 220.181.38.149: ICMP echo request, id 6400, seq 0, length 64
21:23:21.780418 IP 220.181.38.149 > 172.17.70.238: ICMP echo reply, id 6400, seq 0, length 64

  1. 流程总结
1
2
3
4
5
6
7
8
9
1. busybox 发送 ping 包:172.18.0.2 > www.baidu.com。

docker0 收到包,发现是发送到外网的,交给 NAT 处理。

NAT 将源地址换成 eth0 的 IP:172.17.70.238 >www.baidu.com。

ping 包从 eth0 发送出去,到达 www.baidu.com。

通过 NAT,docker 实现了容器对外网的访问。

外网是如何访问容器的

  1. 数据包发送给宿主机后 通过DNAT 转给容器
1
2
3
4
5
6
[root@docker ~]# docker run -it -d -p 88:80 --name web6 nginx:1.16
51972606abafed3a064986f6460ad794c839b62723ba9a48a51ebc5bfcbf7de9

[root@docker ~]# iptables-save
# 端口转发
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 88 -j DNAT --to-destination 172.18.0.3:80

Dockerfile

Dockerfile 格式

1
2
3
4
5
6
7
8
9
10
11
12
FROM                    # 指定基础镜像FROM centos
MAINTAINER # 指定维护者信息,可以没有
RUN # 在命令前面加上RUN即可 RUN yum install httpd -y
ADD # 复制文件,与COPY不同的是,如果 src 是归档文件(tar, zip, tgz, xz 等),文件会被自动解压到 dest
WORKDIR # 设置当前工作目录
VOLUME # 设置卷,挂载主机目录
EXPOSE # 指定对外的端口
CMD # 容器启动时执行的命令CMD [“/bin/bash”],只有最后一个生效。CMD 可以被 docker run 之后的参数替换。

COPY # 将文件从 build context 复制到镜像。COPY src dest
ENV # 环境变量
ENTRYPOINT # 容器启动后执行的命令,只有最后一个生效(无法被替换,CMD 或 docker run 之后的参数会被当做参数传递给 ENTRYPOINT)

Build镜像

快速部署 LNMP 网站平台

  1. 生产上应该注意到的
1
2
3
4
1. 编译安装 (1.configure 2.make 3.make install) 还是 yum安装 (yum源)   RUN  
2. 编译安装需要启用哪些模块 RUN
3. nginx初始化 RUN
4. 启动 CMD
1
2
1. 可以先自己准备一个centos环境 然后自己在上面操作一次
2. 都成功后 再写 Dockerfile
1
2
3
1. 让自己生成的镜像越小越好
2. 使用 && 拼接命令
3. 清理yum缓存,删除源码包
1
2
3
1. 按照项目写dockerfile
2. 底层是操作系统
3. 以操作系统为最底层一层一层构建

目录规划 和 系统基础镜像

1
2
3
4
5
6
7
8
9
10
# 按项目创建目录分层

[root@docker ~]# mkdir -p /opt/game/{base,runtime}
[root@docker ~]# mkdir -p /opt/game/base/centos

[root@docker ~]# tree /opt/game/
/opt/game/ # 项目
├── base # 基础环境
│   └── centos # centos 基础镜像
└── runtime # 运行环境

构建 centos:7 基础镜像

  1. 由于官方提供的centos镜像有很多基础命令没有安装,所有我们自己来构建一个基础base
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@docker centos]# vim Dockerfile

# BASE
FROM centos:7

# WHO
MAINTAINER leo 365042337@qq.com

# EPEL
COPY epel.repo /etc/yum.repos.d/
COPY CentOS-Base.repo /etc/yum.repos.d/


# EPEL And PKG
RUN echo "==> Install curl and helper tools..." && \
yum install -y net-tools wget vim tree lsof tupdump gcc glibc gcc-c++ make && \
echo "==> Clean up..." && \
yum clean all && \
rm -rf /var/cache/yum/*
1
2
3
4
# 也可以下载
#cd /etc/yum.repos.d;mkdir bak;mv *.repo bak/ && \
#curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && \
#curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo && \
1
2
3
4
5
6
# 构建镜像 game/centos:7
[root@docker centos]# docker build -t game/centos:7 .

[root@docker centos]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
game/centos 7 11810c6646f1 About a minute ago 381MB

构建 Nginx 基础镜像

1
2
3
4
5
6
7
8
9
10
11
[root@docker nginx]# mkdir -p /opt/game/runtime/nginx
[root@docker nginx]# cd /opt/game/runtime/nginx

# 使用nginx官方yum源 安装最新稳定版
[root@docker nginx]# vim nginx.repo

[nginx]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@docker nginx]# vim Dockerfile 

# BASE
FROM game/centos:7

# WHO
MAINTAINER leo 365042337@qq.com

# YUM-REPO
COPY nginx.repo /etc/yum.repos.d/

# PKG
RUN yum install pcre-devel zlib-devel openssl-devel autoconf nginx -y && \
echo "==> Clean up..." && \
yum clean all && \
rm -rf /var/cache/yum/*

# PROT
EXPOSE 80

# CMD
CMD ["nginx", "-g", "daemon off;"]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 构建
[root@docker nginx]# docker build -t game/nginx:1.16 .

# 查看镜像
[root@docker nginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
game/nginx 1.16 b3fe0ded9499 8 minutes ago 437MB

# 启动测试容器
[root@docker nginx]# docker run -it -d --name test -p 80:80 game/nginx:1.16

# 查看进程
[root@docker nginx]# docker top test

# 公网测试访问
http://47.94.221.247/
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
# 传个测试首页测试 构建个项目
# 项目镜像基于基础镜像打包
[root@docker nginx]# echo "hello nginx" > index.html

[root@docker nginx]# vim Dockerfile-test

# BASE
FROM game/nginx:1.16

# WHO
MAINTAINER leo 365042337@qq.com

# COPY
COPY index.html /usr/share/nginx/html/

# 构建成项目镜像
[root@docker nginx]# docker build -t game/app:v1 -f Dockerfile-test .

[root@docker nginx]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
game/app v1 3cba5a6f4f7b 4 seconds ago 437MB
game/nginx 1.16 b3fe0ded9499 24 minutes ago 437MB

# 启动容器测试
[root@docker nginx]# docker run -it -d --name v1 -p 88:80 game/app:v1
75c0c995be3e04a3cd2e8f413a5a95222f89837c74440f7483e259b2f3e8cd0b

# 访问测试
http://47.94.221.247:88/

构建 PHP 基础镜像

  1. 创建构建目录
1
[root@docker runtime]# mkdir -p /opt/game/runtime/php/
  1. 编写php运行环境dockerfile
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
# 提前准备好优化的配置文件

[root@docker php]# vim Dockerfile

# BASE
FROM game/centos:7

# WHO
MAINTAINER leo 365042337@qq.com

# CMD
RUN rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm && \
rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm && \
yum install php72w php72w-cli php72w-common php72w-devel \
php72w-embedded php72w-gd php72w-mbstring php72w-pdo php72w-xml \
php72w-fpm php72w-mysqlnd php72w-opcache -y && \

# CONF
COPY php.ini /etc/
COPY php-fpm.conf /etc/
COPY php-fpm.conf /etc/php-fpm.d/

# PROT
EXPOSE 9000

# CMD
CMD ["php-fpm"]
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
# 编译安装
[root@docker php]# vim Dockerfile

# BASE
FROM game/centos:7

# WHO
MAINTAINER leo 365042337@qq.com

# CMD
RUN yum install -y wget vim pcre pcre-devel openssl openssl-devel libicu-devel gcc gcc-c++ \
autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel \
zlib zlib-devel glibc glibc-devel glib2 glib2-devel ncurses ncurses-devel curl curl-devel \
krb5-devel libidn libidn-devel openldap openldap-devel nss_ldap jemalloc-devel cmake \
boost-devel bison automake libevent libevent-devel gd gd-devel libtool* libmcrypt libmcrypt-devel \
mcrypt mhash libxslt libxslt-devel readline readline-devel gmp gmp-devel libcurl libcurl-devel openjpeg-devel && \
yum clean all && \
rm -rf /var/cache/yum/*

# COPY
COPY php-7.2.24.tar.gz /opt/

RUN cd /opt/ && groupadd www && useradd -g www www && tar -zxf php-7.2.24.tar.gz && cd php-7.2.24 && cp -frp /usr/lib64/libldap* /usr/lib/ && \
./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --enable-fpm \
--with-fpm-user=www --with-fpm-group=www --enable-mysqlnd --with-mysqli=mysqlnd --with-pdo-mysql=mysqlnd --enable-mysqlnd-compression-support \
--with-iconv-dir --with-freetype-dir --with-jpeg-dir --with-png-dir --with-zlib --with-libxml-dir --enable-xml --disable-rpath --enable-bcmath \
--enable-shmop --enable-sysvsem --enable-inline-optimization --with-curl --enable-mbregex --enable-mbstring --enable-intl --with-mcrypt --with-libmbfl \
--enable-ftp --with-gd --enable-gd-jis-conv --enable-gd-native-ttf --with-openssl --with-mhash --enable-pcntl --enable-sockets --with-xmlrpc --enable-zip \
--enable-soap --with-gettext --disable-fileinfo --enable-opcache --with-pear --enable-maintainer-zts --with-ldap=shared --without-gdbm && \
make -j 4 && make install && \
mkdir -p /var/log/php-fpm && \
cd /opt/ && rm -rf php* \

# CONF
COPY php.ini /usr/local/php/etc/
COPY php-fpm.conf /usr/local/php/etc/
COPY www.conf /usr/local/php/etc/php-fpm.d/

ENV PATH $PATH:/usr/local/php/sbin
WORKDIR /usr/local/php

# PROT
EXPOSE 9000

# CMD
CMD ["php-fpm"]
1
2
3
4
5
6
# 编译
[root@docker php]# docker build -t game/php:v2 .

[root@docker php]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
game/php v2 02d40f53884b 13 minutes ago 880MB
1
2
3
4
5
6
7
8
9
10
# 测试

[root@docker php]# docker run -d --name php-test game/php:v2

[root@docker php]# docker exec -it php-test bash

[root@8b89eebb72ad php]# php-fpm -v
PHP 7.2.24 (fpm-fcgi) (built: Oct 30 2019 06:48:25)
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies

构建 yum版 PHP 基础镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@Docker php]# vim Dockerfile 

# BASE
FROM game/centos:7

# WHO
MAINTAINER leo 365042337@qq.com

# YUM
RUN rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm && \
rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm && \
yum -y install php72w php72w-cli php72w-common php72w-devel \
php72w-embedded php72w-gd php72w-mbstring php72w-pdo php72w-xml php72w-fpm php72w-mysqlnd php72w-opcache && \
echo "==> Clean up..." && \
yum clean all && \
rm -rf /var/cache/yum/*
1
[root@Docker php]# docker build -t game/php:7.2 .
1
2
3
[root@Docker php]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
game/php 7.2 a2219aedc08d 6 seconds ago 514MB
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
# 添加优化配置文件 构建成可用的php镜像
# 注意:
# 1. 由于我的nginx也是yum安装的,所以用户是启动用户也是nginx php应该使用和nginx同一用户,需要useradd nginx
# 2. ; Default Value: any 一定要注释 使用默认值any 允许访问FastCGI进程的IP,any不限制
; listen.allowed_clients = 127.0.0.1
# 3. daemonize = no

[root@Docker php]# vim Dockerfile

# BASE
FROM game/php:7.2

# WHO
MAINTAINER leo 365042337@qq.com

RUN useradd nginx

# CONF
COPY php.ini /etc/
COPY php-fpm.conf /etc/
COPY www.conf /etc/php-fpm.d/

# PROT
EXPOSE 9000

# CMD
CMD ["php-fpm"]

构建 TOMCAT 基础镜像

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
[root@docker runtime]# mkdir -p /opt/game/runtime/tomcat

[root@docker tomcat]# vim Dockerfile

# BASE
FROM game/centos:7

# WHO
MAINTAINER leo 365042337@qq.com

# tomcat
COPY apache-tomcat-8.5.47.tar.gz /opt/

# JDK
RUN yum install java-1.8.0-openjdk wget curl unzip net-tools -y && \
yum clean all && \
rm -rf /var/cache/yum/* && \
cd /opt/ && \
tar zxf apache-tomcat-8.5.47.tar.gz -C /usr/local/ && \
mv /usr/local/apache-tomcat-8.5.47 /usr/local/tomcat && \
rm -rf apache-tomcat-8.5.47.tar.gz /usr/local/tomcat/webapps/* && \
mkdir -p /usr/local/tomcat/webapps/test && \
echo "hello tomcat" > /usr/local/tomcat/webapps/test/status.html && \
sed -i '1a JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom"' /usr/local/tomcat/bin/catalina.sh

ENV PATH $PATH:/usr/local/tomcat/bin

WORKDIR /usr/local/tomcat

EXPOSE 8080

CMD ["/usr/local/tomcat/bin/catalina.sh", "run"]
1
2
3
4
5
6
7
8
9
10
11
# 测试
[root@docker tomcat]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
game/tomcat v1 164bc8a196b9 2 minutes ago 637MB
game/php v2 02d40f53884b About an hour ago 880MB
game/app v1 3cba5a6f4f7b 7 hours ago 437MB

# 启动容器
[root@docker tomcat]# docker run -d --name tom1 -p 88:8080 game/tomcat:v1

http://47.94.221.247:88/test/status.html
1
2
3
4
5
6
7
8
9
# 测试个小项目
[root@docker tomcat]# vim Dockerfile-testapp

FROM game/tomcat:v1
COPY jenkins.war /usr/local/tomcat/webapp/ROOT.war

# 构建

# 容器运行

快速部署LNMP网站平台

构建 自定义网络 lnmp

1
2
# 构建后在lnmp网络的容器可以互相访问
docker network create lnmp

构建 php 容器

1
2
3
4
5
docker run -d --name lnmp_php --net lnmp --mount src=wwwroot,dst=/wwwroot game/app-php:7.2 

[root@Docker runtime]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
49c19215722f game/app-php:7.2 "php-fpm" 2 seconds ago Up 1 second 9000/tcp lnmp_php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
查看创建的 wwwroot 数据卷 
docker inspect wwwroot
[root@Docker runtime]# docker inspect wwwroot
[
{
"CreatedAt": "2019-10-31T09:49:13+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/wwwroot/_data",
"Name": "wwwroot",
"Options": null,
"Scope": "local"
}
]

# 进入数据卷 创建一个 phpinfo文件 用于测试nginx->php
[root@Docker runtime]# cd /var/lib/docker/volumes/wwwroot/_data

[root@docker _data]# vim test.php

<?php
phpinfo();
?>

构建 nginx 容器

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
# yum 安装的nginx 配置文件在 conf.d/里 把里面的default.conf 覆盖下
# lnmp_php 是通过自定义网络dns找到lnmp_php容器

[root@Docker opt]# vim default.conf

server {
server_name location;
listen 80;
root /wwwroot;
index index.php index.html;

location ~ \.php$ {
fastcgi_pass lnmp_php:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /wwwroot$fastcgi_script_name;
include fastcgi_params;
}
}

[root@Docker opt]# docker run -d --name lnmp_nginx --net lnmp -p 88:80 \
> --mount type=bind,src=/opt/default.conf,dst=/etc/nginx/conf.d/default.conf --mount src=wwwroot,dst=/wwwroot game/nginx:1.16

[root@Docker opt]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d9e9535d8e0b game/nginx:1.16 "nginx -g 'daemon of…" 6 seconds ago Up 6 seconds 0.0.0.0:88->80/tcp lnmp_nginx
49c19215722f game/app-php:7.2 "php-fpm" 11 minutes ago Up 11 minutes 9000/tcp lnmp_php

# 创建一个index.html一会用于测试
[root@Docker runtime]# cd /var/lib/docker/volumes/wwwroot/_data/
[root@Docker _data]# echo "hello nginx" >> index.html

测试 nginx 与 php的互通性

1
2
3
4
5
# 访问公网88端口
http://60.205.217.112:88/

# 访问测试php页面
http://60.205.217.112:88/test.php

创建 mysql 容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 使用官方的mysql镜像
# -e 传入变量 mysql容器会帮忙执行 创建数据库 和 设置密码 mysql:5.7 帮我们做了很多设置

docker run -d \
--name lnmp_mysql \
--net lnmp \
--mount src=mysql-vol,dst=/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress mysql:5.7 --character-set-server=utf8

[root@Docker opt]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6b769d4ef1b0 mysql:5.7 "docker-entrypoint.s…" 11 seconds ago Up 10 seconds 3306/tcp, 33060/tcp lnmp_mysql
d9e9535d8e0b game/nginx:1.16 "nginx -g 'daemon of…" 16 minutes ago Up 16 minutes 0.0.0.0:88->80/tcp lnmp_nginx
49c19215722f game/app-php:7.2 "php-fpm" 27 minutes ago Up 27 minutes 9000/tcp lnmp_php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# mysql容器的数据在数据款中 mysql_vol

[root@Docker _data]# cd /var/lib/docker/volumes/mysql-vol/_data/

[root@Docker _data]# ls -l
total 188480
-rw-r----- 1 polkitd input 56 Oct 31 10:16 auto.cnf
-rw------- 1 polkitd input 1676 Oct 31 10:16 ca-key.pem
-rw-r--r-- 1 polkitd input 1112 Oct 31 10:16 ca.pem
-rw-r--r-- 1 polkitd input 1112 Oct 31 10:16 client-cert.pem
-rw------- 1 polkitd input 1680 Oct 31 10:16 client-key.pem
-rw-r----- 1 polkitd input 1346 Oct 31 10:17 ib_buffer_pool
-rw-r----- 1 polkitd input 79691776 Oct 31 10:17 ibdata1
-rw-r----- 1 polkitd input 50331648 Oct 31 10:17 ib_logfile0
-rw-r----- 1 polkitd input 50331648 Oct 31 10:16 ib_logfile1
-rw-r----- 1 polkitd input 12582912 Oct 31 10:17 ibtmp1
drwxr-x--- 2 polkitd input 4096 Oct 31 10:16 mysql
drwxr-x--- 2 polkitd input 4096 Oct 31 10:16 performance_schema
-rw------- 1 polkitd input 1676 Oct 31 10:16 private_key.pem
-rw-r--r-- 1 polkitd input 452 Oct 31 10:16 public_key.pem
-rw-r--r-- 1 polkitd input 1112 Oct 31 10:16 server-cert.pem
-rw------- 1 polkitd input 1676 Oct 31 10:16 server-key.pem
drwxr-x--- 2 polkitd input 12288 Oct 31 10:16 sys
drwxr-x--- 2 polkitd input 4096 Oct 31 10:17 wordpress

部署 wordpress php版

1
2
3
4
5
6
7
8
9
10
11
12
[root@Docker _data]# cd /var/lib/docker/volumes/wwwroot/_data/
[root@Docker _data]# wget https://cn.wordpress.org/wordpress-4.9.4-zh_CN.tar.gz
[root@Docker _data]# tar -zxvf wordpress-4.9.4-zh_CN.tar.gz

# 访问 wordpress
# 跳转了话自己加下端口
http://60.205.217.112:88/wordpress/
http://60.205.217.112:88/wordpress/wp-admin/setup-config.php
# 按照步骤安装

# 安装后的首页
http://60.205.217.112:88/wordpress/
1
2
3
4
[root@Docker wordpress]# cp wp-config-sample.php wp-config.php
[root@Docker wordpress]# >wp-config.php
[root@Docker wordpress]# vim wp-config.php
# 把页面上的内容粘贴进来即可
1
2
3
4
5
# 看日志
[root@Docker wordpress]# docker exec -it lnmp_nginx bash

[root@d9e9535d8e0b nginx]# cd /var/log/nginx/
[root@d9e9535d8e0b nginx]# tail -200 access.log

镜像仓库 Harbor

介绍

  1. Habor是由VMWare公司开源的容器镜像仓库。事实上,Habor是在Docker Registry上进行了相应的企业级扩展,
  2. 从而获得了更加广泛的应用,这些新的企业级特性包括:管理用户界面,基于角色的访问控制 ,
  3. AD/LDAP集成以及审计日志等,足以满足基本企业需求。
1
2
3
4
5
6
7
8
# github
https://github.com/goharbor/harbor

# 安装文档
https://github.com/goharbor/harbor/blob/master/docs/installation_guide.md

# 下载
https://github.com/goharbor/harbor/releases

离线安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 安装 docker compose 单机编排
https://docs.docker.com/compose/install/

# 下载
curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 增加执行权限
[root@Docker opt]# chmod +x /usr/local/bin/docker-compose

# 测试
[root@Docker opt]# docker-compose version
docker-compose version 1.24.1, build 4667896b
docker-py version: 3.7.3
CPython version: 3.6.8
OpenSSL version: OpenSSL 1.1.0j 20 Nov 2018
1
2
3
4
5
6
7
8
9
10
# 解压安装包,修改配置文件
[root@Docker opt]# tar -xf harbor-offline-installer-v1.8.4.tgz
[root@Docker opt]# cd harbor

[root@Docker harbor]# vim harbor.yml
# 修改主机名和管理员密码、数据库密码
hostname: 172.17.70.239 # http
harbor_admin_password: 123456 # 访问密码
database:
password: 123456
1
2
3
4
5
6
# 准备
[root@Docker harbor]# ./prepare
# 安装
[root@Docker harbor]# ./install.sh
# web访问
http://60.205.217.112
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 列出

[root@Docker harbor]# docker-compose ps
Name Command State Ports
---------------------------------------------------------------------------------------------
harbor-core /harbor/start.sh Up (healthy)
harbor-db /entrypoint.sh postgres Up (healthy) 5432/tcp
harbor-jobservice /harbor/start.sh Up
harbor-log /bin/sh -c /usr/local/bin/ ... Up (healthy) 127.0.0.1:1514->10514/tcp
harbor-portal nginx -g daemon off; Up (healthy) 80/tcp
nginx nginx -g daemon off; Up (healthy) 0.0.0.0:80->80/tcp
redis docker-entrypoint.sh redis ... Up 6379/tcp
registry /entrypoint.sh /etc/regist ... Up (healthy) 5000/tcp
registryctl /harbor/start.sh Up (healthy)


[root@Docker harbor]# vim docker-compose.yml
# 看看是如何编排的
# 每个功能都用到一个容器

免https使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@Docker harbor]# vim /etc/docker/daemon.json 
# 写入进项仓库 IP+port

{
"registry-mirrors": ["http://f1361db2.m.daocloud.io"],
"insecure-registries": ["172.17.70.239"]
}

# 重启docker
systemctl daemon-reload
systemctl restart docker.service

# 重启harbor仓库
# cd 到 harbor的安装目录
cd /opt/harbor

# 执行命令
docker-compose stop
docker-compose up -d

[root@Docker nginx]# docker info
Insecure Registries:
172.17.70.239
127.0.0.0/8

创建项目管理员用户

登录镜像仓库

1
2
3
[root@Docker nginx]# docker login 172.17.70.239
Username: leo
Password:

推送镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 在项目中标记镜像:
# docker tag SOURCE_IMAGE[:TAG] 172.17.70.239/library/IMAGE[:TAG]
[root@Docker harbor]# docker tag game/nginx:1.16 172.17.70.239/library/nginx:1.16
[root@Docker harbor]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
172.17.70.239/library/nginx 1.16 15d5ee003c55 4 hours ago 420MB


# 推送

[root@Docker harbor]# docker push 172.17.70.239/library/nginx:1.16
The push refers to repository [172.17.70.239/library/nginx]
180f55013204: Pushed
1439954d81bb: Pushed
f7c2c1cea963: Pushed
63406e473c91: Pushed
877b494a9f30: Pushed
1.16: digest: sha256:4505325827b9b4d8d9164c7617280792b2b1e05a0b185ed3e1d2d7fccad0e2f7 size: 1368

创建一个新项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 私有项目需要认证才能下载
# 添加成员
# 管理员 所有权限 对项目上传 下载
# 开发者 对项目
# 访客 只读

[root@Docker harbor]# docker tag game/app-php:7.2 172.17.70.239/mygame/app-php:v1

[root@Docker harbor]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
172.17.70.239/library/nginx 1.16 15d5ee003c55 4 hours ago 420MB
game/nginx 1.16 15d5ee003c55 4 hours ago 420MB
172.17.70.239/mygame/app-php v1 16d838c461cd 4 hours ago 515MB

[root@Docker harbor]# docker push 172.17.70.239/mygame/app-php:v1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 权限演示:
# 退出仓库
[root@Docker harbor]# docker logout 172.17.70.239
Removing login credentials for 172.17.70.239

# 删除本地的镜像
[root@Docker harbor]# docker rmi 172.17.70.239/library/nginx:1.16
[root@Docker harbor]# docker rmi 172.17.70.239/mygame/app-php:v1

# 下载公有项目镜像
[root@Docker harbor]# docker pull 172.17.70.239/library/nginx:1.16
1.16: Pulling from library/nginx
Digest: sha256:4505325827b9b4d8d9164c7617280792b2b1e05a0b185ed3e1d2d7fccad0e2f7
Status: Downloaded newer image for 172.17.70.239/library/nginx:1.16

# 下载私有项目镜像
[root@Docker harbor]# docker pull 172.17.70.239/mygame/app-php:v1
Error response from daemon: pull access denied for 172.17.70.239/mygame/app-php, repository does not exist or may require 'docker login'

[root@Docker harbor]# docker login 172.17.70.239
Username: leo
Password:

[root@Docker harbor]# docker pull 172.17.70.239/mygame/app-php:v1
1
2
3
4
5
6
7
# 启动一个容器
[root@Docker harbor]# docker run -it -d --name mynginx 172.17.70.239/library/nginx:1.16


[root@Docker harbor]# docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2f96c5d40fe9 172.17.70.239/library/nginx:1.16 "nginx -g 'daemon of…" 6 seconds ago Up 5 seconds 80/tcp mynginx

图形页面管理 Portainer

  1. shipyard 已经不提供更新
  2. Portainer 是一个开源、轻量级Docker管理用户界面,基于Docker API,可管理Docker主机或Swarm模式
1
2
3
Rancher
Docker UI
Portainer # https://www.portainer.io/
1
2
3
4
# 数据卷
docker volume create portainer_data
# 启动Portainer容器
docker run -d -p 8000:8000 -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
1
2
3
4
5
# 访问界面
[root@Docker-2 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9b7a7bbcddd0 portainer/portainer "/portainer" 16 minutes ago Exited (1) 11 minutes ago hardcore_bhaskara
[root@Docker-2 ~]# docker start 9b7a7bbcddd0

Prometheus 监控基础

为什么要监控

  1. 对系统不间断实时监控
  2. 实时反馈系统当前状态
  3. 保证业务持续性运行

监控维度

Prometheus 概述

  1. Prometheus(普罗米修斯)是一个最初在SoundCloud上构建的监控系统。自2012年成为社区开源项目,
  2. 拥有非常活跃的开发人员和用户社区。为强调开源及独立维护,Prometheus于2016年加入云原生云计算
  3. 基金会(CNCF),成为继Kubernetes之后的第二个托管项目。
1
2
https://prometheus.io
https://github.com/prometheus

Prometheus 特点

  1. 多维数据模型:由度量名称和键值对标识的时间序列数据
  2. PromSQL:一种灵活的查询语言,可以利用多维数据完成复杂的查询
  3. 不依赖分布式存储,单个服务器节点可直接工作
  4. 基于HTTP的pull方1. 采集时间序列数据
  5. 推送时间序列数据通过PushGateway组件支持
  6. 通过服务发现或静态配置发现目标
  7. 多种图形模式及仪表盘支持(grafana)

Prometheus 架构

  1. Prometheus Server:收集指标和存储时间序列数据,并提供查询接口
  2. ClientLibrary:客户端库
  3. Push Gateway:短期存储指标数据。主要用于临时性的任务
  4. Exporters:采集已有的第三方服务监控指标并暴露metrics
  5. Alertmanager:告警
  6. Web UI:简单的Web控制台

实例 和 作业

  1. 实例:可以抓取的目标称为实例(Instances) -> 客户端
  2. 作业:具有相同目标的实例集合称为作业(Job) -> 实例分类

通过容器 安装部署

1
2
Docker部署:   https://prometheus.io/docs/prometheus/latest/installation/
访问Web: http://localhost:9090
  1. prometheus的监控大多数都是通过这个yml文件配置
1
2
docker run -p 9090:9090 -v /tmp/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus
  1. 下载镜像
1
2
3
4
[root@Docker-2 ~]# docker pull prom/prometheus
[root@Docker-2 opt]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
prom/prometheus latest 2c8e464e47f4 13 days ago 129MB
  1. 下载二进制包
1
2
# 用里面的 prometheus.yml文件
wget https://github.com/prometheus/prometheus/releases/download/v2.13.1/prometheus-2.13.1.linux-amd64.tar.gz
  1. 启动容器
1
2
3
# 数据在容器中
docker run -p 9090:9090 -v /tmp/prometheus.yml:/etc/prometheus/prometheus.yml \
prom/prometheus
1
2
3
# 持久化数据到宿主机上
docker run -p 9090:9090 -v /prometheus-data \
prom/prometheus --config.file=/prometheus-data/prometheus.yml
1
2
3
4
5
6
7
8
9
# 后台启动
[root@Docker-2 opt]# docker run -d -p 9090:9090 -v /opt/prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus

[root@Docker-2 opt]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
294ee83dfb5c prom/prometheus "/bin/prometheus --c…" 5 seconds ago Up 4 seconds 0.0.0.0:9090->9090/tcp jolly_wiles

# 访问
http://39.106.100.108:9090/graph

cAdvisor(Container Advisor)

  1. 用于收集正在运行的容器资源使用和性能信息。
  2. cAdvisor 不具备存储 只具备采集 数据存储可持久访问的容器中
1
https://github.com/google/cadvisor
1
2
3
4
5
6
7
8
9
10
sudo docker run \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:ro \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--volume=/dev/disk/:/dev/disk:ro \
--publish=8080:8080 \
--detach=true \
--name=cadvisor \
google/cadvisor:latest
1
2
3
4
[root@Docker-2 opt]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ff52b0f280c8 google/cadvisor:latest "/usr/bin/cadvisor -…" 4 seconds ago Up 2 seconds 0.0.0.0:8080->8080/tcp cadvisor
294ee83dfb5c prom/prometheus "/bin/prometheus --c…" 12 minutes ago Up 12 minutes 0.0.0.0:9090->9090/tcp jolly_wiles
1
2
3
4
5
# web
http://39.106.100.108:8080/containers/

# 数据接口
http://39.106.100.108:8080/metrics
  1. 数据接口采集的数据,交给prometheus解析,遵循他的数据格式
  2. 拿这个接口 就可以告诉prometheus,我当前docker主机上的所有容器指标,他已经帮忙采集到了

Grafana

  1. 是一个开源的度量分析和可视化系统。
1
2
https://grafana.com/grafana/download
https://grafana.com/dashboards/193
1
2
# 容器安装
docker run -d --name=grafana -p 3000:3000 grafana/grafana
1
2
3
# web 
# 默认用户名和密码 admin/admin 进去自己修改
http://39.106.100.108:3000/login
  1. 添加 prometheus 数据源
1
# 1. 如果访问不了 会提示网络连接有问题

修改 prometheus 数据文件

1
2
3
4
5
6
7
8
# prometheus 去找 cAdvisor 拿监控docker的数据
# static_configs: 主动的配置被监控端
# ,分割 可以写多个被监控端

[root@Docker-2 opt]# vim /opt/prometheus.yml
- job_name: 'docker'
static_configs:
- targets: ['172.17.70.240:8080']
1
2
3
4
5
6
7
8
9
10
11
[root@Docker-2 opt]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f239732834d3 grafana/grafana "/run.sh" 18 minutes ago Up 18 minutes 0.0.0.0:3000->3000/tcp grafana
ff52b0f280c8 google/cadvisor:latest "/usr/bin/cadvisor -…" 29 minutes ago Up 29 minutes 0.0.0.0:8080->8080/tcp cadvisor
294ee83dfb5c prom/prometheus "/bin/prometheus --c…" 41 minutes ago Up 41 minutes 0.0.0.0:9090->9090/tcp jolly_wiles

# 重启下 prom
[root@Docker-2 opt]# docker restart 294ee83dfb5c

# 去看看有没有 cadvisor_version_info 和 container 的数据
http://39.106.100.108:9090

添加 Grafana 仪表盘

1
2
3
4
5
6
7
1. 导入一个别人写好的 
2. 193是别人写好的ID
3. 可以去官网看 那些人家写好的 下载使用数高的 兼容性好
https://grafana.com/grafana/dashboards

4. 可以选择监控指标的edit 拿到 rate(container_cpu_user_seconds_total{image!=""}[5m]) * 100
5. 去prometheus里面执行查看数据,这是promsql
1
2
3
# 再启动一个容器 
[root@Docker-2 opt]# docker run -it -d --name=mynginx nginx:1.16
# 查看监控