Kategori arşivi: Docker

Docker yapılandırma örnekleri, dockerfile yazım teknikleri, dockerfile nasıl yazılır, docker compose kullanımı, docker güvenlik ipuçları ve daha fazlası

CentOS 7 Kullanan Docker İmajlarında mirror hatasının giderilmesi

Biliyorsunuz CentOS 7 bugün EOL oldu ve kullanılamıyor. Geliştirme süreçlerinde ise hızlı aksiyon almanız gerekiyorsa ve hali hazırda kullandığınız yazılıma güncelleme gelmediyse aşağıdaki işlemleri yapın.

Hatadan örnek vermem gerekirse

  1. centos-sclo-rh paketini bulamıyor
  2. direk olarak mirrorliste erişemiyor.

Docker’da en kolay çözüm yöntemi ise şu şekilde kullanabilirsiniz.

# Workaround the fact that CentOS 7 is EOL
RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-* \
  && sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-* \
  && yum-config-manager --disable centos-sclo-rh \
  && yum install epel-release -y
ShellScript

Silinmeyen Docker Ağları Otomatik Olarak Nasıl Kaldırılır?

docker compose kurulumlarda çok fazla kullanılıyor pratikte çokta işe yarıyor ama zaman zaman hataya düşebiliyor, bir işlem yapıyorsunuz ve çakışmalara, aynı ağ adlarını almaya başlıyor.

Bu durumda docker güncellemelerinde ve/veya sistem güncellemelerinden sonra ortaya çıkıyor. Ağların ve konteyner’ların başlamasına engel oluyor.

Örnek bir hata mesajı

Sep 01 18:30:15 debian docker compose[2281641]: Removing network mertcan_default
Sep 01 18:30:15 debian docker compose[2281641]: network mertcan_default is ambiguous (2 matches found based on name)
Sep 01 18:30:16 debian systemd[1]: mertcan_default.service: Control process exited, code=exited, status=1/FAILURE

Aşağıdaki gibi yazdığımız ufacık bir python kodu ile sistemden başa bela olan bu ağları otomatik kaldırma işlemi yapabilirsiniz.

import subprocess
import json
import logging

logger = logging.getLogger(__name__)

output = subprocess.check_output(["docker", "network", "ls", "--format", "{{json .}}"])
networks = set()  # use a set to avoid duplicates

for line in output.decode("utf-8").splitlines():
    # check line is not empty
    if not line:
        logger.debug("Empty line found in docker network ls output (ignoring)")
        continue

    network = json.loads(line)
    if network["Name"] in networks:
        logger.info("Removing duplicate network: %s", network["Name"])
        subprocess.check_call(["docker", "network", "rm", network["Name"]])

    networks.add(network["Name"])

Kullanılan Özel Container Registrylerin Watchtower ile Güncellenmesi

Bazı şeyleri otomatize ederken oto deployun yanı sıra otomatik güncelleme yapmakta önemli, bunun için rsync veya ssh üzerinden güncelleme yapmak yerine direk olarak docker containerı güncelleyebilirsiniz.

Bu işlem için ise direk olarak docker container build etmemiz bizim için yeterli, uygulamamız bunun içerisinde çalışacak ve watchtower bunu otomatik güncelleyecek.

İmaj oluşturmak için .gitlab-ci.yml için aşağıdaki yapılandırmayı kullanabilirsiniz.

image: docker:latest

docker-build:
  stage: build

  before_script:
    - docker info
    - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"

  variables:
    DOCKER_DRIVER: overlay2
    DOCKER_TLS_CERTDIR: "/certs"
    IMAGE_TAG: $CI_REGISTRY_IMAGE:latest

  script:
    - docker build -t "$IMAGE_TAG" .
    - docker push "$IMAGE_TAG"

Bu yapılandırma direk olarak mastera gönderilen her kodu container haline getirecek ve gitlab “container registry” içerisine basacak. Burada çift branch kullanmanızı öneririm. dev ve master olarak yoksa otomatik güncellemede sorunlarla karşılaşabilirsiniz.

Dockerfile yapılandırmamız ise aşağıdaki gibi, NodeJS uygulamamız çalışıyor siz kendinize göre güzel bir yapılandırma uygulayın.

FROM node:17

RUN set -eux; \
  npm install -g pm2

COPY app .

RUN npm install

CMD ["pm2-runtime", "index.js"]

İşlemler tamamlandığında imajımız gitlab’da duracak, isterseniz bu imajı docker hub üzerinede gönderebilirsiniz, karar size kalmış. Gitlab kullandığımız için biz başka bir alanda barındırmak istemedik.

Otomatik güncelleştirme kısmına geldik aşağıdaki gibi bir docker-compose.yml dosyası işimizi fazlasıyla görecek

version: "3.7"
services:
  monit:
    image: developer-core-team/monitoring_v01
    container_name: core_monitoring
    restart: always
  watchtower:
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /root/.docker/config.json:/config.json
    command: --interval 30

Yapılandırmayı kendinize göre özelleştirmekte özgürsünüz 30 saniyede bir container registrymiz kontrol edilecek varsa güncelleme alınacak, isterseniz bu süreyi arttırabilirsiniz.

Docker Notlarım

Docker CLI ve Docker Compose ile ilgili sıkça kullandığım komutlar yer almaktadır. bu komutları terminal aracınıza kaydedip snippet olarak kullanabilirsiniz. Termius ve xShell üzerinde işlemlerinizi yapabilirsiniz.

Docker-compose Kullanarak Mevcut Docker Imajları Nasıl Güncellenir?

Birden fazla geliştirme ortamı kullandığımız ve imajların güncelliğini yitirdiği senaryolar olabiliyor, mevcut bir konteyneri silmek istemeyebiliriz. Kullanılan bu konteyner production ortamında çalışıyor ve üzerinde işlem yapılıyor olabilir.

Docker kardeşimizde imajlar geçici olacak şekilde tasarlanmıştır. Yani mevcut bir imajı güncellemek için eskisini mecbur kaldırır ve yenisini başlatırsınız.

Bu işlemi yapmak için aşağıdaki prosedürü izlemeniz yeterlidir.

docker-compose pull
docker-compose up -d --remove-orphans
docker image prune -f

Bir imajın indirilmesinin başarısız olacağı senaryolar olabilir, kesintilerin sizin için önemli olacağı senaryolar olabilir. Bu sebeple tekrar imajı composer üzerinden pull etmeden ayağa kaldırma komutumuzu vermiyoruz. Güncel imajı indirmek ve bunun üzerinden tekrar konteyner ayağa kaldırmak bizim için önemli.

Docker ile Gitea Git Sunucusu Nasıl Kurulur?

Gitea ile kendinize özgü kişiselleştirilmiş git sunucusu kurabilir, repolarınızı kendi bünyenizde barındırabilir siniz. Normalde kurulum biraz daha uzun ancak Docker kullanarak bu kurulumu sancısız atlatmak ve çok basit bir şekilde kullanmak mümkün

Resmi belgelendirmede paylaşılan docker-compose.yml dosyasının aksine ben uygulamayı lokalde çalıştırmayı seçtim ve dışarıya da NGINX ile açma yoluna gittim. Çalıştırmak için;

docker-compose up -d

Düzenlediğim dosya şu şekilde

version: "3"

networks:
  gitea:
    external: false

volumes:
  gitea:
    driver: local


services:
  server:
    image: gitea/gitea:latest
    container_name: gitea
    environment:
      - USER_UID=1000
      - USER_GID=1000
      - DB_TYPE=postgres
      - DB_HOST=db:5432
      - DB_NAME=gitea
      - DB_USER=gitea
      - DB_PASSWD=change-me
    restart: always
    networks:
      - gitea
    volumes:
      - gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
    ports:
      - "127.0.0.1:3000:3000"
      - "22:22"
    depends_on:
      - db

  db:
    image: postgres:latest
    restart: always
    environment:
      - POSTGRES_USER=gitea
      - POSTGRES_PASSWORD=change-me
      - POSTGRES_DB=gitea
    networks:
      - gitea
    volumes:
      - ./postgres:/var/lib/postgresql/data

Hemen ardından kullandığım, NGINX yapılandırma dosyası ise şu şekilde

server {
    listen                  443 ssl http2;
    listen                  [::]:443 ssl http2;
    server_name             git.mertcan.dev;
    root                    /var/www/git.mertcan.dev/public;

    # SSL
    ssl_certificate         /root/.acme.sh/mertcan.dev/fullchain.cer;
    ssl_certificate_key     /root/.acme.sh/mertcan.dev/mertcan.dev.key;
    ssl_trusted_certificate /root/.acme.sh/mertcan.dev/fullchain.cer;

    # reverse proxy
    location / {
        proxy_pass http://localhost:3000;
    }

}

Ardından NGINX yeniden başlattığınız anda kurulum ekranı karşınıza çıkacak, gerisi size kalmış durumda. Gitea uygulaması üzerinde yapacağınız değişiklikler için /var/lib/docker/volumes/gitea_gitea/_data/gitea/conf/ yolu içerisinde yer alan app.ini dosyasını değiştiriniz.

Docker Container Log Dosyalarının Boyutu Nasıl Küçültülür?

Yanlış yapılandırılan her docker konteyneri dışarıya yüklü miktarda log basar, disk alanı az olan sunucularda sistem yöneticileri güneşi hızlı bir şekilde görebilir ve servis kesintileri yaşayabilir.

Aşağıda yapacaklarımızdan hemen önce lütfen docker-compose dosyanız üzerinde gerekli olan ince ayarları yapınız, aksi taktirde yapacağınız bu işlemlerin log dosyalarının boyutunu düşürmede bir etkisi olmayacaktır.

logging:
      driver: "json-file"
      options:
        max-size: "1g"
        max-file: "10"

Bir müşteride mevcut logların boyutunu şu şekilde gördüm, burada anladığım kadarıyla 2 konteyner aktif olarak kullanılmış ve üzerinde ciddi anlamda işlem yapılmış ve log üretilmiş.

-rw-r----- 1 root root 40,2M 2020-08-20  /var/lib/docker/containers/cff872f1769dfed9c36d5f7e61627d2ff77973441325b07847fb4874ef1a0acf/cff872f1769dfed9c36d5f7e61627d2ff77973441325b07847fb4874ef1a0acf-json.log
-rw-r----- 1 root root 8,2M 2020-08-20  /var/lib/docker/containers/02a78f490f6b7ace252f3126689cf9200f488e94b909b9a2e5ee29613a1ee305/02a78f490f6b7ace252f3126689cf9200f488e94b909b9a2e5ee29613a1ee305-json.log
-rw-r----- 1 root root 3,3M 06-10 11:19 /var/lib/docker/containers/4cb9f353b394287dff456c1ce08d13c1a3206ad902e44eb5a5f3cd33e747320c/4cb9f353b394287dff456c1ce08d13c1a3206ad902e44eb5a5f3cd33e747320c-json.log
-rw-r----- 1 root root 578G 09-22 10:12 /var/lib/docker/containers/7e1bb748c785343f47511e08cb10aa676df09d4fc4abe1f371bc7b49a3d88b9d/7e1bb748c785343f47511e08cb10aa676df09d4fc4abe1f371bc7b49a3d88b9d-json.log

Bu loga ulaşmak için şu komutu kullandım

docker ps -aq | xargs -I'{}' docker inspect --format='{{.LogPath}}' '{}' | xargs ls -lh

Müşteri bana logların önemli olabilecek verileri uzak storageda tutabileceğini ve gerektiği kadarını silmemi (bence saçma, bir işine yaramaz bu log ama neyse)

cat /var/lib/docker/containers/*-json.log | bzip2 --best --compress --stdout > /mnt/nfs-storage-shared/2020-09-22.log.bz2

Ardından log dosyasını bölmem ve çoğunu kesmem gerektiği için apt install util-linux kurulumu yaptım(ilginç şekilde yüklü değildi), ardından fallocate ile dosyayı bölme işlemine geçiş yaptım ve aşağıdaki gibi büyük çoğunluğunu uçurdum.

fallocate --collapse-range --offset 0 --length 577GiB --verbose /var/lib/docker/containers/7e1bb748c785343f47511e08cb10aa676df09d4fc4abe1f371bc7b49a3d88b9d/7e1bb748c785343f47511e08cb10aa676df09d4fc4abe1f371bc7b49a3d88b9d-json.log

Böylelikle dosyada 1 GB‘lik kısmı bıraktım, ancak ben bunu yapıncaya kadar sistemde yapılan işlemler sebebiyle +4 GB kadar daha log birikmişti. Docker ile ilgilenen arkadaşa ilettim konuyu(ancak çözmemiş)

Otomatik log rotasyonu aktif edildiği taktirde bu makalede anlatılan çözümü yapmanıza gerek yok.

Docker’da Varsayılan Veri Klasörü Nasıl Değiştirilir?

Docker’ı sisteme ilk kurduğunuzda indirilen imajlara kadar her şeyi /var/lib/docker içerisinde tutmaktadır. Başka bir yerde bu verileri depolamak istiyoruz, ben genellikle bu tarz verileri SSD üzerinde tutmam, Cold Storage üzerine alırım.

Çözümümüze gelecek olursak başlamadan önce varsayılan klasörü yani /var/lib/docker dizinini temizliyoruz.

docker system prune -a

Komutu işinizi fazlasıyla görecektir. Dizin temizlendikten sonra /etc/docker/daemon.json yoluna gidiyoruz karşınıza aşağıdaki gibi bir yapılandırma dosyası çıkacak.

{
 "authorization-plugins": [],
 "dns": [],
 "dns-opts": [],
 "dns-search": [],
 "exec-opts": [],
 "exec-root": "",
 "experimental": false,
 "storage-driver": "",
 "storage-opts": [],
 "labels": [],
 "live-restore": true,
 "log-driver": "",
 "log-opts": {},
 "mtu": 0,
 "pidfile": "",
 "graph": "",
 "cluster-store": "",
 "cluster-store-opts": {},
 "cluster-advertise": "",
 "max-concurrent-downloads": 3,
 "max-concurrent-uploads": 5,
 "shutdown-timeout": 15,
 "debug": true,
 "hosts": [],
 "log-level": "",
 "tls": true,
 "tlsverify": true,
 "tlscacert": "",
 "tlscert": "",
 "tlskey": "",
 "swarm-default-advertise-addr": "",
 "api-cors-header": "",
 "selinux-enabled": false,
 "userns-remap": "",
 "group": "",
 "cgroup-parent": "",
 "default-ulimits": {},
 "init": false,
 "init-path": "/usr/libexec/docker-init",
 "ipv6": false,
 "iptables": false,
 "ip-forward": false,
 "ip-masq": false,
 "userland-proxy": false,
 "userland-proxy-path": "/usr/libexec/docker-proxy",
 "ip": "0.0.0.0",
 "bridge": "",
 "bip": "",
 "fixed-cidr": "",
 "fixed-cidr-v6": "",
 "default-gateway": "",
 "default-gateway-v6": "",
 "icc": false,
 "raw-logs": false,
 "registry-mirrors": [],
 "seccomp-profile": "",
 "insecure-registries": [],
 "disable-legacy-registry": false,
 "default-runtime": "runc",
 "oom-score-adjust": -500,
 "runtimes": {
  "runc": {
   "path": "runc"
  },
  "custom": {
   "path": "/usr/local/bin/my-runc-replacement",
   "runtimeArgs": [
    "--debug"
   ]
  }
 }
}

Yapılandırma dosyasının içerisinde şu değişikliği yapıyoruz ve kayıt ediyoruz.

"data-root": "/disk1/data/docker_datum",
"storage-driver": "overlay2"

bu işlemin ardından systemctl restart docker komutu ile yeniden başlatıktan sonra gereken docker dosyalarını yeni yerinde bulabilirsiniz. Bu sayede ana diskiniz dolmadan işlemleri yapabilirsiniz.

Docker Makinada hv_get_dhcp_info: not found İle Başlayan Sorun

Hyper-V kullanan kurum ve kuruluşlarda kimi zaman linux ile işlem yapacaksınız. Bu esnada bilmeniz gereken en ilginç durum docker kurulumundan sonra gerçekleşmektedir. Özellikle Windows Server 2012 ve Windows Server 2012 R2 veya daha aşağısı kullanılan bir yapıda Docker kullanılmak isteniyor ise aşağıdaki logları görme ihtimaliniz yüksek, görmeme ihtimalinizde var. Üstelik docker çalıştığında yaptığı kontrollerden sonra konteynerleri yeniden başlatacak yada kapatacak sonucunda sizden yeniden başlatma isteyecek.

docker container gorsel

Loglarda karşımıza çıkan durum şu şekilde olacak tabi herşeyi kurulu olmasına karşın bu satırları kimi zaman görmeye devam edeceğiz.

May 25 19:16:43 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dhcp_info: not found
May 25 19:16:49 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dns_info: not found
May 25 19:16:49 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dhcp_info: not found
May 25 19:16:49 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dns_info: not found
May 25 19:16:49 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dhcp_info: not found
May 25 19:16:49 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dns_info: not found
May 25 19:16:49 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dhcp_info: not found
May 25 19:16:49 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dns_info: not found
May 25 19:16:49 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dhcp_info: not found
May 25 19:25:35 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dns_info: not found
May 25 19:25:35 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dhcp_info: not found
May 25 19:25:35 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dns_info: not found
May 25 19:25:35 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dhcp_info: not found
May 25 19:21:10 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dns_info: not found
May 25 19:21:10 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dhcp_info: not found
May 25 19:21:10 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dns_info: not found
May 25 19:21:10 generic-hostname hv_kvp_daemon[480]: sh: 1: hv_get_dhcp_info: not found

Bunu çözmek için Windows server 2012 ve R2 makinaya direk apt install hyperv-daemons komutunu vereceğinizi düşünüyorum. Kurulumlar tamamlandıktan sonra yukarıdaki durumun geçeceğini sanıyor olabilirsiniz. Haklsınızda lakin dockerda sıkıntı çıkmaya devam edecek.

Docker kardeşimizin çalışmasını sağlayabilmek için ilk başta Hyper-V ile sanallaştırma yapıp herhangi bir linux distro kurmayı bırakın. Direk olarak Windows Server 2016 kurulumu gerçekleştirin sistemin güncelleştirmelerini yapıp, temel sunucu kurulum prosedürünüzü izleyin.

docker windows

Burada en önemli nokta Windows server 2016‘da çekirdek tabanında uzunca süre çalışılıp native bir şekilde windows container yapısının aktif edilmiş olmasıdır. Çekirdek kapsayıcı yalnızca Windows Server 2016 ile kullanılabilir. Merak etmeyin WS 2016 üzerinde docker oldukça kararlı çalışıyor. Ne bir hata ne bir sorun ile karşılaşma imkanınızda pek yok.

Desteklenen sistemler için lütfen referans olarak uygunluk matrisine bakın.

Docker’da Tüm İmaj ve Konteynerları Kaldırma

Bu hafta kısa bir yazı ile sizlerleyim. Docker üzerinde çalışırken geliştirme ortamınızda saçma salak hatalar alıyorsanız. Uğraştınız ve çözemediyseniz. Kafanızı daha fazla yormayıp soruna neden olan bütün imaj ve konteynerları uçurabilirsiniz.

docker rm $(docker ps -a -q) && docker rmi $(docker images -q)

bu komutlar geliştirme ortamında kullandığınız tüm konteyner ve imaj yapısını ortadan kaldıracak ve size temiz bir geliştirme ortamı açmanıza imkan sağlayacaktır.

Kaldırılma işlemi sırasında aşağıdaki gibi bir hata ile karşılaşırsanız.

Error response from daemon: conflict: unable to delete 259ee57c2a34 (must be forced) - image is referenced in multiple repositories
Error response from daemon: conflict: unable to delete 259ee57c2a34 (must be forced) - image is referenced in multiple repositories
Error response from daemon: conflict: unable to delete 7721d6b4045f (cannot be forced) - image has dependent child images

Zorlama yaparak temizleme işlemini sonlandırabilirsiniz.

docker rmi $(docker images -q) --force

Her ne kadar önermesemde geliştirme ortamlarında dockerın saçmaladığı zamanlar gözü karartıp bu işlemleri yapmalısınız.

Docker’da Grup Atama İşini Oturumdan Çıkmadan Yapma

Herhangi bir kurulum makalesi ile Docker kurduğunuzda Docker komutlarını kullanabilmeniz için, kullanıcı adınızı docker grubuna almalısınız. Komutlar aracılığı ile bunu normal bir şekilde yapıp oturumunuz da komut satırına geldiğinizde haliyle sorunsuz bir şekilde çalışması gerekiyor.

Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: connect: permission denied

Şu komutu çalıştırarak kullanıcının yeni grup atamasının kabuğa yansımadığını görebiliriz:

id

baktığımızda gruba geçtiğini ama yetkilendirmenin olmadığını göreceksiniz. Yeni grup atamasını almanın tek yolu da, masaüstü ortamından çıkış yapmak ve tekrar oturum açmaktır. Tabi bunu istemeyebilirsiniz çok fazla sayfa açıktır işiniz vardır kapatılmaması gerekiyordur. Hiç sorun değil

Çakallık yapacağız sanal terminal ile bu işi çözeceğiz nasılsa oraya karışan kimse olmayacak

su - mertcan

Ardından diğer terminalde direk istediğiniz gibi komutunuzu çalıştırabilirsiniz.

Başka bir yöntem ise belirli bir grup veya GID ile bir komutu çalıştırmak tabi bunu aklımızda tutmamız zor ama ben genede buraya bırakıyorum.

sg docker -c "docker ps"

En Basit Haliyle Docker Nasıl Kurulabilir

Docker’ı yüklemenin herkes tarafından kullanılan yolu, deposunu ekleyerek bu depodan yüklemektir. bunu yaparken uygulayabileceğiniz bir sürü yöntem bulunuyor hatta zamanında bende anlatmıştım.

Docker paketini sık sık güncellemeyi tercih etmediğimden dolayı direk debian tabanlı sistemler için deb dosyasını indirip elle kurmayı tercih ediyorum.

Siz aynı alandan kullandığınız sistem için hazırlanmış olan paketi indirip kurabilirsiniz.

  • https://download.docker.com/linux/debian/dists/ Sisteminizin sürümünü seçerek gerekli olan paketi indirin
  • İndirdiğiniz paketi sudo dpkg -i docker*.deb komutu ile kurun
  • Kullanıcı adınızı docker grubuna eklemeyi unutmayın.
  • Değişikliklerin geçerli olması için oturumdan çıkıp girmeniz gerekebilir.
  • Hadi hayırlı olsun docker kurulumumuz tamamlandı

Veya bunlar bir yana dursun komut satırı ile de basit bir şekilde kurulum yapabilirsiniz.

sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

Bundan sonra dockerı istediğiniz gibi kullanabilirsiniz. Unutmayın yeni bir güncelleme geldiğinde otomatik olarak indirip kuramazsınız. El ile kuruluma devam etmeniz gerekir. (Güvenlik güncelleştirmeleri dışında güncellemeyebilirsiniz.)