我有一个应用程序,执行各种有趣的东西与Git(像运行Git克隆& Git推送),我试图docker-ize它。

我遇到了一个问题,虽然我需要能够添加一个SSH密钥到容器的容器“用户”使用。

我试着把它复制到/root/。ssh/,更改$HOME,创建一个git ssh包装器,仍然没有运气。

以下是Dockerfile供参考:

#DOCKER-VERSION 0.3.4                                                           

from  ubuntu:12.04                                                              

RUN  apt-get update                                                             
RUN  apt-get install python-software-properties python g++ make git-core openssh-server -y
RUN  add-apt-repository ppa:chris-lea/node.js                                   
RUN  echo "deb http://archive.ubuntu.com/ubuntu precise universe" >> /etc/apt/sources.list
RUN  apt-get update                                                             
RUN  apt-get install nodejs -y                                                  

ADD . /src                                                                       
ADD ../../home/ubuntu/.ssh/id_rsa /root/.ssh/id_rsa                             
RUN   cd /src; npm install                                                      

EXPOSE  808:808                                                                 

CMD   [ "node", "/src/app.js"]

App.js运行git命令,比如git pull


当前回答

在docker(17.05)的后续版本中,您可以使用多阶段构建。哪个是最安全的选择,因为之前的构建只能被后续的构建使用,然后被销毁

有关更多信息,请参阅我的stackoverflow问题的答案

其他回答

我试图用另一种方式解决这个问题:向映像添加公共ssh密钥。但在我的试验中,我发现“docker cp”是用于从容器复制到主机的。creak回答的第3项似乎在说你可以使用docker cp向容器中注入文件。参见https://docs.docker.com/engine/reference/commandline/cp/

摘录

将文件/文件夹从容器的文件系统复制到主机路径。 路径相对于文件系统的根目录。 用途:docker cp CONTAINER:PATH HOSTPATH 将PATH中的文件/文件夹复制到HOSTPATH中

如果你正在使用Docker Compose,一个简单的选择是像这样转发SSH代理:

something:
    container_name: something
    volumes:
        - $SSH_AUTH_SOCK:/ssh-agent # Forward local machine SSH key to docker
    environment:
        SSH_AUTH_SOCK: /ssh-agent

或者等效地,如果使用docker run:

$ docker run --mount type=bind,source=$SSH_AUTH_SOCK,target=/ssh-agent \
             --env SSH_AUTH_SOCK=/ssh-agent \
             some-image

我把一个非常简单的解决方案放在一起,它适用于我的用例,我使用一个“builder”docker映像来构建一个单独部署的可执行文件。换句话说,我的“构建器”映像永远不会离开我的本地机器,并且只需要在构建阶段访问私有回购/依赖。

对于这个解决方案,您不需要更改Dockerfile。

运行容器时,挂载~/。SSH目录(这避免了必须将键直接烘焙到映像中,而是确保它们在构建阶段只在短时间内对单个容器实例可用)。在我的例子中,我有几个构建脚本来自动化我的部署。

在我的build-and-package.sh脚本中,我像这样运行容器:

# do some script stuff before    

...

docker run --rm \
   -v ~/.ssh:/root/.ssh \
   -v "$workspace":/workspace \
   -w /workspace builder \
   bash -cl "./scripts/build-init.sh $executable"

...

# do some script stuff after (i.e. pull the built executable out of the workspace, etc.)

build-init.sh脚本如下所示:

#!/bin/bash

set -eu

executable=$1

# start the ssh agent
eval $(ssh-agent) > /dev/null

# add the ssh key (ssh key should not have a passphrase)
ssh-add /root/.ssh/id_rsa

# execute the build command
swift build --product $executable -c release

因此,我们不是直接在docker run命令中执行swift构建命令(或任何与您的环境相关的构建命令),而是执行build-init.sh脚本,该脚本启动ssh-agent,然后将ssh密钥添加到代理,最后执行swift构建命令。

注1:为了使其工作,您需要确保您的ssh密钥没有密码短语,否则ssh-add /root/。Ssh /id_rsa行将要求口令并中断构建脚本。

注意2:确保在脚本文件上设置了适当的文件权限,以便它们可以运行。

希望这为具有类似用例的其他人提供了一个简单的解决方案。

扩展Peter Grainger的回答,我可以使用Docker 17.05以来可用的多级构建。官方页面说明:

对于多阶段构建,您可以在Dockerfile中使用多个FROM语句。每个FROM指令都可以使用不同的基,并且每个FROM指令都开始构建的一个新阶段。您可以有选择地将工件从一个阶段复制到另一个阶段,在最终图像中留下您不想要的所有东西。

请记住这一点,这里是我的Dockerfile示例,包括三个构建阶段。它的目的是创建客户端web应用程序的生产映像。

# Stage 1: get sources from npm and git over ssh
FROM node:carbon AS sources
ARG SSH_KEY
ARG SSH_KEY_PASSPHRASE
RUN mkdir -p /root/.ssh && \
    chmod 0700 /root/.ssh && \
    ssh-keyscan bitbucket.org > /root/.ssh/known_hosts && \
    echo "${SSH_KEY}" > /root/.ssh/id_rsa && \
    chmod 600 /root/.ssh/id_rsa
WORKDIR /app/
COPY package*.json yarn.lock /app/
RUN eval `ssh-agent -s` && \
    printf "${SSH_KEY_PASSPHRASE}\n" | ssh-add $HOME/.ssh/id_rsa && \
    yarn --pure-lockfile --mutex file --network-concurrency 1 && \
    rm -rf /root/.ssh/

# Stage 2: build minified production code
FROM node:carbon AS production
WORKDIR /app/
COPY --from=sources /app/ /app/
COPY . /app/
RUN yarn build:prod

# Stage 3: include only built production files and host them with Node Express server
FROM node:carbon
WORKDIR /app/
RUN yarn add express
COPY --from=production /app/dist/ /app/dist/
COPY server.js /app/
EXPOSE 33330
CMD ["node", "server.js"]

.dockerignore重复.gitignore文件的内容(它防止项目的node_modules和结果dist目录被复制):

.idea
dist
node_modules
*.log

创建镜像的命令示例:

$ docker build -t ezze/geoport:0.6.0 \
  --build-arg SSH_KEY="$(cat ~/.ssh/id_rsa)" \
  --build-arg SSH_KEY_PASSPHRASE="my_super_secret" \
  ./

如果您的私钥没有密码短语,请指定空的SSH_KEY_PASSPHRASE参数。

它是这样工作的:

1).只对第一级包装。json,纱线。锁文件和私有SSH密钥被复制到第一个名为sources的中间映像。为了避免进一步的SSH密钥口令提示,它被自动添加到SSH -agent。最后yarn命令从NPM安装所有需要的依赖项,并通过SSH从Bitbucket克隆私有git存储库。

2).第二阶段构建并缩小web应用程序的源代码,并将其放置在下一个名为production的中间映像的dist目录中。注意,安装的node_modules的源代码是从第一阶段生成的名为sources的镜像中复制的:

COPY --from=sources /app/ /app/

也可能是下面这句话:

COPY --from=sources /app/node_modules/ /app/node_modules/

我们只有来自第一个中间映像的node_modules目录,不再有SSH_KEY和SSH_KEY_PASSPHRASE参数。构建所需的所有其余内容都是从项目目录中复制的。

3).在第三阶段,我们减少最终图像的大小,将标记为ezze/geoport:0.6.0,只包括dist目录从第二个中间图像名为生产和安装Node Express启动一个web服务器。

列出图像给出如下输出:

REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ezze/geoport        0.6.0               8e8809c4e996        3 hours ago         717MB
<none>              <none>              1f6518644324        3 hours ago         1.1GB
<none>              <none>              fa00f1182917        4 hours ago         1.63GB
node                carbon              b87c2ad8344d        4 weeks ago         676MB

其中非标记图像对应于第一和第二个中间构建阶段。

如果你跑了

$ docker history ezze/geoport:0.6.0 --no-trunc

在最终的映像中,您不会看到任何提及SSH_KEY和SSH_KEY_PASSPHRASE的内容。

为了在容器中注入ssh密钥,你有多种解决方案:

使用带有ADD指令的Dockerfile,你可以在构建过程中注入它 简单地执行cat id_rsa | docker run -i <image> sh -c 'cat > /root/.ssh/id_rsa' 使用docker cp命令,允许您在容器运行时注入文件。