1
0
mirror of https://github.com/rancher/os.git synced 2025-06-25 14:31:33 +00:00

Early cloud-init

This commit is contained in:
Josh Curl 2016-11-09 11:08:30 -08:00
parent 72f7f9a572
commit 13b34a6668
No known key found for this signature in database
GPG Key ID: 82B504B9BCCFA677
18 changed files with 312 additions and 249 deletions

View File

@ -17,10 +17,10 @@ package cloudinitsave
import ( import (
"errors" "errors"
"flag"
"os" "os"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
yaml "github.com/cloudfoundry-incubator/candiedyaml" yaml "github.com/cloudfoundry-incubator/candiedyaml"
@ -36,7 +36,9 @@ import (
"github.com/coreos/coreos-cloudinit/datasource/proc_cmdline" "github.com/coreos/coreos-cloudinit/datasource/proc_cmdline"
"github.com/coreos/coreos-cloudinit/datasource/url" "github.com/coreos/coreos-cloudinit/datasource/url"
"github.com/coreos/coreos-cloudinit/pkg" "github.com/coreos/coreos-cloudinit/pkg"
"github.com/docker/docker/pkg/mount"
"github.com/rancher/os/cmd/cloudinitsave/gce" "github.com/rancher/os/cmd/cloudinitsave/gce"
"github.com/rancher/os/cmd/network"
rancherConfig "github.com/rancher/os/config" rancherConfig "github.com/rancher/os/config"
"github.com/rancher/os/netconf" "github.com/rancher/os/netconf"
"github.com/rancher/os/util" "github.com/rancher/os/util"
@ -46,73 +48,42 @@ const (
datasourceInterval = 100 * time.Millisecond datasourceInterval = 100 * time.Millisecond
datasourceMaxInterval = 30 * time.Second datasourceMaxInterval = 30 * time.Second
datasourceTimeout = 5 * time.Minute datasourceTimeout = 5 * time.Minute
configDevName = "config-2"
configDev = "LABEL=" + configDevName
configDevMountPoint = "/media/config-2"
) )
var (
network bool
flags *flag.FlagSet
)
func init() {
flags = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
flags.BoolVar(&network, "network", true, "use network based datasources")
}
func Main() { func Main() {
flags.Parse(os.Args[1:]) log.Info("Running cloud-init-save")
log.Infof("Running cloud-init-save: network=%v", network) cfg := rancherConfig.LoadConfig()
network.ApplyNetworkConfig(cfg)
if err := saveCloudConfig(); err != nil { if err := SaveCloudConfig(true); err != nil {
log.Errorf("Failed to save cloud-config: %v", err) log.Errorf("Failed to save cloud-config: %v", err)
} }
} }
func saveFiles(cloudConfigBytes, scriptBytes []byte, metadata datasource.Metadata) error { func MountConfigDrive() error {
os.MkdirAll(rancherConfig.CloudConfigDir, os.ModeDir|0600) if err := os.MkdirAll(configDevMountPoint, 644); err != nil {
if len(scriptBytes) > 0 {
log.Infof("Writing to %s", rancherConfig.CloudConfigScriptFile)
if err := util.WriteFileAtomic(rancherConfig.CloudConfigScriptFile, scriptBytes, 500); err != nil {
log.Errorf("Error while writing file %s: %v", rancherConfig.CloudConfigScriptFile, err)
return err
}
}
if len(cloudConfigBytes) > 0 {
if err := util.WriteFileAtomic(rancherConfig.CloudConfigBootFile, cloudConfigBytes, 400); err != nil {
return err
}
log.Infof("Written to %s:\n%s", rancherConfig.CloudConfigBootFile, string(cloudConfigBytes))
}
metaDataBytes, err := yaml.Marshal(metadata)
if err != nil {
return err return err
} }
if err = util.WriteFileAtomic(rancherConfig.MetaDataFile, metaDataBytes, 400); err != nil { configDev := util.ResolveDevice(configDev)
return err
}
log.Infof("Written to %s:\n%s", rancherConfig.MetaDataFile, string(metaDataBytes))
return nil if configDev == "" {
return mount.Mount(configDevName, configDevMountPoint, "9p", "trans=virtio,version=9p2000.L")
}
return mount.Mount(configDev, configDevMountPoint, "iso9660,vfat", "")
} }
func currentDatasource() (datasource.Datasource, error) { func UnmountConfigDrive() error {
cfg := rancherConfig.LoadConfig() return syscall.Unmount(configDevMountPoint, 0)
dss := getDatasources(cfg)
if len(dss) == 0 {
return nil, nil
}
ds := selectDatasource(dss)
return ds, nil
} }
func saveCloudConfig() error { func SaveCloudConfig(network bool) error {
userDataBytes, metadata, err := fetchUserData() userDataBytes, metadata, err := fetchUserData(network)
if err != nil { if err != nil {
return err return err
} }
@ -146,9 +117,67 @@ func saveCloudConfig() error {
return saveFiles(userDataBytes, scriptBytes, metadata) return saveFiles(userDataBytes, scriptBytes, metadata)
} }
func fetchUserData() ([]byte, datasource.Metadata, error) { func RequiresNetwork(datasource string) bool {
parts := strings.SplitN(datasource, ":", 2)
requiresNetwork, ok := map[string]bool{
"ec2": true,
"file": false,
"url": true,
"cmdline": true,
"configdrive": false,
"digitalocean": true,
"gce": true,
"packet": true,
}[parts[0]]
return ok && requiresNetwork
}
func saveFiles(cloudConfigBytes, scriptBytes []byte, metadata datasource.Metadata) error {
os.MkdirAll(rancherConfig.CloudConfigDir, os.ModeDir|0600)
if len(scriptBytes) > 0 {
log.Infof("Writing to %s", rancherConfig.CloudConfigScriptFile)
if err := util.WriteFileAtomic(rancherConfig.CloudConfigScriptFile, scriptBytes, 500); err != nil {
log.Errorf("Error while writing file %s: %v", rancherConfig.CloudConfigScriptFile, err)
return err
}
}
if len(cloudConfigBytes) > 0 {
if err := util.WriteFileAtomic(rancherConfig.CloudConfigBootFile, cloudConfigBytes, 400); err != nil {
return err
}
log.Infof("Written to %s:\n%s", rancherConfig.CloudConfigBootFile, string(cloudConfigBytes))
}
metaDataBytes, err := yaml.Marshal(metadata)
if err != nil {
return err
}
if err = util.WriteFileAtomic(rancherConfig.MetaDataFile, metaDataBytes, 400); err != nil {
return err
}
log.Infof("Written to %s:\n%s", rancherConfig.MetaDataFile, string(metaDataBytes))
return nil
}
func currentDatasource(network bool) (datasource.Datasource, error) {
cfg := rancherConfig.LoadConfig()
dss := getDatasources(cfg, network)
if len(dss) == 0 {
return nil, nil
}
ds := selectDatasource(dss)
return ds, nil
}
func fetchUserData(network bool) ([]byte, datasource.Metadata, error) {
var metadata datasource.Metadata var metadata datasource.Metadata
ds, err := currentDatasource() ds, err := currentDatasource(network)
if err != nil || ds == nil { if err != nil || ds == nil {
log.Errorf("Failed to select datasource: %v", err) log.Errorf("Failed to select datasource: %v", err)
return nil, metadata, err return nil, metadata, err
@ -170,7 +199,7 @@ func fetchUserData() ([]byte, datasource.Metadata, error) {
// getDatasources creates a slice of possible Datasources for cloudinit based // getDatasources creates a slice of possible Datasources for cloudinit based
// on the different source command-line flags. // on the different source command-line flags.
func getDatasources(cfg *rancherConfig.CloudConfig) []datasource.Datasource { func getDatasources(cfg *rancherConfig.CloudConfig, network bool) []datasource.Datasource {
dss := make([]datasource.Datasource, 0, 5) dss := make([]datasource.Datasource, 0, 5)
for _, ds := range cfg.Rancher.CloudInit.Datasources { for _, ds := range cfg.Rancher.CloudInit.Datasources {

View File

@ -1,54 +1,24 @@
package network package network
import ( import (
"flag"
"os"
"golang.org/x/net/context"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/resolvconf" "github.com/docker/libnetwork/resolvconf"
"github.com/rancher/os/config" "github.com/rancher/os/config"
"github.com/rancher/os/docker"
"github.com/rancher/os/hostname" "github.com/rancher/os/hostname"
"github.com/rancher/os/netconf" "github.com/rancher/os/netconf"
) )
var (
stopNetworkPre bool
flags *flag.FlagSet
)
func init() {
flags = flag.NewFlagSet(os.Args[0], flag.ContinueOnError)
flags.BoolVar(&stopNetworkPre, "stop-network-pre", false, "")
}
func Main() { func Main() {
flags.Parse(os.Args[1:]) log.Infof("Running network")
log.Infof("Running network: stop-network-pre=%v", stopNetworkPre)
if stopNetworkPre {
client, err := docker.NewSystemClient()
if err != nil {
log.Error(err)
}
err = client.ContainerStop(context.Background(), "network-pre", 10)
if err != nil {
log.Error(err)
}
_, err = client.ContainerWait(context.Background(), "network-pre")
if err != nil {
log.Error(err)
}
}
cfg := config.LoadConfig() cfg := config.LoadConfig()
ApplyNetworkConfig(cfg)
select {}
}
func ApplyNetworkConfig(cfg *config.CloudConfig) {
nameservers := cfg.Rancher.Network.Dns.Nameservers nameservers := cfg.Rancher.Network.Dns.Nameservers
search := cfg.Rancher.Network.Dns.Search search := cfg.Rancher.Network.Dns.Search
userSetDns := len(nameservers) > 0 || len(search) > 0 userSetDns := len(nameservers) > 0 || len(search) > 0
@ -77,6 +47,4 @@ func Main() {
if err := hostname.SyncHostname(); err != nil { if err := hostname.SyncHostname(); err != nil {
log.Error(err) log.Error(err)
} }
select {}
} }

View File

@ -201,7 +201,7 @@ func newCoreServiceProject(cfg *config.CloudConfig, useNetwork, loadConsole bool
go func() { go func() {
for event := range projectEvents { for event := range projectEvents {
if event.EventType == events.ContainerStarted && event.ServiceName == "ntp" { if event.EventType == events.ContainerStarted && event.ServiceName == "network" {
useNetwork = true useNetwork = true
} }
} }

View File

@ -18,7 +18,7 @@ func Merge(bytes []byte) error {
} }
func Export(private, full bool) (string, error) { func Export(private, full bool) (string, error) {
rawCfg := loadRawDiskConfig(full) rawCfg := loadRawDiskConfig("", full)
if !private { if !private {
rawCfg = filterPrivateKeys(rawCfg) rawCfg = filterPrivateKeys(rawCfg)
} }

View File

@ -31,27 +31,32 @@ func ReadConfig(bytes []byte, substituteMetadataVars bool, files ...string) (*Cl
return c, nil return c, nil
} }
func loadRawDiskConfig(full bool) map[interface{}]interface{} { func loadRawDiskConfig(dirPrefix string, full bool) map[interface{}]interface{} {
var rawCfg map[interface{}]interface{} var rawCfg map[interface{}]interface{}
if full { if full {
rawCfg, _ = readConfigs(nil, true, false, OsConfigFile, OemConfigFile) rawCfg, _ = readConfigs(nil, true, false, OsConfigFile, OemConfigFile)
} }
files := append(CloudConfigDirFiles(), CloudConfigFile) files := CloudConfigDirFiles(dirPrefix)
files = append(files, path.Join(dirPrefix, CloudConfigFile))
additionalCfgs, _ := readConfigs(nil, true, false, files...) additionalCfgs, _ := readConfigs(nil, true, false, files...)
return util.Merge(rawCfg, additionalCfgs) return util.Merge(rawCfg, additionalCfgs)
} }
func loadRawConfig() map[interface{}]interface{} { func loadRawConfig(dirPrefix string) map[interface{}]interface{} {
rawCfg := loadRawDiskConfig(true) rawCfg := loadRawDiskConfig(dirPrefix, true)
rawCfg = util.Merge(rawCfg, readCmdline()) rawCfg = util.Merge(rawCfg, readCmdline())
rawCfg = applyDebugFlags(rawCfg) rawCfg = applyDebugFlags(rawCfg)
return mergeMetadata(rawCfg, readMetadata()) return mergeMetadata(rawCfg, readMetadata())
} }
func LoadConfig() *CloudConfig { func LoadConfig() *CloudConfig {
rawCfg := loadRawConfig() return LoadConfigWithPrefix("")
}
func LoadConfigWithPrefix(dirPrefix string) *CloudConfig {
rawCfg := loadRawConfig(dirPrefix)
cfg := &CloudConfig{} cfg := &CloudConfig{}
if err := util.Convert(rawCfg, cfg); err != nil { if err := util.Convert(rawCfg, cfg); err != nil {
@ -63,8 +68,10 @@ func LoadConfig() *CloudConfig {
return cfg return cfg
} }
func CloudConfigDirFiles() []string { func CloudConfigDirFiles(dirPrefix string) []string {
files, err := ioutil.ReadDir(CloudConfigDir) cloudConfigDir := path.Join(dirPrefix, CloudConfigDir)
files, err := ioutil.ReadDir(cloudConfigDir)
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
// do nothing // do nothing
@ -78,7 +85,7 @@ func CloudConfigDirFiles() []string {
var finalFiles []string var finalFiles []string
for _, file := range files { for _, file := range files {
if !file.IsDir() && !strings.HasPrefix(file.Name(), ".") { if !file.IsDir() && !strings.HasPrefix(file.Name(), ".") {
finalFiles = append(finalFiles, path.Join(CloudConfigDir, file.Name())) finalFiles = append(finalFiles, path.Join(cloudConfigDir, file.Name()))
} }
} }

View File

@ -101,6 +101,7 @@ type RancherConfig struct {
Environment map[string]string `yaml:"environment,omitempty"` Environment map[string]string `yaml:"environment,omitempty"`
Services map[string]*composeConfig.ServiceConfigV1 `yaml:"services,omitempty"` Services map[string]*composeConfig.ServiceConfigV1 `yaml:"services,omitempty"`
BootstrapContainers map[string]*composeConfig.ServiceConfigV1 `yaml:"bootstrap,omitempty"` BootstrapContainers map[string]*composeConfig.ServiceConfigV1 `yaml:"bootstrap,omitempty"`
CloudInitServices map[string]*composeConfig.ServiceConfigV1 `yaml:"cloud_init_services,omitempty"`
BootstrapDocker DockerConfig `yaml:"bootstrap_docker,omitempty"` BootstrapDocker DockerConfig `yaml:"bootstrap_docker,omitempty"`
CloudInit CloudInit `yaml:"cloud_init,omitempty"` CloudInit CloudInit `yaml:"cloud_init,omitempty"`
Debug bool `yaml:"debug,omitempty"` Debug bool `yaml:"debug,omitempty"`

View File

@ -41,9 +41,7 @@ func (s *Service) DependentServices() []project.ServiceRelationship {
} }
if s.requiresUserDocker() { if s.requiresUserDocker() {
// Linking to cloud-init is a hack really. The problem is we need to link to something rels = appendLink(rels, "docker", false, s.project)
// that will trigger a reload
rels = appendLink(rels, "cloud-init", false, s.project)
} else if s.missingImage() { } else if s.missingImage() {
rels = appendLink(rels, "network", false, s.project) rels = appendLink(rels, "network", false, s.project)
} }

View File

@ -81,7 +81,6 @@
<li><a href="{{site.baseurl}}/os/configuration/setting-up-docker-tls/">Setting up Docker TLS</a></li> <li><a href="{{site.baseurl}}/os/configuration/setting-up-docker-tls/">Setting up Docker TLS</a></li>
<li><a href="{{site.baseurl}}/os/configuration/private-registries/">Private Registries</a></li> <li><a href="{{site.baseurl}}/os/configuration/private-registries/">Private Registries</a></li>
<li><a href="{{site.baseurl}}/os/configuration/switching-docker-versions/">Switching Docker Versions</a></li> <li><a href="{{site.baseurl}}/os/configuration/switching-docker-versions/">Switching Docker Versions</a></li>
<li><a href="{{site.baseurl}}/os/configuration/prepacking-docker-images/">Pre-packing Docker Images</a></li>
<li><a href="{{site.baseurl}}/os/configuration/users/">Users</a></li> <li><a href="{{site.baseurl}}/os/configuration/users/">Users</a></li>
<li><a href="{{site.baseurl}}/os/configuration/resizing-device-partition/">Resizing a Device Partition</a></li> <li><a href="{{site.baseurl}}/os/configuration/resizing-device-partition/">Resizing a Device Partition</a></li>
<li><a href="{{site.baseurl}}/os/configuration/sysctl/">sysctl Settings</a></li> <li><a href="{{site.baseurl}}/os/configuration/sysctl/">sysctl Settings</a></li>
@ -96,10 +95,17 @@
<a href="#systemservices" data-toggle="collapse" class="collapsed" data-parent="#mainmenu">System Services<i class="pull-right fa fa-angle-down"></i><i class="pull-right fa fa-angle-up"></i></a> <a href="#systemservices" data-toggle="collapse" class="collapsed" data-parent="#mainmenu">System Services<i class="pull-right fa fa-angle-down"></i><i class="pull-right fa fa-angle-up"></i></a>
<ul class="collapse list-group-submenu" id="systemservices"> <ul class="collapse list-group-submenu" id="systemservices">
<li><a href="{{site.baseurl}}/os/system-services/adding-system-services/">System Services</a></li> <li><a href="{{site.baseurl}}/os/system-services/adding-system-services/">System Services</a></li>
<li><a href="{{site.baseurl}}/os/system-services/built-in-system-services/">Built-in System Services</a></li>
<li><a href="{{site.baseurl}}/os/system-services/system-docker-volumes/">System Docker Volumes</a></li> <li><a href="{{site.baseurl}}/os/system-services/system-docker-volumes/">System Docker Volumes</a></li>
<li><a href="{{site.baseurl}}/os/system-services/environment/">Environment</a></li> <li><a href="{{site.baseurl}}/os/system-services/environment/">Environment</a></li>
</ul> </ul>
<li class="panel">
<a href="#bootprocess" data-toggle="collapse" class="collapsed" data-parent="#mainmenu">Boot Process<i class="pull-right fa fa-angle-down"></i><i class="pull-right fa fa-angle-up"></i></a>
<ul class="collapse list-group-submenu" id="bootprocess">
<li><a href="{{site.baseurl}}/os/boot-process/built-in-system-services/">Built-in System Services</a></li>
<li><a href="{{site.baseurl}}/os/boot-process/cloud-init">Cloud-init</a></li>
<li><a href="{{site.baseurl}}/os/boot-process/image-preloading">Image Preloading</a></li>
</ul>
</li>
</li> </li>
<li class="panel"> <li class="panel">
<a href="#storage" data-toggle="collapse" class="collapsed" data-parent="#mainmenu">Storage<i class="pull-right fa fa-angle-down"></i><i class="pull-right fa fa-angle-up"></i></a> <a href="#storage" data-toggle="collapse" class="collapsed" data-parent="#mainmenu">Storage<i class="pull-right fa fa-angle-down"></i><i class="pull-right fa fa-angle-up"></i></a>

View File

@ -1,54 +1,19 @@
--- ---
title: Built-in System Services in RancherOS title: Built-in System Services in RancherOS
layout: os-default layout: os-default
redirect_from:
- os/system-services/built-in-system-services/
--- ---
## Built-in System Services ## Built-in System Services
To launch RancherOS, we have built-in system services. They are defined in the [Docker Compose](https://docs.docker.com/compose/compose-file/) format, and can be found in the default system config file, `/usr/share/ros/os-config.yml`. You can [add your own system services]({{site.baseurl}}/os/system-services/) or override services in the cloud-config. To launch RancherOS, we have built-in system services. They are defined in the [Docker Compose](https://docs.docker.com/compose/compose-file/) format, and can be found in the default system config file, `/usr/share/ros/os-config.yml`. You can [add your own system services]({{site.baseurl}}/os/system-services/) or override services in the cloud-config.
In start up order, here are the groups of services: ### preload-user-images
1. Device and power management: Read more about [image preloading]({{site.baseurl}}/os/boot-process/image-preloading/).
- udev-cold
- udev
- acpid
2. syslog ### network
3. System configuration and networking:
- preload-system-images
- cloud-init-pre
- network-pre
- ntp
- cloud-init
- network
4. User interaction:
- console
- docker
5. Post configuration:
- preload-user-images
### preload-system-images & preload-user-images
Read more about [pre-packing Docker images]({{site.baseurl}}/os/configuration/prepacking-docker-images/).
### cloud-init-pre
User-data (i.e. [cloud-config]({{site.baseurl}}/os/configuration/#cloud-config)) and metadata from cloud provider, VM runtime, or a management service, is loaded in this service.
The user-data is written to:
* `/var/lib/rancher/conf/cloud-config.d/boot.yml` - If the user-data is a cloud-config, i.e. begins with `#cloud-config` and is YAML format.
* `/var/lib/rancher/conf/cloud-config-script` - If the user-data is a script, i.e begins with `#!`.
* `/var/lib/rancher/conf/metadata` - If it is serialized cloud provider metadata.
It is configured by the `rancher.cloud_init.datasources` list in [cloud-config]({{site.baseurl}}/os/configuration/#cloud-config). It is pre-configured in cloud-provider specific images (e.g. AWS, GCE).
### network-pre
During this service, networking is set up, e.g. hostname, interfaces, and DNS. During this service, networking is set up, e.g. hostname, interfaces, and DNS.
@ -58,16 +23,6 @@ It is configured by `hostname` and `rancher.network`[settings]({{site.baseurl}}/
Runs `ntpd` in a System Docker container. Runs `ntpd` in a System Docker container.
### cloud-init
It does the same thing as cloud-init-pre, but in this step, it can also use the network to fetch user-data and metadata (e.g. in cloud providers).
### network
Completes setting up networking with configuration obtained by cloud-init.
### console ### console
This service provides the RancherOS user interface by running `sshd` and `getty`. It completes the RancherOS configuration on start up: This service provides the RancherOS user interface by running `sshd` and `getty`. It completes the RancherOS configuration on start up:
@ -90,20 +45,8 @@ This service provides the RancherOS user interface by running `sshd` and `getty`
6. Runs `/etc/rc.local` if it exists and is executable. Any errors are ignored. 6. Runs `/etc/rc.local` if it exists and is executable. Any errors are ignored.
### docker ### docker
This system service runs the user docker daemon. Normally it runs inside the console system container by running `docker-init` script which, in turn, looks for docker binaries in `/opt/bin`, `/usr/local/bin` and `/usr/bin`, adds the first found directory with docker binaries to PATH and runs `dockerlaunch docker daemon` appending the passed arguments. This system service runs the user docker daemon. Normally it runs inside the console system container by running `docker-init` script which, in turn, looks for docker binaries in `/opt/bin`, `/usr/local/bin` and `/usr/bin`, adds the first found directory with docker binaries to PATH and runs `dockerlaunch docker daemon` appending the passed arguments.
Docker daemon args are read from `rancher.docker.args` cloud-config property (followed by `rancher.docker.extra_args`). Docker daemon args are read from `rancher.docker.args` cloud-config property (followed by `rancher.docker.extra_args`).
### RancherOS Configuration Load Order
[Cloud-config]({{site.baseurl}}/os/configuration/#cloud-config/) is read by system services when they need to get configuration. Each additional file overwrites and extends the previous configuration file.
1. `/usr/share/ros/os-config.yml` - This is the system default configuration, which should **not** be modified by users.
2. `/usr/share/ros/oem/oem-config.yml` - This will typically exist by OEM, which should **not** be modified by users.
3. Files in `/var/lib/rancher/conf/cloud-config.d/` ordered by filename. If a file is passed in through user-data, it is written by cloud-init and saved as `/var/lib/rancher/conf/cloud-config.d/boot.yml`.
4. `/var/lib/rancher/conf/cloud-config.yml` - If you set anything with `ros config set`, the changes are saved in this file.
5. Kernel parameters with names starting with `rancher`.
6. `/var/lib/rancher/conf/metadata` - Metadata added by cloud-init.

View File

@ -0,0 +1,28 @@
---
title: Cloud-init
layout: os-default
---
## Cloud-init
Userdata and metadata can be fetched from a cloud provider, VM runtime, or management service during the RancherOS boot process. Since v0.8.0, this process occurs while RancherOS is still running from memory and before System Docker starts. It is configured by the `rancher.cloud_init.datasources` configuration parameter. For cloud-provider specific images, such as AWS and GCE, the datasource is pre-configured.
### Userdata
Userdata is a file given by users when launching RancherOS hosts. It is stored in different locations depending on its format. If the userdata is a [cloud-config]({{site.baseurl}}/os/configuration/#cloud-config) file, indicated by beginning with `#cloud-config` and being in YAML format, it is stored in `/var/lib/rancher/conf/cloud-config.d/boot.yml`. If the userdata is a script, indicated by beginning with `#!`, it is stored in `/var/lib/rancher/conf/cloud-config-script`.
### Metadata
Although the specifics vary based on provider, a metadata file will typically contain information about the RancherOS host and contain additional configuration. Its primary purpose within RancherOS is to provide an alternate source for SSH keys and hostname configuration. For example, AWS launches hosts with a set of authorized keys and RancherOS obtains these via metadata. Metadata is stored in `/var/lib/rancher/conf/metadata`.
## Configuration Load Order
[Cloud-config]({{site.baseurl}}/os/configuration/#cloud-config/) is read by system services when they need to get configuration. Each additional file overwrites and extends the previous configuration file.
1. `/usr/share/ros/os-config.yml` - This is the system default configuration, which should **not** be modified by users.
2. `/usr/share/ros/oem/oem-config.yml` - This will typically exist by OEM, which should **not** be modified by users.
3. Files in `/var/lib/rancher/conf/cloud-config.d/` ordered by filename. If a file is passed in through user-data, it is written by cloud-init and saved as `/var/lib/rancher/conf/cloud-config.d/boot.yml`.
4. `/var/lib/rancher/conf/cloud-config.yml` - If you set anything with `ros config set`, the changes are saved in this file.
5. Kernel parameters with names starting with `rancher`.
6. `/var/lib/rancher/conf/metadata` - Metadata added by cloud-init.

View File

@ -1,10 +1,12 @@
--- ---
title: Pre-packing Docker Images title: Image Preloading
layout: os-default layout: os-default
redirect_from:
- os/configuration/prepacking-docker-images/
--- ---
## Pre-packing Docker Images ## Image Preloading
--- ---
On boot, RancherOS scans `/var/lib/rancher/preload/docker` and `/var/lib/rancher/preload/system-docker` directories and tries to load container image archives it finds there, with `docker load` and `system-docker load`. On boot, RancherOS scans `/var/lib/rancher/preload/docker` and `/var/lib/rancher/preload/system-docker` directories and tries to load container image archives it finds there, with `docker load` and `system-docker load`.

View File

@ -1,3 +0,0 @@
FROM rancher/os-base
COPY cloud-init.sh /
CMD ["/cloud-init.sh"]

View File

@ -1,15 +0,0 @@
#!/bin/bash
set -x -e
MOUNT_POINT=/media/config-2
CONFIG_DEV=$(ros dev "LABEL=config-2")
mkdir -p ${MOUNT_POINT}
if [ -e "${CONFIG_DEV}" ]; then
mount -t iso9660,vfat ${CONFIG_DEV} ${MOUNT_POINT}
else
mount -t 9p -o trans=virtio,version=9p2000.L config-2 ${MOUNT_POINT} 2>/dev/null || true
fi
cloud-init-save -network=${CLOUD_INIT_NETWORK:-true}

View File

@ -19,6 +19,12 @@ func bootstrapServices(cfg *config.CloudConfig) (*config.CloudConfig, error) {
return cfg, err return cfg, err
} }
func runCloudInitServiceSet(cfg *config.CloudConfig) (*config.CloudConfig, error) {
log.Info("Running cloud-init services")
_, err := compose.RunServiceSet("cloud-init", cfg, cfg.Rancher.CloudInitServices)
return cfg, err
}
func startDocker(cfg *config.CloudConfig) (chan interface{}, error) { func startDocker(cfg *config.CloudConfig) (chan interface{}, error) {
launchConfig, args := getLaunchConfig(cfg, &cfg.Rancher.BootstrapDocker) launchConfig, args := getLaunchConfig(cfg, &cfg.Rancher.BootstrapDocker)
launchConfig.Fork = true launchConfig.Fork = true
@ -62,3 +68,17 @@ func bootstrap(cfg *config.CloudConfig) error {
bootstrapServices) bootstrapServices)
return err return err
} }
func runCloudInitServices(cfg *config.CloudConfig) error {
c, err := startDocker(cfg)
if err != nil {
return err
}
defer stopDocker(c)
_, err = config.ChainCfgFuncs(cfg,
loadImages,
runCloudInitServiceSet)
return err
}

View File

@ -5,6 +5,7 @@ package init
import ( import (
"bufio" "bufio"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
@ -12,6 +13,7 @@ import (
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/mount"
"github.com/rancher/os/cmd/cloudinitsave"
"github.com/rancher/os/config" "github.com/rancher/os/config"
"github.com/rancher/os/dfs" "github.com/rancher/os/dfs"
"github.com/rancher/os/util" "github.com/rancher/os/util"
@ -143,23 +145,18 @@ func tryMountState(cfg *config.CloudConfig) error {
return mountState(cfg) return mountState(cfg)
} }
func tryMountAndBootstrap(cfg *config.CloudConfig) (*config.CloudConfig, error) { func tryMountAndBootstrap(cfg *config.CloudConfig) (*config.CloudConfig, bool, error) {
if !isInitrd() || cfg.Rancher.State.Dev == "" { if !isInitrd() || cfg.Rancher.State.Dev == "" {
return cfg, nil return cfg, false, nil
} }
if err := tryMountState(cfg); !cfg.Rancher.State.Required && err != nil { if err := tryMountState(cfg); !cfg.Rancher.State.Required && err != nil {
return cfg, nil return cfg, false, nil
} else if err != nil { } else if err != nil {
return cfg, err return cfg, false, err
} }
log.Debugf("Switching to new root at %s %s", STATE, cfg.Rancher.State.Directory) return cfg, true, nil
if err := switchRoot(STATE, cfg.Rancher.State.Directory, cfg.Rancher.RmUsr); err != nil {
return cfg, err
}
return mountOem(cfg)
} }
func getLaunchConfig(cfg *config.CloudConfig, dockerCfg *config.DockerConfig) (*dfs.Config, []string) { func getLaunchConfig(cfg *config.CloudConfig, dockerCfg *config.DockerConfig) (*dfs.Config, []string) {
@ -218,6 +215,9 @@ func RunInit() error {
} }
boot2DockerEnvironment := false boot2DockerEnvironment := false
var shouldSwitchRoot bool
var cloudConfigBootFile []byte
var metadataFile []byte
initFuncs := []config.CfgFunc{ initFuncs := []config.CfgFunc{
func(c *config.CloudConfig) (*config.CloudConfig, error) { func(c *config.CloudConfig) (*config.CloudConfig, error) {
return c, dfs.PrepareFs(&mountConfig) return c, dfs.PrepareFs(&mountConfig)
@ -265,7 +265,84 @@ func RunInit() error {
return cfg, nil return cfg, nil
}, },
tryMountAndBootstrap, func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
var err error
cfg, shouldSwitchRoot, err = tryMountAndBootstrap(cfg)
if err != nil {
return nil, err
}
return cfg, nil
},
func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
if err := os.MkdirAll(config.CloudConfigDir, os.ModeDir|0755); err != nil {
log.Error(err)
}
cfg.Rancher.CloudInit.Datasources = config.LoadConfigWithPrefix(STATE).Rancher.CloudInit.Datasources
if err := config.Set("rancher.cloud_init.datasources", cfg.Rancher.CloudInit.Datasources); err != nil {
log.Error(err)
}
network := false
for _, datasource := range cfg.Rancher.CloudInit.Datasources {
if cloudinitsave.RequiresNetwork(datasource) {
network = true
break
}
}
if network {
if err := runCloudInitServices(cfg); err != nil {
log.Error(err)
}
} else {
if err := cloudinitsave.MountConfigDrive(); err != nil {
log.Error(err)
}
if err := cloudinitsave.SaveCloudConfig(false); err != nil {
log.Error(err)
}
if err := cloudinitsave.UnmountConfigDrive(); err != nil {
log.Error(err)
}
}
return cfg, nil
},
func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
var err error
cloudConfigBootFile, err = ioutil.ReadFile(config.CloudConfigBootFile)
if err != nil {
log.Error(err)
}
metadataFile, err = ioutil.ReadFile(config.MetaDataFile)
if err != nil {
log.Error(err)
}
return cfg, nil
},
func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
if !shouldSwitchRoot {
return cfg, nil
}
log.Debugf("Switching to new root at %s %s", STATE, cfg.Rancher.State.Directory)
if err := switchRoot(STATE, cfg.Rancher.State.Directory, cfg.Rancher.RmUsr); err != nil {
return cfg, err
}
return cfg, nil
},
mountOem,
func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
if err := os.MkdirAll(config.CloudConfigDir, os.ModeDir|0755); err != nil {
log.Error(err)
}
if err := util.WriteFileAtomic(config.CloudConfigBootFile, cloudConfigBootFile, 400); err != nil {
log.Error(err)
}
if err := util.WriteFileAtomic(config.MetaDataFile, metadataFile, 400); err != nil {
log.Error(err)
}
return cfg, nil
},
func(cfg *config.CloudConfig) (*config.CloudConfig, error) { func(cfg *config.CloudConfig) (*config.CloudConfig, error) {
if boot2DockerEnvironment { if boot2DockerEnvironment {
if err := config.Set("rancher.state.dev", cfg.Rancher.State.Dev); err != nil { if err := config.Set("rancher.state.dev", cfg.Rancher.State.Dev); err != nil {

View File

@ -30,6 +30,30 @@ rancher:
- /lib/firmware:/lib/firmware - /lib/firmware:/lib/firmware
- /usr/bin/ros:/usr/bin/ros:ro - /usr/bin/ros:/usr/bin/ros:ro
- /usr/share/ros:/usr/share/ros:ro - /usr/share/ros:/usr/share/ros:ro
cloud_init_services:
cloud-init:
image: {{.OS_REPO}}/os-base:{{.VERSION}}{{.SUFFIX}}
command: cloud-init-save
labels:
io.rancher.os.detach: "false"
io.rancher.os.scope: system
io.rancher.os.after: ntp
log_driver: json-file
net: host
uts: host
pid: host
ipc: host
privileged: true
volumes:
- /dev:/host/dev
- /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt.rancher
- /lib/modules:/lib/modules
- /lib/firmware:/lib/firmware
- /usr/bin/ros:/usr/bin/ros:ro
- /usr/bin/ros:/usr/bin/cloud-init-save
- /usr/share/ros:/usr/share/ros:ro
- /var/lib/rancher:/var/lib/rancher
- /var/lib/rancher/conf:/var/lib/rancher/conf
bootstrap_docker: bootstrap_docker:
bridge: none bridge: none
storage_driver: overlay storage_driver: overlay
@ -80,43 +104,13 @@ rancher:
- command-volumes - command-volumes
- user-volumes - user-volumes
- system-volumes - system-volumes
cloud-init:
image: {{.OS_REPO}}/os-cloudinit:{{.VERSION}}{{.SUFFIX}}
labels:
io.rancher.os.detach: "false"
io.rancher.os.reloadconfig: "true"
io.rancher.os.scope: system
io.rancher.os.after: ntp
net: host
uts: host
privileged: true
volumes_from:
- command-volumes
- system-volumes
cloud-init-execute: cloud-init-execute:
image: {{.OS_REPO}}/os-base:{{.VERSION}}{{.SUFFIX}} image: {{.OS_REPO}}/os-base:{{.VERSION}}{{.SUFFIX}}
command: cloud-init-execute -pre-console command: cloud-init-execute -pre-console
labels: labels:
io.rancher.os.detach: "false" io.rancher.os.detach: "false"
io.rancher.os.scope: system io.rancher.os.scope: system
io.rancher.os.after: cloud-init io.rancher.os.after: ntp
net: host
uts: host
privileged: true
volumes_from:
- system-volumes
volumes:
- /usr/bin/ros:/usr/bin/ros
- /usr/bin/ros:/usr/bin/cloud-init-execute
cloud-init-pre:
image: {{.OS_REPO}}/os-cloudinit:{{.VERSION}}{{.SUFFIX}}
environment:
- CLOUD_INIT_NETWORK=false
labels:
io.rancher.os.detach: "false"
io.rancher.os.reloadconfig: "true"
io.rancher.os.scope: system
io.rancher.os.after: udev
net: host net: host
uts: host uts: host
privileged: true privileged: true
@ -151,7 +145,7 @@ rancher:
command: ros console-init command: ros console-init
labels: labels:
io.rancher.os.scope: system io.rancher.os.scope: system
io.rancher.os.after: network io.rancher.os.after: cloud-init-execute
io.docker.compose.rebuild: always io.docker.compose.rebuild: always
io.rancher.os.console: default io.rancher.os.console: default
net: host net: host
@ -178,25 +172,13 @@ rancher:
read_only: true read_only: true
volumes: volumes:
- /var/lib/docker:/var/lib/docker - /var/lib/docker:/var/lib/docker
network-pre: network:
image: {{.OS_REPO}}/os-base:{{.VERSION}}{{.SUFFIX}} image: {{.OS_REPO}}/os-base:{{.VERSION}}{{.SUFFIX}}
command: netconf command: netconf
labels: labels:
io.rancher.os.scope: system io.rancher.os.scope: system
io.rancher.os.after: cloud-init-pre io.rancher.os.after: udev
net: host io.rancher.os.reloadconfig: "true"
uts: host
pid: host
privileged: true
volumes_from:
- command-volumes
- system-volumes
network:
image: {{.OS_REPO}}/os-base:{{.VERSION}}{{.SUFFIX}}
command: netconf --stop-network-pre
labels:
io.rancher.os.scope: system
io.rancher.os.after: cloud-init-execute
net: host net: host
uts: host uts: host
pid: host pid: host
@ -209,7 +191,7 @@ rancher:
command: ntpd --nofork -g command: ntpd --nofork -g
labels: labels:
io.rancher.os.scope: system io.rancher.os.scope: system
io.rancher.os.after: network-pre io.rancher.os.after: network
net: host net: host
uts: host uts: host
privileged: true privileged: true

20
tests/cloud_init_test.go Normal file
View File

@ -0,0 +1,20 @@
package integration
import . "gopkg.in/check.v1"
func (s *QemuSuite) TestReadDatasourcesFromDisk(c *C) {
s.RunQemu(c)
s.CheckCall(c, `
sudo tee /var/lib/rancher/conf/cloud-config.d/datasources.yml << EOF
rancher:
cloud_init:
datasources:
- url:https://gist.githubusercontent.com/joshwget/e1c49f8b1ddeeba01bc9d0a3be01ed60/raw/9168b380fde182d53acea487d49b680648a0ca5b/gistfile1.txt
EOF
`)
s.Reboot(c)
s.CheckCall(c, "sudo ros config get rancher.log | grep true")
}

View File

@ -4,7 +4,7 @@ import . "gopkg.in/check.v1"
func (s *QemuSuite) TestNetworkFromUrl(c *C) { func (s *QemuSuite) TestNetworkFromUrl(c *C) {
netArgs := []string{"-net", "nic,vlan=0,model=virtio"} netArgs := []string{"-net", "nic,vlan=0,model=virtio"}
args := []string{"--cloud-config", "./tests/assets/test_10/cloud-config.yml"} args := []string{"--append", "rancher.cloud_init.datasources=[url:https://gist.githubusercontent.com/joshwget/0bdc616cd26162ad87c535644c8b1ef6/raw/8cce947c08cf006e932b71d92ddbb96bae8e3325/gistfile1.txt]"}
for i := 0; i < 7; i++ { for i := 0; i < 7; i++ {
args = append(args, netArgs...) args = append(args, netArgs...)
} }