docker

docker

docker架构

c/s架构,分为docker client,docker daemon,docker register。彼此之间基于http或https进行通信。docker采用分层构建,依赖于特定的文件系统。
docker daemon包括containers和images
当client执行诸如docker build,docker pull,docker run等指令时,docker daemon首先会查看images中是否有该镜像,如果有就直接创建或启动,如果没有就会去register上下来该镜像然后创建或启动指定的容器。
container的状态有:running,stopped,paused,created,deleted;使用docker container ps或docker ps命令可以查看容器的状态;创建容器:docker create或docker run命令。
restful概念:一种分布式程序开发调用的API,一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

docker container

安装docker

配置yum源

1
[root@mariadb yum.repos.d]#wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装docker

1
[root@mariadb yum.repos.d]#yum -y install docker-ce

由于docker的官方镜像时国外服务器,有可能比较慢,此时可以借助国内的镜像加速器进行加速,例如使用阿里云的docker镜像加速器。(需要注册阿里云账号)
登录阿里云的主站点dev.aliyun.com,登录自己的账号,在产品与服务上找到容器镜像服务,然后点到镜像加速器就可以看到自己的加速器地址。按照操作文档的提示修改daemon配置文件后即可使用。

docker常用命令

images:
  • pull:下载镜像
  • push:上传镜像
  • rm:删除镜像
  • tag:给镜像打标签
container

创建容器

  • docker create
  • docker run

    -t,–tty 指定一个终端
    -i,–interactive 交互式
    –name string 指定容器的名字
    –rm 容器停止时删除该容器
    -d 后台运行,并打印容器的ID

删除容器

  • docker container rm name或docker rm name,删除容器时,容器内部的数据都会丢失,慎用。

在容器内执行命令
docker container exec [选项] 容器 命令

  • docker container exec -it redis /bin/sh 在redis内部以交互式的方式打开一个终端,运行/bin/sh程序
  • docker container exec redis netstat -ntl 在redis容器内部执行netstat -ntl命令,执行完命令后吧结果输出到当前终端,并退出容器。

查看容器的详细信息

  • docker container inspect container… 查看一个或多个容器的详细信息

查看容器资源占用情况

  • docker container stats container-name

查看容器内运行的所有进程的相关信息

  • docker container top container-name

进入某container
docker container attach cotainer-name

logs
  • docker container logs [options] container

使用docker管理容器

使用docker search 关键词可以搜索想要使用的仓库,仓库分为顶层仓库(官方维护)和属于某组织或个人的仓库,也可以在docker官方站点搜索仓库https://hub.docker.com/

下载redis镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@mariadb docker]#docker pull redis:4-alpine
4-alpine: Pulling from library/redis
cd784148e348: Pull complete
48d4c7155ddc: Pull complete
6d908603dbe8: Pull complete
fd4371c1c78e: Pull complete
e6818dc808c2: Pull complete
f1884d594f6f: Pull complete
Digest: sha256:775bbf766a5b711acce88e4142faf56cd587d63ddc4d57b49f7872f71d56fab6
Status: Downloaded newer image for redis:4-alpine
[root@mariadb docker]#docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis 4-alpine 37abb58bfd68 12 days ago 30MB

基于下载的image启动一个容器

1
$ docker run --name redis -d redis:4-alpine

docker images

docker镜像含有启动容器所需要的文件系统及其内容,因此,其用于创建并启动docker容器。
image采用分层构建机制,最底层为bootfs,其次为rootfs

  • bootfs:用于系统引导的文件系统,包括BootLoader和kernel,容器启动完成后会被卸载以节约内存资源。
  • rootfs位于bootfs之上,表现为dicker容器的根文件系统

    传统模式中,系统启动时,内核挂载rootfs时会首先将其挂载为”只读”模式,完整性自检完成后将其重新挂在为读写模式。
    docker中,rootfs由内核挂载为”只读”模式,而后通过”联合挂载”技术额外挂载一个”可写”层。

Aufs
advanced multi-layered unification filesystem : 高级多层同一文件系统
用于为linux文件系统实现”联合挂载”
aufs时之前的unionfs的重新实现
dicker最初使用aufs作为容器文件系统层,它目前仍作为存储后端之一来支持
aufs的竞争产品时overlayfs,后者自从3.18版本开始被合并到linux内核
docker的分层镜像,除了aufs,还有btrfs、devicemapper和vfs等

  • 在Ubuntu系统下,docker默认使用Ubuntu的aufs,而在早期centos7上,用的是devicemapper。

docker registry

启动容器时,docker daemon会试图从本地获取相关的镜像,本地镜像不存在时,其将从registry重下载该镜像并保存到本地。

自制镜像
创建自己的container后,在container上做了修改之后可以基于该container创建image,在创建image时不要停止container

  • docker container commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

例如:docker container commit redis myimg/redis:v0.1

自制镜像后,可以将自制的镜像推到个人docker账号的仓库中
docker image push name[:tag]

给已有的镜像打标签

  • docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

docker网络的实现

封闭式网络

  • docker run -it –rm –network none –name tinyweb1 busybox

上述命令以busybox为image创建一个封闭式网络的container tinyweb1,此container只有lo接口。

bridge网络
bridge网络可以实现不同的容器共用一个网络名称空间,而除了网络空间之外,其余的空间都是相互隔离的,这就能实现将amp分别放在不同的容器中运行,而让各自只监听127.0.0.1网址,依然能彼此通信。
示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@mariadb ~]#docker run -it --rm --network bridge --name tinyweb1 busybox
/ # mkdir data
/ # cd /data/
/data # vi index.html
/data # httpd -h /data/
/data # wget -O - -q 127.0.0.1
This is a test
/data # ps aux
PID USER TIME COMMAND
1 root 0:00 sh
16 root 0:00 httpd -h /data/
20 root 0:00 ps aux
[root@mariadb ~]#docker run --name joinedc1 -it --rm --network container:tinyweb1 busybox
/ # wget -O - -q 127.0.0.1
This is a test
/ # ps aux
PID USER TIME COMMAND
1 root 0:00 sh
11 root 0:00 ps aux

说明,上述分别创建了两个容器,在容器tinyweb1上运行httpd服务,容器joinedc1指定network为与tinyweb1共享网络,此时在容器joinedc1上访问127.0.0.1,能够访问容器tinyweb1上的httpd服务,说明两个容器在使用同一网络空间,而使用ps aux查看两个容器上运行的服务不同,说明两个容器仅共享网络空间,这就能实现上述所说的在不同的容器中运行amp,mp仅监听127.0.0.1地址,依然能实现彼此之间的通信的功能。

host网络

1
[root@mariadb ~]#docker run --name tinyweb1 -it --rm --network host busybox

使用该命令创建的container和宿主机共用一个网络名称空间。
此外,docker创建container时还支持直接注入hosts解析和dns服务器,

  • –add-host list Add a custom host-to-IP mapping (host:ip)
  • –dns list Set custom DNS servers
  • –dns-search list Set custom DNS search domains

docker在创建container时,还可以指定将container的某个端口映射至主机的某个端口上,这就要使用-p选项,-p的使用格式有四种:
-p, –publish list Publish a container’s port(s) to the host

  • -p < containerPort> 将指定的容器端口映射至主机所有地址的一个动态端口
  • -p < hostPort>:< containerPort> 将容器端口映射至指定的主机端口
  • -p < ip>::< containerPort> 将指定的容器端口映射至主机指定ip的动态端口
  • -p < ip>:< hostPort>:< containerPort> 将指定的容器端口映射至主机指定ip的端口

其中”动态端口”指的是随机端口,具体的映射结果可使用dicker port命令查看

创建网络名称空间

docker可以手动创建网络名称空间,然后将container加入指定的网络名称空间

[root@mariadb ~]#docker network create --help
Usage:    docker network create [OPTIONS] NETWORK
Create a network
Options:
      --attachable           Enable manual container attachment
      --aux-address map      Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[])
      --config-from string   The network from which copying the configuration
      --config-only          Create a configuration only network
  -d, --driver string        Driver to manage the Network (default "bridge")
      --gateway strings      IPv4 or IPv6 Gateway for the master subnet
      --ingress              Create swarm routing-mesh network
      --internal             Restrict external access to the network
      --ip-range strings     Allocate container ip from a sub-range
      --ipam-driver string   IP Address Management Driver (default "default")
      --ipam-opt map         Set IPAM driver specific options (default map[])
      --ipv6                 Enable IPv6 networking
      --label list           Set metadata on a network
  -o, --opt map              Set driver specific options (default map[])
      --scope string         Control the network's scope
      --subnet strings       Subnet in CIDR format that represents a network segment
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@mariadb ~]#docker network create --subnet 10.10.0.0/24 mybr0
5b1543c1500084026c08eec4a34d84f85452c975a013aa6c55c18addc9550e40
[root@mariadb ~]#docker network ls
NETWORK ID NAME DRIVER SCOPE
8787fa490049 bridge bridge local
afb6f1041481 host host local
5b1543c15000 mybr0 bridge local
30376c034536 none null local
[root@mariadb ~]#docker run --name web -d --network mybr0 -p 80:80 nginx:1.15-alpine
[root@mariadb ~]#docker container exec -it web /bin/sh
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0A:0A:00:02
inet addr:10.10.0.2 Bcast:10.10.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:16 errors:0 dropped:0 overruns:0 frame:0
TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1569 (1.5 KiB) TX bytes:504 (504.0 B)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

此外,还可以将容器connect至指定的network上,此时相当于container上多了一块网卡,还可已使用disconnect命令将container从指定的接口上拔下来。

[root@mariadb ~]#docker network connect --help
Usage:    docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
      --alias strings           Add network-scoped alias
                                for the container
      --ip string               IPv4 address (e.g.,
                                172.30.100.104)
      --ip6 string              IPv6 address (e.g.,
                                2001:db8::33)
      --link list               Add link to another container
      --link-local-ip strings   Add a link-local address
                                for the container

示例

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
[root@mariadb ~]#docker container exec -it web /bin/sh
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0A:0A:00:02
inet addr:10.10.0.2 Bcast:10.10.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:0 (0.0 B)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
[root@mariadb ~]#docker network connect bridge web
/ # ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:0A:0A:00:02
inet addr:10.10.0.2 Bcast:10.10.0.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:8 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:648 (648.0 B) TX bytes:0 (0.0 B)

eth1 Link encap:Ethernet HWaddr 02:42:AC:11:00:02
inet addr:172.17.0.2 Bcast:172.17.255.255 Mask:255.255.0.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:6 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:508 (508.0 B) TX bytes:0 (0.0 B)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

docker自带的bridge桥的ip时172.17.0.1,这个默认的ip也是可以修改的,在/etc/docker/daemon.json文件中定义

1
2
3
4
5
[root@mariadb ~]#cat /etc/docker/daemon.json 
{
"registry-mirrors": ["https://gwtcf0z6.mirror.aliyuncs.com"],
"bip": "172.31.0.1/16"
}

重启docker后生效。

docker存储卷

docker镜像是由多个只读层叠加而成,启动容器时,docker会加载只读镜像层并在镜像栈顶部添加一个读写层
如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏,此即”写时复制(COW)”机制。
关闭并重启容器,其数据不受影响;但删除docker容器,则其更改将会全部丢失,此时存在以下问题:

  • 存储于联合文件系统中,不易于宿主机访问
  • 容器间数据共享不便
  • 删除容器其数据会丢失

此时,就需要引入卷(volunme)来解决这些问题,卷是容器上的一个或多个”目录”,此类目录可绕过联合文件系统,与宿主机上的某目录”绑定(关联)”,此volunme就类似一个传送门,可以让container和宿主机之间传输文件。

1
[root@mariadb ~]#docker run --name web -v /mydata -it --rm busybox

此时创建的container会自动生成/mydata目录,此目录映射到宿主机上的一个目录,即实现了容器和宿主机之间的文件共享,使用rm命令删除container时,宿主机上的卷是不会删除的,这就实现了在container生命周期结束时,container中的数据依然保留。但是此时的宿主机上的目录是自动生成的,可以使用如下方法指定宿主机的目录。

1
[root@mariadb ~]#docker run --name web -v /data/volumes/v1:/mydata -it --rm busybox

使用上述命令创建的容器,容器中的/mydata目录和宿主机上的/data/volumes目录之间共享目录,如果此时让另一个容器仍然映射到/data/volumes目录,就可以实现容器之间共享目录的功能,相当于将宿主机上的目录挂在到容器上。

–volume-from container选项可以实现容器之间目录的共享

1
[root@mariadb ~]#docker run --name web --volumes-from web -it --rm busybox

实验:使用docker以容器化方式运行lamp+WordPress架构

创建一个mysql容器,其数据目录使用卷的方式放在宿主机上,并在mysql上创建WordPress专用的数据和账号

1
2
3
4
5
6
7
8
9
10
[root@docker ~]#docker image pull mysql:latest
[root@docker ~]#docker run --name db -d -v /data/volumes/mysql/:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:latest
[root@docker ~]#docker container exec -it db /bin/sh
# mysql -p123456
mysql> create user wpuser@'172.17.0.%';
Query OK, 0 rows affected (0.05 sec)
mysql> create database wpdb;
Query OK, 1 row affected (0.02 sec)
mysql> grant all on wpdb.* to wpuser@'172.17.0.%';
Query OK, 0 rows affected (0.01 sec)

这里有一点需要注意,就是本次实验室用的是mysql的最新版本即8.x版本的,而8.x版本默认的认证方式是caching_sha2_password而非mysql_native_password。这会导致WordPress连接数据库失败,解决办法有以下两种。
第一种:修改my.cnf文件更改认证方式并重启mysql

1
2
3
[root@docker ~]#vi my.cnf
[mysqld]
default_authentication_plugin=mysql_native_password

第二种:兼容新老版本的认证方式

1
2
3
4
5
6
7
8
9
10
mysql> alter user wpuser@'172.17.0.%' identified by 'centos' password expire never; #修改加密规则
Query OK, 0 rows affected (0.13 sec)
mysql> alter user wpuser@'172.17.0.%' identified with mysql_native_password by 'centos'; #更新用户密码
Query OK, 0 rows affected (0.05 sec)
mysql> flush privileges; #刷新权限
Query OK, 0 rows affected (0.00 sec)
--创建新用户时使用以下语句,mysql8.x版本不支持授权的同时创建用户,只能分开来写。
mysql>create user root@'%' identified WITH mysql_native_password BY 'root';
mysql>grant all privileges on *.* to root@'%' with grant option;
mysql>flush privileges;

创建一个centos容器,运行httpd+php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@docker ~]#docker image pull centos:7 #从registry拉取centos7的image
[root@docker ~]#docker run -it -d --name apache -v /data/volumes/wp/:var/www/html/ -h apache centos:7 #基于拉取的image创建一个用于跑httpd+php的容器
# 之后下载WordPress解压后放入宿主机的/data/volumes/wp/目录下并修改WordPress的配置文件
[root@docker worpress]#mv wp-config-sample.php wp-config.php
[root@docker worpress]#vi wp-config.php
/** WordPress数据库的名称 */
define('DB_NAME', 'wpdb');

/** MySQL数据库用户名 */
define('DB_USER', 'wpuser');

/** MySQL数据库密码 */
define('DB_PASSWORD', 'centos');

/** MySQL主机 */
define('DB_HOST', '172.17.0.2');
[root@docker ~]#docker container exec -it apache /bin/bash #进入container
[root@apache html]#yum -y install httpd php php-mysql #安装相关包
[root@apache html]#httpd #启动httpd

使用lvs做前端负载均衡器,就可以管理该WordPress站点

1
2
3
4
[root@docker wp]#yum -y install ipvsadm
[root@docker wp]#modprobe ip_vs
[root@docker wp]#ipvsadm -A -t 192.168.34.102:80(vip) -s wrr
[root@docker wp]#ipvsadm -a -t 192.168.34.102:80(vip) -r 172.17.0.3(rip) -m

此时,在浏览器输入vip,就可以登入管理自己的WordPress站点,实验完毕。