标签 容器 下的文章

本文的主角是容器,一种类似虚拟机但更轻量级的构造。你可以轻易地在你的 Ubuntu 桌面系统中创建一堆容器!

虚拟机会虚拟出整个电脑让你来安装客户机操作系统。相比之下,容器复用了主机的 Linux 内核,只是简单地 包容 了我们选择的根文件系统(也就是运行时环境)。Linux 内核有很多功能可以将运行的 Linux 容器与我们的主机分割开(也就是我们的 Ubuntu 桌面)。

Linux 本身需要一些手工操作来直接管理他们。好在,有 LXD(读音为 Lex-deeh),这是一款为我们管理 Linux 容器的服务。

我们将会看到如何:

  1. 在我们的 Ubuntu 桌面上配置容器,
  2. 创建容器,
  3. 安装一台 web 服务器,
  4. 测试一下这台 web 服务器,以及
  5. 清理所有的东西。

设置 Ubuntu 容器

如果你安装的是 Ubuntu 16.04,那么你什么都不用做。只要安装下面所列出的一些额外的包就行了。若你安装的是 Ubuntu 14.04.x 或 Ubuntu 15.10,那么按照 LXD 2.0 系列(二):安装与配置 来进行一些操作,然后再回来。

确保已经更新了包列表:

sudo apt update
sudo apt upgrade

安装 lxd 包:

sudo apt install lxd

若你安装的是 Ubuntu 16.04,那么还可以让你的容器文件以 ZFS 文件系统的格式进行存储。Ubuntu 16.04 的 Linux kernel 包含了支持 ZFS 必要的内核模块。若要让 LXD 使用 ZFS 进行存储,我们只需要安装 ZFS 工具包。没有 ZFS,容器会在主机文件系统中以单独的文件形式进行存储。通过 ZFS,我们就有了写入时拷贝等功能,可以让任务完成更快一些。

安装 zfsutils-linux 包(若你安装的是 Ubuntu 16.04.x):

sudo apt install zfsutils-linux

安装好 LXD 后,包安装脚本应该会将你加入 lxd 组。该组成员可以使你无需通过 sudo 就能直接使用 LXD 管理容器。根据 Linux 的习惯,你需要先登出桌面会话然后再登录 才能应用 lxd 的组成员关系。(若你是高手,也可以通过在当前 shell 中执行 newgrp lxd 命令,就不用重登录了)。

在开始使用前,LXD 需要初始化存储和网络参数。

运行下面命令:

$ sudo lxd init
Name of the storage backend to use (dir or zfs): zfs
Create a new ZFS pool (yes/no)? yes
Name of the new ZFS pool: lxd-pool
Would you like to use an existing block device (yes/no)? no
Size in GB of the new loop device (1GB minimum): 30
Would you like LXD to be available over the network (yes/no)? no
Do you want to configure the LXD bridge (yes/no)? yes 
> You will be asked about the network bridge configuration. Accept all defaults and continue.
Warning: Stopping lxd.service, but it can still be activated by:
 lxd.socket
 LXD has been successfully configured.
$ _

我们在一个(单独)的文件而不是块设备(即分区)中构建了一个文件系统来作为 ZFS 池,因此我们无需进行额外的分区操作。在本例中我指定了 30GB 大小,这个空间取之于根(/) 文件系统中。这个文件就是 /var/lib/lxd/zfs.img

行了!最初的配置完成了。若有问题,或者想了解其他信息,请阅读 https://www.stgraber.org/2016/03/15/lxd-2-0-installing-and-configuring-lxd-212/

创建第一个容器

所有 LXD 的管理操作都可以通过 lxc 命令来进行。我们通过给 lxc 不同参数来管理容器。

lxc list

可以列出所有已经安装的容器。很明显,这个列表现在是空的,但这表示我们的安装是没问题的。

lxc image list

列出可以用来启动容器的(已经缓存的)镜像列表。很明显这个列表也是空的,但这也说明我们的安装是没问题的。

lxc image list ubuntu:

列出可以下载并启动容器的远程镜像。而且指定了显示 Ubuntu 镜像。

lxc image list images:

列出可以用来启动容器的(已经缓存的)各种发行版的镜像列表。这会列出各种发行版的镜像比如 Alpine、Debian、Gentoo、Opensuse 以及 Fedora。

让我们启动一个 Ubuntu 16.04 容器,并称之为 c1

$ lxc launch ubuntu:x c1
Creating c1
Starting c1
$ 

我们使用 launch 动作,然后选择镜像 ubuntu:xx 表示 Xenial/16.04 镜像),最后我们使用名字 c1 作为容器的名称。

让我们来看看安装好的首个容器,

$ lxc list

+---------|---------|----------------------|------|------------|-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+---------|---------|----------------------|------|------------|-----------+
| c1 | RUNNING | 10.173.82.158 (eth0) | | PERSISTENT | 0 |
+---------|---------|----------------------|------|------------|-----------+

我们的首个容器 c1 已经运行起来了,它还有自己的 IP 地址(可以本地访问)。我们可以开始用它了!

安装 web 服务器

我们可以在容器中运行命令。运行命令的动作为 exec

$ lxc exec c1 -- uptime
 11:47:25 up 2 min,0 users,load average:0.07,0.05,0.04
$ _

exec 后面,我们指定容器、最后输入要在容器中运行的命令。该容器的运行时间只有 2 分钟,这是个新出炉的容器:-)。

命令行中的 -- 跟我们 shell 的参数处理过程有关。若我们的命令没有任何参数,则完全可以省略 -

$ lxc exec c1 -- df -h

这是一个必须要 - 的例子,由于我们的命令使用了参数 -h。若省略了 -,会报错。

然后我们运行容器中的 shell 来更新包列表。

$ lxc exec c1 bash
root@c1:~# apt update
Ign http://archive.ubuntu.com trusty InRelease
Get:1 http://archive.ubuntu.com trusty-updates InRelease [65.9 kB]
Get:2 http://security.ubuntu.com trusty-security InRelease [65.9 kB]
...
Hit http://archive.ubuntu.com trusty/universe Translation-en 
Fetched 11.2 MB in 9s (1228 kB/s) 
Reading package lists... Done
root@c1:~# apt upgrade
Reading package lists... Done
Building dependency tree 
...
Processing triggers for man-db (2.6.7.1-1ubuntu1) ...
Setting up dpkg (1.17.5ubuntu5.7) ...
root@c1:~# _

我们使用 nginx 来做 web 服务器。nginx 在某些方面要比 Apache web 服务器更酷一些。

root@c1:~# apt install nginx
Reading package lists... Done
Building dependency tree
...
Setting up nginx-core (1.4.6-1ubuntu3.5) ...
Setting up nginx (1.4.6-1ubuntu3.5) ...
Processing triggers for libc-bin (2.19-0ubuntu6.9) ...
root@c1:~# _

让我们用浏览器访问一下这个 web 服务器。记住 IP 地址为 10.173.82.158,因此你需要在浏览器中输入这个 IP。

lxd-nginx

让我们对页面文字做一些小改动。回到容器中,进入默认 HTML 页面的目录中。

root@c1:~# cd /var/www/html/
root@c1:/var/www/html# ls -l
total 2
-rw-r--r-- 1 root root 612 Jun 25 12:15 index.nginx-debian.html
root@c1:/var/www/html#

使用 nano 编辑文件,然后保存:

lxd-nginx-nano

之后,再刷一下页面看看,

lxd-nginx-modified

清理

让我们清理一下这个容器,也就是删掉它。当需要的时候我们可以很方便地创建一个新容器出来。

$ lxc list
+---------+---------+----------------------+------+------------+-----------+
| NAME | STATE   | IPV4                 | IPV6 | TYPE       | SNAPSHOTS    |
+---------+---------+----------------------+------+------------+-----------+
| c1   | RUNNING | 10.173.82.169 (eth0) |      | PERSISTENT | 0            |
+---------+---------+----------------------+------+------------+-----------+
$ lxc stop c1
$ lxc delete c1
$ lxc list
+---------+---------+----------------------+------+------------+-----------+
| NAME | STATE   | IPV4                 | IPV6 | TYPE       | SNAPSHOTS    |
+---------+---------+----------------------+------+------------+-----------+
+---------+---------+----------------------+------+------------+-----------+

我们停止(关闭)这个容器,然后删掉它了。

本文至此就结束了。关于容器有很多玩法。而这只是配置 Ubuntu 并尝试使用容器的第一步而已。


via: https://blog.simos.info/trying-out-lxd-containers-on-our-ubuntu/

作者:Simos Xenitellis 译者:lujun9972 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

为什么选择 Docker 和 Kubernetes 呢?

容器允许我们构建、发布和运行分布式应用。它们使应用程序摆脱了机器限制,可以让我们以一定的方式创建一个复杂的应用程序。

使用容器编写应用程序可以使开发、QA 更加接近生产环境(如果你努力这样做的话)。通过这样做,可以更快地发布修改,并且可以更快地测试整个系统。

Docker 这个容器式平台就是为此为生,可以使软件独立于云提供商。

但是,即使使用容器,移植应用程序到任何一个云提供商(或私有云)所需的工作量也是不可忽视的。应用程序通常需要自动伸缩组、持久远程磁盘、自动发现等。但是每个云提供商都有不同的机制。如果你想使用这些功能,很快你就会变的依赖于云提供商。

这正是 Kubernetes 登场的时候。它是一个容器 编排 orchestration 系统,它允许您以一定的标准管理、缩放和部署应用程序的不同部分,并且成为其中的重要工具。它的可移植抽象层兼容主要云的提供商(Google Cloud,Amazon Web Services 和 Microsoft Azure 都支持 Kubernetes)。

可以这样想象一下应用程序、容器和 Kubernetes。应用程序可以视为一条身边的鲨鱼,它存在于海洋中(在这个例子中,海洋就是您的机器)。海洋中可能还有其他一些宝贵的东西,但是你不希望你的鲨鱼与小丑鱼有什么关系。所以需要把你的鲨鱼(你的应用程序)移动到一个密封的水族馆中(容器)。这很不错,但不是特别的健壮。你的水族馆可能会被打破,或者你想建立一个通道连接到其他鱼类生活的另一个水族馆。也许你想要许多这样的水族馆,以防需要清洁或维护……这正是应用 Kubernetes 集群的作用。

进化到 Kubernetes

主流云提供商对 Kubernetes 提供了支持,从开发环境到生产环境,它使您和您的团队能够更容易地拥有几乎相同的环境。这是因为 Kubernetes 不依赖专有软件、服务或基础设施。

事实上,您可以在您的机器中使用与生产环境相同的部件启动应用程序,从而缩小了开发和生产环境之间的差距。这使得开发人员更了解应用程序是如何构建在一起的,尽管他们可能只负责应用程序的一部分。这也使得在开发流程中的应用程序更容易的快速完成测试。

如何使用 Kubernetes 工作?

随着更多的人采用 Kubernetes,新的问题出现了;应该如何针对基于集群环境进行开发?假设有 3 个环境,开发、质量保证和生产, 他们如何适应 Kubernetes?这些环境之间仍然存在着差异,无论是在开发周期(例如:在运行中的应用程序中我的代码的变化上花费时间)还是与数据相关的(例如:我不应该在我的质量保证环境中测试生产数据,因为它里面有敏感信息)。

那么,我是否应该总是在 Kubernetes 集群中编码、构建映像、重新部署服务,在我编写代码时重新创建部署和服务?或者,我是否不应该尽力让我的开发环境也成为一个 Kubernetes 集群(或一组集群)呢?还是,我应该以混合方式工作?

用本地集群进行开发

如果继续我们之前的比喻,上图两边的洞表示当使其保持在一个开发集群中的同时修改应用程序的一种方式。这通常通过来实现

Kubernetes 系列

本 Kubernetes 系列资源是开源的,可以在这里找到: https://github.com/red-gate/ks

我们写这个系列作为以不同的方式构建软件的练习。我们试图约束自己在所有环境中都使用 Kubernetes,以便我们可以探索这些技术对数据和数据库的开发和管理造成影响。

这个系列从使用 Kubernetes 创建基本的 React 应用程序开始,并逐渐演变为能够覆盖我们更多开发需求的系列。最后,我们将覆盖所有应用程序的开发需求,并且理解在数据库生命周期中如何最好地迎合容器和集群。

以下是这个系列的前 5 部分:

  1. ks1:使用 Kubernetes 构建一个 React 应用程序
  2. ks2:使用 minikube 检测 React 代码的更改
  3. ks3:添加一个提供 API 的 Python Web 服务器
  4. ks4:使 minikube 检测 Python 代码的更改
  5. ks5:创建一个测试环境

本系列的第二部分将添加一个数据库,并尝试找出最好的方式来开发我们的应用程序。

通过在各种环境中运行 Kubernetes,我们被迫在解决新问题的同时也尽量保持开发周期。我们不断尝试 Kubernetes,并越来越习惯它。通过这样做,开发团队都可以对生产环境负责,这并不困难,因为所有环境(从开发到生产)都以相同的方式进行管理。

下一步是什么?

我们将通过整合数据库和练习来继续这个系列,以找到使用 Kubernetes 获得数据库生命周期的最佳体验方法。

这个 Kubernetes 系列是由 Redgate 研发部门 Foundry 提供。我们正在努力使数据和容器的管理变得更加容易,所以如果您正在处理数据和容器,我们希望听到您的意见,请直接联系我们的开发团队。 [email protected]


我们正在招聘。您是否有兴趣开发产品、创建未来技术 并采取类似创业的方法(没有风险)?看看我们的软件工程师 - 未来技术的角色吧,并阅读更多关于在 英国剑桥的 Redgate 工作的信息。


via: https://medium.com/ingeniouslysimple/adopting-kubernetes-step-by-step-f93093c13dfe

作者:santiago arias 译者:aiwhj 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

Q:我正在使用基于 LXD(“Linux 容器”)的虚拟机。如何在 Linux 系统中启动时自动启动 LXD 容器?

当 LXD 在启动时运行,你就可以随时启动容器。你需要将 boot.autostart 设置为 true。你可以使用 boot.autostart.priority(默认值为 0)选项来定义启动容器的顺序(从最高开始)。你也可以使用 boot.autostart.delay(默认值 0)选项定义在启动一个容器后等待几秒后启动另一个容器。

语法

上面讨论的关键字可以使用 lxc 工具用下面的语法来设置:

$ lxc config set {vm-name} {key} {value}
$ lxc config set {vm-name} boot.autostart {true|false}
$ lxc config set {vm-name} boot.autostart.priority integer
$ lxc config set {vm-name} boot.autostart.delay integer

如何在 Ubuntu Linux 16.10 中让 LXD 容器在启动时启动?

输入以下命令:

$ lxc config set {vm-name} boot.autostart true

设置一个 LXD 容器名称 “nginx-vm” 以在启动时启动

$ lxc config set nginx-vm boot.autostart true

你可以使用以下语法验证设置:

$ lxc config get {vm-name} boot.autostart
$ lxc config get nginx-vm boot.autostart

示例输出:

true

你可以使用下面的语法在启动容器后等待 10 秒钟后启动另一个容器:

$ lxc config set nginx-vm boot.autostart.delay 10

最后,通过设置最高值来定义启动容器的顺序。确保 dbvm 容器首先启动,然后再启动 nginxvm。

$ lxc config set db_vm boot.autostart.priority 100
$ lxc config set nginx_vm boot.autostart.priority 99

使用下面的 bash 循环在 Linux 上查看所有配置值:

#!/bin/bash
echo 'The current values of each vm boot parameters:'
for c in db_vm nginx_vm memcache_vm
do
    echo "*** VM: $c ***"
    for v in boot.autostart boot.autostart.priority boot.autostart.delay
    do
        echo "Key: $v => $(lxc config get $c $v) "
    done
    echo ""
done

示例输出:

Fig.01: Get autostarting LXD containers values using a bash shell script


via: https://www.cyberciti.biz/faq/how-to-auto-start-lxd-containers-at-boot-time-in-linux/

作者:Vivek Gite 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

在 2017 年红帽峰会上,有几个人问我“我们通常用完整的虚拟机来隔离如 DNS 和 DHCP 等网络服务,那我们可以用容器来取而代之吗?”答案是可以的,下面是在当前红帽企业版 Linux 7 系统上创建一个系统容器的例子。

我们的目的

创建一个可以独立于任何其它系统服务而更新的网络服务,并且可以从主机端容易地管理和更新。

让我们来探究一下在容器中建立一个运行在 systemd 之下的 BIND 服务器。在这一部分,我们将了解到如何建立自己的容器以及管理 BIND 配置和数据文件。

在本系列的第二部分,我们将看到如何整合主机中的 systemd 和容器中的 systemd。我们将探究如何管理容器中的服务,并且使它作为一种主机中的服务。

创建 BIND 容器

为了使 systemd 在一个容器中轻松运行,我们首先需要在主机中增加两个包:oci-register-machineoci-systemd-hookoci-systemd-hook 这个钩子允许我们在一个容器中运行 systemd,而不需要使用特权容器或者手工配置 tmpfs 和 cgroups。oci-register-machine 这个钩子允许我们使用 systemd 工具如 systemctlmachinectl 来跟踪容器。

[root@rhel7-host ~]# yum install oci-register-machine oci-systemd-hook  

回到创建我们的 BIND 容器上。红帽企业版 Linux 7 基础镜像包含了 systemd 作为其初始化系统。我们可以如我们在典型的系统中做的那样安装并激活 BIND。你可以从 git 仓库中下载这份 Dockerfile

[root@rhel7-host bind]# vi Dockerfile

# Dockerfile for BIND
FROM registry.access.redhat.com/rhel7/rhel
ENV container docker
RUN yum -y install bind && \
    yum clean all && \
    systemctl enable named
STOPSIGNAL SIGRTMIN+3
EXPOSE 53
EXPOSE 53/udp
CMD [ "/sbin/init" ]  

因为我们以 PID 1 来启动一个初始化系统,当我们告诉容器停止时,需要改变 docker CLI 发送的信号。从 kill 系统调用手册中 (man 2 kill):

唯一可以发送给 PID 1 进程(即 init 进程)的信号,是那些初始化系统明确安装了 信号处理器 signal handler 的信号。这是为了避免系统被意外破坏。

对于 systemd 信号处理器,SIGRTMIN+3 是对应于 systemd start halt.target 的信号。我们也需要为 BIND 暴露 TCP 和 UDP 端口号,因为这两种协议可能都要使用。

管理数据

有了一个可以工作的 BIND 服务,我们还需要一种管理配置文件和区域文件的方法。目前这些都放在容器里面,所以我们任何时候都可以进入容器去更新配置或者改变一个区域文件。从管理的角度来说,这并不是很理想。当要更新 BIND 时,我们将需要重建这个容器,所以镜像中的改变将会丢失。任何时候我们需要更新一个文件或者重启服务时,都需要进入这个容器,而这增加了步骤和时间。

相反的,我们将从这个容器中提取出配置文件和数据文件,把它们拷贝到主机上,然后在运行的时候挂载它们。用这种方式我们可以很容易地重启或者重建容器,而不会丢失所做出的更改。我们也可以使用容器外的编辑器来更改配置和区域文件。因为这个容器的数据看起来像“该系统所提供服务的特定站点数据”,让我们遵循 Linux 文件系统层次标准 File System Hierarchy ,并在当前主机上创建 /srv/named 目录来保持管理权分离。

[root@rhel7-host ~]# mkdir -p /srv/named/etc

[root@rhel7-host ~]# mkdir -p /srv/named/var/named     

提示:如果你正在迁移一个已有的配置文件,你可以跳过下面的步骤并且将它直接拷贝到 /srv/named 目录下。你也许仍然要用一个临时容器来检查一下分配给这个容器的 GID。

让我们建立并运行一个临时容器来检查 BIND。在将 init 进程以 PID 1 运行时,我们不能交互地运行这个容器来获取一个 shell。我们会在容器启动后执行 shell,并且使用 rpm 命令来检查重要文件。

[root@rhel7-host ~]# docker build -t named . 

[root@rhel7-host ~]# docker exec -it $( docker run -d named ) /bin/bash

[root@0e77ce00405e /]# rpm -ql bind

对于这个例子来说,我们将需要 /etc/named.conf/var/named/ 目录下的任何文件。我们可以使用 machinectl 命令来提取它们。如果注册了一个以上的容器,我们可以在任一机器上使用 machinectl status 命令来查看运行的是什么。一旦有了这些配置,我们就可以终止这个临时容器了。

如果你喜欢,资源库中也有一个样例 named.conf 和针对 example.com 的区域文件

[root@rhel7-host bind]# machinectl list

MACHINE                          CLASS     SERVICE
8824c90294d5a36d396c8ab35167937f container docker 

[root@rhel7-host ~]# machinectl copy-from 8824c90294d5a36d396c8ab35167937f /etc/named.conf /srv/named/etc/named.conf

[root@rhel7-host ~]# machinectl copy-from 8824c90294d5a36d396c8ab35167937f /var/named /srv/named/var/named

[root@rhel7-host ~]# docker stop infallible_wescoff

最终的创建

为了创建和运行最终的容器,添加卷选项以挂载:

  • 将文件 /srv/named/etc/named.conf 映射为 /etc/named.conf
  • 将目录 /srv/named/var/named 映射为 /var/named

因为这是我们最终的容器,我们将提供一个有意义的名字,以供我们以后引用。

[root@rhel7-host ~]# docker run -d -p 53:53 -p 53:53/udp -v /srv/named/etc/named.conf:/etc/named.conf:Z -v /srv/named/var/named:/var/named:Z --name named-container named

在最终容器运行时,我们可以更改本机配置来改变这个容器中 BIND 的行为。这个 BIND 服务器将需要在这个容器分配的任何 IP 上监听。请确保任何新文件的 GID 与来自这个容器中的其余的 BIND 文件相匹配。

[root@rhel7-host bind]# cp named.conf /srv/named/etc/named.conf 

[root@rhel7-host ~]# cp example.com.zone /srv/named/var/named/example.com.zone

[root@rhel7-host ~]# cp example.com.rr.zone  /srv/named/var/named/example.com.rr.zone
很好奇为什么我不需要在主机目录中改变 SELinux 上下文? 注1

我们将运行这个容器提供的 rndc 二进制文件重新加载配置。我们可以使用 journald 以同样的方式检查 BIND 日志。如果运行出现错误,你可以在主机中编辑该文件,并且重新加载配置。在主机中使用 hostdig,我们可以检查来自该容器化服务的 example.com 的响应。

[root@rhel7-host ~]# docker exec -it named-container rndc reload       
server reload successful

[root@rhel7-host ~]# docker exec -it named-container journalctl -u named -n
-- Logs begin at Fri 2017-05-12 19:15:18 UTC, end at Fri 2017-05-12 19:29:17 UTC. --
May 12 19:29:17 ac1752c314a7 named[27]: automatic empty zone: 9.E.F.IP6.ARPA
May 12 19:29:17 ac1752c314a7 named[27]: automatic empty zone: A.E.F.IP6.ARPA
May 12 19:29:17 ac1752c314a7 named[27]: automatic empty zone: B.E.F.IP6.ARPA
May 12 19:29:17 ac1752c314a7 named[27]: automatic empty zone: 8.B.D.0.1.0.0.2.IP6.ARPA
May 12 19:29:17 ac1752c314a7 named[27]: reloading configuration succeeded
May 12 19:29:17 ac1752c314a7 named[27]: reloading zones succeeded
May 12 19:29:17 ac1752c314a7 named[27]: zone 1.0.10.in-addr.arpa/IN: loaded serial 2001062601
May 12 19:29:17 ac1752c314a7 named[27]: zone 1.0.10.in-addr.arpa/IN: sending notifies (serial 2001062601)
May 12 19:29:17 ac1752c314a7 named[27]: all zones loaded
May 12 19:29:17 ac1752c314a7 named[27]: running

[root@rhel7-host bind]# host www.example.com localhost
Using domain server:
Name: localhost
Address: ::1#53
Aliases: 
www.example.com is an alias for server1.example.com.
server1.example.com is an alias for mail
你的区域文件没有更新吗?可能是因为你的编辑器,而不是序列号。 注2

终点线

我们已经达成了我们打算完成的目标,从容器中为 DNS 请求和区域文件提供服务。我们已经得到一个持久化的位置来管理更新和配置,并且更新后该配置不变。

在这个系列的第二部分,我们将看到怎样将一个容器看作为主机中的一个普通服务来运行。


关注 RHEL 博客,通过电子邮件来获得本系列第二部分和其它新文章的更新。


附加资源

你可能已经注意到当我从容器向本地主机拷贝文件时,我没有运行 chcon 将主机中的文件类型改变为 svirt_sandbox_file_t。为什么它没有出错?将一个文件拷贝到 /srv 会将这个文件标记为类型 var_t。我 setenforce 0 (关闭 SELinux)了吗?

当然没有,这将让 Dan Walsh 大哭(LCTT 译注:RedHat 的 SELinux 团队负责人,倡议不要禁用 SELinux)。是的,machinectl 确实将文件标记类型设置为期望的那样,可以看一下:

启动一个容器之前:

[root@rhel7-host ~]# ls -Z /srv/named/etc/named.conf
-rw-r-----. unconfined_u:object_r:var_t:s0   /srv/named/etc/named.conf

不过,运行中我使用了一个卷选项可以使 Dan Walsh 先生高兴起来,:Z-v /srv/named/etc/named.conf:/etc/named.conf:Z 命令的这部分做了两件事情:首先它表示这需要使用一个私有卷的 SELiunx 标记来重新标记;其次它表明以读写挂载。

启动容器之后:

[root@rhel7-host ~]# ls -Z /srv/named/etc/named.conf 
-rw-r-----. root 25 system_u:object_r:svirt_sandbox_file_t:s0:c821,c956 /srv/named/etc/named.conf
  • 注2: VIM 备份行为能改变 inode

如果你在本地主机中使用 vim 来编辑配置文件,而你没有看到容器中的改变,你可能不经意的创建了容器感知不到的新文件。在编辑时,有三种 vim 设定影响备份副本:backupwritebackupbackupcopy

我摘录了 RHEL 7 中的来自官方 VIM backup\_table 中的默认配置。

backup    writebackup
off      on backup current file, deleted afterwards (default)

所以我们不创建残留下的 ~ 副本,而是创建备份。另外的设定是 backupcopyauto 是默认的设置:

"yes" make a copy of the file and overwrite the original one
"no" rename the file and write a new one
"auto" one of the previous, what works best

这种组合设定意味着当你编辑一个文件时,除非 vim 有理由(请查看文档了解其逻辑),你将会得到一个包含你编辑内容的新文件,当你保存时它会重命名为原先的文件。这意味着这个文件获得了新的 inode。对于大多数情况,这不是问题,但是这里容器的 绑定挂载 bind mount 对 inode 的改变很敏感。为了解决这个问题,你需要改变 backupcopy 的行为。

不管是在 vim 会话中还是在你的 .vimrc中,请添加 set backupcopy=yes。这将确保原先的文件被清空并覆写,维持了 inode 不变并且将该改变传递到了容器中。


via: http://rhelblog.redhat.com/2017/07/19/containing-system-services-in-red-hat-enterprise-linux-part-1/

作者:Matt Micene 译者:liuxinyu123 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

多阶段构建是 Docker 17.05 及更高版本提供的新功能。这对致力于优化 Dockerfile 的人来说,使得 Dockerfile 易于阅读和维护。

致谢: 特别感谢 Alex Ellis 授权使用他的关于 Docker 多阶段构建的博客文章 Builder pattern vs. Multi-stage builds in Docker 作为以下示例的基础。

在多阶段构建之前

关于构建镜像最具挑战性的事情之一是保持镜像体积小巧。 Dockerfile 中的每条指令都会在镜像中增加一层,并且在移动到下一层之前,需要记住清除不需要的构件。要编写一个非常高效的 Dockerfile,你通常需要使用 shell 技巧和其它方式来尽可能地减少层数,并确保每一层都具有上一层所需的构件,而其它任何东西都不需要。

实际上最常见的是,有一个 Dockerfile 用于开发(其中包含构建应用程序所需的所有内容),而另一个裁剪过的用于生产环境,它只包含您的应用程序以及运行它所需的内容。这被称为“构建器模式”。但是维护两个 Dockerfile 并不理想。

下面分别是一个 Dockerfile.build 和遵循上面的构建器模式的 Dockerfile 的例子:

Dockerfile.build

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN go get -d -v golang.org/x/net/html \
  && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

注意这个例子还使用 Bash 的 && 运算符人为地将两个 RUN 命令压缩在一起,以避免在镜像中创建额外的层。这很容易失败,难以维护。例如,插入另一个命令时,很容易忘记继续使用 \ 字符。

Dockerfile

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]

build.sh

#!/bin/sh
echo Building alexellis2/href-counter:build

docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \
    -t alexellis2/href-counter:build . -f Dockerfile.build

docker create --name extract alexellis2/href-counter:build
docker cp extract:/go/src/github.com/alexellis/href-counter/app ./app
docker rm -f extract

echo Building alexellis2/href-counter:latest

docker build --no-cache -t alexellis2/href-counter:latest .
rm ./app

当您运行 build.sh 脚本时,它会构建第一个镜像,从中创建一个容器,以便将该构件复制出来,然后构建第二个镜像。 这两个镜像会占用您的系统的空间,而你仍然会一个 app 构件存放在你的本地磁盘上。

多阶段构建大大简化了这种情况!

使用多阶段构建

在多阶段构建中,您需要在 Dockerfile 中多次使用 FROM 声明。每次 FROM 指令可以使用不同的基础镜像,并且每次 FROM 指令都会开始新阶段的构建。您可以选择将构件从一个阶段复制到另一个阶段,在最终镜像中,不会留下您不需要的所有内容。为了演示这是如何工作的,让我们调整前一节中的 Dockerfile 以使用多阶段构建。

Dockerfile

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

您只需要单一个 Dockerfile。 不需要另外的构建脚本。只需运行 docker build 即可。

$ docker build -t alexellis2/href-counter:latest .

最终的结果是和以前体积一样小的生产镜像,复杂性显著降低。您不需要创建任何中间镜像,也不需要将任何构件提取到本地系统。

它是如何工作的呢?第二条 FROM 指令以 alpine:latest 镜像作为基础开始新的建造阶段。COPY --from=0 这一行将刚才前一个阶段产生的构件复制到这个新阶段。Go SDK 和任何中间构件都被留在那里,而不会保存到最终的镜像中。

命名您的构建阶段

默认情况下,这些阶段没有命名,您可以通过它们的整数来引用它们,从第一个 FROM 指令的 0 开始。但是,你可以通过在 FROM 指令中使用 as <NAME> 来为阶段命名。以下示例通过命名阶段并在 COPY 指令中使用名称来改进前一个示例。这意味着,即使您的 Dockerfile 中的指令稍后重新排序,COPY 也不会出问题。

FROM golang:1.7.3 as builder
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html
COPY app.go    .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]

via: https://docs.docker.com/engine/userguide/eng-image/multistage-build/

作者:docker 译者:iron0x 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

 title=

Phonton OS 专注于容器,是一个非常出色的平台。 —— Jack Wallen

容器在当下的火热,并不是没有原因的。正如之前讨论的,容器可以使您轻松快捷地将新的服务与应用部署到您的网络上,而且并不耗费太多的系统资源。比起专用硬件和虚拟机,容器都是更加划算的,除此之外,他们更容易更新与重用。

更重要的是,容器喜欢 Linux(反之亦然)。不需要太多时间和麻烦,你就可以启动一台 Linux 服务器,运行Docker,然后部署容器。但是,哪种 Linux 发行版最适合部署容器呢?我们的选择很多。你可以使用标准的 Ubuntu 服务器平台(更容易安装 Docker 并部署容器)或者是更轻量级的发行版 —— 专门用于部署容器。

Photon 就是这样的一个发行版。这个特殊的版本是由 VMware 于 2005 年创建的,它包含了 Docker 的守护进程,并可与容器框架(如 Mesos 和 Kubernetes )一起使用。Photon 经过优化可与 VMware vSphere 协同工作,而且可用于裸机、Microsoft AzureGoogle Compute EngineAmazon Elastic Compute Cloud 或者 VirtualBox 等。

Photon 通过只安装 Docker 守护进程所必需的东西来保持它的轻量。而这样做的结果是,这个发行版的大小大约只有 300MB。但这足以让 Linux 的运行一切正常。除此之外,Photon 的主要特点还有:

  • 内核为性能而调整。
  • 内核根据内核自防护项目(KSPP)进行了加固。
  • 所有安装的软件包都根据加固的安全标识来构建。
  • 操作系统在信任验证后启动。
  • Photon 的管理进程可以管理防火墙、网络、软件包,和远程登录在 Photon 机器上的用户。
  • 支持持久卷。
  • Project Lightwave 整合。
  • 及时的安全补丁与更新。

Photon 可以通过 ISO 镜像OVAAmazon Machine ImageGoogle Compute Engine 镜像Azure VHD 安装使用。现在我将向您展示如何使用 ISO 镜像在 VirtualBox 上安装 Photon。整个安装过程大概需要五分钟,在最后您将有一台随时可以部署容器的虚拟机。

创建虚拟机

在部署第一台容器之前,您必须先创建一台虚拟机并安装 Photon。为此,打开 VirtualBox 并点击“新建”按钮。跟着创建虚拟机向导进行配置(根据您的容器将需要的用途,为 Photon 提供必要的资源)。在创建好虚拟机后,您所需要做的第一件事就是更改配置。选择新建的虚拟机(在 VirtualBox 主窗口的左侧面板中),然后单击“设置”。在弹出的窗口中,点击“网络”(在左侧的导航中)。

在“网络”窗口(图1)中,你需要在“连接”的下拉窗口中选择桥接。这可以确保您的 Photon 服务与您的网络相连。完成更改后,单击确定。

 title=

图 1: 更改 Photon 在 VirtualBox 中的网络设置。经许可使用

从左侧的导航选择您的 Photon 虚拟机,点击启动。系统会提示您去加载 ISO 镜像。当您完成之后,Photon 安装程序将会启动并提示您按回车后开始安装。安装过程基于 ncurses(没有 GUI),但它非常简单。

接下来(图2),系统会询问您是要最小化安装,完整安装还是安装 OSTree 服务器。我选择了完整安装。选择您所需要的任意选项,然后按回车继续。

 title=

图 2: 选择您的安装类型。经许可使用

在下一个窗口,选择您要安装 Photon 的磁盘。由于我们将其安装在虚拟机,因此只有一块磁盘会被列出(图3)。选择“自动”按下回车。然后安装程序会让您输入(并验证)管理员密码。在这之后镜像开始安装在您的磁盘上并在不到 5 分钟的时间内结束。

 title=

图 3: 选择安装 Photon 的硬盘。经许可使用

安装完成后,重启虚拟机并使用安装时创建的用户 root 和它的密码登录。一切就绪,你准备好开始工作了。

在开始使用 Docker 之前,您需要更新一下 Photon。Photon 使用 yum 软件包管理器,因此在以 root 用户登录后输入命令 yum update。如果有任何可用更新,则会询问您是否确认(图4)。

 title=

图 4: 更新 Photon。经许可使用

用法

正如我所说的,Photon 提供了部署容器甚至创建 Kubernetes 集群所需要的所有包。但是,在使用之前还要做一些事情。首先要启动 Docker 守护进程。为此,执行以下命令:

systemctl start docker
systemctl enable docker

现在我们需要创建一个标准用户,以便我们可以不用 root 去运行 docker 命令。为此,执行以下命令:

useradd -m USERNAME
passwd USERNAME

其中 “USERNAME” 是我们新增的用户的名称。

接下来,我们需要将这个新用户添加到 “docker” 组,执行命令:

usermod -a -G docker USERNAME

其中 “USERNAME” 是刚刚创建的用户的名称。

注销 root 用户并切换为新增的用户。现在,您已经可以不必使用 sudo 命令或者切换到 root 用户来使用 docker 命令了。从 Docker Hub 中取出一个镜像开始部署容器吧。

一个优秀的容器平台

在专注于容器方面,Photon 毫无疑问是一个出色的平台。请注意,Photon 是一个开源项目,因此没有任何付费支持。如果您对 Photon 有任何的问题,请移步 Photon 项目的 GitHub 下的 Issues,那里可以供您阅读相关问题,或者提交您的问题。如果您对 Photon 感兴趣,您也可以在该项目的官方 GitHub中找到源码。

尝试一下 Photon 吧,看看它是否能够使得 Docker 容器和 Kubernetes 集群的部署更加容易。

欲了解 Linux 的更多信息,可以通过学习 Linux 基金会和 edX 的免费课程,“Linux 入门”


via: https://www.linux.com/learn/intro-to-linux/2017/11/photon-could-be-your-new-favorite-container-os

作者:JACK WALLEN 译者:KeyLD 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出