Docker的镜像和容器,是课程第三章的内容

1.Docker架构和底层技术简介

2.Docker Image概述

1
2
3
4
# 查看现有的Image
sudo docker image ls
# 拉取镜像,如centos镜像
sudo docker pull centos

Image的获取方法:

  1. Build from dockerfile
  2. pull from registry (https://hub.docker.com/explore/,或者是其他私有仓库)

3.DIY一个Base Image

3.1优化输入docker命令的时候不用加sudo

  1. sudo groupadd docker,创建docker用户组,不过这个组一般安装好docker之后就会有了
  2. sudo gpasswd -a vagrant docker,把vagrant这个用户加入到docker用户组
  3. 有的时候会需要重新打开terminal才可以

3.2通过Dockerfile简单构建镜像

1
2
3
4
5
FROM scratch
# 把hello这个可执行文件拷贝到/路径下
ADD hello /
# 运行hello
CMD ["/hello"]

docker build -t liguoqinjim/hello-world .,构建镜像的命令,".“表示在当前目录里面找Dockerfile,-t指定了镜像的名称

4.初识Container

什么是Container

4.1容器相关命令:

  • docker container ls,列出现在正在运行的容器
  • docker container ls -a,列出所有的容器,包括没有在运行的
  • docker ps -a,列出所有的容器,包括没有在运行的
  • docker run -it centos,交互式运行容器的方法,这样的话运行之后contianer不会退出,但是这个对话关闭这个,contianer还是就会退出了
  • docker container rm container_id,可以删除一个容器(注意,这里的id可以是完整的容器id,也可以是缩写,只要是可以和别的id区分开的就可以了)
  • docker rm container_id,默认就是去删除container的,所以是和docker container rm id是一样的
  • docker images,和docker image ls是一样的,显示本机的所有镜像
  • docker image rm image_id,删除image,可以是image_id或者image_name
  • docker rmi id,是和docker image rm id一样的,是简写
  • docker container ls -aq,只会显示出容器的id,在批量删除容器的时候有用
  • docker rm $(docker container ls -aq),删除所有的容器
  • docker rm $(docker ps -aq),也是删除所有的容器
  • docker rm $(docker container ls -f "status=exited" -q),删除所有不在运行的容器

4.2docker镜像拉取加速:

5.构建自己的Docker镜像

有两种方法可以构建镜像,一种是通过容器创建,我们创建一个容器,比如在里面安装软件,安装完成之后,我们把这个容器commit成镜像。 还有一种是通过Dockerfile来打包镜像。比较推荐Dockerfile的方式,因为使用Dockerfile别人可以清楚看到这个镜像是怎么打包的。 但是要是我们使用commit的方式,别人无法了解我们是怎么生成镜像的,有没有发一些恶意代码等等。

5.1docker commit的方式创建镜像

docker commit docker_name liguoqinjim/centos_vim
这个命令就是把docker_name这个容器,生成一个镜像,镜像的名称是liguoqinjim/centos_vim

5.2使用Dockerfile创建镜像

docker build -t liguoqinjim/centos_vim_new .
“.“表示使用当前目录的Dockerfile来创建镜像,liguoqinjim/centos_vim_new就是镜像的名字

NOTICE

在docker中镜像是只读的,也就是不能写的。那么docker是怎么实现在镜像里面安装好vim的呢?
这是因为docker会自动启动一个container,然后在container里面安装vim,然后再通过这个container来制作成镜像。

6.Dockerfile语法梳理及最佳实践

Dockerfile命令:

6.1FROM

尽量使用官方镜像作为base image

1
2
3
FROM scratch #制作base image,不依靠别的镜像
FROM centos #使用base image
FROM ubuntu:14.04 #使用base image

6.2LABEL

设置Metadata

1
2
3
LABEL maintainer="xiaoquwl@gmail.com"
LABEL version="1.0"
LABEL description="This is description"

6.3RUN

run命令为了美观,最好使用反斜杠换行。
每运行一次run,image都会生成新的一层,所以尽量合并多条命令成为一行

1
2
3
4
5
6
7
# 使用反斜杠换行
RUN yum update && yum install -y vim \
    python-dev   
# 主要清理cache
RUN apt-get update && apt-get install -y perl \
    pwgen --no-install-recommends && rm -rf \
    /var/lib/apt/lists/*

6.4WORKDIR

能用WORKDIR的就用WORKDIR,不要用RUN去运行cd命令。
WORKDIR后面跟的路径也可以尽量使用绝对路径

1
2
3
4
5
6
7
# 切换到/root路径
WORKDIR /root
# 如果没有会自动创建test文件夹
WORKDIR /test 
WORKDIR demo
# 输出结果是/test/demo
RUN pwd

6.5ADD and COPY

大部分情况下COPY是要优于ADD的,但是ADD有一个解压缩的功能。
要是想添加远程文件的话,可以用RUN命令来运行curl或者wget

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 把hello这个可执行文件添加到/路径下
ADD hello /
# 添加到根目录下并且解压test.tar.gz
ADD test.tar.gz /
# 文件应该是在/root/test/hello
WORKDIR /root
ADD hello test/
# copy版本
WORKDIR /root
COPY hello test/

6.6ENV

使用ENV可以增加可维护性

1
2
3
4
5
# 设置ENV
ENV MYSQL_VERSION 5.6
# 使用ENV
RUN apt-get install -y mysql-server= "${MYSQL_VERSION}" \
    && rm -rf /var/lib/apt/lists/*

NOTICE

可以参考官方给出的一些例子,和一些官方镜像的Dockerfile
链接:

7.RUN vs CMD vs ENTRYPOINT

RUN:执行命令并创建新的Image Layer
CMD:设置容器启动后默认执行的命令和参数
ENTRYPOINT:设置容器启动时运行的命令
这三个命令里面,CMD和ENTRYPOINT是一组的,RUN和他们两个是不一样的

7.1Shell和Exec格式的区别

Shell格式

1
2
3
RUN apt-get install -y vim
CMD echo "hello docker"
ENTRYPOINT echo "hello docker"
1
2
3
FROM centos
ENV name Docker
ENTRYPOINT echo "hello $name"

###Exec格式

1
2
3
RUN ["apt-get","install","-y","vim"]
CMD ["/bin/echo","hello docker"]
ENTRYPOINT ["bin/echo","hello docker"]
1
2
3
FROM centos
ENV name Docker
ENTRYPOINT ["bin/echo","hello $name"]

NOTICE

SHELL格式的时候,echo "hello $name"是会把$name替换掉的,
但是在Exec的格式中,直接使用ENTRYPOINT ["/bin/echo","hello $name"]是不会把$name替换掉的,因为这样不是在shell中运行,只是调用了echo命令 想要替换掉$name,需要用ENTRYPOTIN ["/bin/bash","-c","echo hello $name"]

7.2CMD和ENTRYPOINT的区别

CMD

CMD

ENTRYPOINT

ENTRYPOINT

NOTICE

  • CMD是可能会被忽略的,但是ENTRYPOINT不会
  • 如果定义多个CMD,只有最后一个会被执行

8.镜像的发布

镜像的发布一共有三种方式:

  1. docker push到hub docker
  2. docker hub和github相连接,我们在github里面写好dockerfile,docker hub会自动打成镜像
  3. 自建docker registry

8.1docker push

1
2
3
4
#登录docker hub的账号
docker login
#push镜像,注意镜像名称的开头需要和docker hub的账号是一样的
docker push liguoqinjim/hello-world

8.2docker hub和github相连接

Dockerhub

8.3自建docker registry

  1. 启动docker registry,docker run -d -p 5000:5000 --restart always --name registry registry:2
  2. 修改/etc/docker/daemon.json
    在deamon.json里面添加,“insecure-registries:[“10.75.44.222:5000”]",10.75.44.222就是本机的ip
  3. 重启docker
  4. docker push localhost:5000/hello-world,推送镜像到docker registry,注意image的开头一定要是docker registry的地址
  5. 可以通过api查看是否push成功,curl localhost:5000/v2/_catalog,这个是docker registry提供的api

9.Dockerfile实战

怎么把一个写好的服务放进docker里面:

  1. 先FROM编程语言的官方库
  2. RUN命令安装需要的依赖,如(pip install等)
  3. 把程序放进通过ADD或者COPY放到镜像里面
  4. EXPOSE 端口,把服务的端口暴露
  5. CMD里面运行程序即可
  6. docker run -d image_name,这样我们就运行了这个镜像,且是在后台运行

NOTICE

在创建镜像的时候要是出错的话,因为docker在创建镜像的时候,每一步都会创建一个中间镜像。要调试的时候,我们可以run这些中间镜像去调试,比如docker run -it 中间镜像id /bin/bash,就可以进入启动一个这个镜像的容器,然后进入内部查看有什么错误

10.容器的操作

关于容器的几个命令:

  • docker run -d --name=demo liguoqinjim/flask-hello-world,运行一个容器,–name可以指定容器的名称,不然docker会指定一个随机的名称
  • docker exec -it container_id /bin/bash,exec可以让容器执行一个命令,在这个命令里面我们用交互式的方式执行/bin/bash,执行完之后我们就会进入容器内部。当然也是可以执行其他的命令的
  • docker exec -it container_id ip a,运行ip a命令
  • docker stop container_id,停止一个容器
  • docker start container_id,启动一个容器
  • docker inspect container_id,查看一个容器的详细参数
  • docker logs container_id,会打印出容器里面的输出

11.Dockerfile实战(2)

docker的容器大致可以分类两类,一种是app或者服务,就是一直运行在后台的程序。还有一种就是工具类的,运行一次出个结果。

这里引入一个linux下的工具软件,stress,是一个linux系统下测试系统性能的工具。
安装:apt-get update && apt-get install -y stress
使用:stress --vm 1 --verbose

使用ENTRYPOINT和CMD配合来获得运行容器时候输入的参数,ENTRYPOINT是要运行的命令,CMD获取运行容器时候输入的命令
如:docker run -it liguoqinjim/ubuntu-stress --vm 1,这样--vm 1就传给了/usr/bin/stress并运行

1
2
3
4
FROM ubuntu
RUN apt-get update && apt-get install -y stress
ENTRYPOINT ["/usr/bin/stress"]
CMD []

12.容器的资源限制

可以在容器运行的时候加上参数对容器的资源进行限制。

12.1限制内存

1
docker run --memory=300M liguoqinjim/ubuntu-stress --vm 1 --verbose

–memory设置了300M内存,但是要是没有设置–memory-swap的话,他的默认值就和–memory一样。那么在–memory设为200的时候,机器的内存就可以认为是200的memory加上200的memory-swap,也就是400M的内存,在上面这个命令中就是600M内存

12.2限制cpu

1
2
docker run --cpu-shares=10 --name=test1 liguoqinjim/ubuntu-stress --cpu 1
docker run --cpu-shares=5 --name=test2 liguoqinjim/ubuntu-stress --cpu 1

–cpu-shares是docker的参数,–cpu是给stress的参数。–cpu-shares就是当有多个设置了–cpu-shares的时候,他们分配的到的cpu就是他们的权重。比如上面的情况就是test1可以使用大约66%的cpu,而test2可以使用33%