落 Docker 归 Linux

之前玩 Docker 都是在 macOS 里面,为向生产应用更靠近一步,必须要在 Linux 中玩才行。

说明:本文的 Linux 均指 64位 CentOS 7 最小化版(不带桌面)。

安装配置

安装最新版本 Docker

CentOS 最大的特点就是默认使用 yum 安装软件版本都比较低,除了从源码编译安装之外,一般也可以通过添加专属的源来安装最新版本,docker 也一样,这里我们通过官方推荐的方式先添加一个 Docker 的一个 repo。

1
2
3
4
5
6
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo

sudo yum-config-manager --enable|disable docker-ce-edge|test
sudo yum makecache fast

旧的 YUM 源中 docker 的软件包名叫 docker/docker-engine,引入如果之前安装过它们,需要清理镜像容器后卸载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 清理镜像/容器
sudo docker stop `sudo docker ps -aq`
sudo docker rm `sudo docker ps -aq`
sudo docker rmi -f `sudo docker images -aq`

# 卸载旧版本
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

# 安装
sudo yum install docker-ce

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

如果 YUM 删除不干净,可以使用 rpm 手动在检查一遍:

1
2
rpm -qa | grep docker
rpm -e docker # yum remove -y docker

说明

  • 必须启用 centos-extra 仓库,CentOS 7 默认也已经启用了。更多 CentOS YUM 仓库见: https://wiki.centos.org/AdditionalResources/Repositories
  • 国内添加 docker 官方源可能下载不了,可以参考我其他关于 CentOS 文章中对于 SS 客户端代理的配置,然后使用下代理。
  • 如果要安装旧版 Docker 可以移除 docker-ce.repo,然后使用 YUM 重新安装

当然,除了使用 YUM,也可以直接下载 Docker 的 rpm 包直接安装,不过需要手动一个个安装依赖,不推荐:

1
2
wegt https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-18.06.1.ce-3.el7.x86_64.rpm
sudo yum install /path/to/package.rpm

最后,可以使用下列命令检查更新、卸载:

1
2
3
4
5
6
7
8
# 检查更新
yum list docker-ce --showduplicates | sort -r
yum -y upgrade docker-ce
yum -y install docker-ce-<VERSION>

# 卸载
sudo yum remove docker-ce
sudo rm -rf /var/lib/docker

配置镜像地址

  • 通过修改 /etc/docker/daemon.json
1
2
3
4
5
{
"registry-mirrors": [
"https://xxxxx.mirror.aliyuncs.com"
]
}
  • 通过修改 docker 启动参数

编辑 /etc/systemd/system/multi-user.target.wants/docker.service 服务文件,找到 ExecStart=,在启动参数后面加一条 --registry-mirror=https://xxxxx.mirror.aliyuncs.com

以上两种方式修改后均需要执行以下命令:

1
2
3
sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart|start docker

然后登录 Docker

1
sudo docker login xxxxx.cn-hangzhou.aliyuncs.com

docker-compose

1
2
3
4
5
6
sudo yum install -y docker-compose
sudo pip install docker-compose

# See: https://github.com/docker/compose/releases
curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

如果使用 pip 安装时如果遇到类似 “Cannot uninstall ‘requests’. It is a distutils installed…” 等失败信息,可以尝试降级 pip 后再安装 docker-compose:

1
sudo pip install --upgrade --force-reinstall pip==9.0.3

容器编排

在现实的生产环境中 Docker 本身是一个相对底层的容器引擎,在有很多服务器的集群中,不太可能直接通过 docker 命令来管理任务和资源。

所以我们需要其他的机制来进行 Docker 容器的编排和调度。

docker-compose

一个需要由多个容器组成的运行环境更适合使用 docker-compose,而不是通过 shell 脚本来启动容器,因为可以通过 docker-compose 来统一启动、停止和重启应用容器及其服务容器、依赖容器等。

使用 docker-compose 管理一个真实的运行环境按顺序一般分为以下几个步骤:

定义应用层

项目目录、应用代码、组建依赖等,即一个代码库。

需要注意的是,代码中用到的服务,比如数据库等连接地址,一定要是后面构建完镜像、启动好应用容器后能够在这个应用容器中实际的网络情况能够连同的地址。

定义 Dockerfile

如果容器是自定义的,那么我们需要自己编写 Dockerfile,docker-compose 会在运行的时候从这些 Dockerfiles 中生成镜像(需要指定 build 参数)。

当然如果是直接使用现有的 Docker 镜像,则这部可以跳过,只要在 docker-compose.yml 中使用 image 参数指定即可。

比如:

1
2
3
4
5
FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]

编写 docker-compose.yml

说明:文件名后缀也可以为 yaml,效果一样。

一个由 Docker 容器组成的集群在由 docker-compose 来管理的时候,只需要在一个 docker-compose 的配置文件中定义好所有容器关系、网络、数据卷等关系即可,其中所有的容器通过 services 来定义,。

一个基本的 docker-compose.yml 文件语法结构类似如下:

1
2
3
4
5
6
7
8
version: '3'    => 第一层
services:
web: => 第二层
build: .
ports:
- "5000:5000" => 第三层
redis:
image: "redis:alpine"

配置选项一般有三层,通过换行缩进体现层级关系。其中第三层的值可以为列表,用 - 开头,一行一项。键值对直接用 : 隔开。

其中,对经常使用的配置选项说明如下,完整的选项届时需要看官方文档。

说明:有些选项既可以在第二层,也可以在第一层,比如 volumes。docker-compose.yaml 的语法和 docker 命令很多都是一一对应的。

version

每份 docker-compose.yml 文件的语法都会因版本号不同而不同,甚至不兼容,详细可以查看官方的对比

版本声明必须放在 YAML 文件的开头。值的选择原则上尽量使用最新的,如果不指定 version 关键字,那么就是 1。版本号既可以是小数也可以是整数。

services

每个容器就是一个 service,比如上面的 webredis 都是 service 名。

在 service 名称下面常用的二级选项如下:

  • build

表示从这个选项指定的路径(这里是当前路径 .)下的 Dockerfiles 构建镜像。

说明:如果没有 image 指定名称,那么构建的镜像名是随机的。

  • image

表示从已有的、名为 image 指定的 Docker 镜像启动服务,如果本地不存在,则从配置的 Registry 地址中尝试拉取该镜像。

说明:在 version 1 里不允许同时使用 image 和 build,version 2 以后则可以,如果同时指定了两者,会将 build 出来的镜像打上名为 image 指定的标签。

  • volumes

挂载主机目录或者命名过的卷。如果是要挂载命名卷,那么 volumes 关键字必须是配置选项的第一层。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
volumes:
# Just specify a path and let the Engine create a volume
- /var/lib/mysql

# Specify an absolute path mapping
- /opt/data:/var/lib/mysql

# Path on the host, relative to the Compose file
- ./cache:/tmp/cache

# User-relative path
- ~/configs:/etc/configs/:ro

# Named volume
- datavolume:/var/lib/mysql
  • volumes_from

挂载数据卷容器,挂载是容器。同 docker run --volumes-from

1
2
3
4
5
volumes_from:
- service_name
- service_name:ro
- container:container_name
- container:container_name:rw
  • restart

是否需要重启容器,默认 no,表示任何情况下都不要重启容器。为 always 表示容器总会重启,为 on-failure 表示如果容器返回状态码出错则重启容器。

1
2
3
- restart: no
- restart: always
- restart: on-failure
  • environment

添加环境变量,键值对可自定义。同 docker run -e。比如:

1
2
3
4
5
6
7
environment:
RACK_ENV: development
SESSION_SECRET:

environment:
- RACK_ENV=development
- SESSION_SECRET
  • depends_on

用于指定服务依赖,一般是 MySQL、Redis 等。 指定了依赖,将会优先于服务创建并启动依赖。

  • links

links 也可以指定依赖——用于链接另一容器服务,如需要使用到另一容器的 MySQL 服务。可以给出服务名和别名,也可以仅给出服务名,这样别名将和服务名相同。

1
2
3
4
links:
- db
- db:mysql
- redis
  • external_links

链接搭配 docker-compose.yml 文件或者 Compose 之外定义的服务,通常是提供共享或公共服务。格式与 links 相似:

1
2
3
4
external_links:
- redis_1
- project_db_1:mysql
- project_db_1:postgresql
  • extra_hosts

添加主机名映射,等价于往 /etc/hosts 文件中添加记录。

1
2
3
extra_hosts:
- "dev.app.local:192.168.56.42"
- "mysql.server.local:192.168.56.42"
  • ports

暴露容器端口给主机,等价于 docker run -p: 左边是主机端口,右边是容器端口。

1
2
3
4
5
ports:
- "3000"
- "8000:8000"
- "49100:22"
- "127.0.0.1:8001:8001"
  • expose

暴露容器端口给容器,不会暴露给主机。同 docker run --expose

1
2
3
expose:
- "3000"
- "8000"
  • extends

继承自当前 yml 文件或者其它文件中定义的服务,可以选择性的覆盖原有配置。

1
2
3
extends:
file: common.yml
service: webapp

其中,service 必须有,file 可选。service 是需要继承的服务,例如 web、db。

  • net

设置网络模式。同 Docker 的 --net参数。

1
2
3
4
net: "bridge"
net: "none"
net: "container:[name or id]"
net: "host"
  • dns

自定义dns服务器。

1
2
3
4
dns: 8.8.8.8
dns:
- 8.8.8.8
- 9.9.9.9
volumes

管理服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 启动服务
sudo docker-compose up -d # -d 表示后台运行

# 停止后台运行的服务
sudo docker-compose stop

# 运行一次性命令
sudo docker-compose run {service} {command}
#docker-compose help run

# 停止所有服务
sudo docker-compose down --volumes # --volumes 表示同时移除挂载在主机上的数据卷

# 查看当前正在运行的服务
sudo docker-compose ps

Kubernetes/K8S

TODO:实现一个简单的负载均衡集群(一个 nginx 服务 + 多个 php 服务*)。

macOS 上体验本地 K*8S 集群环境 - minikube

这里的 minikube 虽然是安装在 macOS 上的但是,docker 实际还是运行在 virtualbox 虚拟机中的 (Linux),因此也没算偏题。

我们需要安装三样东西:Kubernetes 的命令行客户端 kubctl、一个可以在本地跑起来的 Kubernetes 环境 Minikube、以及给 Minikube 使用的虚拟化引擎 xhyve。

1
2
3
4
5
6
7
8
9
#brew install kubectl docker-machine-driver-hyperkit    # 如果 Homebrew 可以直接安装 hyperkit 或者你愿意手动编译安装, 则优先使用 hpyerkit
brew install kubectl
brew install docker-machine-driver-xhyve
brew cask install minikube

sudo chown root:wheel /usr/local/opt/docker-machine-driver-xhyve/bin/docker-machine-driver-xhyve
sudo chmod u+s /usr/local/opt/docker-machine-driver-xhyve/bin/docker-machine-driver-xhyve

minikube start --vm-driver xhyve

Minikube 启动时会自动配置 kubectl,把它指向 Minikube 提供的 Kubernetes API 服务。可用 kubectl config current-context 测试。

作为一个开发和测试的环境,Minikube 会建立一个有一个 node 的集群,可用 kubectl get nodes 查看。

FAQ

docker cannot connect to internet

修改 /etc/docker/daemon.json:

1
2
3
{
"dns": ["1.1.1.1", "8.8.8.8"]
}

最后执行 sudo systemctl restart docker 即可。

See: https://stackoverflow.com/questions/20430371/my-docker-container-has-no-internet

sudo docker-compose command not found

1
2
3
4
5
6
7
8
# 1. 使用绝对路径
sudo `which docker-compose`

# 2. 修改 sudoers 配置环境变量(sudo visudo)
Defaults secure_path=/path1:/path2:/path3

# 3. 使用 sudo -i
sudo -i nginx

See: https://unix.stackexchange.com/questions/91384/how-is-sudo-set-to-not-change-home-in-ubuntu-and-how-to-disable-this-behavior

docker change container port

  • 停止相应容器(必须先停止,否则重启 docker 后又会恢复)
  • 修改 hostconfig.json 文件里面的 HostPort

修改容器的 /var/lib/docker/containers/[hash_of_container]/hostconfig.json 文件,找到 PortBindings:

1
2
3
4
5
6
7
8
"PortBindings": {
"80/tcp": [
{
"HostIp": "",
"HostPort": "8080"
}
]
}
  • 重启 docker: sudo systemctl restart docker
  • 重启容器: sudo docker restart {hash_of_container}

See: https://stackoverflow.com/questions/19335444/how-do-i-assign-a-port-mapping-to-an-existing-docker-container

macOS 启动 minikube 时 timeout?

检查 VirtualBox 是否创建并启用了一个虚拟网卡。如果没有配置过主机网络管理器,则启动到一半时会一直卡住。

如果已经启用,则可以尝试运行:sudo ifconfig vboxnet0 up

https://github.com/kubernetes/minikube/issues/1224

参考