Docker零基础到实战 三剑客之 Compose

Praise 2019年07月26日 179次浏览

背景与介绍

容器从根本上改变了人们开发、发布以及运行软件的方式。软件开发者可以在本地构建软件,因为他们知道软件能够在任何主机环境下运行,无论是 IT 部门的机房、用户的笔记本电脑,还是云端集群,而且运行时并无差异。运维工程师只需专注于维护网络和资源,
保证正常运行时间,减少了花在配置环境及解决系统依赖关系上的时间。从规模很小的创业公司,到规模庞大的企业,容器的使用率和理解程度正以惊人的速度提升。可以预见,在未来几年,开发者和运维工程师将以不同形式广泛使用容器技术。容器是对应用程序及其依赖关系的封装。乍一看容器只是个轻量级的虚拟机,它和虚拟机一样拥有一被隔离的操作系统实例,用来运行应用程序。但容器拥有一些优点,使它能实现一些传统虚拟机很难实现甚至无法实现的用例。

  • 容器能与主机的操作系统共享资源,因而它的效率高出一个数量级。启动和停止容器均只需一瞬间。相比在主机上直接运行程序,容器的性能损耗非常低,甚至是零损耗。
  • 容器具有可移植性,这极有可能彻底解决由于运行环境的些许改变而导致的问题,甚至有可能彻底终止开发者的抱怨:“可是程序在我的计算机上能正常工作!”
  • 容器是轻量的,这意味着开发者能同时运行数十个容器,并能模拟分布式系统在真实运行环境下的情况。运维工程师在一台主机上能运行的容器数量,远远超过仅使用虚拟机时。
  • 对于最终用户及开发者而言,容器的优势不仅仅体现在云端部署。用户可以下载并执行复杂的应用程序,而无需花费大量时间在配置和安装的问题上,也无需担心对系统本身的改动。另一方面,应用程序的开发者不用再操心用户环境的差异,以及依赖关系是否满足。更重要的是,虚拟机和容器的根本目标不尽相同。虚拟机的目的是要完整地模拟另一个环境,而容器的目的则是使应用程序能够移植,并把所有依赖关系包含进去。

补充个知识点:
从容器内部访问宿主机ip:
mac:docker.for.mac.host.internal
其他:docker.for.host.internal

一般手动的部署方式?

你现在做了一个 xxxx 系统 or xxx 软件。但是你这个项目所用到的技术面非常的多,如果我们按照正常手动的部署方式,那就是折腾服务器下载各种依赖、软件、插件... 乱七八糟的各种配置。OK 对于喜欢爱折腾的人来说,我觉得这也是可以令人接受的,但是想想有一天你服务器崩了,或者需要换服务器,是不是又要来一遍骚操作,想一想你的项目如果以后又增加了什么功能有需要什么依赖支持,那是不是还要折腾。

为什么要使用 Docker?

使用 Docker 的好处很多,不光可以解决上述的问题,而且还有以下几点:

  • 无论是安装应用、搭建环境、部署应用、环境配置还是切换应用都非常方便灵活
  • 节省资源开销
  • 灵活迁移你开发的应用程序
  • 一致的运行环境
  • 更轻松维护和扩展

...

而且会 docker 会让你找工作时多加几分,同事之间谈起来多几分资本

Docker 的基本概念

镜像 Images

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变

容器 Container

容器是镜像的实例化,实质运行与自己的独立命名空间的进程。因此容器具有自己的 root 系统、网络配置、进程空间、用户ID空间等
相对于镜像来说容器是动态的,容器在启动的时候,以镜像为基础层创建一层可写层作为最上层

容器与虚拟机对比

仓库 Repository

仓库的概念跟 git 类似,注册服务器可以理解为 github、codding、gitlab、码云... 这样的托管服务

交互图


画的比较丑 不懂可以直接问我

Docker 基本命令

  • docker start 容器名(容器ID也可以)
  • docker stop 容器名(容器ID也可以)
  • docker run 命令加 -d 参数,docker 会将容器放到后台运行
  • docker ps 正在运行的容器
  • docker top 容器名 查看容器内部运行的进程
  • docker exec -d 容器名 touch /etc/new_config_file 通过后台命令创建一个空文件
  • docker inspect 容器名 对容器进行详细的检查,可以加 --format='{(.State.Running)}' 来获取指定的信息
  • docker rm 容器ID 删除容器,注:运行中的容器无法删除
  • docker rm docker ps -a -q 这样可以删除所有的容器
  • docker images 列出镜像
  • docker pull 镜像名:标签 拉镜像
  • docker search 查找docker Hub 上公共的可用镜像
  • docker login 登陆到Docker Hub,个人认证信息将会保存到$HOME/.dockercfg
  • docker history 镜像ID 深入探求镜像是如何构建出来的
  • docker push 镜像名 将镜像推送到 Docker Hub
  • docker rmi 镜像名 删除镜像
  • docker attach 容器ID 进入容器
  • docker network create --subnet=172.171.0.0/16 docker-at 选取172.172.0.0网段

...

安装 Docker

请大家 移步 到另一篇文章,里面有详细讲解安装 dockerdocker-compose

运行我们的第一个镜像

查找镜像

我们通过 docker search 来查看镜像是否存在

$ docker search redis

拉取镜像

接下来我们通过 docker pull 来拉取镜像

$docker pull redis

注意 tag:latest 这表示我们拉取的最新的镜像,如需其他版本 在 docker pull redis:xxxx 跟上版本

查看镜像列表

$ docker images

查看镜像详情信息

docker inspect 查看镜像元数据

$ docker inspect redis:latest

容器

生成容器

我们使用 redis 这个镜像来生成我们的第一个容器

docker run -p 6379:6379 -v $PWD/data:/data --name redis-server -d redis:latest
  • -p 容器中可以运行一些网络应用,要让外部也可以访问这些应用,可以通过 -P(大写) 或 -p (小写) 参数来指定端口映射
  • -v 将主机中当前目录下的data挂载到容器的/data
  • --name 自定义容器名称
  • -i 展示容器输入信息
  • -t 命令行交互模式
  • -u 设置用户名
  • -w 指定容器内的目录
  • -e 设置环境变量
  • -d 后台运行

docker ps 显示当前正在运行的容器 如果再加上 -a 是所有容器

是不是感觉比我们自己在服务器下载一个redis要方便的多啊 不要急 慢慢往下看

进入容器

docker exec 是需要容器处于运行中且 PID1 进程也处于运行中才能执行的操作。命令执行后会进入容器的默认工作目录

$ docker exec -it redis-server /bin/sh

对容器的一些操作

$ docker stop redis-server # 停止
$ docker start redis-rever # 启动
$ docker rm redis-server   # 删除

有些人感觉命令也很繁琐 而且输入错了还要从头来一次 而且很多东西也没有配置 docker 也就这样啊, 也是拉取一个个的镜像然后构建容器配置

我们继续...

使用 Dockerfile 构建redis容器

FROM ubuntu          

MAINTAINER ruifengyun "push_over@163.com"

RUN apt-get update &&  apt-get -y install epel-release && apt-get -y install redis && apt-get -y install net-tools

EXPOSE 6379
ENTRYPOINT [ "/usr/bin/redis-server" ]
CMD []
  • FROM 是作为镜像的基础
  • MAINTAINER 指定维护者信息
  • RUN 可以理解为在FROM下来的镜像做一些环境的部署
  • CMD 是创建容器后,会运行的命令
  • EXPOSE 是暴露的端口
  • MAINTAINER 通知的邮件
  • ADD 相当于把主机的start.sh脚本传递给了容器里面
  • VOLUME 是本地的路径的映射
  • WORKDIR 是执行的路径,也就是cmd entrypoint执行的路径
  • ENV 用于指定环境变量,这些环境变量,后续可以被RUN指令使用,容器运行起来之后,也可以在容器中获取这些环境变量
  • -ENTRYPOINT 配置容器启动时的执行命令
  • COPY 复制本地主机目录或文件到容器的目录,不存在时会自动创建
  • USER 指定容器运行时的用户名或UID,后续的RUN也会使用指定的用户

创建后之后我们就可以执行 docker build . . 不能被省略 切记一定要在Dockerfile文件所在的目录

我们通过 docker images 查看当前镜像列表是不是多了一个ubuntu

们简单说一下整个Dockerfile的内容:

首先选择了基础镜像是ubuntu的最新版,然后填写作者信息, 再之后更新软件源, 下载安装一些所需要的依赖并下载redis,随即我们又开放了6379端口

学习使用 Docker Compose

前面我们使用 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

build 构建或者重新构建服务 必须在docker-compose.yml 文件同级下

$ docker-compose build

start 启动指定服务已存在的容器

$ docker-compose start

stop 停止已运行的服务的容器

$ docker-compose stop

up 构建、启动容器

$ docker-compose up 
-d 后台运行

docker-compose.yml 命令

  • version 指定 docker-compose.yml 文件的写法格式
  • services 多个容器集合
  • build 配置构建时,Compose 会利用它自动构建镜像,该值可以是一个路径,也可以是一个对象,用于指定 Dockerfile 参数
  • command 覆盖容器启动后默认执行的命令
  • dns 配置 dns 服务器,可以是一个值或列表
  • dns_search 配置 DNS 搜索域,可以是一个值或列表
  • environment 环境变量配置,可以用数组或字典两种方式
  • env_file 从文件中获取环境变量,可以指定一个文件路径或路径列表,其优先级低于 environment 指定的环境变量
  • expose 暴露端口,只将端口暴露给连接的服务,而不暴露给主机
  • image 指定服务所使用的镜像
  • network_mode 设置网络模式
  • ports 对外暴露的端口定义,和 expose 对应
  • links 将指定容器连接到当前连接,可以设置别名,避免ip方式导致的容器重启动态改变的无法连接情况
  • volumes 卷挂载路径
  • logs 日志输出信息

docker-compose 实战

我们做一个简单的实例

选择一个目录创建 Dockerfiledocker-compose.yml 文件

我们来撸 docker-compose.yml

version: '3'
services:
  redis:
    image: redis
    ports:
      - 6379:6379
    restart: always

  mongo:
    image: mongo
    ports:
      - 27017:27017
    restart: always
    volumes:
      - ./mongo:/data/db

  app:
    build: .    # 我们接下来要撸的Dcokerfile
    restart: always
    command: npm start
    environment:
      - TZ=Asia/Shanghai
    # volumes:
    #   - .:/app
      # - /app/node_modules
    ports:
      - 7001:7001
    depends_on:
      - redis
      - mongo
    links:
      - redis:redis
      - mongo:mongo

继续撸 Dcokerfile

FROM node:lts

# LABEL maintainer = "gaozan <push_over@163.com>"

# 环境变量
ENV NODE_ENV production  

# 创建app目录
# RUN mkdir /app

# 设置工作目录
WORKDIR /app

# 拷贝package.json文件到工作目录
# !!重要:package.json需要单独添加。
# Docker在构建镜像的时候,是一层一层构建的,仅当这一层有变化时,重新构建对应的层。
# 如果package.json和源代码一起添加到镜像,则每次修改源码都需要重新安装npm模块,这样木有必要。
# 所以,正确的顺序是: 添加package.json;安装npm模块;添加源代码
COPY package*.json ./

# 安装npm依赖(使用淘宝的镜像源)
# 如果使用的境外服务器,无需使用淘宝的镜像源,即改为`RUN npm i`。
RUN npm install --registry=https://registry.npm.taobao.org && npm install egg-scripts --save

# 指定时区
# RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 拷贝所有源代码到工作目录
COPY ./ /app

USER root
# 暴露容器端口
EXPOSE 7001

然后我们需要执行 docker-compose up -d 启动并在后台运行

结尾

到这就是 docker 的一个简单介绍和小demo 写的不好的地方多多包含, 谢谢大家