部署内网Docker Registry

目前docker 已经分为社区版 (docker CE)和 商业版(docker EE),最新的版本由原来的1.13直接跳到了17.06,目前由于17.06的刚刚发布,在使用Docker 的时候可以根据自己的需求选择相应的版本。

Docker 安装

先移除其他非官方的版本:

yum -y remove docker docker-common container-selinux
yum -y remove docker-selinux

添加yum源,这里选择1.13的版本:

yum install -y yum-utils
yum-config-manager --add-repo https://docs.docker.com/v1.13/engine/installation/linux/repo_files/centos/docker.repo

对yum仓库快速缓存:

 yum makecache fast

安装docker:

yum -y install docker-engine-1.13.1

如果对版本有特殊要求,这里可以使用如下命令,列出可选的版本信息,然后指定版本安装:

yum list docker-engine.x86_64  --showduplicates |sort -r

在启动的配置文件中添加国内的镜像仓库:

 vim /usr/lib/systemd/system/docker.service
...
ExecStart=/usr/bin/dockerd --registry-mirror https://qxx96o44.mirror.aliyuncs.com
...

启动docker:

systemctl start docker

Docker Registry

我们可以使用docker registry 作为我们的私有镜像仓库,当本地制作好镜像后,可以直接上传到镜像仓库中,方便其他主机拉取镜像。

在生产环境中,官方建议使用权威的CA证书,如果我们可以申请到公共的CA证书,就可以部署我们的共有镜像仓库。

也可以通过创建私有的证书,在需要访问仓库的主机上添加认证即可。

由于目前都是内部使用,加上服务器权限控制非常严格,所以这里直接使用免CA证书的方式(官方强烈不推荐)

修改registry配置为免CA模式,指定Registry服务器的域名或者IP地址,并指定访问端口(端口可任意,和registry容器端口映射上即可)

创建daemon.json:

 vim /etc/docker/daemon.json
{
  "insecure-registries" : ["192.168.60.18:5000"]
}

重启docker 服务:

systemctl restart docker

配置用户密码

mkdir auth

下载registry镜像,并配置账户密码:

docker run  --entrypoint htpasswd registry -Bbn trying 123123 > auth/htpasswd

这个命令会拉取registry镜像,以htpasswd的方式对密码进行加密,指定用户 trying 和密码123123,并将密码存在指定文件中。

启动容器仓库:

docker run -d -p 5000:5000 --restart=always --name registry_docker -v `pwd`/auth:/auth -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" registry:2

指定容器的映射端口,容器启动时应用自动启动,指定一个数据卷,挂载验证文件htpasswd,并指定验证信息。

使用账号登录仓库,输入账号密码:

docker login 192.168.60.18:5000

推送本地镜像到仓库:

docker tag 256ab8c63c04 192.168.60.18:5000/self-registry:v1
docker push 192.168.60.18:5000/self-registry:v1

其他主机下载镜像:

在另外一台需要获取镜像的主机上配置registry为无CA模式:

 vim /etc/docker/daemon.json
{
  "insecure-registries" : ["192.168.60.18:5000"]
}

启动docker, 登录:

docker login 192.168.60.18:5000
docker pull 192.168.60.18:5000/self-registry:v1

拉取成功:

# docker images
REPOSITORY                         TAG                 IMAGE ID            CREATED             SIZE
192.168.60.18:5000/self-registry   v1                  256ab8c63c04        About an hour ago   401 MB

补充说明:

1、内网的其他主机如果需要从本地仓库拉取镜像,都需要添加daemon.json的文件。

2、在registry的容器中,我们可以看到挂载的宿主机磁盘信息:

 # df -h
Filesystem                Size      Used Available Use% Mounted on
overlay                  80.0G      3.3G     76.7G   4% /
/dev/vda1                80.0G      3.3G     76.7G   4% /auth
/dev/vda1                80.0G      3.3G     76.7G   4% /etc/resolv.conf
/dev/vda1                80.0G      3.3G     76.7G   4% /etc/hostname
/dev/vda1                80.0G      3.3G     76.7G   4% /etc/hosts
/dev/vda1                80.0G      3.3G     76.7G   4% /var/lib/registry

在容器中,镜像的存放位置为/var/lib/registry/docker/registry/v2/repositories/  那么对应的宿主机目录是/var/lib/docker/image/

可以在启动registry时挂载宿主机上指定的目录到容器的/var/lib/registry上。

本文出自 “Trying” 博客,请务必保留此出处http://tryingstuff.blog.51cto.com/4603492/1948328

runC入门

runC入门

这周在旧金山举行的DockerCon大会上, 开放容器项目 和一个运行容器的CLI工具runC同时和大家见面。runC是一个包装的 libcontainer

它是Docker捐献给开放容器项目用来作为参考实现的。这听起来很有意思,尤其是主要的供应商是OCP的一部分,就像rkt之于CoreOS。

让我们签出 runC 源码用它把Docker容器跑起来吧。

安装runC

runC目前不提供二进制源。需要先安装Go语言环境,然后签出runC代码才能编译runC:

bash
mkdir -p ~/golang/src/github.com/opencontainers/
cd ~/golang/src/github.com/opencontainers/
git clone https://github.com/opencontainers/runc
cd ./runc
make
sudo make install

以上这些指令是在Ubuntu14.04机器上运行的,并未在其他环境验证。

现在,你应该编译得到自己的二进制runC了。

运行一个容器

runC的运行级别比Docker更低,因此他只需要镜像文件在文件夹或者包文件中。

我们使用正在运行的Docker容器导出一个打包文件并解压到文件夹中即可。

bash
mkdir ./goapp
cd ./goapp
docker pull geku/go-app:0.1
CONTAINER_ID=$(docker run -d geku/go-app:0.1)
docker export -o go-app.tar $CONTAINER_ID
tar -xf go-app.tar
rm go-app.tar

现在我们的文件中都是容器文件。我们必须先创建 container.json 文件,然后运行它。

bash
{
"version": "0.1",
"os": "linux",
"arch": "amd64",
"processes": [
{
"tty": true,
"user": "daemon",
"args": [
"/app/go-app"
],
"env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm"
],
"cwd": ""
}
],
"root": {
"path": "./",
"readonly": true
},
"cpus": 1.1,
"memory": 128,
"namespaces": [
{
"type": "process"
},
{
"type": "mount"
},
{
"type": "ipc"
}
],
"capabilities": [
"AUDIT_WRITE",
"KILL",
"NET_BIND_SERVICE"
],
"devices": [
"null",
"random",
"full",
"tty",
"zero",
"urandom"
],
"mounts": [
{
"type": "proc",
"source": "proc",
"destination": "/proc",
"options": ""
},
{
"type": "tmpfs",
"source": "tmpfs",
"destination": "/dev",
"options": "nosuid,strictatime,mode=755,size=65536k"
},
{
"type": "devpts",
"source": "devpts",
"destination": "/dev/pts",
"options": "nosuid,noexec,newinstance,ptmxmode=0666,mode=0620,gid=5"
},
{
"type": "tmpfs",
"source": "shm",
"destination": "/dev/shm",
"options": "nosuid,noexec,nodev,mode=1777,size=65536k"
},
{
"type": "mqueue",
"source": "mqueue",
"destination": "/dev/mqueue",
"options": "nosuid,noexec,nodev"
},
{
"type": "sysfs",
"source": "sysfs",
"destination": "/sys",
"options": "nosuid,noexec,nodev"
}
]
}

当运行 runc spec 时,runC工具根据上面的一些配置生成容器。很显然,我们必须在 processes/args

设置过程命令而且要移除 network ,再将 uts 命名空间分享到主机网络。基于以上原因,

我们也应该移除不必要的 hostname 。最后我们还应该设置一下 root path为 ./.

当准备启动一个容器时确保我们的 container.json 存在镜像的root根目录。

bash
cd ./goapp
sudo runc

为了验证容器内部的应用程序是否正在运行,我们可以发送一个HTTP请求:

bash
curl localhost:5000/json
{"hostname":"demo","env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"TERM=xterm","HOME=/usr/sbin"],"num_cpu":4,"go_max_procs":1}

哇,我们的第一个容器使用runC开始运行了!这是一个简单的例子,但演示了如何使用runC运行Docker镜像。

traefik简介

traefik(https://traefik.io/)是一款开源的反向代理与负载均衡工具。它最大的优点是能够与常见的微服务系统直接整合,可以实现自动化动态配置。目前支持Docker, Swarm, Mesos/Marathon, Mesos, Kubernetes, Consul, Etcd, Zookeeper, BoltDB, Rest API等等后端模型。

traefik的具体模型如下:

为什么选择traefik?

事实上在之前我对LB的选择一直更倾向于使用 HAProxy 。但是选择traefik主要是有以下特点让我们决定使用:

  • Golang编写,单文件部署,与系统无关,同时也提供小尺寸Docker镜像。
  • 支持Docker/Etcd后端,天然连接我们的微服务集群。
  • 内置Web UI,管理相对方便。
  • 自动配置ACME(Let’s Encrypt)证书功能。
  • 性能尚可,我们也没有到压榨LB性能的阶段,易用性更重要。

除了这些以外,traefik还有以下特点:

  • Restful API支持。
  • 支持后端健康状态检查,根据状态自动配置。
  • 支持动态加载配置文件和graceful重启。
  • 支持WebSocket和HTTP/2。

除了上面提到的微服务化集群支持,一些AB测试阶段也可以通过frontend的路由特性进行动态分配,当然这些对HAProxy等软件都是标准支持的。

traefik的配置

traefik支持的配置方式支持文件方式进行配置,这个也是比较常见的配置方式,我们这里简单介绍一下。

traefik支持的toml方式进行配置,官方提供了一个 示例的traefik.toml文件 用于演示配置。除此之外,后端服务一般是采用单独文件进行存储,比如演示配置中指定的rules.toml。

具体一个例子,如果我们有两个后端,127.0.0.1:7727,127.0.0.1:7728,我们希望所有的Chrome用户都可以访问127.0.0.1:7727,其它人都访问127.0.0.1:7728,这样这个rules.toml应该如何配置呢?

# rules.toml
[backends]
  [backends.backend1]
    [backends.backend1.servers.server1]
    url = "http://127.0.0.1:7727"
  [backends.backend2]
    [backends.backend2.servers.server1]
    url = "http://127.0.0.1:7728"


[frontends]
  [frontends.frontend1]
  entrypoints = ["http"]
  backend = "backend1"
    [frontends.frontend1.routes.test_1]
    rule = "HeadersRegexp: User-Agent, Chrome"
  [frontends.frontend2]
  entrypoints = ["http"]
  backend = "backend2"

首先定义两个后端服务,每个后端服务可以支持多个服务单元,这里我们只有一个。前端frontends用于匹配请求落到哪个后端服务中。我们这里定义一个规则test_1,设置规则为根据HTTP请求头部正则进行分配:如果UserAgent中包含Chrome字样,则访问到127.0.0.1:7727。匹配的规则方式包含了以下几种方式:

  • Headers / HeaderRegexp : 头部匹配方式,分别对应按值和正则表达式两种方式。
  • Host / HostRegexp : 按照请求主机名进行匹配,与头部信息相似。
  • Method : 按照请求方式区分。
  • Path / PathStrip / PathPrefix / PathPrefixStrip : 按照路径区分后端。

traefik与微服务集群

这个有人已经写过相关的文章了,这里简单推荐一下: Microservices Bliss with Docker and Traefik中文译文

Docker管理工具Shipyard安装配置

shipyard是一个基于Web的Docker管理工具,基于Docker Swarm,支持多主机,可以把多个Docker主机上的容器统一管理;可以查看镜像,下拉镜像;可以管理私有镜像仓库;并提供 RESTful API 等。本文将在两台docker主机上安装配置shipyard并分别在不同的Docker上发布两个MySQL实例,MySQL-dev与MySQL-Online。
环境准备 centos 7 + docker 1.9.1,准备两台。
主节点:docker41,IP:192.168.199.41;
从节点:docker42,IP:192.168.199.42。
1.Shipyard生态介绍
shipyard是由shipyard控制器以及周围生态系统构成,都是以容器封装,以下按照启动顺序进行介绍。
1)RethinkDB
首先启动的就是RethinkDB容器,shipyard采用RethinkDB作为数据库来保存账户,引擎,服务键值以及元信息等信息。
2)Discovery
为了使用Swarm的选举机制,我们需要一个外部的密钥值存储容器,shipyard默认采用了etcd
3)shipyard_certs
证书管理容器,实现证书验证功能
4)Proxy
默认情况下,Docker引擎只监听Socket,我们可以重新配置引擎使用TLS或者使用一个代理容器,转发请求从TCP到Docker监听的UNIX Socket。
5)Swarm Manager
Swarm管理器
6)Swarm Agent
Swarm代理,运行在每个节点上。
7)Controller
shipyard控制器,Remote API的实现和web的实现。
2.准备工作
在两台主机上分别做几下两点。
1)首先清除iptables
#iptables -F
2016-01-22_19-22-52
2)设置daemon参数,重启docker
#vi /usr/lib/systemd/system/docker.service


ExecStart=/usr/bin/docker daemon -H unix:///var/run/docker.sock

2016-01-22_19-22-23
3.Shipyard主节点安装(IP:192.168.199.41)
1)下拉镜像
虽然官网提供了一键安装的命令,curl -sSL https://shipyard-project.com/deploy | bash -s , 但我们还是体验一下non-TLS手工安装,也就是说不安装shipyard_certs。所以我们先下拉以下几个镜像。
docker pull rethinkdb
docker pull microbox/etcd
docker pull shipyard/docker-proxy
docker pull swarm:latest
docker pull shipyard/shipyard
2016-01-22_19-18-51
如果在下拉镜像速度上有问题,建议使用国内的镜像代理。
2)启动容器shipyard-rethinkdb


docker run \
    -ti \
    -d \
    --restart=always \
    --name shipyard-rethinkdb \
    rethinkdb

3)启动容器shipyard-discovery


docker run \
    -ti \
    -d \
    -p 4001:4001 \
    -p 7001:7001 \
    --restart=always \
    --name shipyard-discovery \
    microbox/etcd -name discovery

2016-01-22_19-25-11
4)启动shipyard-proxy


docker run \
    -ti \
    -d \
    -p 2375:2375 \
    --hostname=$HOSTNAME \
    --restart=always \
    --name shipyard-proxy \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -e PORT=2375 \
    shipyard/docker-proxy:latest

2016-01-22_19-25-34
5)启动shipyard-swarm-manager


docker run \
    -ti \
    -d \
    --restart=always \
    --name shipyard-swarm-manager \
    swarm:latest \
    manage --replication --addr 192.168.199.41:3375 --host tcp://0.0.0.0:3375 etcd://192.168.199.41:4001

2016-01-22_22-46-33
6)启动shipyard-swarm-agent


docker run \
    -ti \
    -d \
    --restart=always \
    --name shipyard-swarm-agent \
    swarm:latest \
    join --addr 192.168.199.41:2375 etcd://192.168.199.41:4001

2016-01-22_19-26-28
7)启动shipyard-controller


docker run \
    -ti \
    -d \
    --restart=always \
    --name shipyard-controller \
    --link shipyard-rethinkdb:rethinkdb \
    --link shipyard-swarm-manager:swarm \
    -p 8080:8080 \
    shipyard/shipyard:latest \
    server \
    -d tcp://swarm:3375

2016-01-22_19-26-40
至此主节点安装完毕,我们可以查看log来检查是否安装成功。
8)Web界面访问
访问http://192.168.199.41:8080,输入默认用户名和密码:admin/shipyard
查看启动的容器
2016-01-22_19-27-55
查看镜像
2016-01-22_19-28-11
查看节点,此时只有docker41这个节点
2016-01-22_19-29-11
4.添加从节点(IP:192.168.199.42)
1)下拉镜像
docker pull shipyard/docker-proxy
docker pull swarm:latest
2)启动容器shipyard-proxy


docker run \
    -ti \
    -d \
    -p 2375:2375 \
    --hostname=$HOSTNAME \
    --restart=always \
    --name shipyard-proxy \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -e PORT=2375 \
    shipyard/docker-proxy:latest

3)启动容器shipyard-swarm-manager


docker run \
    -ti \
    -d \
    --restart=always \
    --name shipyard-swarm-manager \
    swarm:latest \
    manage --replication --addr 192.168.199.42:3375 --host tcp://0.0.0.0:3375 etcd://192.168.199.41:4001

4)启动容器shipyard-swarm-agent


docker run \
    -ti \
    -d \
    --restart=always \
    --name shipyard-swarm-agent \
    swarm:latest \
    join --addr 192.168.199.42:2375 etcd://192.168.199.41:4001

2016-01-22_22-49-38
查看容器shipyard-swarm-manager的log,看到已经注册
2016-01-22_21-04-51
5)Web界面访问。
还是访问http://192.168.199.41:8080,查看容器,已经多了节点docker42上的容器。
2016-01-22_21-06-18
查看节点,多了 docker42
2016-01-22_21-05-59
5.给节点设置label
为了分清节点的功能,我们分别给节点设置不同label,Shipyard好像取消了直接维护,我们只能在docker daemon设置。在docker41上修改。
vi /usr/lib/systemd/system/docker.service


ExecStart=/usr/bin/docker daemon -H unix:///var/run/docker.sock --label docker=dev

在docker42上修改。


ExecStart=/usr/bin/docker daemon -H unix:///var/run/docker.sock --label docker=online

分别重启docker,我们再看node节点label已改变。
2016-01-22_22-07-02
6.添加Mysql实例
1)添加Mysql-dev, 按图配置参数,端口,路径,容器名。swarm约束,我们选择标签为dev的那台。
2016-01-22_22-35-02
查看容器详细信息。
2016-01-22_22-38-36
2)添加Mysql-online,按图配置参数,端口,路径,容器名。swarm约束,我们选择标签为online的那台。
2016-01-22_22-37-33
查看容器详细信息。
2016-01-22_22-39-26
看到新增的两台容器,分别在不同的docker上。
2016-01-22_22-40-02
以上,我们看到Shipyard确实可以很方便的统一管理位于不同主机的容器。

PS.
启动从节点的容器shipyard-swarm-manager时,出现ID duplicated错误的解决办法。


ERRO[0008] ID duplicated. UGJV:S4FQ:NSHM:GL2I:OBON:UOJF:OZHB:E6FA:DVML:L67M:5ZLT:YM2G shared by 192.168.199.41:2375 and 192.168.199.42:2375

这个错误产生的原因,应该是安装docker后,我们两台主机是复制的,所以key值相同。
解决办法是,删除/etc/docker/key.json这个文件,重启docker。

参考:
https://docs.docker.com/swarm/multi-manager-setup/
https://shipyard-project.com/deploy
https://shipyard-project.com/docs/deploy/manual/