【葵花宝典】一天掌握Docker
第1章Docker 概述
1-1 Docker是什么
没有虚拟化技术的原始年代
我们仔细想想,在没有计算虚拟化技术的“远古”年代,如果我们要部署一个应用程序(Application),一般的步骤是怎么样的?
第一步肯定是先要准备一台物理服务器,然后在物理服务器上安装一个操作系统(Operating System),有了操作系统之后,便在操作系统上安装运行我们的应用程序,这个过程可以用下面的图来表示:

物理服务器部署应用示意图
那么,这种方式有什么问题呢?其实,在物理机上部署应用有以下几个缺点:
- 部署非常慢:因为我们得先准备硬件服务器,接着还要安装操作系统,然后再部署应用程序,而且应用程序还有很多的依赖软件,所以这个过程是比较慢的。
- 成本非常高:主要是物理器成本太高,即使是部署一个简单的应用,也需要一台服务器。
- 资源浪费:如果应用太简单,也容易浪费硬件资源,比如CPU和内存。
- 迁移和扩展太慢:如果需要迁移应用,或者扩展应用,都要再准备其他的物理服务器,过程很麻烦,也很慢。
那么有什么办法可以解决这些问题呢?答案便是虚拟化技术。
使用虚拟机部署应用程序的年代
什么是虚拟化技术
谈到计算机的虚拟化技术,我们直接想到的便是虚拟机,虚拟机允许我们在一台物理计算机模拟出多台机器,简单地理解,虚拟化技术就是在一台物理计算机上,通过中间虚拟软件层Hypervisor隔离CPU、内存等硬件资源,虚拟出多台虚拟服务器,这样做的话,一台物理服务器便可以安装多个应用程序,达到资源利用的最大化,而且多个应用之间相互隔离,如下图所示:

虚拟机上部署应用示意图
虚拟机的优点
- 可以把资源分配到不同的虚拟机,达到硬件资源的最大化利用
- 与直接在物理机上部署应用,虚拟更容易扩展应用。
- 云服务:通过虚拟机虚拟出不同的物理资源,可以快速搭建云服务。
虚拟机的不足之处
虚拟机的不足之外来自于对物理服务器资源的消耗,当我们在物理服务器创建一台虚拟机时,便需要虚拟出一套硬件并在上面运行完整的操作系统,每台虚拟机都占用许多的服务器资源。
Docker是什么?
相对于虚拟机的笨重,Docker则更显得轻量化,因此不会占用太多的系统资源。
Docker是使用时下很火的Golang语言进行开发的,其技术核心是Linux内核的Cgroup,Namespace和AUFS类的Union FS等技术,这些技术都是Linux内核中早已存在很多年的技术,所以严格来说并不是一个完全创新的技术,Docker通过这些底层的Linux技术,对Linux进程进行封装隔离,而被隔离的进程也被称为容器,完全独立于宿主机的进程。如果你想和更多Docker技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态。
所以Docker是容器技术的一种实现,也是操作系统层面的一种虚拟化,与虚拟机的通过一套硬件再安装操作系统完全不同。

Docker容器与系统关系示意图
容器解决了开发与生产环境的问题
开发环境与生产环境折射的是开发人员与运维人员之间的矛盾,也许我们常常会听到开发人员对运维人员说的这样一句话:“在我的电脑运行没问题,怎么到了你那里就出问题了,肯定是你的问题”,而运维人员是认为是开发人员的问题。
开发人员需要在本机安装各种各样的测试环境,因此开发的项目需要软件越多,依赖越多,安装的环境也就越复杂。
同样的,运维人员需要为开发人员开发的项目提供生产环境,而运维人员除了应对软件之间的依赖,还需要考虑安装软件与硬件之间的兼容性问题。
就是这样,所以我们经常看到开发与运维相互甩锅,怎么解决这个问题呢?
容器就是一个不错的解决方案,容器能成为开发与运维之间沟通的语言,因为容器就像一个集装箱一样,提供了软件运行的最小化环境,将应用与其需要的环境一起打包成为镜像,便可以在开发与运维之间沟通与传输。

Docker的版本
Docker分为社区版(CE)和企业版(EE)两个版本,社区版本可以免费使用,而企业版则需要付费使用,对于我们个人开发者或小企业来说,一般是使用社区版的。
Docker CE有三个更新频道,分别为stable、test、nightly,stable是稳定版本,test是测试后的预发布版本,而nightly则是开发中准备在下一个版本正式发布的版本,我们可以根据自己的需求下载安装。
1-2 容器 vs 虚拟机
Docker与虚拟机之间的比较
Docker是在操作系统进程层面的隔离,而虚拟机是在物理资源层面的隔离,两者完全不同,另外,我们也可以通过下面的一个比较,了解两者的根本性差异。

容器与虚拟机的比较【摘自《Docker-从入门到实践》】
从上面的容器与虚拟机的对比中,我们明白了容器技术的优势。
1-3 Docker应用场景
1. 简化配置
这是Docker公司宣传的Docker的主要使用场景。虚拟机的最大好处是能在你的硬件设施上运行各种配置不一样的平台(软件、系统),Docker在降低额外开销的情况下提供了同样的功能。它能让你将运行环境和配置放在代码中然后部署,同一个Docker的配置可以在不同的环境中使用,这样就降低了硬件要求和应用环境之间耦合度。
2. 代码流水线(Code Pipeline)管理
前一个场景对于管理代码的流水线起到了很大的帮助。代码从开发者的机器到最终在生产环境上的部署,需要经过很多的中间环境。而每一个中间环境都有自己微小的差别,Docker给应用提供了一个从开发到上线均一致的环境,让代码的流水线变得简单不少。
3. 提高开发效率
这就带来了一些额外的好处:Docker能提升开发者的开发效率。如果你想看一个详细一点的例子,可以参考Aater在DevOpsDays Austin 2014大会或者是DockerCon上的演讲。
不同的开发环境中,我们都想把两件事做好。一是我们想让开发环境尽量贴近生产环境,二是我们想快速搭建开发环境。
理想状态中,要达到第一个目标,我们需要将每一个服务都跑在独立的虚拟机中以便监控生产环境中服务的运行状态。然而,我们却不想每次都需要网络连接,每次重新编译的时候远程连接上去特别麻烦。这就是Docker做的特别好的地方,开发环境的机器通常内存比较小,之前使用虚拟的时候,我们经常需要为开发环境的机器加内存,而现在Docker可以轻易的让几十个服务在Docker中跑起来。
4. 隔离应用
有很多种原因会让你选择在一个机器上运行不同的应用,比如之前提到的提高开发效率的场景等。
我们经常需要考虑两点,一是因为要降低成本而进行服务器整合,二是将一个整体式的应用拆分成松耦合的单个服务(译者注:微服务架构)。如果你想了解为什么松耦合的应用这么重要,请参考Steve Yege的这篇论文,文中将Google和亚马逊做了比较。
5. 整合服务器
正如通过虚拟机来整合多个应用,Docker隔离应用的能力使得Docker可以整合多个服务器以降低成本。由于没有多个操作系统的内存占用,以及能在多个实例之间共享没有使用的内存,Docker可以比虚拟机提供更好的服务器整合解决方案。
6. 调试能力
Docker提供了很多的工具,这些工具不一定只是针对容器,但是却适用于容器。它们提供了很多的功能,包括可以为容器设置检查点、设置版本和查看两个容器之间的差别,这些特性可以帮助调试Bug。你可以在《Docker拯救世界》的文章中找到这一点的例证。
7. 多租户环境
另外一个Docker有意思的使用场景是在多租户的应用中,它可以避免关键应用的重写。我们一个特别的关于这个场景的例子是为IoT(译者注:物联网)的应用开发一个快速、易用的多租户环境。这种多租户的基本代码非常复杂,很难处理,重新规划这样一个应用不但消耗时间,也浪费金钱。
使用Docker,可以为每一个租户的应用层的多个实例创建隔离的环境,这不仅简单而且成本低廉,当然这一切得益于Docker环境的启动速度和其高效的diff
命令。
8. 快速部署
在虚拟机之前,引入新的硬件资源需要消耗几天的时间。虚拟化技术(Virtualization)将这个时间缩短到了分钟级别。而Docker通过为进程仅仅创建一个容器而无需启动一个操作系统,再次将这个过程缩短到了秒级。这正是Google和Facebook都看重的特性。
你可以在数据中心创建销毁资源而无需担心重新启动带来的开销。通常数据中心的资源利用率只有30%,通过使用Docker并进行有效的资源分配可以提高资源的利用率。
1-4 Linux安装Docker
https://docs.docker.com/engine/install/centos/
1、 卸载旧版本
- $ sudo yum remove docker \
- docker-client \
- docker-client-latest \
- docker-common \
- docker-latest \
- docker-latest-logrotate \
- docker-logrotate \
- docker-engine
2、 安装 Docker Engine-Community
使用 Docker 仓库进行安装
在新主机上首次安装 Docker Engine-Community 之前,需要设置 Docker 仓库。之后,您可以从仓库安装和更新 Docker。
设置仓库
安装所需的软件包。yum-utils 提供了 yum-config-manager ,并且 device mapper 存储驱动程序需要 device-mapper-persistent-data 和 lvm2。
- $ sudo yum install -y yum-utils \
- device-mapper-persistent-data \
- lvm2
使用以下命令来设置稳定的仓库。
- $ sudo yum-config-manager \
- --add-repo \
- https://download.docker.com/linux/centos/docker-ce.repo
安装 Docker Engine-Community
安装最新版本的 Docker Engine-Community 和 containerd,或者转到下一步安装特定版本:
- $ sudo yum install docker-ce docker-ce-cli containerd.io
如果提示您接受 GPG 密钥,请选是。
有多个 Docker 仓库吗?
如果启用了多个 Docker 仓库,则在未在 yum install 或 yum update 命令中指定版本的情况下,进行的安装或更新将始终安装最高版本,这可能不适合您的稳定性需求。
Docker 安装完默认未启动。并且已经创建好 docker 用户组,但该用户组下没有用户。要安装特定版本的 Docker Engine-Community,请在存储库中列出可用版本,然后选择并安装:
(1) 列出并排序您存储库中可用的版本。此示例按版本号(从高到低)对结果进行排序。
- $ yum list docker-ce --showduplicates | sort -r
(2) 通过其完整的软件包名称安装特定版本,该软件包名称是软件包名称(docker-ce)加上版本字符串(第二列),从第一个冒号(:)一直到第一个连字符,并用连字符(-)分隔。例如:docker-ce-18.09.1。
- $ sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io
- 启动 Docker,并验证
- $ sudo systemctl start docker
- $ sudo docker run hello-world
安装地址可以更换阿里云的地址:
- sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
安装pstree
- yum -y install psmisc
- pstree -apnh
更换国内镜像源:
- # vi /etc/docker/daemon.json
- {
- "registry-mirrors": ["http://hub-mirror.c.163.com"]
- }
- #systemctl restart docker.service
第2章Docker 镜像管理
2-1 镜像与容器联系和存储驱动
2-2 镜像管理常用命令
第3章Docker 容器管理
3-1 创建容器常见选项
3-2 容器管理常用命令
第4章Docker 数据管理
4-1 Volume(数据卷)
4-2 应用案例:搭建LNMP网站平台实战
方式一:
1、安装WordPress
获取相关镜像
- 1 [root@localhost ~]# docker pull wordpress
- 2 [root@localhost ~]# docker pull mysql
启动mysql
- [root@localhost ~]# docker run -d \
- --name wordpress-mysql \
- -e MYSQL_ROOT_PASSWORD=toor \
- -p 3306:3306 \
- mysql
启动wordpress(需要修改mysql root远程访问限制)
- [root@localhost ~]# docker run -d \
- --name wordpress-wordpress \
- --link wordpress-mysql:mysql \
- -p 80:80 \
- wordpress
- [root@localhost ~]# docker ps
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- 361cd4280640 wordpress "docker-entrypoint.s…" 6 seconds ago Up 3 seconds 0.0.0.0:80->80/tcp wordpress-wordpress
- 3a4cd23ba46e mysql "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:3306->3306/tcp, 33060/tcp wordpress-mysql
- [root@localhost /]# docker exec -it 3a4cd23ba46e /bin/bash
- root@3a4cd23ba46e:/#
root@3a4cd23ba46e:/# mysql -uroot -p
mysql> use mysql;
mysql> grant all on *.* to 'root'@'%';
mysql> alter user 'root'@'localhost' identified by 'toor' password expire never;
mysql> alter user 'root'@'%' identified with mysql_native_password by 'toor';
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
- [root@localhost /]# docker restart 361cd4280640
备注:如果使用外部mysql可以使用如下命令
- docker run -d\
- --name wordpress-wordpress \
- -e WORDPRESS_DB_HOST=数据库Ip地址:3306 \
- -e WORDPRESS_DB_USER=... \
- -e WORDPRESS_DB_PASSWORD=... \
- -wordpress
打开WordPress配置
方式二:
参考:https://www.cnblogs.com/zc1741845455/p/11082079.html
第5章网络模式
5-1 四种网络模式
第6章教你制作镜像
6-1 Dockerfile常用指令
指令 | 描述 | 指令 | 描述 |
FROM |
构造的新镜像是基于哪个镜像 例如:FROM centos:v1 |
COPY |
拷贝文件或者目录到镜像,用法同ADD 例如:COPY ./startup.sh /startup.sh |
MAINTAINER |
维护者信息 例如:MAINTAINER yanglin |
ENTRYPOINT |
运行容器时执行的shell命令 例如:ENTRYPOINT ["/bin/bash","-c","/startup.sh"] ENTRYPOINT /bin/bash -c '/startup.sh' |
RUN |
构建镜像时运行的shell命令 例如:RUN ["yum", "install", "http"] RUN yum install httpd |
VOLUME |
指定容器挂载点到宿主机自动生成的目录或者其他容器 例如:VOLUME ["/path/to/dir"] |
CMD |
运行容器时执行的shell命令 例如:CMD ["-c","/startup.sh"] CMD ["/usr/sbin/sshd","-D"] CMD /usr/sbin/sshd -D |
USER |
为RUN,CMD,ENTRYPOINT执行命令指定运行用户 例如:USER www 镜像构建完成后,通过docker run运行容器时,可以通过-u参数来覆盖所指定的用户。 |
EXPOSE |
指定于外界交互的端口,即容器在运行时监听的端口 EXPOSE 8081 8082 |
WORKDIR |
为RUN,CMD,ENTRYPOINT,COPY和ADD设置工作目录 例如:WORKDIR /data |
ENV |
设置容器内环境变量 例如:ENV MYSQL_ROOT_PASSWORD 123456 |
HEALTHCHECK |
健康检查 HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ ||exit 1 |
ADD |
拷贝文件或者目录到镜像,如果是URL或者压缩包会自动下载或者自动解压 例如: ADD hom* /mydir/ |
ARG |
在构建镜像时指定一些参数 例如: FROM centos:6 ARG age=100 |
1.RUN,CMD,ENTRYPOINT的区别
- A.RUN 指令:用于指定 docker build 过程中要运行的命令,可以写多条
- B.CMD和ENTRYPOINT在运行容器时运行,只能写一条,如果是写多条,最后一条生效
- C.CMD 指令指定的程序可被 docker run 命令行参数中指定要运行的程序所覆盖。
- D.ENTRYPOINT类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,如果运行 docker run 时使用了 --entrypoint 选项,
此选项的参数可当作要运行的程序覆盖 ENTRYPOINT 指令指定的程序;
2.通过dockerfile构建Tomcat网站环境镜像

- FROM centos
- MAINTAINER liuqingliang
- ADD apache-tomcat-8.5.37.tar.gz /usr/local/
- ADD jdk-8u191-linux-x64.rpm /root/
- RUN rpm -ivh /root/jdk-8u191-linux-x64.rpm
- WORKDIR /usr/local/apache-tomcat-8.5.37/
- ENTRYPOINT ["bin/catalina.sh","run"]
- EXPOSE 8080

- [root@localhost codetest]# docker build -t tomcat:v1 -f dockerfile_tomcat .
删除有所有镜像
[root@localhost ~]# docker rm -f $(docker ps -q -a)
3.通过dockerfile构建ssh服务器

- FROM centos
- MAINTAINER liuqingliang
- ENV ROOT_PASSWORD toor
- RUN yum install -y openssh-server
- RUN echo $ROOT_PASSWORD |passwd --stdin root
- RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
- RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
- CMD ["/usr/sbin/sshd","-D","&"]
- EXPOSE 22

6-2 构建PHP网站平台镜像
6-3 构建JAVA网站镜像
第7章企业级镜像仓库 Habor
7-1 官方Registry镜像仓库搭建与使用
- [root@localhost ~]# docker pull registry
7-2 构建单机版企业级Harbor
Harbor简介
在说harbor之前,我们首先说一说直接使用docker registry的一些缺陷:
- 缺少认证机制,任何人都可以随意拉取及上传镜像,安全性缺失
- 缺乏镜像清理机制,镜像可以push却不能删除,日积月累,占用空间会越来越大
- 缺乏相应的扩展机制
鉴于以上缺点,我们通常在生产环境中,不会直接使用docker registry来实现提供镜像服务。而Harbor正好解决了上述所有的问题。
Harbor是一个用于存储和分发Docker镜像的企业级Registry服务器,通过添加一些企业必需的功能特性,例如安全、标识和管理等,扩展了开源Docker Distribution。作为一个企业级私有Registry服务器,Harbor提供了更好的性能和安全。提升用户使用Registry构建和运行环境传输镜像的效率。Harbor支持安装在多个Registry节点的镜像资源复制,镜像全部保存在私有Registry中,确保数据和知识产权在公司内部网络中管控。另外,Harbor也提供了高级的安全特性,诸如用户管理,访问控制和活动审计等。
Harbor官方网站:http://vmware.github.io/harbor/
Harbor源码地址:https://github.com/vmware/harbor
harbor的二进制包同时提供online和offline版本,我们这里直接使用online版本。
配置
架构图
如上图所示,harbor由6大模块级成:
- Proxy: Harbor的registry、UI、token services等组件,都处在一个反向代理后边。该代理将来自浏览器、docker clients的请求转发到后端服务上。
- Registry: 负责存储Docker镜像,以及处理Docker push/pull请求。因为Harbor强制要求对镜像的访问做权限控制, 在每一次push/pull请求时,Registry会强制要求客户端从token service那里获得一个有效的token。
- Core services: Harbor的核心功能,主要包括如下3个服务:
- UI: 作为Registry Webhook, 以图像用户界面的方式辅助用户管理镜像。1) WebHook是在registry中配置的一种机制, 当registry中镜像发生改变时,就可以通知到Harbor的webhook endpoint。Harbor使用webhook来更新日志、初始化同步job等。 2) Token service会根据该用户在一个工程中的角色,为每一次的push/pull请求分配对应的token。假如相应的请求并没有包含token的话,registry会将该请求重定向到token service。 3) Database 用于存放工程元数据、用户数据、角色数据、同步策略以及镜像元数据。
- Job services: 主要用于镜像复制,本地镜像可以被同步到远程Harbor实例上。
- Log collector: 负责收集其他模块的日志到一个地方
组件说明
需要说明的是,harbor的每个组件都是以Docker容器的形式构建的,可以使用Docker Compose来进行部署,当然,如果你的环境中使用了kubernetes,harbor也提供了kubernetes的配置文件。
harbor共有8个容器组成:
- ui:harbor的核心服务。
- log:运行着rsyslog的容器,进行日志收集。
- mysql:由官方mysql镜像构成的数据库容器
- nginx:使用Nginx做反向代理
- registry:官方的Docker registry
- adminserver:harbor的配置数据管理器
- jobservice:Harbor的任务管理服务。
- redis:用于存储session
下面我们所有的配置都以harbor 1.5.2版本作配置说明。
harbor可以支持http和https,建议使用https,https证书最好是受信任的ca颁发的证书,这样,在配置docker的时候,就不需要添加"insecure-registries"配置项。我们这里就直接使用自签名证书。
生成证书:
openssl genrsa -des3 -out server.key 1024
openssl rsa -in server.key -out server.key #去除server.key的加密口令
openssl req -new -key server.key -out server.csr
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
将证书放入/data/certs/目录下:
cp server.crt server.key /data/certs
harbor.cfg配置文件示例
harbor的核心配置文件是harbor.cfg,路径在源码目录下的make目录下,示例配置如下:
_version = 1.5.0
# harbor的访问地址
hostname = hub.dz11.com
# 使用https协议
ui_url_protocol = https
# harbor的数据库密码
db_password = xxxxx
max_job_workers = 50
customize_crt = on
# 证书相关路径
ssl_cert = /data/certs/dz11.com.crt
ssl_cert_key = /data/certs/dz11.com.key
secretkey_path = /data
admiral_url = NA
# 定义日志滚动
log_rotate_count = 50
log_rotate_size = 200M
http_proxy =
https_proxy =
no_proxy = 127.0.0.1,localhost,ui
# email相关配置
email_identity =
email_server = smtp.163.com
email_server_port = 25
email_username = yan_ruo_gu@163.com
email_password = yan_ruo_gu0711
email_from = admin <yan_ruo_gu@163.com>
email_ssl = false
# 登录harbor的管理员密码
harbor_admin_password = xxxxxx
# harbor的验证方式,支持db_auth和ldap_auth,这里使用的是ldap_auth,如果使用db_auth的话,直接将auth_mode修改为db_auth即可,ldap的配置不再需要。
auth_mode = ldap_auth
ldap_url = ldap://10.1.1.1:389
ldap_searchdn = CN=jenkins,OU=LDAP,OU=Dev-wh.xxx.com,DC=dev-wh,DC=xxx,DC=com
ldap_search_pwd = xxxx
ldap_basedn = OU=Dev-wh.xxx.com,DC=dev-wh,DC=xxx,DC=com
ldap_filter = (objectClass=person)
ldap_uid = 0
ldap_scope = 2
ldap_timeout = 5
self_registration = on
token_expiration = 30
project_creation_restriction = everyone
verify_remote_cert = on
# 数据库相关配置,默认如果不需要使用自建的数据库,这些配置就都不需要
db_host = mysql
db_password = root123
db_port = 3306
db_user = root
redis_url = redis:6379
clair_db_host = postgres
clair_db_password = password
clair_db_port = 5432
clair_db_username = postgres
clair_db = postgres
uaa_endpoint = uaa.mydomain.org
uaa_clientid = id
uaa_clientsecret = secret
uaa_verify_cert = true
uaa_ca_cert = /path/to/ca.pem
registry_storage_provider_name = filesystem
registry_storage_provider_config =
部署harbor
harbor支持docker-compose和kubernetes的部署方式,默认采用docker-compose作单机部署。
先执行./prepare,然后执行./install.sh进行启动。执行./install.sh的时候,即调用了docker-compose运行了当前目录下的docker-compose.yml文件。
在执行.prepare的时候抛出如下异常:
root@ubuntu:~/harbor# ./prepare
Generated and saved secret to file: /data/secretkey
Generated configuration file: ./common/config/nginx/nginx.conf
Generated configuration file: ./common/config/adminserver/env
Generated configuration file: ./common/config/ui/env
Generated configuration file: ./common/config/registry/config.yml
Generated configuration file: ./common/config/db/env
Generated configuration file: ./common/config/jobservice/env
Generated configuration file: ./common/config/jobservice/config.yml
Generated configuration file: ./common/config/log/logrotate.conf
Generated configuration file: ./common/config/jobservice/config.yml
Generated configuration file: ./common/config/ui/app.conf
Fail to generate key file: ./common/config/ui/private_key.pem, cert file: ./common/config/registry/root.crt
需要修改prepare文件,将第498行:
empty_subj = "/C=/ST=/L=/O=/CN=/"
修改如下:
empty_subj = "/C=US/ST=California/L=Palo Alto/O=VMware, Inc./OU=Harbor/CN=notarysigner"
在实际启动过程中,出现过registry启动失败的情况,/var/log/harbor/registry.log输出如下:
May 30 21:06:00 172.18.0.1 registry[3218]: panic: unable to configure authorization (token): unable to open token auth root certificate bundle file "/etc/registry/root.crt": open /etc/registry/root.crt: permission denied
May 30 21:06:00 172.18.0.1 registry[3218]:
May 30 21:06:00 172.18.0.1 registry[3218]: goroutine 1 [running]:
May 30 21:06:00 172.18.0.1 registry[3218]: panic(0xb4cd40, 0xc4203ae160)
May 30 21:06:00 172.18.0.1 registry[3218]: #011/usr/local/go/src/runtime/panic.go:500 +0x1a1
May 30 21:06:00 172.18.0.1 registry[3218]: github.com/docker/distribution/registry/handlers.NewApp(0x1067820, 0xc4203a8630, 0xc4202df180, 0x1067820)
May 30 21:06:00 172.18.0.1 registry[3218]: #011/go/src/github.com/docker/distribution/registry/handlers/app.go:302 +0x1b6a
May 30 21:06:00 172.18.0.1 registry[3218]: github.com/docker/distribution/registry.NewRegistry(0x7fcfa30dd198, 0xc4203a8630, 0xc4202df180, 0xe, 0x0, 0x0)
May 30 21:06:00 172.18.0.1 registry[3218]: #011/go/src/github.com/docker/distribution/registry/registry.go:86 +0x213
May 30 21:06:00 172.18.0.1 registry[3218]: github.com/docker/distribution/registry.glob..func1(0x108f1a0, 0xc42036d240, 0x1, 0x1)
May 30 21:06:00 172.18.0.1 registry[3218]: #011/go/src/github.com/docker/distribution/registry/registry.go:55 +0x106
May 30 21:06:00 172.18.0.1 registry[3218]: github.com/docker/distribution/vendor/github.com/spf13/cobra.(*Command).execute(0x108f1a0, 0xc42036d1f0, 0x1, 0x1, 0x108f1a0, 0xc42036d1f0)
May 30 21:06:00 172.18.0.1 registry[3218]: #011/go/src/github.com/docker/distribution/vendor/github.com/spf13/cobra/command.go:495 +0x190
May 30 21:06:00 172.18.0.1 registry[3218]: github.com/docker/distribution/vendor/github.com/spf13/cobra.(*Command).Execute(0x108f340, 0xc4201d7f40, 0xc4200001a0)
May 30 21:06:00 172.18.0.1 registry[3218]: #011/go/src/github.com/docker/distribution/vendor/github.com/spf13/cobra/command.go:560 +0x3c3
May 30 21:06:00 172.18.0.1 registry[3218]: main.main()
May 30 21:06:00 172.18.0.1 registry[3218]: #011/go/src/github.com/docker/distribution/cmd/registry/main.go:24 +0x2d
这是因为registry容器没有/etc/registry/root.crt的访问权限导致。这个文件默认是挂载的./common/config/registry/root.crt
这个文件,所以我们需要对这个文件作授权。
通过观察可知,harbor的容器启动用户非root身份,而是以一个uid和gid都为10000的用户。所以只需要为该用户授权即可:
chown 10000.10000 ./common/config/registry/root.crt
正常启动之后,配置完成。
可以通过如下方式访问: https://hub.dz11.com
注意事项
需要说明的是,harbor支持http和https,但如果使用http的话,在拉取镜像的时候,会抛出仓库不受信任的异常。需要在所有的docker客户端的docker配置文件/etc/docker/daemon.json中添加如下配置:
{
"insecure-registries": ["https://hub.dz11.com"],
}
如果使用自签名的https证书,仍然会提示证书不受信任的问题。需要将自签名的ca证书发送到所有的docker客户端的指定目录。
关于使用自签名证书配置harbor的具体过程可以参考:https://github.com/WingkaiHo/docker-calico/blob/master/harbor/README.md
7-3 构建高可用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进来的请求,可以分流到不同的实例中去处理,这样就实现了负载均衡,也避免了单点故障:
这个方案在实际生产环境中部署需要考虑三个问题:
- 共享存储的选取,Harbor的后端存储目前支持AWS S3、Openstack Swift, Ceph等,在我们的实验环境里,就直接使用nfs
- Session在不同的实例上共享,这个现在其实已经不是问题了,在最新的harbor中,默认session会存放在redis中,我们只需要将redis独立出来即可。可以通过redis sentinel或者redis cluster等方式来保证redis的可用性。在我们的实验环境里,仍然使用单台redis
- Harbor多实例数据库问题,这个也只需要将harbor中的数据库拆出来独立部署即可。让多实例共用一个外部数据库,数据库的高可用也可以通过数据库的高可用方案保证。
环境说明
实验环境:
ip | role |
---|---|
192.168.198.133 | harbor |
192.168.198.135 | harbor |
192.168.198.136 | 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.198.136:/data /data
修改harbor.cfg配置
在harbor节点上,下载好harbor的安装包,生成好自签名证书,修改prepare文件,可直接参考《企业级镜像管理系统Harbor》,不同的是,harbor.cfg文件需要修改数据库及redis配置如下:
db_host = 192.168.198.136
db_password = 123456
db_port = 3306
db_user = root
redis_url = 192.168.198.136: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到不同的节点来验证两个节点的负载均衡效果。
https://baike.baidu.com/item/kubernetes/22864162?fr=aladdin
【葵花宝典】一天掌握Docker的更多相关文章
- 老司机实战Windows Server Docker:5 Windows Server Dockerfile葵花宝典
前面两篇(简单运维1.简单运维2)介绍了一些Windows Server Docker相关的基本运维知识.今天这一篇,Windows Server Dockerfile葵花宝典,涵盖了许多典型场景的W ...
- 老司机实战Windows Server Docker:4 单节点Windows Docker服务器简单运维(下)
上篇中,我们主要介绍了使用docker-compose对Windows Docker单服务器进行远程管理,编译和部署镜像,并且设置容器的自动启动.但是,还有一些重要的问题没有解决,这些问题不解决,就完 ...
- docker——容器安装tomcat
写在前面: 继续docker的学习,学习了docker的基本常用命令之后,我在docker上安装jdk,tomcat两个基本的java web工具,这里对操作流程记录一下. 软件准备: 1.jdk-7 ...
- Docker笔记一:基于Docker容器构建并运行 nginx + php + mysql ( mariadb ) 服务环境
首先为什么要自己编写Dockerfile来构建 nginx.php.mariadb这三个镜像呢?一是希望更深入了解Dockerfile的使用,也就能初步了解docker镜像是如何被构建的:二是希望将来 ...
- Docker 第一篇--初识docker
已经多年不写博客, 看完<晓松奇谈>最后一期猛然觉醒, 决定仔细梳理下自己这几年的知识脉络. 既然决定写, 那么首先就从最近2年热门的开源项目Docker开始.Docker 这两年在国内很 ...
- 在docker中运行ASP.NET Core Web API应用程序(附AWS Windows Server 2016 widt Container实战案例)
环境准备 1.亚马逊EC2 Windows Server 2016 with Container 2.Visual Studio 2015 Enterprise(Profresianal要装Updat ...
- docker for mac 学习记录
docker基本命令 docker run -d -p 80:80 --name webserver nginx 运行容器并起别名 docker ps 展示目前启动的容器 docker ps -a 展 ...
- scrapy爬虫docker部署
spider_docker 接我上篇博客,为爬虫引用创建container,包括的模块:scrapy, mongo, celery, rabbitmq,连接https://github.com/Liu ...
- [原][Docker]特性与原理解析
Docker特性与原理解析 文章假设你已经熟悉了Docker的基本命令和基本知识 首先看看Docker提供了哪些特性: 交互式Shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上, ...
随机推荐
- ubuntu ImportError: No module named setuptools 一句命令解决方案
https://blog.csdn.net/Super_jm_/article/details/81947563 使用pip安装文件时候提示 ImportError: No module named ...
- Python SQLALchemy框架
SQLALchemy SQLALchemy是Python中的一款优秀的ORM框架,它可以作用于任何第三方Web框架,如flask,tornado等框架. SQLALchemy相较于DjangoORM来 ...
- js上 八.语句
1.什么是语句 概念:在JavaScript中表达式是短语,语句就是整句或命令. 作用:用来执行以使某件事发生 特征:用 ; 结尾 Js中语句的常见形式: ü 表达式语句 ü 复合语句 ü 声明语句 ...
- [GXYCTF2019]禁止套娃(无参RCE)
[GXYCTF2019]禁止套娃 1.扫描目录 扫描之后发现git泄漏 使用githack读取泄漏文件 <?php include "flag.php"; echo &quo ...
- 容器编排系统k8s之Service资源
前文我们了解了k8s上的DemonSet.Job和CronJob控制器的相关话题,回顾请参考:https://www.cnblogs.com/qiuhom-1874/p/14157306.html:今 ...
- CentOS8/Windows 安装RabbitMQ
Centos8安装rabbitmq 1.安装erlang(rabbitmq是用erlang语言开发的,erlang版本需要22.x以上) yum install erlang 2.下载rabbitmq ...
- vs2012新特性
VS2012的六大技术特点: 1.VS2012和VS2010相比,最大的新特性莫过于对Windows 8Metro开发的支持.Metro天生为云端而生,简洁.数字化.内容优于形式.强调交互的设计已经成 ...
- (二)、vim即gvim的炫酷搜索模式与技巧
一.进入搜索模式 1. 打开文件,狂按 <Esc> 进入normal模式,然后按 / 或者 :/ 进入搜索模式,添加上关键字例如world,按回车即搜索world: :/wo ...
- 利用 Watermill 实现 Golang CQRS
CQRS CQRS 的意思是"命令-查询责任隔离".我们分离了命令(写请求)和查询(读请求)之间的责任.写请求和读请求由不同的对象处理. 就是这样.我们可以进一步分割数据存储,使用 ...
- flowable获取上级主管
//主管 Dept managerDept = deptUserUtil.getManagerDept(bean.getCreateDept(),bean.getCreateUser()); //上级 ...