Docker 基本概念

1. 整理namespace和cgroup

1.1 Linux NameSpace

命名空间将全局系统资源包装在一个抽象中,使命名空间内的进程看起来它们拥有自己独立的全局资源实例。命名空间内对全局资源的改变对其他进程可见,命名空间的成员对其他进程不可见。

目前linux 内核已实现的7种命名空间如下:

Namespace   Flag(API操作类型别名)    Isolates(隔离内容)
Cgroup      CLONE_NEWCGROUP   Cgroup root directory (since Linux 4.6)
IPC         CLONE_NEWIPC      System V IPC, POSIX message queues (since Linux 2.6.19)
Network     CLONE_NEWNET      Network devices, stacks, ports, etc. (since Linux 2.6.24)
Mount       CLONE_NEWNS       Mount points (since Linux 2.4.19)
PID         CLONE_NEWPID      Process IDs (since Linux 2.6.24)
User        CLONE_NEWUSER     User and group IDs (started in Linux 2.6.23 )
UTS         CLONE_NEWUTS      Hostname and NIS domain name (since Linux 2.6.19)

查看进程的namespace

root@ubuntu:~# pidof bash
2011 1270
root@ubuntu:~# ls -l /proc/2011/ns
total 0
lrwxrwxrwx 1 root root 0 Dec 29 21:01 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Dec 29 21:01 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Dec 29 21:01 mnt -> 'mnt:[4026531840]'
lrwxrwxrwx 1 root root 0 Dec 29 21:01 net -> 'net:[4026531993]'
lrwxrwxrwx 1 root root 0 Dec 29 21:01 pid -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Dec 29 21:01 pid_for_children -> 'pid:[4026531836]'
lrwxrwxrwx 1 root root 0 Dec 29 21:01 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Dec 29 21:01 uts -> 'uts:[4026531838]'

 每一个进程在/proc/[pid]/ns都可以看到其所属的namespace信息,这些链接文件指向所属的namespace及inode ID,我们可以通过readlink 来查看两个进程的是否属于同一个命名空间,inode相同则他们所属相同命名空间

root@ubuntu:~# readlink /proc/2011/ns/uts
uts:[4026531838]

下面我们通过shell 命令 unshare 来看看命名空间7大隔离实现

1.PID Namespace

PID Namespace 的作用是用来隔离进程,利用 PID Namespace 可以实现每个容器的主进程为 1 号进程,而容器内的进程在主机上却拥有不同的PID。

root@ubuntu:~# unshare --fork --pid --mount-proc /bin/bash
root@ubuntu:~# ps -aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.1  20196  3700 pts/1    S    21:29   0:00 /bin/bash
root         11  0.0  0.1  38388  3488 pts/1    R+   21:29   0:00 ps -aux
root@ubuntu:~# ls -l /proc/1/ns
total 0
lrwxrwxrwx 1 root root 0 Dec 29 21:29 cgroup -> 'cgroup:[4026531835]'
lrwxrwxrwx 1 root root 0 Dec 29 21:29 ipc -> 'ipc:[4026531839]'
lrwxrwxrwx 1 root root 0 Dec 29 21:29 mnt -> 'mnt:[4026532598]'
lrwxrwxrwx 1 root root 0 Dec 29 21:29 net -> 'net:[4026531993]'
lrwxrwxrwx 1 root root 0 Dec 29 21:29 pid -> 'pid:[4026532599]'
lrwxrwxrwx 1 root root 0 Dec 29 21:29 pid_for_children -> 'pid:[4026532599]'
lrwxrwxrwx 1 root root 0 Dec 29 21:29 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 Dec 29 21:29 uts -> 'uts:[4026531838]'

在新的PID Namespace中我们只能看到自身命名空间的进程。并且当前的bash处于起来的mnt、pid命名空间。

2.Mount Namespace

它可以用来隔离不同的进程或进程组看到的挂载点。在容器内的挂载操作不会影响主机的挂载目录。

我们创建一个命名空间

root@ubuntu:~# unshare --mount --fork /bin/bash

挂载一个目录

root@ubuntu:~# mkdir /tmp/mnt
root@ubuntu:~# mount -t tmpfs -o size=1m tmpfs /tmp/mnt
root@ubuntu:~# df -h | grep mnt
tmpfs           1.0M     0  1.0M   0% /tmp/mnt

在命名空间内的挂载并不影响我们的主机目录,我们在主机上查看不到挂载信息

root@ubuntu:~# df -h | grep mnt

 3.User Namespace

User Namespace用来隔离用户和用户组。我们来创建一个用户命名空间并修改提示符

root@ubuntu:~# PS1='\u@container#' unshare --user -r /bin/bash
root@container:~#

再查看ns,用户链接是不同的,已处于不同空间。

root@ubuntu:~# readlink /proc/1835/ns/user
user:[4026532192]
root@ubuntu:~# readlink /proc/$$/ns/user
user:[4026531837]

用户命名空间的最大优势是无需 root 权限即可运行容器,避免应用使用root对主机的影响。

4.UTS Namespace

UTS Namespace 用于隔离主机名的,它允许每个 UTS Namespace 拥有一个独立的主机名。

root@ubuntu:~# unshare --fork --uts /bin/bash

在命名空间中修改主机名,在主机中不受影响

root@ubuntu:~# hostname -b container
root@ubuntu:~# hostname
container

主机中

root@ubuntu:~# hostname
ubuntu

5.IPC Namespace

IPC 命名空间隔离某些 IPC 资源,即 System V IPC 对象和POSIX 消息队列。容器通过IPC Namespace、PID Namespace实现同一 IPC Namespace 内的进程彼此可以通信,不同 IPC Namespace 的进程却不能通信。

我们使用linux中ipc相关命令来测试

ipcs -q 命令:用来查看系统间通信队列列表。

ipcmk -Q 命令:用来创建系统间通信队列。

我们先创建一个IPC Namespace

root@ubuntu:~# unshare --fork --ipc /bin/bash

创建一个通信队列后查询一下

root@ubuntu:~# ipcmk -Q
Message queue id: 0
​
root@ubuntu:~# ipcs -q
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x1de4aef6 0          root       644        0            0

在主机上查询,可以看到通信已经被隔离了

root@ubuntu:~# ipcs -q
​
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

6.Net Namespace

Net Namespace 可用于隔离网络设备、IP 地址和端口等信息。Net Namespace 可以让每个进程拥有自己独立的 IP 地址,端口和网卡信息。

我们继续创建一个Net Namespace

root@ubuntu:~# unshare --net --fork /bin/bash

查看网络和端口信息

root@ubuntu:~# ip addr
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    
root@ubuntu:~# netstat -ntlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name   

上面看到了一个回环接口lo,状态处于DOWN,我们将它启动,这样我们的Namespace有了自己的网络地址。

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever

主机中

root@ubuntu:~# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 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
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 52:54:96:e1:36:04 brd ff:ff:ff:ff:ff:ff
    inet 10.150.25.9/24 brd 10.150.25.255 scope global noprefixroute dynamic eth0
       valid_lft 80720sec preferred_lft 80720sec
    inet6 fe80::5054:96ff:fee1:3604/64 scope link 
       valid_lft forever preferred_lft forever
       
root@ubuntu:~# netstat -ntlp
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:22              0.0.0.0:*               LISTEN      757/sshd            
tcp        0      0 127.0.0.1:25            0.0.0.0:*               LISTEN      1112/master 

7.Cgroup Namespace

Cgroup是对进程的cgroup视图虚拟化。 每个 cgroup 命名空间都有自己的一组 cgroup 根目录。Linux 4.6开始支持。

cgroup 命名空间提供的虚拟化有多种用途:

  • 防止信息泄漏。否则容器外的cgroup 目录路径对容器中的进程可见。
  • 简化了容器迁移等任务。
  • 允许更好地限制容器化进程。可以挂载容器的 cgroup 文件系统,这样容器无需访问主机 cgroup 目录。

8.Time Namespace

虚拟化两个系统时钟,用于隔离时间。 linux 5.7内核开始支持

1.2  Linux Cgroup

从上面我们了解到当我们要运行一个容器时,docker等应用会为该容器创建一组 namespace,对操作系统而言可以理解为一组进程。这下我们完成了“权利”的集中,但是“权利越大,责任也大”,我们不能放任这组“大权“不管,所以又有了Cgroup(Linux Control Group)这个东西。

Cgroup最主要的作用,就是限制一个进程组能够使用的资源上限,包括 CPU、内存、磁盘、网络带宽等等。

cgroups 框架提供了以下内容:

  • 资源限制: 可以为我们的进程组配置内存限制或cpu个数限制又或者仅限于某个特定外围设备。
  • 优先级: 一个或多个组可以配置为优先占用 CPU 或磁盘 I/O 吞吐量。
  • 资源记录: 监视和测量组的资源使用情况。
  • 控制: 可以冻结或停止和重新启动进程组。

一个 cgroup 可以由一个或多个进程组成,这些进程都绑定到同一组限制。这些组也可以是分层的,即子组可以继承父组管理的限制。

Linux 内核为 cgroup 技术提供了对一系列控制器或子系统的访问。控制器负责将特定类型的系统资源分配给一组一个或多个进程。例如,memory控制器限制内存使用,而cpuacct控制器监控 CPU 使用。

我们通过Mount查看系统中cgroup的子系统

root@ubuntu:~# mount -t cgroup 
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)

可以看到cgroup已通过文件系统方式挂载到/sys/fs/cgroup/

root@ubuntu:~# ls -l /sys/fs/cgroup/
total 0
drwxr-xr-x 2 root root  0 Jul 20 12:23 blkio
lrwxrwxrwx 1 root root 11 Jul 20 12:23 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 Jul 20 12:23 cpuacct -> cpu,cpuacct
drwxr-xr-x 2 root root  0 Jul 20 12:23 cpu,cpuacct
drwxr-xr-x 2 root root  0 Jul 20 12:23 cpuset
drwxr-xr-x 4 root root  0 Jul 20 12:23 devices
drwxr-xr-x 2 root root  0 Jul 20 12:23 freezer
drwxr-xr-x 2 root root  0 Jul 20 12:23 hugetlb
drwxr-xr-x 2 root root  0 Jul 20 12:23 memory
lrwxrwxrwx 1 root root 16 Jul 20 12:23 net_cls -> net_cls,net_prio
drwxr-xr-x 2 root root  0 Jul 20 12:23 net_cls,net_prio
lrwxrwxrwx 1 root root 16 Jul 20 12:23 net_prio -> net_cls,net_prio
drwxr-xr-x 2 root root  0 Jul 20 12:23 perf_event
drwxr-xr-x 2 root root  0 Jul 20 12:23 pids
drwxr-xr-x 4 root root  0 Jul 20 12:23 systemd

接下来我们通过一个实例看看cgroup是如何限制CPU使用的

我们启动一个循环脚本,这个循环脚本将占用近100%的CPU,我们通过cgroup限制到50%

$ cat loop.sh
#!/bash/sh
​
while [ 1 ]; do
:
done

将我们的脚本放到后台,获取它的PID为21497

nohup bash loop.sh &   

我们需要创建一个cgroup控制组loop

root@ubuntu:~# mkdir /sys/fs/cgroup/cpu/loop

loop组是CPU的子组,上面提到子组可以继承父组管理的限制所以loop将继承对系统整个cpu的访问权限

root@ubuntu:~# ls -l /sys/fs/cgroup/cpu/loop
total 0
-rw-r--r-- 1 root root 0 Jul 20 17:15 cgroup.clone_children
--w--w--w- 1 root root 0 Jul 20 17:15 cgroup.event_control
-rw-r--r-- 1 root root 0 Jul 20 17:15 cgroup.procs
-r--r--r-- 1 root root 0 Jul 20 17:15 cpuacct.stat
-rw-r--r-- 1 root root 0 Jul 20 17:15 cpuacct.usage
-r--r--r-- 1 root root 0 Jul 20 17:15 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Jul 20 17:15 cpu.shares
-r--r--r-- 1 root root 0 Jul 20 17:15 cpu.stat
-rw-r--r-- 1 root root 0 Jul 20 17:15 notify_on_release
-rw-r--r-- 1 root root 0 Jul 20 17:15 tasks

查看继承后的loop组cpu限制,计算周期为100000us,采样时间无限制(-1)

root@ubuntu:~# cat /sys/fs/cgroup/cpu/loop/cpu.cfs_period_us
100000
root@ubuntu:~# cat /sys/fs/cgroup/cpu/loop/cpu.cfs_quota_us
-1

为了限制进程的的cpu使用率为50%,我们需要更新cpu.cfs_quota_us的值为50000

echo 50000 >/sys/fs/cgroup/cpu/loop/cpu.cfs_quota_us

将脚本PID更新到loop控制组下的tasks

root@ubuntu:~# echo 21497 >/sys/fs/cgroup/cpu/loop/tasks

此时我们的脚本CPU使用率已被限制到50%

  PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND                                        
21497 root      20   0  113284   1176    996 R 50.0  0.1  12:17.48 bash 

在docker启动容器时做的cpu限制参数--cpu-period--cpu-quota实际上就是调整对应容器控制组的cpu配额。

2. docker的安装

# 更新
sudo apt-get update # 安装依赖包 sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common # 添加 Docker 的官方 GPG 密钥 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # 验证您现在是否拥有带有指纹的密钥 sudo apt-key fingerprint 0EBFCD88 # 设置稳定版仓库 sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
# 再次更新
$ sudo apt-get update
# 安装最新的Docker-ce 
sudo apt-get install docker-ce
# 启动
sudo systemctl enable docker
sudo systemctl start docker

测试

root@ubuntu:~# sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete 
Digest: sha256:2498fce14358aa50ead0cc6c19990fc6ff866ce72aeb5546e1d59caac3d0d60f
Status: Downloaded newer image for hello-world:latest

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

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

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

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

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

3. docker的常用命令使用

3.1 Docker容器信息

##查看docker容器版本
docker version
##查看docker容器信息
docker info
##查看docker容器帮助
docker --help

3.2 镜像操作

提示:对于镜像的操作可使用镜像名、镜像长ID和短ID。

3.2.1 镜像查看
##列出本地images
docker images
##含中间映像层
docker images -a
##只显示镜像ID
docker images -q
##含中间映像层
docker images -qa
##显示镜像摘要信息(DIGEST列)
docker images --digests
##显示镜像完整信息
docker images --no-trunc

##显示指定镜像的历史创建;参数:-H 镜像大小和日期,默认为true;--no-trunc 显示完整的提交记录;-q 仅列出提交记录ID
docker history -H redis
3.2.2 镜像搜索
##搜索仓库MySQL镜像
docker search mysql
## --filter=stars=600:只显示 starts>=600 的镜像
docker search --filter=stars=600 mysql
## --no-trunc 显示镜像完整 DESCRIPTION 描述
docker search --no-trunc mysql
## --automated :只列出 AUTOMATED=OK 的镜像
docker search  --automated mysql
3.2.3 镜像下载
##下载Redis官方最新镜像,相当于:docker pull redis:latest
docker pull redis
##下载仓库所有Redis镜像
docker pull -a redis
##下载私人仓库镜像
docker pull bitnami/redis
3.2.4 镜像删除
##单个镜像删除,相当于:docker rmi redis:latest
docker rmi redis
##强制删除(针对基于镜像有运行的容器进程)
docker rmi -f redis
##多个镜像删除,不同镜像间以空格间隔
docker rmi -f redis tomcat nginx
##删除本地全部镜像
docker rmi -f $(docker images -q)
3.2.5 镜像构建
##(1)编写dockerfile
cd /docker/dockerfile
vim mycentos
##(2)构建docker镜像
docker build -f /docker/dockerfile/mycentos -t mycentos:1.1

3.3 容器操作

提示:对于容器的操作可使用CONTAINER ID 或 NAMES。

3.3.1 容器启动
##新建并启动容器,参数:-i  以交互模式运行容器;-t  为容器重新分配一个伪输入终端;--name  为容器指定一个名称
docker run -i -t --name mycentos
##后台启动容器,参数:-d  已守护方式启动容器
docker run -d mycentos

注意:此时使用"docker ps -a"会发现容器已经退出。这是docker的机制:要使Docker容器后台运行,就必须有一个前台进程。解决方案:将你要运行的程序以前台进程的形式运行。

##启动一个或多个已经被停止的容器
docker start redis
##重启容器
docker restart redis
3.3.2 容器进程
##top支持 ps 命令参数,格式:docker top [OPTIONS] CONTAINER [ps OPTIONS]
##列出redis容器中运行进程
docker top redis
##查看所有运行容器的进程信息
for i in  `docker ps |grep Up|awk '{print $1}'`;do echo \ &&docker top $i; done
3.3.3 容器日志
##查看redis容器日志,默认参数
docker logs rabbitmq
##查看redis容器日志,参数:-f  跟踪日志输出;-t   显示时间戳;--tail  仅列出最新N条容器日志;
docker logs -f -t --tail=20 redis
##查看容器redis从2019年05月21日后的最新10条日志。
docker logs --since="2019-05-21" --tail=10 redis
3.3.4 容器的进入与退出
##使用run方式在创建时进入
docker run -it centos /bin/bash
##关闭容器并退出
exit
##仅退出容器,不关闭
快捷键:Ctrl + P + Q
##直接进入centos 容器启动命令的终端,不会启动新进程,多个attach连接共享容器屏幕,参数:--sig-proxy=false  确保CTRL-D或CTRL-C不会关闭容器
docker attach --sig-proxy=false centos 
##在 centos 容器中打开新的交互模式终端,可以启动新进程,参数:-i  即使没有附加也保持STDIN 打开;-t  分配一个伪终端
docker exec -i -t  centos /bin/bash
##以交互模式在容器中执行命令,结果返回到当前终端屏幕
docker exec -i -t centos ls -l /tmp
##以分离模式在容器中执行命令,程序后台运行,结果不会反馈到当前终端
docker exec -d centos  touch cache.txt 
3.3.5 查看容器
##查看正在运行的容器
docker ps
##查看正在运行的容器的ID
docker ps -q
##查看正在运行+历史运行过的容器
docker ps -a
##显示运行容器总文件大小
docker ps -s
##显示最近创建容器
docker ps -l
##显示最近创建的3个容器
docker ps -n 3
##不截断输出
docker ps --no-trunc
##获取镜像redis的元信息
docker inspect redis
##获取正在运行的容器redis的 IP
docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' redis

3.3.6 容器的停止与删除
##停止一个运行中的容器
docker stop redis
##杀掉一个运行中的容器
docker kill redis
##删除一个已停止的容器
docker rm redis
##删除一个运行中的容器
docker rm -f redis
##删除多个容器
docker rm -f $(docker ps -a -q)
docker ps -a -q | xargs docker rm
## -l 移除容器间的网络连接,连接名为 db
docker rm -l db 
## -v 删除容器,并删除容器挂载的数据卷
docker rm -v redis
3.3.7 生成镜像
##基于当前redis容器创建一个新的镜像;参数:-a 提交的镜像作者;-c 使用Dockerfile指令来创建镜像;-m :提交时的说明文字;-p :在commit时,将容器暂停
docker commit -a="DeepInThought" -m="my redis" [redis容器ID]  myredis:v1.1
3.3.8 容器与主机间的数据拷贝
##将rabbitmq容器中的文件copy至本地路径
docker cp rabbitmq:/[container_path] [local_path]
##将主机文件copy至rabbitmq容器
docker cp [local_path] rabbitmq:/[container_path]/
##将主机文件copy至rabbitmq容器,目录重命名为[container_path](注意与非重命名copy的区别)
docker cp [local_path] rabbitmq:/[container_path]

4. 部署单机harbor

4.1 安装docker-compose1.下载docker-compose的最新版本

 

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

 

4.2  为docker-compose添加可执行权限
chmod +x /usr/local/bin/docker-compose
4.3 验证查看docker-compose版本

 

root@ubuntu:~# docker-compose --version
docker-compose version 1.23.1, build b02f1306
安装Harbor
tar -zxvf harbor-offline-installer-v1.8.0.tgz #解压离线安装包
mv harbor /opt/ #移到/opt目录下
cd /opt #进入到/opt目录
cd harbor
ll #查看目录内容

进入harbor目录修改harbor.yml 文件如下

hostname: 192.168.0.18
harbor_admin_password: 123456
password: 123456
data_volume: /root/docker/harbor/data

然后安装

./prepare
./install.sh

 

 

5. 实现基于负载均衡的harbor高可用

单机部署显然无法满足在生产中需求,必须要保证应用的高可用性。

目前有两种主流的方案来解决这个问题:

  • 双主复制
  • 多harbor实例共享后端存储

双主复制

主从同步

harbor官方默认提供主从复制的方案来解决镜像同步问题,通过复制的方式,我们可以实时将测试环境harbor仓库的镜像同步到生产环境harbor,类似于如下流程:

在实际生产运维的中,往往需要把镜像发布到几十或上百台集群节点上。这时,单个Registry已经无法满足大量节点的下载需求,因此要配置多个Registry实例做负载均衡。手工维护多个Registry实例上的镜像,将是十分繁琐的事情。Harbor可以支持一主多从的镜像发布模式,可以解决大规模镜像发布的难题:

只要往一台Registry上发布,镜像就像“仙女散花”般地同步到多个Registry中,高效可靠。

如果是地域分布较广的集群,还可以采用层次型发布方式,如从集团总部同步到省公司,从省公司再同步到市公司,然而单靠主从同步,仍然解决不了harbor主节点的单点问题。

双主复制说明

所谓的双主复制其实就是复用主从同步实现两个harbor节点之间的双向同步,来保证数据的一致性,然后在两台harbor前端顶一个负载均衡器将进来的请求分流到不同的实例中去,只要有一个实例中有了新的镜像,就是自动的同步复制到另外的的实例中去,这样实现了负载均衡,也避免了单点故障,在一定程度上实现了Harbor的高可用性:

这个方案有一个问题就是有可能两个Harbor实例中的数据不一致。假设如果一个实例A挂掉了,这个时候有新的镜像进来,那么新的镜像就会在另外一个实例B中,后面即使恢复了挂掉的A实例,Harbor实例B也不会自动去同步镜像,这样只能手动的先关掉Harbor实例B的复制策略,然后再开启复制策略,才能让实例B数据同步,让两个实例的数据一致。

另外,我还需要多吐槽一句,在实际生产使用中,主从复制十分的不靠谱。

所以这里推荐使用下面要说的这种方案。

多harbor实例共享后端存储

方案说明

共享后端存储算是一种比较标准的方案,就是多个Harbor实例共享同一个后端存储,任何一个实例持久化到存储的镜像,都可被其他实例中读取。通过前置LB进来的请求,可以分流到不同的实例中去处理,这样就实现了负载均衡,也避免了单点故障:

这个方案在实际生产环境中部署需要考虑三个问题:

1.共享存储的选取,Harbor的后端存储目前支持AWS S3、Openstack Swift, Ceph等,在我们的实验环境里,就直接使用nfs

2.Session在不同的实例上共享,这个现在其实已经不是问题了,在最新的harbor中,默认session会存放在redis中,我们只需要将redis独立出来即可。可以通过redis sentinel或者redis cluster等方式来保证redis的可用性。在我们的实验环境里,仍然使用单台redis

3.Harbor多实例数据库问题,这个也只需要将harbor中的数据库拆出来独立部署即可。让多实例共用一个外部数据库,数据库的高可用也可以通过数据库的高可用方案保证。

环境说明

实验环境:

iprole
192.168.0.101 harbor
192.168.0.102 harbor
192.168.0.106 redis、mysql、nfs

配置说明

安装nfs
# 安装nfs
apt install nfs-kernel-server nfs-common
# 编辑/etc/exports文件
/data	*(rw,no_root_squash)
chmod 777 -R /data
systemctl start nfs-server
安装redis和mysql

这里我们就直接通过docker安装,docker-compose.yml文件内容如下:

version: '3'
services:
  mysql-server:
    hostname: mysql-server
    container_name: mysql-server
    image: mysql:5.7
    network_mode: host
    volumes:
      - /mysql57/data:/var/lib/mysql
    command: --character-set-server=utf8
    environment:
      MYSQL_ROOT_PASSWORD: 123456
  redis:
    hostname: redis-server
    container_name: redis-server
    image: redis:3
    network_mode: host

启动:

docker-compose up -d
导入registry数据库

配置好了mysql以后,还需要往mysql数据库中导入harbor registry库。安装了一个单机版harbor,启动了一个mysql,里面有一个registry数据库,直接导出来,然后再导入到新数据库中:

# 导出数据库:
docker exec -it harbor_db /bin/bash
mysqldump -uroot -p --databases registry > registry.dump
# 在宿主机上将registry.dump复制出来
docker cp  harbor_db:/registry.dump ./
# 将宿主机上的registry.dump复制到独立的mysql容器中
docker cp ./registry.dump <mysql-server-container>:/registry.dump
# 在独立的mysql容器将将registry数据库导入
docker exec -it <mysql-server-container> /bin/bash
mysql -uroot -p
mysql> source /registry.dump

配置harbor

挂载nfs目录

在harbor节点上挂载nfs目录:

mount -t nfs 192.168.0.106:/data /data
修改harbor.cfg配置

在harbor节点上,下载好harbor的安装包,生成好自签名证书,修改prepare文件,harbor.cfg文件需要修改数据库及redis配置如下:

db_host = 192.168.0.106
db_password = 123456
db_port = 3306
db_user = root
redis_url = 192.168.0.106:6379
修改docker-compose.yml配置

与单机版harbor相比,集群配置不再需要启动mysql和redis,所以docker-compose.yml也需要作相应修改。事实上,在harbor的安装目录中,有个ha的目录,里面已经提供了我们需要的docker-compose.yml文件,只需要复制出来即可。实际上,在这个目录中,还提供了使用lvs作为负载均衡器时,keepalived的配置。

cp ha/docker-compose.yml
./prepare
./install.sh

在两个harbor节点上完成安装以后,我们可以通过绑定hosts到不同的节点来验证两个节点的负载均衡效果。

 
posted @ 2021-12-29 20:41  Gamelook  阅读(51)  评论(1编辑  收藏  举报