一、容器技术(Container)

在虚拟机(VMware)出现以前,应用的部署由于依赖问题,往往新应用要部署在独立的服务器上,导致硬件资源利用率低下,VMware的出现完美的解决了这个问题。

但是,VMware也存在不足,首先VMware实际上是虚拟独立的OS,这需要额外的CPU、RAM和存储资源;其次,由于虚拟机是独立的OS,因此启动比较慢;最后,对于独立的OS,需要单独的补丁&监控,对于商业应用还需要系统的授权许可。

基于以上原因,以google为代表的大型互联网公司一直在探索以容器技术替代虚拟机模型的缺点。

全栈工程化实战之(一)—容器化和基础环境

在虚拟机模型中,Hypervisor是硬件虚拟化(Hardware Virtualization)—将硬件资源划分为虚拟资源打包进VM的软件结构中,而容器模型则是操作系统虚拟化(OS Virtualization)

容器共享宿主机OS,实现进程级别的隔离,相对于虚拟机,容器启动快、资源消耗低、便于迁移

全栈工程化实战之(一)—容器化和基础环境

1.1 Linux容器

现代容器技术起源于Linux,依赖的核心技术包括内核命名空间(Kernel Namespace)、控制组(Control Group)、联合文件系统(Union File System)

  • Kernel Namespace

    负责隔离

容器技术的复杂性使其难于推广,直到Docker的出现,Docker使得容器技术的使用变得简单。

目前还没有Mac容器,windows平台win10和win server2016已经支持windows容器。

注意,因为容器是共享宿主机操作系统的,因此windows容器是不能运行在Linux系统的,但Docker for Mac和Docker for Windows 通过启动一个轻量的Linux VM来支持Linux容器

在windows中,系统必须是64位windows 10且开启Hyper-V和容器特性。

1.2 Docker安装

略,Mac安装参考这里,windows安装参考这里。

1.3 基本概念

1.3.1 docker引擎架构

安装成功后,执行docker version输出类似下面的内容:

全栈工程化实战之(一)—容器化和基础环境

  • Docker daemon

    • 镜像管理、镜像构建、API、身份验证、核心网络等。

    早期的Docker引擎只包含LXC和daemon,daemon则耦合了客户端、API、运行时、镜像构建等功能,拆解后的daemon的核心功能聚焦在镜像构建管理和API。

全栈工程化实战之(一)—容器化和基础环境

  • containerd

    • 封装容器的执行逻辑,负责容器的生命周期管理。
  • shim

    • 用于将运行中的容器和daemon解耦(daemon可独立升级)
    • 保持所有的STDIN和STDOUT流开启状态
    • 将容器退出状态同步给daemon
  • runc(容器运行时)

    • OCI容器运行时的规范参考实现(因此也称OCI层)
    • 轻量级的Lincontainer包装命令行交互工具
    • 唯一作用是创建容器
1.3.2 镜像

镜像是由多个层组成的,包含一个精简的OS和应用和依赖包的独立对象。镜像存储于镜像仓库官方镜像仓库服务。

全栈工程化实战之(一)—容器化和基础环境

可以通过命令docker image pull 镜像名拉取镜像,一个完整的镜像名如下:

:repository:

  • 相关操作

    • 拉取镜像docker image pull repository:tag
    • 查看本地镜像docker image ls
    • 查看镜像详细信息docker image inspect repository:tag
    • 删除镜像docker image rm repository:tag(注意,存在运行容器的镜像不能删除)
  • 构建镜像的方式

    • 通过运行中的容器构建docker container commit
    • 通过Dockerfile文件
1.3.3 容器

容器是镜像的运行时实例。

全栈工程化实战之(一)—容器化和基础环境

  • 运行容器

    $ docker container run [OPIONS] <IMAGE> <APP>
    

    注意,对于linux容器,容器中默认只运行app对应的进程,如果app进程退出,容器也就退出了,即kill掉容器中的主进程,也就kill掉容器

    通过Ctr-PQ组合键可以退出容器回到宿主机并保留容器后台运行,这个时候可以通过docker container exec -it <containerId> bash再次进入容器。

    由于运行容器的参数相当复杂,比如端口映射、挂载磁盘、设置环境变量、设置网络等等,尤其在运行多容器时更繁琐,因此一般都通过Docker Compose这个工具来编排和管理容器。新版的Docker中默认已安装了Docker Compose。

  • 查看运行的容器

    $ docker cotainer ls [-a]
    $ docker ps [-a]
    
  • 启动或停止容器

    $ docker container start <containerId>
    $ docker container stop <containerId>
    

二、容器化的意义

2.1 统一开发环境

2.2 自动化测试

2.3 部署(CI)

容器即应用。

三、制作基础环境(镜像)

3.1 容器化(Containerizing)

将应用整合到容器运行的过程称为容器化。

全栈工程化实战之(一)—容器化和基础环境

容器化的一般步骤:

  • 应用代码开发
  • 创建Dockerfile
  • 在Dockerfile上下文执行docker image build
  • 启动容器

下面正式进入正题,通过容器化的方式定制一个全栈开发基础环境。

3.2 定制开发镜像

3.2.1 环境要求
  • CentOS7.x

  • gcc gcc-c++ pcre-devel openssl-devel make perl zlib zlib-devel

  • vim/openssh-server

  • NodeJS 12.9.1

  • 设置npm registory为团队私有仓库

  • 挂载项目目录

  • 暴露80、22端口

3.2.1 编制Dockerfile

FROM centos:7
MAINTAINER xuhui120 <xuhui120@xxx.com>

# 安装依赖、ssh
RUN yum -y install 
  gcc 
  gcc-c++ 
  pcre-devel 
  openssl-devel  
  make 
  perl 
  zlib 
  vim 
  zlib-devel 
  openssh-server 
  && ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key 
  && ssh-keygen -t rsa -f /etc/ssh/ssh_host_ecdsa_key 
  && ssh-keygen -t rsa -f /etc/ssh/ssh_host_ed25519_key 
  && /bin/sed -i "s/#UsePrivilegeSeparation.*/UsePrivilegeSeparation no/g" /etc/ssh/sshd_config 
  && /bin/sed -i "s/#PermitRootLogin.*/PermitRootLogin yes/g" /etc/ssh/sshd_config 
  && /bin/sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config 
  && /bin/echo "cd /var/www" >> /root/.bashrc

#######################
# 安装node
#######################
USER root
ARG NODE_VERSION=12.9.1
RUN cd /usr/local/src 
    && curl -O -s https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.gz 
    && mkdir -p /usr/local/nodejs 
    && tar zxf node-v${NODE_VERSION}-linux-x64.tar.gz -C /usr/local/nodejs --strip-components=1 --no-same-owner 
    && ls -al /usr/local/nodejs 
    && ln -s /usr/local/nodejs/bin/* /usr/local/bin/ 
    && npm config set registry http://registry.xxx.com 
    && cd /usr/local/src 
    && rm -rf *.tar.gz

# 修改root密码
ARG SSH_ROOT_PWD=root
RUN /bin/echo "${SSH_ROOT_PWD}" | passwd --stdin root

# 免密码登录
ARG LOCAL_SSH_KEY
RUN if [ -n "${LOCAL_SSH_KEY}" ]; then 
    mkdir -p /root/.ssh 
    && chmod 750 /root/.ssh 
    && echo ${LOCAL_SSH_KEY} > /root/.ssh/authorized_keys 
    && sed -i s/"//g /root/.ssh/authorized_keys 
    && chmod 600 /root/.ssh/authorized_keys 
;fi

# 暴露端口
EXPOSE 22
EXPOSE 80
COPY startup.sh /
RUN chmod +x /startup.sh
WORKDIR /var/www
CMD /startup.sh

创建容器运行脚本startup.sh:

#!/bin/bash
source /etc/profile
/usr/sbin/sshd -D

最后执行构建命令docker image build .