mirror of
https://github.com/rancher/os.git
synced 2025-09-06 01:01:43 +00:00
Various fixes
This commit is contained in:
3
Dockerfile.docs
Normal file
3
Dockerfile.docs
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM squidfunk/mkdocs-material
|
||||
RUN pip install mkdocs-markdownextradata-plugin
|
||||
RUN apk add -U git openssh
|
62
README.md
62
README.md
@@ -1,62 +0,0 @@
|
||||
# RancherOS v2
|
||||
|
||||
# WORK IN PROGRESS
|
||||
|
||||
RancherOS v2 is an immutable Linux distribution built to run Rancher and
|
||||
it's corresponding Kubernetes distributions [RKE2](https://rke2.io)
|
||||
and [k3s](https://k3s.io). It is built using the [cOS-toolkit](https://rancher-sandbox.github.io/cos-toolkit-docs/docs/)
|
||||
and based on openSUSE. Initial node configurations is done using only a
|
||||
cloud-init style approach and all further maintenance is done using
|
||||
Kubernetes operators.
|
||||
|
||||
## Use Cases
|
||||
|
||||
RancherOS is intended to be ran as the operating system beneath a Rancher Multi-Cluster
|
||||
Management server or as a node in a Kubernetes cluster managed by Rancher. RancherOS
|
||||
also allows you to build stand alone Kubernetes clusters that run an embedded
|
||||
and smaller version of Rancher to manage the local cluster. A key attribute of RancherOS
|
||||
is that it is managed by Rancher and thus Rancher will exist either locally in the cluster
|
||||
or centrally with Rancher Multi-Cluster Manager.
|
||||
|
||||
## Architecture
|
||||
|
||||
### OCI Image based
|
||||
|
||||
RancherOS v2 is an A/B style image based distribution. One first runs
|
||||
on a read-only image A and to do an upgrade pulls a new read only image
|
||||
B and then reboots the system to run on B. What is unique about
|
||||
RancherOS v2 is that the runtime images come from OCI Images. Not an
|
||||
OCI Image containing special artifacts, but an actual Docker runnable
|
||||
image that is built using standard Docker build processes. RancherOS is
|
||||
built using normal `docker build` and if you wish to customize the OS
|
||||
image all you need to do is create a new `Dockerfile`.
|
||||
|
||||
### rancherd
|
||||
|
||||
RancherOS v2 includes no container runtime, Kubernetes distribution,
|
||||
or Rancher itself. All of these assests are dynamically pulled at runtime. All that
|
||||
is included in RancherOS is [rancherd](https://github.com/rancher/rancherd) which
|
||||
is responsible for bootstrapping RKE2/k3s and Rancher from an OCI registry. This means
|
||||
an update to containerd, k3s, RKE2, or Rancher does not require an OS upgrade
|
||||
or node reboot.
|
||||
|
||||
### cloud-init
|
||||
|
||||
RancherOS v2 is initially configured using a simple version of `cloud-init`.
|
||||
It is not expected that one will need to do a lot of customization to RancherOS
|
||||
as the core OS's sole purpose is to run Rancher and Kubernetes and not serve as
|
||||
a generic Linux distribution.
|
||||
|
||||
### RancherOS Operator
|
||||
|
||||
RancherOS v2 includes an operator that is responsible for managing OS upgrades
|
||||
and assiting with secure device onboarding (SDO).
|
||||
|
||||
|
||||
### openSUSE Leap
|
||||
|
||||
RancherOS v2 is based off of openSUSE Leap. There is no specific tie in to
|
||||
openSUSE beyond that RancherOS assumes the underlying distribution is
|
||||
based on systemd. We choose openSUSE for obvious reasons, but beyond
|
||||
that openSUSE Leap provides a stable layer to build upon that is well
|
||||
tested and has paths to commercial support, if one chooses.
|
@@ -3,6 +3,14 @@ kind: ClusterRole
|
||||
metadata:
|
||||
name: rancheros-operator
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- 'secrets'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'watch'
|
||||
- 'list'
|
||||
- apiGroups:
|
||||
- rancheros.cattle.io
|
||||
resources:
|
||||
@@ -15,10 +23,19 @@ rules:
|
||||
- 'bundles'
|
||||
verbs:
|
||||
- '*'
|
||||
- apiGroups:
|
||||
- provisioning.cattle.io
|
||||
resources:
|
||||
- 'clusters'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'watch'
|
||||
- 'list'
|
||||
- apiGroups:
|
||||
- management.cattle.io
|
||||
resources:
|
||||
- 'settings'
|
||||
- 'clusterregistrationtokens'
|
||||
verbs:
|
||||
- 'get'
|
||||
- 'watch'
|
||||
|
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
output = flag.Bool("automatic", false, "Check for and run automatic installation")
|
||||
automatic = flag.Bool("automatic", false, "Check for and run automatic installation")
|
||||
printConfig = flag.Bool("print-config", false, "Print effective configuration and exit")
|
||||
configFile = flag.String("config-file", "", "Config file to use, local file or http/tftp URL")
|
||||
)
|
||||
@@ -31,7 +31,7 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := install.Run(*output, *configFile); err != nil {
|
||||
if err := install.Run(*automatic, *configFile); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This is required for booting a squashfs from network
|
||||
add_dracutmodules+=" livenet "
|
||||
install_items+=" /etc/ssl "
|
||||
install_optional_items+=" /var/lib/ca-certificates "
|
||||
install_items+=" /etc/ssl/* "
|
||||
install_optional_items+=" /var/lib/ca-certificates/* /var/lib/ca-certificates/*/* "
|
||||
|
@@ -13,4 +13,4 @@ stages:
|
||||
entity: |
|
||||
kind: "shadow"
|
||||
username: "root"
|
||||
password: "cos"
|
||||
password: "ros"
|
||||
|
@@ -12,8 +12,6 @@ metadata:
|
||||
namespace: fleet-local
|
||||
spec:
|
||||
osImage: "${IMAGE}"
|
||||
clusterTargets:
|
||||
- clusterName: local
|
||||
EOF
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,6 @@ type ManagedOSImage struct {
|
||||
}
|
||||
|
||||
type ManagedOSImageSpec struct {
|
||||
Paused bool `json:"paused,omitempty"`
|
||||
OSImage string `json:"osImage,omitempty"`
|
||||
NodeSelector *metav1.LabelSelector `json:"nodeSelector,omitempty"`
|
||||
Concurrency *int64 `json:"concurrency,omitempty"`
|
||||
|
@@ -15,7 +15,10 @@ type Install struct {
|
||||
NoFormat bool `json:"noFormat,omitempty"`
|
||||
Debug bool `json:"debug,omitempty"`
|
||||
TTY string `json:"tty,omitempty"`
|
||||
Password string `json:"password,omitempy"`
|
||||
ServerURL string `json:"-"`
|
||||
Token string `json:"-"`
|
||||
Role string `json:"-"`
|
||||
Password string `json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
@@ -25,12 +28,19 @@ type Config struct {
|
||||
|
||||
type YipConfig struct {
|
||||
Stages map[string][]Stage `json:"stages,omitempty"`
|
||||
Rancherd Rancherd `json:"rancherd,omitempty"`
|
||||
}
|
||||
|
||||
type Stage struct {
|
||||
Users map[string]User `json:"users,omitempty"`
|
||||
}
|
||||
|
||||
type Rancherd struct {
|
||||
Server string `json:"server,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
Token string `json:"token,omitempty"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
PasswordHash string `json:"passwd,omitempty"`
|
||||
|
@@ -54,6 +54,35 @@ func mapToEnv(prefix string, data map[string]interface{}) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
func readFileFunc(path string) func() (map[string]interface{}, error) {
|
||||
return func() (map[string]interface{}, error) {
|
||||
return readFile(path)
|
||||
}
|
||||
}
|
||||
|
||||
func readNested(data map[string]interface{}) (map[string]interface{}, error) {
|
||||
var (
|
||||
nestedConfigFiles = convert.ToStringSlice(values.GetValueN(data, "rancheros", "install", "configUrl"))
|
||||
funcs []reader
|
||||
)
|
||||
|
||||
if len(nestedConfigFiles) == 0 {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
values.RemoveValue(data, "rancheros", "install", "configUrl")
|
||||
|
||||
for _, nestedConfigFile := range nestedConfigFiles {
|
||||
funcs = append(funcs, readFileFunc(nestedConfigFile))
|
||||
}
|
||||
|
||||
funcs = append(funcs, func() (map[string]interface{}, error) {
|
||||
return data, nil
|
||||
})
|
||||
|
||||
return merge(funcs...)
|
||||
}
|
||||
|
||||
func readFile(path string) (result map[string]interface{}, _ error) {
|
||||
result = map[string]interface{}{}
|
||||
defer func() {
|
||||
@@ -109,26 +138,17 @@ func merge(readers ...reader) (map[string]interface{}, error) {
|
||||
if err := schema.Mapper.ToInternal(newData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newData, err = readNested(newData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d = values.MergeMapsConcatSlice(d, newData)
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
func readConfigMap(cfg string) (map[string]interface{}, error) {
|
||||
var (
|
||||
err error
|
||||
data map[string]interface{}
|
||||
)
|
||||
return merge(func() (map[string]interface{}, error) {
|
||||
data, err = merge(readCmdline,
|
||||
func() (map[string]interface{}, error) {
|
||||
return readFile(cfg)
|
||||
},
|
||||
)
|
||||
return data, err
|
||||
}, func() (map[string]interface{}, error) {
|
||||
return readFile(convert.ToString(values.GetValueN(data, "rancheros", "install", "configUrl")))
|
||||
})
|
||||
return merge(readCmdline, readFileFunc(cfg))
|
||||
}
|
||||
|
||||
func ReadConfig(cfg string) (result Config, err error) {
|
||||
|
@@ -2,6 +2,7 @@ package managedos
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/rancher/fleet/pkg/apis/fleet.cattle.io/v1alpha1"
|
||||
provv1 "github.com/rancher/os2/pkg/apis/rancheros.cattle.io/v1"
|
||||
@@ -71,6 +72,10 @@ func (h *handler) OnChange(mos *provv1.ManagedOSImage, status provv1.ManagedOSIm
|
||||
return nil, status, err
|
||||
}
|
||||
|
||||
if mos.Namespace == "fleet-local" && len(mos.Spec.Targets) > 0 {
|
||||
return nil, status, fmt.Errorf("spec.targets should be empty if in the fleet-local namespace")
|
||||
}
|
||||
|
||||
bundle := &v1alpha1.Bundle{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name.SafeConcatName("mos", mos.Name),
|
||||
@@ -79,12 +84,15 @@ func (h *handler) OnChange(mos *provv1.ManagedOSImage, status provv1.ManagedOSIm
|
||||
Spec: v1alpha1.BundleSpec{
|
||||
Resources: resources,
|
||||
BundleDeploymentOptions: v1alpha1.BundleDeploymentOptions{},
|
||||
Paused: mos.Spec.Paused,
|
||||
RolloutStrategy: mos.Spec.ClusterRolloutStrategy,
|
||||
Targets: mos.Spec.Targets,
|
||||
},
|
||||
}
|
||||
|
||||
if mos.Namespace == "fleet-local" {
|
||||
bundle.Spec.Targets = []v1alpha1.BundleTarget{{ClusterName: "local"}}
|
||||
}
|
||||
|
||||
status, err = h.updateStatus(status, bundle)
|
||||
return []runtime.Object{
|
||||
bundle,
|
||||
|
@@ -26,6 +26,10 @@ func Ask(cfg *config.Config) error {
|
||||
if err := AskPassword(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := AskServerAgent(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -50,9 +54,33 @@ func AskInstallDevice(cfg *config.Config) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func isServer(cfg *config.Config) (bool, bool, error) {
|
||||
func AskToken(cfg *config.Config, server bool) error {
|
||||
var (
|
||||
token string
|
||||
err error
|
||||
)
|
||||
|
||||
if cfg.RancherOS.Install.Token != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := "Token or cluster secret"
|
||||
if server {
|
||||
msg += " (optional)"
|
||||
}
|
||||
if server {
|
||||
token, err = questions.PromptOptional(msg+": ", "")
|
||||
} else {
|
||||
token, err = questions.Prompt(msg+": ", "")
|
||||
}
|
||||
cfg.RancherOS.Install.Token = token
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func isServer() (bool, bool, error) {
|
||||
opts := []string{"server", "agent", "none"}
|
||||
i, err := questions.PromptFormattedOptions("Run as server or agent?", 0, opts...)
|
||||
i, err := questions.PromptFormattedOptions("Run as server or agent (Choose none if building an image)?", 0, opts...)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
@@ -60,6 +88,35 @@ func isServer(cfg *config.Config) (bool, bool, error) {
|
||||
return i == 0, i == 1, nil
|
||||
}
|
||||
|
||||
func AskServerAgent(cfg *config.Config) error {
|
||||
if cfg.RancherOS.Install.ServerURL != "" || cfg.RancherOS.Install.Silent {
|
||||
return nil
|
||||
}
|
||||
|
||||
server, agent, err := isServer()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !server && !agent {
|
||||
return nil
|
||||
}
|
||||
|
||||
if server {
|
||||
cfg.RancherOS.Install.Role = "server"
|
||||
return AskToken(cfg, true)
|
||||
}
|
||||
|
||||
cfg.RancherOS.Install.Role = "agent"
|
||||
url, err := questions.Prompt("URL of server: ", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cfg.RancherOS.Install.ServerURL = url
|
||||
|
||||
return AskToken(cfg, false)
|
||||
}
|
||||
|
||||
func AskPassword(cfg *config.Config) error {
|
||||
if cfg.RancherOS.Install.Silent || cfg.RancherOS.Install.Password != "" {
|
||||
return nil
|
||||
|
@@ -55,7 +55,13 @@ func runInstall(cfg config.Config, output string) error {
|
||||
}
|
||||
|
||||
if cfg.RancherOS.Install.ConfigURL == "" && !cfg.RancherOS.Install.Silent {
|
||||
yip := config.YipConfig{}
|
||||
yip := config.YipConfig{
|
||||
Rancherd: config.Rancherd{
|
||||
Server: cfg.RancherOS.Install.ServerURL,
|
||||
Token: cfg.RancherOS.Install.Token,
|
||||
Role: cfg.RancherOS.Install.Role,
|
||||
},
|
||||
}
|
||||
if cfg.RancherOS.Install.Password != "" || len(cfg.SSHAuthorizedKeys) > 0 {
|
||||
yip.Stages = map[string][]config.Stage{
|
||||
"network": {{
|
||||
|
@@ -124,22 +124,31 @@ RUN cd /iso && \
|
||||
luet-makeiso iso.yaml
|
||||
|
||||
FROM iso-build AS qcow-build
|
||||
ARG ACCEL=tcg
|
||||
RUN SUFFIX= && \
|
||||
FIRMWARE= && \
|
||||
if [ "$(uname -m)" = "aarch64" ]; then SUFFIX=-arm64; FIRMWARE=/usr/share/qemu/qemu-uefi-aarch64.bin; fi && \
|
||||
PACKER_LOG=1 packer build \
|
||||
echo '#!/bin/bash' > /usr/bin/image && \
|
||||
echo 'set -e -x' >> /usr/bin/image && \
|
||||
echo PACKER_LOG=1 packer build \
|
||||
-var "root_password=ros" \
|
||||
-var "firmware=${FIRMWARE}" \
|
||||
-var "memory=1024" \
|
||||
-var "iso=/iso/output.iso" \
|
||||
-var "accelerator=tcg" \
|
||||
-only qemu.cos${SUFFIX} .
|
||||
RUN mkdir /output && \
|
||||
mv *.box /output/output.box && \
|
||||
pigz -dc *.tar.gz | tar xvf - && \
|
||||
cat cOS | pigz -c > /output/output.qcow.gz
|
||||
-var "accelerator=${ACCEL}" \
|
||||
-only qemu.cos${SUFFIX} . >> /usr/bin/image && \
|
||||
chmod +x /usr/bin/image
|
||||
RUN echo 'mkdir /output &&' >> /usr/bin/image && \
|
||||
echo 'mv *.box /output/output.box' >> /usr/bin/image && \
|
||||
echo 'pigz -dc *.tar.gz | tar xvf -' >> /usr/bin/image && \
|
||||
echo 'cat cOS | pigz -c > /output/output.qcow.gz'>> /usr/bin/image
|
||||
CMD ["bash", "-x", "/usr/bin/image"]
|
||||
|
||||
FROM qcow-build AS qcow-build2
|
||||
RUN bash -x /usr/bin/image
|
||||
|
||||
FROM scratch AS qcow
|
||||
COPY --from=qcow-build /output/ /
|
||||
COPY --from=qcow-build2 /output/ /
|
||||
|
||||
FROM scratch AS iso
|
||||
COPY --from=iso-build /iso/output.iso /
|
||||
@@ -153,12 +162,17 @@ ARG NAME=RancherOS-Image-dev
|
||||
ARG VERSION=1
|
||||
ARG GIT_COMMIT=HEAD
|
||||
RUN packer build \
|
||||
-var "root_password=ros" \
|
||||
-var "cos_version=${VERSION}" \
|
||||
-var "git_sha=${GIT_COMMIT}" \
|
||||
-var 'aws_source_ami_filter_owners=["053594193760"]' \
|
||||
-var "aws_cos_deploy_args=cos-deploy --no-verify --docker-image ${IMAGE}" \
|
||||
-var "name=${NAME}" \
|
||||
-only amazon-ebs.cos .
|
||||
|
||||
FROM scratch AS default
|
||||
COPY --from=iso / /
|
||||
COPY --from=qcow / /
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -170,7 +184,22 @@ iso()
|
||||
|
||||
qcow()
|
||||
{
|
||||
build --target qcow -o build/
|
||||
ID=qcow-${RANDOM}
|
||||
if docker run -it --device /dev/kvm busybox /bin/true; then
|
||||
build --target qcow-build --build-arg ACCEL=kvm -t $ID
|
||||
docker run --net=host -it --device /dev/kvm --name $ID $ID
|
||||
else
|
||||
build --target qcow-build --build-arg ACCEL=tcg -t $ID
|
||||
docker run --net=host -it --name $ID $ID
|
||||
fi || {
|
||||
docker rm -fv $ID
|
||||
docker rmi $ID
|
||||
exit 1
|
||||
}
|
||||
mkdir -p build/
|
||||
docker export $ID | tar xvf - -C build/ output/ --strip-components=1
|
||||
docker rm -fv $ID
|
||||
docker rmi $ID
|
||||
}
|
||||
|
||||
ami()
|
||||
@@ -204,6 +233,11 @@ VERSION=${IMAGE##*:}
|
||||
NAME=${IMAGE%%:${VERSION}}
|
||||
NAME=${NAME//[^a-zA-Z0-9-@.\/_]/-}
|
||||
|
||||
if [ "$1" == dockerfile ]; then
|
||||
dockerfile
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "${OUTPUT}" ] || [ -z "${IMAGE}" ] || echo "$@" | grep -q -- -h; then
|
||||
usage
|
||||
exit 1
|
||||
@@ -222,10 +256,6 @@ fi
|
||||
iso)
|
||||
iso
|
||||
;;
|
||||
dockerfile)
|
||||
dockerfile
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo Unknown format $i
|
||||
exit 1
|
||||
|
Reference in New Issue
Block a user