Docker的内容很多的,不是一篇文章就能讲清楚的,而且不是一朝一夕就能掌握的,只能在工作中慢慢积累
Docker是什么?我相信每个人心中都有一个哈姆雷特。我简单说说自己的看法。
记得以前电脑很贵,微软系统很流行。为了解决兼容性测试问题,我们搭建了很多虚拟机。当时比较流行的是vitural box, vmware。比如当时最新系统是win7,但是市面上还有vista, xp。就可以安装这样的虚拟机,如果每个系统需要不同的浏览器和其它软件,那么还得在虚拟机中安装。整个过程耗时很长,而且比较耗费资源。
另外一个问题是,在测试上线过程中,明明UAT测试环境过程中,结果是好的,可到了QA环境,就是有问题。让开发排查,费了九牛二虎之力,发现却是环境问题。
Docker的想法是创建软件程序可移植的轻量容器,让其可以在任何安装了Docker的机器上运行,而不用关心底层操作系统,类似船舶使用的集装箱。容器化现在是一个趋势,Docker现在被很多公司所采用,像在devops中,微服务中,用得比较多。
Docker可以解决虚拟机能够解决的问题,同时也能够解决虚拟机由于资源要求过高而无法解决的问题。Docker能处理的事情包括:
标准化应用发布,docker容器包含了运行环境和可执行程序,可以跨平台和主机使用;
节约时间,快速部署和启动,VM启动一般是分钟级,docker容器启动是秒级; 方便构建基于SOA架构或微服务架构的系统,通过服务编排,更好的松耦合; 节约成本,以前一个虚拟机至少需要几个G的磁盘空间,docker容器可以减少到MB级;
方便持续集成,通过与代码进行关联使持续集成非常方便; 可以作为集群系统的轻量主机或节点,在IaaS平台上,已经出现了CaaS,通过容器替代原来的主机。
Docker的优点就是容器化。什么是容器化?就如同国际贸易,用的是集装箱。如洋山港码头的集装箱里,有可能集装箱里装的是义乌的小商品,也可能是华为的手机,也可以能是某种农产品。这样就运输方便,管理方便。 这也解决了前面几个问题:用docker,轻巧方便,可以自己定制化,而且可以复用,这样就不会环境不同而引起不必要的环境问题。
Docker提供功能广泛,这里有几个的例子:
镜像是Docker管理最基础的部分,同时也是Docker最大的亮点。镜像管理涉及到镜像的制作、更新、存储、分发、权限等多个方面。
镜像制作方面,应该坚持三个原则,第一是坚持镜像总是从Dockerfile生成。这样做最大的好处是可以通过Dockerfile“阅读”镜像,在后续的协作、升级维护等方面会带来巨大的便利。第二是镜像之间应该避免依赖过深,建议为三层,这三层分别是基础的操作系统镜像、中间件镜像和应用镜像。第三是坚持所有镜像都应该有对应的Git仓库,以方便后续的更新。
镜像的更新需要一个自动化的流程,这可以通过SCM和CI系统自动触发实现。具体的流程如下图所示。开发者首先将代码和Dockerfile提交到Git仓库,然后Git通过webhook方式触发Jenkins的主动获取代码和Dockerfile文件,Jenkins再通过Docker相关的插件生成镜像并推送镜像到私有的Registry。这样,在服务器上就可以通过拉取新的镜像部署容器。
关于Registry,可能会涉及三方面的问题,一个是单点问题,对应的解决方案可以考虑DRBD、分布式存储以及云存储。二是Regitry的性能问题,目前可用的解决方案是通过HTTP反向代理缓存来加速Layer的下载。三是Registry用户权限,Nginx LUA可以提供一个简单快速的实现方案。
和传统的发布流程相比,Docker最大的好处是不需要考虑外部依赖,利用容器的自包含的特点,我们可以将发布回滚流程标准化和产品化。而传统的发布和回滚,需要casebycase去针对不同应用做升级回滚的方案。要做到基于Docker的发布,镜像的生成必须坚持自动化,否则会发现升级比传统的方法更麻烦。因此在现实中我们也发现很多企业将代码目录放到主机目录映射到容器内,这样做破坏了Docker的自包含特性,解决的办法是坚持应用镜像更新自动化。
由于容器是无状态的,所以存储在容器内的日志会随着容器的销毁而消失。stdout/stderr类型的日志,可通过logspout转发到syslog中心来收集。打印到文件的支持,比如accesslog,需要将日志存储到外部的Volume,并在Docker主机上使用logstash收集转发。
容器里没有CM agent,无法接收CM指令。CM运行到Host上也无法管理容器中的文件,如果手工修改容器内的配置,那新创建的容器仍然是旧的。配置大体上分为两种类型,一种是服务之间的连接信息,这种配置建议考虑使用服务发现系统,也可以使用一些更加传统的方法,比如通过环境变量来协调开发和生产环境的配置差异。一种是一般的配置文件参数,配置文件和Dockerfile应该一起存储到一个Git仓库,修改后自动build更新镜像。
目前Docker支持的网络包括Host网络、NAT网络、物理网桥和网络虚拟化。Host网络中容器和主机共享网络命名空间,不同容器需要做好端口规划,防止端口冲突。Nat网络是基于四层代理以及NAT技术,依赖portmap,进出都需要转发,性能低,主机上需要做好端口规划,容易搞混。物理网桥方案,和传统虚拟机的网桥没有区别,适合容器数量有限且相对静止的场景。网络虚拟化是基于隧道的overlay网络,目前开源方案有SocketPlane、Weave、Flannel,适合数量大,动态创建销毁容器的场景。
说了这么多概念,可能都晕了,我们来看看如何操作。
有一种方法是Homebrew安装,很简单的,一条命令搞定:
brew cask install docker
本人电脑是mac,去官网下了一个客户端安装。 docker默认官方镜像可能在国内访问速度会比较慢,pull镜像时间会很长。所以需要配置国内的镜像加速, 我配置了阿里云的和Daocloud
开一个terminal,敲一个命令看看是否能work了。
好了,现在到了比较烧脑的环节,各种命令的记忆了。
docker images
docker search mysql
自动下载最新版本
docker pull mysql
根据IMAGE ID也就是镜像id删除 例: 查询镜像
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql latest 91dadee7afee 10 days ago 477MB
删除某一个镜像
[root@localhost ~]# docker rmi 91dadee7afee
Untagged: mysql:latest
Untagged: mysql@sha256:4589ba2850b93d103e60011fe528fc56230516c1efb4d3494c33ff499505356f
Deleted: sha256:91dadee7afeebe274c51104d572ab6a2dc0ae97473f71afc57fbfd48c0ceb8aa
确认是否删除成功
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
1、查看正在运行的容器
docker ps
查看所有容器,包括没有启动的
docker ps -a
2、启动容器 1)、普通启动 无法与Linux通信,没有做端口映射
docker run --name myTomcat -d tomcat:8.5-alpine
--name:自定义容器名,不指定时,docker 会自动生成一个名称 -d:表示后台运行容器 image-name:指定运行的镜像名称以及 Tag 2)、端口映射启动
docker run --name myTomcat -d -p 8090:8080 tomcat:8.5-alpine
使用命令:docker run --name container-name:tag -d -p 服务器端口:Docker 端口 image-name --name:自定义容器名,不指定时,docker 会自动生成一个名称 -d:表示后台运行容器 image-name:指定运行的镜像名称以及 Tag -p 表示进行服务器与 Docker 容器的端口映射,默认情况下容器中镜像占用的端口是 Docker 容器中的端口与外界是隔绝的,必须进行端口映射才能访问 3、停止容器 根据容器的id来停止,使用命令 docker stop 容器id 代码如下:
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b3d1907c8c3d tomcat:8.5-alpine "catalina.sh run" 5 minutes ago Up 5 minutes 8080/tcp myTomcat
[root@localhost ~]# docker stop b3d1907c8c3d
b3d1907c8c3d
4、重启容器
docker start b3d1907c8c3d
5、删除容器 需要先停止容器,再删除容器
docker rm b3d1907c8c3d
显示所有容器的 ID,'-q' 只显示容器 ID 号
docker ps -aq
删除多个容器
docker rm $(docker ps -aq)
显示已经退出的容器 ID
docker ps -f "status=exited" -q
删除已经退出的容器
docker rm $(docker ps -f "status=exited" -q)
6、查看日志 直接用容器名字查看
docker logs myTomcat
7、查看容器详细信息
# docker inspect
docker inspect container-ID
8、端口映射 将容器的 80 端口映射到 docker 主机的 80 端口
docker run -p 80:80 image-name
9、指定 volume
# 在 Dockerfile 中定义 VOLUME ["/var/lib/mysql"]
# 指定从 mysql 镜像中创建的容器的 volume 在 /var/lib/mysql 中
docker run -v mysql:/var/lib/mysql --name mysql1 mysql
10、docker exec 相关操作 交互式进入后执行 /bin/bash 进行命令操作
docker exec -it container-ID /bin/bash
交互式进入后执行 python 进入 python 解释器
docker exec -it container-ID python
交互式进入后执行 ip a 查看运行中的容器 IP 地址
docker exec -it container-ID ip a
三、运行mysql 1、下载mysql
docker pull mysql:5.5
2、运行mysql镜像 错误运行:
docker run --name mysql01 -d mysql
正确运行: 需要用-e指定用户密码 并且需要端口映射
docker run --name mysql -e MYSQL_ROOT_PASSWORD=root -p 3307:3306 -d mysql:5.5
四、运行centos 1、下载centos
docker pull centos
2、启动centos
docker run -it --name mycentos2 -d centos
3、进入centos
docker exec -it mycentos2 /bin/bash
4、下载可以使用ifconfig命令的插件
yum install -y net-tools
5、运行ifconfig
[root@77bfb721d749 /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.17.0.3 netmask 255.255.0.0 broadcast 172.17.255.255
6、退出centos
exit
命令不需要死记硬背,记住几个常用的,关键的就可以了,如果不记得了, 可以参考:https://yeasy.gitbooks.io/docker_practice/content/appendix/best_practices.html
五、镜像定制 1.先下载centos镜像 如果有该镜像跳过此步
[root@docker ~]# docker pull centos
2.启动容器并进行配置 启动容器,
[root@docker ~]# docker run -it -d --name test-centos1 centos `
命令注释:-it : 进行交互式操作 -d : 等同于 -d=true,容器将会在后台运行,不然执行一次命令后,退出后,便是exit状态了。 --name : 容器启动后的名字,默认不指定,将会随机产生一个名字。或者使用 -name="containers_name" centos:使用的镜像名称 进入容器,安装ssh server,以及配置开机启动
[root@docker ~]# docker exec -it test-centos1 /bin/bash
[root@d72250ecaa5e /]# ifconfig
bash: ifconfig: command not found
*注:命令最后参数 /bin/bash:指进入容器时执行的命令 OK,配置基本完毕咯。清理命令历史记录,之后退出容器。现在可以生成一个新的docker 镜像了。 3.配置完成后,进行打包成新的镜像 1.普通打包镜像
[root@docker ~]# docker run -d -it --name my_centos centos_sshd ec17e553d5c4c60865afeb99df8dfd1f4e7d4ba6e1b0d5516f9127f09d1d6356
[root@docker ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ec17e553d5c4 centos_sshd "/auto_sshd.sh" 6 seconds ago Up 5 seconds 22/tcp my_centos
2.执行build 创建镜像
docker build -t centos_new .
Dockerfile其实可以看做一个命令集。 Dockerfile 由一行行命令语句组成,并且支持以 # 开头的注释行。 一般的,Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com
# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Commands when creating a new container
CMD /usr/sbin/nginx
FROM 格式为 FROM <image>或FROM <image>:<tag>。
第一条指令必须为 FROM 指令。并且,如果在同一个Dockerfile中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。
MAINTAINER 格式为 MAINTAINER <name>,指定维护者信息。
RUN 格式为 RUN <command> 或 RUN ["executable", "param1", "param2"]。
前者将在 shell 终端中运行命令,即 /bin/sh -c;后者则使用 exec 执行。指定使用其它终端可以通过第二种方式实现,例如 RUN
["/bin/bash", "-c", "echo hello"]
每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。
CMD 支持三种格式 CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式; CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用; CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数; 指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD 指定的命令。
EXPOSE 格式为 EXPOSE <port> [<port>...]。
告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P,Docker 主机会自动分配一个端口转发到指定的端口。
ENV 格式为 ENV <key> <value>。指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。
例如
ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
ADD 格式为 ADD <src> <dest>。
该命令将复制指定的 <src> 到容器中的 <dest>。其中 <src> 可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。
COPY 格式为 COPY <src> <dest>。
复制本地主机的 <src>(为 Dockerfile 所在目录的相对路径)到容器中的 <dest>。
当使用本地目录为源目录时,推荐使用 COPY。
ENTRYPOINT 两种格式:
ENTRYPOINT ["executable", "param1", "param2"]
ENTRYPOINT command param1 param2(shell中执行)
配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。
每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效。
VOLUME 格式为 VOLUME ["/data"]。
创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。
USER 格式为 USER daemon。
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。
当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres。要临时获取管理员权限可以使用 gosu,而不推荐 sudo。
WORKDIR 格式为 WORKDIR /path/to/workdir。
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。
可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c。
Doker file的不足 层数过多:过多行的Dockerfile 不能清理volume等配置:volume、expose等多个参数只能单向增加。不能删除。比如在某个镜像层加入了VOLUME /var/lib/docker。那么在该镜像之后的所有层将继承这一属性。
前面我们定义 Dockerfile 文件,然后使用 docker build、docker run 等命令操作容器。然而微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而知 使用 Docker Compose 可以轻松、高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具
Docker Compose 将所管理的容器分为三层,分别是工程(project)、服务(service)、容器(container) Docker Compose 运行目录下的所有文件(docker-compose.yml)组成一个工程,一个工程包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖,一个服务可包括多个容器实例 Docker Compose 常用命令与配置 常见命令 ps:列出所有运行容器
docker-compose ps
logs:查看服务日志输出
docker-compose logs
port:打印绑定的公共端口,下面命令可以输出 eureka 服务 8761 端口所绑定的公共端口
docker-compose port eureka 8761
build:构建或者重新构建服务
docker-compose build
start:启动指定服务已存在的容器
docker-compose start eureka
stop:停止已运行的服务的容器
docker-compose stop eureka
rm:删除指定服务的容器
docker-compose rm eureka
up:构建、启动容器
docker-compose up
kill:通过发送 SIGKILL 信号来停止指定服务的容器
docker-compose kill eureka
pull:下载服务镜像 scale:设置指定服务运气容器的个数,以 service=num 形式指定
docker-compose scale user=3 movie=3
run:在一个服务上执行一个命令
docker-compose run web bash
docker-compose.yml 属性 version:指定 docker-compose.yml 文件的写法格式 services:多个容器集合 build:配置构建时,Compose 会利用它自动构建镜像,该值可以是一个路径,也可以是一个对象,用于指定 Dockerfile 参数
build: ./dir
---------------
build:
context: ./dir
dockerfile: Dockerfile
args:
buildno: 1
command:覆盖容器启动后默认执行的命令
command: bundle exec thin -p 3000
----------------------------------
command: [bundle,exec,thin,-p,3000]
dns:配置 dns 服务器,可以是一个值或列表
dns: 8.8.8.8
------------
dns:
- 8.8.8.8
- 9.9.9.9
dns_search:配置 DNS 搜索域,可以是一个值或列表
dns_search: example.com
------------------------
dns_search:
- dc1.example.com
- dc2.example.com
environment:环境变量配置,可以用数组或字典两种方式
environment:
RACK_ENV: development
SHOW: 'ture'
-------------------------
environment:
- RACK_ENV=development
- SHOW=ture
env_file:从文件中获取环境变量,可以指定一个文件路径或路径列表,其优先级低于 environment 指定的环境变量
env_file: .env
---------------
env_file:
- ./common.env
expose:暴露端口,只将端口暴露给连接的服务,而不暴露给主机
expose:
- "3000"
- "8000"
image:指定服务所使用的镜像
image: java
network_mode:设置网络模式
network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"
ports:对外暴露的端口定义,和 expose 对应
ports: # 暴露端口信息 - "宿主机端口:容器暴露端口"
- "8763:8763"
- "8763:8763"
links:将指定容器连接到当前连接,可以设置别名,避免ip方式导致的容器重启动态改变的无法连接情况
links: # 指定服务名称:别名
- docker-compose-eureka-server:compose-eureka
volumes:卷挂载路径
volumes:
- /lib
- /var
logs:日志输出信息
--no-color 单色输出,不显示其他颜.
-f, --follow 跟踪日志输出,就是可以实时查看日志
-t, --timestamps 显示时间戳
--tail 从日志的结尾显示,--tail=200
当docker Compose比较多的时候,又不方便管理了,这时候 kubernetes(以下简称k8s,k和s之间有8个字母)出现了,这里我就不展开了。
OK, 来个实际的例子吧。我把自己的某个flask应用docker来运行。
首先得在项目中建一个Dockerfile文件。
# 基于镜像基础
FROM python:3.5.3
# 设置代码文件夹工作目录 /myweb
WORKDIR /myweb
# 复制当前代码文件到容器中 /myweb
ADD . /myweb
# Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt
RUN pip install ectools -U --extra-index-url http://jenkins.town.com:8081/pypi --trusted-host jenkins.englishtown.com
# Make port 8000 available to the world outside this container
EXPOSE 8000
# Run app.py when the container launches
CMD ["python", "app.py", "--host=0.0.0.0"]
然后就可build的镜像了:
docker build -t "testflask" .
执行一下就可以了
docker run -it --rm -p 8000:8000 testflask
检验一下:
当然我这个很简单,如果是个大系统,很多人在上面不停的迭代。如果不用docker,他可能要不停的编译,重启等操作,会影响到正在使用中的人。如果出问题,要回滚回去也是个麻烦的事情。 如果用docker, 能这种影响会降到最低。