《docker实践(1) 入门和springBoot实践部署》
《docker实践(2)常用命令和DockerFile详解》
《docker实践(3) 仓库registry和Nexus3作为私有镜像仓库 容器镜像原理和容器仓库registry详解》
一、 Docker镜像image原理
Docker Image 就是一个只读的文件,作为创建 Docker Container 的模板。镜像是容器的基石,容器基于镜像启动,镜像就像是容器的源代码,保存了用于容器启动的各种条件。
Docker 支持通过扩展现有镜像,继而创建新的镜像。实际上,Docker Hub 中 99% 的镜像都是通过在 base image 中安装和配置需要的软件构建出来的。
Docker Image 是一个层叠的只读文件系统,结构如下:
- bootfs(引导文件系统):与 Linux Kernel 交互的引导系统。
- rootfs(root 文件系统):根文件系统,即 base image,可以是一种或多种操作系统,如:Ubuntu 或 CentOS,rootfs 永远是只读状态。
- unionFS(联合文件系统):即所有 base image 之上的文件系统。Docker 应用了 union mount(联合加载技术),一次可以加载多个只读文件系统到 rootfs 之上,从外面看到的只是一个文件系统。union mount 将各层文件系统叠加到一起,使最终呈现出来的文件系统包含了所有底层文件系统和目录,这样的文件系统就是镜像。
一个镜像可以放到另一个镜像的顶部,位于下边的镜像叫做父镜像,依次类推,最底部的镜像叫做 base image,指的就是 rootfs,即 Ubuntu 或 CentOS 等。
当使用 Image 启动一个 Container 后,一个新的可写的文件系统被加载到镜像的顶部,即:可写层,通常也称作 “容器层”,“容器层” 之下的都叫 “镜像层”。
Container 中运行的程序就是在这个 “容器层” 中执行的。第一次启动 Container 时,“容器层” 是空的,当文件系统发生变化,都会应用到这一层。如果想修改一个文件,该文件首先会从 “容器层” 下边的 “镜像只读层” 复制到可写层,该文件的只读版本依然存在,但是已经被可写层中的该文件副本所隐藏。这个是 Docker 重要的写时复制(copy on write)机制。
Docker Image 的生命周期如下图所示。
五. Dockerfile详解
Dockerfile是一个包含用于组合映像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile
中的指令自动生成映像。
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。有了 Dockerfile,当我们需要定制自己额外的需求时,只需在 Dockerfile 上添加或者修改指令,重新生成 image 即可,省去了敲命令的麻烦。
可以用一张图来说明:Docker镜像,Dockerfile ,容器之间的关系
1、Dockerfile的基本结构
Dockerfile 一般分为四部分:基础镜像信息、维护者信息、镜像操作指令、容器启动时执行指令.
## Dockerfile文件格式
# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# 1、第一行必须指定 基础镜像信息
FROM ubuntu
# 2、维护者信息
MAINTAINER docker_user docker_user@email.com
# 3、镜像操作指令
RUN apt-get update && apt-get install -y nginx
# 4、容器启动执行指令
CMD /usr/sbin/nginx
2、Dockerfile命令详细说明
Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像,第一条指令必须是FROM。一个声明以#
字符开头则被视为注释。可以在Docker文件中使用RUN
,CMD
,FROM
,EXPOSE
,ENV
等指令。
在这里列出了一些常用的指令。
FROM:指定基础镜像
所有Dockerfile都必须以FROM
命令开始。 FROM
命令会指定镜像基于哪个基础镜像创建,接下来的命令也会基于这个基础镜像(译者注:CentOS和Ubuntu有些命令可是不一样的)。FROM
命令可以多次使用,表示会创建多个镜像。
FROM <image>
FROM <image>:<tag>
FROM <image>:<digest>
#三种写法,其中<tag>和<digest> 是可选项,如果没有选择,那么默认值为latest
MAINTAINER: 维护者信息
格式:
MAINTAINER <author name>
示例:
MAINTAINER Jasper Xu
MAINTAINER sorex@163.com
MAINTAINER Jasper Xu <sorex@163.com>
RUN:构建镜像时执行的命令
RUN用于在镜像容器中执行命令,其有以下两种命令执行方式:
shell执行
格式:
RUN <command>
exec执行
格式:
RUN ["executable", "param1", "param2"]
第二种是类似于函数调用。可将executable理解成为可执行文件,后面就是两个参数。
示例:
RUN ["executable", "param1", "param2"]
RUN apk update
RUN ["/etc/execfile", "arg1", "arg1"]
注:
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
ADD:将本地文件添加到容器中
tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget
复制文件指令。它有两个参数<source>和<destination>。destination是容器内的路径。source可以是URL或者是启动配置上下文中的一个文件。
格式:
ADD <src>... <dest>
ADD ["<src>",... "<dest>"] 用于支持包含空格的路径
示例:
ADD hom* /mydir/ # 添加所有以"hom"开头的文件
ADD hom?.txt /mydir/ # ? 替代一个单字符,例如:"home.txt"
ADD test relativeDir/ # 添加 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # 添加 "test" 到 /absoluteDir/
ADD 命令可以完成 COPY 命令的所有功能,并且还可以完成两类超酷的功能:
- 解压压缩文件并把它们添加到镜像中
- 从 url 拷贝文件到镜像中
当然,这些功能也让 ADD 命令用起来复杂一些,不如 COPY 命令那么直观。
1)解压压缩文件并把它们添加到镜像中
如果我们有一个压缩文件包,并且需要把这个压缩包中的文件添加到镜像中。需不需要先解开压缩包然后执行 COPY 命令呢?当然不需要!我们可以通过 ADD 命令一次搞定:
WORKDIR /app
ADD nickdir.tar.gz .
这应该是 ADD 命令的最佳使用场景了!
2)从 url 拷贝文件到镜像中
这是一个更加酷炫的用法!但是在 docker 官方文档的最佳实践中却强烈建议不要这么用!!docker 官方建议我们当需要从远程复制文件时,最好使用 curl 或 wget 命令来代替 ADD 命令。原因是,当使用 ADD 命令时,会创建更多的镜像层,当然镜像的 size 也会更大(下面的两段代码来自 docker 官方文档):
ADD http://example.com/big.tar.xz /usr/src/things/
RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
RUN make -C /usr/src/things all
如果使用下面的命令,不仅镜像的层数减少,而且镜像中也不包含 big.tar.xz 文件:
RUN mkdir -p /usr/src/things \
&& curl -SL http://example.com/big.tar.xz \
| tar -xJC /usr/src/things \
&& make -C /usr/src/things all
好吧,看起来只有在解压压缩文件并把它们添加到镜像中时才需要 ADD 命令!
COPY:功能类似ADD
但是是不会自动解压文件,也不能访问网络资源:
除了指定完整的文件名外,COPY 命令还支持 Go 风格的通配符,比如:
COPY check* /testdir/ # 拷贝所有 check 开头的文件
COPY check?.log /testdir/ # ? 是单个字符的占位符,比如匹配文件 check1.log
对于目录而言,COPY 和 ADD 命令具有相同的特点:只复制目录中的内容而不包含目录自身。
比如我们在 Dockerfile 中添加下面的命令:
在制作 docker 镜像时,有复制某一个路径下所有文件和文件夹到镜像的需求,写下了如下 dockerfile:
FROM alpine
WORKDIR /root/test_docker_proj
COPY * ./
原始目录结构是这样的:
/projects/test_docker_proj
├── Dockerfile
├── dir1
│ ├── dir11
│ │ └── file11
│ └── file1
└── file2
然而复制到 docker 镜像里的目录结构变成了这样:
/root/test_docker_proj
├── Dockerfile
├── dir11
│ └── file11
├── file1
└── file2
可以看到 dir1 这个文件夹并没有被复制到镜像里,但是 dir1 中的子文件夹和文件都被复制进来了,和 dir1 同级的文件也被复制了。也就是说,在 COPY 执行的过程中,第一层文件夹被「解包」了。
CMD:构建容器后调用,也就是在容器启动时才进行调用。
格式:
CMD ["executable","param1","param2"] (执行可执行文件,优先)
CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
CMD command param1 param2 (执行shell内部命令)
示例:
CMD echo "This is a test." | wc -
CMD ["/usr/bin/wc","--help"]
注:
CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。
ENTRYPOINT:配置容器,使其可执行化。
配合CMD可省去"application",只使用参数。
docker run
docker run
LABEL:用于为镜像添加元数据
格式:
LABEL <key>=<value> <key>=<value> <key>=<value> ...
示例:
LABEL version="1.0" description="这是一个Web服务器" by="IT笔录"
注:
使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。
ENV:设置环境变量
格式:
ENV <key> <value> #<key>之后的所有内容均会被视为其<value>的组成部分,因此,一次只能设置一个变量
ENV <key>=<value> ... #可以设置多个变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
示例:
ENV myName John Doe
ENV myDog Rex The Dog
ENV myCat=fluffy
EXPOSE:指定于外界交互的端口
docker run
-P
VOLUME:用于指定持久化目录
格式:
VOLUME ["/path/to/dir"]
示例:
VOLUME ["/data"]
VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"
注:
一个卷可以存在于一个或多个容器的指定目录,该目录可以绕过联合文件系统,并具有以下功能:
1 卷可以容器间共享和重用
2 容器并不一定要和其它容器共享卷
3 修改卷后会立即生效
4 对卷的修改不会对镜像产生影响
5 卷会一直存在,直到没有任何容器在使用它
WORKDIR:工作目录,类似于cd命令
docker run
USER:指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。
使用USER指定用户时,可以使用用户名、UID或GID,或是两者的组合。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户
格式:
USER user
USER user:group
USER uid
USER uid:gid
USER user:gid
USER uid:group
示例:
USER www
注:
使用USER指定用户后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT都将使用该用户。镜像构建完成后,通过docker run
运行容器时,可以通过-u参数来覆盖所指定的用户。
ARG:用于指定传递给构建运行时的变量
构建参数,作用于ENV相同,不同的是ARG的参数只在构建镜像的时候起作用,也就是docker build的时候。
Dockerfile中的ARG指令用以定义构建时需要的参数,使用格式如下:
格式:
ARG <name>[=<default value>]
示例:
ARG site
ARG build_user=www
ARG指令定义的参数,在docker build命令中以--build-arg a_name=a_value形式赋值。
如果docker build命令传递的参数,在Dockerfile中没有对应的参数,将抛出如下警告:
[Warning] One or more build-args [foo] were not consumed.
如果在Dockerfile中,ARG指令定义参数之前,就有其他指令引用了参数,则参数值为空字符串。
ARG指令定义参数在CMD指令为空。
不建议在构建的过程中,以参数的形式传递保密信息,如key, password等。
Docker自带的如下ARG参数,可以在其他指令中直接引用:
- HTTP_PROXY
- http_proxy
- HTTPS_PROXY
- https_proxy
- FTP_PROXY
- ftp_proxy
- NO_PROXY
- no_proxy
ONBUILD:用于设置镜像触发器
格式:
ONBUILD [INSTRUCTION]
示例:
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
注:
当所构建的镜像被用做其它镜像的基础镜像,该镜像中的触发器将会被钥触发
以下是一个小例子:
# This my first nginx Dockerfile
# Version 1.0
# Base images 基础镜像
FROM centos
#MAINTAINER 维护者信息
MAINTAINER tianfeiyu
#ENV 设置环境变量
ENV PATH /usr/local/nginx/sbin:$PATH
#ADD 文件放在当前目录下,拷过去会自动解压
ADD nginx-1.8.0.tar.gz /usr/local/
ADD epel-release-latest-7.noarch.rpm /usr/local/
#RUN 执行以下命令
RUN rpm -ivh /usr/local/epel-release-latest-7.noarch.rpm
RUN yum install -y wget lftp gcc gcc-c++ make openssl-devel pcre-devel pcre && yum clean all
RUN useradd -s /sbin/nologin -M www
#WORKDIR 相当于cd
WORKDIR /usr/local/nginx-1.8.0
RUN ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_ssl_module --with-pcre && make && make install
RUN echo "daemon off;" >> /etc/nginx.conf
#EXPOSE 映射端口
EXPOSE 80
#CMD 运行以下命令
CMD ["nginx"]
三、 docker 仓库
1、docker 官方仓库
1.1 下载官方的 CentOS 镜像到本地
docker pull centos
默认会从docker.io上下载镜像。
1.2 搜索官方的 CentOS 镜像
[root@iZ235fz06auZ docker]# docker search centos
INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
docker.io docker.io/centos The official build of CentOS. 3476 [OK]
docker.io docker.io/jdeathe/centos-ssh CentOS-6 6.9 x86_64 / CentOS-7 7.3.1611 x8... 77 [OK]
docker.io docker.io/tutum/centos Simple CentOS docker image with SSH access 32
docker.io docker.io/kinogmt/centos-ssh CentOS with SSH 15 [OK]
STARS:星级( 表示该镜像的受欢迎程度)
OFFICIAL:是否官方创建
AUTOMATED:是否自动创建。
官方的镜像说明是官方项目组创建和维护的,automated资源允许用户验证镜像的来源和内容。
根据是否是官方提供, 可将镜像资源分为两类。 一种是类似centos这样的基础镜像, 被称为基础或根镜像。这些基础镜像是由Docker公司创建、验证、 支持、 提供。这样的镜像往往使用单个单词作为名字。
还有一种类型, 比如 tianon/centos 镜像, 它是由 Docker 的用户创建并维护的, 往往带有用户名称前缀。 可以通过前缀user_name/来指定使用某个用户提供的镜像, 比如tianon 用户。
另外, 在查找的时候通过-s N 参数可以指定仅显示评价为N星以上的镜像。
1.3修改默认的仓库地址
修改成阿里云:
vi /etc/docker/daemon.json
{
"registry-mirrors": ["https://3bxiiqzd.mirror.aliyuncs.com"]
}
2. docker 私有仓库
有时候使用 Docker Hub 这样的公共仓库可能不方便, 用户可以创建一个本地仓库供私人使用。
Registry在github上有两份代码:老代码库和新代码库。老代码是采用python编写的,存在pull和push的性能问题,出到0.9.1版本之后就标志为deprecated,不再继续开发。从2.0版本开始就到在新代码库进行开发,新代码库是采用go语言编写,修改了镜像id的生成算法、registry上镜像的保存结构,大大优化了pull和push镜像的效率。
2.1 私有仓库镜像安装
官方在Docker hub上提供了registry的镜像(详情),我们可以直接使用该registry镜像来构建一个容器,搭建我们自己的私有仓库服务。Tag为latest的registry镜像是0.9.1版本的,我们直接采用2.1.1版本。
1、docker run -d -p 5000:5000 registry
1)指定私有仓库位置:
用户可以通过指定参数来配置私有仓库位置, 例如配置镜像存储到Amazon S3 服务。
$ sudo docker run \
-e SETTINGS_FLAVOR=s3 \
-e AWS_BUCKET=acme-docker \
-e STORAGE_PATH=/registry \
-e AWS_KEY=AKIAHSHB43HS3J92MXZ \
-e AWS_SECRET=xdDowwlK7TJajV1Y7EoOZrmuPEJlHYcNP2k4j49T \
-e SEARCH_BACKEND=sqlalchemy \
-p 5000:5000 \
registry
2)配置文件:
还可以指定本地路径( 如/home/user/registry-conf) 下的配置文件
docker run -d -p 5000:5000 -v /etc/docker/registry/config.yml:/etc/docker/registry/config.yml registry
3)仓库本地:
之前老版Register,默认上传的镜像保存在容器的位置是/tmp/registry 。新registry的仓库目录是在/var/lib/registry,所以运行时挂载目录需要注意。
docker run -d -p 5000:5000 -v /data/registry:/var/lib/registry registry
我们将主机的/data/registry目录挂载到该目录,即可实现将镜像保存到主机的/data/registry目录了。
2.2、登陆私有仓库:
docker login --username $DOCKER_USER registry.xxxx.com
要以非交互方式运行该命令,您可以设置 flag 以提供密码通过。使用防止密码最终出现在外壳的历史记录中, 或日志文件。
下面的示例从文件中读取密码,并使用以下命令将其传递给命令:
docker login https://xxx.com --username macy.hou --password-stdin < /root/.docker/passwd
或
cat /root/.docker/passwd |docker login https://xxx.com -u macy.hou --password-stdin
也可以以环境变量形式登录,以下示例从变量读取密码,然后使用STDIN将其传递给docker login命令:
echo "$PASSWORD" | docker login --username foo --password-stdin
linux下可以通过/etc/profile 设置 PASSWORD变量
2.4 上传pull
创建好私有仓库之后, 就可以使用 docker tag 来标记一个镜像, 然后推送它到本地仓库,别的机器上就可以下载下来了。 例如私有仓库本地地址为127.0.0.1:5000。
$ docker tag java8 127.0.0.1:5000/java8
使用 docker push 上传标记的镜像使用 docker push 上传标记的镜像
$ docker push 127.0.0.1:5000/java8
当使用curl http://192.168.0.100:5000/v2/_catalog能看到json格式的返回值时,说明registry已经运行起来了。
2.4 远程访问
到目前为止,docker registry 已经可以正常使用,且可以指定数据存储位置。但也只能在本地使用,要想在远程使用该 registry,就必须使用 TLS 来确保通信安全,就像使用 SSL 来配置 web 服务器。也可以强制 docker registry 运行在 insecure 模式,这种模式虽然配置起来要简单一些,但很不安全,一般不建议使用。
这里偷懒使用这个简单的 insecure 模式,假设你在一个域名为 test.docker.midea.registry.hub 的主机上运行 docker registry,步骤如下:
/etc/default/docker 或 /etc/sysconfig/docker,具体是哪个取决于你的系统。
DOCKER_OPTS
ADD_REGISTRY='--add-registry test.docker.midea.registry.hub:5000'
DOCKER_OPTS="--insecure-registry test.docker.midea.registry.hub:5000"
INSECURE_REGISTRY='--insecure-registry test.docker.midea.registry.hub:5000'
3、重启你的 docker 守护进程
通过以上3步,你的这个机器就能远程从 test.docker.midea.registry.hub 上运行的 docker registry 拉取镜像了
2.5 本地安装运行
有时候需要本地运行仓库服务,可以通过源码方式进行安装。首先安装Golang环境支持,以Ubuntu为例,可以执行如下命令:
$ sudo add-apt-repository ppa:ubuntu-lxc/lxd-stable
$ sudo apt-get update
$ sudo apt-get install golang
确认Golang环境安装成功,并配置$GOPATH环境变量,例如/go。
创建$GOPATH/src/github.com/docker/目录,并获取源码,如下所示:
$ mkdir -p $GOPATH/src/github.com/docker/
$ cd $GOPATH/src/github.com/docker/
$ git clone https://github.com/docker/distribution.git
$ cd distribution
将自带的模板配置文件复制到/etc/docker/registry/路径下,创建存储目录/var/lib/registry:
$ cp cmd/registry/config-dev.yml /etc/docker/registry/config.yml
$ mkdir -p /var/lib/registry
然后执行安装操作:
$ make PREFIX=/go clean binaries
编译成功后,可以通过下面的命令来启动:
$ registry server /etc/docker/registry/config.yml
此时使用访问本地的5000端口,看到返回成功(200 OK),则说明运行成功:
$ curl -i 127.0.0.1:5000/v2/
HTTP/1.1 200 OK
3. Registry 仓库v2认证模式
Docker Registry v2的认证模式和v1有了较大的变化,降低了系统的复杂度、减少了服务之间的交互次数,其基本工作模式:
具体交互过程包括如下步骤:
1)Docker Daemon或者其他客户端尝试访问Registry服务器,比如pull、push或者访问manifiest文件;
2)在Registry服务器开启了认证服务模式时,就会直接返回401 Unauthorized错误,并通知调用方如何获得授权;
3)调用方按照要求,向Authorization Service发送请求,并携带Authorization Service需要的信息,比如用户名、密码;
4)如果授权成功,则可以拿到合法的Bearer token,来标识该请求方可以获得的权限;
5)请求方将拿到Bearer token加到请求的Authorization header中,再次尝试步骤1中的请求;
6)Registry服务通过验证Bearer token以及JWT格式的授权数据,来决定用户是否有权限进行请求的操作。
当启用认证服务时,需要注意以下两个地方:
- 对于Authentication Service,Docker官方目前并没有放出对应的实现方案,需要自行实现对应的服务接口;
- Registry服务和Authentication服务之间通过证书进行Bearer token的生成和认证,所以要保证两个服务之间证书的匹配。
除了使用第三方实现的认证服务(如docker_auth、SUSE Portus等)外,还可以通过Nginx代理方式来配置基于用户名密码的认证。
4. Nexus3作为私有镜像仓库
使用nexus3作为私有仓库是最简单的方式。
4.1 、安装
1.下载Nexus:
下载最新版的nexus下载地址: https://www.sonatype.com/oss-thank-you-tar.gz
2.创建nexus文件夹并解压:
mkdir /mnt/app/nexus
tar -xvf nexus-3.15.2-01-unix.tar.gz -C /mnt/app/nexus
3.使用java8启动:
修改bin/nexus :
INSTALL4J_JAVA_HOME_OVERRIDE=/usr/java8/
进入nexus-3.15.2-01/bin目录下,执行./nexus start 命令启动
启动成功后要等一会儿,8081端口才会起来 ,可以使用lsof -i:8081 命令查看端口占用情况。
4.安全起见,我们建立一个nexus用户用来启动nexus
adduser nexus
更改nexus文件夹的访问权限更改为nexus用户
sudo chown -R nexus:nexus /nexus
打开/nexus/bin/nexus.rc 文件, 修改启动用户如下
run_as_user=”nexus”
5.如果你想更改默认的数据目录,可以打开nexus的配置文件,修改-Dkaraf.data属性。
vi /nexus/nexus/bin/nexus.vmoptions
下面来一个例子
-Xms1200M
-Xmx1200M
-XX:+UnlockDiagnosticVMOptions
-XX:+UnsyncloadClass
-Djava.net.preferIPv4Stack=truer
-Dkaraf.home=.
-Dkaraf.base=.
-Dkaraf.etc=etc
-Djava.util.logging.config.file=etc/java.util.logging.properties
-Dkaraf.data=/nexus/nexus-data
-Djava.io.tmpdir=data/tmp
-Dkaraf.startLocalConsole=false
6. 修改端口号: etc/nexus-default.properties
7: supervisor启动:
[program:turing-nexus2]
command=/mnt/app/nexus/bin/nexus run
process_name=%(program_name)s
numprocs=1
directory=/mnt/app/nexus
umask=022
user=appuser
stdout_logfile=/mnt/logs/turing-nexus2/server.log
stderr_logfile=/mnt/logs/turing-nexus2/error.log
environment=
serverurl=AUTO
4.2 、配置使用
访问地址:http://192.168.10.50:18082/
使用域名:http://nexus.xxxxx.com admin admin123
首次默认登陆账号:admin admin123
nexus可以作用yum的私库,maven私库、nuget等,可以参见官方说明,我们这里只是针对docker的私有镜像进行配置
代理镜像库(proxy repository)的配置和访问:在内网总是有下载hub.docker.com 的需求,可以通过配置nexus的代理模式来实现
4.3、配置公共读:
4.4、nginx代理
配置host repository的HTTPS访问:nginx代理
设置Docker Bearer Token Realm可用:
5、docker使用私有registry仓库
1、登陆:
docker login xxxx.com#登陆
需要输入创建repository时指定的账号和密码 登陆成功后可以pull私有库的镜像。
2、拉取镜像:
docker pull xxxx.com/nginx # 拉取xxxx.com上的nginx镜像,如果没有则拉取失败
3、提交本地镜像nexus仓库:
创建好私有仓库之后, 就可以使用 docker tag 来标记一个镜像, 然后推送它到本地仓库,别的机器上就可以下载下来了。 例如 标记镜像:
docker tag java8 xxxx.com/java8
使用 docker push 上传标记的镜像使用 docker push 上传标记的镜像
4、上传镜像:
docker push xxxx.com/java8
5、测试:
1)使用curl http://xxxx.com/v2/_catalog能看到json格式的返回值时,说明registry已经运行起来了。
2)# docker search registry.xxxx.com/java8
INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
6、保存私有仓库的登陆信息文件位置:
我们在命令行方式下,输入docker login登陆成功后,会在 ~/.docker/目录下生成一个config.json文件。打开后可以看到如下的内容:
# cat ~/.docker/config.json
{
"auths": {
"registry.xxxx.com": {
"auth": "cmVnaXN0cnktYWRtaW46cmVnaXN0cnktYWRtaW4="
}
}}
如果不logout ,下次拉取镜像就不再需要输入账号和密码:
这种方式,在kubernetes 中使用deployment定义一个pod,是不能下载镜像成功的。
四、k8s使用私有仓库
(后续章节我们讲到)
通常情况下,在私有云环境中使用kubernetes时,我们要从docker registry拉取镜像的时候,都会给docker daemo配置--insecure-registry属性来告诉docker daemo我们所使用的docker registry是可信的,这样才能从私有的docker registry中拉取镜像,但是如果要使用nexus作为kubernetes的镜像仓库的话,这种方式就不适用了,下面让我们看看如何来使用nexus作为kubernetes的镜像仓库。
1、kubenetes设置pause镜像
由于在Kubenetes中是以pod而不是Docker容器管理单元,在kubelet创建pod的时候,还通过启动一个名为google_containers/pause的镜像来实现pod概念。
Pause容器 全称infrastucture container(又叫infra)基础容器。其使用c语言编写,官方使用的镜像为gcr.io/google_containers/pause-amd64:3.1
kubernetes中的pause容器主要为每个业务容器提供以下功能:
PID命名空间:Pod中的不同应用程序可以看到其他应用程序的进程ID。
网络命名空间:Pod中的多个容器能够访问同一个IP和端口范围。
IPC命名空间:Pod中的多个容器能够使用SystemV IPC或POSIX消息队列进行通信。
UTS命名空间:Pod中的多个容器共享一个主机名;Volumes(共享存储卷):
Pod中的各个容器可以访问在Pod级别定义的Volumes。
我们可以给每台node的kubelet服务启动参数加上--pod_infra_container_image参数,指定pause镜像地址。例如指定为官方基础镜像:
kubelet --kubeconfig=/mnt/app/kubernetes/conf/kubelet.yaml --hostname-override=192.68.10.37 --logtostderr=false --log-dir=/mnt/logs/k8s-kubelet/ --v=2 --cgroup-driver=systemd --runtime-cgroups=/systemd/system.slice --kubelet-cgroups=/systemd/system.slice --pod_infra_container_image=docker.io/mirrorgooglecontainers/pause-amd64:3.1
由于我们使用私有仓库,需要将其下载,导出或者标记,再push到私有Docker registry中去。
docker pull mirrorgooglecontainers/pause-amd64:3.1
docker tag da86e6ba6ca1 registry.xxxx.com/pause-amd64
docker push registry.xxxx.com/pause-amd64
之后修改--pod_infra_container_image参数指定为私有仓库镜像:
--pod_infra_container_image=registry.xxx.com/pause-amd64
然后重启kubelet服务
2、设置k8s的Secret
k8s的Secret类型
Kubernetes提供了Secret来处理敏感信息,目前Secret的类型有3种:
- Opaque:使用base64编码存储信息,可以通过
base64 --decode
解码获得原始数据,因此安全性弱。 - kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息,作用于Docker registry(nexus),用户下载docker镜像认证使用。
- kubernetes.io/service-account-token:用于被 serviceaccount 引用。serviceaccout 创建时 Kubernetes 会默认创建对应的 secret。Pod 如果使用了 serviceaccount,对应的 secret 会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中。
3、k8s的registry认证Secret
当pod从私用仓库拉取镜像时,k8s集群使用类型为docker-registry
的Secret来提供身份认证,创建一个名为registry-key的Secret,执行如下命令:
$ kubectl delete secret registry-key
$ kubectl -n k8s-dev create secret docker-registry registry-key-secret \
--docker-server=registry.xxxx.com.com \
--docker-username=registry-admin \
--docker-password=registry-admin \
--docker-email=xxxxx@qq.com
注意 -n k8s-dev为指定命名空间,一般搭建k8s集群时,建议新建一个命名空间来隔离资源。
检查Secret: kubectl get secret registry-key-secret -o yaml
通过 base64 对 secret 中的内容解码:
也可以直接读取 ~/.dockercfg 的内容来创建:
$ kubectl create secret docker-registry registry-key-secret --from-file="~/.dockercfg"
4、配置和部署Pod
imagePullSecrets标签指定拉取镜像时的身份验证信息
在创建 Pod 的时候,通过 imagePullSecrets 来引用registry-key-secret:指定拉取镜像时的身份验证信息
springboot-deployment.yml:
apiVersion: v1
kind: Pod
metadata:
name: springbootweb1
spec:
containers:
- name: springbootweb1
image: registry.xxxx.com/springboot:latest
imagePullSecrets:
- name: registry-key-secret
五、问题
问题1:登陆出现401 Unauthorized
Error response from daemon: login attempt to https://registry.xxxx.com/v2/ failed with status: 401 Unauthorized
解决:Docker Bearer Token Realm可用: