# Skopeo
[TOC]
## 認識與安裝 Skopeo
1. skopeo is a command line utility that performs various operations on container images and image repositories.
2. skopeo does not require the user to be running as root to do most of its operations.
3. skopeo does not require a daemon to be running to perform its operations.
4. skopeo can work with OCI images as well as the original Docker v2 images.
在 Alpine Linux 執行以下命令
```
$ sudo apk update; sudo apk add umoci skopeo
$ skopeo -v
skopeo version 1.8.0 commit: b7fc5b608b641cd9b9aec2647f9236e31f8f3b27
```
## 檢查image
由以下命令可得知 ozone image 有那些版本可下載
```
##不用下載image就可以先看他的內容
##這裏docker代表是 image 的標準
##這個image目前最新版本代號1.2.1
$ sudo skopeo list-tags docker://docker.io/apache/ozone
{
"Repository": "docker.io/apache/ozone",
"Tags": [
"0.3.0",
"0.4.0",
"0.4.1",
"0.5.0",
"1.0.0",
"1.1.0",
"1.2.0",
"1.2.1",
"latest"
]
}
##skopeo可以不用下載image,我們就可以先看到他的內容,之後再決定要不要下載他
$ sudo skopeo inspect docker://quay.io/cloudwalker/alpine
{
"Name": "quay.io/cloudwalker/alpine",
........
"DockerVersion": "19.03.12", ##這個image是docker做的
"Labels": null,
....
}
```
## 探索 Docker Image
```
##下載image並且解開它,存到/tmp/alpine
$ sudo skopeo --insecure-policy copy docker://quay.io/cloudwalker/alpine dir:/tmp/alpine
Getting image source signatures
Copying blob ca3cd42a7c95 done
Copying config 49f356fa45 done
Writing manifest to image destination
Storing signatures
$ tree /tmp/alpine
/tmp/alpine
├── 540db60ca9383eac9e418f78490994d0af424aab7bf6d0e47ac8ed4e2e9bcbba ##這個是壓縮檔放一堆目錄區
├── 6dbb9cc54074106d46d4ccb330f2a40a682d49dda5f4844962b7dce9fe44aaec ##這個是設定檔
├── manifest.json ##image的說明檔
└── version
```
* The Config and Image Layers are there, but remember we need to rely on a graph driver in a container engine to map them into a RootFS.
## 檢視 Docker Image Manifest 資訊
```
##上面是設定檔,下面是壓縮檔
$ cat /tmp/alpine/manifest.json
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"config": {
"mediaType": "application/vnd.docker.container.image.v1+json",
"size": 1472,
"digest": "sha256:6dbb9cc54074106d46d4ccb330f2a40a682d49dda5f4844962b7dce9fe44aaec"
},
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"size": 2811969,
"digest": "sha256:540db60ca9383eac9e418f78490994d0af424aab7bf6d0e47ac8ed4e2e9bcbba"
}
]
}
```

* runc需要兩個東西才能產生container,一個是設定檔(conf),一個是目錄區(rootfs)
* image裡面有兩個東西一個是conf,一個是layer1
* conf是放container的設定項目
* layer1就是目錄區,他會放到lower這一層,是read only ,如果後面跟前面有相同名稱的目錄,後面的目錄會蓋掉前面目錄的內容
```
$ cd /tmp/alpine; sudo mkdir img
##檢查壓縮檔(目錄區)
$ tar xvfz 540db60ca9383eac9e418f78490994d0af424aab7bf6d0e47ac8ed4e2e9bcbba -C img/
$ tree -L 1 img
img
├── bin
├── dev
├── etc
├── home
├── lib
├── media
├── mnt
├── opt
├── proc
.......
└── var
```
檢視設定檔,因為他是json檔,所以後面導入用jq顯示
```
$ cat c059bfaa849c4d8e4aecaeb3a10c2d9b3d85f5165c66ad3a4d937758128c4d18 | jq
......
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759"
]
}
}
......
"os": "linux",
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:8d3ac3489996423f53d6087c81180006263b79f206d3fdec9e66f0e27ceb8759"
]
}
}
```
## 探索 OCI Image
檢視 OCI Image 結構順序
Image Index -> Image Manifest -> Image Configuration + Filesystem Layer
Runtime Filesystem bundle is what you get when you download the container image and unpack it.
OCI Image -> OCI Runtime Filesystem Bundle -> OCI Runtime (runc)
```
$ sudo skopeo --insecure-policy copy docker://quay.io/cloudwalker/busybox:latest oci:/home/bigred/busybox-oci:latest
$ cd; tree busybox-oci/
busybox_oci/
├── blobs
│ └── sha256
│ ├── 03781489f3738437ae98f13df5c28cc98bbc582254cfbf04cc7381f1c2ac1cc0
│ ├── 3cb635b06aa273034d7080e0242e4b6628c59347d6ddefff019bfd82f45aa7d5
│ └── bcd679c508ab0387148e6208479400b004f9bf9959124ce536b4be155f3251ca
├── index.json
└── oci-layout
```
* blob : Binary Large Object
```
##看index.json說明檔,才可以知道真正說明檔是誰
$ cat busybox-oci/index.json | jq
{
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:67c32e0fe983b2f71469c2343e6747e3f664af16f1b414e14a70cbaabed53da6",
"size": 404,
"annotations": {
"org.opencontainers.image.ref.name": "latest"
}
}
]
}
```
```
$ dir busybox-oci/blobs/sha256/
total 768K
drwxr-sr-x 2 root bigred 4.0K Jul 23 14:14 .
drwxr-sr-x 3 root bigred 4.0K Jul 23 14:14 ..
-rw-r--r-- 1 root bigred 404 Jul 23 14:14 67c32e0fe983b2f71469c2343e6747e3f664af16f1b414e14a70cbaabed53da6 ##他是說明檔
-rw-r--r-- 1 root bigred 749K Jul 23 14:14 92f8b3f0730fef84ba9825b3af6ad90de454c4c77cde732208cf84ff7dd41208
-rw-r--r-- 1 root bigred 575 Jul 23 14:14 9dcbd1eb45054c937d5cc5816bff2a9af8fa4603cb81c7ab7161dc0224ef72b9
```
> 結論OCI的image比docker的image還要麻煩,需要很多前置作業才能找到說名檔。
* 看到設定檔跟壓縮檔
```
$ cat busybox-oci/blobs/sha256/67c32e0fe983b2f71469c2343e6747e3f664af16f1b414e14a70cbaabed53da6 | jq
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:9dcbd1eb45054c937d5cc5816bff2a9af8fa4603cb81c7ab7161dc0224ef72b9", ##config.json設定檔
"size": 575
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:92f8b3f0730fef84ba9825b3af6ad90de454c4c77cde732208cf84ff7dd41208", ##壓縮檔
"size": 766640
}
]
}
```
## 產生 Runtime Filesystem bundle
* 他可以直接產讓我們看得懂的image的設定檔跟目錄區
* 有了這些內容我們可以清楚地知道,runc/runsc/crun他們有什麼差別
```
$ sudo apk add umoci
##把image unpack 變成bundle
$ sudo umoci unpack --image busybox-oci:latest busybox-oci-bundle
$ sudo tree -L 2 busybox-oci-bundle/
busybox-oci-bundle/
├── config.json
├── rootfs
│ ├── bin
│ ├── dev
│ ├── etc
│ ├── home
│ ├── root
│ ├── tmp
│ ├── usr
│ └── var
├── sha256_3005b0c54259146abf2e464f1cb2785b5ce34738337b681939b013e8bb674408.mtree
└── umoci.json
```
## Skopeo images 同步
`$ sudo skopeo login --tls-verify=false -u myquay -p myquay2022 $IP`
將 Quay.io 中的 alpine image (包含所有 Tag) 同步到 自建的 Project Quay
https://github.com/containers/skopeo/blob/main/docs/skopeo-sync.1.md
```
##--src docker 代表要從 registry 拉 image,--dest docker 代表存到的地方也是 registry
##skopeo會直接抓image,並且上傳到我們的落地quay
$ sudo skopeo sync --tls-verify=false --src docker --dest docker quay.io/cloudwalker/alpine $IP/myquay
```
檢視同步到 自建 Project Quay 所有不同 Tag 的 alpine image
```
$ sudo skopeo list-tags --tls-verify=false docker://$IP/myquay/alpine
{
"Repository": "192.168.61.165/myquay/alpine",
"Tags": [
"latest"
]
}
```
## YAML file content 上傳yaml檔到落地quay
```
$ nano sync.yaml
quay.io:
images:
cloudwalker/busybox: []
cloudwalker/alpine.sshd:
- latest
cloudwalker/alpine.derby:
- latest
$ sudo skopeo sync --tls-verify=false --src yaml --dest docker sync.yaml $IP/myquay/
$ sudo skopeo list-tags --tls-verify=false docker://$IP/myquay/busybox
{
"Repository": "192.168.61.149/myquay/busybox",
"Tags": [
"1.35.0",
"latest"
]
}
```
## 移除落地quay images 與 登出
```
##skopeo 可以維運落地quay,新增/刪除/修改/查詢。
$ sudo skopeo delete --tls-verify=false docker://$IP/myquay/busybox
$ sudo skopeo logout $IP
Removed login credentials for 120.96.143.100:9026
```
## 安裝 Buildah
```
$ sudo apk add buildah
fetch http://dl-cdn.alpinelinux.org/alpine/latest-stable/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/latest-stable/community/x86_64/APKINDEX.tar.gz
(1/1) Installing buildah (1.26.1-r1)
Executing busybox-1.35.0-r14.trigger
OK: 1111 MiB in 282 packages
```
## Container Image 設計 (sshd)
```
##編寫Dockerfile
$ mkdir ~/alpine.sshd; cd ~/alpine.sshd
$ nano Dockerfile
FROM 192.168.61.149/myquay/alpine:3.15.4
RUN \
apk update && \
apk add --no-cache nano sudo bash wget curl git tree grep \
elinks shadow procps util-linux coreutils binutils \
findutils openssh-server tzdata && \
# 設定時區
cp /usr/share/zoneinfo/Asia/Taipei /etc/localtime && \
ssh-keygen -t rsa -P "" -f /etc/ssh/ssh_host_rsa_key && \
echo -e 'Welcome to ALP sshd 6000\n' > /etc/motd && \
# 建立管理者帳號 bigred
adduser -s /bin/bash -h /home/bigred -G wheel -D bigred && \
echo "%wheel ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers && \
echo -e 'bigred\nbigred\n' | passwd bigred &>/dev/null && \
rm /sbin/reboot && rm /usr/bin/killall
ENTRYPOINT ["/usr/sbin/sshd"]
CMD ["-D"]
```
## Container Image 建立與測試
```
##buildah bud 把Dockerfile建成image。
##--format=docker image的格式是docker
##--squash 表示等等image裡面只會有一個壓縮檔,在Dockerfile裡面只要有一個run就會有一個壓縮檔。壓縮檔多耗的時間也多
##-t image取名
$ buildah bud --tls-verify=false --format=docker --squash -t alp.sshd .
* --squash 這參數在 image 中只產生一個 資料目錄 (layer), 但會保留 image 製作過程, 也就是 Dockerfile 內容
$ buildah images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/alp.sshd latest f662096fabbe 6 minutes ago 62.2 MB
$ podman run --name s1 -d -p 22100:22 localhost/alp.sshd
00125e52bcb7f56ef74d63a8937a67c41bd00f16c0f21eaf37092b8ee0598505
$ ssh bigred@localhost -p 22100
bigred@localhost's password: bigred
Welcome to ALP sshd 6000
00125e52bcb7:~$ exit
```
## Building a Image
```
##從alpine的image產生一個container,並且把container存在一個linux變數裡
$ container=$(buildah from alpine)
$ echo $container
alpine-working-container ##這是container的名稱
##到這個container裝套件,-- 是規定的格式,要對container下達命令要寫在這後面
$ buildah run $container -- apk add bash
$ buildah run $container -- apk add --update nodejs nodejs-npm
##設定container等等裡面要跑這個目錄
$ buildah config --workingdir /usr/src/app/ $container
##設定等等container要跑的網站
$ buildah run $container -- npm init -y
Wrote to /usr/src/app/package.json:
{
"name": "app",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
$ buildah run $container -- npm install express
```
攥寫javascript
```
$ nano HelloWorld.js
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
##把這個程式加到container
$ buildah copy $container HelloWorld.js
##設定entrypoint
$ buildah config --entrypoint "node HelloWorld.js" $container
WARN[0000] cmd "/bin/sh" exists but will be ignored because of entrypoint settings
##做出image名子叫做buildah-hello-world
$ buildah commit $container buildah-hello-world
```
## 使用 Image
```
$ buildah images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/buildah-hello-world latest d686509502c4 4 minutes ago 74.9 MB
localhost/alp.sshd latest f662096fabbe 53 minutes ago 62.2 MB
$ podman run -dt -p 3000:3000 buildah-hello-world
$ curl http://localhost:3000
Hello World!
```
## 檢視 image 備份檔 是否是 oci image 格式
* 使用 podman 做出 oci image 格式
```
$ sudo podman images
REPOSITORY TAG IMAGE ID CREATED SIZE
localhost/macque 3.0 84b5c3499dc1 29 seconds ago 17.6 MB
…
$ sudo podman save --format oci-archive localhost/macque:3.0 > /ckad/DF/macque-3.0.tar
```
```
$ skopeo copy oci-archive:/ckad/DF/macque-3.0.tar oci:/home/bigred/macque-oci:latest
Getting image source signatures
Copying blob ff457f537ccb done |
Copying blob 5808f1dc9432 done |
Copying config 330102a905 done |
Writing manifest to image destination
$ dir /home/bigred/macque-oci
total 24K
drwxr-sr-x 3 bigred wheel 4.0K Nov 6 22:48 .
drwxr-sr-x 1 bigred wheel 4.0K Nov 6 22:48 ..
drwxr-sr-x 3 bigred wheel 4.0K Nov 6 22:48 blobs
-rw-r--r-- 1 bigred wheel 247 Nov 6 22:48 index.json
-rw-r--r-- 1 bigred wheel 30 Nov 6 22:48 oci-layout
```
## 下載 windows donet image
```
$ sudo skopeo --override-os windows --insecure-policy copy --src-no-creds docker://mcr.microsoft.com/windows:ltsc2019 oci:/home/$USER/windows
```
###### tags: `系統工程`