antony@notes:~/container$ cat "Project-Quay-練習.md"
Project Quay 練習
Project Quay 練習
[TOC]
學習目標
- Red Hat Quay / Project Quay
- Project Quay 部署
- Image 上傳落地 Quay
- K8S 使用落地 Quay
- Docker Image 設計 (httpd)
- Docker Image 設計 (sshd)
- Image 備份與還原
Red Hat Quay
Red Hat 這家公司有做了一個系統 OpenShift,裡面就是 k8s Cluster 的完整生態系。 而 Quay 就是 image 儲存的空間 ( 光片中心 ),它是包裝在 OpenShift 裡面。
- docker hub
- docker 的 image 儲存空間
- 1個匿名 ip 在六小時內,只能下載 100 次光碟片,超過要付錢。
- Red Hat Quay
- 也可以下載 image ,不過下載都不用錢,無限制。
- Projecct Quay
- 企業如果要離線儲存 image 就要自己建立 Project Quay 來達到目的,也可以個人自己建立來使用,Red Hat Quay 的開源發行版。
部署前置作業
在 K8s Master 執行
## 安裝 acl 套件
$ sudo apk update; sudo apk add acl
## 透過 podman 來 run project-Quay (落地Quay or 個人光片中心)
$ wget wget https://web.flymks.com/quay-podman.tar.gz
$ tar xvfz quay-podman.tar.gz; cd quay-podman
## 執行程式
$ ./keyman.sh
Usage:
keyman.sh [options]
Available options:
ps podman ps -a
start deploy quay
stop delete quay
stop -v delete quay and hostPath volume
clean delete hostPath volume檢視目錄結構
$ tree
.
├── config
│ └── config.yaml
├── initdb.sql
├── keyman.sh
└── quay-pod.yaml ## 定義落地 quay 更改 k8s master node (m1) 和 worker nodes ( w1 , w2 ) 的 /etc/hosts
## 修改後的結果
$ sudo nano /etc/hosts
127.0.0.1 localhost alp
192.168.61.0 x1
192.168.61.1 x2
192.168.61.2 x3
192.168.61.3 a1
192.168.61.4 m1 quay
192.168.61.5 m2
192.168.61.6 w1
192.168.61.7 w2
192.168.61.8 w3
192.168.61.9 w4部署 Quay
Quay 部署時需要初始化,需等待3~5分鐘
$ sed -i "s|192.168.1.197|${IP}|g" keyman.sh
$ ./keyman.sh startImage 上傳落地 Quay
$ sudo podman images | grep 1.35.0
REPOSITORY TAG IMAGE ID CREATED SIZE
quay.io/flysangel/busybox 1.35.0 af4897967787 5 days ago 1.47 MB設定 image 的新 tag
$ sudo podman tag quay.io/flysangel/busybox:1.35.0 $IP/myquay/busybox:1.35.0後面的
$IP/myquay/busybox:1.35.0,$IP,代表我們的落地 quay 的 ip 位址 myquay 是落地 quay 的帳號busybox:1.35.0, image 的名字和代號
- 當版本是 latest 時,代表該光片維護者認定的最新安全穩定的版本,不一定是功能最多最新的。
檢查
$ sudo podman images | grep 1.35.0
REPOSITORY TAG IMAGE ID CREATED SIZE
192.168.61.4/myquay/busybox 1.35.0 af4897967787 5 days ago 1.47 MB
quay.io/flysangel/busybox 1.35.0 af4897967787 5 days ago 1.47 MB登入落地 Quay
$ sudo podman login $IP
Username: myquay
Password: myquay2022噴錯
Error: authenticating creds for "192.168.61.4": pinging container registry 192.168.61.4: Get "https://192.168.61.4/v2/": dial tcp 192.168.61.4:443: connect: connection refused加上參數 --tls-verify=false 再傳一次
$ sudo podman login --tls-verify=false $IP
Username: myquay
Password: myquay2022
Login Succeeded!因為我們沒有 https ,所以要靠
--tls-verify=false解決它
Image 上傳落地 Quay
$ sudo podman push --tls-verify=false $IP/myquay/busybox:1.35.0到quay 的網站上檢查

練習
請先 pull quay.io/flysangel/alpine:3.15.4 並將 image push 至落地 quay
請先 pull quay.io/flysangel/nginx:1.22.0 並將 image push 至落地 quay
# 第一題
## 拉 image
sudo podman pull quay.io/flysangel/alpine:3.15.4
## 檢查
sudo podman images | grep alpine
## 改 image 的標籤
sudo podman tag quay.io/flysangel/alpine:3.15.4 $IP/myquay/alpine:3.15.4
## 檢查
sudo podman images | grep alpine
## 將 image 上傳到落地 quay
sudo podman push --tls-verify=false $IP/myquay/alpine:3.15.4
# 第二題
## 拉 image
sudo podman pull quay.io/flysangel/nginx:1.22.0
## 檢查
sudo podman images | grep nginx
## 改 image 的標籤
sudo podman tag quay.io/flysangel/nginx:1.22.0 $IP/myquay/nginx:1.22.0
## 檢查
sudo podman images | grep nginx
## 將 image 上傳到落地 quay
sudo podman push --tls-verify=false $IP/myquay/nginx:1.22.0Image 上傳完之後通常都是私人的(不開放公開下載), 如果要公開設定如下

點右邊的齒輪,設成 Make Public
查看 k8s 提交的物件 運作資訊
$ kubectl describe < 物件 > < 物件名稱 >
## 舉例 pod
$ kubectl describe pod a1describe 裡面的詳細資訊都存在 etcd 裡面
看 pod 裡面 Container 運作過程的 log
$ kubectl logs < pod name >logs 的命令是 k8s 進到 Container 裡面把資訊輸出到螢幕上,Container 如果掛了,就抓不到他的 logs 了。
Container Image 設計
:::success
$ mkdir alpine.httpd
$ nano alpine.httpd/Dockerfile
FROM 192.168.61.4/myquay/alpine:3.15.4
RUN \
apk update && \
apk add --no-cache nano sudo bash wget curl git tree grep && \
wget https://busybox.net/downloads/binaries/1.28.1-defconfig-multiarch/busybox-x86_64 && \
chmod +x busybox-x86_64 && \
mv busybox-x86_64 bin/busybox1.28 && \
mkdir -p /opt/www && echo "let me go" > /opt/www/index.html
ENTRYPOINT ["/bin/busybox1.28"]
CMD ["httpd", "-f", "-h", "/opt/www"]ENTRYPOINT ["/bin/busybox1.28"],代表等等透過 Dockerfile 建立出來的 Container 裡面第一支跑的程式為/bin/busybox1.28
CMD ["httpd", "-f", "-h", "/opt/www"] ,代表程式後面接的參數為 httpd -f -h /opt/www ( -f ,前景執行; -h ,指定網頁目錄 )
:::
:::success
$ mkdir alpine.sshd
$ echo 'FROM 192.168.61.4/myquay/alpine.base:1.0.0
RUN \
apk update && \
apk add --no-cache 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"]' > alpine.sshd/Dockerfile /usr/sbin/sshd,執行 ssh Server 的命令-D,前景執行 (sshd will not detach and does not become a daemon)ENTRYPOINT,Container 啟動後預設不可隨意更改的程式CMD,當前面有ENTRYPOINT時,會變成給參數
$ kubectl run a1 --image=$IP/myquay/alpine.sshd:1.0.0
pod/a1 created失敗原因:CRI-O 原本就不允許使用 SSH 連線到 Container
要自訂 pod 的 yaml 檔,破格 CRI-O 的 capabilities
$ nano pod-ssh.yaml
apiVersion: v1
kind: Pod
metadata:
name: a1
labels:
app: a1
spec:
containers:
- name: a1
image: 192.168.61.4/myquay/alpine.sshd:1.0.0
imagePullPolicy: IfNotPresent
securityContext:
capabilities:
add: ["AUDIT_WRITE", "SYS_CHROOT", "CAP_NET_RAW"]
restartPolicy: Always:::
:::info CRI-O default 的 capabilities
default_capabilities = [
"CHOWN",
"DAC_OVERRIDE",
"FSETID",
"FOWNER",
"SETGID",
"SETUID",
"SETPCAP",
"NET_BIND_SERVICE",
"KILL",
]網址:CRI-O default 的 capabilities 連結 :::
練習2
:::success
題目:
請設計一個 Container Image 名為 alpine.base,代號 1.0.0,並滿足以下功能
需要能啟動 httpd
具有 bigred、rbean、gbean 帳號,密碼與帳號相同
登入 alpine 提示為 Welcome to ALP base 6666
第一隻程式為 sshd
將 Docker Image 上傳至落地 Quay
在 K8S 使用 alpine.base
kubectl apply -f pod-base.yaml
ssh bigred@a1_ip 並執行 sudo busybox1.28 httpd -f -h /opt/www
ssh rbean@a1_ip 並執行 curl localhost
答案:
$ nano alpine.base/Dockerfile
FROM 192.168.61.4/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 && \
# 啟動httpd
wget https://web.flymks.com/busybox-x86_64 && \
chmod +x busybox-x86_64 && \
mv busybox-x86_64 bin/busybox1.28 && \
mkdir -p /opt/www && echo "let me go" > /opt/www/index.html && \
# 設定時區
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 base 6666\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 && \
# 建立一般使用者 rbean gbean
adduser -s /bin/bash -h /home/rbean -D rbean && \
echo -e 'rbean\nrbean\n' | passwd rbean && \
adduser -s /bin/bash -h /home/gbean -D gbean && \
echo -e 'gbean\ngbean\n' | passwd gbean
ENTRYPOINT ["/usr/sbin/sshd"]
CMD ["-D"]sudo podman build --tls-verify=false -t $IP/myquay/alpine.base:1.0.0 alpine.basesudo podman images | grep base
sudo podman push --tls-verify=false $IP/myquay/alpine.base:1.0.0$ nano pod-base.yaml
apiVersion: v1
kind: Pod
metadata:
name: a1
labels:
app: a1
spec:
containers:
- name: a1
image: 192.168.61.4/myquay/alpine.base:1.0.0
imagePullPolicy: IfNotPresent
securityContext:
capabilities:
add: ["AUDIT_WRITE", "SYS_CHROOT", "CAP_NET_RAW"]
restartPolicy: Always$ kubectl apply -f pod-base.yaml
$ kg pods -o wide
$ ssh bigred@10.244.2.13 sudo busybox1.28 httpd -f -h /opt/www
$ ssh rbean@10.244.2.13:::
Container Image 設計 (fbs)
$ nano svc-fbs.yaml
kind: Service
apiVersion: v1
metadata:
name: svc-fbs
spec:
externalIPs:
- 192.168.61.4
ports:
- port: 8080
targetPort: 4000
selector:
name: pod-fbsexternalIPs 可以讓我們的 pod 從外面連進來
Distroless Image
$ mkdir distroless
$ wget https://web.flymks.com/app_golang/v1/goapp -O distroless/goapp; chmod a+x distroless/goapp測試 goapp
$ ./distroless/goapp &
$ curl -w '\n' localhost:8080
{"message":"Hello Golang"}
$ pkill -f 'goapp'$ echo 'FROM scratch
COPY goapp /
ENTRYPOINT ["/goapp"]' > distroless/Dockerfile- 因 執行的是編譯好的 GO 語言的網站,在任何作業系統都能直接跑,
- 所以可以從一個空白的 image 開始,而不用
FROM alpine - 好處是 image 會更小
Distroless Image 建立與測試
$ sudo podman build --tls-verify=false -t $IP/myquay/goapp:1.0.0 distroless build,Build an image using instructions from Containerfiles--tls-verify,require HTTPS and verify certificates when accessing the registry (default true)-t, --tag name, tagged name to apply to the built imagedistroless,目錄名稱,裡面放需要 build image 的檔案 (Dockerfile)
output :
STEP 1/3: FROM scratch
STEP 2/3: COPY goapp /
--> 0411ca03d77
STEP 3/3: ENTRYPOINT ["/goapp"]
COMMIT 192.168.64.21/myquay/goapp:1.0.0
--> 68227ba71f7
Successfully tagged 192.168.64.21/myquay/goapp:1.0.0
68227ba71f76403c49144cc9edcbe7cb15ae132f691c594e8946102b8c830c7$ sudo podman run --rm --name a1 -d -p 8080:8080 $IP/myquay/goapp:1.0.0$ curl -w '\n' localhost:8080 output :
{"message":"Hello Golang"}Distroless Image 上傳落地 Quay
$ sudo podman stop a1$ sudo podman login --tls-verify=false $IP
Username: myquay
Password: myquay2022Login Succeeded!
$ sudo podman push --tls-verify=false $IP/myquay/goapp:1.0.0 K8S 使用 goapp
K8S 執行
$ kubectl run a1 --image=$IP/myquay/goapp:1.0.0
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
a1 1/1 Running 0 2m1s 10.233.1.118 w1
$ curl -w '\n' 10.233.1.118:8080
{"message":"Hello Golang"}
刪除 a1 pod
$ kubectl delete pod a1image 建置流程
- 建存放 image 的資料夾
- 編輯 Dockerfile
- 建置 image (
podman build image) - 測試 image 功能是否正常 (
podman run) - 上傳至 image 中心 (
podman push) - image 設公開 ( Quay make public )
- 用 K8s 跑一次 (
kubectl run)
進企業後 image 建置流程
- 建立 image 的資料夾
- 編輯 Dockerfile
- podman buid
- 測試 podman run
- 本地測試
- CI ( 公司內部規定的一些測試 )
- 測試環境
- 正式環境
- image 上傳至落地 QUAY
- image public
- kubectl run