随笔
所有由cst05001发布的文章
MOOSEFS部署和基本操作
环境: CentOS 7 x86_64
配置安装源
curl "http://ppa.moosefs.com/RPM-GPG-KEY-MooseFS" > /etc/pki/rpm-gpg/RPM-GPG-KEY-MooseFS
curl "http://ppa.moosefs.com/MooseFS-stable-el7.repo" > /etc/yum.repos.d/MooseFS.repo
主要有4个角色
- Master是客户端挂载的入口,负责调度。
- Chunk是实际存储数据的角色。
- Client是客户端挂载点
- Metalogger是可选的,但是为了安全一般都要。用于定期备份MooseFS的Metadata和Log。
Master
yum install -y moosefs-master moosefs-cgiserv
/etc/mfs/mfsexports.cfg 里配置下访问权限。
# grep -v "^#" /etc/mfs/mfsexports.cfg | grep -v "^$"
* / rw,alldirs,maproot=0:0,md5pass=6df4d50a41a5d20bc4faad8a6f09aa8f
启动服务
systemctl enable moosefs-master moosefs-cgiserv
systemctl start moosefs-master moosefs-cgiserv
ChunkServer
yum install -y moosefs-chunkserver
/etc/mfs/mfschunkserver.cfg 里指定 master 节点。
# grep -v '^#' /etc/mfs/mfschunkserver.cfg | grep -v '^$'
MASTER_HOST = mfsmaster
/etc/mfs/mfshdd.cfg 里指定存储路径以及空间配额
# grep -v '^#' /etc/mfs/mfshdd.cfg | grep -v '^$'
/mfs/chunk -10GiB
请创建该存储目录,并注意权限改成和配置一致,默认是 mfs 用户。
启动服务
systemctl enable moosefs-chunkserver
systemctl start moosefs-chunkserver
Metalogger
yum install -y moosefs-metalogger
/etc/mfs/mfsmetalogger.cfg 里指定 master 以及备份频率是 1 小时
# grep -v '^#' /etc/mfs/mfsmetalogger.cfg | grep -v '^$'
META_DOWNLOAD_FREQ = 1
MASTER_HOST = mfsmaster.wae.haplat.net
Client
yum install -y moosefs-client fuse
实时挂载
mfsmount -H mfsmaster -o mfsmd5pass=6df4d50a41a5d20bc4faad8a6f09aa8f /mnt/mfs
开机自动挂载,在/etc/fstab里写入
mfsmount /mnt/mfs fuse mfsmaster=mfsmaster,mfsmd5pass=6df4d50a41a5d20bc4faad8a6f09aa8f,_netdev 0 0
副本数
MooseFS 默认对文件只存储一个副本,建议设置多一点,但是不能超过chunk数量。
mfssetgoal -r 2 /mnt/mfs
Master恢复
如果Master故障,可以从Metalogger的备份数据,把Master恢复到任意一台机器。所以Metalogger最好不要和Master在同一台机器上。
从Metalogger的/var/lib/mfs拷贝
metadata_ml.mfs.back changelog_ml.0.mfs
这两个文件到要恢复的Master上的 /var/lib/mfs 目录下,执行命令
mfsmaster -a start
即可。
MooseFS 1.7 以及以上版本不支持 mfsmetarestore 命令,请用 mfsmaster -a 替代。
如果不想用命令启动mfsmaster,那么就再执行
mfsmaster stop
systemctl start moosefs-master
CGROUP学习笔记
安装
CentOS 6
yum install libcgroup
CentOS 7
yum install libcgroup-tools
使用
默认情况下有几个控制器可以进行限制,分别是
- cpuset
- cpu,cpuacct
- memory
- devices
- freezer
- net_cls
- blkio
- perf_event
- hugetlb
限制CPU负载
我写了一个脚本来消耗CPU
count.sh
#!/bin/bash
N=0
while true; do
N=$((N+1))
echo $N
done
执行命令
CMD=/root/count.sh
cgcreate -g cpu:/cpu50
cgset -r cpu.cfs_quota_us=50000 cpu50
cgexec -g cpu:cpu50 $CMD
可见 count.sh 的CPU负载一直维持在50%以内
[root@app-client ~]# ps aux | grep count.sh
root 18594 48.2 0.0 106152 1032 pts/0 R+ 09:53 0:11 /bin/bash /root/count.sh
root 18666 0.0 0.0 103304 808 pts/1 S+ 09:54 0:00 grep count.sh
此外也可以把已经在运行的进程加入cgroup限制,不需要重启程序。
先执行
/root/count.sh
获取进程号
[root@app-client ~]# ps aux | grep count.sh
root 18695 62.1 0.0 106152 1036 pts/0 R+ 09:57 0:04 /bin/bash ./count.sh
root 18697 0.0 0.0 103304 808 pts/1 S+ 09:57 0:00 grep count.sh
把进程加入控制器
cgclassify -g cpu:cpu50 18695
Tips:
一个资源组可以加多个进程,资源组限制的是加入该组的进程资源总和。比如上面例子,限制50%的负载,是指所有该组的进程总共消耗50%的负载,而不是每个资源能消耗50%的负载。
可以用 lscgroup 查看配置的控制器
[root@app-client ~]# lscgroup
cpuset:/
cpu:/
cpu:/cpu50
cpuacct:/
memory:/
devices:/
freezer:/
net_cls:/
blkio:/
可以用命令 cgdelete 进行控制器的删除。
所有控制器格式
请参考 https://www.kernel.org/doc/Documentation/cgroups/
比较常用的有
- blkio.throttle.read_bps_device – 磁盘读取限速
- blkio.throttle.write_bps_device – 磁盘写入限速
- cpu.cfs_quota_us – CPU load限制
- memory.limit_in_bytes – 最大内存限制
- cpuset.cpus – 绑定CPU的core,必须和 cpuset.mems 一起使用。
其他
各种控制器,除了使用 cg 系列命令可以修改,也可以直接修改映射到文件系统的目录。默认情况下,CentOS 6 的路径是 /cgroup,CentOS 7 的路径是 /sys/fs/cgroup 。
配置文件 /etc/cgconfig.conf 或者目录 /etc/cgconfig.d/ 下可对映射的路径进行配置,有兴趣请自行打开看看。
比如 CentOS 6 下的默认配置是
mount {
cpuset = /cgroup/cpuset;
cpu = /cgroup/cpu;
cpuacct = /cgroup/cpuacct;
memory = /cgroup/memory;
devices = /cgroup/devices;
freezer = /cgroup/freezer;
net_cls = /cgroup/net_cls;
blkio = /cgroup/blkio;
}
CENTOS 7 部署 KUBERNETES
请各流量站点别抄我笔记
截止至2015年9月1日,CentOS 已经把 kubernetes 加入官方源。
目前各相关组件版本如下
- kubernetes-1.0.0
- docker-1.7.1
- flannel-0.2.0
- etcd-2.0.11
kubernetes环境角色如下
- 192.168.1.248 etcd server
- 192.168.1.247 kubernetes master
- 192.168.1.246 kubernetes node
- 192.168.1.245 kubernetes node
Tips:
- 虽然我下面均用IP进行操作,但是建议部署的时候,建议用hosts或者DNS取代IP。
- 建议做部署前,先更新系统到最新版本,免得一些(我遇到过)的安装问题
yum update -y
各组件用途
- kube master
- kube-apiserver
k8s的管理接口
- kube-scheduer
k8s调度器,容器的启动、迁移、扩容缩减时候,选择哪个node,就看它了。
- kube-controller-manager
k8s对node的控制行为,比如怎么去调用node启动一个容器。
- kube-apiserver
- kube node
- kubelet
负责node的管理,基本所有操作都靠它。
- kube-proxy
每个node里的container都在一个私有网络中,kube-proxy的作用就是做一个反向代理,让访问者访问这个node的时候,可以转发到内部对应的container。
- kubelet
Tips: 当然,这是我狭隘的理解,仅供参考。
etcd
- 作为kubernetes的数据库,存储了k8s自身的信息、以及各种业务容器信息等。
- 存储flannel网络配置信息,供各节点协调。
安装
安装 etcd 节点
yum install etcd -y
安装 k8s master 节点
yum install kubernetes-master -y
安装 k8s node 节点
yum install kubernetes-node flannel docker -y
配置
etcd 节点配置
[root@localhost ~]# egrep -v “^#” /etc/etcd/etcd.conf
ETCD_NAME=default
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.1.248:2379"
启动服务
systemctl enable etcd
systemctl start etcd
k8s master 节点配置
[root@localhost ~]# egrep -v ‘^#’ /etc/kubernetes/apiserver | grep -v ‘^$’
KUBE_API_ADDRESS="--address=0.0.0.0"
KUBE_ETCD_SERVERS="--etcd_servers=http://192.168.1.248:2379"
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.254.0.0/16"
KUBE_ADMISSION_CONTROL="--admission_control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ResourceQuota"
KUBE_API_ARGS=""
[root@localhost kubernetes]# egrep -v ‘^#’ /etc/kubernetes/controller-manager |grep -v ‘^$’
KUBE_CONTROLLER_MANAGER_ARGS="--node-monitor-grace-period=10s --pod-eviction-timeout=10s"
[root@localhost ~]# egrep -v ‘^#’ /etc/kubernetes/config | egrep -v ‘^$’
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=0"
KUBE_ALLOW_PRIV="--allow_privileged=false"
KUBE_MASTER="--master=http://192.168.1.247:8080"
启动服务
systemctl enable kube-apiserver kube-scheduler kube-controller-manager
systemctl start kube-apiserver kube-scheduler kube-controller-manager
k8s node 节点配置
[root@localhost ~]# egrep -v ‘^#’ /etc/kubernetes/config | grep -v ‘^$’
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=0"
KUBE_ALLOW_PRIV="--allow_privileged=false"
KUBE_MASTER="--master=http://192.168.1.247:8080"
[root@localhost ~]# egrep -v ‘^#’ /etc/kubernetes/kubelet | grep -v ‘^$’
KUBELET_ADDRESS="--address=127.0.0.1"
KUBELET_HOSTNAME="--hostname_override=192.168.1.246"
KUBELET_API_SERVER="--api_servers=http://192.168.1.247:8080"
KUBELET_ARGS="--pod-infra-container-image=kubernetes/pause"
启动服务
systemctl enable kubelet kube-proxy
systemctl start kubelet kube-proxy
k8s node 节点配置 flannel
初始化flannel的etcd配置
etcdctl -C 192.168.1.248:2379 set /coreos.com/network/config '{ "Network": "10.1.0.0/16" }'
[root@bogon ~]# egrep -v ‘^#’ /etc/sysconfig/flanneld | grep -v ‘^$’
FLANNEL_ETCD="http://192.168.1.248:2379"
FLANNEL_ETCD_KEY="/coreos.com/network"
启动服务
systemctl enable flanenld
systemctl restart flanneld, docker
若网络还不通,就重启下node吧。
部署KUBERNETES(通用版)
部署Kubernetes(通用版)
本文主旨是脱离各Linux发行版本的特性,描述Kubernetes的部署。
预期效果:
- 服务器10秒故障容灾
- 业务副本10秒故障容灾
- 前段负载均衡
Author: 方云麟 cst05001
Site: http://www.fangyunlin.com/
欢迎引用链接,拒绝转载。
安装
环境
CentOS 7 x76_64
docker
下载最新二进制版本:https://docs.docker.com/installation/binaries/,然后把 docker-latest 复制并重命名为 /usr/local/bin/docker
chown 755 /usr/local/bin/docker
kubernetes
下载最新二进制版本:https://github.com/GoogleCloudPlatform/kubernetes/releases
解压后进到 kubernetes/server 目录,里面有一个名为 kubernetes-server-linux-amd64.tar.gz 的压缩包。再次解压。
解压后把 kubernetes/server/bin/ 目录如下文件拷贝到 /usr/local/bin/ 目录下
hyperkube
kube-apiserver
kube-controller-manager
kubectl
kubelet
kube-proxy
kubernetes
kube-scheduler
etcd
下载最新二进制版本:https://github.com/coreos/etcd/releases
解压后把如下文件拷贝到 /usr/local/bin/ 目录下
etcd
etcdctl
启动脚本
kubernetes是一个C/S结构的服务。
master上的启动脚本
#!/bin/bash
etcd --listen-peer-urls http://0.0.0.0:2380 --data-dir=/var/lib/etcd --listen-client-urls http://0.0.0.0:2379 --advertise-client-urls http://master:2379 >> /var/log/etcd 2>&1 &
sleep 2
etcdctl set /coreos.com/network/config '{ "Network": "10.1.0.0/16" }'
flanneld --listen=0.0.0.0:8888 >> /var/log/flanneld 2>&1 &
docker -d -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375 >> /var/log/dockerd 2>&1 &
kube-apiserver --logtostderr=true --v=0 --etcd_servers=http://master:2379 --address=0.0.0.0 --allow_privileged=false --service-cluster-ip-range=10.254.0.0/16 --v=0 >> /var/log/kubernetes/kube-apiserver 2>&1 &
kube-controller-manager --logtostderr=true --v=0 --master=http://master:8080 --v=0 --node-monitor-grace-period=10s --pod-eviction-timeout=10s >> /var/log/kubernetes/kube-controller-manager 2>&1 &
kube-scheduler --logtostderr=true --v=0 --master=http://master:8080 >> /var/log/kubernetes/kube-scheduler 2>&1 &
注意,这里所有脚本仅供展现最基本意图之用,创建目录、启动错误等判断,请自行添加。这点问题都搞不定应该不用继续看下去了。
node上的启动脚本
#!/bin/bash
flanneld -etcd-endpoints=http://master:2379 -remote=master:8888 >> /var/log/flannel 2>&1 &
sleep 2
source /run/flannel/subnet.env
docker -d -H unix:///var/run/docker.sock -H tcp://0.0.0.0:2375 >> /var/log/dockerd --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU} 2>&1 &
kubelet --logtostderr=true --v=0 --api_servers=http://master:8080 --address=0.0.0.0 --allow_privileged=false > /var/log/kubernetes/kubelet 2>&1 &
kube-proxy --logtostderr=true --v=0 --master=http://master:8080 > /var/log/kubernetes/kube-proxy 2>&1 &
填坑
kubernetes运行Pods需要伴随运行一个叫 pause的镜像。但是这个镜像被墙了。
请在境外docker服务器执行 docker pull 命令下载镜像
gcr.io/google_containers/pause:latest
gcr.io/google_containers/pause:1.0
gcr.io/google_containers/pause:0.8.0
再用导出镜像
docker save -o pause.tar gcr.io/google_containers/pause
gzip pause.tar
最后把这个包放到 kubernetes 环境所有的 docker 服务器上
docker load -i pause.tar.gz
测试
创建复制实例配置 phpinfo-rc.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: phpinfo-controller
spec:
replicas: 2
# selector identifies the set of Pods that this
# replication controller is responsible for managing
selector:
app: phpinfo
# podTemplate defines the cookie cutter used for creating
# new pods when necessary
template:
metadata:
labels:
# Important: these labels need to match the selector above
# The api server enforces this constraint.
app: phpinfo
spec:
containers:
- name: phpinfo
image: cst05001/phpinfo
ports:
- containerPort: 80
执行kubectl命令导入
[root@master rc]# kubectl create -f phpinfo-rc.yaml
replicationcontrollers/phpinfo-controller
[root@master rc]# kubectl get rc
CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
phpinfo-controller phpinfo cst05001/phpinfo app=phpinfo 2
[root@master rc]# kubectl get pods
NAME READY STATUS RESTARTS AGE
phpinfo-controller-e6ej4 1/1 Running 0 14s
phpinfo-controller-ttrw2 1/1 Running 0 14s
[root@master rc]#
创建前段负载分发配置 phpinfo-service.yaml
apiVersion: v1
kind: Service
metadata:
name: phpinfo-service
spec:
ports:
- port: 8000 # the port that this service should serve on
# the container on each pod to connect to, can be a name
# (e.g. www) or a number (e.g. 80)
targetPort: 80
protocol: TCP
# just like the selector in the replication controller,
# but this time it identifies the set of pods to load balance
# traffic to.
selector:
app: phpinfo
执行kubectl命令导入
[root@master service]# kubectl create -f phpinfo-service.yaml
services/phpinfo-service
[root@master service]# kubectl get services
NAME LABELS SELECTOR IP(S) PORT(S)
kubernetes component=apiserver,provider=kubernetes <none> 10.254.0.1 443/TCP
phpinfo-service <none> app=phpinfo 10.254.160.53 8000/TCP
[root@master service]#
在有启动 kube-proxy 服务的机器上可通过 http://10.254.160.53:8000 访问到实例,并可通过 页面 System 里的主机名确认负载均衡实例的效果。
GOLANG.ORG/X/CRYPTO/SSH RUN() START() OUTPUT() COMBINEDOUTPUT() 四个函数的区别
- Start 不管命令是否执行成功均返回 nil
- Run 命令执行成功返回 nil,失败返回 err
- Output 输出StdOut
- CombinedOutput 输出 StdOut 和 StdErr
可持续集成工具JENKINS用于代码发布+DOCKER打包镜像的应用
安装jenkins
以CentOS 7为例,通过 yum 安装
wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat/jenkins.repo
rpm --import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.key
yum install jenkins
或者直接 rpm 安装,到 http://pkg.jenkins-ci.org/redhat/ 下载最新版本 rpm 。其他系统的安装包自己到官网找。
修改 /etc/sysconfig/jenkins
把
JENKINS_USER="jenkins"
改成
JENKINS_USER="root"
jenkins默认使用8080端口,如果冲突,把
JENKINS_PORT="8080"
改成别的端口。
启动jenkins服务
systemctl restart jenkins
- 用浏览器登陆Jenkins界面。新建一个项目,比如叫做test。
- 勾选“自由风格”项目。
- 源码管理选择“SVN”,补充需要的信息。
- Repository URL是svn checkout的路径。
- Local module directory可不填,填了就是代码checkout下来的相对目录。我这里填写“test”
- 构建里选择“Execute shell”,内容如下
export BUILDROOT=/docker/build/test/ /usr/bin/rsync -vzrtopg --delete test $BUILDROOT/files/test cd $BUILDROOT /usr/bin/docker build -t registry.ws.com/test . /usr/bin/docker push registry.ws.com/test
保存配置后,点“构建”就可以实现拉去代码、打包成镜像、推送到镜像服务器这几个步骤了。
DOCKER1.6.2导致的CANNOT CHDIR: NO SUCH FILE OR DIRECTORY /USR/BIN/TAR: ERROR IS NOT RECOVERABLE: EXITING NOW 的BUG
今天安装dockerui ,遇到一个报错:docker run异常退出。下面是默认用 docker run -d 的报错,以及为了诊断,去掉 -d 的详细信息
[root@docker147 ~]# docker run -d -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock dockerui/dockerui
919bf6a1f912dd0f823e6b20a805b70658b22f86353f93bb6d0790440ad11bca
FATA[0000] Error response from daemon: : exit status 2
[root@docker147 ~]# docker run -p 9000:9000 --privileged -v /var/run/docker.sock:/var/run/docker.sock dockerui/dockerui
Timestamp: 2015-06-17 10:36:43.285451213 +0800 CST
Code: System error
Message: [/usr/bin/tar -cf /var/lib/docker/tmp/bc2a56ed9154afccb801b1552a3576b6592a40a764afa4f402b806d1efd57e02908036333/_tmp.tar -C /var/lib/docker/devicemapper/mnt/bc2a56ed9154afccb801b1552a3576b6592a40a764afa4f402b806d1efd57e02/rootfs/tmp .] failed: /usr/bin/tar: /var/lib/docker/devicemapper/mnt/bc2a56ed9154afccb801b1552a3576b6592a40a764afa4f402b806d1efd57e02/rootfs/tmp: Cannot chdir: No such file or directory
/usr/bin/tar: Error is not recoverable: exiting now
: exit status 2
Frames:
---
0: setupRootfs
Package: github.com/docker/libcontainer
File: rootfs_linux.go@30
---
1: Init
Package: github.com/docker/libcontainer.(*linuxStandardInit)
File: standard_init_linux.go@52
---
2: StartInitialization
Package: github.com/docker/libcontainer.(*LinuxFactory)
File: factory_linux.go@223
---
3: initializer
Package: github.com/docker/docker/daemon/execdriver/native
File: init.go@35
---
4: Init
Package: github.com/docker/docker/pkg/reexec
File: reexec.go@26
---
5: main
Package: main
File: docker.go@29
---
6: main
Package: runtime
File: proc.go@63
---
7: goexit
Package: runtime
File: asm_amd64.s@2232
FATA[0000] Error response from daemon: : exit status 2
关键信息是
Message: [/usr/bin/tar -cf /var/lib/docker/tmp/bc2a56ed9154afccb801b1552a3576b6592a40a764afa4f402b806d1efd57e02908036333/_tmp.tar -C /var/lib/docker/devicemapper/mnt/bc2a56ed9154afccb801b1552a3576b6592a40a764afa4f402b806d1efd57e02/rootfs/tmp .] failed: /usr/bin/tar: /var/lib/docker/devicemapper/mnt/bc2a56ed9154afccb801b1552a3576b6592a40a764afa4f402b806d1efd57e02/rootfs/tmp: Cannot chdir: No such file or directory
/usr/bin/tar: Error is not recoverable: exiting now
搜索步骤如下图
所以判断是和 docker 版本相关的 known issue。
本机docker版本是
[root@docker147 ~]# docker version
Client version: 1.6.0
Client API version: 1.18
Go version (client): go1.4.2
Git commit (client): 8aae715/1.6.0
OS/Arch (client): linux/amd64
Server version: 1.6.0
Server API version: 1.18
Go version (server): go1.4.2
Git commit (server): 8aae715/1.6.0
OS/Arch (server): linux/amd64
升级到官方最新版本
[root@docker147 ~]# docker-1.6.2 version
Client version: 1.6.2
Client API version: 1.18
Go version (client): go1.4.2
Git commit (client): 7c8fca2
OS/Arch (client): linux/amd64
Server version: 1.6.0
Server API version: 1.18
Go version (server): go1.4.2
Git commit (server): 8aae715/1.6.0
OS/Arch (server): linux/amd64
问题解决。
附上升级docker办法:https://docs.docker.com/installation/binaries/
用PIPEWORK给CONTAINER设置独立IP
官方网站:https://github.com/jpetazzo/pipework
以CentOS 7为例:
给宿主机创建网桥
[root@docker1 ~]# cat /etc/sysconfig/network-scripts/ifcfg-br0
TYPE="Bridge"
BOOTPROTO="static"
IPADDR="192.168.150.128"
PREFIX="24"
NAME="br0"
DEVICE="br0"
ONBOOT="yes"
设置物理网卡桥接到网桥
[root@docker1 ~]# cat /etc/sysconfig/network-scripts/ifcfg-eno16777736
TYPE="Ethernet"
BOOTPROTO="none"
DEFROUTE="yes"
PEERDNS="yes"
PEERROUTES="yes"
IPV4_FAILURE_FATAL="no"
IPV6INIT="yes"
IPV6_AUTOCONF="yes"
IPV6_DEFROUTE="yes"
IPV6_PEERDNS="yes"
IPV6_PEERROUTES="yes"
IPV6_FAILURE_FATAL="no"
NAME="eno16777736"
UUID="872e13a4-d0a5-4392-a270-a157f70a5bd2"
DEVICE="eno16777736"
ONBOOT="yes"
BRIDGE="br0"
用pipework启动容器
pipework br0 $(docker run -d -it -p 8080:80 nginx) 192.168.150.200/24@192.168.150.2
pipework会用bridge方式启动容器,run的-p参数失效。启用端口以expose为准。
container 关闭后如果重新start,网络需重新配置。把
ipework br0 $(docker run -d -it -p 8080:80 nginx) 192.168.150.200/24@192.168.150.2
改成
ipework br0 service1 192.168.150.200/24@192.168.150.2
service1是容器的–name
DOCKER入门笔记(4) – 搭建私有 REGISTRY
在实际工作中可能会遇到几个场景
- 紧急部署业务,需要在几分钟内 pull 并 run 一个 contaienr。并且需要稳定,不能总是出现莫名其妙的 pull 异常。
- 业务代码和数据私有,为了安全考虑,不便放于公有 registry。
这可以描述在内网或者在私有环境部署自己的 registry 的必要性。
这里有两个链接,推荐大家参考:
- docker 官方的 第三方 registry 使用手册(请只看使用部分,不推荐部署部分)
- 如何快速又简单的部署一个 registry
部署步骤
部署没有账号验证功能 registry
直接利用 docker 一键部署的特性,安装 registry image(这就是为什么我不推荐 docker 官方部署方法的原因。而且这个 registry image 也是 docker 官方提供的,请放心使用。)
docker run \
--name registry-server \
-d \
-e SETTINGS_FLAVOR=local \
-e SEARCH_BACKEND=sqlalchemy \
-e STORAGE_PATH=/docker/registry \
-v /docker/registry:/docker/registry \
-p 192.168.0.112:80:5000 \
registry
这里有两个参数比较重要:
- -v 参数,把 registry container 里的 image 存储目录映射到主机上。
- 因为存储是永久性的,如果把 image 存在 container 中,一旦 contaienr 给销毁,那么数据就没了。
- 而且 image 的文件系统必须有容量定义,一般是8G或者16G,所以一旦 image 多了,会导致主机磁盘还有空间,而 container 却报磁盘空间满,无法正常工作。不要在 container 里存放永久性数据
- -p 参数,registry 默认监听 5000 端口,而 docker 默认使用 80端口,所以把 container 的 5000 端口映射到主机的 80 端口,免得在 dockre push 或者 pull 的时候加端口麻烦。
问题:为什么我 run 的是 containerops.cn/docker/registry ,而不是 registry ?这个请看我之前笔记。其实都可以,但是因为国内访问 docker.com 的 registry 太慢,所以我直接使用国内的 registry 缓存。
为 registry 添加账号验证
出于安全性考虑,账号是必须的。registry 在前端加一个 web server,用 web auth 模块专门做账号验证。
创建 htpasswd 账号密码数据库(htpasswd工具是apache组件的一部分,以下操作均以Ubuntu 14.04 TLS 为例)
mkdir /etc/nginx
apt-get install apache2-utils
htpasswd -c /etc/nginx/docker-registry.htpasswd USERNAME
vim /etc/nginx/nginx.conf
nginx.conf 内容如下:
worker_processes auto;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80 default_server;
server_name _;
location / {
proxy_pass http://192.168.0.112:80;
proxy_set_header Host $http_host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
auth_basic "Restricted";
auth_basic_user_file nginx.htpasswd;
}
location /_ping {
auth_basic off;
proxy_pass http://192.168.0.112:80;
proxy_set_header Host $http_host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
location /v1/_ping {
auth_basic off;
proxy_pass http://192.168.0.112:80;
proxy_set_header Host $http_host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
}
- 上面命令中 USERNAME 代表你指定的用户名,命令会交互式要求你设置密码
- 如果需要创建多个账号,可以追加命令 htpasswd 不带 -c (create),否则会清空之前数据库。
htpasswd /etc/nginx/docker-registry.htpasswd USERNAME_2
-
nginx.conf 中,默认路径 / 就是要验证的地方。而用于 registry 可用性监测的 /_ping 和 /v1/_ping 两个路径切勿加验证。否则会导致 registry 无法正常工作。
最后启动 nginx,就可以使用带验证功能的 registry了。
docker run \
--name registry-nginx \
-d \
-p 117.***.148.***:80 \
-v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf:ro \
-v /etc/nginx/docker-registry.htpasswd:/etc/nginx/nginx.htpasswd:ro \
nginx:latest
问:上述架构有什么特点?
答:内网免验证,外网有验证。
问:要做成内外网均要验证,怎么改?是否涉及到端口冲突?要怎么处理?
DOCKER入门笔记(3) – IMAGE和CONTAINER
Docker入门笔记(3) – image和container
docker最简单的运行关系是这样的
镜像仓库和镜像/registry and image
registry是docker镜像的仓库。默认仓库是https://registry.hub.docker.com/。
镜像(image)是存放在registry里的。
我们可以直接在这里获取到centos、ubuntu等操作系统的镜像。这种系统镜像一般用来做二次定制,定制私有业务。
此外还有一些具体定制的镜像,比如tomcat、apache、nginx、mysql、mongodb、redis等。这些镜像的操作系统都裁剪的很小,所以一些简单需求直接拿这些镜像来用,会方便一些。centos、ubuntu这些系统基础镜像大小都在200M以上,而像busybox、flannel这些具体定制的镜像都不到50M。
以镜像为基础运行一个容器/run a container from image
我先拉取一个busybox镜像下来。
root@docker:~# docker pull busybox
latest: Pulling from busybox
511136ea3c5a: Pulling fs layer
511136ea3c5a: Download complete
df7546f9f060: Download complete
ea13149945cb: Download complete
4986bf8c1536: Download complete
Status: Downloaded newer image for busybox:latest
root@docker:~# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
busybox latest 4986bf8c1536 7 weeks ago 2.433 MB
busybox buildroot-2014.02 4986bf8c1536 7 weeks ago 2.433 MB
root@docker:~#
可以看到busybox这种定制版本的镜像就很小,才3M不到。以这个镜像为基础运行一个contaienr:
root@docker:~# docker run -it busybox
/ # whoami
root
/ # date
Sat Feb 21 06:07:57 UTC 2015
/ # uname -r
3.13.0-32-generic
/ #
我们可以登陆 https://registry.hub.docker.com/ 很直观的查询有哪些可用的镜像以及使用方法。
我比较常用的镜像有
- centos:centos6
- tomcat:7
- mongo:2
container 状态
启动另外一个终端,查看container状态:
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
861856052a59 busybox:buildroot-2014.02 "/bin/sh" 10 seconds ago Up 10 seconds evil_newton
这时候输入exit或者直接ctrl+D可以退出这个container。这之后container会变成exited状态,并不会停留在后台执行。也就是说,container默认不会自动销毁。
# root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
root@docker:~# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
861856052a59 busybox:buildroot-2014.02 "/bin/sh" 3 minutes ago Exited (0) 4 seconds ago evil_newton
root@docker:~#
- image只是一个文件,运行起来之后的东西叫做container
- 一个image可以启动多个contaienr
- 每个container之间的读写变更不会相互影响
- container的读写变更不会影响到image
- 默认请款下,contaienr退出后会保存在磁盘上,不会自动销毁
- docker ps只能查看up状态的contaienr,要查看所有状态的container需要加 -a 参数
上面docker run命令,加了-i -t 两个参数。-i保证交互式输入。-t表示分配tty终端。这两个都和交互式输入输出有关系,默认加上即可,和本主题无关,我不想深究。大家在后期可以试试不加这两个参数的效果,对比一下。现在对比,除了无法输入输出,其他应该体验不出来。这个话题以后也许会再讨论。
前台和后台
上面通过 -i -t 的方式把 container 挂到终端上运行。这个只是在学习和打包 image 的时候会用到。如果是在线上业务中,把 contaienr 直接作为后台程序运行会更合适点。使用参数 -d 就行了。
root@docker:~# docker run -it -d busybox
0976249c0af6baab618043187ce067faba27832e8cf85a7d434ba6b2c25256aa
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0976249c0af6 busybox:buildroot-2014.02 "/bin/sh" 3 seconds ago Up 2 seconds angry_archimedes
root@docker:~#
-d 后 busybox 不会在前台运行,docker直接给出 container id ,而在 docker ps 命令中可以看到 busybox 是出于运行状态。
网络服务相关的 container
基本所有业务都是需要通过网络对外提供服务的。所以我们可以通过 docker run -p 参数来实现。
docker run -p 80,在主机上找一个随机端口和 container 的 80 端口做映射(个人觉得比较没用)
docker run -P,在主机上找对应的若干个随机端口,和 image 文件指定的 expose 端口做映射(个人觉得比较没用)
docker run -p 10080:80,把主机的 10080 端口和 contaienr的 80 端口做映射。这时候业务要通过主机IP访问 container 里的 80 端口业务,要访问的可是 10080 端口。
docker run -p 192.168.0.10:10080:80,把主机的 192.168.0.10 IP的 10080 端口和 container 里的 80 端口做映射。
docker run -p -p -p,如果有多个端口需要映射,可以写多次 -p 参数。
root@docker:~# docker run -p 80:80 -p 23:23 -d -it busybox
13af89b8ef87c857cd1efb88f8fd5b8ffeca3faea691bcaa73c224fcc126183d
root@docker:~# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
13af89b8ef87 busybox:buildroot-2014.02 "/bin/sh" 4 seconds ago Up 4 seconds 0.0.0.0:23->23/tcp, 0.0.0.0:80->80/tcp fervent_bohr
root@docker:~# netstat -ntpl
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 916/sshd
tcp6 0 0 :::23 :::* LISTEN 1780/docker-proxy
tcp6 0 0 :::80 :::* LISTEN 1789/docker-proxy
tcp6 0 0 :::22 :::* LISTEN 916/sshd
大家观察下上面输出。
为什么我的 container 总是退出?
docker run 一个 container 的时候,最后都需要加一个 CMD 参数,表示我 container 要启动什么程序。一旦这个程序退出或者挂到后台,就会导致整个 container 退出结束。从经验上看,下面涵盖了大多数情况:
- 程序无法启动
- 没有指定正确的程序路径或者程序不存在
- 启动程序的参数正确,比如没有这个参数
- 程序配置文件有错误
- 程序默认挂到后台执行了
比如我们执行 docker run -it -d busybox,其实 busybox 有一个默认的 CMD 值,是 /bin/sh ,也就是等同于 docker run -it -d busybox /bin/sh 。这两句命令是一样的。 /bin/sh 这个程序会一直等待键盘输入,所以不会退出。
定制自己需要的镜像
基于 container 进行 commit
光有基础系统的 image 只能玩一玩,没什么实际意义。我们尝试定制一个有自己功能的 image。
创建一个 container ,并且进入 container 中进行操作:
docker run -it busybox /bin/sh
在 container 中创建文件 /usr/bin/run.sh
root@docker:~# docker run -it busybox /bin/sh
/ # pwd
/
/ # vi /usr/bin/run.sh
内容如下:
#!/bin/sh
COUNT=0
while(true); do
COUNT=$(($COUNT+1))
echo $COUNT
sleep 2
done
授予执行权限:
chmod 755 /usr/bin/run.sh
测试,直接输入命令 run.sh 执行,可见终端每2秒就会输出一个计数,并且永不退出(while(true))。
/ # run.sh
1
2
3
4
5
6
...
+ C退出脚本,再exit或者 + D退出 container 。这时候可以通过 docker ps -a 看到 container 处于 exitted 状态,并且获得 container ID 是 d321110c0473 。执行命令
docker commit d3 cst05001/study
可以把该 container 提交为 docker 默认镜像仓库下,名为 cst05001/study 的镜像。不过这只是一个标记,镜像只存在于本地,并未真正的传到镜像服务器,只能本地使用。
root@docker:~# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
cst05001/study latest a836bfbda876 2 minutes ago 2.433 MB
busybox buildroot-2014.02 4986bf8c1536 7 weeks ago 2.433 MB
busybox latest 4986bf8c1536 7 weeks ago 2.433 MB
我们尝试运行我们定制的这个镜像:
root@docker:~# docker run -it cst05001/study run.sh
1
2
3
...
成功。
Dockerfile(推荐)
通过 Dockerfile 进行镜像定制是在生产环境更高效更稳定的方法。不过我会放到以后再表。
push image
之前有说,我们定制的镜像并未传送到镜像服务器。如果希望共享出这个镜像给多台服务器使用,则需要把镜像推送到镜像服务器上(registry)。步骤大致如下:
- 在镜像服务器上注册自己的账号,创建自己的仓库(如果是自己创建的registry可以略过这一步)
- docker login,输入账号、密码、邮箱三个信息进行登陆验证
- docker push <镜像名(tag)全程>
比如我们提交刚才的 cst05001/study,则可以这么做
root@docker:~# docker login
Username: cst05001
Password:
Email: 65141838@qq.com
Login Succeeded
root@docker:~# docker push cst05001/study
The push refers to a repository [cst05001/study] (len: 1)
Sending image list
Pushing repository cst05001/study (1 tags)
511136ea3c5a: Image already pushed, skipping
df7546f9f060: Image already pushed, skipping
ea13149945cb: Image already pushed, skipping
4986bf8c1536: Image already pushed, skipping
a836bfbda876: Image successfully pushed
Pushing tag for rev [a836bfbda876] on {https://cdn-registry-1.docker.io/v1/repositories/cst05001/study/tags/latest}
现在我们就可以在全球所有接入互联网的机器上直接通过命令
docker pull cst05001/study
获取这个镜像,或者直接用命令
docker run cst05001/study run.sh
获取并运行这个镜像了。
第三方 registry
由于一些不方便讲的原因,在大陆地区使用官方 registry 经常会有下面几个问题
- pull速度慢
- pull失败
- push速度慢
- push失败
- 各种奇怪问题
所以就催生了第三方的 registry。我比较信赖的国内 registry 有docker.cn,最近刚发现改名成 https://containerops.cn/ 了。这个 registry 曾经缓存了 docker 官方 registry 的所有内容,据说一星期更新一次。用了 又拍云 的加速服务,还可以,实测大概两三百kbytes/s。可是改版后官方镜像不见了。所以如果你懒得搭建自己的私有registry,可以直接用他的。
推送镜像到 containerops.cn
前提是已经在 containerops.cn 注册了账号,并且创建了镜像库。登陆账号(如果你已经登陆了,可以略过这个步骤)。我偷个懒,把刚才的 cst05001/study 镜像做一个别名,也绑定到 docker。是的,image 名称也起到指定 registry地址、仓库的作用。再然后推送镜像就可以了。
root@docker:~# docker login containerops.cn
Username: cst05001
Password:
Email: 65141838@qq.com
Login Succeeded
root@docker:~# docker tag cst05001/study containerops.cn/cst05001/study
root@docker:~# docker push containerops.cn/cst05001/study
The push refers to a repository [containerops.cn/cst05001/study] (len: 1)
Sending image list
Pushing repository containerops.cn/cst05001/study (1 tags)
Image 511136ea3c5a already pushed, skipping
Image df7546f9f060 already pushed, skipping
Image 4986bf8c1536 already pushed, skipping
Image ea13149945cb already pushed, skipping
a836bfbda876: Image successfully pushed
Pushing tag for rev [a836bfbda876] on {https://containerops.cn/v1/repositories/cst05001/study/tags/latest}
root@docker:~#
部署私有 registry
这个是挺重要的一件事情,我会单独开一章来表。
小建议
请执行 docker run –help ,把每一个参数都一个字一个字的读一遍。这样挺好的。
问题
本文在使用第三方 registry 的时候,漏了一个重要的点没有说。如果有亲自测试,一定会遇到,并且不解决不行,但是会有明显的报错以及提示解决方法。请问这是什么?