Docker运行Studio容器

基于Docker构建快速启用的开发环境,结合 github 保存的初始化配置以及脚本,来实现跨平台studio。

Docker镜像

fedoraproject Dockerfile 在GitHub上提供的 fedora-cloud/Fedora-Dockerfiles 现在已经不在持续开发。不过提供的Dockerfile可以作为参考(实际也不复杂)。

备注

现在Fedora的容器构建都是通过 containerbuildsystem 实现的,需要在 OpenShift Atlas 中部署OpenShift Build System(基于koji)实现容器构建。

Fedora项目提供的Dockerfile可以方便我们快速部署不同的运行环境,你可以通过 fedora-cloud/Fedora-Dockerfiles git仓库clone,也可以直接安装软件包:

sudo dnf -y install fedora-dockerfiles
ls /usr/share/fedora-dockerfiles
  • 快速启动:

    cd /usr/share/fedora-dockerfiles/ssh
    docker build -t fedora-ssh .
    
    docker run --name fedora-ssh --detach -ti -v /sys/fs/cgroup:/sys/fs/cgroup:ro fedora-ssh /usr/sbin/init
    

初始化镜像

  • 先部署一个最基础的镜像fedora,验证运行,只包含最小化运行环境:

docker_studio/fedora/Dockerfile
 1# syntax = docker/dockerfile:1.3
 2FROM fedora:34
 3MAINTAINER huatai
 4
 5ENV container docker
 6
 7#RUN dnf -y update && dnf -y install systemd && dnf clean all
 8#VOLUME [ "/sys/fs/cgroup", "/tmp", "/run" ]
 9
10#CMD ["/sbin/init"]
11
12RUN --mount=type=bind,target=/sys/fs/cgroup \
13    --mount=type=bind,target=/sys/fs/fuse \
14    --mount=type=tmpfs,target=/tmp \
15    --mount=type=tmpfs,target=/run \
16    --mount=type=tmpfs,target=/run/lock \
17    dnf -y update && dnf -y install systemd && dnf clean all
18
19EXPOSE 22 80 443
20
21ENTRYPOINT [ "/usr/lib/systemd/systemd" ]
22CMD [ "log-level=info", "unit=sysinit.target" ]
  • 构建镜像:

    export DOCKER_BUILDKIT=1
    docker build -t local:fedora34-systemd .
    
  • 运行容器:

    docker run --privileged=true --name fedora34-systemd -d -it local:fedora34-systemd
    

备注

这个容器中没有常用的维护工具,所以我们下一步开始自己定制,然后将所有定制命令转换成Dockerfile配置,以便能够复用。

软件包安装参考 CentOS系统管理初始化

ssh服务容器(ssh)

  • 由于fedora的容器镜像默认使用了systemd,安装和启动sshd服务非常容易:

docker_studio/ssh/Dockerfile
 1FROM fedora:34
 2MAINTAINER vincent huatai <vincent@huatai.me>
 3
 4ENV container docker
 5
 6RUN --mount=type=bind,target=/sys/fs/cgroup \
 7    --mount=type=bind,target=/sys/fs/fuse \
 8    --mount=type=tmpfs,target=/tmp \
 9    --mount=type=tmpfs,target=/run \
10    --mount=type=tmpfs,target=/run/lock \
11    dnf -y update && dnf -y install systemd && dnf clean all
12
13RUN dnf -y install openssh-server \
14    which net-tools iputils procps-ng
15
16# iputils: ping arping tracepath ...
17# net-tools: arp ifconfig route ...
18
19#RUN systemctl enable sshd
20
21# add account "admin" and give sudo privilege
22RUN groupadd -g 505 admin
23RUN useradd -g 505 -u 505 -d /home/admin -m admin
24RUN usermod -aG wheel admin
25RUN echo "%wheel        ALL=(ALL)       NOPASSWD: ALL" >> /etc/sudoers
26
27# Add ssh public key for login
28RUN mkdir -p /home/admin/.ssh
29COPY authorized_keys /home/admin/.ssh/authorized_keys
30RUN chown -R admin:admin /home/admin/.ssh
31RUN chmod 600 /home/admin/.ssh/authorized_keys
32RUN chmod 700 /home/admin/.ssh
33
34EXPOSE 22 80 443
35
36ENTRYPOINT [ "/usr/lib/systemd/systemd" ]
37CMD [ "log-level=info", "unit=sysinit.target" ]

备注

ssh容器安装了系统工具,方便维护

  • 构建带有ssh服务的镜像:

    docker build -t local:fedora34-systemd-ssh .
    

访问虚拟机ssh

在macOS上运行的Docker容器实际上是运行在 xhyve - macOS平台的KVM 虚拟机中,这个虚拟机所运行的精简Linux操作系统 Alpine Linux ,使得在macOS主机上无法直接ssh到容器内部(需要通过虚拟机操作系统转发)。

这是比直接在Linux主机上运行docker容器要麻烦一些(多隔离了一层虚拟机),不能像Linux主机上,能够直接看到一个NAT网络接口在Linux物理主机(Linux端IP通常是 172.17.0.1 ),这个NAT网络接口现在在 xhyve - macOS平台的KVM 虚拟机的Linx系统上,所以如果要实现macOS能够访问到容器,需要做一个端口映射(Port Mapping)。

你可以将这个Port Mapping看成Linux主机的端口映射,将容器内部端口映射到Linux虚拟机对外的网络接口上,由于Linux虚拟机对外网络接口和macOS是互通的,我们就能够通过映射访问到容器:

网络流量 => Linux虚拟机回环接口Port =端口映射=> 容器内部服务Port
  • 我们修订以下运行容器命令,增加 -p 222:22 把端口从回环地址映射到容器上:

    docker run --privileged=true --hostname fedora34 --name fedora34 \
        -p 122:22 -p 180:80 -p 1443:443 -dti local:fedora34-systemd-ssh
    

备注

在 Dockerfile 中 EXPOSE 的端口只能是完全相同的输出,只有 docker run 命令参数才能映射成不同端口

完成启动后检查:

docker ps

可以看到:

CONTAINER ID   IMAGE                        COMMAND                  CREATED         STATUS        PORTS                                                                                                               NAMES
57c7cde18f5c   local:fedora34-systemd-ssh   "/usr/lib/systemd/sy…"   2 seconds ago   Up 1 second   0.0.0.0:122->22/tcp, :::122->22/tcp, 0.0.0.0:180->80/tcp, :::180->80/tcp, 0.0.0.0:1443->443/tcp, :::1443->443/tcp   fedora34
  • 新创建容器就是能通过 122 端口访问到容器:

    ssh admin@127.0.0.1 -p 122
    

则通过密钥认证可以登陆容器系统

登陆以后,可以检查验证 systemd 运行情况:

$ ps aux | grep systemd
root           1  0.0  0.0  20396 12044 ?        Ss   17:19   0:00 /usr/lib/systemd/systemd log-level=info unit=sysinit.target
root          23  0.0  0.0  34224 14072 ?        Ss   17:19   0:00 /usr/lib/systemd/systemd-journald
systemd+      33  0.0  0.0  29468 17128 ?        Ss   17:19   0:00 /usr/lib/systemd/systemd-resolved
root          39  0.0  0.0  17956  8896 ?        Ss   17:19   0:00 /usr/lib/systemd/systemd-homed
root          40  0.0  0.0  17792  8944 ?        Ss   17:19   0:00 /usr/lib/systemd/systemd-logind
root          68  0.5  0.0  17540  7724 ?        Ss   17:24   0:00 /usr/lib/systemd/systemd-userdbd
root          69  0.0  0.0  18004  8552 ?        S    17:24   0:00 systemd-userwork
root          70  0.0  0.0  18004  8580 ?        S    17:24   0:00 systemd-userwork
root          71  0.0  0.0  18004  8528 ?        S    17:24   0:00 systemd-userwork
admin         73  0.5  0.0  19548 11012 ?        Ss   17:24   0:00 /usr/lib/systemd/systemd --user
root          86  0.6  0.0  17556  7588 ?        Ss   17:24   0:00 /usr/lib/systemd/systemd-hostnamed
admin        100  0.0  0.0  10424   852 pts/1    S+   17:24   0:00 grep --color=auto systemd

编译开发的软件安装(dev)

接下来我们在这个 fedora-ssh 基础上,添加各种常用的开发软件包,以便构成一个非常容易使用的开发环境,预计安装:

  • gcc

  • automake, autoconf

  • openjdk

  • python

  • go

  • swift

备注

swift语言开发包安装参考 在Linux环境开发Swift

docker_studio/dev/Dockerfile
FROM fedora:34
MAINTAINER vincent huatai <vincent@huatai.me>

ENV container docker

RUN --mount=type=bind,target=/sys/fs/cgroup \
    --mount=type=bind,target=/sys/fs/fuse \
    --mount=type=tmpfs,target=/tmp \
    --mount=type=tmpfs,target=/run \
    --mount=type=tmpfs,target=/run/lock \
    dnf -y update && dnf -y install systemd && dnf clean all

RUN dnf -y install openssh-server which procps-ng nmap-ncat mlocate net-tools file \
           iputils tmux bzip2 sysstat unzip nfs-utils parted lsof bind-utils

RUN dnf -y install gcc gcc-c++ make flex autoconf automake ncurses-devel zlib-devel git

# Python 3.9 is installed default

RUN dnf -y install java-latest-openjdk

RUN dnf -y install golang

RUN dnf -y install swift-lang

# add account "admin" and give sudo privilege
RUN groupadd -g 505 admin
RUN useradd -g 505 -u 505 -d /home/admin -m admin
RUN usermod -aG wheel admin
RUN echo "%wheel        ALL=(ALL)       NOPASSWD: ALL" >> /etc/sudoers

# Add ssh public key for login
RUN mkdir -p /home/admin/.ssh
COPY authorized_keys /home/admin/.ssh/authorized_keys
RUN chown -R admin:admin /home/admin/.ssh
RUN chmod 600 /home/admin/.ssh/authorized_keys
RUN chmod 700 /home/admin/.ssh

# golang env
USER admin
RUN mkdir /home/admin/go
RUN echo 'export GOPATH=$HOME/go' >> $HOME/.bashrc

EXPOSE 22 80 443

ENTRYPOINT [ "/usr/lib/systemd/systemd" ]
CMD [ "log-level=info", "unit=sysinit.target" ]

备注

请注意,当前这个Dockerfile仅仅是安装了开发所需的一些软件包,并没有解决容器销毁以后数据的丢失,这样小心翼翼使用尚可,但是非常容易丢失数据。所以,我们需要通过 Docker 卷 把开发环境的数据持久化,并且能够直接输出到物理主机(macOS环境)中,方便我们数据备份和在物理主机上同样共享数据。

  • 初始化镜像:

    docker build -t local:fedora34-dev .
    
  • 启动容器:

    docker run --privileged=true --hostname fedora34-dev --name fedora34-dev \
        -p 122:22 -p 180:80 -p 1443:443 -dti local:fedora34-dev
    

这里遇到一个奇怪的问题,我确实使用了 --privileged=true ,这个参数可以让我在之前运行 fedora34 (基于 local:fedora34-systemd-ssh 镜像) 没有问题,但是现在再次出现报错:

Failed to mount tmpfs at /run: Operation not permitted
[!!!!!!] Failed to mount API filesystems.
Exiting PID 1...

这个问题待查…

2020年的问题排查见 Docker Desktop for macOS文件共享

容器持久化数据存储(dev-data)

简单的bind mount

对于单机运行工作平台,直接将物理主机的 home 目录映射进容器内部:

docker run --privileged=true --hostname fedora34-dev --name fedora34-dev \
    -p 222:22 -p 280:80 -p 2443:443 -v /home/huatai/dev:/home/admin/dev \
    -dti local:fedora34-systemd-ssh

启动以后检查:

docker ps

可以看到:

CONTAINER ID   IMAGE                        COMMAND                  CREATED         STATUS        PORTS                                                                                                               NAMES
abd0f1fc7ae6   local:fedora34-systemd-ssh   "/usr/lib/systemd/sy…"   2 seconds ago   Up 1 second   0.0.0.0:222->22/tcp, :::222->22/tcp, 0.0.0.0:280->80/tcp, :::280->80/tcp, 0.0.0.0:2443->443/tcp, :::2443->443/tcp   fedora34-dev

备注

目前我采用这个开发环境进行学习

开发环境部署参考 Fedora 构建不同的语言开发环境

  • 为了能够在物理服务器重启时自动重启容器,设置 自动启动Docker容器 (修订运行命令):

    docker run --privileged=true --hostname fedora34-dev --name fedora34-dev \
        -p 222:22 -p 280:80 -p 2443:443 -v /home/huatai/dev:/home/admin/dev --restart unless-stopped \
        -dti local:fedora34-systemd-ssh
    

多容器共享NFS

为了能够在容器销毁并重建等常见操作情况下,不丢失自己辛苦开发工作的数据,我构想了一个方案:

  • 在macOS操作系统上配置 macOS系统NFS服务 ,将用户的 $HOME 目录下子目录 home_admin 作为NFS服务卷输出给Docker环境(注意配置内部网络IP访问确保安全) - (遗憾,似乎不好实现)

  • 在Docker中配置 Docker容器使用NFS ,将容器的 /home/admin 目录bind到NFS挂载的目录下,这样所有容器中数据都能够直接存储到物理主机的macOS系统的用户目录

    • 为了避免多个容器 数据踩踏 ,需要每个容器单独创建一个子目录,分别映射,例如 dev1 容器使用的是 home_admin/dev1dev2 容器使用是 home_admin/dev2

    • 如果是公用的数据目录,例如代码仓库,则采用共享的挂在目录

  • 在物理主机上采用定时备份同步方法,能够实现数据不丢失

我在macOS上中尚未找到如何在物理主机和Docker VM之间共享NFS的方法,所以在macOS上修订成 Docker Desktop for macOS文件共享

在Linux主机上,非常容易构建NFS共享,所以这个方案可以采用 Docker容器使用NFS 来实现

开发环境框架搭建(studio)

在开发环境中,还有非常重要的部署框架:

我希望能够快速完成很多我学习和使用的开发环境构建,这块我会不断补充

备注

更为复杂的部署环境,可以集成到一个容器中,也可以分散到不同容器采用 Kubernetes Atlas 实现,这块将不在studio段落展开,我将构建一个部署到生产环境到持续集成,并且结合:

  • 负载均衡

  • 反向代理

  • 缓存

  • 消息队列

参考