docker pull镜像失败init 失败了,怎么办

41885人阅读
DOCKER windows安装
编者: xiaym&
排版工具: 马克飞象&
windows必须是64位的
1.下载程序包
下载最新版本的:docker-install.exe即可。&
该安装包安装完成后,系统上会多出三个软件:
Oracle VM VirtualBox&
Boot2Docker for Windows
以上三个默认安装即可。
2. 设置环境变量
在命令窗口中,输入ls 如果能找到命令说明环境添加正确。
3. 启动DOCKERT
在命令窗口中,切到docker的安装目录下&
然后输入start.sh,等待启动&
第一次启动中,如果有新版本会更新,时间比较长。
如果第二次启动,就非常快了。&
4. 分析start.sh
#!/bin/bashset -e
# clear the MSYS MOTD
cd &$(dirname &$BASH_SOURCE&)&
ISO=&$HOME/.boot2docker/boot2docker.iso&
if [ ! -e &$ISO& ]; then
echo 'copying initial boot2docker.iso (run &boot2docker.exe download& to update)'
mkdir -p &$(dirname &$ISO&)&
cp ./boot2docker.iso &$ISO&fi
echo 'initializing...'
./boot2docker.exe init
echo 'starting...'
./boot2docker.exe start
./boot2docker.exe ip
echo 'connecting...'
./boot2docker.exe ssh
echoecho '[Press any key to exit]'read
从内容上看主要是执行,如下语句
boot2docker.exe init
boot2docker.exe start
boot2docker.exe ssh
所有在命令行下执行 sh start.sh 即可
5. 利用SSH工具管理
在windows命令行进入docker后,不能复制,而且操作也不方便,因此用支持SSH的工具来管理是很好的,比如SECURECRT, PUTTY等,推荐用SECURECRT.&
在命令行下用boot2docker ip 可以查询到IP
默认的用户名和密码是:&docker/tcuser
登录后的界面:&
6. 下载镜像
6.1 下载地址
选择下载&ubuntu-14.04-x86_64.tar.gz
6.2 用FTP工具上传tar包
推荐使用:FileZilla&
命令:cat ubuntu-14.04-x86_64.tar.gz |docker import - ubuntu:ubuntu14&
速度非常快,大概10几秒就完成了。
6.4 查看镜像
查看:&docker images&
运行:docker run -i -t ubuntu:ubuntu14 /bin/bash
可以开始DOCKER旅行了。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:183414次
积分:2270
积分:2270
排名:第10865名
原创:78篇
转载:15篇
评论:26条
(4)(2)(4)(1)(2)(3)(1)(2)(7)(2)(6)(1)(8)(1)(3)(5)(3)(1)(1)(3)(1)(2)(3)(2)(2)(2)(1)(3)(1)(1)(2)(3)(2)(7)& & & & 在上文中,笔者通过分析Docker的架构,初步作了Docker的架构图。架构图本身更多的出于笔者的理解,为了便于理解,对于Docker代码本身做了一些抽象,例如Server的运行都是以一个Job的形式存在的,而架构图中并未明显的表明这一点。Docker模块简介& & & && & & &本文将从源码的角度分析Docker的启动,主要是作为一个daemon进程的启动。在这之前,需要先清晰Docker内部最主要的几个概念:Daemon,Engine以及Job。Daemon& & & && & & & Daemon可以认为是Docker守护进程的载体。从源码的视角来看,Daemon可以认为是Daemon结构体,以及Daemon package中定义的一系列方法的总和。同时Daemon也是Docker内部的一个结构体,从结构体的定义,可以看出Daemon关联了Docker的绝大部分的内容,Daemon结构的定义如下:type Daemon struct { repository
string sysInitPath
string containers
*contStore graph
*graph.Graph repositories
*graph.TagStore idIndex
*truncindex.TruncIndex sysInfo
*sysinfo.SysInfo volumes
*graph.Graph eng
*engine.Engine config
*Config containerGraph *graphdb.Database driver
graphdriver.Driver execDriver
execdriver.Driver}& & & &以下简要介绍结构体内部每个对象:repository & & & & & &所有container的目录的父目录sysInitPath & & & & & sysInit的路径containers & & & & & &所有container的存储记录graph & & & & & & & & & 关于image的graph信息repositories & & & & Graph的存储库idIndex & & & &sysInfo & & & & & & & & Docker所处host的系统信息volumes & & & & & & & 宿主机上且在容器根目录外的一些目录,可以挂载至容器内部eng & & & & & & & & & & & Docker内部所有Job的执行引擎config & & & & & & & & & Docker所需要的配置信息containerGraph & &GraphDB对象,用于graph信息的存取driver & & & & & & & & & &有关image的存储的graph驱动execDriver & & & & & 有关容器运行与管理的操作驱动& & & &&由于介绍繁杂的Docker内属性不是本文的目的,故不再赘述。Engine& & & && & & &在源代码中关于Engine的介绍非常确切:Engine是整个Docker的核心部分,它扮演所有Docker Container的存储仓库的角色,并且通过执行Job来实现操纵这些容器。& & & &Engine的结构体定义如下:type Engine struct { handlers
map[string]Handler catchall
Handler hack
Hack // data for temporary hackery (see hack.go) id
string Stdout
io.Writer Stderr
io.Writer Stdin
io.Reader Logging
bool tasks
sync.WaitGroup l
sync.RWMutex // lock for shutdown shutdown
bool onShutdown []func() // shutdown handlers}& & & &其中需要特别注意的就是handlers属性,该属性为一个map类型的对象,存储的都是关于某个特定handler的处理方法,之后会详细分析handler。Job& & & & 关于Job的定义,源码中注释如此说道:在Docker的engine中,Job是最基本基本工作单位。Docker可以做的所有工作,最终都必须表示成一个Job。例如:在容器内执行一个进程,这是一个Job;创建一个新容器,这是一个Job;从Internet上下载一份文档,这是一个Job;服务于HTTP的API,这也是一个Job,等等。Job的定义源码如下:type Job struct { Eng
*Engine Name
string Args
[]string env
*Env Stdout
*Output Stderr
*Output Stdin
*Input handler Handler status
Status end
time.Time}& & & & 同时,Job的API设计得很像一个unix的进程:比如说,Job有一个名称,有参数,有环境变量,有标准的输入输出,有错误处理,有返回状态,其中返回0表示执行成功,返回其他数字表示错误。Docker的启动& & & & Docker的启动可以认为是通过Docker的可执行文件,启动一个Docker的守护进程,这个守护进程在启动过程中完成了启动所需要的所有工作,并且最终作为一个server可以为docker client发送来的众多请求服务。& & & & 以下从源码的角度分析Docker的启动。& & & & 首先,Docker的main函数位于./docker/docker.go中。执行流程如下。reexec、flag解析与判断& & & &&& & & & 在main函数中,首先执行了以下内容: if reexec.Init() {
return } flag.Parse() // FIXME: validate daemon flags here if *flVersion {
showVersion()
return } if *flDebug {
os.Setenv(&DEBUG&, &1&) }& & & & 首先判断reexec.Init()的返回值,若为真,直接返回;否则进行flag.Parse()方法,该方法主要解析了flag参数,并为之后的flag参数判断做准备。众多的flag参数位于./docker/flag.go中,并且在main函数执行之前就完成var的定义以及init函数的执行。解析玩flag参数之后,随即判断众参数,若*flVersion为真的话,直接通过showVersion()方法显示Docker的版本号,随后返回;若*flVersion不为真,则继续往下判断,若*flDebug为真,对于DEBUG环境变量设置值为1,继续往下执行。flHost信息的获取& & & & 接着的代码执行如下: if len(flHosts) == 0 {
defaultHost := os.Getenv(&DOCKER_HOST&)
if defaultHost == && || *flDaemon {
// If we do not have a host, default to unix socket
defaultHost = fmt.Sprintf(&unix://%s&, api.DEFAULTUNIXSOCKET)
defaultHost, err := api.ValidateHost(defaultHost)
if err != nil {
log.Fatal(err)
flHosts = append(flHosts, defaultHost) }& & & & 以上代码的功能主要是查找host地址,作为之后server的监听地址。如果在flag的定义以及初始化之后,flHost的长度依旧为0的话,则说明配置中没有设定Host地址,需要程序自行查找。首先,通过宿主机环境变量中的DOCKER_HOST来给默认host变量defaultHost赋值,如果仍然为空,或者*flDaemon为真的话,通过api定义的DEFAULTDAEMON属性来初始化defaultHost,默认为一个unix socket。经过验证该defaultHost之后,将defaultHost添加至flHost末尾。& & & & 以上可以认为是为Docker daemon的运行做了充足的准备工作,以下的代码真正在做Docker Daemon的启动。 if *flDaemon {
mainDaemon()
return }& & & & 也就是说若*flDaemon为真,则直接运行mainDaemon()方法。以下将大篇幅分析介绍mainDaemon()所做的工作。mainDaemon()& & & & mainDaemon()的实现位于文件./docker/daemon.go中。flag.Narg()&&&&&&& 首先,Daemon执行flag.NArg(),当flag参数被处理后,已经没有其他的参数时,继续往下执行。创建engine并捕获shutdown&&&&&&& Daemon创建一个engine,并随时捕获engine的shutdown信号。加载builtins&&&&&&& 加载builtins,代码为builtins.Register(eng),进入./docker/builtins.go,在该方法中主要包含了五个步骤,如下:func Register(eng *engine.Engine) error { if err := daemon(eng); err != nil {
return err } if err := remote(eng); err != nil {
return err } if err := events.New().Install(eng); err != nil {
return err } if err := eng.Register(&version&, dockerVersion); err != nil {
return err } return registry.NewService().Install(eng)}&&&&&&& 1. daemon(eng) : 所做工作是为engine注册一个handler,具体的handler名称为“init_networkdriver”。具体的功能是初始化Docker环境的docker0网桥,处理方法的实现位于./daemon/networkdriver/bridge/driver.go中的InitDriver.func daemon(eng *engine.Engine) error {
return eng.Register(&init_networkdriver&, bridge.InitDriver)}&&&&&&& 2.remote(eng)& : 所做的工作是为engine注册两个handler,第一个handler的名称为“serveapi”,具体的功能是使得daemon提供RESTful的API,保证daemon可以与外界建立通信,处理方法的实现为./api/server/server.go中的ServeApi;第二个handler的名称为“acceptconnection”,具体的功能是使得初始化完毕的daemon可以接收请求,处理方法的实现为./api/server/server.go中的AcceptConnections。代码如下:func remote(eng *engine.Engine) error {if err := eng.Register(&serveapi&, apiserver.ServeApi); err != nil {return err}return eng.Register(&acceptconnections&, apiserver.AcceptConnections)}&&&&&&& 3.events.New().Install(eng): Docker的event事件的实现,功能是让外界知道Docker内部的events,内部的log以及内部的subscribers_count,具体的job名称分别为“events”,“logs”,subscribers_count“,处理方法的实现为./events/events.go中的Get,Log,SubscribersCount 。// Install installs events public api in docker enginefunc (e *Events) Install(eng *engine.Engine) error { // Here you should describe public interface jobs := map[string]engine.Handler{
&subscribers_count&: e.SubscribersCount, } for name, job := range jobs {
if err := eng.Register(name, job); err != nil {
return err
} } return nil}&&&&&&&& 4.eng.Register(&version&,dockerVersion): Docker的engine注册一个名称为“ version”的handler,处理方法的实现为当前builtins.go文件中的dockerVersion。&&&&&&&& 5.registry.NewService().Install(eng):方法实现位于./registry/service.go,首先先获取Service对象,随后通过Install方法来注册两个handler,第一个的名称为“auth”,实现在公有registry中的认证;第二个的名称为“search”,实现在公有registry中查找image的功能。// Install installs registry capabilities to eng.func (s *Service) Install(eng *engine.Engine) error { eng.Register(&auth&, s.Auth) eng.Register(&search&, s.Search) return nil}加载Daemon&&&&&&&& 以上分析大部分builtins.Register(eng)的实现。回到mainDaemon方法中,即进入一个goroutine,如下:go func() { d, err := daemon.NewDaemon(daemonCfg, eng) if err != nil {
log.Fatal(err) } if err := d.Install(eng); err != nil {
log.Fatal(err) } b := &builder.BuilderJob{eng, d} b.Install() // after the daemon is done setting up we can tell the api to start // accepting connections if err := eng.Job(&acceptconnections&).Run(); err != nil {
log.Fatal(err) }}()&&&&&&& 以上使用一个goroutine来加载daemon,以保证与此同时,可以尽快的运行serveapi的job,以致有些connection来临时不会因为daemon正在加载而得不到相应。NewDaemon()&& &&&& 首先执行的是d, err := daemon.NewDaemon(daemonCfg, eng),作用为创建一个daemon对象,代码实现位于./daemon/daemon.go的NewDaemon方法。在NewDaemon的实现过程中,可以发现具体调用的方法为daemon, err := NewDaemonFromDirectory(config, eng)。在这里,我们可以先来看看该config参数的来历。在加载daemon的goroutine中,NewDaemon的实参为daemonCfg。在./docker/daemon.go中,有daemonCfg = &daemon.Config{},而在该文件中的init()方法中实现了daemonCfg.InstallFlags(),而InstallFlags()的实现位于./docker/daemon/config.go,实现过程中加载了很多需要的配置项,几乎Docker所需要的所有配置信息都在该放啊中实现初始化。&&&&&&&& 这里涉及到了Golang的一个特性,即init()方法的执行。在golang中init()方法的特性如下:init方法用于程序执行前包的初始化工作,比如初始化变量等;每个包可以有多个init方法;包的每一个源文件也可以有多个init方法;同一个包内的init方法的执行顺序没有明确的定义;不同包的init方法按照包导入的依赖关系决定初始化的顺序;init方法不能内调用,而是在main()函数调用前自动被调用。&&&&&&& 了解完config的来历,进入NewDaemonFromDirectory的实现。该方法的实现,可以简易的认为提供以下功能。&&&&&&& 1.验证或配置config参数// Apply configuration defaultsif config.Mtu == 0 { // FIXME: GetDefaultNetwork Mtu doesn't need to be public anymore config.Mtu = GetDefaultNetworkMtu()}// Check for mutually incompatible config optionsif config.BridgeIface != && && config.BridgeIP != && { return nil, fmt.Errorf(&You specified -b & --bip, mutually exclusive options. Please specify only one.&)}if !config.EnableIptables && !config.InterContainerCommunication { return nil, fmt.Errorf(&You specified --iptables=false with --icc=false. ICC uses iptables to function. Please set --icc or --iptables to true.&)}// FIXME: DisableNetworkBidge doesn't need to be public anymoreconfig.DisableNetwork = config.BridgeIface == DisableNetworkBridge// Claim the pidfile first, to avoid any and all unexpected race conditions.// Some of the init doesn't need a pidfile lock - but let's not try to be smart.if config.Pidfile != && { if err := utils.CreatePidFile(config.Pidfile); err != nil {
return nil, err        }        eng.OnShutdown(func() {         // Always release the pidfile last, just in case         utils.RemovePidFile(config.Pidfile) })}&&&&&&& 2.验证系统支持度以及执行用户的权限// Check that the system is supported and we have sufficient privileges// FIXME: return errors instead of calling Fatalif runtime.GOOS != &linux& { log.Fatalf(&The Docker daemon is only supported on linux&)}if os.Geteuid() != 0 { log.Fatalf(&The Docker daemon needs to be run as root&)}if err := checkKernelAndArch(); err != nil { log.Fatalf(err.Error())}&& & & &3.配置或创建Docker所需要的工作路径// set up the TempDir to use a canonical pathtmp, err := utils.TempDir(config.Root)if err != nil { log.Fatalf(&Unable to get the TempDir under %s: %s&, config.Root, err)}realTmp, err := utils.ReadSymlinkedDirectory(tmp)if err != nil { log.Fatalf(&Unable to get the full path to the TempDir (%s): %s&, tmp, err)}os.Setenv(&TMPDIR&, realTmp)if !config.EnableSelinuxSupport { selinuxSetDisabled()}// get the canonical path to the Docker root directoryvar realRoot stringif _, err := os.Stat(config.Root); err != nil && os.IsNotExist(err) { realRoot = config.Root} else { realRoot, err = utils.ReadSymlinkedDirectory(config.Root) if err != nil {
log.Fatalf(&Unable to get the full path to root (%s): %s&, config.Root, err) }}config.Root = realRoot// Create the root directory if it doesn't existsif err := os.MkdirAll(config.Root, 0700); err != nil && !os.IsExist(err) { return nil, err}&&&&&&& 4.设置以及加载多种driver// Set the default drivergraphdriver.DefaultDriver = config.GraphDriver// Load storage driverdriver, err := graphdriver.New(config.Root, config.GraphOptions)if err != nil { return nil, err}log.Debugf(&Using graph driver %s&, driver)// As Docker on btrfs and SELinux are incompatible at present, error on both being enabledif config.EnableSelinuxSupport && driver.String() == &btrfs& { return nil, fmt.Errorf(&SELinux is not supported with the BTRFS graph driver!&)}&&&&&&& 5.创建Docker Image所需要的graph,graphdb,volumns等log.Debugf(&Creating images graph&)g, err := graph.NewGraph(path.Join(config.Root, &graph&), driver)if err != nil { return nil, err}// We don't want to use a complex driver like aufs or devmapper// for volumes, just a plain filesystemvolumesDriver, err := graphdriver.GetDriver(&vfs&, config.Root, config.GraphOptions)if err != nil { return nil, err}log.Debugf(&Creating volumes graph&)volumes, err := graph.NewGraph(path.Join(config.Root, &volumes&), volumesDriver)if err != nil { return nil, err}log.Debugf(&Creating repository list&)repositories, err := graph.NewTagStore(path.Join(config.Root, &repositories-&+driver.String()), g)if err != nil { return nil, fmt.Errorf(&Couldn't create Tag store: %s&, err)……graphdbPath := path.Join(config.Root, &linkgraph.db&)    graph, err := graphdb.NewSqliteConn(graphdbPath)    if err != nil {        return nil, err} &&&&&&& 6.关于dockerinit的一系列操作localCopy := path.Join(config.Root, &init&, fmt.Sprintf(&dockerinit-%s&, dockerversion.VERSION))sysInitPath := utils.DockerInitPath(localCopy)if sysInitPath == &&
return nil, fmt.Errorf(&Could not locate dockerinit: This usually means docker was built incorrectly. See /contributing/devenvironment for official build instructions.&)}&&&&&&& 7.验证DNS,判断docker container是否可以使用host的resolv.conf文件,若不能的话,使用默认的外界DNS server:“8.8.8.8”和“8.8.4.4”;if err := daemon.checkLocaldns(); err != nil { return nil, err}&&&&&&& 8.重新加载之前的docker container。例如,当Docker进程重启后,会restore之前运行着的docker container。if err := daemon.restore(); err != nil { return nil, err}&&&&&&& 9.最终返回daemon对象return daemon, nil&&&&&& 以上的9个步骤执行完NewDaemonFromDirectory之后,在goroutine之间执行d.Install(eng),该方法的实现位于./daemon/daemon.go中的Install方法,功能是为engine注册众多的handler,handler的actions位于./daemon/下的众多go文件中。例如有以下{&create&:&&&&&&&&&&& daemon.ContainerCreate}handler,则当job的名称为create时,运行时的action为daemon.ContainerCreate, 位于./daemon/create.go。builderJob.Install()&&&&&&& 随后执行代码为:b := &builder.BuilderJob{eng, d}b.Install()&&&&&& &&&&&&& 这部分内容的功能为注册build的handler,位于./builder/job.go文件中,job的名称为“build”,处理方法为CmdBuild具体实现如下:func (b *BuilderJob) Install() { b.Engine.Register(&build&, b.CmdBuild)}eng.Job(&acceptconnections&).Run()&&&&&&& goroutine的最后一个步骤就是开始执行接收请求,即执行名称为“acceptconnections”的job,处理方法为./api/server/server.go中的AcceptConnections。&&&&&&& 以上的部分,即表示goroutine的运行流程,即加载daemon的运行流程。&&&& eng.Job(&serveapi&, flHosts...).Run()&&&&&&& 在goroutine运行的同时,mainDaemon同时还在执行名称为“serveapi“的job,代码如下: // Serve api job := eng.Job(&serveapi&, flHosts...) job.SetenvBool(&Logging&, true) job.SetenvBool(&EnableCors&, *flEnableCors) job.Setenv(&Version&, dockerversion.VERSION) job.Setenv(&SocketGroup&, *flSocketGroup) job.SetenvBool(&Tls&, *flTls) job.SetenvBool(&TlsVerify&, *flTlsVerify) job.Setenv(&TlsCa&, *flCa) job.Setenv(&TlsCert&, *flCert) job.Setenv(&TlsKey&, *flKey) job.SetenvBool(&BufferRequests&, true) if err := job.Run(); err != nil {
log.Fatal(err) }&&&&&&& 在创建job的同时,使用到了参数flHost,也就是在mainDaemon之前的获取的flHost。由于在./builtins/builtins.go中注册了名称为“serveapi”的handler,所以只要运行相应的处理方法即可,为./api/server/server.go中的ServeApi方法。总结& & & & 以上的分析都是假设flDaemon为真,那样的话Docker的启动流程就结束了。& & & & 由于Docker中所有关于container以及image等工作都必须暴露为一个job,因此Docker启动的完毕标志,可以认为是Docker完成server的启动,并最终为通过api来访问的请求进行服务。通过server来代理请求,并最终分发到相应的job上来执行。& & & & 在Docker整个启动过程中,笔者认为最为重要,最为核心的部分为NewDaemonFromDirectory的实现,该部分配置了众多Daemon结构内部的属性,而这些属性在之后,都会涉及到很多实际操作container以及graph的工作,换言之,daemon保留了其他模块的访问接口。& & & & 因此,在Docker内部,运行靠engine,执行靠job,访问driver等靠daemon。转载请注明出处。本文更多出于我本人的理解,肯定在一些地方存在不足和错误。希望本文能够对接触Docker的人有些帮助,如果你对这方面感兴趣,并有更好的想法和建议,也请联系我。我的邮箱:shlallen@新浪微博:构建一个高可用及自动发现的Docker基础架构
发表于 18:00|
来源/post/242/|
作者刘天斯
摘要:Docker的发展前景一片大好。但在企业当中,如何选择适合自己的Docker构建方案?下面详细介绍如何使用Haproxy+etcd+confd构建一个高可用及自动发现的Docker基础架构。
Docker的生态日趋成熟,开源社区也不断孵化出优秀的周边项目,覆盖网络、监控、维护、部署、开发等方面。帮助开发、运维人员快速构建、运营Docker服务环境,其中也不乏有大公司的影子,如Google、IBM、Redhat,甚至微软也宣称后续将提供Docker在Windows平台的支持。Docker的发展前景一片大好。但在企业当中,如何选择适合自己的Docker构建方案?可选的方案有kubernetes与CoreOS(都已整合各类组件),另外一种方案为Haproxy+etcd+confd,采用松散式的组织结构,但各个组件之间的通讯是非常严密的,且扩展性更强,定制也更加灵活。下面详细介绍如何使用Haproxy+etcd+confd构建一个高可用及自动发现的Docker基础架构。
作者简介:刘天斯,目前就职于腾讯-互动娱乐部(高级工程师),曾就职于天涯社区,担任架构师/系统管理员,热衷开源技术的研究,包括系统架构、运维开发、负载均衡、缓存技术、数据库、分布式存储及云计算等领域,擅长大规模集群的运维工作。关注互联网技术发展动向,努力紧靠技术前沿。充当一名普通的传播者和分享者。
著有《python自动化运维:技术与实践》、《Docker技术与实践》(预计2015年5月出版)。
一、架构优势
约定由Haproxy+etcd+confd+Docker构建的基础服务平台简称“HECD” 架构,整合了多种开源组件,看似松散的结构,事实上已经是一个有机的整体,它们互相联系、互相作用,是Docker生态圈中最理想的组合之一,具有以下优势:
自动、实时发现及无感知服务刷新;&
支持任意多台Docker主宿机;&
支持多种APP接入且打散至不分主宿机;&
采用Etcd存储信息,集群支持可靠性高;&
采用Confd配置引擎,支持各类接入层,如Nginx;&
支持负载均衡、故障迁移;&
具备资源弹性,伸缩自如(通过生成、销毁容器实现);&
二、架构说明
在HECD架构中,首先管理员操作Docker Client,除了提交容器(Container)启动与停止指令外,还通过REST-API方式向Etcd(K/V)存储组件注册容器信息,包括容器名称、主宿机IP、映射端口等。Confd配置组件会定时查询Etcd组件获取最新的容器信息,根据定义好的配置模板生成Haproxy配置文件Haproxy.cfg,并且自动reload
haproxy服务。用户在访问业务服务时,完全没有感知后端APP的上线、下线、切换及迁移,达到了自动发现、高可用的目的。详细架构图见图1-1。
图1-1 平台架构图
为了方便大家理解各组件间的关系,通过图1-2进行架构流程梳理,首先管理员通过Shell或API操作容器,下一步将容器信息注册到Etcd组件,Confd组件会定时查询Etcd,获取已经注册到Etcd中容器信息,最后通过Confd的模板引擎生成Haproxy配置,整个流程结束。
图1-2架构流程图
了解架构流程后,我们逐一对流程中各组件进行详细介绍。
1、Etcd介绍
Etcd是一个高可用的 Key/Value 存储系统,主要用于分享配置和服务发现。
简单:支持 curl 方式的用户 API (HTTP+JSON)&
安全:可选 SSL 客户端证书认证&
快速:单实例可达每秒 1000 次写操作&
可靠:使用 Raft 实现分布式
2、Confd介绍
Confd是一个轻量级的配置管理工具。通过查询Etcd,结合配置模板引擎,保持本地配置最新,同时具备定期探测机制,配置变更自动reload。
3、Haproxy介绍
HAProxy是提供高可用性、负载均衡以及基于TCP和HTTP应用的代理,支持虚拟主机,它是免费、快速并且可靠的一种解决方案。(来源百科)
三、架构部署
平台环境基于Centos6.5+Docker1.2构建,其中Etcd的版本为etcd version 0.5.0-alpha,Confd版本为confd
0.6.2,Haproxy版本为HA-Proxy version 1.4.24。下面对平台的运行环境、安装部署、组件说明等进行详细说明,环境设备角色表如下:
1、组件安装
1.1 Docker安装
&SSH终端登录192.168.1.22服务器,执行以下命令:
# yum -y install docker-io &
# service docker start &
# chkconfig docker on &
1.2 Haproxy、confd安装
SSH终端登录192.168.1.20服务器,执行以下命令:
1、haproxy &
# yum –y install haproxy &
2、confd &
# mv confd /usr/local/bin/confd &
# chmod +x /usr/local/bin/confd &
# /usr/local/bin/confd -version &
confd 0.6.2 &
1.3 Etcd安装&&&
&SSH终端登录192.168.1.21服务器,执行以下命令:
# yum -y install golang &
# mkdir -p /home/install && cd /home/install &
# git clone
# cd etcd &
# ./build &
# cp bin/etcd /bin/etcd &
# /bin/etcd -version &
etcd version 0.5.0-alpha &
2、组件配置&&&
2.1 Etcd配置
由于etcd是一个轻量级的K/V存储平台,启动时指定相关参数即可,无需配置。&
# /bin/etcd -peer-addr 192.168.1.21:7001 -addr 192.168.1.21:4001 -data-dir /data/etcd -peer-bind-addr 0.0.0.0:7001 -bind-addr 0.0.0.0:4001 &
由于etcd具备多机支持,参数“-peer-addr”指定与其它节点通讯的地址;参数“-addr”指定服务监听地址;参数“-data-dir”为指定数据存储目录。&
由于etcd是通过REST-API方式进行交互,常见操作如下:&&&
1) 设置(set) key操作&
-XPUT -d value="this is awesome"
{"action":"set","node":{"key":"/mykey","value":"this is awesome","modifiedIndex":28,"createdIndex":28}}&
获取(get) key信息
&&{"action":"get","node":{"key":"/mykey","value":"this
is awesome","modifiedIndex":28,"createdIndex":28}} &
删除key信息&
-XDELETE & & &
& {"action":"delete","node":{"key":"/mykey","modifiedIndex":29,"createdIndex":28},"prevNode":{"key":"/mykey","value":"this
is awesome","modifiedIndex":28,"createdIndex":28}} &&更多操作API见/coreos/etcd/blob/master/Documentation/api.md。&
2.2 Confd+Haproxy配置&&
由于Haproxy的配置文件是由Confd组件生成,要求Confd务必要与haproxy安装在同一台主机上,Confd的配置有两种,一种为Confd资源配置文件,默认路径为“/etc/confd/conf.d”目录,另一种为配置模板文件,默认路径为“/etc/confd/templates”。具体配置如下:&
创建配置文件目录&
# mkdir -p /etc/confd/{conf.d,templates} (1)配置资源文件
详细见以下配置文件,其中“src”为指定模板文件名称(默认到路径/etc/confd/templates中查找);“dest”指定生成的Haproxy配置文件路径;“keys”指定关联Etcd中key的URI列表;“reload_cmd”指定服务重载的命令,本例中配置成haproxy的reload命令。&
【/etc/confd/conf.d/ haproxy.toml】
[template]
src = "haproxy.cfg.tmpl"
dest = "/etc/haproxy/haproxy.cfg"
"/app/servers",
reload_cmd = "/etc/init.d/haproxy reload"
(2)配置模板文件&&
Confd模板引擎采用了Go语言的文本模板,更多见http://golang.org/pkg/text/template/,具备简单的逻辑语法,包括循环体、处理函数等,本示例的模板文件如下,通过range循环输出Key及Value信息。
【/etc/confd/templates/haproxy.cfg.tmpl】
log 127.0.0.1 local3
maxconn 5000
log 127.0.0.1 local3
option dontlognull
option redispatch
maxconn 2000
contimeout
clitimeout
srvtimeout
listen frontend 0.0.0.0:80
balance roundrobin
maxconn 2000
option forwardfor
{{range gets "/app/servers/*"}}
server {{base .Key}} {{.Value}} check inter 5000 fall 1 rise 2
stats enable
stats uri /admin-status
stats auth admin:123456
stats admin if TRUE
(3)模板引擎说明&&
本小节详细说明Confd模板引擎基础语法与示例,下面为示例用到的KEY信息。&
# curl -XPUT
value="192.168.1.22:49156" &
# curl -XPUT
-d value="192.168.1.22:49158"
# curl -XPUT
-d value="192.168.1.22:49160"
# curl -XPUT
-d value="192.168.1.22:49162"1、base
作为path.Base函数的别名,获取路径最后一段。
{{ with get "/app/servers/prickly_blackwell"}}
&server {{base .Key}} {{.Value}} check
prickly_blackwell 192.168.1.22:49162
返回一对匹配的KV,找不到则返回错误。&&&
{{with get "/app/servers/prickly_blackwell"}}
&key: {{.Key}}
&value: {{.Value}}
/app/servers/prickly_blackwell 192.168.1.22:49162
{{range gets "/app/servers/*"}}
&{{.Key}} {{.Value}}
/app/servers/backstabbing_rosalind 192.168.1.22:49156
/app/servers/cocky_morse 192.168.1.22:49158
/app/servers/goofy_goldstine 192.168.1.22:49160
app/servers/prickly_blackwell 192.168.1.22:49162
返回一个匹配key的字符串型Value,找不到则返回错误。&&
{{getv "/app/servers/cocky_morse"}}
192.168.1.22:49158
返回所有匹配key的字符串型Value,找不到则返回错误。
&{{range getvs "/app/servers/*"}}
& value: {{.}}
value: 192.168.1.22:49156
value: 192.168.1.22:49158
value: 192.168.1.22:49160
value: 192.168.1.22:49162
对输入的字符串做split处理,即将字符串按指定分隔符拆分成数组。
&{{ $url := split (getv "/app/servers/cocky_morse") ":" }}
&host: {{index $url 0}}
&port: {{index $url 1}}
host: 192.168.1.22
port: 49158
返回所有的字符串型子key,找不到则返回错误。
{{range ls "/app/servers/"}}
& subkey: {{.}}
subkey: backstabbing_rosalind
subkey: cocky_morse
subkey: goofy_goldstine
subkey: prickly_blackwell
返回所有的字符串型子目录,找不到则返回一个空列表。
{{range lsdir "/app/"}}
& subdir: {{.}}
subdir: servers (4)启动confd及haproxy服务
下面为启动Confd服务命令行,参数“interval”为指定探测etcd的频率,单位为秒,参数“-node”为指定etcd监听服务主地址,以便获取容器信息。
# /usr/local/bin/confd -verbose -interval 10 -node '192.168.1.21:; -confdir /etc/confd & /var/log/confd.log &
# /etc/init.d/haproxy start &
3、容器配置
前面HECD架构说明内容,有讲到容器的操作会即时注册到etcd组件中,是通过curl命令进行REST-API方式提交的,下面详细介绍通过SHELL及Python-api两种方式的实现方法,支持容器启动、停止的联动。
3.1、SHELL实现方法
实现的原理是通过获取“Docker run ***”命令输出的Container ID,通过“docker inspect Container
ID”得到详细的容器信息,分析出容器服务映射的外部端口及容器名称,将以“/app/servers/容器名称”作为Key,“主宿机: 映射端口”作为Value注册到etcd中。其中Key信息前缀(/app/servers)与“/etc/confd/conf.d/haproxy.toml”中的keys参数是保持一致的。&
【docker.sh】&
#!/bin/bash &
if [ -z $1 ]; then &
& & & & echo "Usage: c run &image name&:&version&"
& & & & echo " & & & c stop &container
& & & & exit 1 &
if [ -z $ETCD_HOST ]; then &
& ETCD_HOST="192.168.1.21:4001" &
if [ -z $ETCD_PREFIX ]; then &
& ETCD_PREFIX="app/servers" &
if [ -z $CPORT ]; then &
& CPORT="80" &
if [ -z $FORREST_IP ]; then &
& FORREST_IP=`ifconfig eth0| grep "inet addr" | head -1 | cut -d
: -f2 | awk '{print $1}'` &
function launch_container { &
& & & & echo "Launching $1 on $FORREST_IP ..." &
& & & & CONTAINER_ID=`docker run -d --dns 172.17.42.1
-P -v /data:/data -v /etc/httpd/conf:/etc/httpd/conf -v /etc/httpd/conf.d:/etc/httpd/conf.d
-v /etc/localtime:/etc/localtime:ro $1 /bin/sh -c "/usr/bin/supervisord
-c /etc/supervisord.conf"` &
& & & & PORT=`docker inspect $CONTAINER_ID|grep "\"Ports\""
-A 50|grep "\"$CPORT/tcp\"" -A 3| grep HostPort|cut -d '"' -f4|head -1`
& & & & NAME=`docker inspect $CONTAINER_ID | grep
Name | cut -d '"' -f4 | sed "s/\///g"|sed -n 2p` &
& & & & echo "Announcing to $ETCD_HOST..." &
& & & & curl -XPUT "http://$ETCD_HOST/v2/keys/$ETCD_PREFIX/$NAME"
-d value="$FORREST_IP:$PORT" &
& & & & echo "$1 running on Port $PORT with name $NAME"
function stop_container { &
& & & & echo "Stopping $1..." &
& & & & CONTAINER_ID=`docker ps -a| grep $1 | awk
'{print $1}'` &
& & & & echo "Found container $CONTAINER_ID" &
& & & & docker stop $CONTAINER_ID &
& & & & curl -XDELETE
&& /dev/null &
& & & & echo "Stopped." &
if [ $1 = "run" ]; then &
& launch_container $2 &
& stop_container $2 &
fi &docker.sh使用方法:
&1) 启动一个容器
&# ./docker.sh run yorko/webserver:v3(镜像)&&&
&2) 停止一个容器
&# ./docker.sh stop berserk_hopper(容器名)
3.2、Docker-py API实现方法
通过Python语言调用Docker-py的API实现容器的远程操作(创建、运行、停止),并结合python-etcd模块对etcd进行操作(set、delete),达到与SHELL方式一样的效果,很明显,Docker-py方式更加容易扩展,可以无缝与现有运营平台对接。
为兼顾到远程API支持,需对docker启动文件“exec”处进行修改,详细见如下:
# vi /etc/init.d/docker&
$exec -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock -d &&& $logfile & &
启动容器的程序如下:
【docker_run.py】
#!/usr/local/Python/bin/python
import docker
import etcd
import sys
Etcd_ip="192.168.1.21"
Server_ip="192.168.1.22"
App_port="80"
App_protocol="tcp"
Image="yorko/webserver:v3"
c = docker.Client(base_url='tcp://'+Server_ip+':;,version='1.14',timeout=15)
except Exception,e:
print "Connection docker server error:"+str(e)
sys.exit()
rinfo=c.create_container(image=Image,stdin_open=True,tty=True,command="/usr/bin/supervisord -c /etc/supervisord.conf",volumes=['/data','/etc/httpd/conf','/etc/httpd/conf.d
','/etc/localtime'],ports=[80,22],name=None)
containerId=rinfo['Id']
except Exception,e:
print "Create docker container error:"+str(e)
sys.exit()
c.start(container=containerId, binds={'/data':{'bind': '/data','ro': False},'/etc/httpd/conf':{'bind': '/etc/httpd/conf','ro': True},'/etc/httpd/conf.d':{'bind': '/etc/htt
pd/conf.d','ro': True},'/etc/localtime':{'bind': '/etc/localtime','ro': True}}, port_bindings={80:None,22:None}, lxc_conf=None,publish_all_ports=True, links=None, privileged=F
alse,dns='172.17.42.1', dns_search=None, volumes_from=None, network_mode=None,restart_policy=None, cap_add=None, cap_drop=None)
except Exception,e:
print "Start docker container error:"+str(e)
sys.exit()
idict=c.inspect_container(containerId)
Name=idict["Name"][1:]
skey=App_port+'/'+App_protocol
for _key in idict["NetworkSettings"]["Ports"].keys():
if _key==skey:
Port=idict["NetworkSettings"]["Ports"][skey][0]["HostPort"]
except Exception,e:
print "Get docker container inspect error:"+str(e)
sys.exit()
if Name!="" and Port!="":
client = etcd.Client(host=Etcd_ip, port=4001)
client.write('/app/servers/'+Name, Server_ip+":"+str(Port))
print Name+" container run success!"
except Exception,e:
print "set etcd key error:"+str(e)
print "Get container name or port error."
停止容器的程序如下:
【docker_stop.py】
#!/usr/local/Python/bin/python
import docker
import etcd
import sys
Etcd_ip="192.168.1.21"
Server_ip="192.168.1.22"
containerName="grave_franklin" #指定需要停止容器的名称
c = docker.Client(base_url='tcp://'+Server_ip+':;,version='1.14',timeout=10)
c.stop('furious_heisenberg')
except Exception,e:
print str(e)
sys.exit()
client = etcd.Client(host=Etcd_ip, port=4001)
client.delete('/app/servers/'+containerName)
print containerName+" container stop success!"
except Exception,e:
print str(e)
注意:由于容器是无状态的,尽量让其以松散的形式存在,映射端口选项要求使用“-P”参数,即使用随机端口的模式,减少人手干预。&
四、业务上线
HECD架构已部署完毕,接下来就是让其为我们服务,案例中使用的镜像“yorko/webserver:v3”为已经构建好的LAMP平台。类似的镜像也可以在docker-pub中下载到,开始跑起,运行dockery.sh创建两个容器:
# ./docker.sh run yorko/webserver:v3
Launching yorko/webserver:v3 on 192.168.1.22 ...
Announcing to 192.168.1.21:4001...
{"action":"set","node":{"key":"/app/servers/berserk_hopper","value":"192.168.1.22:49170","modifiedIndex":33,"createdIndex":33}}
yorko/webserver:v3 running on Port 49170 with name berserk_hopper
# ./docker.sh run yorko/webserver:v3
Launching yorko/webserver:v3 on 192.168.1.22 ...
Announcing to 192.168.1.21:4001...
{"action":"set","node":{"key":"/app/servers/lonely_meitner","value":"192.168.1.22:49172","modifiedIndex":34,"createdIndex":34}}
yorko/webserver:v3 running on Port 49172 with name lonely_meitner
访问Haproxy监控地址:http://192.168.1.20/admin-status,刚创建的容器已经添加到haproxy中,见图1-3。
图1-3 Haproxy监控后台截图
1)观察Haproxy的配置文件(更新部分):
# vi /etc/haproxy/haproxy.cfg
listen frontend 0.0.0.0:80
balance roundrobin
maxconn 2000
option forwardfor
server berserk_hopper 192.168.1.22:49170 check inter 5000 fall 1 rise 2
server lonely_meitner 192.168.1.22:49172 check inter 5000 fall 1 rise 2
2)访问php测试文件http://192.168.1.20/info.php
图1-4 php测试文件截图
从图1-4可以看出,获取的服务器端IP为容器本身的IP地址(172.17.0.11),在System环境变量处输出容器名为“598cf10a50a2”的信息。&
相关参考:
原文链接:
(责编:周小璐)
如需要了解更多Docker相关的资讯或是技术文档可访问;如有更多的疑问请在提出,我们会邀请专家回答。CSDN Docker技术交流QQ群:。
全国大数据创新项目评选活动目前正在如火如荼进行中,详情点击。
Data Technology Conference 2014,BDTC 2014)将于日-14日在北京新云南皇冠假日酒店召开。传承自2008年,历经七届沉淀,“中国大数据技术大会”是目前国内最具影响、规模最大的大数据领域技术盛会。本届会议,你不仅可以了解到Apache
Hadoop提交者Uma Maheswara Rao G(兼项目管理委员会成员)、Yi Liu,以及Apache Hadoop和Tez项目管理委员会成员Bikas
Saha等分享的通用大数据开源项目的最新成果和发展趋势,还将斩获来自腾讯、阿里、Cloudera、LinkedIn、网易等机构的数十场干货分享。&当下门票团购还有些许优惠,
免费订阅“CSDN大数据”微信公众号,实时了解最新的大数据进展!
CSDN大数据,专注大数据资讯、技术和经验的分享和讨论,提供Hadoop、Spark、Impala、Storm、HBase、MongoDB、Solr、机器学习、智能算法等相关大数据观点,大数据技术,大数据平台,大数据实践,大数据产业资讯等服务。
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章

我要回帖

更多关于 docker pull 失败 的文章

 

随机推荐