有测试机可以使用,但是想了想还是在自己电脑上装一份,就是低配mac硬盘不够用啊。
稳定版下载地址:
https://download.docker.com/mac/stable/Docker.dmg
找了一圈好像稳定版都是这个地址。
还在下载,说说问啥要自己电脑上搞一份,准备把之前写的mysql查询平台放到github上,之前同事有要。想了想虽然没啥技术含量,放到github上搞个镜像,写个脚本,一键启动。这样方便安装,虽然项目我基本都写安装文档,但是还是一键启动的爽。初步想法是写个Dockerfile,执行安装脚本build。这样更新的话还是需要重新build。先这么搞搞看看。
动动鼠标,然后就安装成功了?
-:> docker version
Client:
Version: 17.09.1-ce
API version: 1.32
Go version: go1.8.3
Git commit: 19e2cf6
Built: Thu Dec 7 22:22:25 2017
OS/Arch: darwin/amd64
Server:
Version: 17.09.1-ce
API version: 1.32 (minimum version 1.12)
Go version: go1.8.3
Git commit: 19e2cf6
Built: Thu Dec 7 22:28:28 2017
OS/Arch: linux/amd64
Experimental: true
Read more...
Archive for docker
kubernetes 初次安装使用
kubernetes 安装
yum install etcd kubernetes
vim /etc/sysconfig/docker
# OPTIONS='--selinux-enabled --log-driver=journald --signature-verification=false'
OPTIONS='--selinux-enabled=false --insecure-registry gcr.io --log-driver=journald --signature-verification=false'
vim /etc/kubernetes/apiserver
# KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota"
KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,LimitRanger,SecurityContextDeny,ResourceQuota"
vim /etc/kubernetes/apiserver
# KUBE_API_ADDRESS="--insecure-bind-address=127.0.0.1"
KUBE_API_ADDRESS="--insecure-bind-address=0.0.0.0"
这个后边api访问要用 一起改了
systemctl start etcd
systemctl start docker
systemctl start kube-apiserver
systemctl start kube-controller-manager
systemctl start kube-scheduler
systemctl start kubelet
systemctl start kube-proxy
搞一个可以执行python的镜像
docker search python
docker pull docker.io/python
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/python latest b6cc5d70bc28 3 weeks ago 688.9 MB
测试
docker run -t -i b6cc5d70bc28 /bin/bash
启动输入python, 麻痹 3.6.1的,还好有python2。2.7.9 哈哈
创建个新的镜像,新建Dockerfile
# 测试用滴
FROM docker.io/python
EXPOSE 8080
# COPY main.py .
CMD python2 -m SimpleHTTPServer 8080
docker build -t mytest/pyhttp:v1 .
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mytest/pyhttp v1 eefd8b0e5723 About a minute ago 688.9 MB
docker.io/python latest b6cc5d70bc28 3 weeks ago 688.9 MB
docker run -d eefd8b0e57
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
02e9d5c001c9 eefd8b0e57 "/bin/sh -c 'python2 " 8 seconds ago Up 6 seconds 8080/tcp jovial_shockley
docker exec -i -t 02e9d5c001c9 /bin/bash
ss -at
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 5 *:http-alt *:*
或者curl 127.0.0.1:8080
没问题了
docker kill 02e9d5c001c9
下边就开始搞k8s了
编辑pyhttp.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: pyhttp
spec:
replicas: 2
selector:
app: pyhttp
template:
metadata:
labels:
app: pyhttp
spec:
containers:
- name: pyhttp
image: mytest/pyhttp:v1
ports:
- containerPort: 8080
kubectl create -f ./pyhttp.yaml
replicationcontroller "pyhttp" created
kubectl get rc
kubectl get pods
NAME READY STATUS RESTARTS AGE
pyhttp-26pd2 0/1 ContainerCreating 0 1m
pyhttp-v7qk2 0/1 ContainerCreating 0 1m
kubectl describe replicationcontrollers/pyhttp
kubectl get events
几个看状态的命令,等到都running了,就成功了
docker ps命令看启动了四个容器,两个pyhttp的。两个registry.access.redhat.com/rhel7/pod-infrastructure:latest
再写个httpserver.yaml
apiVersion: v1
kind: Service
metadata:
name: http-service
spec:
type: NodePort
selector:
app: pyhttp
ports:
- protocol: TCP
port: 8080
nodePort: 30080
kubectl create -f ./httpserver.yaml
kubectl create -f ./httpserver.yaml
service "http-service" created
[root@localhost code]# kubectl get services
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
http-service 10.254.245.101 8080:30080/TCP 12s
kubernetes 10.254.0.1 443/TCP 1h
然后就能通过30080访问了. 不过默认安装防火墙给过滤掉了,从机器外是访问不了的
装了个Dashboard,安装方法从官网github上装的。
我装的k8s是1.5的,安装命令
kubectl create -f https://git.io/kube-dashboard-no-rbac
默认监听的127.0.0.1需要制定地址
kubectl proxy --address='0.0.0.0'
然后就可以了
访问发现是
Read more...
Unauthorized
看文档好像master上的只能在设置用户名密码的时候才能用 kubectl proxy --address='0.0.0.0' --accept-hosts='^*$' 然后跳转变成503. status: "Failure", message: "no endpoints available for service "kubernetes-dashboard"", reason: "ServiceUnavailable", 发现后端启动不成功 [root@localhost ~]# kubectl get pods --namespace=kube-system NAME READY STATUS RESTARTS AGE kubernetes-dashboard-3585514280-q29ml 0/1 CrashLoopBackOff 2 43s [root@localhost ~]# kubectl logs kubernetes-dashboard-3585514280-q29ml -n kube-system Using HTTP port: 8443 Error while initializing connection to Kubernetes apiserver. This most likely means that the cluster is misconfigured (e.g., it has invalid apiserver certificates or service accounts configuration) or the --apiserver-host param points to a server that does not exist. Reason: open /var/run/secrets/kubernetes.io/serviceaccount/token: no such file or directory Refer to the troubleshooting guide for more information: https://github.com/kubernetes/dashboard/blob/master/docs/user-guide/troubleshooting.md 然后搜索有人说要把yaml指定,发现里边确实有注释,默认不行就指定。 - --apiserver-host=http://10.254.0.1:80 这个apiserver ip 命令 kubectl describe svc kubernetes 获取 然后发现还是不行,会出现timeout,这次能running了但是一会儿还是503 但是配置成http://192.168.49.111:8080的api访问地址就行了,外网也能访问了。具体还不太了解原理Docker源码阅读6-创建server启动和containerd的启动
接上次看的往下看
cli.Pidfile 创建pid文件"/var/run/docker.pid"
serverConfig := &apiserver.Config{
Logging: true,
SocketGroup: cli.Config.SocketGroup,
Version: dockerversion.Version,
EnableCors: cli.Config.EnableCors,
CorsHeaders: cli.Config.CorsHeaders,
}
这几个默认值为 SocketGroup 为docker, EnableCors false, CorsHeaders 这个看EnableCors
api := apiserver.New(serverConfig) 这个就是实例化了个Server,cfg为上边的serverConfig
cli.api = api
返回一个, 初始化cfg为serverConfig,看起来所有请求的路由都在这里了
type Server struct {
cfg *Config
servers []*HTTPServer
routers []router.Router
routerSwapper *routerSwapper
middlewares []middleware.Middleware
}
cli.Config.Hosts没有的话创建一个长度1的数组,有的话进入循环进行监听
listeners.Init
wrapListeners(proto, ls)
allocateDaemonPort(addr)
api.Accept(addr, ls...)
监听地址, 根据hosts初始化的多个httpserver。
migrateKey 这个看起来好像是为了升级还是啥的。把之前配置文件目录的key.json移动到/etc/docker/下边,并把旧的删除
具体不知道为啥这么做
registryService := registry.NewService(cli.Config.ServiceOptions) 返回一个DefaultService
newServiceConfig(options)里边,配置镜像仓库地址
containerdRemote, err := libcontainerd.New(cli.getLibcontainerdRoot(), cli.getPlatformRemoteOptions()...)
重头戏来了,
getLibcontainerdRoot /var/run/docker/libcontainerd
getPlatformRemoteOptions 里边好像就是设置了一些参数
libcontainerd.WithRemoteAddr 这个是设置了ContainerdAddr配置,containerd参数走这个,调用rpcAddr(addr)。
没设置 走libcontainerd.WithStartDaemon(true), 然后调用startDaemon(start)
type RemoteOption interface {
Apply(Remote) error
}
New里边先初始化remote
eventTsPath getLibcontainerdRoot/event.ts
option.Apply(r)这里应该就是真正设置参数到初始化的remote了。不知道为啥这么写
创建getLibcontainerdRoot也就是stateDir 700权限
如果没有rpcAddr 设置 stateDir/docker-containerd.sock
startDaemon默认是true的所以肯定执行r.runContainerdDaemon()
runContainerdDaemon里
pidFilename stateDir/docker-containerd.pid,下面判断pid文件是否正确,如果进程在运行赋值r.daemonPid,就返回结束了
如果没运行,先设置运行参数,然后执行命令启动docker-containerd
setOOMScore oomScoreAdjPath := fmt.Sprintf("/proc/%d/oom_score_adj", pid) 好像就把参数那个数写文件里了,默认-500
写进程号
建了一个r.daemonWaitCh channel,container进程结束后,关闭这个channel。赋值r.daemonPid
总的来说就是启动container进程
使用grpc创建rpc连接
r.rpcConn = conn
r.apiClient = containerd.NewAPIClient(conn) 返回
type aPIClient struct {
cc *grpc.ClientConn
}
就保存了rpc连接
getLastEventTimestamp就是看 stateDir/event.ts 文件,没数据就返回当前时间,如果有,里边保存的是序列号化的,反序列化一下返回
tsp, err := ptypes.TimestampProto(t)转了一下时间
r.restoreFromTimestamp = tsp
go r.handleConnectionChange()
大体看了一下,就是开个协程进行rpc连接探测, 连续失败到三次,如果进程存在kill.<-r.daemonWaitCh,然后r.runContainerdDaemon()启动
r.startEventsMonitor(); 监控请求事件
发送了一个"/types.API/Events"请求, 然后开个协程有事件就处理事件
处理完更新事件时间
处理事件这里先不仔细看了。感觉看下去脑子回不来了.
libcontainerd/remote_unix.go
startEventsMonitor()标记一下,以后看
主要就是server的启动和containerd的启动
Read more...
Docker源码阅读5-用户映射
dockerd/daemon.go start()
loadDaemonCliConfig(opts) 里
daemon.MergeDaemonConfigurations(config, flags, opts.configFile)将配置文件设置的参数和命令参数合并。
过了好多代码,感觉都是配置啥的就不看具体干啥了。
setDefaultUmask() 设置umask 0022,把goup和other的写权限干掉了。
daemon.CreateDaemonRoot(cli.Config)
先获取docker daemon根目录 默认/var/lib/docker
setupRemappedRoot 根据配置映射容器用户和组的
这个参数默认是空的,也就是不映射
如果设置了这个参数
parseRemappedRoot就是解析参数的,有四种格式
username
username:groupname
uid
uid:gid
然后解析/etc/passwd和/etc/group看看参数对不对。其中解析是否是defaultIDSpecifier 就是个'default',然后映射的host用户为dockremap。
也就是RemappedRoot这个参数传个default就行了,使用docker创建好的账户
idtools.AddNamespaceRangesUser(defaultRemappedID)就是判断是否有default的用户,没有就创建
uidMaps, gidMaps, err = idtools.CreateIDMappings(username, groupname) 这个从/etc/subuid和/etc/subgid获取,根据username判断是第一个
startid是uid是第二个,length还不知道干啥的第三个
ALL不知道干啥的 返回了所有的行,就是所有用户
createIDMap返回IDMap,ContainerID开始是0然后后边的都加length。hostid,就是第二个startid, size 是length.
然后setupRemappedRoot就完了,返回了俩idmap
idtools.GetRootUIDGID(uidMaps, gidMaps)
里边就一个ToHost(0, uidMap)这个单个的时候就是返回之前的hostid就是startid对应host上的uid, 之前的ALL的话,好像还是只返回了第一个的hostid
setupDaemonRoot(config, realRoot, rootUID, rootGID)
修改docker的根目录权限为711
RemappedRoot 为空就直接返回了。不空的话创建一个config.Root目录在docker根目录下。目录名为rootUID.rootGID,权限700.然后好像判断目录下的rootUID都有访问权限,这个应该是为已经有这个目录的判断
然后CreateDaemonRoot完结撒花,设置了一下目录,用户的映射
Read more...
Docker源码阅读4-daemon流程
东西太多了,大体过了一下,感觉会少了很多东西。有一些三方库,也没去看库怎么用。就看函数名大体猜一下功能。
完全就是从main函数一步一步往下看,这种看法就是缺少一个大局的认识,开始会有很多不理解的做法。
cmd/dockerd/docker.go
reexec.Init()
必须要提前调用的方法,用来初始化。如果初始化过了就会返回true,main函数也退出了。
判断是否调用过,就是看看registeredInitializers里有没有os.Args[0]的key。
如果有调用value,没有直接返回false,接着走main函数
具体value是个啥函数,慢慢看.里边还有个Register(函数用来往registeredInitializers里添加值。
cmd := newDaemonCommand()
cmd.Execute()
这里一看就跟client的结构一样,突然就感觉好看多了。。
daemonOptions
daemon.NewConfig() 返回一个Config,所有配置应该都在这,太多了,也看不到啥意思先过了。
cliflags.NewCommonOptions() 对应client TLS相关的东西,这里一样。
其他都一样。
opts.daemonConfig.InstallFlags(flags)
定义都有哪些参数,太多不看,在help命令里应该是有的。配置了对应的Config里边的字段。
看看return runDaemon(opts)
daemonCli := NewDaemonCli() 返回一个DaemonCli
type DaemonCli struct {
*daemon.Config
configFile *string
flags *pflag.FlagSet
api *apiserver.Server
d *daemon.Daemon
authzMiddleware *authorization.Middleware // authzMiddleware enables to dynamically reload the authorization plugins
}
err = daemonCli.start(opts) 这里流程就完了。详细看看start是干嘛的
Read more...
Docker源码阅读3-client命令
先看看上次没看完的client的初始化。
NewAPIClientFromFlags里
getServerHost(opts.Hosts, opts.TLSOptions),如果参数有指定用参数的,如果没指定用os.Getenv("DOCKER_HOST").
设置customHeaders,环境变量如果有设置DOCKER_API_VERSION,设置api版本
newHTTPClient(host, opts.TLSOptions)返回一个http.client
client.NewClient(host, verStr, httpClient, customHeaders)初始化一个新的Client对象,Client是啥,
client/interface_stable.go
package client
// APIClient is an interface that clients that talk with a docker server must implement.
type APIClient interface {
CommonAPIClient
apiClientExperimental
}
// Ensure that Client always implements APIClient.
var _ APIClient = &Client{}
APIClient是个接口类型,看了一下包含了好多函数,应该是所有命令集中都是调的这里的。Client就是一个APIClient接口类型的实现。这样写一句应该能保证,不然编译不过,哈哈 会玩。。
client的命令都是在上一篇的AddCommands里边集中添加,对应各个目录里边的cmd.go里的NewCommand方法进行添加。
client.Ping( 在client/ping.go里,
先req, err := cli.buildRequest("GET", fmt.Sprintf("%s/_ping", cli.basePath), nil, nil),构造请求
serverResp, err := cli.doRequest(ctx, req),发送请求返回结果,都在request.go里边,普通的http请求。
然后ping.APIVersion和ping.Experimental都是根据返回的header来判断的,Experimental具体干啥的还不清楚
client.UpdateClientVersion,这个仅仅是改了一下client的版本,可能请求是带上这个版本,服务端会根据这个版本进行不同操作返回吧。还不确定。cli.version = v
这个样整个dockerCli就初始化完了。核心就是那个Client,各种请求的定义都在里边。
再看看具体命令的执行,看个docker info命令,感觉这个应该简单点。
system.NewSystemCommand(dockerCli),
system.NewVersionCommand(dockerCli),
正好还有个version,先看看这个。在runVersion(里边,
ctx := context.Background()
先创建一个空context,versionTemplate 格式化模板,如果提供opts.format参数,就使用提供的模板。上边有这个操作
APIVersion := dockerCli.Client().ClientVersion()
这里找了一下Client()返回dockerCli.client就是上边提到的初始化的那个,client.APIClient类型的
ClientVersion() 在client/client.go里边定义:func (cli *Client) ClientVersion() string返回Client .version字段。也和之前的一样
下边就是版本不统一,api版本低的话会让cli的版本也低,然后会跟随打印出来。api版本高的话,可能兼容的,这个也不确定。
dockerCli.Client().ServerVersion(ctx)然后请求server的版本。cli.get(ctx, "/version", nil, nil)走接口请求.
最后就是渲染模板,进行打印。
types 都在api/types定义的。可能会公用到吧,不太清楚。
总的来说就是client解析参数,然后找到对应的处理方法,发送请求给server,然后server返回。这个和官方提供的api是用的一样的。我记得之前搭建docker环境的时候踩过一个坑,配置api的访问接口的时候,把socket给去掉了,只留个ip:port的,本地命令就没法执行了。本地默认好像走的socket文件的。
其他client端的命令就不看了 ,应该都是一样的。下边就看看server的。
Read more...
Docker学习源码阅读2-client流程阅读
先看docker/cmd/docker/docker.go
主要就三行
dockerCli := command.NewDockerCli(stdin, stdout, stderr)
cmd := newDockerCommand(dockerCli)
if err := cmd.Execute(); err != nil {
NewDockerCli( 在docker/cli/command/cli.go里返回一个DockerCli实例
type DockerCli struct {
configFile *configfile.ConfigFile
in *InStream
out *OutStream
err io.Writer
keyFile string
client client.APIClient
hasExperimental bool
defaultVersion string
}
设置一些client都用的些东西。初始化了in,out,err,其他字段先不管,按流程看。
newDockerCommand
先实例化一个ClientOptions,
type ClientOptions struct {
Common *CommonOptions
ConfigDir string
Version bool
}
然后用了一个cobra库,应该就是管理命令行参数的。
PersistentPreRunE里边,一些参数设置。
daemon启动废弃了,所以就不设置这些参数啥的了。
opts.Common.SetDefaultOptions(flags)设置CommonOptions的一些默认参数.
dockerPreRun(opts)里opts.ConfigDir可以通过设置配置文件路径,这个是默认的:configDir = os.Getenv("DOCKER_CONFIG"),
参数肯定优先级高,哈哈,SetDir会替换成参数里的。config参数的定义在newDockerCommand的最下边。
func Enable() {
os.Setenv("DEBUG", "1")
logrus.SetLevel(logrus.DebugLevel)
}
根据参数是否开启debug
dockerCli.Initialize(opts)进行dockerCli的初始化,之前只是初始化输入输出,还有其他字段要初始化。
LoadDefaultConfigFile初始化configFile字段,看名字基本就知道啥功能了。文件路径是环境变量定义的configDir = os.Getenv("DOCKER_CONFIG"),怎么load的就不看了.再往下好像是证书相关的配置需要设置参数。
NewAPIClientFromFlags(opts.Common, cli.configFile)是初始化dockerCli的client字段。具体怎么初始化的,这个等后边看。内容挺多的
cli.defaultVersion初始化另一个字段,就是client的版本.keyFile初始化,
ping操作主要是获取api的版本,如果ping.APIVersion为空,就默认1.24,是1。24之前不支持这个字段,因为现在肯定大于1.24了。HasExperimental初始化
如果api版本小于客户端版本,应该是不能使用,需要把客户端版本降级。UpdateClientVersion操作。这个也后边集中分析一下。
return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental())
HasExperimental这个也是ping的时候返回的,这里主要还是版本判断,过了,cobra参数也看不太懂。
opts.Common.InstallFlags(flags)设置了一些参数,一些指定host,key,版本啥参数.与client连接server有关的设置。
cmd.AddCommand(newDaemonCommand()) 之前好像启动client和daemon是一个文件,现在分开了。这里看的是client的代码,使用之前的daemon参数进行启动的时候,提示新版本已经分开了。
commands.AddCommands(cmd, dockerCli)
docker/cli/commands/commands.go
这里就是添加命令参数了。注释分类挺清楚的,分别在不同文件里边。
这个后边再细看,都差不多,命令定义都在command目录下
cmd.Execute();
这个应该就是cobra一切设置完了 开始监听参数并执行相应方法了。
Read more...
Docker学习源码阅读1-入口分析
直接从makefile入手找到hack/make.sh,具体怎么编译的不看,只找入口.
看make.sh要编译的东西主要应该就看俩就行了,一个是clien,一个是daemon。
binary-client
binary-daemon
dynbinary
test-unit
test-integration-cli
test-docker-py
cross
tgz
其他一些测试用的,不管,还有一些不知道。以后再说。
然后往下翻发现都在hack/make/目录里边对应的文件。
俩文件样子差不多,binary-client 二进制名为docker ,binary-daemon为dockerd。源码目录分别对应
docker/cmd/docker,docker/cmd/dockerd
搞定,后边再接着看。
Read more...
docker server服务启动参数定制
想要对docker的server服务进行启动参数的改变,绕了好多圈子,最后终于搞定。
debian的系统直接使用service启动docker服务,然后看官方文档有讲,直接修改/etc/default/docker文件就行了。
添加DOCKER_OPTS参数
然后修改了重启服务发现没用。然后发现/etc/init/docker是调的这个文件。然后找init.d/docker文件发现DOCKER_OPTS参数文件里自己定义了一下什么没有,然后在这里改。改完还是不行,google之,github上有问,修改/lib/systemd/system/docker.service文件。
修改成:
#EnvironmentFile=-/etc/default/docker
#ExecStart=
#ExecStart=/usr/bin/docker -d $DOCKER_OPTS
这样直接启动不起来。
然后写死命令,不用配置文件就好用了
ExecStart=/usr/bin/docker daemon -g /home/www/wwwroot/docker_image -H tcp://10.0.10.119:7878 -H fd://
vi /lib/systemd/system/docker.service
加载配置,直接restart会有提示
systemctl daemon-reload
/etc/init.d/docker restart
-H参数光指定tcp的不行,必须保留原来的unix socket /var/run/docker.sock,不然本地命令执行不了了。
Read more...