http://www.blogjava.net/yongboy/archive/2013/12/29/408173.html
---------------------------------------------------------
1104 add
use dockerfile and images build a image:TAG,docker run -t -i
docker run -t -i can modify the container ,docker commit will save the chages to the image
----------------------------------------------------------------
Docker images
A Docker image is a read-only template. For example, an image could contain an Ubuntu operating system with Apache
and your web application installed. Images are used to create Docker containers. Docker provides a simple way to
build new images or update existing images, or you can download Docker images that other people have already
created. Docker images are the build component of Docker.
Docker Registries
Docker registries hold images. These are public or private stores from which you upload or download images. The
public Docker registry is called Docker Hub. It provides a huge collection of existing images for your use. These
can be images you create yourself or you can use images that others have previously created. Docker registries are
the distribution component of Docker.
Docker containers
Docker containers are similar to a directory. A Docker container holds everything that is needed for an application
to run. Each container is created from a Docker image. Docker containers can be run, started, stopped, moved, and
deleted. Each container is an isolated and secure application platform. Docker containers are the run component of
Docker.
#第二种方法,下载下来然后根据自己配置安装
wget http://docker.widuu.com/ubuntu.tar
cat test.tar | sudo docker import - xiaowei:new
---------------------------------------------
Docker使用了一种叫AUFS的文件系统,这种文件系统可以让你一层一层地叠加修改你的文件,最底下的文件系统是只读的,如果需要修改文件,AUFS会增加一个可写的层(Layer),这样有很多好处,例如不同的Container可以共享底层的只读文件系统(同一个Kernel),使得你可以跑N多个Container而不至于你的硬盘被挤爆了!这个只读的层就是Image!而如你所看到的,一个可写的层就是Container。
那Image和Container的区别是什么?很简单,他们的区别仅仅是一个是只读的层,一个是可写的层,你可以使用docker commit 命令,将你的Container变成一个Image,也就是提交你所运行的Container的修改内容,变成一个新的只读的Image,这非常类似于git commit命令
---------------------------------------------------------
part 1
http://docker.widuu.com/
————————————————————————————————
part2
Docker使用的是Linux容器,这是运行在与它的宿主机器同样的操作系统上。这准许它可以和宿主机器共享许多系统资源。它也会使用AuFS作为文件系统,也为你管理网络。
AuFS是一个层状的文件系统,因此你可以有一个只读部分和一个只写部分,然后将二者组合起来。你可以使系统的共同的部分用作只读,那块是被所有容器共享,并且给每个容器自己的可写区域
这几天一直搞KVM,但发现目前的应用实在是太简单了,多启动几个kvm有点浪费了。现在N多人都在讨论Docker,所以就查了下看看,安装在Centos6.5上,其它的也没多试。
使用Centos安装它还是挺方便的,配置好epel源后直接 yum install docker-io即可安装。service docker start就启动了,还是比较简单的。依赖安装包有libcgroup,lxc,lxc-libs。cgroup lxc这里不介绍了。
因为Centos比较熟悉,所以先下载它。获取最新的docker image 使用 docker pull centos即可。使用docker run -i -t centos /bin/bash就可以运行了。当初以为运行后直接配置个IP、启动SSH就行了,可搞了半天都不对,N多错误。后来找了下,Docker其实不是这个样子玩滴。
所以综合了下,还是自己根据Dockerfile来创建一个新的镜像吧,首先就是要编译Dockerfile,它的内容如下
# cat Dockerfile
# liuxin/centos:ssh
#
# VERSION 0.0.1
FROM centos
MAINTAINER liuxin "[email protected]"
RUN yum install -y openssh openssh-server openssh-clients
RUN mkdir /var/run/sshd
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
RUN /bin/echo 'root:aaaaaa' |chpasswd
RUN useradd liu
RUN /bin/echo 'liu:aaaaaa' |chpasswd
RUN /bin/sed -i 's/.*session.*required.*pam_loginuid.so.*/session optional pam_loginuid.so/g' /etc/pam.d/sshd
RUN /bin/echo -e "LANG=\"en_US.UTF-8\"" > /etc/default/local
EXPOSE 22
CMD /usr/sbin/sshd -D
然后在文件所在目录下执行 docker build -rm -t liuxin/centos:ssh . (这里有个点,别忽略了),等一会就可以看到过程了,这里就不复制了。
这里的两条ssh-keygen要加上,如果不加ssh启动就会报错。因为网上大多都是Ubuntu的,当初我照着U的系统来做,根本没成功。理论上来说/usr/sbin/sshd -D就会创建了主机的rsakey,但U系统可以C系统就没成。
当执行完后,使用docker images就能看到自己创建,如下
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
liuxin/centos ssh 2854690ce065 About an hour ago 306.7 MB
ubuntu latest 7dc370b825b0 2 hours ago 275.3 MB
centos centos7 b157b77b1a65 4 weeks ago 243.7 MB
centos latest b157b77b1a65 4 weeks ago 243.7 MB
然后执行docker run -d -p 22 liuxin/centos:ssh、docker ps -a就可以看到
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
72a06bd5bf41 liuxin/centos:ssh /bin/sh -c '/usr/sbi About an hour ago Up About an hour 0.0.0.0:49153->22/tcp sleepy_euclid
这里的49153就是宿主机的端口,22就是Docker的ssh端口
然后运行ssh 127.0.0.1(或者其它IP) -p 49153 就可以ssh连接到Docker启动的这个了。
这里没介绍关于Docker的一些常用的命令,可以看下http://blog.chinaunix.net/uid-10915175-id-4443127.html
参考
https://docs.docker.com/installation/centos/
https://docs.docker.com/userguide/dockerimages/
https://docs.docker.com/examples/running_ssh_service/
_______________________________
1. 查看docker信息(version、info)
- # 查看docker版本
- $docker version
-
- # 显示docker系统的信息
- $docker info
2. 对image的操作(search、pull、images、rmi、history)
- # 检索image
- $docker search image_name
-
- # 下载image
- $docker pull image_name
-
- # 列出镜像列表; -a, --all=false Show all images; --no-trunc=false Don't truncate output; -q, --quiet=false Only show numeric IDs
- $docker images
-
- # 删除一个或者多个镜像; -f, --force=false Force; --no-prune=false Do not delete untagged parents
- $docker rmi image_name
-
- # 显示一个镜像的历史; --no-trunc=false Don't truncate output; -q, --quiet=false Only show numeric IDs
- $docker history image_name
3. 启动容器(run)
docker容器可以理解为在沙盒中运行的进程。这个沙盒包含了该进程运行所必须的资源,包括文件系统、系统类库、shell 环境等等。但这个沙盒默认是不会运行任何程序的。你需要在沙盒中运行一个进程来启动某一个容器。这个进程是该容器的唯一进程,所以当该进程结束的时候,容器也会完全的停止。
- # 在容器中运行"echo"命令,输出"hello word"
- $docker run image_name echo "hello word"
-
- # 交互式进入容器中
- $docker run -i -t image_name /bin/bash
-
-
- # 在容器中安装新的程序
- $docker run image_name apt-get install -y app_name
Note: 在执行apt-get 命令的时候,要带上-y参数。如果不指定-y参数的话,apt-get命令会进入交互模式,需要用户输入命令来进行确认,但在docker环境中是无法响应这种交互的。apt-get 命令执行完毕之后,容器就会停止,但对容器的改动不会丢失。
4. 查看容器(ps)
- # 列出当前所有正在运行的container
- $docker ps
- # 列出所有的container
- $docker ps -a
- # 列出最近一次启动的container
- $docker ps -l
5. 保存对容器的修改(commit)
当你对某一个容器做了修改之后(通过在容器中运行某一个命令),可以把对容器的修改保存下来,这样下次可以从保存后的最新状态运行该容器。
- # 保存对容器的修改; -a, --author="" Author; -m, --message="" Commit message
- $docker commit ID new_image_name
Note: image相当于类,container相当于实例,不过可以动态给实例安装新软件,然后把这个container用commit命令固化成一个image。
6. 对容器的操作(rm、stop、start、kill、logs、diff、top、cp、restart、attach)
- # 删除所有容器
- $docker rm `docker ps -a -q`
-
- # 删除单个容器; -f, --force=false; -l, --link=false Remove the specified link and not the underlying container; -v, --volumes=false Remove the volumes associated to the container
- $docker rm Name/ID
-
- # 停止、启动、杀死一个容器
- $docker stop Name/ID
- $docker start Name/ID
- $docker kill Name/ID
-
- # 从一个容器中取日志; -f, --follow=false Follow log output; -t, --timestamps=false Show timestamps
- $docker logs Name/ID
-
- # 列出一个容器里面被改变的文件或者目录,list列表会显示出三种事件,A 增加的,D 删除的,C 被改变的
- $docker diff Name/ID
-
- # 显示一个运行的容器里面的进程信息
- $docker top Name/ID
-
- # 从容器里面拷贝文件/目录到本地一个路径
- $docker cp Name:/container_path to_path
- $docker cp ID:/container_path to_path
-
- # 重启一个正在运行的容器; -t, --time=10 Number of seconds to try to stop for before killing the container, Default=10
- $docker restart Name/ID
-
- # 附加到一个运行的容器上面; --no-stdin=false Do not attach stdin; --sig-proxy=true Proxify all received signal to the process
- $docker attach ID
Note: attach命令允许你查看或者影响一个运行的容器。你可以在同一时间attach同一个容器。你也可以从一个容器中脱离出来,是从CTRL-C。
7. 保存和加载镜像(save、load)
当需要把一台机器上的镜像迁移到另一台机器的时候,需要保存镜像与加载镜像。
- # 保存镜像到一个tar包; -o, --output="" Write to an file
- $docker save image_name -o file_path
- # 加载一个tar包格式的镜像; -i, --input="" Read from a tar archive file
- $docker load -i file_path
-
- # 机器a
- $docker save image_name > /home/save.tar
- # 使用scp将save.tar拷到机器b上,然后:
- $docker load < /home/save.tar
8、 登录registry server(login)
- # 登陆registry server; -e, --email="" Email; -p, --password="" Password; -u, --username="" Username
- $docker login
9. 发布image(push)
- # 发布docker镜像
- $docker push new_image_name
10. 根据Dockerfile 构建出一个容器
- #build
- --no-cache=false Do not use cache when building the image
- -q, --quiet=false Suppress the verbose output generated by the containers
- --rm=true Remove intermediate containers after a successful build
- -t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
- $docker build -t image_name Dockerfile_path
----------------------------------------------------------------
命令实践
[root@private-cloud-59 DragonBall]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
49e7b710ab2c application-all:hudson-20141030054318 "/bin/sh -c 'cat /op 7 hours ago Up 7 hours 192.168.1.59:80->80/tcp, 192.168.1.59:10022->22/tcp grave_pare
[root@private-cloud-59 DragonBall]# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
application-all hudson-20141030054318 1f24f092f12e 7 hours ago 1.809 GB
application-config-s1 hudson-20141030054318 39bc69376a89 7 hours ago 1.808 GB
[root@private-cloud-59 DragonBall]# docker logs49e7b710ab2c(containter ID from docker ps)
docker run -ti application-config-s1:hudson-20141030054318(from docker images)
-------------------------------------------------------------
unbuntu==application-config-s1:hudson-20141030054318(from docker images)
1. 获取最近运行容器的id
这是我们经常会用到的一个操作,按照官方示例,你可以这样做(环境ubuntu):
1
2
3
4
|
$ ID=$(docker run ubuntu
echo
hello world)
hello world
$ docker commit $ID helloworld
fd08a884dc79
|
这种方式在编写脚本的时候很有用,比如你想在脚本中批量获取id,然后进一步操作。但是这种方式要求你必须给ID赋值,如果是直接敲命令,这样做就不太方便了。 这时,你可以换一种方式:
1
2
3
4
5
6
7
|
$
alias
dl=’docker
ps
-l -q’
$ docker run ubuntu
echo
hello world
hello world
$ dl
1904cf045887
$ docker commit `dl` helloworld
fd08a884dc79
|
docker ps -l -q命令将返回最近运行的容器的id,通过设置别名(alias),dl命令就是获取最近容器的id。这样,就无需再输入冗长的docker ps -l -q命令了。通过两个斜引号“,可以获取dl命令的值,也就是最近运行的容器的id。
2.尽量在Dockerfile中指定要安装的软件,而不用Docker容器的shell直接安装软件
说实话,我有时候也喜欢在shell中安装软件,也许你也一样,喜欢在shell中把所有软件安装都搞定。但是,搞来搞去,最后还是发现,你还是需要在Doockerfile中指定安装文件。在shell中安装软件,你要这样做:
1
2
|
$ docker run -i -t ubuntu
bash
root@db0c3967abf8:/
|
然后输入下面的命令来安装文件:
1
|
apt-get
install
postgresql
|
然后再调用exit:
退出docker容器,再给docker commit命令传递一个复杂的JSON字符串来提交新的镜像:
1
|
$ docker commit -run=”{“Cmd”:[“postgres”,”-too -many -opts”] }” `dl` postgres
|
太麻烦了,不是吗?还是在Dockerfile中指定安装文件吧,只要两个步骤:
1
2
|
1.在一个小巧的Dockerfile中,指定当前操作的镜像为FROM命令的参数
2.然后在Dockerfile中指定一些docker的命令,如CMD, ENTERPOINT, VOLUME等等来指定安装的软件
|
3.超-超-超级用户
你可能需要一直用超级用户来操作docker,就像早期示例里一直提示的:
1
2
3
4
5
6
7
8
|
$
sudo
groupadd docker
$
sudo
gpasswd -a myusername docker
$
sudo
service docker restart
$
exit
|
Wow!连续三个sudo!三次化身“超级用户”,真可谓是“超-超-超级用户”啊!别担心,设置完毕,以后你就再也不用打那么多sudo了!
4. 清理垃圾
如果你想删除所有停止运行的容器,用这个命令:
1
|
$ docker
rm
$(docker
ps
-a -q)
|
顺便说一句,docker ps命令很慢,不知道为啥这么慢,按理说Go语言是很快的啊。docker ps -a -q命令列出所有容器的id,然后根据id删除容器。docker rm命令遇到正在运行的容器就会失效,所以这个命令完美的删除了所有没在运行的容器。
5. docker inspect输出结果的解析利器:jq
要对docker inspect的输出结果进行过滤,一般情况下,用grep命令,你需要这样操作:
1
|
$docker inspect `dl` |
grep
IPAddress |
cut
-d ‘“‘ -f 4 172.17.0.52
|
哦!看上去很复杂,用jq吧,专业解析docker inspect输出结果,具有更强的可读性,方便易用:
1
|
$docker inspect `dl` | jq -r ‘.[0].NetworkSettings.IPAddress’ 172.17.0.52
|
其中第一个’.’代表所有的结果。’[0]’代表数组的第一个元素。就像JavaScript访问一个JSON对象一样,简单方便。
6.镜像有哪些环境变量?
有时候,你需要知道自己创建的镜像有哪些环境变量。简单!只要这样:
输出结果如下:
1
2
3
4
|
HOME=/
PATH=
/usr/local/sbin
:
/usr/local/bin
:
/usr/sbin
:
/usr/bin
:
/sbin
:
/bin
container=lxc
HOSTNAME=5e1560b7f757
|
调用env查看环境变量,对于后面要讲到的“链接”(-link)很有用,在连接两个容器时候需要用到这些环境变量,具体请看最后一个要点“链接”。
7.RUN命令 vs CMD命令
Docker的新手用户比较容易混淆RUN和CMD这两个命令。
RUN命令在构建(Build)Docker时执行,这时CMD命令不执行。CMD命令在RUN命令执行时才执行。我们来理清关系,假设Dockerfile内容如下:
1
2
|
FROM thelanddownunder
MAINTAINER crocdundee
|
我们要向系统中安装一些软件,那么:
1
2
3
4
5
|
RUN apt-get update
RUN apt-get
install
softwares
CMD [“softwares”]
|
Build时执行RUN,RUN时执行CMD,也就是说,CMD才是镜像最终执行的命令。
8.CMD命令 vs ENTRYPOINT命令
又是两条容易混淆的命令!具体细节我们就不说了,举个例子,假设一个容器的Dockerfile指定CMD命令,如下:
1
2
|
FROM ubuntu
CMD [“
echo
”]
|
另一个容器的Dockerfile指定ENTRYPOINT命令,如下:
1
2
|
FROM ubuntu
ENTRYPOINT [“
echo
”]
|
运行第一个容器:
1
|
docker run image1
echo
hello
|
得到的结果:
运行第二个容器:
1
|
docker run image2
echo
hello
|
得到的结果:
看到不同了吧?实际上,CMD命令是可覆盖的,docker run后面输入的命令与CMD指定的命令匹配时,会把CMD指定的命令替换成docker run中带的命令。而ENTRYPOINT指定的命令只是一个“入口”,docker run后面的内容会全部传给这个“入口”,而不是进行命令的替换,所以得到的结果就是“echo hello”。
9.Docker容器有自己的IP地址吗?
刚接触Docker的人或许会有这样的疑问:Docker容器有自己的IP地址吗?Docker容器是一个进程?还是一个虚拟机?嗯…也许两者兼具?哈哈,其实,Docker容器确实有自己的IP,就像一个具有IP的进程。只要分别在主机和Docker容器中执行查看ip的命令就知道了。
查看主机的ip:
1
|
$ ip -4 -o addr show eth0
|
得到结果:
1
|
2: eth0 inet 162.243.139.222
/24
|
查看Docker容器的ip:
1
|
$ docker run ubuntu ip -r -o addr show eth0
|
得到结果:
1
|
149: eth0 inet 172.17.0.43
/16
|
两者并不相同,说明Docker容器有自己的ip。
10.基于命令行的瘦客户端,使用UNIX Socket和Docker后台服务的REST接口进行通信
Docker默认是用UNIX socket通信的,一直到大概0.5、0.6的版本还是用端口来通信,但现在则改成UNIX socket,所以从外部无法控制Docker容器的内部细节。下面我们来搞点有趣的事情,从主机链接到docker的UNIX socket:
1
2
|
$ nc -U /
/var/run/docker
.sock
|
连接成功后,输入:
1
|
GET
/images/json
HTTP
/1
.1
|
输入后连敲两个回车,第二个回车表示输入结束。然后,得到的结果应该是:
1
2
3
4
5
6
|
HTTP
/1
.1 200 OK
Content-Type: application
/json
Date: Tue, 05 Nov 2013 23:18:09 GMT
Transfer-Encoding: chunked
16aa
[{“Repository”:”postgres”,”Tag”:”......
|
有一天,我不小心把提交的名称打错了,名字开头打成”-xxx”(我把命令和选项的顺序搞混了),所以当我删除的时候出了问题,docker rm -xxx,会把-xxx当成参数而不是镜像的名称。所以我只得通过socket直接连到容器来调用REST Server把错误的东西删掉。
11.把镜像的依赖关系绘制成图
docker images命令有一个很拉风的选项:-viz,可以把镜像的依赖关系绘制成图并通过管道符号保存到图片文件:
1
2
|
$ docker images -viz | dot -T png -o docker.png
|
这样,主机的当前路径下就生成了一张png图,然后,用python开启一个微型的HTTP服务器:
1
|
python -m SimpleHTTPServer
|
然后在别的机器上用浏览器打开:
1
|
http:
//machinename
:8000
/docker
.png
|
OK,依赖关系一目了然!
(译者注:要使用dot命令,主机要安装graphviz包。另外,如果主机ip没有绑定域名,machinename换成主机的ip即可。)
12.Docker把东西都存到哪里去了?
Docker实际上把所有东西都放到/var/lib/docker路径下了。切换成super用户,到/var/lib/docker下看看,你能学到很多有趣的东西。执行下面的命令:
1
2
3
4
|
$
sudo
su
containers/ graph/ repositories volumes/
|
可以看到不少目录,containers目录当然就是存放容器(container)了,graph目录存放镜像,文件层(file system layer)存放在graph/imageid/layer路径下,这样你就可以看看文件层里到底有哪些东西,利用这种层级结构可以清楚的看到文件层是如何一层一层叠加起来的。
13.Docker源代码:Go, Go, Go, Golang!
Docker的源代码全部是用Go语言写的。Go是一门非常酷的语言。其实,不只是Docker,很多优秀的软件都是用Go写的。对我来说,Docker源文件中,有4个是我非常喜欢阅读的:
commands.go
docker的命令行接口,是对REST API的一个轻量级封装。Docker团队不希望在命令中出现逻辑,因此commands.go只是向REST API发送指令,确保其较小的颗粒性。
api.go
REST API的路由(接受commands.go中的请求,转发到server.go)
server.go
大部分REST API的实现
buildfile.go
Dockerfile的解析器
有的伙计惊叹”Wow!Docker是怎么实现的?!我无法理解!”没关系,Docker是开源软件,去看它的源代码就可以了。如果你不太清楚Dockerfile中的命令是怎么回事,直接去看buildfile.go就明白了。
14.运行几个Docker后台程序,再退出容器,会发生什么?
OK,倒数第二个要点。如果在Docker中运行几个后台程序,再退出Docker容器,会发生什么?答案是:不要这么做!因为这样做后台程序就全丢了。
Dockerfile中用RUN命令去开启一个后台程序,如:
这样的话,RUN命令开启的后台程序就会丢失。调用容器的bash连到容器的shell:
1
|
$ docker run -i -t postgresimage
bash
|
然后调用 ps aux查看进程,你会发现postgres的进程并没有跑起来。 RUN命令会影响文件系统。因此,不要再Dockerfile中用启动后台程序,要把后台程序启动成前台进程。或者,像一些高手提议的那样,写一个启动脚本,在脚本中启动这些后台程序或进程。
15.容器之间进行友好沟通:链接
这是最拉风的功能!我把它留到最后压轴!这是0.6.5中最重要的新功能,我们前面已经提过两次了。运行一个容器,给它一个名称,在下面的例子中,我们通过-name参数给容器指定名称”loldb”:
1
|
$ docker run -d -name loldb loldbimage
|
再运行另一个容器,加上-link参数来连接到第一个容器(别名为loldb),并给第二个容器也指定一个别名(这里用的是cheez):
1
|
$ docker run -link
/loldb
:cheez otherimage
env
|
顺便得到cheez的环境变量:
1
2
3
4
5
|
CHEEZ_PORT=tcp:
//172
.17.0.8:6379
CHEEZ_PORT_1337_TCP=tcp:
//172
.17.0.8.6379
CHEEZ_PORT_1337_TCP_ADDR=tcp:
//172
.17.0.12
CHEEZ_PORT_1337_TCP_PORT=6379
CHEEZ_PORT_1337_TCP_PROTO=tcp
|
这样,我们就在两个容器间建立起一个网络通道(bridge),基于此,我们可以建立一个类似rails的程序:一个容器可以访问数据库容器而不对外暴露其他接口。非常酷!数据库容器只需要知道第一个容器的别名(在本例中为cheez)和要打开的端口号。所以数据库容器也可以env命令来查看这个端口是否打开。