EXIN DevOps Foundation取りました
今年は、あまり勉強しておかないとこうと思っていたが、時間があったので、EXIN DevOps Foundationを受けてみた。この試験を選んだ主な理由は、
- CI/CDパイプラインの構築に携わっているため、DevOpsの概念から勉強してみたい
- DevOps, アジャイルの言葉はざっくりはわかるけど、どのように説明したらいいかわからない
- DevOps大変だけど、楽しい
といったぼんやりしたことからです。
EXINとは
EXINとは、ITILの認定試験を行うオランダの会社です。よく聞くAgile Scrum xxxの各認定資格も行っている企業かな。
EXIN DevOps Foundationとは
ITに携わる人でDevOpsの基礎を学び、理解するための認定資格であり、上位にはDevOps Professional, DevOps Masterがある。興味ある方は、ぜひ以下リンクから飛んでみてください。公式の練習問題もあります!!
https://embed.exin.totalservices.io/certifications/exin-devops-foundation-exam?language=ja
勉強時間・方法
公式では、60時間の勉強時間が目安となっている。おそらく私もこの時間ぐらい2ヶ月と、だらだら勉強した。
8月から資格取ることを決意しました。以下のUdemyのコースをセール時に購入して、気力ある時に週2/3日に1時間程度を勉強しました。日本語翻訳がないので、英語字幕にして学んでました。
9月からも、同じく週2/3日に1時間程度を公式の練習問題を解いては、内容を覚えていた。あとは、公式からキーワードリストが公開されているので、その内容と関係性など覚えました。
ちなみに、以下のガイドブックが公式のガイドブックになっています。
受験方法
公式サイトから購入した後に、メールが来ます。受信メールに記載されているURLを使えば、24時間いつでもオンラインで受験できます。受験するにあたって、顔写真付き身分証明証の準備とカメラ付きPCを準備して、試験を開始します。ビデオ録画になるので、試験管とのやりとりはなかったです。
結果
公式の練習問題以外は、情報が全くないので、恐る恐る受験しました。結果は、68%(27/40)のスコアでした。あと1問間違えてたらアウトでした。
感想
DevOpsでよく聞くアジャイル、リーン、デプロイメントなどとは何か、これらによって、何が期待できるかなど学べてた気がする。DevOpsを実践するにあたって、ビジネスと組織にどのような影響を与えるか、期待ができるかなど学びたい初学者向きに試験だと思います。
AWS/GCP/Azureなどのサービスを使って、実践的なスキルをつけたい方は各クラウドベンダーの試験を受けた方がいいかなと思います。
気が向いたら、上位のDevOps Professionalも勉強して、取ってみようかなと思います。(年末以降...今年はもう遊ぼう
閉域環境のラズパイ4にk3sをインストールする
背景
閉域(インターネットへ疎通ができない)環境に対して、k3sをインストールすることがあったので、備忘録として書きます。
*閉域環境を英語ではair-gapと呼ばれることがある。Rancher公式サイトにはair-gapと記載されている。
環境
- ラズパイ4
- Ubuntu 18.04
- aarch64 (arm64)
- k3s v1.23.6+k3s1
閉域環境へk3sインスール
1. k3sのimage/バイナリとinstall.shをダウンロード
GitHubからローカル環境に手動でk3sのimageとバイナリをダウンロードします。本環境では、現時点(2022/05/10)で最新のv1.23.6+k3s1を使用します。
https://github.com/k3s-io/k3s/releases
次に、ローカル環境にラズパイサーバーで実行するk3sインストール用スクリプトをダウンロードします。
# get.k3s.ioからスクリプトをダウンロード % curl https://get.k3s.io -o ~/Downloads/install.sh % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 27501 100 27501 0 0 26412 0 0:00:01 0:00:01 --:--:-- 26622 # ローカル環境にダウンロードできたか確認 % ls ~/Downloads/install.sh /Users/****/Downloads/install.sh
2. ラズパイサーバーへ転送
先ほどダウンロードしたものをラズパイサーバーへ転送します。
# localからラズパイサーバーへimageを転送 % scp ~/Downloads/k3s-airgap-images-arm64.tar ubuntu@192.168.3.101:/home/ubuntu ubuntu@192.168.3.101's password: k3s-airgap-images-arm64.tar 100% 462MB 4.5MB/s 01:42 % scp ~/Downloads/k3s ubuntu@192.168.3.101:/home/ubuntu ubuntu@192.168.3.101's password: k3s 100% 60MB 6.1MB/s 00:09 % scp ~/Downloads/install.sh ubuntu@192.168.3.101:/home/ubuntu ubuntu@192.168.3.101's password: install.sh 100% 27KB 2.2MB/s 00:00 # ラズパイサーバーでファイルを確認 ubuntu@ras01:~$ ls -lh total 522M -rw-r--r-- 1 ubuntu ubuntu 27K Dec 15 19:39 install.sh -rw-r--r-- 1 ubuntu ubuntu 60M Dec 15 19:37 k3s -rw-r--r-- 1 ubuntu ubuntu 462M Dec 15 19:25 k3s-airgap-images-arm64.tar # /usr/local/bin配下にk3sバイナリを格納 ubuntu@ras01:~$ sudo mv k3s-arm64 /usr/local/bin/k3s ubuntu@ras01:~$ ls /usr/local/bin/ k3s # /var/lib/rancher/k3s/agent/images配下にk3s imageをコピー sudo mkdir -p /var/lib/rancher/k3s/agent/images/ sudo cp k3s-airgap-images-arm64.tar /var/lib/rancher/k3s/agent/images/
3. スクリプトとk3sバイナリに実行権限を付与
# install.shに実行権限を付与 ubuntu@ras01:~$ sudo chmod +x install.sh ubuntu@ras01:~$ ls -lh install.sh -rwxr-xr-x 1 ubuntu ubuntu 27K Dec 15 19:39 install.sh # k3sバイナリに実行権限を付与 ubuntu@ras01:~$ sudo chmod +x /usr/local/bin/k3s ubuntu@ras01:~$ ls -lh /usr/local/bin/ total 60M -rwxr-xr-x 1 ubuntu ubuntu 60M Dec 15 19:37 k3s
4. k3sインストールを実行
install.sh
を実行して、ラズパイサーバーにk3sをインストールします。
# インストールの実行 ubuntu@ras01:~$ INSTALL_K3S_SKIP_DOWNLOAD=true ./install.sh [INFO] Skipping k3s download and verify [INFO] Skipping installation of SELinux RPM [INFO] Skipping /usr/local/bin/kubectl symlink to k3s, command exists in PATH at /usr/bin/kubectl [INFO] Skipping /usr/local/bin/crictl symlink to k3s, command exists in PATH at /usr/bin/crictl [INFO] Skipping /usr/local/bin/ctr symlink to k3s, command exists in PATH at /usr/bin/ctr [INFO] Creating killall script /usr/local/bin/k3s-killall.sh [INFO] Creating uninstall script /usr/local/bin/k3s-uninstall.sh [INFO] env: Creating environment file /etc/systemd/system/k3s.service.env [INFO] systemd: Creating service file /etc/systemd/system/k3s.service [INFO] systemd: Enabling k3s unit Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service. [INFO] systemd: Starting k3s # k3sのステータス確認 ubuntu@ras01:~$ systemctl status k3s ● k3s.service - Lightweight Kubernetes Loaded: loaded (/etc/systemd/system/k3s.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2021-12-15 19:54:15 JST; 1min 44s ago Docs: https://k3s.io Process: 18730 ExecStartPre=/sbin/modprobe overlay (code=exited, status=0/SUCCESS) Process: 18725 ExecStartPre=/sbin/modprobe br_netfilter (code=exited, status=0/SUCCESS) Process: 18723 ExecStartPre=/bin/sh -xc ! /usr/bin/systemctl is-enabled --quiet nm-cloud-setup.service (code=exited, status=0/SUCCESS) Main PID: 18733 (k3s-server) Tasks: 65 CGroup: /system.slice/k3s.service ├─18733 /usr/local/bin/k3s server └─18894 containerd -c /var/lib/rancher/k3s/agent/etc/containerd/config.toml -a /run/k3s/containerd/containerd.sock --state /run/k3s/containerd --root /var/lib/rancher/k3s Dec 15 19:55:37 ras01 k3s[18733]: E1215 19:55:37.805540 18733 remote_runtime.go:209] "RunPodSandbox from runtime service failed" err="rpc error: code = Unknown desc = failed to ge Dec 15 19:55:37 ras01 k3s[18733]: E1215 19:55:37.805691 18733 kuberuntime_sandbox.go:70] "Failed to create sandbox for pod" err="rpc error: code = Unknown desc = failed to get san Dec 15 19:55:37 ras01 k3s[18733]: E1215 19:55:37.805771 18733 kuberuntime_manager.go:833] "CreatePodSandbox for pod failed" err="rpc error: code = Unknown desc = failed to get san Dec 15 19:55:37 ras01 k3s[18733]: E1215 19:55:37.805964 18733 pod_workers.go:951] "Error syncing pod, skipping" err="failed to \"CreatePodSandbox\" for \"metrics-server-7cd5fcb6b7 Dec 15 19:55:45 ras01 k3s[18733]: E1215 19:55:45.015469 18733 resource_quota_controller.go:413] unable to retrieve the complete list of server APIs: metrics.k8s.io/v1beta1: the se Dec 15 19:55:45 ras01 k3s[18733]: W1215 19:55:45.523766 18733 garbagecollector.go:707] failed to discover some groups: map[metrics.k8s.io/v1beta1:the server is currently unable to Dec 15 19:55:46 ras01 k3s[18733]: W1215 19:55:46.502560 18733 handler_proxy.go:104] no RequestInfo found in the context Dec 15 19:55:46 ras01 k3s[18733]: E1215 19:55:46.502809 18733 controller.go:116] loading OpenAPI spec for "v1beta1.metrics.k8s.io" failed with: failed to retrieve openAPI spec, ht Dec 15 19:55:46 ras01 k3s[18733]: , Header: map[Content-Type:[text/plain; charset=utf-8] X-Content-Type-Options:[nosniff]] Dec 15 19:55:46 ras01 k3s[18733]: I1215 19:55:46.502881 18733 controller.go:129] OpenAPI AggregationController: action for item v1beta1.metrics.k8s.io: Rate Limited Requeue.
5. kubectlの確認
この段階ではPATH設定などを行なっていないため、kubectl が使えません。最後に、kubectlコマンドが使えるように以下を設定していきます。
# kubectlの実行 (この段階ではrefusedとなる) ubuntu@ras01:~$ kubectl get nodes The connection to the server localhost:8080 was refused - did you specify the right host or port? # kubectlのパスを設定 ubuntu@ras01:~$ echo "export KUBECONFIG=/etc/rancher/k3s/k3s.yaml" >> ~/.bashrc ubuntu@ras01:~$ source ~/.bashrc # ファイル所有者を一般ユーザーに変更 ubuntu@ras01:~$ sudo chown ubuntu /etc/rancher/k3s/k3s.yaml ubuntu@ras01:~$ ls -lh /etc/rancher/k3s/k3s.yaml -rw------- 1 ubuntu root 2.9K Dec 15 19:54 /etc/rancher/k3s/k3s.yaml # kubectlの実行 ubuntu@ras01:~$ kubectl get nodes NAME STATUS ROLES AGE VERSION ras01 Ready control-plane,master 6m55s v1.23.6+k3s1
6. お片付け
k3sをアンインストールしたい場合は、以下のスクリプトを実行すれば、アンインストールが実行されます。
ubuntu@ras01: /usr/local/bin/k3s-uninstall.sh
参照
2021年読んだ本 まとめ
2019~2021年の間、本格的に業務範囲がiOSからk8sにチェンジしたことによって、2021年に読んだ本を紹介します。各本に対して、なぜ読んだのか、何がよかったのかを書いております。
読んだ本リスト
- Linuxのしくみ
- Kubernetes on AWS アプリケーションエンジニア本番環境へ備える
- マイクロサービスアーキテクチャ
- New Relic 実践入門
- エンジニアリング組織論への招待
- SREサイトリライアビリティエンジニアリング
- SREの探求
- 分散システムデザインパターン
- 実践Terraform AWSにおけるシステム設計とベストプラクティス
- マスタリングTCP/IP 応用編 SNMP編 SSL/TLS編
- Wiresharkではじめるパケット解析入門
- 転職2.0
Linuxのしくみ
Linuxサーバーを触る機会が増えたので、基本的なメモリ/CPU/プロセスの動作を勉強するために購入しました。少し難しい章があるが、全体的にわかりやすい図解でまとめられていて良い本になります。Linuxの仕組みで困った際に、何度も振り返りたい本かと思います。
Kubernetes on AWS アプリケーションエンジニア本番環境へ備える
昨今、AWS/GCPなどのクラウドプロバイダーが提供するk8sプラットファームが増えているので、自主勉強用に購入しました。業務では、オンプレk8s環境しか触ってこなかったので、AWSでk8sを動かしたい方にオススメになります。また自宅サーバー(ラズパイなど)でk8sを構築する際に、ノードのOS設定やCPUのアーキテクチャを考慮しなければならないので、手軽に試したい方にオススメになります。
マイクロサービスアーキテクチャ
マイクロサービスの概念がよくわからなかったため、購入しました。本を読めばマイクロサービスが実現できると言うわけではありません。これまでのシステムと比べ、マイクロサービスの概念が、近年のITシステムの要求に近づけれるのではないかという内容になっていると思います。マイクロサービスについて知りたい方への良本になります。
New Relic 実践入門
オブザーバビリティを実現するNew Relicの実践方法を紹介されている本になります。現時点で無料枠があるので、無料でNew Relicを試したい方にオススメになります。New Relicも日々新しい機能がリリースされているので、時間が経てば、内容が古くなっている可能性もあります。
エンジニアリング組織論への招待
昨年度から少人数ながらメンバーを持たせて頂いたため、チームビルディングするならきちんとしたエンジニアリングがしたいと思い、本書を購入しました。いわゆる開発チームの作りではなく、不明確な問題に対するチーム作りとして書かれているので、非開発チーム(運用保守やビジネスサイドなど)でもアジャイルを実践できるのではないかと思いました。(ちなみにチームは会社都合で1年後に解散しましたが .....)
SREの探求
SREの興味本心で購入しました。SREを導入したい組織において、米国有名企業の例などを用いて、どのようなアプローチしていけば、SREとして実践できるかという内容になっているかと思います。読んでいて、オンコール反対論に関して、非常に共感を得ました。章によっては、理解が難しい内容があったため、振り返り読むことが良いかなと思っています。
分散システムデザインパターン
現場のk8s上のコンテナのデザインが開発側から情報提供がなかったため、自分自身でそのデザインを理解するために購入しました。タイトル通り、分散システム(k8sやOpenStackなど)のデザインパターンが説明されている本になります。200ページぐらいで上手く内容がまとめられています。コンテナを扱ったシステムに関わっている方は、読みやすいので、一度読んでおいた方が良い本かと思います。
実践Terraform AWSにおけるシステム設計とベストプラクティス
Kubernetes on AWSを試した後、AWS上のIaCに興味を持って、購入しました。しかし、あまりTerraformをいじる時間なかったので、今後機会があれば、再びチャレンジしたいと思ってます。 www.amazon.co.jp
マスタリングTCP/IP 応用編 SNMP編 SSL/TLS編
SNMPv2/SNMPv3/FTP/FTPS/SFTPに関して、学びたかったため購入しました。学びたいプロトコルによっては、内容が薄いこともあるが、持っておいて損はない本だと思います。大学の頃を思うと、入門編以外に3冊も購入することになるとは思わなかった.....。
Wiresharkではじめるパケット解析入門
タイトル通り、Wiresharkを使ってパケット解析をしたい方とKindle unlimitedの方に対してオススメの本である。ただ、Windows環境での説明になるので、Mac/Linux環境の方は、Wiresharkの設定は別途調べる必要があります。
転職2.0
自分のキャリアに悩んでしまったので、購入しました。LinkedIn 日本代表の村上 臣さんが現代のキャリア論について述べられている本になります。著者の経歴がありきでのキャリア論になっているので、万人に当てはまるかはやや怪しいです。ただし、従来の終身雇用におけるキャリア論とは全く別物になっているので、今後の転職市場では本書で述べられているようなキャリアを持つ人材が有利になっていくのではないかと思います。
所感
2021年において、本ブログで挙げた本だけでも、月1で新しい知識をアップデートしていたので、今年もよく生き残ったなと思います。来年から、業務内容がかわるので、分散システムにおけるGitOps(CI/CD)やテストケースなど知識をもっと深くして、物にできればと思います。あと引き続き、英語もがんばります .....
FTP/FTPS/SFTPの備忘録
はじめに
FTP/FTPS/SFTPについて調べたことを忘れないための備忘録になります。
FTP
File Transfer Protocolの略であり、基本的にクライアント-サーバ間においてファイル転送を行う通信プロトコルである。
チャネル
FTPでは、ファイル転送を行うために、2つの通信用チャネルを使用して動作する。
- 制御チャネル: ポート番号 21, 主にFTPコマンドの送受信を行うためのチャネル。
- データチャネル: ポート番号 20, ファイルデータの送受信を行うためのチャネル。但し、パッシブモードの場合は、ランダムなポートが選出される。
モード
FTPでは、クライアント-サーバ間において、どちらに対して接続を行うかの2つのモードがある。
- アクティブモード: サーバからクライアントに対して、データチャネル用ポートを通知して、ファイルデータの送受信を行う。
- パッシブモード: クライアントからデータチャネル用のランダムなポート選出して、サーバへ通知し、ファイルデータの送受信を行う。
FTPS
FTP over SSL/TLSの略であり、SSL/TLSを使用してデータチャネルを送受信を暗号化する。
モード
- 明示モード (explicit): クライアントが暗号化に使用するプロトコルを選択して、暗号化してファイル転送を行う。
- 暗黙モード (implicit): クライアントは、サーバが使用しているプロトコルに対してのみ暗号化されたファイル転送が可能になる。
SFTP
SSH FTPの略であり、SSHで暗号化されたチャネルを用いて、安全なデータの送受信を行う。
所感
FTPを学ぶ必要があったため、FTP関連のことをまとめてみました。実際の環境に使われているファイル転送のプロトコルが、FTP/FTPS/SFTPなのか把握していないと、どのレイヤーで問題が起きているか時間がかかりそうだと思う。特にSSL/TLSとSSHの場合だと、サーバ側の調査も必要になる。
参考文献
Wiresharkを使って、FTPパケットのやりとりを見よう
はじめに
FTPを使ったアプリケーションに問題があった際に、正常であるFTPの挙動を確認しました。その際に、Wiresharkを使った方法を備忘録として残してます。
docker 環境
FTP Server: stilliard/pure-ftpd
https://hub.docker.com/r/stilliard/pure-ftpd/
docker 環境の構築
今回はFTP ServerとClientを分けて、dockerホストを2つ作成します。最初に、docker環境上でFTP Serverを構築します。
FTP Server
前述したpure-ftpdのサンプルコマンドで、docker環境上でFTP Serverを立ち上げます。
~ $ :~$ docker run -d --name ftpd_server -p 21:21 -p 30000-30009:30000-30009 -e "PUBLICHOST=localhost" stilliard/pure-ftpd
ftp-userを作成するために、先ほど作成したftpd_server
にアクセスします。
# 先ほど作成したpure-ftpdのdocker process ~ $ :~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7382b1acd311 stilliard/pure-ftpd "/bin/sh -c '/run.sh…" About a minute ago Up About a minute 0.0.0.0:21->21/tcp, :::21->21/tcp, 0.0.0.0:30000-30009->30000-30009/tcp, :::30000-30009->30000-30009/tcp ftpd_server # /bin/bashを実行 ~ $ :~$ docker exec -it 7382b1acd311 /bin/bash # ftpアップロード先のディレクトリ test-ftp 作成 root@7382b1acd311:~# mkdir /home/ftpusers/test-ftp # ftp-userの作成 # ftp-user: test-ftp # Uplaod先:/home/ftpusers/test-ftp/ root@7382b1acd311:~# pure-pw useradd test-ftp -f /etc/pure-ftpd/passwd/pureftpd.passwd -m -u ftpuser -d /home/ftpusers/test-ftp/ Password: Enter it again: # ディレクトリのアクセス権の付与 root@7382b1acd311:~# chown test-ftp:ftpgroup /home/ftpusers/test-ftp/ # logout root@7382b1acd311:~# exit
FTP Client
FTP Clientととして、docker環境上にUbuntu 18.04を起動する。
~ $ :~$ docker run -it -d --name ftpd_client ubuntu:18.04 /bin/bash
dockerが作成されたことを確認して、必要なパッケージをインストールします。今回は、ftp
とtcpdump
だけインストールします。
~ $ :~$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b1457c177a5a ubuntu:18.04 "/bin/bash" About a minute ago Up About a minute ftpd_client 7382b1acd311 stilliard/pure-ftpd "/bin/sh -c '/run.sh…" 16 minutes ago Up 16 minutes 0.0.0.0:21->21/tcp, :::21->21/tcp, 0.0.0.0:30000-30009->30000-30009/tcp, :::30000-30009->30000-30009/tcp ftpd_server ~ $ :~$ docker exec -it b1457c177a5a /bin/bash # パッケージの一覧を更新 root@b1457c177a5a:/# apt-get update # ftpのインストール root@b1457c177a5a:/# apt-get install ftp # tcpdumpのインストール root@b1457c177a5a:/# apt-get install tcpdump
FTPの挙動確認
FTP Serverであるftpd_server
のIPv4アドレスを確認します。そのIPv4は、FTP Clientのftpで使われるのでコピーしておきます。
~ $ :~$ docker inspect network bridge .... "Containers": { "7382b1acd3117c281148aacb75f11f1e509e128351da48094fe63d6dabff0b7c": { "Name": "ftpd_server", "EndpointID": "9906eba71be08fee78e4a7f2a09dae004db3214d375cdf86e316609fea57354a", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "2001:db8:1::242:ac11:2/64" }, }, ....
後ほど、WiresharkでFTPのパケットを見るので、もう1つターミナルを立ち上げて、FTP Clientにアクセスします。その後、tcpdumpによってpcapを作成します。
~ $ :~$ docker exec -it b1457c177a5a /bin/bash # tcpdumpを使って、pcapを取得 root@b1457c177a5a:/# tcpdump -w ftp.pcap
次に、FTP Clientからテストのファイルを作成して、ftpを実行します。
# テストファイル用の作成 root@b1457c177a5a:~# echo "test" > test.txt # FTPの実行 root@b1457c177a5a:~# ftp 172.17.0.2 Connected to 172.17.0.2. 220---------- Welcome to Pure-FTPd [privsep] [TLS] ---------- 220-You are user number 1 of 5 allowed. 220-Local time is now 06:22. Server port: 21. 220-This is a private system - No anonymous login 220-IPv6 connections are also welcome on this server. 220 You will be disconnected after 15 minutes of inactivity. Name (172.17.0.2:root): test-ftp 331 User test-ftp OK. Password required Password: 230 OK. Current directory is / Remote system type is UNIX. Using binary mode to transfer files. ftp> put test.txt local: test.txt remote: test.txt 200 PORT command successful 150 Connecting to port 33289 226-File successfully transferred 226 0.001 seconds (measured here), 9.30 Kbytes per second 5 bytes sent in 0.00 secs (28.2243 kB/s) ftp> exit 221-Goodbye. You uploaded 1 and downloaded 0 kbytes. 221 Logout.
FTP Serverでtest.txt
がアップロードされていることを、以下のコマンドで確認します。
# テストファイルがアップロードされているか確認 root@7382b1acd311:/# ls /home/ftpusers/test-ftp/ test.txt
最後に、docker環境内において、FTPのパケットが保存されたftp.pcap
をlocalhostへコピーします。
# localhostにpcapのコピー ~ $ :~$ docker cp b1457c177a5a:ftp.pcap ~/Desktop
Wiresharkを使った挙動の確認
今回は、下図に流れに沿ってFTPの挙動を確認します。
FTP Serverがデータコネクション用のPort 20を通知
WiresharkでFTPパケットのやりとりを表示したのが、下図になります。
所感
実運用において問題が発生した際に、正常な通信の挙動を把握していないと、どのプロセス段階で問題が起きているが把握出来なかった。今回、Wiresharkを使ってFTPの正常な挙動を確認してみた。このようなことを積み重ねさえすれば、トラブルシューティングのノウハウの土台となるはずである。
参考文献
【Docker】FTPサーバコンテナ構築手順と使い方(Pure-ftpd Server) | インフラエンジニアの技術LOG