Docker
概念
Docker是一种运行于 Linux 和 Windows 上的软件,用于创建、管理和编排容器。
Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
Docker是CS架构,Docker的组成:
- Docker daemon: 运行在宿主机上,Docker守护进程,用户通过Docker client(Docker命令)与Docker daemon交互
- Docker client: Docker 命令行工具,是用户使用Docker的主要方式,Docker client与Docker daemon通信并将结果返回给用户,Docker client也可以通过socket或者RESTful api访问远程的Docker daemon
了解了Docker的组成,再来了解一下Docker的三个主要概念:
- Docker image:镜像是只读的,镜像中包含有需要运行的文件。镜像用来创建container,一个镜像可以运行多个container;镜像可以通过Dockerfile创建,也可以从Docker hub/registry上下载。
- Docker container:容器是Docker的运行组件,启动一个镜像就是一个容器,容器是一个隔离环境,多个容器之间不会相互影响,保证容器中的程序运行在一个相对安全的环境中。
- Docker hub/registry: 共享和管理Docker镜像,用户可以上传或者下载上面的镜像,官方地址为
https://registry.hub.docker.com/
,也可以搭建自己私有的Docker registry。注册服务器是存放仓库的地方,其上往往存放着多个仓库。每个仓库集中存放某一类镜像,往往包括多个镜像文件,通过不同的标签(tag)来进行区分。例如存放Ubuntu操作系统镜像的仓库,称为Ubuntu仓库,其中可能包括14.04、12.04等不同版本的镜像。
镜像就相当于打包好的版本,镜像启动之后运行在容器中,仓库就是装存储镜像的地方。
镜像(image)是动态的容器的静态表示(specification),包括容器所要运行的应用代码以及运行时的配置。Docker 镜像包括一个或者多个只读层( read-only layers ),因此,镜像一旦被创建就再也不能被修改了。一个运行着的Docker 容器是一个镜像的实例( instantiation )。从同一个镜像中运行的容器包含有相同的应用代码和运行时依赖。但是不像镜像是静态的,每个运行着的容器都有一个可写层( writable layer ,也成为容器层 container layer),它位于底下的若干只读层之上。运行时的所有变化,包括对数据和文件的写和更新,都会保存在这个层中。因此,从同一个镜像运行的多个容器包含了不同的容器层。
Docker 有两种方式来创建一个容器镜像:
- 创建一个容器,运行若干命令,再使用 docker commit 来生成一个新的镜像。不建议使用这种方案。
- 创建一个 Dockerfile 然后再使用 docker build 来创建一个镜像。大多人会使用 Dockerfile 来创建镜像。
安装
使用wget命令来运行一个 Shell 脚本,完成 Docker CE 的安装。
$ curl -fsSL https://get.docker.com/ | sh
# daocloud.io 国内镜像
$ curl -sSL https://get.daocloud.io/docker | sh
该安装包适用于 Ubuntu,Debian,Centos 等大部分主流 Linux 发行版。
权限
当以普通用户身份去使用docker images
时,如果出现以下错误:
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.26/images/json: dial unix /var/run/docker.sock: connect: permission denied
是因为权限问题:
$ sudo groupadd docker
$ sudo gpasswd -a ${USER} docker
$ sudo service docker restart
$ newgrp - docker
系统操作
查看Docker信息info
$ docker info
查看版本version
$ docker version
启动Dockerstart
service docker start
# 或者
systemctl start docker
修改配置daemon.json
配置docker镜像加速
$ vi /etc/docker/daemon.json
#添加后
{
"registry-mirrors": ["https://registry.docker-cn.com"],
"live-restore": true
}
登录服务器login
$ docker login [options] [server] # 登录到Docker registry服务器
$ docker login localhost:8080 # 登录自己主机的registry
image
镜像
搜索镜像search
搜索官方仓库镜像
参数 | 说明 |
NAME | 镜像名称 |
DESCRIPTION | 镜像说明 |
STARS | 点赞数量 |
OFFICIAL | 是否是官方的 |
AUTOMATED | 是否是自动构建的 |
$ docker search <image_name>
拉取镜像pull
Docker 主机安装之后,本地并没有镜像。
如果本地没有某一镜像,我们可以使用 docker pull 命令来载入某一镜像
镜像从远程镜像仓库服务的仓库中下载。默认情况下,镜像会从 Docker Hub 的仓库中拉取。
$ docker image pull <image_name>
$ docker pull <image_name> # 默认获取最新的
$ docker image pull alpine:latest # 从 Docker Hub 的 alpine 仓库中拉取标签为 latest 的镜像。
$ docker pull ubuntu:16.04
$ docker pull daocloud.io/library/ubuntu:16.04
# 下载镜像名称其实由三部分组成:daocloud.io/library/ubuntu:16.04
# 其中其中daocloud.io是注册服务器地址,默认是registry.hub.docker.com;ubuntu是仓库名,16.04是标签名,默认是latest。
查看镜像
Docker镜像保存在/var/lib/docker目录下中
- REPOSTITORY:表示镜像的仓库源
- TAG:镜像的标签
- IMAGE ID:镜像ID
- CREATED:镜像创建时间
- SIZE:镜像大小
$ docker image ls # 查看本地所有镜像
$ docker images # 查看docker镜像
$ docker image inspect <image-name> # 查看详细信息
导出镜像save
$ docker save <image-name> > /opt/<image-name>.tar.gz # 导出docker镜像至本地
导入镜像load
$ docker load < /opt/centos.tar.gz #导入本地镜像到docker镜像库
删除镜像rmi
$ docker image rmi <image-name>
$ docker rmi docker.io/tomcat:7.0.77-jre7
$ docker rmi b39c68b7af30
$ docker rm `docker ps -aq` # 一次性删除所有容器记录
$ docker rmi `docker images -aq` # 一次性删除所有本地的镜像记录
更新镜像update
更新镜像之前,我们需要使用镜像来创建一个容器。
$ docker run -t -i ubuntu:15.10 /bin/bash
在运行的容器内使用 apt-get update 命令进行更新。
在完成操作之后,输入 exit 命令来退出这个容器。
此时 ID 为 e218edb10161 的容器,是按我们的需求更改的容器。我们可以通过命令 docker commit 来提交容器副本。
$ docker commit -m="has update" -a="runoob" e218edb10161 runoob/ubuntu:v2
sha256:70bf1840fd7c0d2d8ef0a42a817eb29f854c1af8f7c59fc03ac7bdee9545aff8
各个参数说明:
- -m: 提交的描述信息
- -a: 指定镜像作者
- **e218edb10161:**容器 ID
- runoob/ubuntu:v2: 指定要创建的目标镜像名
构建镜像build
$ docker build
设置标签tag
docker tag 镜像ID,这里是 860c279d2fec ,用户名称、镜像源名(repository name)和新的标签名(tag)。
$ docker tag 860c279d2fec runoob/centos:dev
容器container
查看容器ps
$ docker ps # 正在运行的容器
$ docker ps -a # 所有容器
$ docker ps -l # 查看最近一次运行的容器
# CONTAINER ID: 容器 ID。
# IMAGE: 使用的镜像。
# COMMAND: 启动容器时运行的命令。
# CREATED: 容器的创建时间。
# STATUS: 容器状态。
# 状态有7种:
- created(已创建)
- restarting(重启中)
- running 或 Up(运行中)
- removing(迁移中)
- paused(暂停)
- exited(停止)
- dead(死亡)
# PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。
# NAMES: 自动分配的容器名称。
查看端口port
$ docker port <container_name>/<image_name>
查看进程top
$ docker top <image_name>
查看状态inspect
收集有关容器和镜像的底层信息
$ docker inspect <container_name>/<image_name> # 查看Docker的底层信息
# 返回一个 JSON 文件记录着 Docker 容器的配置和状态信息
$ docker inspect --format='{{.State.Status}}' <image_name>
查看日志logs
在宿主主机内使用 docker logs 命令,查看容器内的标准输出
$ docker logs <container_name>/<container_id>
$ docker logs -f <container_name>/<container_id> # 不间断打印容器的日志信息
# -f: 让 docker logs 像使用 tail -f 一样来输出容器内部的标准输出。
创建容器create
$ docker create --name web31 training/webapp python app.py #创建名字为 web31 的容器
运行容器run
$ docker run <image-name>
$ docker run -d -P centos /bin/sh -c "while true;do echo 正在运行; sleep 1;done" # 后台运行一个docker
# -d 后台运行容器 默认不会进入容器,想要进入容器需要使用指令 docker exec
# -P 将容器内部使用的网络端口随机映射到我们使用的主机上。
# -P 是容器内部端口随机映射到主机的高端口。
# -p 是容器内部端口绑定到指定的主机端口。
# Docker 开放了 5000 端口(默认 Python Flask 端口)映射到主机端口 32769 上。
# /bin/sh 指定使用centos的bash解释器
# -c 运行一段shell命令
# "while true;do echo 正在运行; sleep 1;done" 在linux后台,每秒中打印一次正在运行
$ docker run -d -p 5000:5000 training/webapp python app.py
# 容器内部的 5000 端口映射到我们本地主机的 5000 端口上
$ docker run -d -p 127.0.0.1:5001:5000 training/webapp python app.py
# 指定容器绑定的网络地址,比如绑定 127.0.0.1
$ docker run --name mydocker -it centos /bin/bash # 启动一个bash终端,允许用户进行交互
# --name 给容器定义一个名称 可以无
# -i 让容器的标准输入保持打开
# -t 让Docker分配一个伪终端,并绑定到容器的标准输入上
# /bin/bash 指定docker容器,用shell解释器交互
当利用docker run来创建容器时,Docker在后台运行的步骤如下:
# 1. 检查本地是否存在指定的镜像,不存在就从公有仓库下载
# 2. 利用镜像创建并启动一个容器
# 3. 分配一个文件系统,并在只读的镜像层外面挂在一层可读写层
# 4. 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
# 5. 从地址池配置一个ip地址给容器
# 6. 执行用户指定的应用程序
# 7. 执行完毕后容器被终止
启动容器start
$ docker start <container_name>/<container_id>
交互界面exec
$ docker exec -it <container_name>/<container_id> /bin/bash # 进入容器交互式界面
暂停容器pause
$ docker pause <container_name>/<container_id>
继续容器unpause
$ docker unpause <container_name>/<container_id>
重命名rename
$ docker rename <old_name> <new_mame>
变化记录diff
$ docker diff container
复制文件cp
$ docker cp container:path hostpath # 从容器内复制文件到指定的路径上
停止容器stop
$ docker stop <container_name>/<container_id>
$ docker stop `docker ps -aq` # 停止所有正在运行的容器
$ docker container kill <container_name>/<container_id>
重启容器restart
正在运行的容器,我们可以使用 docker restart 命令来重启。
$ docker restart <container_name>/<container_id>
进入后台容器attach
在使用 -d 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入
$ docker attach <container_name>/<container_id> # 如果从这个容器退出,会导致容器的停止。
$ docker exec -it 243c32535da7 /bin/bash # 如果从这个容器退出,容器不会停止,推荐使用。
导出容器export
导出容器 1e560fca3906 快照到本地文件 ubuntu.tar。
$ docker export 1e560fca3906 > ubuntu.tar
导入容器import
可以使用 docker import 从容器快照文件中再导入为镜像,以下实例将快照文件 ubuntu.tar 导入到镜像 test/ubuntu:v1:
$ cat docker/ubuntu.tar | docker import - test/ubuntu:v1
$ docker import http://example.com/exampleimage.tgz example/imagerepo # 通过指定 URL 或者某个目录来导入
退出容器exit
$ exit # ctrl+D也可以退出
删除容器rm
删除容器时,容器必须是停止状态,否则会报错误。
$ docker rm <container_name>/<container_id>
清理容器prune
以清理掉所有处于终止状态的容器。
$ docker container prune
新建网络network
下面先创建一个新的 Docker 网络。
$ docker network create -d bridge test-net
参数说明:
-d:参数指定 Docker 网络类型,有 bridge、overlay。
其中 overlay 网络类型用于 Swarm mode,可以忽略。
连接容器
运行一个容器并连接到新建的 test-net 网络:
$ docker run -itd --name test1 --network test-net ubuntu /bin/bash
打开新的终端,再运行一个容器并加入到 test-net 网络:
$ docker run -itd --name test2 --network test-net ubuntu /bin/bash
下面通过 ping 来证明 test1 容器和 test2 容器建立了互联关系。
如果 test1、test2 容器内中无 ping 命令,则在容器内执行以下命令安装 ping(即学即用:可以在一个容器里安装好,提交容器到镜像,在以新的镜像重新运行以上俩个容器)。
apt-get update
apt install iputils-ping
在 test1 容器输入以下命令:
$ ping test2
实例
root@devstack:/home/sammy# docker create --name web31 training/webapp python app.py #创建名字为 web31 的容器
7465f4cb7c49555af32929bd1bc4213f5e72643c0116450e495b71c7ec128502
root@devstack:/home/sammy# docker inspect --format='{{.State.Status}}' web31 #其状态为 created
created
root@devstack:/home/sammy# docker start web31 #启动容器
web31root@devstack:/home/sammy# docker exec -it web31 /bin/bash #在容器中运行 bash 命令
root@devstack:/home/sammy# docker inspect --format='{{.State.Status}}' web31 #其状态为 running
running
root@devstack:/home/sammy# docker pause web31 #暂停容器
web31
root@devstack:/home/sammy# docker inspect --format='{{.State.Status}}' web31
paused
root@devstack:/home/sammy# docker unpause web31 #继续容器
web31
root@devstack:/home/sammy# docker inspect --format='{{.State.Status}}' web31
running
root@devstack:/home/sammy# docker rename web31 newweb31 #重命名
root@devstack:/home/sammy# docker inspect --format='{{.State.Status}}' newweb31
running
root@devstack:/home/sammy# docker top newweb31 #在容器中运行 top 命令
UID PID PPID C STIME TTY TIME CMD
root 5009 4979 0 16:28 ? 00:00:00 python app.py
root@devstack:/home/sammy# docker logs newweb31 #获取容器的日志
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
root@devstack:/home/sammy# docker stop newweb31 #停止容器
newweb31
root@devstack:/home/sammy# docker inspect --format='{{.State.Status}}' newweb31
exited
root@devstack:/home/sammy# docker rm newweb31 #删除容器
newweb31
root@devstack:/home/sammy# docker inspect --format='{{.State.Status}}' newweb31
Error: No such image, container or task: newweb31
GitLab Runner
GitLab Runner包含一组命令,可以使用这些命令注册,管理和运行构建。
安装register
- 在目标主机上安装 GitLab Runner,目标主机是指要部署的服务器
- Ubuntu 安装脚本:
# 外网 因为访问了外网的网址去安装
$ curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-ci-multi-runner/script.deb.sh | sudo bash
$ sudo apt-get update
$ sudo apt-get install gitlab-ci-multi-runner
注册相关命令
注册 register
- gitlab-ci-multi-runner register:执行注册命令
- Please enter the gitlab-ci coordinator URL:输入 ci 地址
- Please enter the gitlab-ci token for this runner:输入 ci token
- Please enter the gitlab-ci description for this runner:输入 runner 名称
- Please enter the gitlab-ci tags for this runner:设置 tag
- Whether to run untagged builds:这里选择 true ,代码上传后会能够直接执行
- Whether to lock Runner to current project:直接回车,不用输入任何口令
- Please enter the executor:选择 runner 类型,这里我们选择的是 shell
其中ci的URL和token在gitlab工程中的设置->CI/CD->Runner->手动设置specific Runner中
$ gitlab-ci-multi-runner register
Running in system-mode.
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
https://yfgitlab.dahuatech.com/
Please enter the gitlab-ci token for this runner:
fsYy75DXECZx8hdUxzxM
Please enter the gitlab-ci description for this runner:
[dahua]: autotest
Please enter the gitlab-ci tags for this runner (comma separated):
test
Whether to run untagged builds [true/false]:
[false]: true
Whether to lock the Runner to current project [true/false]:
[true]: false
Registering runner... succeeded runner=fsYy75DX
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
这里的token是ci注册的令牌,注册成功之后每一个runner会有各自的令牌。虽然上面runner=fsYy75DX,此处的令牌还是注册令牌,但是list
、verify
还有配置文件中的token都是各自的token
列表list
此命令列出配置文件中保存的所有runner。
$ gitlab-runner list
Listing configured runners ConfigFile=/etc/gitlab-runner/config.toml
GitLab Runner配置使用TOML格式。
要编辑的文件可以在以下位置找到:
- /etc/gitlab-runner/config.toml当gitlab-runner作为超级用户(root)执行时,在* nix系统上
- ~/.gitlab-runner/config.toml 当gitlab-runner以非root身份执行时,在* nix系统上
- ./config.toml 在其他系统上
$ cat /etc/gitlab-runner/config.toml
concurrent = 1
check_interval = 0
[[runners]]
name = "test1"
url = "https://yfgitlab.dahuatech.com/"
token = "1HqQCT3CkSM_4HGxqaJ3"
tls-ca-file = "/home/dahua/桌面/server.cer"
executor = "shell"
[runners.cache]
当有一个不小心在gitlab上删除了,但是仍会显示在gitlab-runner的列表里,如果想删除,可以直接进入配置文件把相关的配置删除。
大多数命令接受参数来指定自定义配置文件,允许您在一台计算机上具有多个不同的配置。要指定自定义配置文件,请使用-c or --config标志,或使用CONFIG_FILE环境变量。
验证verify
此命令检查已注册的runner是否可以连接到GitLab,但它不验证GitLab Runner服务是否正在使用runner。示例输出是:
Verifying runner... is alive runner=1HqQCT3C
注意此处令牌token是各自的runner独有的令牌
要删除旧的并从GitLab Runner删除,请执行以下命令。
警告: 此操作无法撤消,它将更新配置文件,因此请确保config.toml在执行之前备份。
gitlab-runner verify --delete
注销unregister
此命令允许注销其中一个注册的runner。它需要一个完整的URL和Runner的令牌,或者Runner的名字。使用该 --all-runners选项,它将取消注册所有附加的Runner。
要取消注册特定的Runner,首先通过执行gitlab-runner list
以下步骤获取Runner的详细信息 :
$ gitlab-runner list
Listing configured runners ConfigFile=/etc/gitlab-runner/config.toml
test1 Executor=shell Token=BtxBtfxGXfnwj93769Ef URL=https://yfgitlab.dahuatech.com/
test1 Executor=shell Token=1HqQCT3CkSM_4HGxqaJ3 URL=https://yfgitlab.dahuatech.com/
test2 Executor=shell Token=2PDsoTpkqQxMSRxtnw6j URL=https://yfgitlab.dahuatech.com/
然后使用以下命令之一使用此信息取消注册。
警告: 此操作无法撤消,它将更新配置文件,因此请确保config.toml在执行之前备份。
$ gitlab-runner unregister --url https://yfgitlab.dahuatech.com/ --token BtxB # 通过URL和令牌
$ gitlab-runner unregister --name test-runner # 按名字:如果有多个具有给定名称的runner,则只会删除第一个
$ gitlab-runner unregister --all-runners # all
服务相关命令
安装install
卸载uninstall
开始start
停止stop
重启restart
状态status
运行相关命令
此命令允许从GitLab获取和处理构建
运行run
run-single
这是一个补充命令,可用于从单个GitLab实例仅运行单个构建。它不使用任何配置文件,并且需要将所有选项作为参数或环境变量传递。还需要指定GitLab URL和Runner令牌。
exec
此命令允许本地运行构建,尝试尽可能多地复制CI环境。它不需要连接到GitLab,而是读取本地.gitlab-ci.yml
并创建一个新的构建环境,其中执行所有构建步骤。
问题
注册失败
可能是没有证书
如果遇到下面注册失败的问题:
ERROR: Registering runner... failed runner=fsYy75DX status=couldn't execute POST against https://gitlab.example.com/api/v4/runners: Post https://gitlab.example.com/api/v4/runners: x509: certificate signed by unknown authority
PANIC: Failed to register this runner. Perhaps you are having network problems
其实是证书的原因,首先打开gitlab的地址,地址栏的左边有个小锁,点击之后的下拉菜单里有证书选项,点击之后选择详细信息然后复制到文件,下一步之后出现选格式的,选择Bash64编码。
导出之后放到Linux环境中,供gitlab runner配置调用(这里放在了/home/dahua/桌面/server.cer,server.cer就是导出的证书)
下面是证书的配置(全部复制前8行,直接粘贴到Bash界面):
$ gitlab-runner register -n \
--tls-ca-file=/home/dahua/桌面/server.cer \
--url=https://yfgitlab.dahuatech.com/ \
--registration-token=fsYy75DXECZx8hdUxzxM \
--name=test1 \
--run-untagged=true \
--locked=true \
--executor=shell
Running in system-mode.
Registering runner... succeeded runner=fsYy75DX
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!
灰色感叹号
在服务器注册完 runner 后,在 gitlab 的 runner 处出现灰色感叹号,并提示 new runner has not connected yet
。这个问题一般都是权限问题造成的,加 sudo 和不加 sudo 所创建的配置文件不同。在使用 gitlab-runner register
注册的时候,加上 sudo,在启动 runner 时也要加上 sudo , sudo gitlab-runner restart
或sudo gitlab-runner start
。
无法删除 runner
在使用 gitlab-runner unregister 删除 runner 时,提示 Error: unregistering runner from gitlab forbidden
。
使用 gitlab-runner verify
检测 runner 是否已连接至 gitlab ,然后gitlab-runner verify --delete
在注册列表中删除它们。有可能是在gitlab上手动删掉了
list 不显示注册的
使用 sudo 权限注册的 runner 要使用sudo gitlab-runner list
查看。
构建时提示
mkdir: cannot create directop ‘/home/gitlab-runner/builds/xxxxx’: Permission denied
用户 gitlab-runner 没有构建目录的权限,加上权限即可,或切换至 root 用户,给该文件夹上权限。
环境变量
如在构建的时候,提示bash: npm: command not found
,此时可以去服务器上给bash
加上环境变量,或者在before_script
阶段加上环境变量,例如:
before_script:
- export PATH=/home/souche/n/bin:$PATH
yaml文件
下一个 Stages 无法使用上一个 Stages 生成的文件
这是因为在进行一个新的 Stages 时,会把上一个 Stages 所产生的文件删除掉,解决方案就是使用缓存 cache,如:
cache:
key: '$CI_COMMIT_REF_NAME'
paths:
- .nuxt
磁盘满了
构建时在 Getting source from Git repository
阶段出现错误提示 error: failed to write new configuration file /home/gitlab-runner/builds/xxxx/0/xxxx/xxxx.tmp/git-template/config.lock
出现这个提示,请检查服务器磁盘是不是已经满了,请释放些磁盘空间
git 版本低
构建过程出现 fatal: git fetch-pack: expected shallow list fatal: The remote end hung up unexpectedly
这是因为服务器 git 版本太低,不支持新的 api,去服务器升级一下 git 版本