Docker

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。 容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。 Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版),我们用社区版就可以了。 Docker的应用场景 Web 应用的自动化打包和发布。 自动化测试和持续集成、发布。 在服务型环境中部署和调整数据库或其他的后台应用。 从头编译或者扩展现有的 OpenShift 或 Cloud Foundry 平台来搭建自己的 PaaS 环境。

Docker 安装

Docker有很多种安装的选择,我们推荐您在Ubuntu下面安装,因为docker是在Ubuntu下面开发的,安装包测试比较充分,可以保证软件包的可用性。Mac, windows和其他的一些linux发行版本无法原生运行Docker,可以使用虚拟软件创建一个ubuntu的虚拟机并在里面运行docker。

Docker 安装

Ubuntu Docker 安装

Docker Engine-Community 支持以下的 Ubuntu 版本:

Docker Engine - Community 支持上 x86_64(或 amd64)armhf,arm64,s390x (IBM Z),和 ppc64le(IBM的Power)架构。

卸载旧版本

Docker 的旧版本被称为 docker,docker.io 或 docker-engine 。如果已安装,请卸载它们:

apt remove docker docker-engine docker.io containerd runc

当前称为 Docker Engine-Community 软件包 docker-ce 。

安装 Docker Engine-Community,以下介绍两种方式。

使用 Docker 仓库进行安装

在新主机上首次安装 Docker Engine-Community 之前,需要设置 Docker 仓库。之后,您可以从仓库安装和更新 Docker 。

设置仓库

更新 apt 包索引。

curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

curl -fsSL http://mirrors.huaweicloud.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] http://mirrors.huaweicloud.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

apt update

添加 Docker 的官方 GPG 密钥:

curl -fsSL http://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

||

curl -fsSL http://mirrors.huaweicloud.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

||

curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -

安装 Docker Engine-Community

更新 apt 包索引。

sudo apt update

安装最新版本的 Docker Engine-Community 和 containerd ,或者转到下一步安装特定版本:

sudo apt install docker-ce docker-ce-cli containerd.io -y

测试 Docker 是否安装成功,输入以下指令,打印出以下信息则安装成功:

docker run hello-world

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete                                                                                                                                  Digest: sha256:c3b4ada4687bbaa170745b3e4dd8ac3f194ca95b2d0518b417fb47e5879d9b5f
Status: Downloaded newer image for hello-world:latest


Hello from Docker!
This message shows that your installation appears to be working correctly.


To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.


To try something more ambitious, you can run an Ubuntu container with:
docker run -it ubuntu bash


Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/


For more examples and ideas, visit:
 https://docs.docker.com/get-started/

使用 Shell 脚本进行安装

Docker 在 get.docker.com 和 test.docker.com 上提供了方便脚本,用于将快速安装 Docker Engine-Community 的边缘版本和测试版本。脚本的源代码在 docker-install 仓库中。 不建议在生产环境中使用这些脚本,在使用它们之前,您应该了解潜在的风险:

本示例使用 get.docker.com 上的脚本在 Linux 上安装最新版本的Docker Engine-Community。要安装最新的测试版本,请改用 test.docker.com。在下面的每个命令,取代每次出现 get 用 test。

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

如果要使用 Docker 作为非 root 用户,则应考虑使用类似以下方式将用户添加到 docker 组:

usermod -aG docker your-user


usermod -aG docker develop

公共的镜像加速器

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://xviwkyb8.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
Docker 安装

CentOS Docker 安装

Docker 支持以下的 64 位 CentOS 版本:

建议使用 overlay2 存储驱动程序。

卸载旧版本

较旧的 Docker 版本称为 docker 或 docker-engine 。如果已安装这些程序,请卸载它们以及相关的依赖项。

yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

安装 Docker Engine-Community

使用 Docker 仓库进行安装

在新主机上首次安装 Docker Engine-Community 之前,需要设置 Docker 仓库。之后,您可以从仓库安装和更新 Docker。

设置仓库

安装所需的软件包。yum-utils 提供了 yum-config-manager ,并且 device mapper 存储驱动程序需要 device-mapper-persistent-data 和 lvm2。

yum install -y yum-utils \
device-mapper-persistent-data \
lvm2

使用以下命令来设置稳定的仓库。

yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
http://mirrors.huaweicloud.com/docker-ce/linux/centos/docker-ce.repo

yum makecache fast

安装 Docker Engine-Community

安装最新版本的 Docker Engine-Community 和 containerd,或者转到下一步安装特定版本:

yum install docker-ce docker-ce-cli containerd.io

如果提示您接受 GPG 密钥,请选是。

有多个 Docker 仓库吗?

如果启用了多个 Docker 仓库,则在未在 yum install 或 yum update 命令中指定版本的情况下,进行的安装或更新将始终安装最高版本,这可能不适合您的稳定性需求。

Docker 安装完默认未启动。并且已经创建好 docker 用户组,但该用户组下没有用户。

要安装特定版本的 Docker Engine-Community,请在存储库中列出可用版本,然后选择并安装:

1、列出并排序您存储库中可用的版本。此示例按版本号(从高到低)对结果进行排序。

yum list docker-ce --showduplicates | sort -r

docker-ce.x86_64  3:18.09.1-3.el7                     docker-ce-stable
docker-ce.x86_64  3:18.09.0-3.el7                     docker-ce-stable
docker-ce.x86_64  18.06.1.ce-3.el7                    docker-ce-stable
docker-ce.x86_64  18.06.0.ce-3.el7                    docker-ce-stable

2、通过其完整的软件包名称安装特定版本,该软件包名称是软件包名称(docker-ce)加上版本字符串(第二列),从第一个冒号(:)一直到第一个连字符,并用连字符(-)分隔。例如:docker-ce-18.09.1。

yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io

启动 Docker。

systemctl start docker
systemctl enable docker

通过运行 hello-world 映像来验证是否正确安装了 Docker Engine-Community 。

docker run hello-world

如果要使用 Docker 作为非 root 用户,则应考虑使用类似以下方式将用户添加到 docker 组:

usermod -aG docker your-user
Docker 安装

Windows Docker 安装

win7、win8 系统

win7、win8 等需要利用 docker toolbox 来安装,国内可以使用阿里云的镜像来下载,下载地址:windows/docker-toolbox

安装比较简单,双击运行,点下一步即可,可以勾选自己需要的组件:

docker toolbox 是一个工具集,它主要包含以下一些内容:

下载完成之后直接点击安装,安装成功后,桌边会出现三个图标,入下图所示:

点击 Docker QuickStart 图标来启动 Docker Toolbox 终端。

如果系统显示 User Account Control 窗口来运行 VirtualBox 修改你的电脑,选择 Yes。

$ 符号那你可以输入以下命令来执行。

$ docker run hello-world
 Unable to find image 'hello-world:latest' locally
 Pulling repository hello-world
 91c95931e552: Download complete
 a8219747be10: Download complete
 Status: Downloaded newer image for hello-world:latest
 Hello from Docker.
 This message shows that your installation appears to be working correctly.

 To generate this message, Docker took the following steps:
  1. The Docker Engine CLI client contacted the Docker Engine daemon.
  2. The Docker Engine daemon pulled the "hello-world" image from the Docker Hub.
     (Assuming it was not already locally available.)
  3. The Docker Engine daemon created a new container from that image which runs the
     executable that produces the output you are currently reading.
  4. The Docker Engine daemon streamed that output to the Docker Engine CLI client, which sent it
     to your terminal.

 To try something more ambitious, you can run an Ubuntu container with:
  $ docker run -it ubuntu bash

 For more examples and ideas, visit:
  https://docs.docker.com/userguide/

Win10 系统

现在 Docker 有专门的 Win10 专业版系统的安装包,需要开启Hyper-V。

开启 Hyper-V

1、安装 Toolbox

最新版 Toolbox 下载地址: get-docker

点击 Download Desktop and Take a Tutorial,并下载 Windows 的版本,如果你还没有登录,会要求注册登录:

2、运行安装文件

镜像加速

Windows 10

对于使用 Windows 10 的系统,在系统右下角托盘 Docker 图标内右键菜单选择 Settings,打开配置窗口后左侧导航菜单选择 Daemon。在 Registrymirrors 一栏中填写加速器地址 registry.docker-cn ,之后点击 Apply 保存后 Docker 就会重启并应用配置的镜像地址了。

Docker 安装

MacOS Docker 安装

使用 Homebrew 安装

macOS 我们可以使用 Homebrew 来安装 Docker。

Homebrew 的 Cask 已经支持 Docker for Mac,因此可以很方便的使用 Homebrew Cask 来进行安装:

$ brew cask install docker

==> Creating Caskroom at /usr/local/Caskroom
==> We'll set permissions properly so we won't need sudo in the future
Password:          # 输入 macOS 密码
==> Satisfying dependencies
==> Downloading https://download.docker.com/mac/stable/21090/Docker.dmg
######################################################################## 100.0%
==> Verifying checksum for Cask docker
==> Installing Cask docker
==> Moving App 'Docker.app' to '/Applications/Docker.app'.
&#x1f37a;  docker was successfully installed!

在载入 Docker app 后,点击 Next,可能会询问你的 macOS 登陆密码,你输入即可。之后会弹出一个 Docker 运行的提示窗口,状态栏上也有有个小鲸鱼的图标()。

手动下载安装

如果需要手动下载,请点击以下链接下载 StableEdge 版本的 Docker for Mac。

$ docker --version
Docker version 17.09.1-ce, build 19e2cf6

镜像加速

鉴于国内网络问题,后续拉取 Docker 镜像十分缓慢,我们可以需要配置加速器来解决,我使用的是网易的镜像地址:hub-mirror.c.163

在任务栏点击 Docker for mac 应用图标 -> Perferences... -> Daemon -> Registry mirrors。在列表中填写加速器地址即可。修改完成之后,点击 Apply & Restart 按钮,Docker 就会重启并应用配置的镜像地址了。

$ docker info
...
Registry Mirrors:
 http://hub-mirror.c.163.com
Live Restore Enabled: false
Docker 安装

阿里云 docker 容器镜像服务

阿里云 docker 容器镜像服务

Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版) 和 EE(Enterprise Edition: 企业版),我们用社区版就可以了。

Docker的应用场景

Docker 的优点

Docker 架构

Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。

Docker 容器通过 Docker 镜像来创建。

容器与镜像的关系类似于面向对象编程中的对象与类。

Docker 面向对象
容器 对象
镜像
Docker 面向对象
Docker 镜像(Images) Docker 镜像是用于创建 Docker 容器的模板。
Docker 容器(Container) 容器是独立运行的一个或一组应用。
Docker 客户端(Client) Docker 客户端通过命令行或者其他工具使用 Docker API (https://docs.docker.com/reference/api/docker_remote_api) 与 Docker 的守护进程通信。
Docker 主机(Host) 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。
Docker 仓库(Registry) Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。
Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。
Docker Machine Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。

Docker 安装

Ubuntu Docker 安装

CentOS Docker 安装

Windows Docker 安装

MacOS Docker 安装

Docker 命令大全

Docker 资源汇总

Docker官方英文资源

docker官网:http://www.docker.com

Docker Windows 入门:https://docs.docker.com/docker-for-windows/

Docker CE(社区版) Ubuntu:https://docs.docker.com/install/linux/docker-ce/ubuntu/

Docker mac 入门:https://docs.docker.com/docker-for-mac/

Docker 用户指引:https://docs.docker.com/config/daemon/

Docker 官方博客:http://blog.docker.com/

Docker Hub: https://hub.docker.com/

Docker开源: https://www.docker.com/open-source

Docker 国内镜像

阿里云的加速器:https://help.aliyun.com/document_detail/60750.html

网易加速器:http://hub-mirror.c.163.com

官方中国加速器:https://registry.docker-cn.com

ustc的镜像:https://docker.mirrors.ustc.edu.cn

daocloud:https://www.daocloud.io/mirror#accelerator-doc(注册后使用)


容器镜像服务

https://cr.console.aliyun.com/

登录

docker login --username=itqmdx@qq.com registry.cn-hangzhou.aliyuncs.com

华东1(杭州)

公网地址

registry.cn-hangzhou.aliyuncs.com/wzhz/[image:[version]]

专有网络

registry-vpc.cn-hangzhou.aliyuncs.com/wzhz/[image:[version]]

经典网络

registry-internal.cn-hangzhou.aliyuncs.com/wzhz/[image:[version]]

华北1(青岛)

公网地址

registry.cn-qingdao.aliyuncs.com/wzhz/[image:[version]]

专有网络

registry-vpc.cn-qingdao.aliyuncs.com/wzhz/[image:[version]]

经典网络

registry-internal.cn-qingdao.aliyuncs.com/wzhz/[image:[version]]

华南1(深圳)

公网地址

registry.cn-shenzhen.aliyuncs.com/wzhz/[image:[version]]

专有网络

registry-vpc.cn-shenzhen.aliyuncs.com/wzhz/[image:[version]]

经典网络

registry-internal.cn-shenzhen.aliyuncs.com/wzhz/[image:[version]]

中国(香港)

公网地址

registry.cn-hongkong.aliyuncs.com/wzhz/[image:[version]]

专有网络

registry-vpc.cn-hongkong.aliyuncs.com/wzhz/[image:[version]]

经典网络

registry-internal.cn-hongkong.aliyuncs.com/wzhz/[image:[version]]

镜像加速器

加速器

使用加速器可以提升获取Docker官方镜像的速度

https://wzhz.mirror.aliyuncs.com

操作文档

1. 安装/升级Docker客户端

推荐安装1.10.0以上版本的Docker客户端,参考文档 docker-ce

2. 配置镜像加速器

针对Docker客户端版本大于 1.10.0 的用户

您可以通过修改daemon配置文件 /etc/docker/daemon.json 来使用加速器

创建文本
vim docker-aliyun-daemon
输入
#!/bin/bash

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://wzhz.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
执行
sudo chmod 755 docker-aliyun-daemon

./docker-aliyun-daemon

volume

# 列出未使用的volume
$ docker volume ls -qf dangling=true
# 删除未使用的volume
$ docker volume rm $(docker volume ls -qf dangling=true)
# 全部清除
$ docker system prune

Docker 使用

Docker核心解决的问题是利用LXC来实现类似VM的功能,从而利用更加节省的硬件资源提供给用户更多的计算资源。同VM的方式不同, LXC 其并不是一套硬件虚拟化方法 - 无法归属到全虚拟化、部分虚拟化和半虚拟化中的任意一个,而是一个操作系统级虚拟化方法, 理解起来可能并不像VM那样直观。所以我们从虚拟化到docker要解决的问题出发,看看他是怎么满足用户虚拟化需求的。 用户需要考虑虚拟化方法,尤其是硬件虚拟化方法,需要借助其解决的主要是以下4个问题: 隔离性 - 每个用户实例之间相互隔离, 互不影响。 硬件虚拟化方法给出的方法是VM, LXC给出的方法是container,更细一点是kernel namespace 可配额/可度量 - 每个用户实例可以按需提供其计算资源,所使用的资源可以被计量。硬件虚拟化方法因为虚拟了CPU, memory可以方便实现, LXC则主要是利用cgroups来控制资源 移动性 - 用户的实例可以很方便地复制、移动和重建。硬件虚拟化方法提供snapshot和image来实现,docker(主要)利用AUFS实现 安全性 - 这个话题比较大,这里强调是host主机的角度尽量保护container。硬件虚拟化的方法因为虚拟化的水平比较高,用户进程都是在KVM等虚拟机容器中翻译运行的, 然而对于LXC, 用户的进程是lxc-start进程的子进程, 只是在Kernel的namespace中隔离的, 因此需要一些kernel的patch来保证用户的运行环境不会受到来自host主机的恶意入侵, dotcloud(主要是)利用kernel grsec patch解决的.

Docker 使用

YAML 入门教程

YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。

YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。

YAML 的配置文件后缀为 .yml,如:wzhz.xyz.yml

基本语法

数据类型

YAML 支持以下几种数据类型:

YAML 对象

对象键值对使用冒号结构表示 *key: value,冒号后面要加一个空格。

也可以使用 key:{key1: value1, key2: value2, ...}

还可以使用缩进表示层级关系;

key: 
    child-key: value
    child-key2: value2

较为复杂的对象格式,可以使用问号加一个空格代表一个复杂的 key,配合一个冒号加一个空格代表一个 value:

?  
    - complexkey1
    - complexkey2
:
    - complexvalue1
    - complexvalue2

意思即对象的属性是一个数组 [complexkey1,complexkey2],对应的值也是一个数组 [complexvalue1,complexvalue2]

YAML 数组

以 - 开头的行表示构成一个数组:

- A
- B
- C

YAML 支持多维数组,可以使用行内表示:

key: [value1, value2, ...]

数据结构的子成员是一个数组,则可以在该项下面缩进一个空格。

-
 - A
 - B
 - C

一个相对复杂的例子:

companies:
    -
        id: 1
        name: company1
        price: 200W
    -
        id: 2
        name: company2
        price: 500W

意思是 companies 属性是一个数组,每一个数组元素又是由 id、name、price 三个属性构成。

数组也可以使用流式(flow)的方式表示:

companies: [{id: 1,name: company1,price: 200W},{id: 2,name: company2,price: 500W}]

复合结构

数组和对象可以构成复合结构,例:

languages:
  - Ruby
  - Perl
  - Python 
websites:
  YAML: yaml.org 
  Ruby: ruby-lang.org 
  Python: python.org 
  Perl: use.perl.org

转换为 json 为:

{ 
  languages: [ 'Ruby', 'Perl', 'Python'],
  websites: {
    YAML: 'yaml.org',
    Ruby: 'ruby-lang.org',
    Python: 'python.org',
    Perl: 'use.perl.org' 
  } 
}

纯量

纯量是最基本的,不可再分的值,包括:

使用一个例子来快速了解纯量的基本使用:

boolean: 
    - TRUE  #true,True都可以
    - FALSE  #false,False都可以
float:
    - 3.14
    - 6.8523015e+5  #可以使用科学计数法
int:
    - 123
    - 0b1010_0111_0100_1010_1110    #二进制表示
null:
    nodeName: 'node'
    parent: ~  #使用~表示null
string:
    - 哈哈
    - 'Hello world'  #可以使用双引号或者单引号包裹特殊字符
    - newline
      newline2    #字符串可以拆成多行,每一行会被转化成一个空格
date:
    - 2018-02-17    #日期必须使用ISO 8601格式,即yyyy-MM-dd
datetime: 
    -  2018-02-17T15:02:31+08:00    #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区

引用

& 锚点和 * 别名,可以用来引用:

defaults: &defaults
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  <<: *defaults

test:
  database: myapp_test
  <<: *defaults

相当于:

defaults:
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  adapter:  postgres
  host:     localhost

test:
  database: myapp_test
  adapter:  postgres
  host:     localhost

& 用来建立锚点(defaults),<< 表示合并到当前数据,* 用来引用锚点。

下面是另一个例子:

- &showell Steve
- Clark
- Brian
- Oren
- *showell

转为 JavaScript 代码如下:

[ 'Steve', 'Clark', 'Brian', 'Oren', 'Steve' ]

参考地址:

YAML 语言教程

YAML快速入门

yaml教程

Docker 使用

Docker Compose

Compose 简介

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

如果你还不了解 YML 文件配置,可以先阅读 YAML 入门教程

Compose 使用的三个步骤:

docker-compose.yml 的配置案例如下(配置参数参考下文):

# yaml 配置实例
version: '3'
services:
  web:
    build: .
    ports:
   - "5000:5000"
    volumes:
   - .:/code
    - logvolume01:/var/log
    links:
   - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

Compose 安装

Linux

Linux 上我们可以从 Github 上下载它的二进制包来使用,最新发行的版本地址:compose-releases

运行以下命令以下载 Docker Compose 的当前稳定版本:

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

要安装其他版本的 Compose,请替换 1.24.1。

将可执行权限应用于二进制文件:

$ sudo chmod +x /usr/local/bin/docker-compose

创建软链:

$ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

测试是否安装成功:

$ docker-compose --version
cker-compose version 1.24.1, build 4667896b

注意: 对于 alpine,需要以下依赖包: py-pippython-devlibffi-devopenssl-dev,gcc,libc-dev,和 make

macOS

Mac 的 Docker 桌面版和 Docker Toolbox 已经包括 Compose 和其他 Docker 应用程序,因此 Mac 用户不需要单独安装 ComposeDocker 安装说明可以参阅 MacOS Docker 安装

windows PC

Windows 的 Docker 桌面版和 Docker Toolbox 已经包括 Compose 和其他 Docker 应用程序,因此 Windows 用户不需要单独安装 ComposeDocker 安装说明可以参阅 Windows Docker 安装

使用

1、准备

创建一个测试目录:

$ mkdir composetest
$ cd composetest

在测试目录中创建一个名为 app.py 的文件,并复制粘贴以下内容:

composetest/app.py 文件代码

import time

import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)


def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)


@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)

在此示例中,redis 是应用程序网络上的 redis 容器的主机名,该主机使用的端口为 6379。

在 composetest 目录中创建另一个名为 requirements.txt 的文件,内容如下:

flask
redis

2、创建 Dockerfile 文件

在 composetest 目录中,创建一个名为的文件 Dockerfile,内容如下:

FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP app.py
ENV FLASK_RUN_HOST 0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["flask", "run"]

Dockerfile 内容解释:

3、创建 docker-compose.yml

在测试目录中创建一个名为 docker-compose.yml 的文件,然后粘贴以下内容:

docker-compose.yml 配置文件

# yaml 配置
version: '3'
services:
  web:
    build: .
    ports:
     - "5000:5000"
  redis:
    image: "redis:alpine"

该 Compose 文件定义了两个服务:web 和 redis。

4、使用 Compose 命令构建和运行您的应用

在测试目录中,执行以下命令来启动应用程序:

docker-compose up

如果你想在后台执行该服务可以加上 -d 参数:

docker-compose up -d

yml 配置指令参考

version

指定本 yml 依从的 compose 哪个版本制定的。

build

指定为构建镜像上下文路径:

例如 webapp 服务,指定为从上下文路径 ./dir/Dockerfile 所构建的镜像:

version: "3.7"
services:
  webapp:
    build: ./dir

或者,作为具有在上下文指定的路径的对象,以及可选的 Dockerfile 和 args:

version: "3.7"
services:
  webapp:
    build:
      context: ./dir
      dockerfile: Dockerfile-alternate
      args:
        buildno: 1
      labels:
        - "com.example.description=Accounting webapp"
        - "com.example.department=Finance"
        - "com.example.label-with-empty-value"
      target: prod

cap_add,cap_drop

添加或删除容器拥有的宿主机的内核功能。

cap_add:
  - ALL # 开启全部权限

cap_drop:
  - SYS_PTRACE # 关闭 ptrace权限

cgroup_parent

为容器指定父 cgroup 组,意味着将继承该组的资源限制。

cgroup_parent: m-executor-abcd

command

覆盖容器启动的默认命令。

command: ["bundle", "exec", "thin", "-p", "3000"]

container_name

指定自定义容器名称,而不是生成的默认名称。

container_name: my-web-container

depends_on

设置依赖关系。

version: "3.7"
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres

注意:web 服务不会等待 redis db 完全启动 之后才启动。

deploy

指定与服务的部署和运行有关的配置。只在 swarm 模式下才会有用。

version: "3.7"
services:
  redis:
    image: redis:alpine
    deploy:
      mode:replicated
      replicas: 6
      endpoint_mode: dnsrr
      labels: 
        description: "This redis service label"
      resources:
        limits:
          cpus: '0.50'
          memory: 50M
        reservations:
          cpus: '0.25'
          memory: 20M
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
        window: 120s

可以选参数:

endpoint_mode:访问集群服务的方式。

endpoint_mode: vip 
# Docker 集群服务一个对外的虚拟 ip。所有的请求都会通过这个虚拟 ip 到达集群服务内部的机器。
endpoint_mode: dnsrr
# DNS 轮询(DNSRR)。所有的请求会自动轮询获取到集群 ip 列表中的一个 ip 地址。

labels:在服务上设置标签。可以用容器上的 labels(跟 deploy 同级的配置) 覆盖 deploy 下的 labels。

mode:指定服务提供的模式。

replicas:mode 为 replicated 时,需要使用此参数配置具体运行的节点数量。

resources:配置服务器资源使用的限制,例如上例子,配置 redis 集群运行需要的 cpu 的百分比 和 内存的占用。避免占用资源过高出现异常。

restart_policy:配置如何在退出容器时重新启动容器。

注:仅支持 V3.4 及更高版本。

devices

指定设备映射列表。

devices:
  - "/dev/ttyUSB0:/dev/ttyUSB0"

dns

自定义 DNS 服务器,可以是单个值或列表的多个值。

dns: 8.8.8.8

dns:
  - 8.8.8.8
  - 9.9.9.9

自定义 DNS 搜索域。可以是单个值或列表。

dns_search: example.com

dns_search:
  - dc1.example.com
  - dc2.example.com

entrypoint

覆盖容器默认的 entrypoint。

entrypoint: /code/entrypoint.sh

也可以是以下格式:

entrypoint:
    - php
    - -d
    - zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/xdebug.so
    - -d
    - memory_limit=-1
    - vendor/bin/phpunit

env_file

从文件添加环境变量。可以是单个值或列表的多个值。

env_file: .env

也可以是列表格式:

env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/secrets.env

environment

添加环境变量。您可以使用数组或字典、任何布尔值,布尔值需要用引号引起来,以确保 YML 解析器不会将其转换为 True 或 False。

environment:
  RACK_ENV: development
  SHOW: 'true'

expose

暴露端口,但不映射到宿主机,只被连接的服务访问。

仅可以指定内部端口为参数:

expose:
 - "3000"
 - "8000"

extra_hosts

添加主机名映射。类似 docker client --add-host。

extra_hosts:
 - "somehost:162.242.195.82"
 - "otherhost:50.31.209.229"

以上会在此服务的内部容器中 /etc/hosts 创建一个具有 ip 地址和主机名的映射关系:

162.242.195.82  somehost
50.31.209.229   otherhost

healthcheck

用于检测 docker 服务是否健康运行。

healthcheck:
  test: ["CMD", "curl", "-f", "http://localhost"] # 设置检测程序
  interval: 1m30s # 设置检测间隔
  timeout: 10s # 设置检测超时时间
  retries: 3 # 设置重试次数
  start_period: 40s # 启动后,多少秒开始启动检测程序

image

指定容器运行的镜像。以下格式都可以:

image: redis
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry.com:4000/postgresql
image: a4bc65fd # 镜像id

logging

服务的日志记录配置。

driver:指定服务容器的日志记录驱动程序,默认值为json-file。有以下三个选项

driver: "json-file"
driver: "syslog"
driver: "none"

仅在 json-file 驱动程序下,可以使用以下参数,限制日志得数量和大小。

logging:
  driver: json-file
  options:
    max-size: "200k" # 单个文件大小为200k
    max-file: "10" # 最多10个文件

当达到文件限制上限,会自动删除旧得文件。

syslog 驱动程序下,可以使用 syslog-address 指定日志接收地址。

logging:
  driver: syslog
  options:
    syslog-address: "tcp://192.168.0.42:123"

network_mode

设置网络模式。

network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"

networks

配置容器连接的网络,引用顶级 networks 下的条目 。

services:
  some-service:
    networks:
      some-network:
        aliases:
         - alias1
      other-network:
        aliases:
         - alias2
networks:
  some-network:
    # Use a custom driver
    driver: custom-driver-1
  other-network:
    # Use a custom driver which takes special options
    driver: custom-driver-2

aliases :同一网络上的其他容器可以使用服务名称或此别名来连接到对应容器的服务。

restart

restart: "no"
restart: always
restart: on-failure
restart: unless-stopped

注:swarm 集群模式,请改用 restart_policy。

secrets

存储敏感数据,例如密码:

version: "3.1"
services:

mysql:
  image: mysql
  environment:
    MYSQL_ROOT_PASSWORD_FILE: /run/secrets/my_secret
  secrets:
    - my_secret

secrets:
  my_secret:
    file: ./my_secret.txt

security_opt

修改容器默认的 schema 标签。

security-opt:
  - label:user:USER   # 设置容器的用户标签
  - label:role:ROLE   # 设置容器的角色标签
  - label:type:TYPE   # 设置容器的安全策略标签
  - label:level:LEVEL  # 设置容器的安全等级标签

stop_grace_period

指定在容器无法处理 SIGTERM (或者任何 stop_signal 的信号),等待多久后发送 SIGKILL 信号关闭容器。

stop_grace_period: 1s # 等待 1 秒
stop_grace_period: 1m30s # 等待 1 分 30 秒 

默认的等待时间是 10 秒。

stop_signal

设置停止容器的替代信号。默认情况下使用 SIGTERM 。

以下示例,使用 SIGUSR1 替代信号 SIGTERM 来停止容器。

stop_signal: SIGUSR1

sysctls

设置容器中的内核参数,可以使用数组或字典格式。

sysctls:
  net.core.somaxconn: 1024
  net.ipv4.tcp_syncookies: 0

sysctls:
  - net.core.somaxconn=1024
  - net.ipv4.tcp_syncookies=0

tmpfs

在容器内安装一个临时文件系统。可以是单个值或列表的多个值。

tmpfs: /run

tmpfs:
  - /run
  - /tmp

ulimits

覆盖容器默认的 ulimit。

ulimits:
  nproc: 65535
  nofile:
    soft: 20000
    hard: 40000

volumes

将主机的数据卷或着文件挂载到容器里。

version: "3.7"
services:
  db:
    image: postgres:latest
    volumes:
      - "/localhost/postgres.sock:/var/run/postgres/postgres.sock"
      - "/localhost/data:/var/lib/postgresql/data"
Docker 使用

Dockerfile 配置模板


FROM openjdk:8
VOLUME /tmp
# EXPOSE 80
ENV TZ=Asia/Shanghai JAVA_OPTS=-Xmx512m
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ADD *.jar app.jar
ENTRYPOINT java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar
# ENTRYPOINT exec java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar --Dspring.config.location=/config/*
# 为了缩短 Tomcat 的启动时间,添加java.security.egd的系统属性指向/dev/urandom作为 ENTRYPOINT
ENTRYPOINT ['java', $JAVA_OPTS, '-Djava.security.egd=file:/dev/./urandom','-jar', '/app.jar', '--Dspring.config.location=/config/*']

FROM java:8
VOLUME /tmp
EXPOSE 8080
ENV TZ=Asia/Shanghai JAVA_OPTS=-Xmx512m
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ADD *.jar app.jar
ENTRYPOINT java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar

# 
FROM openjdk:11
VOLUME /tmp
ENV TZ=Asia/Shanghai JAVA_OPTS=-Xmx512m
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
ADD *.jar app.jar
# CMD echo '10.254.7.7 bzdts.chinaetc.org' >> /etc/hosts; java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar --Dspring.config.location=/config/*
ENTRYPOINT echo '10.254.7.7 bzdts.chinaetc.org' >> /etc/hosts && java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar --Dspring.config.location=/config/*


# tomcat_8.5.47-jdk11
FROM tomcat:8.5.47-jdk11
# MAINTAINER itqmdx@gmail.com
# VOLUME /usr/local/tomcat
EXPOSE 8080
# RUN rm -rf /usr/local/tomcat/webapps/*
ENV TZ=Asia/Shanghai JAVA_OPTS=-Xmx512m
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
# ADD ./target/* /usr/local/tomcat/webapps/
ENTRYPOINT ['/usr/local/tomcat/bin/catalina.sh', 'run']
# ENTRYPOINT echo '10.254.7.7 bzdts.chinaetc.org' >> /etc/hosts && /usr/local/tomcat/bin/catalina.sh run

Docker 使用

[shell] docker deploy service script 部署脚本模板

#!/bin/bash

# service name
SERVICE_NAME=service-name
# service port (--net=host invalid)
OPEN_PORT=7000
# 实例
INSTANCES=1

# log path
LOG_PATH=/logs

# author: wzhz
# email: itqmdx@gmail.com
# version: v0.4
version=v0.4

# local ip (--net=host invalid)
IP=$(ip a | grep inet | grep -v 127.0.0.1 | grep -v inet6 | grep -v docker | awk '{print $2}' | tr -d 'addr:' | awk -F '/' '{print $1}' | head -1)
# get ip
# ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | awk -F"/" '{print $1}'
DATEVERSION=$(date +'%Y.%m.%d.%H')

# script_dir
script_dir=$(readlink -f $0)
bootpath=$(dirname $script_dir)

# logspath=$bootpath/logs
# configpath=$bootpath/config
# jarpath=$bootpath/jar

RED='\e[1;31m'
GREEN='\e[1;32m'
YELLOW='\033[1;33m'
BLUE='\E[1;34m'
PINK='\E[1;35m'
RES='\033[0m'

# get all filename in specified path
getFileName() {
  path=$1
  files=$(ls $bootpath/jar)
  for filename in $files
  do
    echo $filename # >> filename.txt
  done

  for file in `find  $1 -name "*.jar"`
  do
    echo $file
  done
}

# touch Dockerfile
createDockerfile() {
#  --Dspring.config.location=/config/*
cat > ./Dockerfile << EOF
FROM openjdk:8
VOLUME /logs
EXPOSE $OPEN_PORT
ENV TZ=Asia/Shanghai JAVA_OPTS="-server -Xms512m -Xmx512m -XX:PermSize=64M -XX:MaxNewSize=256m -XX:MaxPermSize=128m -Djava.awt.headless=true "
RUN ln -snf /usr/share/zoneinfo/\$TZ /etc/localtime && echo \$TZ > /etc/timezone
ADD *.jar app.jar
ENTRYPOINT exec java \$JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar
EOF
}

# delete old and images
deleteOldImage() {
  docker image rm -f $SERVICE_NAME:$DATEVERSION >> /dev/null 2>&1;
  docker image ls
}
# delete old containers
deleteOldContainer() {
  OLD_INSTANCES=$(docker container ps -a | grep -i $SERVICE_NAME | wc -l);
  for((i=0;i<$OLD_INSTANCES;i++));
  do
    docker container stop $SERVICE_NAME-$i >> /dev/null 2>&1;
    docker container rm -f $SERVICE_NAME-$i >> /dev/null 2>&1;
  done
  # rm -rf $bootpath/logs;
  if docker container ps -a | grep -i $SERVICE_NAME;then
    echo -e $RED hase $OLD_INSTANCES instances. $RES
  fi
  docker container ps
}

# build docker image
buildImage() {
  docker build -t $SERVICE_NAME:$DATEVERSION . ;
  docker image ls
}

# run docker container
runImage() {
  for((i=0;i < $INSTANCES;i++));
  do
    name=$SERVICE_NAME-$i
    port=$(($OPEN_PORT+$i))
    docker container rm -f $name >> /dev/null 2>&1
    echo create container is $name:$IP:$port;

    # docker run \
    # --net=host \
    # -v $bootpath/logs:$LOG_PATH \
    # -v $bootpath/config:/config \
    # --name $name \
    # --restart=on-failure:10 \
    # -d $SERVICE_NAME >> /dev/null 2>&1
    docker run \
    --expose=$port \
    -p $port:$port \
    -v $bootpath/logs:$LOG_PATH \
    -e server.port=$port \
    -e spring.application.name=$SERVICE_NAME \
    -e spring.cloud.client.ip-address=$IP \
    -e EUREKA_INSTANCE_INSTANCE-ID=$IP:$SERVICE_NAME:$port \
    -e EUREKA_INSTANCE_IP-ADDRESS=$IP \
    -e SERVER_PORT=$port \
    -e JAVA_OPTS=-Xmx512m \
    --name $name \
    --restart=on-failure:10 \
    -d $SERVICE_NAME:$DATEVERSION # >> /dev/null 2>&1
    CONTAINERID_NEW=`docker container ps -a | grep ${name}| awk '{print $NF}'`
    echo new container created successfully is $CONTAINERID_NEW
    if [ $i -lt $INSTANCES ];then
      sleep 1;
    fi
  done
  docker container ps
}
startContainer() {
  for((i=0;i < $INSTANCES;i++));
  do
    name=$SERVICE_NAME-$i
    port=$(($OPEN_PORT+$i))

    docker container start $name >> /dev/null 2>&1
    echo start container is $name:$IP:$port
    if [ $i -lt $INSTANCES ];then
      sleep 1;
    fi
  done
  docker container ps
}
restartContainer() {
  for((i=0;i < $INSTANCES;i++));
  do
    name=$SERVICE_NAME-$i
    port=$(($OPEN_PORT+$i))

    docker container restart $name >> /dev/null 2>&1
    echo restart container is $name:$IP:$port
    if [ $i -lt $INSTANCES ];then
      sleep 1;
    fi
  done
  docker container ps
}
stopContainer() {
  for((i=0;i < $INSTANCES;i++));
  do
    name=$SERVICE_NAME-$i
    port=$(($OPEN_PORT+$i))

    docker container stop $name >> /dev/null 2>&1
    echo stop container is $name:$IP:$port
    if [ $i -lt $INSTANCES ];then
      sleep 1;
    fi
  done
  docker container ps
}
viewContainerLog() {
  if [ $INSTANCES -eq 1 ];then
    showLog $SERVICE_NAME-0
  else
    # more
    echo -e $GREEN show logs for containers: $RES
    docker ps -a | grep ${SERVICE_NAME}| awk '{print $1, $2, $(NF-1), $NF}'
    read -p 'please input a container id or name: ' input
    showLog $input
  fi
}
showLog() {
  docker container logs -f --tail=100 $1
}
readme() {
  echo -e $GREEN ---- deploy service script $RES
  echo -e $YELLOW ---- author: wzhz $RES
  echo -e $YELLOW ---- email: itqmdx@gmail.com $RES
  echo -e $YELLOW ---- version: $version $RES
}

current() {
  echo 
  echo -e $PINK current time is $(date +'%Y-%m-%d %T') $RES
  echo 
}

var() {
  echo 
  echo IP $IP
  echo SERVICE_NAME $SERVICE_NAME
  echo OPEN_PORT $OPEN_PORT
  echo INSTANCES $INSTANCES
  echo DATEVERSION $DATEVERSION
  echo 
}

# setting env var
setEnvironmentVariable() {
  ARRT=$1
  ARRT_NAME=`echo ${ARRT} | awk -F '=' '{print $1}'`
  ARRT_VALUE=`echo ${ARRT} | awk -F '=' '{print $2}'`
  # echo $ARRT_NAME is $ARRT_VALUE
  if [ $ARRT_NAME == 'name' ]; then
    SERVICE_NAME=$ARRT_VALUE
  elif [ $ARRT_NAME == 'port' ]; then
    OPEN_PORT=$ARRT_VALUE
  elif [ $ARRT_NAME == 'ip' ]; then
    IP=$ARRT_VALUE
  elif [ $ARRT_NAME == 'i' ]; then
    INSTANCES=$ARRT_VALUE
  else
    echo 
    echo -e $RED $ARRT no matches found. $RES
    echo 
  fi
}

functionItems() {
  echo 
  echo -e $GREEN = 0. perform steps 7-8 and 1-3 automatically $RES
  echo -e $BLUE = 1. create current environment\'s Dockerfile $RES
  echo -e $BLUE = 2. build image $SERVICE_NAME $RES
  echo -e $BLUE = 3. run image $SERVICE_NAME $RES
  echo -e $BLUE = 4. start $SERVICE_NAME\'s containers $RES
  echo -e $BLUE = 5. restart $SERVICE_NAME\'s containers $RES
  echo -e $BLUE = 6. stop $SERVICE_NAME\'s containers $RES
  echo -e $BLUE = 7. delete $SERVICE_NAME\'s containers  $RES
  echo -e $BLUE = 8. delete image $SERVICE_NAME $RES
  echo -e $BLUE = 9. view $SERVICE_NAME\'s container log $RES
  echo -e $RED = 99. configure global information $RES
  echo 
}

main() {
  functionItems
  read -p 'please input a function item no: ' input
  echo your input is $input
  case $input in
    0)
    deleteOldContainer
    echo -e $GREEN delete containers successfully. $RES
    deleteOldImage
    echo -e $GREEN delete image successfully. $RES
    createDockerfile
    echo -e $GREEN Dockerfile created successfully, default is based on openjdk:8. $RES
    buildImage
    echo -e $GREEN created successfully, image is $SERVICE_NAME. $RES
    runImage
    echo -e $GREEN runs successfully. $RES
    ;;
    1)
    createDockerfile
    echo -e $GREEN Dockerfile created successfully, default is based on openjdk:8. $RES
    cat Dockerfile
    ;;
    2)
    buildImage
    echo -e $GREEN created successfully, image is $SERVICE_NAME. $RES
    ;;
    3)
    runImage
    echo -e $GREEN runs successfully. $RES
    ;;
    4)
    startContainer
    echo -e $GREEN start container successfully. $RES
    ;;
    5)
    restartContainer
    echo -e $GREEN restart container successfully. $RES
    ;;
    6)
    stopContainer
    echo -e $GREEN stop container successfully. $RES
    ;;
    7)
    deleteOldContainer
    echo -e $GREEN delete containers successfully. $RES
    ;;
    8)
    deleteOldImage
    echo -e $GREEN delete image successfully. $RES
    ;;
    9)
    viewContainerLog
    echo -e $GREEN view container log complete. $RES
    ;;
    99)
    echo -e $YELLOW developing... $RES
    ;;
    *)
    echo -e $RED wrong input, exit 0. $RES
    exit 0
    ;;
  esac
}

echo -e $YELLOW working directory is $bootpath $RES
cd $bootpath;ls -all;

for arg in $@
do
  setEnvironmentVariable $arg
done

readme

current

var

while true
do
  main
  sleep 1
done

Docker 使用

[shell] Docker 部署脚本模板2

#!/bin/bash

if [ $1 == "" ];then
     echo "plsese input the image name of the jar api"
     exit 0
fi

IMAGE=$1 
echo "input image is ${IMAGE}"






######################## delete the container of this jar api  ########################

CONTAINERID=`docker ps -a|grep ${IMAGE} |awk '{print $1}'`
echo "contained id is ${CONTAINERID}"

docker stop ${CONTAINERID}
docker rm ${CONTAINERID}

########################  get image name and image tag ....sed -n 为获取第几行数据  ########################


IMAGENAME=`echo ${IMAGE} | awk -F ":" '{print $1}'`
echo "imagename is ${IMAGENAME}"

IMAGETAG=`echo ${IMAGE}| awk -F ":" '{print $2}'`
echo "imagetag is ${IMAGETAG}"

IMAGEROWNUM=`docker images | grep ${IMAGENAME}| wc -l`
echo "imagerownum is ${IMAGEROWNUM}"

########################  同一镜像名称可能有多个不同的版本,如果同时满足所要删除的镜像名称和tag,则删除该镜像 ########################
for ((i=1;i<=$IMAGEROWNUM;i++))
  do
    echo "i is ${i}"
    IMAGENAME2=`docker images|grep ${IMAGENAME} | sed -n "${i}p" |awk '{print $1}'`
    echo "imagename2 is ${IMAGENAME2}"
    if [ "${IMAGENAME2}" == "${IMAGENAME}" ];then
    IMAGETAG2=`docker images|grep ${IMAGENAME} | sed -n "${i}p" |awk '{print $2}'`
    echo "imagetag2 is ${IMAGETAG2}"
       if [ "${IMAGETAG2}" == "${IMAGETAG}" ];then
       IMAGEID=`docker images|grep ${IMAGENAME} | sed -n "${i}p" |awk '{print $3}'`
       echo "imageid is ${IMAGEID}"

       docker rmi ${IMAGEID}
       fi
    fi
done


######################## 创建镜像  ########################

docker build -t ${IMAGE} . 

######################## 生成容器,并在后台运行,生成容器时才会执行dockerfile文件中的java -jar等指令  ########################

docker run -d --net=host -v /home/xjjtuser/dataAnalysis-logs/:/data-analysis/  -v /home/xjjtuser/docker-program/config/:/config/ --name data-analysis  ${IMAGE}

# 显示日志信息
containerid_new=`docker ps -a | grep ${IMAGE}| awk '{print $1}'`
echo "containerid_new is ${containerid_new}"

docker logs "${containerid_new}"

Docker 使用

修改 docker 日志大小配置

修改 docker 日志大小配置

编辑或新建配置文件需要 root 用户

命 令: vi /etc/docker/daemon.json 添加以下内容:

{
"log-driver":"json-file",
"log-opts":{"max-size":"500M","max-file":"3"}  
}

配置完成重启 docker

重启docker: systemctl restart docker

Docker 使用

如何科学的在Docker Container中运行多个服务

在一个Docker Container中运行多个服务?打扰了。

0x00 前言

Docker,或说任何基于 内核 namespace 的轻量级进程隔离技术,在设计之初,都不是为了当作虚拟机使用的。也就是说,其中运行的并不是一个完整的操作系统。包括 Docker 官方,也是推荐在一个 Container 内仅运行一个服务。如果需要运行多个服务,应通过 docker run --link 或者 docker-compose 来关联多个容器。但是在实际的应用中,我们经常希望将一个完整的可运行环境打包成一个 docker image ,不再依赖其他的容器。比如在 CTF 比赛中,将多个服务打包成一个 Image ,可以有效地提高在环境受损后恢复的效率。在经历了多场比赛,看过各种大师傅用各种奇怪的姿势完成这个任务后,觉得应该好好的讨论一下这个问题。

0x01 错误的姿势

# Dockerfile
From ubuntu:14.04
RUN apt-get update && apt-get upgrade -y && apt-get install mysql apache2 php7.0 
ADD web /var/www/html
RUN service mysql start && /var/www/html/init_sql.sh && service mysql stop
CMD service mysql start && service apache2 start && while true; do sleep 10;done
# Dockerfile
From ubuntu:16.04
RUN apt-get update && apt-get upgrade -y && apt-get install mysql apache2 php7.0 
ADD web /var/www/html
RUN systemctl start mysql && /var/www/html/init_sql.sh && systemctl stop mysql
CMD systemctl start mysql && systemctl start apache2 && while true; do sleep 10;done
# Dockerfile
From ubuntu:16.04
RUN apt-get update && apt-get upgrade -y && apt-get install mysql apache2 php7.0 
ADD web /var/www/html
ADD entrypoint.sh /sbin/
RUN chmod +x /sbin/entrypoint.sh /var/www/html/init_sql.sh&& 
     /etc/init.d/mysql start && /var/www/html/init_sql.sh && /etc/init.d/mysql stop
CMD /sbin/entrypoint.sh
#!/bin/bash 

# entrypoint.sh
/usr/bin/mysqld start &
/usr/bin/httpd &
while true
do
sleep 100
done

在实际中,使用 方法1 或者 方法2 很大几率无法完成将多个服务跑在同一个container中的任务。 方法3 虽然可以,但仍然存在一些问题:

SIGTERM

方法1 和 方法2 不能成功,是因为docker只是一个进程隔离的沙箱环境,并不是真正的虚拟机。而 service xxx start 和 systemctl start xxx 分别是 upstart 和 systemd 这两个 /sbin/init 进程的替代者的服务管理命令。而 upstart 和 systemd 都要求系统必须是物理机或虚拟机,并不支持作为container的 init 进程。方法3存在问题是因为,在正常的系统中, init 进程永远占用 PID=1 的位置,回收僵尸进程、处理未处理的信号等都是由 init 进程帮我们完成的,一个子进程如果失去了父进程,也会由 init 进程接管。但是在container中, init 进程并不存在, PID=1 的进程是我们在 Dockerfile 中定义的 Entrypoint 或最后一个 CMD 指定的命令。

root@vpscn:/var/lib/docker# docker exec -it hackmd sh
/hackmd # ps -ef
PID   USER     TIME   COMMAND
    1 hackmd     1:03 node app.js
   42 hackmd     0:00 /usr/local/bin/node ./lib/workers/dmpWorker.js
   62 root       0:00 sh
   69 root       0:00 ps -ef

因此,对于启动方法3的container,我们应该在启动时加上 --init 参数,来强制使用 tini 作为 init 进程。但是就算这样,在服务多了之后,进行重启等操作仍然很繁琐。

0x02 推荐的姿势

作为一个金牌运维打杂的,简单谈谈我常用的方法。

首先推荐一个超级好用的基础镜像 phusion/baseimage 。截至SUCTF2018环境准备完成时,该镜像的最新版本是 0.10.1 ,基于 ubuntu 16.04 。我们常用的 apt-get 等命令都可以无缝兼容。 phusion/baseimage 采用了作者自己开发的一个基于 python 的 init 进程作为Container的 Entrypoint ,采用 runit 作为服务管理器。这个基础镜像还是在Coding打杂的时候知道的, Coding WebIDE Studio 的Web Terminal也是基于这个镜像做的。NUAACTF/SUCTF的PWN题基础镜像 ctf-xinetd 也是基于这个镜像做的。

然后直接贴个在SUCTF2018运维期间写的 Dockerfile

#Dockerfile
FROM phusion/baseimage:0.10.1
MAINTAINER Yibai Zhang 

RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list &&
    sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list &&
    apt-get update && apt-get install -y apache2 libapache2-mod-php php-mysql mariadb-server &&
    apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/www/html/*

RUN mkdir -p /etc/service/apache2/ && 
    printf "#!/bin/shnntrap "apachectl -k graceful-stop" 1 2 3 6 15nnexec /usr/sbin/apachectl -D FOREGROUNDn" > /etc/service/apache2/run &&
    chmod +x /etc/service/apache2/run && mkdir -p /etc/service/mysql/ &&
    printf "#!/bin/shnntrap "mysqladmin -uroot -psuCTF_P1us_1s shutdown" 1 2 3 6 15nnexec /usr/bin/mysqld_safe" > /etc/service/mysql/run &&
    mkdir -p /var/run/mysqld/ && chown mysql:mysql /var/run/mysqld &&
    chmod 700 /etc/service/mysql/run /etc/service/apache2/run

COPY web /var/www/html
COPY flag /flag
RUN echo "secure-file-priv=/var/www/" >>/etc/mysql/mariadb.conf.d/50-server.cnf && chmod -R 777 /var/www/html/favicon
COPY init_sql.sh /tmp/init_sql.sh
RUN chmod +x /tmp/init_sql.sh && bash -c "/tmp/init_sql.sh" && rm /tmp/init_sql.sh
EXPOSE 80
#!/usr/bin/env bash
#init_sql.sh

mysqld_safe &   
echo -n "Waiting for mysql startup"
while ! mysqladmin --host="localhost" --silent ping ; do
    echo -n "."
    sleep 1
done
echo

mysql -uroot <<EOF
UPDATE mysql.user SET Password=PASSWORD('XXXXXX'), plugin = '' WHERE User='root';
create database calc;
use calc;
create table user(
id INT NOT NULL AUTO_INCREMENT primary key,
username varchar(32) NOT NULL,
password varchar(32) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into user values(1,'admin','aa67095d8e65d624548cb6b50bd4778e');
create table file(
id INT NOT NULL AUTO_INCREMENT primary key,
filename varchar(32) NOT NULL,
filehash varchar(32) NOT NULL,
sig varchar(120) NOT NULL
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
create table flag(
flag varchar(120) primary key
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into flag values('SUCTF{a_very_long_long_long_long_long_fake_flag_d}');
grant SELECT, INSERT on calc.user to 'suctf'@localhost identified by 'suctf';
grant SELECT, INSERT, UPDATE on calc.file to 'suctf'@localhost ;
grant SELECT on calc.flag to 'suctf'@localhost ;
FLUSH PRIVILEGES;
EOF

mysqladmin -uroot -pXXXXXX shutdown

这里着重看一下 printf "#!/bin/shnntrap "apachectl -k graceful-stop" 1 2 3 6 15nnexec /usr/sbin/apachectl -D FOREGROUNDn" > /etc/service/apache2/run ,这个命令就是在创建runit启动脚本。具体的说明可以去看 phusion/baseimage 或者 runit 的手册。执行完成后会在 /etc/service/apache2/run 下面生成如下内容的脚本

#!/bin/sh

trap "apachectl -k graceful-stop" 1 2 3 6 15

exec /usr/sbin/apachectl -D FOREGROUND

这个脚本会作为runit的子进程运行,并将Apache2保持在前台运行。在接收到 1 2 3 6 15 这几个信号的时候友好的(graceful)结束Apache2。如果在运行中需要重启Apache服务,只需要运行 docker exec container_name sv restart apache2 即可。通过这种方式,在Container停止的时候也可以通知相关的进程,而不是直接全部杀死,更可以保证服务的完整性。~~ 虽然在比赛中基本挂了就要恢复环境根本不需要保证完整性。 ~~

Docker 实例

由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。 Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。 由于容器是进程级别的,相比虚拟机有很多优势。 (1)启动快 容器里面的应用,直接就是底层系统的一个进程,而不是虚拟机内部的进程。所以,启动容器相当于启动本机的一个进程,而不是启动一个操作系统,速度就快很多。 (2)资源占用少 容器只占用需要的资源,不占用那些没有用到的资源;虚拟机由于是完整的操作系统,不可避免要占用所有资源。另外,多个容器可以共享资源,虚拟机都是独享资源。 (3)体积小 容器只要包含用到的组件即可,而虚拟机是整个操作系统的打包,所以容器文件比虚拟机文件要小很多。 总之,容器有点像轻量级的虚拟机,能够提供虚拟化的环境,但是成本开销小得多。

Docker 实例

ansible 学习基础

ansible 基础

ansible, ansible-doc, ansible-galaxy, ansible-lint, ansible-playbook, ansible-pull, ansible-vault

管理节点

ssh映射: 10.44.111.115:179

安装常用工具

sudo yum install -y vim wget lrzsz screen
username root/develop
password 2019@eagle/develop

控制管理节点安装 ansible

sudo yum install ansible -y

使用 ssh-keygen 生成 ssh key 并使用 ssh-copy-id 分发到 server 节点

[develop@localhost ~]$ ssh-keygen

Generating public/private rsa key pair.
Enter file in which to save the key (/home/develop/.ssh/id_rsa): 
Created directory '/home/develop/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/develop/.ssh/id_rsa.
Your public key has been saved in /home/develop/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:i8laZFq8RAnOUEBdWLIy8LpDbZUiri9MbFmh+RKnUz4 develop@localhost.locald
The key's randomart image is:
+---[RSA 2048]----+
|..++++o          |
| o =o+..         |
| .*.=oo          |
|.=o*oo           |
|ooXo  * S        |
|oX.E B + .       |
|B o o * .        |
|.+   o           |
| .. .            |
+----[SHA256]-----+

[develop@localhost ~]$ cat .ssh/id_rsa.pub 
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFCAJp1+ER36dn1HN92/e2VbFAex1bLvaKEqXyG07l7NmQ0hXsfLHHMFkaHgRiVKdRccNe1gFOvRmmApxTocm+6X/S+e6SRQUjTlnVlOLWpKG42MqaEnmfNYb10uWrRKboBVUpt4xCIvd41TDAyqSQq5lPPPFPkc5J7vs9DIc1C5Seuzl2epMRhLQ195QPntxjLeD/Pgt+f7TCt8Q7PAkYWFwkIjcozUtFTS69CHobiMec5xPhpfWDUb7El8yC8KVR7hcFeDEG/gkqaU5Hzgow96T182jZG8ujOwVJ9dltpKuWvFnKxavZqVcooiCyi3jXjI2oaY8kdOKvvxJjWhwH develop@localhost.localdomain
[develop@localhost ~]$ 


[develop@localhost ~]$ ssh-copy-id -i ~/.ssh/id_rsa.pub 172.17.163.181
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/develop/.ssh/id_rsa.pub"
The authenticity of host '172.17.163.181 (172.17.163.181)' can't be established.
ECDSA key fingerprint is SHA256:iVZEKj9NsHP/zhwdfu/gZLfV51LIsidtEhp2HpNg9k0.
ECDSA key fingerprint is MD5:54:99:0d:aa:e8:c5:8a:5e:f2:e6:a6:e9:4c:d6:db:ac.
Are you sure you want to continue connecting (yes/no)? yes
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
develop@172.17.163.181's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh '172.17.163.181'"
and check to make sure that only the key(s) you wanted were added.

[develop@localhost ~]$ ssh 172.17.163.181
Last login: Sat Nov  2 14:00:59 2019 from gateway
[develop@localhost ~]$ exit
登出
Connection to 172.17.163.181 closed.
[develop@localhost ~]$ 


配置 ansible 文件

sudo vim /etc/ansible/ansible.cfg

sudo vim /etc/ansible/hosts

配置 host

sudo vim /etc/ansible/hosts

追加参数:


server-spring-cloud ansible_ssh_host=10.44.111.231 ansible_ssh_port=22 ansible_ssh_user=spring-cloud ansible_ssh_pass=spring-cloud

server-test-vars ansible_ssh_host=10.44.111.127 ansible_ssh_port=2222 ansible_ssh_user=develop ansible_ssh_pass=develop

[test]
172.17.163.179
172.17.163.181
172.17.163.186
172.17.163.184

[test:vars] # 属性
ansible_ssh_port=22
ansible_ssh_user=develop
ansible_ssh_pass=develop

[test:children] # 继承
10.44.111.127
server-centos-01 ansible_ssh_host=10.44.111.115  ansible_ssh_port=179 ansible_ssh_pass=develop
server-centos-02 ansible_ssh_host=10.44.111.115  ansible_ssh_port=181 ansible_ssh_pass=develop
server-ubuntu-01 ansible_ssh_host=10.44.111.115  ansible_ssh_port=190 ansible_ssh_pass=develop
server-ubuntu-02 ansible_ssh_host=10.44.111.115  ansible_ssh_port=189 ansible_ssh_pass=develop

inventory 内置参数

ansible_ssh_host # 要连接的主机名
ansible_ssh_port # 端口号默认是22
ansible_ssh_user # ssh连接时默认使用的用户名
ansible_ssh_pass # ssh连接时的密码
ansible_sudo_pass # 使用sudo连接用户是的密码
ansible_ssh_private_key_file # 秘钥文件如果不想使用ssh-agent管理时可以使用此选项
ansible_shell_type # shell的类型默认sh
ansible_connection # SSH 连接的类型: local , ssh , paramiko在 ansible 1.2 之前默认是 paramiko ,后来智能选择,优先使用基于 ControlPersist 的 ssh (支持的前提)
ansible_python _ interpreter #用来指定 python 解释器的路径,同样可以指定ruby 、perl 的路径

测试

ansible test -m shell -a 'echo "Hello Ansible.." && date'

172.17.163.186 | SUCCESS | rc=0 >>
Hello Ansible..
2019年 11月 02日 星期六 14:47:00 CST

172.17.163.184 | SUCCESS | rc=0 >>
Hello Ansible..
2019年 11月 02日 星期六 14:47:00 CST

172.17.163.181 | SUCCESS | rc=0 >>
Hello Ansible..
2019年 11月 02日 星期六 14:47:00 CST

172.17.163.179 | SUCCESS | rc=0 >>
Hello Ansible..
2019年 11月 02日 星期六 14:47:00 CST

[develop@localhost ~]$ 

动态 inventory

动态inventory的意思是所有的变量可以从外部获取,也就是说我们可以从CMDB以及zabbix系统拉取所有的主机信息然后使用ansible进行管理。引用inventory只需要把ansible.cfg文件中的inventory定义值改成一个执行脚本即可

#!/usr/bin/env python
# coding=utf-8
import json
masterIp = ['172.17.163.179']
slaveIp = ['172.17.163.181','172.17.163.186','172.17.163.184']
masterGroup = 'master'
slaveGroup = 'slave'
hostdata = {masterGroup:{"hosts":masterIp},slaveGroup:{"hosts":slaveIp}}
print json.dumps(hostdata,indent=4)

[develop@localhost ~]$ python inverti.py 
{
    "slave": {
        "hosts": [
            "172.17.163.181", 
            "172.17.163.186", 
            "172.17.163.184"
        ]
    }, 
    "master": {
        "hosts": [
            "172.17.163.179"
        ]
    }
}

[root@vagrant-centos65 opt]# ansible -i inverti.py all -a 'date'
SSH password: 
192.168.1.15 | SUCCESS | rc=0 >>
 07:25:27 up  3:56,  3 users,  load average: 0.00, 0.00, 0.00
192.168.1.110 | SUCCESS | rc=0 >>
 07:25:27 up 7 min,  3 users,  load average: 0.00, 0.02, 0.00

ping 模块

ansible test -m ping

setup模块

获取主机信息

ansible test -m setup

过滤

ansible test -m setup -a 'filter=ansible_default_ipv4'
ansible test -m setup -a 'filter=ansible_memory_mb'
ansible test -m setup -a 'filter=*mb'

参数

ansible_all_ipv4_addresses: 仅显示ipv4的信息。
ansible_devices: 仅显示磁盘设备信息。
ansible_distribution: 显示是什么系统,例: centos,suse等。
ansible_distribution_major_version: 显示是系统主版本。
ansible_distribution_version: 仅显示系统版本。
ansible_machine: 显示系统类型,例: 32位,还是64位。
ansible_eth0: 仅显示eth0的信息。
ansible_hostname: 仅显示主机名。
ansible_kernel: 仅显示内核版本。
ansible_lvm: 显示lvm相关信息。
ansible_memtotal_mb: 显示系统总内存。
ansible_memfree_mb: 显示可用系统内存。
ansible_memory_mb: 详细显示内存情况。
ansible_swaptotal_mb: 显示总的swap内存。
ansible_swapfree_mb: 显示swap内存的可用内存。
ansible_mounts: 显示系统磁盘挂载情况。
ansible_processor: 显示cpu个数(具体显示每个cpu的型号)。
ansible_processor_vcpus: 显示cpu个数(只显示总的个数)。
ansible_python_version: 显示python版本。

file 模块

https://docs.ansible.com/ansible/latest/cli/ansible.html

file模块可以帮助我们完成一些对文件的基本操作,比如,创建文件或目录、删除文件或目录、修改文件权限等

[develop@localhost ~]$ ansible test -m file -a 'src=/etc/fstab dest=/home/develop/fstab state=link'
172.17.163.186 | SUCCESS => {
    "changed": true, 
    "dest": "/home/develop/fstab", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0777", 
    "owner": "develop", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 10, 
    "src": "/etc/fstab", 
    "state": "link", 
    "uid": 1000
}
172.17.163.184 | SUCCESS => {
    "changed": true, 
    "dest": "/home/develop/fstab", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0777", 
    "owner": "develop", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 10, 
    "src": "/etc/fstab", 
    "state": "link", 
    "uid": 1000
}
172.17.163.181 | SUCCESS => {
    "changed": true, 
    "dest": "/home/develop/fstab", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0777", 
    "owner": "develop", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 10, 
    "src": "/etc/fstab", 
    "state": "link", 
    "uid": 1000
}
172.17.163.179 | SUCCESS => {
    "changed": true, 
    "dest": "/home/develop/fstab", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0777", 
    "owner": "develop", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 10, 
    "src": "/etc/fstab", 
    "state": "link", 
    "uid": 1000
}
[develop@localhost ~]$ ansible test -a 'ls -l'
172.17.163.181 | SUCCESS | rc=0 >>
总用量 0
lrwxrwxrwx. 1 develop develop 10 11月  2 16:42 fstab -> /etc/fstab

172.17.163.186 | SUCCESS | rc=0 >>
总用量 0
lrwxrwxrwx. 1 develop develop 10 11月  2 16:42 fstab -> /etc/fstab

172.17.163.184 | SUCCESS | rc=0 >>
总用量 0
lrwxrwxrwx. 1 develop develop 10 11月  2 16:42 fstab -> /etc/fstab

172.17.163.179 | SUCCESS | rc=0 >>
总用量 108
lrwxrwxrwx. 1 develop develop     10 11月  2 16:42 fstab -> /etc/fstab
-rwxrwxr-x. 1 develop develop    295 11月  2 15:42 inverti.py
-rw-rw-r--. 1 develop develop 102710 11月  2 16:26 sysinfo

[develop@localhost ~]$ 





[develop@localhost ~]$ ansible test -m file -a 'path=/home/develop/touch.txt state=touch'
172.17.163.186 | SUCCESS => {
    "changed": true, 
    "dest": "/home/develop/touch.txt", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0664", 
    "owner": "develop", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1000
}
172.17.163.181 | SUCCESS => {
    "changed": true, 
    "dest": "/home/develop/touch.txt", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0664", 
    "owner": "develop", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1000
}
172.17.163.184 | SUCCESS => {
    "changed": true, 
    "dest": "/home/develop/touch.txt", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0664", 
    "owner": "develop", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1000
}
172.17.163.179 | SUCCESS => {
    "changed": true, 
    "dest": "/home/develop/touch.txt", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0664", 
    "owner": "develop", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1000
}
[develop@localhost ~]$ ansible test -a 'ls -l'
172.17.163.184 | SUCCESS | rc=0 >>
总用量 0
lrwxrwxrwx. 1 develop develop 10 11月  2 16:42 fstab -> /etc/fstab
-rw-rw-r--. 1 develop develop  0 11月  2 16:45 touch.txt

172.17.163.186 | SUCCESS | rc=0 >>
总用量 0
lrwxrwxrwx. 1 develop develop 10 11月  2 16:42 fstab -> /etc/fstab
-rw-rw-r--. 1 develop develop  0 11月  2 16:45 touch.txt

172.17.163.181 | SUCCESS | rc=0 >>
总用量 0
lrwxrwxrwx. 1 develop develop 10 11月  2 16:42 fstab -> /etc/fstab
-rw-rw-r--. 1 develop develop  0 11月  2 16:45 touch.txt

172.17.163.179 | SUCCESS | rc=0 >>
总用量 108
lrwxrwxrwx. 1 develop develop     10 11月  2 16:42 fstab -> /etc/fstab
-rwxrwxr-x. 1 develop develop    295 11月  2 15:42 inverti.py
-rw-rw-r--. 1 develop develop 102710 11月  2 16:26 sysinfo
-rw-rw-r--. 1 develop develop      0 11月  2 16:45 touch.txt

[develop@localhost ~]$ 

参数

copy模块

[develop@localhost ~]$ ansible test -m copy -a 'src=/home/develop/xxx dest=/home/develop/xxx owner=develop mode=0700'
172.17.163.184 | SUCCESS => {
    "changed": true, 
    "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0700", 
    "owner": "develop", 
    "path": "/home/develop/xxx", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1000
}
172.17.163.181 | SUCCESS => {
    "changed": true, 
    "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0700", 
    "owner": "develop", 
    "path": "/home/develop/xxx", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1000
}
172.17.163.186 | SUCCESS => {
    "changed": true, 
    "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0700", 
    "owner": "develop", 
    "path": "/home/develop/xxx", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1000
}
172.17.163.179 | SUCCESS => {
    "changed": true, 
    "checksum": "da39a3ee5e6b4b0d3255bfef95601890afd80709", 
    "gid": 1000, 
    "group": "develop", 
    "mode": "0700", 
    "owner": "develop", 
    "path": "/home/develop/xxx", 
    "secontext": "unconfined_u:object_r:user_home_t:s0", 
    "size": 0, 
    "state": "file", 
    "uid": 1000
}
[develop@localhost ~]$ ansible test -a 'ls -l'
172.17.163.181 | SUCCESS | rc=0 >>
总用量 0
lrwxrwxrwx. 1 develop develop 10 11月  2 16:42 fstab -> /etc/fstab
-rw-rw-r--. 1 develop develop  0 11月  2 16:45 touch.txt
-rwx------. 1 develop develop  0 11月  2 17:41 xxx

172.17.163.186 | SUCCESS | rc=0 >>
总用量 0
lrwxrwxrwx. 1 develop develop 10 11月  2 16:42 fstab -> /etc/fstab
-rw-rw-r--. 1 develop develop  0 11月  2 16:45 touch.txt
-rwx------. 1 develop develop  0 11月  2 17:41 xxx

172.17.163.184 | SUCCESS | rc=0 >>
总用量 0
lrwxrwxrwx. 1 develop develop 10 11月  2 16:42 fstab -> /etc/fstab
-rw-rw-r--. 1 develop develop  0 11月  2 16:45 touch.txt
-rwx------. 1 develop develop  0 11月  2 17:41 xxx

172.17.163.179 | SUCCESS | rc=0 >>
总用量 108
lrwxrwxrwx. 1 develop develop     10 11月  2 16:42 fstab -> /etc/fstab
-rwxrwxr-x. 1 develop develop    295 11月  2 15:42 inverti.py
-rw-rw-r--. 1 develop develop 102710 11月  2 16:26 sysinfo
-rw-rw-r--. 1 develop develop      0 11月  2 16:45 touch.txt
-rwx------. 1 develop develop      0 11月  2 17:34 xxx

[develop@localhost ~]$ 
名称 必选 默认值 可选值 备注
backup no no yes/no 在覆盖之前将原文件备份,备份文件包含时间信息
content no 当用content代替src参数的时候,可以把文档的内容设置到特定的值
dest yes 目标绝对路径。如果src是一个目录,dest也必须是一个目录。如果dest是不存在的路径,并且如果dest以/结尾或者src是目录,则dest被创建。如果srcdest是文件,如果dest的父目录不存在,任务将失败
follow no no yes/no 是否遵循目的机器中的文件系统链接
force no yes yes/no 当内容不同于源时,将替换远程文件。设置为no,则只有在目标不存在的情况下才会传输文件
group no 设置文件/目录的所属组,将被馈送到chown
local_follow no yes yes/no 是否遵循本地机器中的文件系统链接
mode no 设置文件权限,模式实际上是八进制数字(如0644),少了前面的零可能会有意想不到的结果。从版本1.8开始,可以将模式指定为符号模式(例如u+rwx或u=rw,g=r,o=r)
owner no 设置文件/目录的所属用户,将被馈送到chown
remote_src(2.0+) no no yes/no 如果yes它会从目标机上搜索src文件
src no 将本地路径复制到远程服务器; 可以是绝对路径或相对的。如果是一个目录,它将被递归地复制。如果路径以/结尾,则只有该目录下内容被复制到目的地,如果没有使用/来结尾,则包含目录在内的整个内容全部复制
unsafe_writes no yes/no 是否以不安全的方式进行,可能导致数据损坏
validate no None 复制前是否检验需要复制目的地的路径

service 模块

1、启动服务:

ansible test -m service -a "name=nginx state=started"

2、停止服务:

ansible test -m service -a "name=nginx state=stopped"

3、重启服务:

ansible test -m service -a "name=nginx state=restarted"

4、开机启动:

ansible test -m service -a "name=nginx enable=yes"

5、停止开机启动:

ansible test -m service -a "name=nginx enable=no"

6、带参数启动:

ansible test -m service -a "name=network state=restarted args=eth0"

. . . . . . . .

playbook

首先简单说明一下 playbookplaybook 是什么呢?根本上说 playbookshell 脚本没有任何的区别, playbook 就像 shell 一样,也是把一堆的命令组合起来,然后加入对应条件判断等等,在shell脚本中是一条一条的命令,而在 playbook 中是一个一个的 task 任务构成,每个 task 任务可以看做 shell 中的一条命令;shell脚本一般只是在当前服务器上执行,而 playbook 则是在不止一个服务器上执行,因此 playbook 需要在其中指定运行该 playbook 的服务器名。

playbook的语法结构

playbook 使用 yml 标记语言,这是一种标记语言,这种标记语言在文件的最开始需要使用三个“-”来说明文件开始,然后使用缩进来说明代码块的范围。下面通过一个简易的实例,来说明playbook的语法。【实例来自官方文档】

---                             #标记文件的开始
- hosts: webservers             #指定该playbook在哪个服务器上执行
  vars:                         #表示下面是定义的变量,
    http_port: 80               #变量的形式,key: value,这里http_port是变量名,80是值
    max_clients: 200
  remote_user: root             #指定远程的用户名,这里缩进和vars保持了一致,说明变量的代码块已经结束。
  tasks:                        #下面构成playbook的tasks,每个task都有 - name: 开始,name指定该任务的名称。
  - name: ensure apache is at the latest version  #指定该任务的名称。
    yum: pkg=httpd state=latest                   #yum说明要是用的模板名称,后面指定对应的参数,这两行结合起来就相当于一个shell命令。

  - name: write the apache config file            #每个task之间可以使用空行来做区分。
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf

#需要说明的是缩进的意义和python中缩进的意义是一样,是来区分代码块的。

Tasks:任务,由模板定义的操作列表 Variables:变量 Templates:模板,即使用模板语法的文件 Handlers:处理器 ,当某条件满足时,触发执行的操作 Roles:角色

在playbook中的每一个play都可以选择在哪些服务器和以什么用户完成,hosts一行可以是一个主机组、主机、多个主机,中间以冒号分隔,可使用通配模式。其中remote_user表示执行的用户账号。

---
- hosts: test                        #指定主机组,可以是一个或多个组。
  remote_user: root                #指定远程主机执行的用户名

指定远程主机sudo切换用

# vim ping.yml
---
- hosts: test
  remote_user: root
  become: yes                 #2.6版本以后的参数,之前是sudo,意思为切换用户运行
  become_user: mysql          #指定sudo用户为mysql

执行playbook
# ansible-playbook ping.yml -K

1:Play的主体部分是task列表, task 列表中的各任务按次序逐个在hosts中指定的主机上执行,即在所有主机上完成第一个任务后再开始第二个任务。 在运行playbook时(从上到下执行),如果一个host执行task失败,整个tasks都会回滚,请修正playbook 中的错误,然后重新执行即可。 Task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量,模块执行时幂等的,这意味着多次执行是安全的,因为其结果一致。 2:每一个task必须有一个名称name,这样在运行playbook时,从其输出的任务执行信息中可以很好的辨别出是属于哪一个task的。如果没有定义name,‘action’的值将会用作输出信息中标记特定的task。 3:定义一个task,常见的格式:”module: options” 例如:yum: name=httpd 4:ansible的自带模块中,command模块和shell模块无需使用key=value格式

playbook基础使用

ansible-playbook playbook.yml [options]

-u REMOTE_USER, --user=REMOTE_USER 
# ssh 连接的用户名 
-k, --ask-pass 
#ssh登录认证密码 
-s, --sudo 
#sudo 到root用户,相当于Linux系统下的sudo命令
-U SUDO_USER, --sudo-user=SUDO_USER 
#sudo 到对应的用户 
-K, --ask-sudo-pass #用户的密码(—sudo时使用) 
-T TIMEOUT, --timeout=TIMEOUT 
# ssh 连接超时,默认 10 秒 
-C, --check # 指定该参数后,执行 playbook 文件不会真正去执行,而是模拟执行一遍,然后输出本次执行会对远程主机造成的修改 
-e EXTRA_VARS, --extra-vars=EXTRA_VARS 
# 设置额外的变量如:key=value 形式 或者 YAML or JSON,以空格分隔变量,或用多个-e 
-f FORKS, --forks=FORKS # 进程并发处理,默认 5 
-i INVENTORY, --inventory-file=INVENTORY 
# 指定 hosts 文件路径,默认 default=/etc/ansible/hosts 
-l SUBSET, --limit=SUBSET 
# 指定一个 pattern,对- hosts:匹配到的主机再过滤一次 
--list-hosts # 只打印有哪些主机会执行这个 playbook 文件,不是实际执行该 playbook 
--list-tasks # 列出该 playbook 中会被执行的 task 
--private-key=PRIVATE_KEY_FILE # 私钥路径 
--step # 同一时间只执行一个 task,每个 task 执行前都会提示确认一遍 
--syntax-check # 只检测 playbook 文件语法是否有问题,不会执行该 playbook 
-t TAGS, --tags=TAGS #当 play 和 task 的 tag 为该参数指定的值时才执行,多个 tag 以逗号分隔 
--skip-tags=SKIP_TAGS # 当 play 和 task 的 tag 不匹配该参数指定的值时,才执行 
-v, --verbose #输出更详细的执行过程信息,-vvv可得到所有执行过程信息。

原文链接:https://www.imooc.com/article/22729

-k(–ask-pass) 用来交互输入ssh密码 -K(-ask-become-pass) 用来交互输入sudo密码 -u 指定用户

ansible-playbook a.yml --syntax-check    #检查yaml文件的语法是否正确
ansible-playbook a.yml --list-task       #检查tasks任务
ansible-playbook a.yml --list-hosts      #检查生效的主机
ansible-playbook a.yml --start-at-task='Copy Nginx.conf'     #指定从某个task开始运行
# vim a.yml
---
- hosts: 192.168.200.129                  //指定主机
    remote_user: root                         //指定在被管理的主机上执行任务的用户
    tasks:                                            //任务列表↓
     - name: disable selinux                //任务名关闭防火墙
       command: '/sbin/setenforce 0'    //调用command模块 执行关闭防火墙命令
     - name: start httpd                         //任务名 开启httpd
       service: name=httpd state=started         //调用service模块 开启httpd 服务
# ansible-playbook a.yml --syntax-check    #检查yaml文件的语法是否正确
# ansible-playbook a.yml
mkdir -p ansible-playbook/roles/nginx/tasks

cd ansible-playbook

touch site.yml

touch roles/nginx/tasks/main.yml

site.yml

---
- hosts: dev1
  gather_facts: no
  remote_user: develop
  roles:
    - nginx

roles/nginx/tasks/main.yml

---
- name: install nginx
  yum: name=nginx state=present
- name: start service nginx
  service: name=nginx state=restated

ansible-playbook site.yml --syntax-check

ansible-playbook roles/nginx/tasks/main.yml --syntax-check

ansible centos -m user -a 'name=gdtcs'

vim chgpwd.yml

---
- hosts: centos
  gather_facts: false
  remote_user: root
  tasks:
  - name: Change password
    user: name={{ username }} password={{ passwd | password_hash('sha512') }} update_password=always

ansible-playbook chgpwd.yml -e "username=root passwd=root"

ansible centos -a 'yum install -y /root/docker/docker-ce-19.03.3-3.el7.x86_64.rpm'

Docker 实例

Docker 安装 Nginx

Nginx 是一个高性能的 HTTP 和反向代理 web 服务器,同时也提供了 IMAP/POP3/SMTP 服务 。

1、查看可用的 Nginx 版本

访问 Nginx 镜像库地址: https://hub.docker.com/_/nginx?tab=tags

可以通过 Sort by 查看其他版本的 Nginx,默认是最新版本 nginx:latest

你也可以在下拉列表中找到其他你想要的版本:

此外,我们还可以用 docker search nginx 命令来查看可用版本:

$ docker search nginx
NAME                      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
nginx                     Official build of Nginx.                        3260      [OK]       
jwilder/nginx-proxy       Automated Nginx reverse proxy for docker c...   674                  [OK]
richarvey/nginx-php-fpm   Container running Nginx + PHP-FPM capable ...   207                  [OK]
million12/nginx-php       Nginx + PHP-FPM 5.5, 5.6, 7.0 (NG), CentOS...   67                   [OK]
maxexcloo/nginx-php       Docker framework container with Nginx and ...   57                   [OK]
...

2、取最新版的 Nginx 镜像

这里我们拉取官方的最新版本的镜像:

$ docker pull nginx:latest

3、查看本地镜像

使用以下命令来查看是否已安装了 nginx:

$ docker images

在上图中可以看到我们已经安装了最新版本(latest)的 nginx 镜像。

4、运行容器

安装完成后,我们可以使用以下命令来运行 nginx 容器:

$ docker run --name nginx-test -p 8080:80 -d nginx

参数说明:

5、安装成功

最后我们可以通过浏览器可以直接访问 8080 端口的 nginx 服务:

Docker 实例

Docker 安装 MySQL

MySQL 是世界上最受欢迎的开源数据库。凭借其可靠性、易用性和性能,MySQL 已成为 Web 应用程序的数据库优先选择。

1、查看可用的 MySQL 版本

访问 MySQL 镜像库地址:https://hub.docker.com/_/mysql?tab=tags

可以通过 Sort by 查看其他版本的 MySQL,默认是最新版本 mysql:latest

你也可以在下拉列表中找到其他你想要的版本:

此外,我们还可以用 docker search mysql 命令来查看可用版本:

$ docker search mysql
NAME                     DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
mysql                    MySQL is a widely used, open-source relati...   2529      [OK]       
mysql/mysql-server       Optimized MySQL Server Docker images. Crea...   161                  [OK]
centurylink/mysql        Image containing mysql. Optimized to be li...   45                   [OK]
sameersbn/mysql                                                          36                   [OK]
google/mysql             MySQL server for Google Compute Engine          16                   [OK]
appcontainers/mysql      Centos/Debian Based Customizable MySQL Con...   8                    [OK]
marvambass/mysql         MySQL Server based on Ubuntu 14.04              6                    [OK]
drupaldocker/mysql       MySQL for Drupal                                2                    [OK]
azukiapp/mysql           Docker image to run MySQL by Azuki - http:...   2                    [OK]
...

2、拉取 MySQL 镜像

这里我们拉取官方的最新版本的镜像:

$ docker pull mysql:latest

3、查看本地镜像

使用以下命令来查看是否已安装了 mysql:

$ docker images

在上图中可以看到我们已经安装了最新版本(latest)的 mysql 镜像。

4、运行容器

安装完成后,我们可以使用以下命令来运行 mysql 容器:

$ docker run -itd --name mysql-test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 mysql

参数说明:

5、安装成功

通过 docker ps 命令查看是否安装成功:

本机可以通过 root 和密码 123456 访问 MySQL 服务。

# 查看已启动的服务
systemctl list-units --type=service
# 查看是否设置开机启动
systemctl list-unit-files | grep enable
# 设置开机启动
systemctl enable docker.service
# 关闭开机启动
systemctl disable docker.service

docker image pull mysql:5.7.28

# docker容器设置自动启动


# 启动时加--restart=always

docker run -id --restart=always -e MYSQL_ROOT_PASSWORD=2019@eagle --name mysql -v /mysql/data:/var/lib/mysql -v /etc/localtime:/etc/localtime -p 3306:3306 mysql:5.7.28

# 如果已经过运行的项目


docker container update --restart=always mysql

no 默认策略,在容器退出时不重启容器
on-failure 在容器非正常退出时(退出状态非0),才会重启容器
on-failure:3 在容器非正常退出时重启容器,最多重启3次
always 在容器退出时总是重启容器
unless-stopped 在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器



Flag	Description
no		不自动重启容器. (默认value)
on-failure 	容器发生error而退出(容器退出状态不为0)重启容器
unless-stopped 	在容器已经stop掉或Docker stoped/restarted的时候才重启容器
always 	在容器已经stop掉或Docker stoped/restarted的时候才重启容器

Docker 实例

可视化部署说明

将已经准备好的Hyper-V虚拟机导入

本步骤不做特殊说明,按提示操作即可

本虚拟机中安装了 docker 服务及常用的 image 并配置了开机启动; 安装了基本的常用工具: vim screen wget curl lrzsz

已安装的 docker 镜像


REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
tomcat              8.5.50-jdk8         6fa48e047721        3 days ago          507MB
tomcat              8.5.50-jdk11        f425297f8aa7        3 days ago          624MB
rabbitmq            3.8.1               843e6712e712        6 weeks ago         150MB
openjdk             11                  a7e47afa852b        8 weeks ago         605MB
mysql               5.7.28              cd3ed0dfff7e        2 months ago        437MB
nginx               1.16.1              b50b08c36b60        2 months ago        126MB
openjdk             8                   e8d00769c8a8        3 months ago        488MB
ansible/ansible     centos7             0731001e75a9        2 years ago         669MB

配置虚拟机系统

虚拟机开机之后默认的 IP10.44.111.123 子网掩码为 255.255.255.0 ( 24 ) 网关为 10.44.111.1

需要将以上网络配置为当前网络的信息,以下为操作步骤

说明

用户名 密码
root 2019@eagle
develop develop

nginx-html-path: /home/develop/html/

关键字 以下使用的 $PWD 均为当前目录, 也就是: /home/develop

部署 nginx 前端服务

网络配置完成之后即可通过 ssh 方式连接虚拟机,并把程序上传至虚拟机服务器中;以下使用 XShell 工具操作

部署 mysql 数据库服务

netstat -ntulp | grep 80 netstat -ntplu | grep docker

Docker 实例

Maven私服:Docker安装nexus3

Maven私服:Docker安装nexus3

查找nexus3镜像

develop@ubuntu:~$ docker search nexus3
NAME                                  DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
sonatype/nexus3                       Sonatype Nexus Repository Manager 3             697                                     
shifudao/nexus3                       clone from nexus3 image but based from openj…   4                                       [OK]
madmuffin/nexus3                      Sonatype Nexus3 Milestone7 docker image         2                                       [OK]
home1oss/nexus3                       An auto configured nexus3                       2                                       [OK]
fxinnovation/nexus3                   Sonatype Nexus 3 in a container                 1                                       [OK]
flavioaiello/nexus3                   Production ready lightweight Nexus3 with API…   1                                       [OK]
cirepo/nexus3                         An auto configured nexus3                       1                                       [OK]
alvindaiyan/nexus3-azure-appservice                                                   1                                       
dwolla/nexus3-crowd                   Nexus3 with nexus3-crowd-plugin installed       0                                       [OK]
joshdvir/nexus3                       nexus3                                          0                                       [OK]
freckleiot/nexus3-oss                 A Sonatype Nexus3 OSS image which makes it e…   0                                       [OK]
sdase/nexus3-base-image               An opinionated nexus3 docker image, based on…   0                                       [OK]
fgbulsoni/nexus3                      My fork of the Sonatype/Nexus3 image, with a…   0                                       
stocksoftware/nexus3                  A nexus3 docker instance                        0                                       [OK]
azaa1/nexus3                          Sonyatype Nexus Repository Manager 3            0                                       
salte/nexus3                          Extends the Sonatype nexus3 Docker image by …   0                                       [OK]
mritd/nexus3                          nexus3                                          0                                       [OK]
desiato/nexus3-ssl                    Sonatype Nexus 3 with SSL/TLS support.          0                                       [OK]
lokkju/nexus3-github-auth             Sonatype Nexus 3 with Github authentication …   0                                       [OK]
jullyannem/nexus3                     Custom image for sonatype/nexus3                0                                       
bigseasre/nexus3                      mirror of the original nexus3 dockerfile        0                                       
darthhater/nexus3                     A series of example Dockerfiles and images f…   0                                       [OK]
amribrahim00/nexus3                                                                   0                                       
enieuw/nexus3-oss                     nexus3-oss                                      0                                       [OK]
nasajon/nexus3                        Nexus3                                          0                                       [OK]

拉取nexus3镜像

develop@ubuntu:~$ docker search sonatype/nexus3
NAME                DESCRIPTION         STARS               OFFICIAL            AUTOMATED
develop@ubuntu:~$ docker image pull sonatype/nexus3
Using default tag: latest
latest: Pulling from sonatype/nexus3
c65691897a4d: Pull complete 
641d7cc5cbc4: Pull complete 
c508b13320cd: Pull complete 
79e3bf9d3132: Pull complete 
Digest: sha256:2c33632ccd8f8c5f9023a3d7f5f541e271833e402219f8c5a83a29d1721457ca
Status: Downloaded newer image for sonatype/nexus3:latest
docker.io/sonatype/nexus3:latest

查看镜像

develop@ubuntu:~$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
tomcat              8.5.47              882487b8be1d        12 days ago         507MB
ubuntu              16.04               b9409899fe86        12 days ago         122MB
ubuntu              18.04               cf0f3ca922e0        12 days ago         64.2MB
node                10.16.3             a68faf70e589        13 days ago         904MB
mysql               5.7.28              cd3ed0dfff7e        13 days ago         437MB
nginx               1.17.4              5a9061639d0a        13 days ago         126MB
redis               5.0.6               de25a81a5a0b        2 weeks ago         98.2MB
sonatype/nexus3     latest              8eb898be2a53        3 weeks ago         611MB
centos              8                   0f3e07c0138f        4 weeks ago         220MB
busybox             latest              19485c79a9bb        8 weeks ago         1.22MB
centos              7                   67fa590cfc1c        2 months ago        202MB
centos              7.6.1810            f1cb7c7d58b7        7 months ago        202MB
centos              6                   d0957ffdf8a2        7 months ago        194MB

查看docker nexus3 暴露端口

第一种方式 history

develop@ubuntu:~$ docker image history sonatype/nexus3:latest
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
8eb898be2a53        3 weeks ago         /bin/sh -c #(nop)  CMD ["sh" "-c" "${SONATYP…   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  ENV INSTALL4J_ADD_VM_PARA…   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  USER nexus                   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  EXPOSE 8081                  0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  VOLUME [/nexus-data]         0B                  
<missing>           3 weeks ago         |5 NEXUS_DOWNLOAD_SHA256_HASH=7a2e62848abeb0…   403MB               
<missing>           3 weeks ago         /bin/sh -c #(nop) ADD file:96a8726e4f3ee18b6…   1.97kB              
<missing>           3 weeks ago         /bin/sh -c #(nop)  ARG NEXUS_REPOSITORY_MANA…   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  ARG NEXUS_REPOSITORY_MANA…   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  ENV NEXUS_HOME=/opt/sonat…   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  ENV SONATYPE_DIR=/opt/son…   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  ARG NEXUS_DOWNLOAD_SHA256…   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  ARG NEXUS_DOWNLOAD_URL=ht…   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  ARG NEXUS_VERSION=3.19.1-…   0B                  
<missing>           3 weeks ago         /bin/sh -c #(nop)  LABEL vendor=Sonatype mai…   0B                  
<missing>           6 weeks ago                                                         3.55kB              
<missing>           6 weeks ago                                                         208MB               Imported from -

第二种方式 inspect

develop@ubuntu:~$ docker image inspect sonatype/nexus3:latest 
[
    {
        "Id": "sha256:8eb898be2a53434ed2310948ef1d4eb52d8298d400f0414d1fc5448cb10c1dc1",
        "RepoTags": [
            "sonatype/nexus3:latest"
        ],
        "RepoDigests": [
            "sonatype/nexus3@sha256:2c33632ccd8f8c5f9023a3d7f5f541e271833e402219f8c5a83a29d1721457ca"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2019-10-09T16:17:15.370913182Z",
        "Container": "e313220b483cfb870091d005efa119f8349d671b132191eff68523eb8a0412fe",
        "ContainerConfig": {
            "Hostname": "a3a96c4a2d45",
            "Domainname": "",
            "User": "nexus",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "8081/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "container=oci",
                "SONATYPE_DIR=/opt/sonatype",
                "NEXUS_HOME=/opt/sonatype/nexus",
                "NEXUS_DATA=/nexus-data",
                "NEXUS_CONTEXT=",
                "SONATYPE_WORK=/opt/sonatype/sonatype-work",
                "DOCKER_TYPE=rh-docker",
                "INSTALL4J_ADD_VM_PARAMS=-Xms1200m -Xmx1200m -XX:MaxDirectMemorySize=2g -Djava.util.prefs.userRoot=/nexus-data/javaprefs"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "CMD [\"sh\" \"-c\" \"${SONATYPE_DIR}/start-nexus-repository-manager.sh\"]"
            ],
            "ArgsEscaped": true,
            "Image": "sha256:4394c38c7fba645a92c4f96d71f05c9dcee3cd2259e7a45e69a655cafd775db4",
            "Volumes": {
                "/nexus-data": {}
            },
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": [],
            "Labels": {
                "architecture": "x86_64",
                "authoritative-source-url": "registry.access.redhat.com",
                "build-date": "2019-09-16T12:28:55.971236",
                "com.redhat.build-host": "cpt-1001.osbs.prod.upshift.rdu2.redhat.com",
                "com.redhat.component": "ubi8-container",
                "com.redhat.license_terms": "https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI",
                "com.sonatype.license": "Apache License, Version 2.0",
                "com.sonatype.name": "Nexus Repository Manager base image",
                "description": "The Universal Base Image is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.",
                "distribution-scope": "public",
                "io.k8s.description": "The Universal Base Image is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.",
                "io.k8s.display-name": "Red Hat Universal Base Image 8",
                "io.openshift.expose-services": "",
                "io.openshift.tags": "base rhel8",
                "maintainer": "Sonatype <cloud-ops@sonatype.com>",
                "name": "ubi8",
                "release": "208",
                "summary": "Provides the latest release of Red Hat Universal Base Image 8.",
                "url": "https://access.redhat.com/containers/#/registry.access.redhat.com/ubi8/images/8.0-208",
                "vcs-ref": "592956edf47ff0ce45123f240898eabb469ac836",
                "vcs-type": "git",
                "vendor": "Sonatype",
                "version": "8.0"
            }
        },
        "DockerVersion": "18.09.6",
        "Author": "",
        "Config": {
            "Hostname": "a3a96c4a2d45",
            "Domainname": "",
            "User": "nexus",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "8081/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "container=oci",
                "SONATYPE_DIR=/opt/sonatype",
                "NEXUS_HOME=/opt/sonatype/nexus",
                "NEXUS_DATA=/nexus-data",
                "NEXUS_CONTEXT=",
                "SONATYPE_WORK=/opt/sonatype/sonatype-work",
                "DOCKER_TYPE=rh-docker",
                "INSTALL4J_ADD_VM_PARAMS=-Xms1200m -Xmx1200m -XX:MaxDirectMemorySize=2g -Djava.util.prefs.userRoot=/nexus-data/javaprefs"
            ],
            "Cmd": [
                "sh",
                "-c",
                "${SONATYPE_DIR}/start-nexus-repository-manager.sh"
            ],
            "ArgsEscaped": true,
            "Image": "sha256:4394c38c7fba645a92c4f96d71f05c9dcee3cd2259e7a45e69a655cafd775db4",
            "Volumes": {
                "/nexus-data": {}
            },
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": [],
            "Labels": {
                "architecture": "x86_64",
                "authoritative-source-url": "registry.access.redhat.com",
                "build-date": "2019-09-16T12:28:55.971236",
                "com.redhat.build-host": "cpt-1001.osbs.prod.upshift.rdu2.redhat.com",
                "com.redhat.component": "ubi8-container",
                "com.redhat.license_terms": "https://www.redhat.com/en/about/red-hat-end-user-license-agreements#UBI",
                "com.sonatype.license": "Apache License, Version 2.0",
                "com.sonatype.name": "Nexus Repository Manager base image",
                "description": "The Universal Base Image is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.",
                "distribution-scope": "public",
                "io.k8s.description": "The Universal Base Image is designed and engineered to be the base layer for all of your containerized applications, middleware and utilities. This base image is freely redistributable, but Red Hat only supports Red Hat technologies through subscriptions for Red Hat products. This image is maintained by Red Hat and updated regularly.",
                "io.k8s.display-name": "Red Hat Universal Base Image 8",
                "io.openshift.expose-services": "",
                "io.openshift.tags": "base rhel8",
                "maintainer": "Sonatype <cloud-ops@sonatype.com>",
                "name": "ubi8",
                "release": "208",
                "summary": "Provides the latest release of Red Hat Universal Base Image 8.",
                "url": "https://access.redhat.com/containers/#/registry.access.redhat.com/ubi8/images/8.0-208",
                "vcs-ref": "592956edf47ff0ce45123f240898eabb469ac836",
                "vcs-type": "git",
                "vendor": "Sonatype",
                "version": "8.0"
            }
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 610620831,
        "VirtualSize": 610620831,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/1efc97c10191da75fce41b0da8f423b59a5d96ffb89e7b07d012ee1a64864c51/diff:/var/lib/docker/overlay2/aee2ffe75b92dc90ab960b6e7feb49474fabe44bed6529116a554a3ebf6e5c73/diff:/var/lib/docker/overlay2/5125a1abfcbbea486e7c0df81a40681797c24ff30e3bdbca477f0cb06964266d/diff",
                "MergedDir": "/var/lib/docker/overlay2/9ab78069111e94162980242cf5beb5a0591b29139ffb3bb21cac942a5a7ed14d/merged",
                "UpperDir": "/var/lib/docker/overlay2/9ab78069111e94162980242cf5beb5a0591b29139ffb3bb21cac942a5a7ed14d/diff",
                "WorkDir": "/var/lib/docker/overlay2/9ab78069111e94162980242cf5beb5a0591b29139ffb3bb21cac942a5a7ed14d/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:6866631b657ec5662f4faf948c771d9585a8ea005b1373360ada0a4507cd5c09",
                "sha256:48905dae401049ac43befb4f900a6aa0b5d30119db1f0cd0cca92980e0040ad0",
                "sha256:1e090454f289654b091f9c9c874c269968a8367657b0f2da1487d456c690fe13",
                "sha256:ca9d13755b9ef70fc1e01592d491fecd5661950812aa5bf1e0f4379254551c6b"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

运行nexus容器

develop@ubuntu:~$ docker run -id --privileged=true --name nexus3 --restart=always -p 8081:8081 -v /data/nexus3/nexus-data/:/nexus-data sonatype/nexus3:latest
6cea2db83fc4f708a5278e01a1cc6347f05b065102b3c85f4d49ae5ca3b6e6b3

解释:

8081 端口是 nexus 的服务访问端口 8082 端口是用于 host 镜像仓库的服务端口 8083 端口用户 group 镜像仓库的服务端口

-id 创建守护式容器 --privileged=true 授予root权限(挂载多级目录必须为true,否则容器访问宿主机权限不足) --name=名字 给你的容器起个名字 -p 宿主机端口:容器端口映射 -v 宿主机目录:容器目录 目录挂载

查看容器日志

develop@ubuntu:~$ docker container logs -f nexus3
2019-10-31 03:51:06,996+0000 INFO  [FelixStartLevel] *SYSTEM org.sonatype.nexus.pax.logging.NexusLogActivator - start
2019-10-31 03:51:07,702+0000 INFO  [FelixStartLevel] *SYSTEM org.sonatype.nexus.features.internal.FeaturesWrapper - Fast FeaturesService starting
2019-10-31 03:51:09,267+0000 WARN  [FelixStartLevel] *SYSTEM uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4JInitialiser - Your logging framework class org.ops4j.pax.logging.slf4j.Slf4jLogger is not known - if it needs access to the standard println methods on the console you will need to register it by calling registerLoggingSystemPackage
2019-10-31 03:51:09,270+0000 INFO  [FelixStartLevel] *SYSTEM uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4J - Package org.ops4j.pax.logging.slf4j registered; all classes within it or subpackages of it will be allowed to print to System.out and System.err
2019-10-31 03:51:09,277+0000 INFO  [FelixStartLevel] *SYSTEM uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4J - Replaced standard System.out and System.err PrintStreams with SLF4JPrintStreams
2019-10-31 03:51:09,279+0000 INFO  [FelixStartLevel] *SYSTEM uk.org.lidalia.sysoutslf4j.context.SysOutOverSLF4J - Redirected System.out and System.err to SLF4J for this context
...

注意: 运行容器后访问主机+配置的宿主机映射端口无反应时,请稍等几分钟(视配置时间长短不一),等待nexus3完成初始化才能访问成功

访问nexus3

确保正常启动后 使用浏览器访问http://服务器ip:8081

/nexus-data/admin.password

登录配置

sudo cat /var/lib/docker/volumes/[volume_id]/_data/admin.password

点击右上角登录 账号密码:admin/[admin123]

查看仓库

登录后点击设置界面 选择Repositories,点击Create repository

选择仓库类型 这里选择hosted类型

配置仓库 该仓库指定一个唯一的名称、HTTP的端口、允许交互的API等

Docker 实例

Docker 安装 ActiveMQ

搜索 ActiveMQ 镜像

docker search activemq

获取 ActiveMQ 镜像

docker pull webcenter/activemq

查看本地镜像

docker image ls

docker 启动 ActiveMQ 命令

docker run --name activemq \
-p 8161:8161 \
-p 1883:1883 \
-p 5672:5672 \
-p 61613:61613 \
-p 61614:61614 \
-p 61616:61616 \
-d webcenter/activemq

* 8161 是 web 页面管理端口(对外映射为8161)
* 1883 是 mqtt 使用端口(对外映射为1883)
* 5672 是 amcp 使用端口(对外映射为5672)
* 61613 是 stomp 使用端口(对外映射为61613)
* 61614 是 ws 使用端口(对外映射为61614)
* 61616 是 activemq 的容器使用端口(映射为61616)
* 8161 是 web 页面管理端口(对外映射为8161)

使用 docker ps 查看 ActiveMQ 已经运行了

使用 docker exec -it activemq /bin/bash 进入 ActiveMQ

访问

访问: http://ip:8161  默认用户:admin   默认密码:admin

Docker 实例

使用 GitLab Runner 配置 CI/CD 流水线

使用 GitLab Runner 配置 CI/CD 流水线

前言

本文以实际例子介绍如何使用 GitLab Runner 配置 CI/CD 流水线,实现持续集成和持续交付。

安装配置 Runner

.gitlab-ci.yml 文件中定义的作业运行在 Runner 中。 Runner 需要通过网络访问 GitLab.

虽然 Runner 和 GitLab 可以安装在一台机器上,但不推荐这么做。

Runner 可以由多个项目中共享,但出于安全等方面的原因,一个项目或一组项目最好有专门的服务器、虚拟机或容器运行 Runner.

安装配置的官方文档见公司 GitLab 服务器中相关帮助

安装

各种平台下的安装方法详见官方安装帮助文档

RHEL/CentOS/Fedora

# For RHEL/CentOS/Fedora
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash

# For RHEL/CentOS/Fedora
sudo yum install gitlab-runner

详见官方安装帮助文档

配置

Runner 有 Shared Runners (共享Runner, 整个 GitLab 实例范围内共享使用), Specific Runners (指定Runner, 单个项目专用), Group Runners (群组Runner, 组内项目共享使用) 三种,详见官方文档

Shared Runners 只能由 GitLab 管理配置,对于有特殊要求的项目,应考虑配置 Specific Runners 或 Group Runners.

Step1: 获取注册令牌 (token)

以配置 Specific Runners 为例,在 GitLab 实例中,进入 项目设置 » CI/CD 页面,展开 Runner 可以获取项目的注册令牌。在这个页面也可以修改 Runner 和 CI/CD 的一些配置。

例如,对于 vue-element-admin 这个项目,可以转到这个链接

泄露注册令牌可能会产生一些安全问题,故应妥善保管。万一泄露,可在本页面重置注册令牌。

Step2: 规划 Executors

Shell

Shell 的优势是灵活、调试简单,但一些手工操作并不能从代码中体现出来。在项目初期可以考虑这种方式。

Docker

Docker 是应用最广的方式,可以适应各种类型程序的编译打包。

Step3: 注册

除了 Executors, 在注册 Runner 的过程中还需要考虑命名、标签等,不同平台的注册方法也略有不同,详见官方文档。

Linux

  1. 运行命令
sudo gitlab-runner register
  1. 按提示输入 GitLab instance URL, URL 可从前面复制注册令牌的页面复制
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
https://gitlab.eaglesoftware.cn/
  1. 按提示输入注册令牌
Please enter the gitlab-ci token for this runner:
xxxxxxxxxxxxxxxxxxxx
  1. 按提示输入 Runner 描述信息,以后可从 CI/CD 设置页面修改
Please enter the gitlab-ci description for this runner:
[localhost.localdomain]: vm-wuzhuangzhuang
  1. 按提示输入与 Runner 关联的标签,标签可以用于选择 Runner, 以后可从 CI/CD 设置页面修改
Please enter the gitlab-ci tags for this runner (comma separated):
vm-wzz,centos
  1. 按提示输入 Runner Executors
Please enter the executor: docker+machine, docker-ssh+machine, kubernetes, virtualbox, docker, docker-ssh, parallels, shell, ssh, custom:
shell
  1. 如果选择 Docker 作为 executor, 还需要输入默认镜像,在 .gitlab-ci.yml 中未定义镜像时使用。

示例

官方文档有各种各样的示例。这里以 vue-element-admin 这个项目的配置演进为例,说明配置 CI/CD 过程中遇到的问题、想要达成的目标,以及解决办法。

项目说明

CI/CD 流水线一般由提交代码触发,包括自动编译(打包)、自动化测试、自动部署等步骤 (stages)。

要配置 CI/CD 流水线,首先需要知道手工如何实现这些工作,然后再编写代码实现自动化。

vue-element-admin 这个项目是基于 npm 的前端演示项目,没有后端和数据库等组件,项目组提供了一台 CentOS 虚拟机用于打包和部署,但没有提供自动化测试相关过程,所以只需在同一台机器上做打包和部署两个过程。

Version 1: Shell 版

Step1: Runner 安装配置

见前面的描述。

Step2: 打包工具安装

虽然这个项目有比较详尽的 README.md, 但仍没说明不支持最新的 node 12.16.1, 也没有说明需要安装 Development Tools. (实际上 .travis.yml 中有一行 node_js: 10)

如果对一个历史比较悠久的项目做一点小改动,这可能是一个需要时间修补的小坑。

# 去除以前安装的 nodejs
sudo yum remove nodejs
# 清除以前的安装源
sudo yum clean all
# 按官方文档 https://github.com/nodesource/distributions/blob/master/README.md#debinstall 设置安装源
curl -sL https://rpm.nodesource.com/setup_10.x | sudo bash -
# 安装 nodejs
sudo yum install -y nodejs
# 安装开发工具
yum install gcc-c++ make
# 或
# yum groupinstall 'Development Tools'
# 查看版本信息
npm version

查看版本信息的结果:

{ npm: '6.13.4',
  ares: '1.15.0',
  brotli: '1.0.7',
  cldr: '35.1',
  http_parser: '2.9.3',
  icu: '64.2',
  modules: '64',
  napi: '5',
  nghttp2: '1.39.2',
  node: '10.19.0',
  openssl: '1.1.1d',
  tz: '2019c',
  unicode: '12.1',
  uv: '1.28.0',
  v8: '6.8.275.32-node.55',
  zlib: '1.2.11' }

Step3: 配置权限

因为后面想直接用 gitlab-runner 用户操作 docker 服务,故将 gitlab-runner 加入 docker 组中。

sudo usermod -aG docker gitlab-runner
sudo -u gitlab-runner -H docker info

Step4: 编写 .gitlab-ci.yml 文件

.gitlab-ci.yml 配置流水线如何运行,其编写详见 GitLab CI/CD Pipeline Configuration Reference.

v1.1 - 初始版本

第 1 版设置 2 个步骤,打包和部署, .gitlab-ci.yml 文件的内容如下。

stages:
  - build
  - deploy

# 所有 stage 之前的操作
before_script:
  - pwd
  - npm set registry https://mirrors.huaweicloud.com/repository/npm/

cache:
  paths:
    - node_modules/
    - dist
  
build_job:
  stage: build
  tags:
    - centos
    - vm-wzz
  script:
    - echo '打包'
    - npm install
    - rm -rf ./dist
    - npm run build

deploy_job:
  stage: deploy
  tags:
    - centos
    - vm-wzz
  script:
    - echo '部署'
    - cd docker-compose
    - docker-compose stop
    - docker-compose up -d

在项目组提供的虚拟机上, 总耗时 03:06, 其中 build_job 耗时 02:03, deploy_job 耗时 01:02, deploy_job 耗时超出预期。

经检查,deploy_job 这一步要从 GitLab 上重新拉取代码,前一步 build_job 生成的 node_modules/ 和 dist/ 会被删除,然后从缓存区复制过来,虽然是本地缓存,但 node_modules/ 占用空间多达几百兆,而且不是两个步骤公用的,没有必要进行缓存。

116     ./public
1184    ./.git
44      ./plop-templates
9324    ./dist
8       ./build
12      ./docker-compose
84      ./mock
450948  ./node_modules
2868    ./src
40      ./tests
465488  .

v1.2 - 合并打包和部署

合并两个阶段后,总耗时 02:32. 修改后的 .gitlab-ci.yml 文件内容如下。

stages:
  - build

# 所有 stage 之前的操作
before_script:
  - pwd
  - npm set registry https://mirrors.huaweicloud.com/repository/npm/

cache:
  paths:
    - node_modules/
    - dist
  
build_job:
  stage: build
  tags:
    - shell
    - vm-wzz
  script:
    - echo '打包+部署'
    - npm install
    - npm run build
    - cd docker-compose
    - docker-compose stop
    - docker-compose up -d

package.json 没有变化时,npm install 没有必要每次运行。

v1.3 - npm install 仅在需要时运行

在前面的基础上,继续修改 .gitlab-ci.yml 文件,只在 package.json 文件发生变化时执行 npm install.

新提交版本触发的流水线最快总耗时 01:23。

stages:
  - npm_install
  - build

# 所有 stage 之前的操作
before_script:
  - pwd
  - npm set registry https://mirrors.huaweicloud.com/repository/npm/

cache:
  paths:
    - node_modules/

npm_install_job:
  stage: npm_install
  tags:
    - shell
    - vm-wzz
  script:
    - npm install
  only:
    changes:
      - package.json

build_job:
  stage: build
  tags:
    - shell
    - vm-wzz
  script:
    - npm run build
    - cd docker-compose
    - docker-compose stop
    - docker-compose up -d

按照同样的方法,也可以在 docker-compose/ 文件夹下面的文件发生变化时,重新构建 Docker 镜像。

曾经踩过的坑(修复的问题)

网络问题还是 Git 版本问题?

使用项目组提供的虚拟机,曾经有多次出现拉取代码出错的问题,而在华为云的云主机上注册的 Runner 从未出现过类似问题,当时怀疑是公司网络不稳定。

出现过的错误有:

  1. The remote end hung up unexpectedly
重新初始化现存的 Git 版本库于 /home/gitlab-runner/builds/z5U7Uaf7/0/opd/vue-element-admin/.git/
 error: RPC failed; result=6, HTTP code = 0
 fatal: The remote end hung up unexpectedly
 ERROR: Job failed: exit status 1
  1. Could not resolve host: gitlab.eaglesoftware.cn
Fetching changes with git depth set to 50...
00:01
 重新初始化现存的 Git 版本库于 /home/gitlab-runner/builds/z5U7Uaf7/0/opd/vue-element-admin/.git/
 fatal: unable to access 'https://gitlab-ci-token:[MASKED]@gitlab.eaglesoftware.cn/opd/vue-element-admin.git/': Could not resolve host: gitlab.eaglesoftware.cn; Unknown error
 ERROR: Job failed: exit status 1
  1. Could not resolve host: github.com (npm install 过程中出现的问题)
npm ERR! Error while executing:
npm ERR! /usr/bin/git ls-remote -h -t https://github.com/nhn/raphael.git
npm ERR!
npm ERR! fatal: unable to access 'https://github.com/nhn/raphael.git/': Could not resolve host: github.com; Unknown error
npm ERR!
npm ERR! exited with error code: 128
npm ERR! A complete log of this run can be found in:
npm ERR!     /home/gitlab-runner/.npm/_logs/2020-03-09T07_25_38_871Z-debug.log
ERROR: Job failed: exit status 1
  1. git fetch-pack: expected shallow list
Running on localhost.localdomain...
00:00
Fetching changes with git depth set to 50...
00:03
 重新初始化现存的 Git 版本库于 /home/gitlab-runner/builds/z5U7Uaf7/0/opd/vue-element-admin/.git/
 fatal: git fetch-pack: expected shallow list
 fatal: The remote end hung up unexpectedly
 ERROR: Job failed: exit status 1

前 3 个可能真是网络不稳定,但第 4 个多次重试仍然不行,经百度搜索,确认是 CentOS 7 在安装 Runner 时默认安装的 Git 版本过低 (git version 1.8.3.1), 升级 Git 版本 (git version 2.22.2) 后恢复正常。由此可见,即使是个虚拟机,最好也安装个靠谱的版本。

CentOS 7 下的升级过程在 Git 官网有链接,这里使用 ius.io 提供的安装源:

yum install \
https://repo.ius.io/ius-release-el7.rpm \
https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum repolist
yum remove git
yum provides git
yum install git222
git --version
# 删除 git 时同时删除了依赖 git 的 gitlab-runner, 所以重新安装
yum install gitlab-runner

node 版本

node 之类的工具版本更新比较快,而且新版本不一定支持原先的代码。辛辛苦苦安装好 node 12.16.1, 还要想办法清除。详见前面描述。

如果使用 Docker, 这个坑修复的成本会低很多。

Docker Engine 未启动

$ docker-compose stop
 Couldn't connect to Docker daemon at http+docker://localhost - is it running?
 If it's at a non-standard location, specify the URL with the DOCKER_HOST environment variable.
 ERROR: Job failed: exit status 1

这个问题比较明显,启动 Docker Engine 并设置开机启动后恢复正常。

systemctl start docker
systemctl enable docker

Version 2: Docker 版

Why

前面的版本虽然完成了打包和部署,但坑比较多,而且打包和部署是在同一台机器上,而测试环境、正式运行环境一般都是与开发环境分离的。

在这个版本中,我们使用 docker 完成打包,并使用 ssh 把打包好的软件部署到运行环境中。

这种部署方式需要运行 runner 的机器要能通过网络访问部署目标机器,比如从公司内网可以部署到云服务器,但反过来就不太方便;如果目标机器的 IP 地址是动态获取的,也容易出现问题。

Step1: Runner 安装配置

见前面的描述,以下略有不同:

Step2: 生成密钥对并分别配置到目标机器和 GitLab 环境变量

生成密钥对,不设置 passphrase, 然后将公钥配置到目标机器。

$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/96129/.ssh/id_rsa): id_rsa_ci_test
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in id_rsa_ci_test.
Your public key has been saved in id_rsa_ci_test.pub.
$ ssh-copy-id -i id_rsa_ci_test.pub root@10.44.111.27
$ cat id_rsa_ci_test

在 GitLab 项目设置 » CI/CD 页面,新增 3 个变量:

这 3 个变量会在接下来的 .gitlab-ci.yml 文件中用到。

Step3: 编写 .gitlab-ci.yml 文件

测试好的 .gitlab-ci.yml 文件如下。

image: node:10.19.0-buster

stages:
  - npm_install
  - build_docker
  - deploy

variables:
  TARGET_DIR: ~/vue-element-admin/

# 所有 stage 之前的操作
before_script:
  - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
  - mkdir -p ~/.ssh
  - eval $(ssh-agent -s)
  - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
  - ssh-add <(echo "$STAGING_PRIVATE_KEY")
  - npm set registry https://mirrors.huaweicloud.com/repository/npm/

stage_npm_install:
  stage: npm_install
  tags:
    - docker
    - vm-wzz
  cache:
    paths:
      - node_modules/  
  script:
    - npm install
  only:
    changes:
      - package.json

stage_build_docker:
  stage: build_docker
  tags:
    - docker
    - vm-wzz
  script:
    - ssh $SERVER_USER@$SERVER_HOST "mkdir -p $TARGET_DIR/docker-compose/"
    - scp -r docker-compose/* $SERVER_USER@$SERVER_HOST:$TARGET_DIR/docker-compose/
    - ssh $SERVER_USER@$SERVER_HOST "docker-compose -f $TARGET_DIR/docker-compose/docker-compose.yml build"
  only:
    changes:
      - docker-compose/**/*
      
stage_deploy:
  stage: deploy
  artifacts:
    paths:
      - dist/
  tags:
    - docker
    - vm-wzz
  cache:
    paths:
      - node_modules/  
    policy: pull
  script:
    - npm run build
    - ssh $SERVER_USER@$SERVER_HOST "mkdir -p $TARGET_DIR/dist/"
    - scp -r dist/* $SERVER_USER@$SERVER_HOST:$TARGET_DIR/dist/
    - ssh $SERVER_USER@$SERVER_HOST "mkdir -p $TARGET_DIR/docker-compose/"
    - scp -r docker-compose/* $SERVER_USER@$SERVER_HOST:$TARGET_DIR/docker-compose/
    - ssh $SERVER_USER@$SERVER_HOST "cd $TARGET_DIR/docker-compose/ && docker-compose stop && docker-compose up -d"

这里把整个流水线分成 3 步,第 1 步为 npm_install, 只在 package.json 发生变化时运行,生成的文件作为后续步骤的缓存。

第 2 步为 build_docker, 在 docker-compose/ 文件夹下的内容发生变化时运行,由于其中并没有 Dockerfile, 直接使用的 nginx 服务,这一步并没有实质作用。

第 3 步为 deploy, 拉取第 1 步生成的缓存目录 node_modules/, 进行打包,并将打包后的软件复制到目标机器,指示目标机器重新启动容器。

注意事项

由于第 3 步依赖第 1 步生成的缓存,而第 1 步只在 package.json 发生变化时才触发运行,在首次提交 .gitlab-ci.yml 而 package.json 未同时变化时,流水线只运行第 3 步会出现失败。

此时,应从 GitLab 项目的 CI / CD 页面单击“运行流水线”,手动触发完整流水线的运行,以后再提交代码时触发运行的流水线将不会因此问题失败。

结果分析

由于拉取 docker 镜像、复制文件等相比 Version 1 的方式都将带来额外的资源消耗,增加了向 GitLab 上传工件的环节,流水线最快总耗时为 02:55 (在 package.json 等不发生变化时), 比 Version 1 的最佳结果约慢 90 秒钟。

但这种部署方式的几乎把所有步骤都写入代码中,非常清楚,而性能的下降可以通过更好的硬件设施来弥补。

更好的部署方式是把打包好的工件(如生成容器镜像)推送到统一的注册中心,在运行环境中拉取工件运行。这样,只有注册中心需要有固定的域名或 IP 地址,但这对资源要求较高,暂不考虑这种方式。

Docker 实例

Docker 创建 Nginx 和 php-fpm环境

Docker创建Nginx和php-fpm环境


docker image pull nginx

docker image pull php:fpm

docker container run -d -it --name nginx -p 80:80 nginx

docker container run -d -it --name php-fpm -v /www:/www --network=container:nginx php:fpm


docker container run -d -it --name php-fpm -v /home/develop/service:/www --network=container:nginx php:fpm


docker container run -d \
--name php-fpm \
-v /data/server/pm/pm_service:/www \
-v /data/server/pm/logs:/logs \
-p 9000:9000 \
php:7.4.21-fpm



cd /usr/local/bin
./docker-php-ext-install mysqli

echo '

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /www;
        index  index.html index.htm index.php;
    }

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /service/www/$fastcgi_script_name;
        include        fastcgi_params;
    }
}

' > default.conf

docker cp default.conf nginx:/etc/nginx/conf.d/default.conf

docker exec nginx nginx -s reload

mkdir /www

echo '<?php echo time();' > /www/test.php

curl http://127.0.0.1/test.php

Docker 实例

Docker 安装 MongoDB

Docker 安装 MongoDB

MongoDB 是一个免费的开源跨平台面向文档的 NoSQL 数据库程序。

1、查看可用的 MongoDB 版本

访问 MongoDB 镜像库地址: mongo

可以通过 Sort by 查看其他版本的 MongoDB,默认是最新版本 mongo:latest。

你也可以在下拉列表中找到其他你想要的版本:

此外,我们还可以用 docker search mongo 命令来查看可用版本:

docker image search mongo
NAME                              DESCRIPTION                      STARS     OFFICIAL   AUTOMATED
mongo                             MongoDB document databases ...   1989      [OK]       
mongo-express                     Web-based MongoDB admin int...   22        [OK]       
mvertes/alpine-mongo              light MongoDB container          19                   [OK]
mongooseim/mongooseim-docker      MongooseIM server the lates...   9                    [OK]
torusware/speedus-mongo           Always updated official Mon...   9                    [OK]
jacksoncage/mongo                 Instant MongoDB sharded cluster  6                    [OK]
mongoclient/mongoclient           Official docker image for M...   4                    [OK]
jadsonlourenco/mongo-rocks        Percona Mongodb with Rocksd...   4                    [OK]
asteris/apache-php-mongo          Apache2.4 + PHP + Mongo + m...   2                    [OK]
19hz/mongo-container              Mongodb replicaset for coreos    1                    [OK]
nitra/mongo                       Mongo3 centos7                   1                    [OK]
ackee/mongo                       MongoDB with fixed Bluemix p...  1                    [OK]
kobotoolbox/mongo                 https://github.com/kobotoolb...  1                    [OK]
valtlfelipe/mongo                 Docker Image based on the la...  1                    [OK]

2、取最新版的 MongoDB 镜像

这里我们拉取官方的最新版本的镜像:

docker pull mongo:latest

3、查看本地镜像

使用以下命令来查看是否已安装了 mongo:

docker image ls

在上图中可以看到我们已经安装了最新版本(latest)的 mongo 镜像。

4、运行容器

安装完成后,我们可以使用以下命令来运行 mongo 容器:

docker run -itd --name mongo -p 27017:27017 mongo --auth

docker run -d \
--name mongo \
--restart=always \
-p 27017:27017 \
-v /mongo/initdb:/docker-entrypoint-initdb.d \
-v /mongo/datadic:/data/db \
-v /mongo/configdb:/data/configdb \
-e MONGO_INITDB_ROOT_USERNAME=mongoadmin \
-e MONGO_INITDB_ROOT_PASSWORD=123456 \
-e MONGO_INITDB_DATABASE=test \
mongo:4.4.8 --auth

db.createUser({ user:'mongoadmin',pwd:'123456',roles:[ { role:'userAdminAnyDatabase', db: 'admin'},"readWriteAnyDatabase"]});

db.auth('mongoadmin', '123456')

mongorestore -h 10.44.111.123:27017 -u mongoadmin -p 123456 --authenticationDatabase admin -d v3 ./

参数说明:

5、安装成功

最后我们可以通过 docker container ps 命令查看容器的运行信息:

接着使用以下命令添加用户和设置密码,并且尝试连接。

docker container exec -it mongo mongo admin
# 创建一个名为 admin,密码为 123456 的用户。
>  db.createUser({ user:'admin',pwd:'123456',roles:[ { role:'userAdminAnyDatabase', db: 'admin'},"readWriteAnyDatabase"]});
# 尝试使用上面创建的用户信息进行连接。
> db.auth('admin', '123456')

Docker 实例

Docker 安装 PHP

Docker 安装 PHP

安装 PHP 镜像

方法一、docker pull php

查找 Docker Hub 上的 php 镜像:

可以通过 Sort by 查看其他版本的 php,默认是最新版本 php:latest。

此外,我们还可以用 docker search php 命令来查看可用版本:

docker search php
NAME                      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
php                       While designed for web development, the PH...   1232      [OK]       
richarvey/nginx-php-fpm   Container running Nginx + PHP-FPM capable ...   207                  [OK]
phpmyadmin/phpmyadmin     A web interface for MySQL and MariaDB.          123                  [OK]
eboraas/apache-php        PHP5 on Apache (with SSL support), built o...   69                   [OK]
php-zendserver            Zend Server - the integrated PHP applicati...   69        [OK]       
million12/nginx-php       Nginx + PHP-FPM 5.5, 5.6, 7.0 (NG), CentOS...   67                   [OK]
webdevops/php-nginx       Nginx with PHP-FPM                              39                   [OK]
webdevops/php-apache      Apache with PHP-FPM (based on webdevops/php)    14                   [OK]
phpunit/phpunit           PHPUnit is a programmer-oriented testing f...   14                   [OK]
tetraweb/php              PHP 5.3, 5.4, 5.5, 5.6, 7.0 for CI and run...   12                   [OK]
webdevops/php             PHP (FPM and CLI) service container             10                   [OK]

这里我们拉取官方的镜像,标签为5.6-fpm

docker pull php:5.6-fpm

等待下载完成后,我们就可以在本地镜像列表里查到REPOSITORY为php,标签为5.6-fpm的镜像。

docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
php                 5.6-fpm             025041cd3aa5        6 days ago          456.3 MB

Nginx + PHP 部署 Nginx 部署可以查看:Docker 安装 Nginx,一些 Nginx 的配置参考这篇文章。

启动 PHP:

docker run --name  myphp-fpm -v ~/nginx/www:/www  -d php:5.6-fpm

命令说明:

创建 ~/nginx/conf/conf.d 目录:

mkdir ~/nginx/conf/conf.d 

在该目录下添加 ~/nginx/conf/conf.d/test-php.conf 文件,内容如下:

server {
    listen       80;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm index.php;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    location ~ \.php$ {
        fastcgi_pass   php:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  /www/$fastcgi_script_name;
        include        fastcgi_params;
    }
}

配置文件说明:

启动 nginx:

docker run --name php-nginx -p 8083:80 -d \
-v ~/nginx/www:/usr/share/nginx/html:ro \
-v ~/nginx/conf/conf.d:/etc/nginx/conf.d:ro \
--link myphp-fpm:php \
nginx

接下来我们在 ~/nginx/www 目录下创建 index.php,代码如下:

<?php
echo phpinfo();
?>

浏览器打开 http://127.0.0.1:8083/index.php,显示如下: