Postgres on Docker — volumes

Yazı dizimize Docker üzerinde PostgreSQL ve PgAdmin kurulumuyla başlamıştık. Devamında ise Docker’da önemli bir diğer konu olan volume yönetimini inceleyeceğiz. Bunun için işe PostgreSQL 11 imajını çalıştırarak başlayacağız. Başka bir sürümle başlamayın çünkü yazının devamında verilerimizi bozmadan PostgreSQL 11.7’ye güncelleyeceğiz. Dolayısıyla konteynerin kullandığı veri dizinimiz etkilenmeden PostgreSQL’in iki minör sürümü tarafından kullanılabilecek. Burada veri dizinini, takip edecek olduğumuz ve (bu iki PostgreSQL sürümünün) ortak dosyalarımızı/verilerimizi tuttuğumuz klasör olarak da düşünebiliriz. Aşağıdaki komutta verilen -v etiketi volume ifadesini gösteriyor ve -e ile çevre değişkeni olarak superuser parolasını tanımlıyoruz. 

[profelis@profelis ~]$ docker run –name pg11 -d -e POSTGRES_PASSWORD=pass -v pg11data:/var/lib/postgresql/data postgres:11
Unable to find image ‘postgres:11’ locally
11: Pulling from library/postgres
5e35bd43cf78: Pull complete
0df82dab3c88: Pull complete
0670a6d74375: Pull complete
bd2be22379ef: Pull complete
9eb829e5b266: Pull complete
95d7f9aaa5bd: Pull complete
de97a90167ed: Pull complete
4b50288dfea5: Pull complete
b71c4d54a488: Pull complete
e1828d0d4b60: Pull complete
dce8713bb72b: Pull complete
37db80a505c4: Pull complete
936725e631d9: Pull complete
86ab6b577e4f: Pull complete
Digest: sha256:32f2b2b831bcc5318183eb09915c97552f7c2a9657b225f9ea0d23ed727271bb
Status: Downloaded newer image for postgres:11
2b891069b8bb9bfcb24952f4759ad66f9ad37134e35c6b0c41dc85e4242cd779

[profelis@profelis ~]$ docker container ls -a

CONTAINER ID           IMAGE                 COMMAND                        CREATED                 STATUS               PORTS           NAMES
2b891069b8bb          postgres:11        “docker-entrypoint.s…”    6 seconds ago       Up 4 seconds      5432/tcp        pg11

İlk yazıda Docker’da çalışan konteynerlerin yaşam döngüsünü kontrol etmiştik ancak sistem kaynaklarını nasıl kullandığını ya da ağda nasıl bir davranış gösterdiğini sonraki yazılara bırakmıştık. Aslında Docker, kendi içinde çalışan tüm konteynerlerin hem süreçlerini hem de disk kullanım alanlarını birbirlerinden yalıtır. Her konteyner, bünyesinde ayrı işletim sistemi süreçleri işletilirken ayrı disk alanlarında çalıştırılır.

Peki bir Docker konteyneri verilerini nereye yazar ya da yazar mı? Bunu kim, nerede tanımlar? Bu soruların cevaplarını bulmak için öncelikle Docker’da volume kavramını anlamak lazım. Evet, Docker konteynerleri de üzerinde çalıştığı işletim sisteminde “ön tanımlı” bir disk alanını kullanıyor ve biz bu disk alanını kontrol edebiliyor ya da tanımladığımız başka bir disk alanını kullanabiliyoruz. 

Öncelikle Docker konteynerleri ölümlüdür! Yani docker konteynerleri kapatıldığında içlerinde oluşturulan veriler de silinir. Docker, bir imajdan yeni bir konteyner ürettiğinde, kullanıcının yaptığı değişiklikler artık yoktur. Her Docker imajında ön tanımlı bazı ayarlar vardır ve bunların yer aldığı dosya Dockerfile olarak geçer. Diskte kullanılacak alanı temsilen Dockerfile’da yer alan volume tanımı da bu ayarlardan birisi olup imajdan üretilen konteynerde açıkça tanımlanması halinde diskte verilerin kalıcı olarak saklanabileceği bir alanın ön tanımını içerir. Konteyner durdurulsa ve silinse dahi tanımlanmış volume sayesinde konteynerde oluşturulmuş veriler Docker dosya sisteminde var olmaya devam edebilirler. Özetle “ölümlü” Docker konteynerleri durdurulduklarında verileri de beraberlerinde yok ederken, kullanıcının isteği doğrultusunda diskte saklama seçeneğini de sunarlar. Bu tür durumlarda konteynerde kalmasını istediğimiz veriler için volume tanımlaması yapılmalıdır. 

Volume ismine ve konumuna karar verebileceğimiz gibi bu atama işlerini Docker’a da bırakabiliriz. Ancak Docker otomatik isimlendirdiği volume’lere genellikle “insani” isimler de vermez. Bu süreci ve sonrasını kolayca yönetmek istiyorsak konteynerin kullanacağı disk alanını kendimiz tanımlar, böylece hem kullanılacak disk alanını hem de volume için Docker’ın kullanacağı ismi belirleyebiliriz. Docker, disk alanı tanımı için Data Volume ve Bind Mount olmak üzere iki farklı kullanıma izin verir. Bu yazıda biz sadece Data Volume üzerinden örnek vereceğiz. 

PostgreSQL 11 sürümümüze ait imaj dosyası pull ile Docker Registry’den çekildi ve çalıştırıldı. Eğer çalışan konteyner imajını docker container inspect’le sorgularsak konteynerin kullandığı disk alanını Volumes başlığında göreceğiz. Burada ayrıca Mounts kısmında bulunan source (kaynak) ve destination (hedef) etiketleri de işletim sistemindeki hangi dizinin konteynerdeki hangi dizine yönlendirildiğini (bağlantılarının eşlendiğini) görmek açısından önemli. 

[profelis@profelis ~]$ docker container inspect pg11

“Volumes”: {
                “/var/lib/postgresql/data”: {}
            },


“Mounts”: [
            {
                “Type”: “volume”,
                “Name”: “pg11data”,
                “Source”: “/var/lib/docker/volumes/pg11data/_data”,
                “Destination”: “/var/lib/postgresql/data”,
                “Driver”: “local”,
                “Mode”: “z”,
                “RW”: true,
                “Propagation”: “”

            }
        ],

Eğer bir konteyner -v etiketi kullanılmadan çalıştırılırsa Docker bu konteyneri, /var/lib/docker/volumes/ altında hash isimli bir veri dizini oluşturarak çalıştıracaktır. Örneğin deneme amaçlı başka PostgreSQL sunucusu oluşturalım ve bir volume tanımı yapmayalım. Bu durumda yukarıda bahsettiğimiz gibi volume izlemelerini yapmamızı zorlaştıracak biçimde Docker’ın “insani” olmayan isimlendirmesiyle karşılaşırız.

[omen@omen docker]$ docker run -d -e POSTGRES_PASSWORD=pass –name pg_test postgres:11

2aee185beb35ecd78406bf8578301ffef11d9fdea69854bc51c16c03b8e521d5
[omen@omen docker]$ docker container  ls

CONTAINER ID           IMAGE                 COMMAND                        CREATED                 STATUS               PORTS           NAMES
2b891069b8bb          postgres:11        “docker-entrypoint.s…”    5 seconds ago       Up 4 seconds      5432/tcp        pgtest

[omen@omen docker]$ docker volume ls
DRIVER              VOLUME NAME
local                      f12cc8cf7eee61af18715ba70e16c9ea8fd167a175c3fbfbc32b9dd4f9ad8a2a


[omen@omen docker]$ docker volume  inspect f12cc8cf7eee61af18715ba70e16c9ea8fd167a175c3fbfbc32b9dd4f9ad8a2a 

[
    {
        “CreatedAt”: “2020-04-21T07:10:16+03:00”,
        “Driver”
: “local”,
        “Labels”: null,
        “Mountpoint”: “/var/lib/docker/volumes/f12cc8cf7eee61af18715ba70e16c9ea8fd167a175c3fbfbc3
2b9dd4f9ad8a2a/_data”
,
        “Name”: “f12cc8cf7eee61af18715ba70e16c9ea8fd167a175c3fbfbc32b9dd4f9ad8a2a”,
        “Options”: null,
        “Scope”: “local”
    }
]

Bu volume’de ne olduğuna bakmak için yetkili kullanıcıya geçiyoruz ve Docker’ın sakladığı volume’ün içini incelediğimizde PostgreSQL veri dizinimizle karşılaşıyoruz.

[omen@omen docker]$ sudo su –
[omen ~]
#  cd /var/lib/docker/volumes/f12cc8cf7eee61af18715ba70e16c9ea8fd167a175c3fbfbc32
b9dd4f9ad8a2a/_data/

[omen _data]# ls -l
toplam 120

drwx—— 6 999 adm  4096 Nis 17 19:54 base
drwx—— 2 999 adm  4096 Nis 21 03:38 global
drwx—— 2 999 adm  4096 Nis 17 19:52 pg_commit_ts
drwx—— 2 999 adm  4096 Nis 17 19:52 pg_dynshmem
-rw——- 1 999 adm  4535 Nis 17 19:52 pg_hba.conf
-rw——- 1 999 adm  1636 Nis 17 19:52 pg_ident.conf
drwx—— 4 999 adm  4096 Nis 21 03:42 pg_logical
drwx—— 4 999 adm  4096 Nis 17 19:52 pg_multixact
drwx—— 2 999 adm  4096 Nis 21 03:37 pg_notify
drwx—— 2 999 adm  4096 Nis 17 19:52 pg_replslot
drwx—— 2 999 adm  4096 Nis 17 19:52 pg_serial
drwx—— 2 999 adm  4096 Nis 17 19:52 pg_snapshots
drwx—— 2 999 adm  4096 Nis 21 03:37 pg_stat
drwx—— 2 999 adm  4096 Nis 21 07:18 pg_stat_tmp
drwx—— 2 999 adm  4096 Nis 17 19:52 pg_subtrans
drwx—— 2 999 adm  4096 Nis 17 19:52 pg_tblspc
drwx—— 2 999 adm  4096 Nis 17 19:52 pg_twophase
-rw——- 1 999 adm     3 Nis 17 19:52 PG_VERSION
drwx—— 3 999 adm  4096 Nis 17 19:52 pg_wal
drwx—— 2 999 adm  4096 Nis 17 19:52 pg_xact
-rw——- 1 999 adm    88 Nis 17 19:52 postgresql.auto.conf
-rw——- 1 999 adm 23963 Nis 17 19:52 postgresql.conf
-rw——- 1 999 adm    36 Nis 21 03:37 postmaster.opts
-rw——- 1 999 adm    94 Nis 21 03:37 postmaster.pid

Zamanla bazı konteynerlerin kullanımı durabilir. Docker container rm komutunu kullanarak bir konteyneri sildiğimizde bu konteynerin kullandığı volume’ün silinmediğini söylemiştik. Diskte yer işgal eden, herhangi bir konteyner tarafından kullanılmayan tanımlanmış volume’lerin tamamını temizlemek için en kısa yol docker volume prune komutudur. Bu sayede kullanılmayan tüm data volume’leri tek komutla silinerek diskte yer açılabilir.

[omen _data]# docker volume ls 

DRIVER              VOLUME NAME
local               f12cc8cf7eee61af18715ba70e16c9ea8fd167a175c3fbfbc32b9dd4f9ad8a2a

[omen _data]# docker container rm -f wonderful_bassi
wonderful_bassi
[omen _data]# docker volume ls
DRIVER              VOLUME NAME
local               f12cc8cf7eee61af18715ba70e16c9ea8fd167a175c3fbfbc32b9dd4f9ad8a2a

[omen _data]# docker volume prune
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:

f12cc8cf7eee61af18715ba70e16c9ea8fd167a175c3fbfbc32b9dd4f9ad8a2a

Total reclaimed space: 40.54MB
[omen _data]# docker volume ls
DRIVER              VOLUME NAME

İlk örneğimize dönersek, pg11 konteynerimiz vardı ve bunu -v pg11data:/var/lib/postgresql/data etiketiyle başlattığımız için pg11data isimli bir data volume oluşturmuştuk. Docker diskteki bu dizini konteyner içindeki /var/lib/postgresql/data klasörüne yönlendirdi. Böylece PostgreSQL konteynerinde oluşan tüm datalar için diskimizde /var/lib/docker/volumes/pg11data dizinini kullanmak istediğimizi belirtmiş olduk. Bu klasörü izleyebilir, yedekleyebilir ya da yeni bir konteyneri buraya bağlayarak bu imaj çökse dahi yeni bir PostgreSQL ile kaldığı yerden devam etmesini sağlayabiliriz.

[profelis@profelis ~]$ docker volume ls
DRIVER              VOLUME NAME
local                   pg11data

Bir konteyneri çalıştırmadan da imajın kendi datalarını hangi dizinde tutacağını ilgili imajın Dockerfile’ını inceleyerek görebiliriz. Bunun için Docker Hub’da Postgres imaj sayfasında tags sekmesine geçerek üzerinde çalıştığımız sürüm etiketine tıklayalım. Şu linkten PostgreSQL:11 imajının Dockerfile’ına ulaşabilirsiniz. Dosyanın sonlarına doğru VOLUME satırında PostgreSQL imajı konteynerleştirildiğinde verilerini saklayacağı dizini belirtiyor. 

Bir data volume tanımlayarak başlattığımız PostgreSQL 11 konteynerine girelim ve PostgreSQL kümesine yeni bir veritabanı açarak tablolar ve veriler ekleyelim. Sonrasında ise bu konteyneri durdurarak silelim. Bakalım verilere ne olacak?

[profelis@profelis ~]$ docker exec -it pg11 bash
root@2b891069b8bb:/# su – postgres
postgres@2b891069b8bb:~$ psql
psql (11.7 (Debian 11.7-2.pgdg90+1))
Type “help” for help.

postgres=# \l

                                                         List of databases
  Name    |  Owner   | Encoding |  Collate   |   Ctype    |
———–+———-+———-+————+————+

 postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |

(3 rows)

postgres=# create database profelis;
CREATE DATABASE

postgres=#  \c profelis ;
You are now connected to database “profelis” as user “postgres”.
profelis=# create table departman(id serial, name text);
CREATE TABLE

profelis=# insert into departman values (1, ‘muhasebe’), (2, ‘teknik’), (3, ‘yonetim’);
INSERT 0 3

profelis=# select * from departman;
id |   name  
—-+———-
  1 | muhasebe
  2 | teknik
  3 | yonetim
(3 rows)

profelis=# \q

postgres@2b891069b8bb:~$ logout
root@2b891069b8bb:/# exit

[profelis@profelis ~]$ docker container stop pg11
pg11
[profelis@profelis ~]$ docker container rm pg11
pg11

PostgreSQL 11 durduruldu ve diskten silindi. Şimdi PostgreSQL 11.7 konteynerini, aynı klasörü data volume olarak gösterecek şekilde başlatacağız. Yani ilk veritabanımızda oluşturduğumuz verileri, PostgreSQL’i upgrade ettiğimizde hala görmeyi bekliyoruz.

[profelis@profelis ~]$ docker run –name pg117 -d -e POSTGRES_PASSWORD=pass -v pg11data:/var/lib/postgresql/data postgres:11.7
Unable to find image ‘postgres:11.7’ locally
11.7: Pulling from library/postgres

Digest: sha256:32f2b2b831bcc5318183eb09915c97552f7c2a9657b225f9ea0d23ed727271bb
Status: Downloaded newer image for postgres:11.7
514d87da4305b895c044df3612b801dfb079ac84f2333055b0105fd8c09aab3c

[profelis@profelis ~]$ docker container ls -a

CONTAINER ID           IMAGE                 COMMAND                        CREATED                 STATUS               PORTS           NAMES
2b891069b8bb          postgres:11        “docker-entrypoint.s…”    43 seconds ago       Up 42 seconds      5432/tcp        pg117

[profelis@profelis ~]$ docker exec -ti pg117 bash
root@514d87da4305:/# su – postgres
postgres@514d87da4305:~$ psql
psql (11.7 (Debian 11.7-2.pgdg90+1))
Type “help” for help.

postgres=# \l
                                List of databases
  Name    |  Owner   | Encoding |  Collate   |   Ctype    |   privileges  
———–+———-+———-+————+————+
postgres  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
profelis  | postgres | UTF8     | en_US.utf8 | en_US.utf8 |
template0 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |        
template1 | postgres | UTF8     | en_US.utf8 | en_US.utf8 |       
(4 rows)

postgres=# \c profelis
You are now connected to database “profelis” as user “postgres”.


profelis=# select * from departman;
id |   name  
—-+———-
  1 | muhasebe
  2 | teknik
  3 | yonetim
(3 rows)

Burada farkındaysanız PostgreSQL üzerinde küçük bir güncelleme işlemini Docker konteynerlerinin bize sunduğu yapı sayesinde yapmış olduk. PostgreSQL özelinde büyük sürüm değişikliklerini (PostgreSQL 10’dan 11’e gibi) bu şekilde yapmamız mümkün değil. Çünkü büyük sürüm değişikliklerinde PostgreSQL’in dahili dosya depolama formatı da (muhtemelen) değiştirildiği için yalnız veri dizini değil, PostgreSQL’in yapısı da farklılık gösterecektir. PostgreSQL’de bu tür ana sürüm güncellemelerinde resmi dokümantasyonu da esas alarak farklı araçlar ve yöntemlerle sürüm güncelleme sürecini tamamlayabilirsiniz. 

Yazının sonuna geldiğimizde artık Docker’ın diskte kullandığı alanı nasıl bulacağımızı ve PostgreSQL özelinde bu alandaki verileri nasıl kullanabileceğimizi biliyoruz. Sonraki yazıda Docker network komutlarına bakacağız. Yazı dizimizi takipte kalın!