1
0
mirror of https://github.com/rancher/os.git synced 2025-08-10 02:58:15 +00:00

Optimize image loading to reduce memory footprint and boot time

This commit is contained in:
Jan B 2018-09-09 10:07:50 +02:00 committed by niusmallnan
parent dcce547bad
commit e5b1643bfa
7 changed files with 67 additions and 57 deletions

View File

@ -21,7 +21,8 @@ const (
SystemDockerHost = "unix:///var/run/system-docker.sock" SystemDockerHost = "unix:///var/run/system-docker.sock"
DockerHost = "unix:///var/run/docker.sock" DockerHost = "unix:///var/run/docker.sock"
ImagesPath = "/usr/share/ros" ImagesPath = "/usr/share/ros"
ImagesPattern = "images*.tar" InitImages = "images-init.tar"
SystemImages = "images-system.tar"
ModulesArchive = "/modules.tar" ModulesArchive = "/modules.tar"
Debug = false Debug = false
SystemDockerLog = "/var/log/system-docker.log" SystemDockerLog = "/var/log/system-docker.log"

View File

@ -68,7 +68,7 @@ func bootstrap(cfg *config.CloudConfig) error {
_, err = config.ChainCfgFuncs(cfg, _, err = config.ChainCfgFuncs(cfg,
[]config.CfgFuncData{ []config.CfgFuncData{
config.CfgFuncData{"bootstrap loadImages", loadImages}, config.CfgFuncData{"bootstrap loadImages", loadBootstrapImages},
config.CfgFuncData{"bootstrap Services", bootstrapServices}, config.CfgFuncData{"bootstrap Services", bootstrapServices},
}) })
return err return err
@ -84,7 +84,7 @@ func runCloudInitServices(cfg *config.CloudConfig) error {
_, err = config.ChainCfgFuncs(cfg, _, err = config.ChainCfgFuncs(cfg,
[]config.CfgFuncData{ []config.CfgFuncData{
config.CfgFuncData{"cloudinit loadImages", loadImages}, config.CfgFuncData{"cloudinit loadImages", loadBootstrapImages},
config.CfgFuncData{"cloudinit Services", runCloudInitServiceSet}, config.CfgFuncData{"cloudinit Services", runCloudInitServiceSet},
}) })
return err return err

View File

@ -87,7 +87,7 @@ func recovery(initFailure error) {
_, err = config.ChainCfgFuncs(&recoveryConfig, _, err = config.ChainCfgFuncs(&recoveryConfig,
[]config.CfgFuncData{ []config.CfgFuncData{
config.CfgFuncData{"loadImages", loadImages}, config.CfgFuncData{"loadSystemImages", loadSystemImages},
config.CfgFuncData{"recovery console", recoveryServices}, config.CfgFuncData{"recovery console", recoveryServices},
}) })
if err != nil { if err != nil {

View File

@ -4,6 +4,7 @@ import (
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath"
"syscall" "syscall"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -29,62 +30,47 @@ func hasImage(name string) bool {
return true return true
} }
func findImages(cfg *config.CloudConfig) ([]string, error) { func getImagesArchive(bootstrap bool) string {
log.Debugf("Looking for images at %s", config.ImagesPath) var archive string
if bootstrap {
result := []string{} archive = path.Join(config.ImagesPath, config.InitImages)
} else {
dir, err := os.Open(config.ImagesPath) archive = path.Join(config.ImagesPath, config.SystemImages)
if os.IsNotExist(err) {
log.Debugf("Not loading images, %s does not exist", config.ImagesPath)
return result, nil
}
if err != nil {
return nil, err
} }
defer dir.Close() return archive
files, err := dir.Readdirnames(0)
if err != nil {
return nil, err
}
for _, fileName := range files {
if ok, _ := path.Match(config.ImagesPattern, fileName); ok {
log.Debugf("Found %s", fileName)
result = append(result, fileName)
}
}
return result, nil
} }
func loadImages(cfg *config.CloudConfig) (*config.CloudConfig, error) { func loadBootstrapImages(cfg *config.CloudConfig) (*config.CloudConfig, error) {
images, err := findImages(cfg) return loadImages(cfg, true)
if err != nil || len(images) == 0 { }
return cfg, err
} func loadSystemImages(cfg *config.CloudConfig) (*config.CloudConfig, error) {
return loadImages(cfg, false)
}
func loadImages(cfg *config.CloudConfig, bootstrap bool) (*config.CloudConfig, error) {
archive := getImagesArchive(bootstrap)
client, err := docker.NewSystemClient() client, err := docker.NewSystemClient()
if err != nil { if err != nil {
return cfg, err return cfg, err
} }
for _, image := range images { if !hasImage(filepath.Base(archive)) {
if hasImage(image) { if _, err := os.Stat(archive); os.IsNotExist(err) {
continue log.Fatalf("FATAL: Could not load images from %s (file not found)", archive)
} }
// client.ImageLoad is an asynchronous operation // client.ImageLoad is an asynchronous operation
// To ensure the order of execution, use cmd instead of it // To ensure the order of execution, use cmd instead of it
inputFileName := path.Join(config.ImagesPath, image) log.Infof("Loading images from %s", archive)
log.Infof("Loading images from %s", inputFileName) cmd := exec.Command("/usr/bin/system-docker", "load", "-q", "-i", archive)
if err = exec.Command("/usr/bin/system-docker", "load", "-q", "-i", inputFileName).Run(); err != nil { if out, err := cmd.CombinedOutput(); err != nil {
log.Fatalf("FATAL: failed loading images from %s: %s", inputFileName, err) log.Fatalf("FATAL: Error loading images from %s (%v)\n%s ", archive, err, out)
} }
log.Infof("Done loading images from %s", inputFileName) log.Infof("Done loading images from %s", archive)
} }
dockerImages, _ := client.ImageList(context.Background(), types.ImageListOptions{}) dockerImages, _ := client.ImageList(context.Background(), types.ImageListOptions{})
@ -104,7 +90,7 @@ func SysInit() error {
_, err := config.ChainCfgFuncs(cfg, _, err := config.ChainCfgFuncs(cfg,
[]config.CfgFuncData{ []config.CfgFuncData{
config.CfgFuncData{"loadImages", loadImages}, config.CfgFuncData{"loadSystemImages", loadSystemImages},
config.CfgFuncData{"start project", func(cfg *config.CloudConfig) (*config.CloudConfig, error) { config.CfgFuncData{"start project", func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
p, err := compose.GetProject(cfg, false, true) p, err := compose.GetProject(cfg, false, true)
if err != nil { if err != nil {

View File

@ -7,7 +7,7 @@ mkdir -p ${INITRD_DIR}/usr/{etc,lib,bin,share/ros}
./scripts/template ./scripts/template
cp -rf assets/selinux ${INITRD_DIR}/usr/etc cp -rf assets/selinux ${INITRD_DIR}/usr/etc
cp build/images.tar ${INITRD_DIR}/usr/share/ros/ cp build/images*.tar ${INITRD_DIR}/usr/share/ros/
cp bin/ros ${INITRD_DIR}/usr/bin/ cp bin/ros ${INITRD_DIR}/usr/bin/
ln -s usr/bin/ros ${INITRD_DIR}/init ln -s usr/bin/ros ${INITRD_DIR}/init
ln -s bin ${INITRD_DIR}/usr/sbin ln -s bin ${INITRD_DIR}/usr/sbin

View File

@ -12,24 +12,28 @@ PREPOP_DIR=${IMAGE_CACHE}/var/lib/system-docker
INITRD_DIR=${BUILD}/initrd INITRD_DIR=${BUILD}/initrd
ARTIFACTS=$(pwd)/dist/artifacts ARTIFACTS=$(pwd)/dist/artifacts
INITRD=${ARTIFACTS}/initrd INITRD=${ARTIFACTS}/initrd
INIT_IMAGES="images-init.tar"
SYSTEM_IMAGES="images-system.tar"
mkdir -p ${ARTIFACTS} ${PREPOP_DIR} mkdir -p ${ARTIFACTS} ${PREPOP_DIR}
if [ "$(docker info | grep 'Storage Driver: ' | sed 's/Storage Driver: //')" != "overlay" ]; then if [ "$(docker info | grep 'Storage Driver: ' | sed 's/Storage Driver: //')" != "overlay" ]; then
echo Overlay storage driver is required to prepackage exploded images echo Overlay storage driver is require to prepackage exploded images
echo packaging images.tar instead echo packaging image tar archives instead
tar czf ${ARTIFACTS}/rootfs${SUFFIX}.tar.gz --exclude lib/modules --exclude lib/firmware -C ${INITRD_DIR} . tar czf ${ARTIFACTS}/rootfs${SUFFIX}.tar.gz --exclude lib/modules --exclude lib/firmware -C ${INITRD_DIR} .
exit 0 exit 0
fi fi
DFS=$(docker run -d --privileged -v /lib/modules/$(uname -r):/lib/modules/$(uname -r) ${DFS_IMAGE}${SUFFIX} ${DFS_ARGS}) DFS=$(docker run -d --privileged -v /lib/modules/$(uname -r):/lib/modules/$(uname -r) ${DFS_IMAGE}${SUFFIX} ${DFS_ARGS})
trap "docker rm -fv ${DFS_ARCH} ${DFS}" EXIT trap "docker rm -fv ${DFS_ARCH} ${DFS}" EXIT
docker exec -i ${DFS} docker load < ${INITRD_DIR}/usr/share/ros/images.tar docker exec -i ${DFS} docker load < ${INITRD_DIR}/usr/share/ros/${INIT_IMAGES}
docker exec -i ${DFS} docker load < ${INITRD_DIR}/usr/share/ros/${SYSTEM_IMAGES}
docker stop ${DFS} docker stop ${DFS}
docker run --rm --volumes-from=${DFS} --entrypoint /bin/bash rancher/os-base -c "tar -c -C /var/lib/docker ./image" | tar -x -C ${PREPOP_DIR} docker run --rm --volumes-from=${DFS} --entrypoint /bin/bash rancher/os-base -c "tar -c -C /var/lib/docker ./image" | tar -x -C ${PREPOP_DIR}
docker run --rm --volumes-from=${DFS} --entrypoint /bin/bash rancher/os-base -c "tar -c -C /var/lib/docker ./overlay" | tar -x -C ${PREPOP_DIR} docker run --rm --volumes-from=${DFS} --entrypoint /bin/bash rancher/os-base -c "tar -c -C /var/lib/docker ./overlay" | tar -x -C ${PREPOP_DIR}
tar -cf ${ARTIFACTS}/rootfs${SUFFIX}.tar --exclude usr/share/ros/images.tar --exclude lib/modules --exclude lib/firmware -C ${INITRD_DIR} . tar -cf ${ARTIFACTS}/rootfs${SUFFIX}.tar --exclude usr/share/ros/${INIT_IMAGES} --exclude usr/share/ros/${SYSTEM_IMAGES} --exclude lib/modules --exclude
lib/firmware -C ${INITRD_DIR} .
tar -rf ${ARTIFACTS}/rootfs${SUFFIX}.tar -C ${IMAGE_CACHE} . tar -rf ${ARTIFACTS}/rootfs${SUFFIX}.tar -C ${IMAGE_CACHE} .
rm -f ${ARTIFACTS}/rootfs${SUFFIX}.tar.gz rm -f ${ARTIFACTS}/rootfs${SUFFIX}.tar.gz
gzip ${ARTIFACTS}/rootfs${SUFFIX}.tar gzip ${ARTIFACTS}/rootfs${SUFFIX}.tar

View File

@ -1,29 +1,48 @@
#!/bin/bash #!/bin/bash
set -ex set -ex
INIT_DEP="rancher/os-bootstrap"
SHARED_DEP="rancher/os-base"
INIT_IMAGES_DST="build/images-init.tar"
SYSTEM_IMAGES_DST="build/images-system.tar"
cd $(dirname $0)/.. cd $(dirname $0)/..
IMAGES=$(bin/host_ros c images -i build/initrd/usr/share/ros/os-config.yml) IMAGES=$(bin/host_ros c images -i build/initrd/usr/share/ros/os-config.yml)
echo "tar-image: IMAGES=$IMAGES" INIT_IMAGES=""
for i in $IMAGES; do SYSTEM_IMAGES=""
for i in ${IMAGES}; do
echo "tar-image: pull($i)" echo "tar-image: pull($i)"
if [ "${FORCE_PULL}" = "1" ] || ! docker inspect $i >/dev/null 2>&1; then if [ "${FORCE_PULL}" = "1" ] || ! docker inspect $i >/dev/null 2>&1; then
docker pull $i docker pull ${i}
fi
if [ "${i%%:*}" != "$INIT_DEP" ] ; then
SYSTEM_IMAGES="$SYSTEM_IMAGES $i"
fi
if [ "${i%%:*}" = "$INIT_DEP" ] || [ "${i%%:*}" = "$SHARED_DEP" ] ; then
INIT_IMAGES="$INIT_IMAGES $i"
fi fi
done done
if [ -e ".make-vmware" ]; then if [ -e ".make-vmware" ]; then
docker pull rancher/os-openvmtools:${OPEN_VMTOOLS_VERSION} docker pull rancher/os-openvmtools:${OPEN_VMTOOLS_VERSION}
IMAGES="$IMAGES rancher/os-openvmtools:${OPEN_VMTOOLS_VERSION}" SYSTEM_IMAGES="$SYSTEM_IMAGES rancher/os-openvmtools:${OPEN_VMTOOLS_VERSION}"
fi fi
echo "tar-images: docker save ${IMAGES}" echo "tar-image: SYSTEM_IMAGES=$SYSTEM_IMAGES"
echo "tar-image: INIT_IMAGES=$INIT_IMAGES"
if [ "$COMPRESS" == "" ]; then if [ "$COMPRESS" == "" ]; then
docker save ${IMAGES} | gzip > build/images.tar ARCHIVE_CMD="gzip"
else else
# system-docker can not load images which compressed by xz with a compression level of 9 # system-docker can not load images which compressed by xz with a compression level of 9
# decompression consumes more memory if using level 9 # decompression consumes more memory if using level 9
# the default compression level for xz is 6 # the default compression level for xz is 6
docker save ${IMAGES} | xz -6 -e > build/images.tar ARCHIVE_CMD="xz -4 -e"
fi fi
docker save ${INIT_IMAGES} | ${ARCHIVE_CMD} > ${INIT_IMAGES_DST}
docker save ${SYSTEM_IMAGES} | ${ARCHIVE_CMD} > ${SYSTEM_IMAGES_DST}
echo "tar-images: DONE" echo "tar-images: DONE"